413 lines
13 KiB
Markdown
413 lines
13 KiB
Markdown
# Phase 007 — Arco 迁移:其余 20 视图批量
|
||
|
||
| 项 | 值 |
|
||
|---|---|
|
||
| 目标 | 全仓无 naive-ui;无第二 UI 库 |
|
||
| 前置 | Phase 006 通过 |
|
||
| 产出 | `grep -r "naive-ui" packages/client/src` 零命中 |
|
||
| 参考 | `docs/03-施工蓝图.md` §8.2 |
|
||
|
||
---
|
||
|
||
## 0. 批次划分原则
|
||
|
||
按**耦合度**分批,不按页面重要性。每批独立提交、独立验收,**任何一批都能停下来过夜**。
|
||
|
||
Phase 006 已经把最难的(流式 / 虚拟滚动 / 审批)趟平了,所以下面每批都是体力活。
|
||
|
||
---
|
||
|
||
## 1. 批次一览
|
||
|
||
| 批次 | 范围 | 特征 | 风险 |
|
||
|---|---|---|---|
|
||
| 7a | Settings / Profiles / Models / Logs | 纯表单 + 表格 | 低 |
|
||
| 7b | Skills / Plugins / McpManager / Memory | 卡片列表 + 抽屉 | 低 |
|
||
| 7c | Files / Terminal | 大量 DOM + xterm | **中** |
|
||
| 7d | Usage / Performance / SkillsUsage / Journey | 图表 / 图谱 + 数据表格 | 中 |
|
||
| 7e | Kanban / Jobs / Workflow / Devices | 拖拽 + 复杂交互 | 中高 |
|
||
| 7f | Channels / GroupChat / GlobalAgent | IM 频道 + 群聊 | 中高 |
|
||
| 7g | History / VersionPreview | 历史会话 | 低 |
|
||
|
||
**7f 排在 7e 之后**:群聊也是流式页,虽然比主聊天简单,但仍有 SSE + 增量渲染,靠近 006 的经验更划算。
|
||
|
||
---
|
||
|
||
## 2. 每批的标准动作(**7a 走一遍,之后照抄**)
|
||
|
||
### 2.1 开工前
|
||
|
||
```bash
|
||
cd /Users/jack/source/MyProject/js/hermes-ui
|
||
# 看这批有多少 naive 引用
|
||
grep -rc "naive-ui" <本批文件列表>
|
||
```
|
||
|
||
### 2.2 改法顺序
|
||
|
||
1. **先建/补 `docs/arco-mapping.md`** —— 本批遇到的 Naive 组件若表里没有,先补进去
|
||
2. **Provider 层先换**(`NMessageProvider` → `useMessage()` composable)
|
||
3. **容器 → 布局**(`NLayout` → `a-layout`)
|
||
4. **表单 → 控件**(`NForm` → `a-form`,逐个 `NFormItem`)
|
||
5. **表格 / 列表**(`NDataTable` → `a-table`,差异最大)
|
||
6. **交互层**(`NModal` `NDrawer` `NPopconfirm`)
|
||
7. **删除所有未用的 naive import**(这一步最容易漏,编译器会兜底)
|
||
|
||
### 2.3 收尾
|
||
|
||
```bash
|
||
grep -rn "naive-ui" <本批文件> # 期望零命中
|
||
npm run build
|
||
npm run test
|
||
npm run test:e2e
|
||
git commit -m "feat(ui): migrate <batch> views to Arco Design Vue"
|
||
```
|
||
|
||
### 2.4 每批完成定义(四条全满足)
|
||
|
||
- [ ] 本批文件 `grep -c naive-ui` 归零
|
||
- [ ] `npm run test` 通过
|
||
- [ ] e2e 对应用例通过
|
||
- [ ] 明暗双主题人工过一遍
|
||
|
||
---
|
||
|
||
## 3. 批次内容
|
||
|
||
### 3.1 批次 7a — 纯表单与表格(**先做这批打样**)
|
||
|
||
```
|
||
views/hermes/SettingsView.vue
|
||
views/hermes/ProfilesView.vue
|
||
views/hermes/ModelsView.vue
|
||
views/hermes/LogsView.vue
|
||
components/hermes/settings/** # 约 20 个组件,含 voice/ 子目录
|
||
components/hermes/profiles/** # 6 个
|
||
components/hermes/models/** # 约 12 个,含各种 OAuth 登录弹窗
|
||
components/hermes/settings/SettingRow.vue
|
||
components/layout/ModelSelector.vue
|
||
components/layout/ProfileSelector.vue
|
||
components/layout/LanguageSwitch.vue
|
||
components/layout/ThemeSwitch.vue
|
||
```
|
||
|
||
**本批特有难点**:
|
||
|
||
- `NForm` 的校验规则 API 与 `a-form` 不同,需写适配
|
||
```
|
||
naive: [{ required: true, message: '...' }]
|
||
arco: [{ required: true, message: '...' }] # 规则对象基本一致
|
||
但 field 绑定方式与 error 展示位置不同
|
||
```
|
||
- 各 OAuth 登录弹窗(`AnthropicLoginModal` `CodexLoginModal` `CopilotLoginModal` `GeminiLoginModal` `NousLoginModal` `XaiOAuthLoginModal`)—— 每个都是独立 Modal,改完逐个点
|
||
- `SettingRow.vue` 是所有设置项的骨架容器,先改它
|
||
|
||
**完成后必须过**:
|
||
```bash
|
||
tests/client/default-credential-prompt.test.ts
|
||
tests/client/copilot-login-modal.test.ts
|
||
tests/client/platform-card.test.ts
|
||
tests/client/profile-card-config-edit.test.ts
|
||
tests/client/provider-card-built-in-action.test.ts
|
||
tests/client/provider-base-url.test.ts
|
||
tests/client/display-settings.test.ts
|
||
tests/client/gateway-auto-start-settings.test.ts
|
||
tests/client/compression-settings 相关
|
||
```
|
||
|
||
---
|
||
|
||
### 3.2 批次 7b — 卡片列表与抽屉
|
||
|
||
```
|
||
views/hermes/SkillsView.vue
|
||
views/hermes/PluginsView.vue
|
||
views/hermes/McpManagerView.vue
|
||
views/hermes/MemoryView.vue
|
||
components/hermes/skills/** # 7 个
|
||
components/hermes/mcp/McpServerCard.vue
|
||
components/hermes/common/RouteLinkItem.vue
|
||
components/hermes/settings/MemorySettings.vue
|
||
components/hermes/settings/CompressionSettings.vue
|
||
```
|
||
|
||
**本批特有难点**:
|
||
- `SkillList` / `SkillDetail` / `SkillImportModal` / `SkillExternalDirsModal` 有复杂表单与文件树
|
||
- `PendingWriteApprovals.vue` 有审批交互(与 Phase 006 的审批同源)
|
||
|
||
**完成后必须过**:
|
||
```bash
|
||
tests/client/skills-view.test.ts
|
||
tests/client/skill-list.test.ts
|
||
tests/client/skill-detail-edit.test.ts
|
||
tests/client/plugins-view.test.ts
|
||
tests/client/pending-write-approvals.test.ts
|
||
tests/client/route-link-item.test.ts
|
||
```
|
||
|
||
---
|
||
|
||
### 3.3 批次 7c — 文件与终端(**别把 Arco 塞进来**)
|
||
|
||
```
|
||
views/hermes/FilesView.vue
|
||
views/hermes/TerminalView.vue
|
||
components/hermes/files/** # 9 个
|
||
components/hermes/chat/TerminalPanel.vue # 若 7a/7b 未改
|
||
composables/useDefaultWorkspace.ts
|
||
utils/file-path.ts
|
||
utils/hermes/kanban-assignees.ts
|
||
```
|
||
|
||
**本批特有难点(**本批最特殊**):
|
||
- **文件树** `FileTree.vue` / `FileList.vue` / `FileBreadcrumb.vue` 是**手写 DOM 结构 + CSS**,不要换成 Arco 的 `a-tree`——会丢掉自定义的文件类型图标、右键菜单、拖拽。
|
||
- **xterm.js** 终端是独立 DOM 树,**外面套任何 Arco 布局都可能引入尺寸问题**。终端容器用固定 flex + 原生 CSS。
|
||
- `FileContextMenu.vue` / `FilePreview.vue` / `FileEditor.vue` / `FileToolbar.vue` 涉及上传、预览、编辑
|
||
- 右键菜单:`a-trigger` 的 `trigger="contextmenu"`
|
||
|
||
**完成后必须过**:
|
||
```bash
|
||
tests/e2e/terminal.spec.ts
|
||
tests/client/file-tree-profile-scope.test.ts
|
||
tests/client/file-list-layout.test.ts
|
||
tests/client/file-context-menu.test.ts
|
||
tests/client/folder-picker-windows-drives.test.ts
|
||
tests/client/files-view-profile-config-route.test.ts
|
||
tests/client/file-path.test.ts
|
||
```
|
||
|
||
---
|
||
|
||
### 3.4 批次 7d — 图表与数据密度
|
||
|
||
```
|
||
views/hermes/UsageView.vue
|
||
views/hermes/PerformanceView.vue
|
||
views/hermes/SkillsUsageView.vue
|
||
views/hermes/JourneyView.vue
|
||
components/hermes/usage/** # StatCards DailyTrend ModelBreakdown AgentBreakdown
|
||
api/hermes/journey.ts
|
||
stores/hermes/usage.ts
|
||
```
|
||
|
||
**本批特有难点**:
|
||
- **这是 ArkScope 装饰层的第二个合法落点**:Journey 图谱的背景层可以用装饰位面。但**数据线、节点、文字仍必须原生渲染**,装饰只能垫在最底。
|
||
- 图表组件(若有 echarts / 自绘 SVG)**不要动**,只换外围容器
|
||
- `NDataTable` → `a-table` 在这里是重灾区(数据密度高、列多、有合计行)
|
||
- 表格横向滚动 + 固定列
|
||
|
||
**完成后必须过**:
|
||
```bash
|
||
tests/client/usage-components-cache-visuals.test.ts
|
||
tests/client/usage-view-period.test.ts
|
||
tests/client/usage-store.test.ts
|
||
tests/client/skills-usage-view.test.ts
|
||
```
|
||
|
||
---
|
||
|
||
### 3.5 批次 7e — 拖拽与复杂交互
|
||
|
||
```
|
||
views/hermes/KanbanView.vue
|
||
views/hermes/JobsView.vue
|
||
views/hermes/WorkflowView.vue
|
||
views/hermes/DevicesView.vue
|
||
components/hermes/kanban/** # 4 个
|
||
components/hermes/jobs/** # 4 个
|
||
components/hermes/workflow/** # 3 个
|
||
stores/hermes/kanban.ts
|
||
stores/hermes/jobs.ts
|
||
utils/hermes/workflow-skills.ts
|
||
```
|
||
|
||
**本批特有难点**:
|
||
- **拖拽**(Kanban 列间拖卡片)是**手写 drag 事件**,换成 Arco 布局后要重新验证 drop zone 的坐标计算。**这是本批最大风险。**
|
||
- `WorkflowAgentNode.vue` 画的是节点图,坐标计算与容器尺寸耦合
|
||
- `JobRunHistory` 有时间线
|
||
|
||
**完成后必须过**:
|
||
```bash
|
||
tests/client/kanban-view.test.ts
|
||
tests/client/kanban-task-card.test.ts
|
||
tests/client/kanban-task-drawer.test.ts
|
||
tests/client/kanban-create-form.test.ts
|
||
tests/client/kanban-store.test.ts
|
||
tests/client/kanban-api.test.ts
|
||
tests/client/jobs.test.ts
|
||
tests/client/job-form-modal.test.ts
|
||
tests/client/workflow-skills.test.ts
|
||
tests/client/workflow-socket.test.ts
|
||
```
|
||
|
||
---
|
||
|
||
### 3.6 批次 7f — IM 频道与群聊
|
||
|
||
```
|
||
views/hermes/ChannelsView.vue
|
||
views/hermes/GroupChatView.vue
|
||
views/hermes/GlobalAgentView.vue
|
||
components/hermes/group-chat/** # GroupChatPanel GroupMessageList GroupMessageItem GroupChatInput CreateRoomForm
|
||
stores/hermes/group-chat.ts
|
||
api/hermes/group-chat.ts
|
||
api/hermes/global-agent.ts
|
||
```
|
||
|
||
**本批特有难点**:
|
||
- `GroupMessageList` / `GroupMessageItem` 也是**流式增量渲染**,Phase 006 的规则同样适用:气泡内禁用 Arco 组件
|
||
- 多 agent 路由的可视化(谁在响应)
|
||
- 会话设置抽屉
|
||
|
||
**完成后必须过**:
|
||
```bash
|
||
tests/client/group-chat-store-baseline.test.ts
|
||
tests/client/group-chat-store-streaming.test.ts
|
||
tests/client/group-chat-store-workspace.test.ts
|
||
tests/client/group-chat-view-settings.test.ts
|
||
tests/client/group-chat-workspace-diff.test.ts
|
||
tests/client/group-chat-input-mentions.test.ts
|
||
tests/client/group-chat-mention-options.test.ts
|
||
tests/client/group-chat-panel-workspace-source.test.ts
|
||
tests/client/group-message-list-scroll.test.ts
|
||
tests/client/group-message-item-highlight.test.ts
|
||
tests/e2e/group-chat-room-deeplink.spec.ts
|
||
```
|
||
|
||
---
|
||
|
||
### 3.7 批次 7g — 历史与版本预览(**收尾**)
|
||
|
||
```
|
||
views/hermes/HistoryView.vue
|
||
views/hermes/VersionPreviewView.vue
|
||
stores/hermes/app.ts
|
||
data/changelog.ts # Phase 002 已清空
|
||
```
|
||
|
||
**本批特有难点**:
|
||
- `VersionPreviewView` 若 `changelog` 已清空,要么给空态,要么隐藏入口
|
||
|
||
**完成后必须过**:
|
||
```bash
|
||
tests/e2e/history-session-deeplink.spec.ts
|
||
tests/client/chat-view-tab-title.test.ts
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 收尾(全批次完成后)
|
||
|
||
### 4.1 删依赖
|
||
|
||
`package.json` 删:
|
||
|
||
```jsonc
|
||
"naive-ui": "...",
|
||
"@css-render/vue3-ssr": "...", // naive 的 peer,若直接依赖
|
||
"vdirs": "..." // 若直接依赖
|
||
```
|
||
|
||
先用 `grep -rn "vdirs\|@css-render" packages/client/src` 确认是否真用到。
|
||
|
||
### 4.2 改构建配置
|
||
|
||
`vite.config.ts`:
|
||
|
||
```ts
|
||
// manualChunks
|
||
if (id.includes('naive-ui') || id.includes('vueuc')) return 'ui-vendor'
|
||
// 改为
|
||
if (id.includes('@arco-design')) return 'ui-vendor'
|
||
|
||
// optimizeDeps.include
|
||
// 删掉 'naive-ui', 'vueuc', '@css-render/vue3-ssr'
|
||
// 加上 '@arco-design/web-vue'
|
||
```
|
||
|
||
### 4.3 恢复 `prepare` script
|
||
|
||
Phase 001 摘掉的 `"prepare": "[ -d dist ] || npm run build"` 恢复。
|
||
|
||
### 4.4 重写 `style-system.test.ts`
|
||
|
||
旧版断言的是 Naive 的 theme key。改为断言 Arco token:
|
||
|
||
```ts
|
||
// packages/client/src/styles/arco-theme.ts 导出的令牌完整性
|
||
// colorPrimary / colorBgLayout / colorBgContainer / borderRadius / fontFamily / fontFamilyMono 存在
|
||
// 浅色与深色两套值都定义了
|
||
```
|
||
|
||
### 4.5 新增 `no-naive-ui.test.ts`(**防回流,最强保障**)
|
||
|
||
```ts
|
||
// 递归扫 packages/client/src
|
||
// 任何文件出现 "from 'naive-ui'" 或 "from \"naive-ui\"" 即 fail
|
||
// 同时断言 package.json 的 dependencies 里无 naive-ui
|
||
```
|
||
|
||
### 4.6 更新 `AGENTS.md` 前端规则
|
||
|
||
```
|
||
- 使用 Arco Design Vue。新增控件优先从 @arco-design/web-vue/es/icon 取图标。
|
||
- 禁止引入第二个 UI 库。
|
||
- 消息气泡内部不用 Arco 组件(流式性能)。
|
||
- 装饰层一律 pointer-events: none。
|
||
```
|
||
|
||
### 4.7 更新 `DEVELOPMENT.md` 的依赖说明
|
||
|
||
删掉任何 naive 相关表述。
|
||
|
||
---
|
||
|
||
## 5. 验收
|
||
|
||
```bash
|
||
cd /Users/jack/source/MyProject/js/hermes-ui
|
||
|
||
# 全仓零命中
|
||
grep -rn "naive-ui\|vueuc\|@css-render" packages/client/src/
|
||
grep -n "naive-ui" package.json package-lock.json
|
||
# 期望:零命中
|
||
|
||
# 只 Arco 一个 UI 库
|
||
grep -nE "\"(naive-ui|element-plus|ant-design-vue|primevue|@arco-design/web-vue)\"" package.json
|
||
|
||
npm ci --ignore-scripts
|
||
npm run build
|
||
npm run test:coverage
|
||
npm run test:e2e
|
||
```
|
||
|
||
### 手工
|
||
|
||
- [ ] 全部 24 个视图(20 个 + 登录 + 缺依赖引导)逐个点过
|
||
- [ ] 明暗双主题各过一遍
|
||
- [ ] Kanban 拖拽正常
|
||
- [ ] 文件树正常
|
||
- [ ] 终端正常
|
||
- [ ] 图表正常
|
||
- [ ] 窄屏 / 移动端布局正常
|
||
|
||
---
|
||
|
||
## 6. 风险
|
||
|
||
| # | 风险 | 缓解 |
|
||
|---|---|---|
|
||
| P7-1 | 工作量超预期,7a-7g 拖太久 | 每批独立可停;先做 7a 定模式,再批量 |
|
||
| P7-2 | 批量改中顺手重构业务逻辑,污染 diff | 硬规则:**只换渲染层**。发现逻辑 bug 单独记 issue,不在本次改 |
|
||
| P7-3 | Kanban 拖拽坐标在 Arco 布局下算错 | 7e 单独安排时间手测,不与其他批混做 |
|
||
| P7-4 | 忘记某文件的 naive import | `no-naive-ui.test.ts` 卡住(§4.5) |
|
||
| P7-5 | `a-table` 替换后大数据量卡顿 | 7d 加虚拟滚动(`a-table-virtual`),先测 1000 行 |
|
||
| P7-6 | 依赖删了但 lock 没同步,`npm ci` 失败 | 删依赖后 `rm package-lock.json && npm install --package-lock-only` |
|
||
| P7-7 | 7f 群聊流式性能退化 | 复用 Phase 006 的气泡禁 Arco 规则 |
|
||
|
||
---
|
||
|
||
## 7. 完成标志
|
||
|
||
项目功能完整 + 界面全量 Arco + 无第二 UI 库。进入 Phase 008。
|