Files
hermes-desktop/docs/arco-mapping.md
T
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

8.2 KiB
Raw Blame History

Naive UI → Arco Design Vue 组件映射表

Phase 005 起,新代码与迁移代码一律使用 Arco Design Vue。本文表是迁移的唯一映射依据: 先查表,再改代码。表中没有的组件,先在此表补一行再动手。

  • 全局注册:main.ts 全量 app.use(ArcoVue),不做按需引入
  • 主题:body[arco-theme='dark'](见 styles/arco-theme.ts 与 docs/ui-baseline.md)
  • hooks:useMessage / useDialog / useNotification 适配层在 packages/client/src/composables/, 签名冻结,不得在页面里直接 import Arco 的 Message / Modal / Notification

Provider / hooks

Naive Arco 备注
NConfigProvider 直接用 body[arco-theme] 不需要组件
NMessageProvider / useMessage composables/useMessage.ts 适配层,签名冻结
NDialogProvider / useDialog composables/useDialog.ts positiveText→okText、onPositiveClick→onBeforeOk(resolve false / reject → 保持打开)由适配层转换
NNotificationProvider / useNotification composables/useNotification.ts description→content 由适配层转换

基础组件(按当前仓库用量排序)

Naive Arco 用量 备注
NButton a-button 732 block→long、size="tiny"→size="mini"、type="warning"→type="primary" status="warning"、quaternary/secondary→type="secondary"(Arco status 只有 normal|success|warning|danger,没有 secondary)
NInput a-input / a-textarea 160 type="textarea" 用独立 a-textarea;a-input 关闭了 attrs 透传,autofocus 无效,需 ref + focus()
NModal a-modal 138 v-model:show→v-model:visible;preset="dialog" 无对应,信息型弹窗用 :footer="false"(footer 为 Boolean prop,默认 true)
NFormItem a-form-item 136 校验规则 API 不同(rules 结构、required 表达式)需适配
NSelect a-select 97 options 的 label/value 语义一致;consistent-menu-width 等 prop 无对应
NSpin a-spin 86 show→loading
NTooltip a-tooltip 76 trigger="hover" 默认一致;placement→position;触发元素走默认插槽,naive 的 <template #trigger> 必须拆掉
NPopconfirm / NTooltip 的 #trigger 槽 Arco 默认插槽 — Arco 的 a-tooltip / a-popconfirm / a-dropdown 都用默认插槽承载触发元素,naive 的 <template #trigger> 写法会静默丢内容
Arco Trigger 嵌套 不可嵌套,无论谁包谁 — a-tooltip / a-dropdown / a-popconfirm 内部都用 mergeFirstChild 找首个元素节点并各自接管事件;嵌套时两个 popup 容器互相踩,菜单点不开(.arco-trigger-popup-wrapper 卡在过渡中间态、选项数为 0)。同一触发元素要同时有说明文案和下拉时,说明文案改用原生 :title,不要套 a-tooltip
NTag a-tag 57 bordered prop 存在(默认 true);size="small" 一致;naive type="info"→color="arcoblue" 等内建色(red/orange/green/arcoblue/...),自定义色用 color 传 CSS 颜色
NSwitch a-switch 53 value/v-model 一致
NSpace a-space 51 size 语义一致(数字/预设名)
NInputNumber a-input-number 43 min/max/precision 一致
NPopconfirm a-popconfirm 36 positive-text→ok-text;@positive-click→@ok
NForm a-form 32 校验规则 API 不同,需适配
NTabPane a-tab-pane 30 NTabs/NTabPane → a-tabs/a-tab-pane
NDrawer / NDrawerContent a-drawer 27 Arco 无独立 content 组件;footer slot 用 a-drawer 的 footer
NDropdown a-dropdown 25 Arco 没有 droplist prop:菜单内容必须写进 #content 插槽、用 a-doption / a-dsubmenu 声明。传 :droplist 会被静默忽略,弹层能打开但 <ul class="arco-dropdown-list"> 是空的;子菜单用 a-dsubmenu + 自己的 #content 插槽
NDescriptions / NDescriptionsItem a-descriptions / a-descriptions-item 19 基本一致
NAlert a-alert 19 type 一致;show-icon 一致
NRadioButton / NRadioGroup / NRadio a-radio(type="radio-button")/ a-radio-group 32 Arco radio-button 是 a-radio 的 type prop
NCheckbox / NCheckboxGroup a-checkbox / a-checkbox-group 17 options 数组语义一致
NEmpty a-empty 12 基本一致
NCollapse / NCollapseItem a-collapse / a-collapse-item 24 arrow 语义不同
NUpload a-upload 9 file-list 结构不同,需适配
NText <a-typography> / 原生 span 8 多数场景直接用原生元素 + class
NTabs a-tabs 6 见 NTabPane 行
NBreadcrumb / NBreadcrumbItem a-breadcrumb / a-breadcrumb-item 8 基本一致
NDataTable a-table 4 列定义 API 差异最大(columns/render/slots),逐例处理
NSlider a-slider 3 基本一致
NScrollbar a-scrollbar 3 Arco 全局可用,无需组件包裹注册
NPopselect a-dropdown 3 无直接对应。实测 a-dropdown + :droplist(key/content)可完整替代
NPopover a-popover 3 trigger 语义有差异
NIcon <icon-*>(@arco-design/web-vue/es/icon) 3 Arco 图标是独立组件,需 import
NBadge a-badge 3 value→count
NTree a-tree 2 结构差异大,逐例处理
NCard a-card — 当前仓库未直接使用;若出现按此映射
NProgress a-progress — 当前仓库未直接使用;若出现按此映射
NLayout / NLayoutSider a-layout / a-layout-sider — 当前仓库未使用(侧栏是手写 div),保持手写或按需引入

