96 lines
8.2 KiB
Markdown
96 lines
8.2 KiB
Markdown
# 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 用例卡住。
|