# 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//` 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: - 拉取日期: - 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
``` 暗色由 `body[arco-theme='dark']` 承载(Arco 官方机制),app 只负责切换属性。 ### 3.3 改 `index.html` 的 FOUC 脚本 保留旧版机制(内联脚本读 localStorage 提前定 class),但改为切 `arco-theme` 属性: ```html ``` > 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` | `` + `@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(聊天页迁移)。