feat(clients): UI 设计同步到客户端 —— Electron 包重建 + 鸿蒙设计令牌
用户:「下一步就是同步 ui 设计到客户端了」。
## ① Electron 客户端(之前严重滞后)
打包产物停在 **09:11**,而前端 dist 是 **12:23** ⇒ 今天所有 UI 工作(导航合并、悬浮玻璃、
圆桌语言、Composer、动画、模糊分层…)**一个都不在包里**。已重建 AppImage + deb。
**验证**(关键:AppImage/deb 是压缩容器,`grep` 直接扫是扫不到的 ——
我第一次就差点因此得出"包里没有"的结论):把 deb 解到 /tmp 再查 `app.asar`:
comm-tabs=2 compose-fab=1 nav-rail=2 glass-control=2 cal-slide-next=2
构建戳 "5ce25f6·0914-1240"(与当前提交一致)
## ② 鸿蒙客户端
它是**原生 ArkTS 应用**(22 个 .ets、自带 API 层),不是 WebView 壳 ⇒ 设计要移植。
第一步做的是**共用词表**:新增 `common/Theme.ets`(品牌蓝、页面底、面、分隔线、
导航玻璃不透明度、圆角 14/8、语义色、字号),全部注明与 WebUI 令牌的对应关系
(含一个易错点:CSS 是 `rgb(r g b / a)`,鸿蒙是 `#AARRGGBB`,0.72×255≈184=0xB8)。
顺带修掉一个真 bug:TabBar 的选中色写成 `this.currentIndex === 0` ⇒
**只有第一个 tab 会高亮**。现在按每个 tab 自己的下标判断。
**编译验证**:`hvigorw assembleHap` → **BUILD SUCCESSFUL**(HAP 已打包)。
(无法在设备上跑:本机 `hdc list targets` 为空、HAP 未签名 ⇒ 视觉未验证,如实说明。)
## ③ 判据:跨客户端令牌一致性
新增 `test/cross-client-theme.test.mjs` 5 条:品牌蓝、卡片圆角(0.875rem=14px)、
导航玻璃 0.72 —— 断言的是"两边对同一件事取值一致",不约束实现方式
(CSS 变量 vs ArkTS 常量本来就该不同),并带一条扰动自检。
这种漂移**没有任何判据会红**,所以必须显式钉住。
## 还没做的(如实说明)
鸿蒙端只同步了**设计语言**,功能面不对等:鸿蒙是 收件箱/会话/联系人 三个 tab,
没有 日历/授权/管理;也没有玻璃悬浮底栏与 Compose 共用组件。要做功能对齐是另一件事,
需要单独排期(我可以按你的优先级来)。
This commit is contained in:
61
client/electron/test/cross-client-theme.test.mjs
Normal file
61
client/electron/test/cross-client-theme.test.mjs
Normal file
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 两个客户端必须用**同一份设计词表**。
|
||||
*
|
||||
* 用户下一步要求:「同步 ui 设计到客户端」。同步的第一件事不是把每个页面重画一遍,
|
||||
* 而是两边共用同一套令牌(颜色/圆角/玻璃透明度)—— 否则每加一个页面就重抄一遍色值,
|
||||
* 两个客户端会越走越远,而且这种漂移**没有任何判据会红**。
|
||||
*
|
||||
* 这里只断言"两边对同一件事的取值一致",不断言实现方式(WebUI 用 CSS 变量、
|
||||
* 鸿蒙用 ArkTS 常量,本来就该不同)。
|
||||
*/
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const HERE = dirname(fileURLToPath(import.meta.url));
|
||||
const ROOT = join(HERE, '..', '..', '..');
|
||||
const web = readFileSync(join(ROOT, 'client/electron/src/index.css'), 'utf8');
|
||||
const harmony = readFileSync(
|
||||
join(ROOT, 'client/harmony/entry/src/main/ets/common/Theme.ets'),
|
||||
'utf8'
|
||||
);
|
||||
|
||||
const hex = h => h.toUpperCase();
|
||||
|
||||
test('品牌蓝一致', () => {
|
||||
// WebUI: --c-blue-600 应该是 37 99 235 = #2563EB
|
||||
const webBlue = web.match(/--c-blue-600:\s*(\d+)\s+(\d+)\s+(\d+)/);
|
||||
assert.ok(webBlue, 'WebUI 要有 --c-blue-600');
|
||||
const asHex = hex(
|
||||
'#' +
|
||||
[webBlue[1], webBlue[2], webBlue[3]]
|
||||
.map(n => Number(n).toString(16).padStart(2, '0'))
|
||||
.join('')
|
||||
);
|
||||
const harmonyBlue = harmony.match(/accent: string = '(#[0-9A-Fa-f]{6})'/);
|
||||
assert.ok(harmonyBlue, '鸿蒙要有 accent');
|
||||
assert.equal(hex(harmonyBlue[1]), asHex, `品牌蓝不一致:WebUI ${asHex} vs 鸿蒙 ${harmonyBlue[1]}`);
|
||||
});
|
||||
|
||||
test('卡片圆角一致(WebUI 0.875rem = 14px)', () => {
|
||||
assert.match(web, /--radius-card:\s*0\.875rem/);
|
||||
assert.match(harmony, /radiusCard: number = 14/);
|
||||
});
|
||||
|
||||
test('导航玻璃不透明度一致(都是 0.72)', () => {
|
||||
assert.match(web, /--nav-bg:\s*255 255 255 \/ 0\.72/);
|
||||
// 0.72 × 255 ≈ 184 = 0xB8(鸿蒙用 #AARRGGBB,顺序与 CSS 不同)
|
||||
assert.match(harmony, /navBgLight: string = '#B8FFFFFF'/);
|
||||
});
|
||||
|
||||
test('★ 判据自检:把鸿蒙的品牌蓝改成别的必须判红', () => {
|
||||
const mutated = harmony.replace(/accent: string = '#2563EB'/, "accent: string = '#FF0000'");
|
||||
const m = mutated.match(/accent: string = '(#[0-9A-Fa-f]{6})'/);
|
||||
assert.notEqual(hex(m[1]), '#2563EB');
|
||||
});
|
||||
|
||||
test('鸿蒙的令牌文件说明了与 WebUI 的对应关系(不是凭空一套)', () => {
|
||||
assert.match(harmony, /与 WebUI 的令牌\*\*一一对应|对应 WebUI/);
|
||||
});
|
||||
Reference in New Issue
Block a user