迁移批次(Phase 006/007 使用)

  • Phase 006:聊天页(components/hermes/chat/)——消息气泡内部不用 Arco 组件(性能红线)
  • Phase 007:其余视图 + stores/ + composables/ 批量替换 + 删除 naive-ui 依赖

例外

  • 聊天虚拟滚动区不用 a-scrollbar 包裹。VirtualMessageList.vue 直接用 vue-virtual-scroller 的 DynamicScroller 独占滚动(原本也没有 NScrollbar), 额外包一层滚动容器会破坏高度测量与 sticky 定位。
  • 测试里 Arco 组件的 stub 必须用 Arco 内部组件名(Dropdown / Modal / Spin), 而不是模板里的 a-* 标签名:app.use(ArcoVue) 之后组件已全局注册,VTU 的 stubs 只按内部名匹配,用 ADropdown 会静默不生效。
  • ChatInput.vue 的推理强度按钮、输入设置按钮放弃 a-tooltip,改用原生 :title。 这两个按钮必须挂 a-dropdown,而 Trigger 不能嵌套(见上表同名行),因此失去 Arco 的悬浮气泡;文案与 aria-label 都保留,信息量不变。
  • Arco(和 Vue 本身)不会对拼错的 prop 报错,未声明的 prop 会作为普通 attribute 落到根元素上。所以「弹层能打开、但内容是空的」几乎总是 prop 写错而不是数据为空。 排查手法:el.__vueParentComponent.props 看目标组件到底认不认这个 prop, 再看 el.outerHTML 确认渲染出来的骨架。回归由 tests/e2e/chat-input-dropdown.spec.ts 卡住。
  • 迁移时最容易漏的是 CSS 里的 naive 类名,因为 grep naive-ui 匹配不到 :deep(.n-button__content) 这种写法。聊天页原本有 35 处这类死样式,在 Arco 下 全部不生效,必须逐条改写成 Arco 类名。注意三处结构差异:
    • a-button 没有 content 包裹层(naive 有 .n-button__content), gap / min-width 要直接写在 button 上
    • a-modal 没有 body-wrapper 分层,用 .arco-modal / .arco-modal-body
    • a-drawer 同理,没有 content / body-content-wrapper 分层
  • useKeyboard.ts 里查 .n-modal-mask 的 Esc 兜底属于跨组件的全局约定,迁移 组件时最容易漏掉。改弹窗库时必须同步 grep .n-modal|.n-drawer|.n-base- 这类 字符串字面量里的类名,而不只是组件 import。回归由 tests/client/session-search.test.ts 的 Esc 用例卡住。