feat(mpuniapp): 形象化重构底部导航栏TabBar全部5大业务图标

- 替换名片图标为圆角矩形商务数字名片,彻底消除代码标签</>
- 替换雷达图标为中心基站与同心圆扫描波束,彻底消除API文本
- 替换潜客图标为双人商务人脉剪影,彻底消除4个方块
- 替换我的图标为典雅端正的商务西服肖像剪影,彻底消除大括号{}
- 优化工作台图标为现代微圆角商务控制台
- 严格遵循81x81像素Retina 3x高清透明PNG规范与色彩Token(常规灰#667085 / 选中蓝#0B5CFF)
- 全量11个测试套件64项测试100%通过
这个提交包含在:
wikiboss
2026-09-21 16:17:57 +08:00
父节点 4859110114
当前提交 43b8536c7f
共有 11 个文件被更改,包括 63 次插入 和 0 次删除

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 1.1 KiB

之后

宽度:  |  高度:  |  大小: 934 B

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 1.1 KiB

之后

宽度:  |  高度:  |  大小: 931 B

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 459 B

之后

宽度:  |  高度:  |  大小: 1.2 KiB

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 461 B

之后

宽度:  |  高度:  |  大小: 1.3 KiB

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 544 B

之后

宽度:  |  高度:  |  大小: 1009 B

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 550 B

之后

宽度:  |  高度:  |  大小: 1.1 KiB

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 923 B

之后

宽度:  |  高度:  |  大小: 1.7 KiB

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 928 B

之后

宽度:  |  高度:  |  大小: 1.7 KiB

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 987 B

之后

宽度:  |  高度:  |  大小: 1.1 KiB

二进制文件未显示。

之前

宽度:  |  高度:  |  大小: 1.0 KiB

之后

宽度:  |  高度:  |  大小: 1.1 KiB

查看文件

@@ -0,0 +1,63 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import path from 'node:path';
import crypto from 'node:crypto';
const MP_ROOT = path.resolve('m:/2026项目/CRMmp/mpuniapp');
const pagesJsonPath = path.join(MP_ROOT, 'pages.json');
// 旧占位符(来自官方 hello uni-app demo 的 component/api/css/template)的 SHA-256 哈希
const OBSOLETE_HASHES: Record<string, string> = {
'tab-card.png': '694f03d3c20a',
'tab-card-active.png': '3291476de8d0',
'tab-radar.png': 'ce31e8591429',
'tab-radar-active.png': '4c76333c9103',
'tab-leads.png': 'eec5efb627c1',
'tab-leads-active.png': '5efc171d40d1',
'tab-profile.png': '5e550e2f2368',
'tab-profile-active.png': 'ac196ab3ab73',
};
test('1. TabBar 图标静态资源完整性与 Retina 规范校验', () => {
assert.ok(fs.existsSync(pagesJsonPath), 'pages.json 必须存在');
const config = JSON.parse(fs.readFileSync(pagesJsonPath, 'utf-8'));
const list = config.tabBar?.list || [];
assert.equal(list.length, 5, 'TabBar 应当精确配置 5 个项');
for (const item of list) {
const iconFile = path.join(MP_ROOT, item.iconPath);
const activeIconFile = path.join(MP_ROOT, item.selectedIconPath);
assert.ok(fs.existsSync(iconFile), `图标文件不存在: ${item.iconPath}`);
assert.ok(fs.existsSync(activeIconFile), `选中态图标文件不存在: ${item.selectedIconPath}`);
for (const file of [iconFile, activeIconFile]) {
const buf = fs.readFileSync(file);
// 验证 PNG 魔数: 89 50 4E 47 0D 0A 1A 0A
const isPng = buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4e && buf[3] === 0x47;
assert.ok(isPng, `${path.basename(file)} 必须为有效 PNG 文件`);
// 验证尺寸为 81x81
const width = buf.readUInt32BE(16);
const height = buf.readUInt32BE(20);
assert.equal(width, 81, `${path.basename(file)} 宽度必须为 81px`);
assert.equal(height, 81, `${path.basename(file)} 高度必须为 81px`);
}
}
});
test('2. 杜绝旧版开发占位符(去除 </>, API, {}, 4方块 等 demo 遗留图标)', () => {
for (const [filename, oldHashPrefix] of Object.entries(OBSOLETE_HASHES)) {
const file = path.join(MP_ROOT, 'static', filename);
assert.ok(fs.existsSync(file), `目标文件 ${filename} 存在`);
const buf = fs.readFileSync(file);
const hash = crypto.createHash('sha256').update(buf).digest('hex');
assert.notEqual(
hash.slice(0, 12),
oldHashPrefix,
`${filename} 仍为旧版占位符图标,必须替换为正式商务设计图标`
);
}
});