Files
toor 01d9e301fc
Build / build (push) Canceled after 0s
Playwright / browser (push) Canceled after 0s
initialize Electron desktop app and remove unused files
2026-10-25 01:09:01 +08:00

336 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase 005 — ArkScope vendor + 样板页(风格基线)
| 项 | 值 |
|---|---|
| 目标 | vendor 2 个 ArkScope 组件;用 LoginView + AppSidebar 定视觉语言基线 |
| 前置 | Phase 004 通过(项目功能完整) |
| 产出 | `docs/ui-baseline.md` 定稿;后续批量迁移照它执行 |
| 参考 | `docs/03-施工蓝图.md` §7 |
---
## 0. 为什么必须先做样板页
naive-ui 渗透面是 **100 个文件 / 105 处**(不只 27 个视图,还含 `components/` `stores/` `composables/`)。
先定基线再批量,否则 100 个文件会被改两遍。
---
## 1. 功能任务 1 — vendor ArkScope
### 1.1 上游现状(已核实)
`github.com/triplepiers/ArkScope` — "对鹰角 UI 组件的拙劣复现",Vue 3 + Vite,**无 LICENSE**,仓库 322MB。
`src/components/` 下的组件绝大多数**强绑定游戏美术素材**(干员立绘、Banner)。`public/assets/` 有 131 个路径全是游戏资源。
**只有 2 个组件可脱离素材复用**:
| 组件 | 依赖 | 判定 |
|---|---|---|
| `ScrambleTitle` | 纯 DOM + CSS,文字解码动画 | ✅ vendor |
| `IsometricWave` | CSS filter + 位移,类亚克力 hover | ✅ vendor |
| `Particle2D` / `Particle3D` | 3D 模型 + 遮罩图 | ❌ 太重 |
| `OperatorCarousel` / `ArknightsMedia` / `GameplayAlbum` / `NoticeCarousel` / `FullScreenNavigator` / `NewsList` / `EndfieldSidebar` / `Endfield/*` | 全部依赖游戏素材,脱离素材无意义 | ❌ 不 vendor |
其余组件的价值是**视觉手法**(斜切遮罩、噪点、扫描线、Reveal & Blink),后续页面按需自己实现,不搬代码。
### 1.2 做法
**不作为 npm 依赖,不 clone 整仓。**
1. 定位上游 commit(`main` 分支,记录 `pushed_at`),记下来
2. 只下载 `ScrambleTitle` 与 `IsometricWave` 两个组件目录
3. 放到 `packages/client/src/vendor/arkscore/<ComponentName>/`
4. **剥离**:`useAssetPreloader` / `particle3DModels` / `data/componentRegistry.js` / `router` / `views` / `showcases`(上游 demo 工程的东西)
5. **不搬**:`src/assets/**`、`public/**`、上游全局 `src/style.css`(只取组件 scoped 样式)
6. 保留原文件头注释 + 上游 commit hash
### 1.3 PROVENANCE.md(**必写**)
`packages/client/src/vendor/arkscore/PROVENANCE.md`:
```markdown
# ArkScope vendored components
- 上游:https://github.com/triplepiers/ArkScope
- commit:<hash>
- 拉取日期:<date>
- vendor 的组件:ScrambleTitle、IsometricWave
- 剥离内容:demo 工程(router/views/showcases/componentRegistry)、assets、public
## 许可证
上游仓库无 LICENSE 文件。
本 vendor 仅用于个人自用,不分发、不商用。
若要公开分发,必须先向上游作者取得授权或替换为自研实现。
## 未 vendor 的组件
其余组件强依赖鹰角游戏美术素材,脱离素材无实际价值,故未 vendor。
本项目仅参考其视觉手法(斜切遮罩、噪点、扫描线等),自行实现。
```
### 1.4 新增机械化测试
`tests/client/arkscope-vendor.test.ts`:
- 断言 `vendor/arkscore/` 下无 `.png` / `.jpg` / `.webp` / `.mp3` / `.mp4`
- 断言源码内无 `src/assets` / `public/assets` 引用
- 断言 `PROVENANCE.md` 存在且含 commit hash
- 断言组件总数 ≤ 3(防回流)
---
## 2. 功能任务 2 — 使用边界(硬约束,写进 `AGENTS.md`)
| 允许 | 禁止 |
|---|---|
| 登录页标题区装饰位面 | 聊天页(消息区 / 输入区 / 侧边栏) |
| Journey 图谱的背景层 | 任何表单页(Skills / Profiles / Settings / Models) |
| 首屏 loading / 空状态 | 任何表格页(Usage / Logs / Jobs / Kanban) |
| | 任何点击交互密集区域 |
**判定标准**:装饰位面**不得承载信息、不得承载交互、不得影响可读性**。
**`pointer-events: none` 是硬要求**,装饰层绝不能吃掉点击。
---
## 3. 功能任务 3 — 主题令牌迁移
### 3.1 新建 `packages/client/src/styles/arco-theme.ts`
替换 `styles/theme.ts`(Naive 的 `GlobalThemeOverrides`):
```ts
export const arcoTheme = {
// 延续旧版克制灰阶
colorPrimary: '#2f2f2f',
colorBgLayout: '#fafafa',
colorBgContainer: '#ffffff',
borderRadius: 6,
fontFamily: 'Inter, system-ui, -apple-system, sans-serif',
fontFamilyMono: '"JetBrains Mono", "Fira Code", monospace',
}
```
暗色模式走 CSS 变量覆盖,**不用** Arco 的 `dark` theme + overrides 双轨。
### 3.2 改 `App.vue`
旧版是 `naiveTheme` computed(浅/深两套对象)。改为:
```html
<div class="app-root" :class="{ 'dark': isDark }">
```
暗色由 `body[arco-theme='dark']` 承载(Arco 官方机制),app 只负责切换属性。
### 3.3 改 `index.html` 的 FOUC 脚本
保留旧版机制(内联脚本读 localStorage 提前定 class),但改为切 `arco-theme` 属性:
```html
<script>
// 首屏前定主题,避免闪烁
const t = localStorage.getItem('hermes_style')
document.documentElement.setAttribute('arco-theme', t === 'dark' ? 'dark' : 'light')
</script>
```
> localStorage key `hermes_style` **保留**(Phase 002 白名单)。
### 3.4 `styles/variables.scss`
保留,但把 Naive 相关的 SCSS 变量映射到 Arco CSS 变量名。Phase 007 会重写 `style-system.test.ts`。
---
## 4. 功能任务 4 — 全局 Provider
`main.ts`:
```ts
import ArcoVue from '@arco-design/web-vue'
import '@arco-design/web-vue/dist/arco.css'
app.use(ArcoVue)
```
**全量注册,不做按需 tree-shaking** —— 换迁移速度,包体问题后期再优化。
---
## 5. 功能任务 5 — 三个全局 hook 适配层(**Phase 006/007 强依赖,先写**)
Naive 是 hook API,Arco 是命令式。全仓 200+ 处调用,必须先建适配层并**冻结接口**,后续批次只调用适配层。
新建 `packages/client/src/composables/`:
### 5.1 `useMessage.ts`
```ts
// 模块级单例,main.ts 里 app.use 时把 Message 实例挂上来
export function useMessage() {
return {
success: (c) => Message.success(c),
error: (c) => Message.error(c),
warning: (c) => Message.warning(c),
info: (c) => Message.info(c),
loading: (c) => Message.loading(c),
}
}
```
### 5.2 `useDialog.ts`
```ts
export function useDialog() {
return { confirm, info, success, error, warning }
}
```
### 5.3 `useNotification.ts`
```ts
export function useNotification() {
return { success, error, warning, info }
}
```
**要求**:这三个文件的对外签名定下后**不再改**。后续所有批次只能调它们。若发现签名不够用,先扩适配层,不要在页面里直接引 Arco 的 `Message`。
---
## 6. 功能任务 6 — 组件映射表
新建 `docs/arco-mapping.md`,把全仓用到的 Naive 组件逐个映射。**先建表再改代码。**
已核实的核心映射(完整表在 Phase 006 补全):
| Naive | Arco | 备注 |
|---|---|---|
| `NConfigProvider` | 直接用 `body[arco-theme]` | 不需要组件 |
| `NMessageProvider` / `useMessage` | `useMessage()` composable | §5.1 |
| `NDialogProvider` / `useDialog` | `useDialog()` composable | §5.2 |
| `NNotificationProvider` / `useNotification` | `useNotification()` composable | §5.3 |
| `NButton` | `a-button` | prop 名基本一致 |
| `NInput` | `a-input` / `a-textarea` | `type=textarea` → 独立组件 |
| `NSelect` | `a-select` | `options` 语义不同,需转换函数 |
| `NDataTable` | `a-table` | **列定义 API 差异最大** |
| `NModal` | `a-modal` | `v-model:show` 一致 |
| `NDrawer` | `a-drawer` | |
| `NPopover` / `NTooltip` | `a-popover` / `a-tooltip` | trigger 语义有差异 |
| `NSwitch` | `a-switch` | |
| `NTag` | `a-tag` | `bordered` 无对应,改 style |
| `NCard` | `a-card` | |
| `NProgress` | `a-progress` | |
| `NEmpty` | `a-empty` | |
| `NCollapse` | `a-collapse` | |
| `NTabs` | `a-tabs` | |
| `NForm` / `NFormItem` | `a-form` / `a-form-item` | **校验规则 API 不同,需适配** |
| `NGrid` / `NGridItem` | `a-row` / `a-col` | |
| `NIcon` | `<Icon />` + `@arco-design/web-vue/es/icon` | 需全局注册 |
| `NLayout` / `NLayoutSider` | `a-layout` / `a-layout-sider` | |
| `NScrollbar` | `a-scrollbar` | |
| `NTimeline` | `a-timeline` | |
| `NUpload` | `a-upload` | |
| `NTree` | `a-tree` | 结构差异大 |
生成实际用到的组件清单:
```bash
grep -rho "from 'naive-ui'" packages/client/src | wc -l # 105 处
grep -rhoE "N[A-Z][a-zA-Z]+" packages/client/src --include="*.vue" | sort -u
```
---
## 7. 功能任务 7 — 样板页 A:LoginView
选它的理由:只用到输入框 + 按钮 + 少量文本,是**最小可控面**,且是装饰位面的合法落点。
改造点:
- `NInput` → `a-input`
- `NButton` → `a-button`
- 标题区挂 ArkScope `ScrambleTitle`
- 明暗切换走 §3.2 机制
- 装饰层 `pointer-events: none`
- 保留「hermes CLI 缺失引导」的入口(Phase 004 做的 `HermesMissingView`)
---
## 8. 功能任务 8 — 样板页 B:AppSidebar
选它的理由:全站导航唯一入口。Arco 的 `Menu` / `Layout.Sider` 语义在这里定型后,其余 20+ 视图的导航写法全部照抄。
改造点:
- `NLayout` / `NLayoutSider` → `a-layout` / `a-layout-sider`
- `NMenu` → `a-menu`
- `NIcon` → Arco Icon
- `NSelect`(profile 切换)→ `a-select`
- `NAvatar` → `a-avatar`
- `NTooltip` → `a-tooltip`
**它同时是最难场景**:深层文件 + `v-if="isSuperAdmin"` 条件渲染 + 大量 naive 引用。过了这关,剩下的是体力活。
---
## 9. 功能任务 9 — 定稿 `docs/ui-baseline.md`
本 Phase 的**核心交付物**。必须写清:
1. 色板(浅色 / 深色两套,含强调色)
2. 圆角 / 间距 / 字号阶梯
3. 字体栈(中英 / 等宽)
4. 阴影 / 分割线
5. 组件使用约定(什么时候用 Modal 什么时候用 Drawer、表格空态怎么处理、loading 怎么做)
6. 装饰位面的边界(复述 §2,落到本文档)
7. 三条不可破的规则:
- 不引入第三个 UI 库
- 消息气泡内部不用 Arco 组件(性能,见 Phase 006)
- 装饰层一律 `pointer-events: none`
---
## 10. 验收
```bash
cd /Users/jack/source/MyProject/js/hermes-ui
# vendor 边界
find packages/client/src/vendor/arkscore -type f \
\( -name "*.png" -o -name "*.jpg" -o -name "*.webp" -o -name "*.mp3" \)
# 期望:零命中
npm run test # 含 arkscope-vendor.test.ts
npm run build
```
### 手工
- [ ] 登录页渲染正常,ArkScope 位面出现且鼠标穿透
- [ ] 明暗切换无闪烁(刷新页面验证)
- [ ] 侧边栏菜单全部可点进各视图(视图本身还没迁移,样式不完美是正常的)
- [ ] `npm run dev` 无 console 报错
- [ ] `docs/ui-baseline.md` 已写完
---
## 11. 风险
| # | 风险 | 缓解 |
|---|---|---|
| P5-1 | ArkScope 上游无 LICENSE | 只 vendor 2 个无素材组件;PROVENANCE.md 标注;仅自用;`arkscope-vendor.test.ts` 卡住素材回流 |
| P5-2 | 装饰层吃掉点击导致登录按钮点不动 | `pointer-events: none` 写进基线文档 + 手工点一遍 |
| P5-3 | 暗色切换闪烁 | FOUC 脚本在 `index.html` 内联,必须同步改(§3.3) |
| P5-4 | composable 适配层签名后续不够用 | §5 要求先定后冻结,不够用先扩适配层 |
| P5-5 | 样板页看着好,批量时发现约定不够 | §9 的基线文档要覆盖到 Phase 006/007 会遇到的场景 |
| P5-6 | 忘删 Naive Provider 包裹层 | 改完 grep `NConfigProvider` / `NMessageProvider` 确认归零 |
---
## 12. 完成标志
**风格基线定稿。** 进入 Phase 006(聊天页迁移)。