玄学放置 · 双 Tab 切换、favicon 与学科改名
三个细节优化叠加
这一阶段的改动都不大,但都属于"做完了之后整体感会明显提升"的那类细节:
- 右侧面板加「装备 / 物品」两个 Tab,区分装备和道具
- 生成站点的 favicon,样式用「玄学」像素图标
- 把「语文」改成更通用的「语言」,并调整任务列表
物品分类设计
之前所有物品都是装备(有 slot 字段、能装到对应槽位)。但游戏后续肯定会有消耗品、材料、货币这类不能装备的"道具",需要在数据层先把分类抽出来。
给 ITEMS_DB 加一个 category 字段:
export const CATEGORIES = {
equipment: { name: '装备', color: '#5eead4' },
consumable: { name: '消耗品', color: '#f472b6' },
material: { name: '材料', color: '#a78bfa' },
currency: { name: '货币', color: '#fbbf24' }
}equipment 类才有 slot 字段,其他三类不可装备、只在物品 Tab 展示。
stores/inventory.js 加两个 getter:
equipmentBag() {
return this.bagView.filter(e => e.item?.category === 'equipment')
},
itemBag() {
return this.bagView.filter(e => e.item && e.item.category !== 'equipment')
}视图层只消费 getter,不再自己写过滤逻辑——这是"组件薄、store 厚"的常见做法,确保 UI 层永远拿到符合语义的数据。
Tab 切换交互
EquipmentPanel 顶部加一行 Tab:
<header class="equip-panel__tabs">
<button v-for="tab in TABS"
:class="{ 'equip-panel__tab--active': activeTab === tab.key }"
@click="activeTab = tab.key">
{{ tab.name }}
<span class="equip-panel__tab-badge">{{ tab.count }}</span>
</button>
</header>每个 Tab 右边带数量徽章(显示当前 Tab 下的物品数量),玩家不切到那个 Tab 也能知道"那边有几件物品"。这是相对低成本但有效的提示。
装备 Tab:保留原有的装备槽 + 人物剪影 + 装备类背包
物品 Tab:装备槽不显示,下方是消耗品/材料/货币的网格
物品 Tab 里再加一行二级筛选 chip:「全部 / 消耗品 / 材料 / 货币」。每个 chip 是 ref 切换:
const itemFilter = ref('all')
const filteredItems = computed(() => {
if (itemFilter.value === 'all') return store.itemBag
return store.itemBag.filter(e => e.item?.category === itemFilter.value)
})UI 层的筛选只是过滤已有数据,不动 store,不发请求,状态切换近乎零成本。
5 件 mock 道具
既然分类抽好了,就配套加几件道具进去:
| 道具 | 分类 | 品质 | 效果文案 |
|---|---|---|---|
| 矿泉水 ×6 | 消耗品 | 普通 | 回复体力 +20 |
| 粉笔 ×12 | 消耗品 | 普通 | 下次任务暴击 +20% |
| 笔记本 ×3 | 材料 | 优秀 | 合成材料 |
| 通行证 ×1 | 材料 | 稀有 | 解锁玄学副本 |
| 奖学金 ×188 | 货币 | 传说 | 商店消费 |
每个道具都有自己的 16×16 像素图标,和学科/装备图标共用同一个 PixelIcon 组件。这是上一阶段做像素体系时埋下的好处——新增视觉资产几乎是"加一行字符画 + 调色板"的工作量。
favicon 生成
「玄学」图标本身设计感最强(水晶球 + 全知之眼 + 底座),用它做 favicon 既呼应项目代号 metaphysics,又能立刻让标签页显出风格。
最佳方案是同时提供 SVG 和 ICO 两种格式:
favicon.svg:现代浏览器优先用,无损放大favicon.ico:老浏览器 fallback
SVG favicon
直接复用 pixelIcons.js 里玄学图标的字符画数据,跑一段 Node 脚本生成 SVG:
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"
shape-rendering="crispEdges">
<rect width="16" height="16" rx="2" fill="#0b0f1a"/>
${rects.join('')}
</svg>`
fs.writeFileSync('public/favicon.svg', svg)这里加了一个 rx="2" 的圆角深色底,是给 favicon 专用的(图标本体在游戏里是嵌在面板里的,没有独立背景;做成 favicon 时需要一个完整的"小方块"语义)。
ICO 文件:手写 BMP-in-ICO
ICO 是二进制格式,本来需要 ImageMagick 之类的工具转换。但 ICO 的结构其实很简单,可以直接用 Node Buffer 手写:
[6 字节 ICONDIR]
reserved (2) | type=1 (2) | count=1 (2)
[16 字节 ICONDIRENTRY]
width(1) | height(1) | colors(1) | reserved(1)
planes=1 (2) | bpp=32 (2)
bytes_in_resource (4) | image_offset (4)
[40 字节 BITMAPINFOHEADER]
size=40 | width=16 | height=32 (2x: XOR + AND)
planes=1 | bpp=32 | compression=0 | ...
[1024 字节像素数据] (16×16×4 BGRA, BMP 是底->上扫描)
[32 字节 AND mask] (1 bit/pixel, 16×16)总共 1118 字节。关键细节:
- BMP 像素是 BGRA 而不是 RGBA:要在写入时做颜色通道交换
- BMP 是底->上扫描:循环时
bmpY = H - 1 - y - biHeight 要写两倍高度:因为 ICO 内嵌的 BMP 同时包含 XOR mask(彩色)和 AND mask(透明掩码)
- AND mask 即使 32bpp 也要保留:写全 0 即可
写完后用 file 命令验证:
$ file public/favicon.ico
public/favicon.ico: MS Windows icon resource - 1 icon, 16x16, 32 bits/pixel合规。
index.html 双 link 标签
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="alternate icon" type="image/x-icon" href="/favicon.ico" />alternate icon 是兼容性更好的写法。Vite 会自动把 public/ 下的内容复制到构建产物根目录,开发模式与生产模式都可以直接通过 /favicon.ico 访问。
学科改名:语文 → 语言
「语文」对应中文学科,但游戏里其实想覆盖更广的语言学习——包括外语、文言、写作。改名为「语言」之后任务也跟着调整:
背诵唐诗三百首 4s +6 EXP
记英语单词 3s +5 EXP
写一篇命题作文 9s +16 EXP
文言文断句 6s +10 EXP新增「记英语单词」是这次扩张的关键——不光是中文了。
改名带来一些连锁修改需要同步:
mock/inventory.js中铅笔、画笔的属性描述「语文经验」改成「语言经验」- 装备 Tooltip 里的属性 KV 同步更新
这是产品命名变化时容易忽略的边角工作。但既然涉及到属性文案,就要保持术语一致,否则玩家会困惑「装备到底加哪个学科的经验」。
收获
这一阶段三件事单看都是细节,合在一起会让产品体感跨一个台阶:
- Tab 切换 + 计数徽章让右侧面板能容纳更多物品分类,扩展性打开
- favicon 让浏览器标签页一眼就能识别项目,是产品"成型感"的关键标识
- 学科改名看似小事,但牵动属性文案和任务列表,体现了"命名变化要同步上下游"的工程纪律
下一篇会做一个面向素材复用的工具:把所有像素图标导出成独立的 SVG 文件,方便后续二次使用。