Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CI / CI OK (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Deploy Website on push / Rerun on failure (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
236 lines
9.8 KiB
Plaintext
236 lines
9.8 KiB
Plaintext
---
|
||
description: nl-admin-view Vben 5.7 官方组件用法规范(Page/Form/Modal/Drawer/VxeTable 等)
|
||
globs: apps/web-antd/**/*.{vue,ts}
|
||
alwaysApply: false
|
||
---
|
||
|
||
# Vben 组件规则(对齐 5.7.0 官方文档 + 本仓库约定)
|
||
|
||
当前版本:**Vben Admin 5.7.0**。文档来源:https://doc.vben.pro/components/
|
||
业务页统一在 `apps/web-antd`;表单/表格必须走本仓库适配器,不要直接依赖底层实现细节。
|
||
`lock` / `unlock`、Promise 版 `onBeforeClose`、`animationType`、`center-footer` 等在 5.7 已是基线能力,规则里不再标注「>5.5.x」版本门槛。
|
||
|
||
## 选型速查
|
||
|
||
| 场景 | 用什么 | 不要用 |
|
||
|---|---|---|
|
||
| 页面外壳 | `Page`(`@vben/common-ui`) | 自写页面头/底容器 |
|
||
| 列表+搜索 | `useVbenVxeGrid`(`#/adapter/vxe-table`) | 直接 `new VxeGrid` / antd `Table` |
|
||
| 表单 | `useVbenForm`(`#/adapter/form`) | antd `Form` / 手写校验 |
|
||
| 复杂弹窗(表单/多步) | `useVbenModal` | antd `Modal` |
|
||
| 侧滑面板 | `useVbenDrawer` | antd `Drawer` |
|
||
| 轻量确认/提示/单输入 | `alert` / `confirm` / `prompt` | 为简单确认再开 Modal |
|
||
| 表格行/工具栏按钮 | `#/components/table-action` 的 `TableAction` | 散落一排 `Button` |
|
||
| 远程下拉/树选 | 适配器里的 `ApiSelect` / `ApiTreeSelect`(底层 `ApiComponent`) | 页面里手写 options 拉取逻辑 |
|
||
| 超长文本 | `EllipsisText` | CSS 省略后无 Tooltip |
|
||
| 只读详情字段组 | `VbenDescriptions` | 手写 label-value 网格 |
|
||
| 头像/固定比例裁剪 | `VCropper` | 无裁剪直接上传原图(需要裁剪时) |
|
||
| 富文本 | `VbenTiptap`(`@vben/plugins/tiptap`) | 再引一套编辑器 |
|
||
|
||
---
|
||
|
||
## Page
|
||
|
||
- 业务页根节点用 `<Page>`;列表页常见:`<Page auto-content-height title="xxx">`
|
||
- `title` / `description` / `#extra` 都没有内容时,头部不渲染
|
||
- `autoContentHeight`:内容区按可视高度自动扣减头/底;额外偏移用 `heightOffset`(px)
|
||
- `appendToMain` 的 Modal/Drawer 挂到内容区时,**Page 必须开 `auto-content-height`**,否则高度计算不对
|
||
- 插槽:`default` / `title` / `description` / `extra` / `footer`
|
||
|
||
---
|
||
|
||
## Modal(`useVbenModal`)
|
||
|
||
```ts
|
||
const [Modal, modalApi] = useVbenModal({
|
||
connectedComponent: ExtraModal, // 复杂内容抽离子组件
|
||
draggable: true,
|
||
// overflow: true, // 需要拖出可视区时再开
|
||
});
|
||
```
|
||
|
||
强制约定:
|
||
|
||
- 复杂弹窗用 `connectedComponent`;内外共享 `modalApi`
|
||
- **严禁覆盖 `#footer`**(会丢掉默认取消/确定与 loading)
|
||
- 扩展底部只用:`prepend-footer` / `center-footer` / `append-footer`
|
||
- 提交防重复:优先 `modalApi.lock()` / `unlock()`(`unlock` = `lock(false)`;锁定时确认按钮 loading、禁关弹窗);或 `setState({ confirmLoading: true })`
|
||
- 回填:`modalApi.setData(data).open()`;内部 `onOpenChange` 里 `modalApi.getData()`
|
||
- 参数优先级:`slot` > `props` > `state`;`connectedComponent` 时同名事件**以内侧为准**(`onOpenChange` 内外都会触发)
|
||
- 默认属性可在 `apps/web-antd/src/bootstrap.ts` 的 `setDefaultModalProps` 统一改
|
||
- 动画:`animationType: 'slide' | 'scale'`(默认 slide)
|
||
- `destroyOnClose`:关闭后销毁内部 Modal 及子组件(配合 `connectedComponent` 时同样生效)
|
||
|
||
常用 API:`open` / `close` / `setState` / `setData` / `getData` / `lock` / `unlock` / `useStore`
|
||
|
||
---
|
||
|
||
## Drawer(`useVbenDrawer`)
|
||
|
||
- API 形态与 Modal 基本一致:`connectedComponent`、`setData/getData`、`lock/unlock`、footer 三插槽
|
||
- **同样严禁覆盖 `#footer`**
|
||
- `placement`: `left | right | top | bottom`(默认 `right`)
|
||
- 关闭前校验用 `onBeforeClose`(支持 Promise / 返回 `false`);需要重置内部状态可开 `destroyOnClose`
|
||
- 挂内容区同样要配合 Page 的 `auto-content-height`
|
||
- 默认属性:`setDefaultDrawerProps`
|
||
|
||
---
|
||
|
||
## Alert(轻量提示)
|
||
|
||
来自 `@vben/common-ui`:`alert` / `confirm` / `prompt` / `useAlertContext`
|
||
|
||
- **简单确认、提示、单字段输入**用这套;复杂表单/多控件仍用 Modal
|
||
- `confirm().then/catch`;异步关闭用 `beforeClose`,返回 `false` 可阻止关闭
|
||
- `prompt` 可传 `component` + `componentProps`(antd Select 需 `popupClassName: 'pointer-events-auto'`)
|
||
- 自定义内容里需要主动确认/取消:`useAlertContext().doConfirm/doCancel`(仅 setup/函数组件)
|
||
- 动态创建的 Alert **不支持 HMR**,改代码后需关掉重开
|
||
|
||
---
|
||
|
||
## Form(`useVbenForm` from `#/adapter/form`)
|
||
|
||
```ts
|
||
const [Form, formApi] = useVbenForm({
|
||
schema: [
|
||
{
|
||
fieldName: 'nick_name', // 与后端 snake_case 对齐
|
||
label: '昵称',
|
||
component: 'Input',
|
||
rules: 'required', // 选择类用 selectRequired
|
||
componentProps: { placeholder: '请输入' },
|
||
},
|
||
],
|
||
});
|
||
```
|
||
|
||
强制约定:
|
||
|
||
- 只从 `#/adapter/form` 引入,不直接玩 TanStack Form 实例
|
||
- 动态改字段:`formApi.updateSchema([...])`;**没有** `setComponentProps`
|
||
- 重置:`formApi.resetForm()`;**禁止** `resetFields()`(antd API,会报错)
|
||
- 取值/赋值:`getValues()` / `setValues()`;需要提交编解码时用 `codec` / `getRawValues()`
|
||
- 校验:`formApi.validate().then(e => { if (e.valid) ... })` 或 `validateAndSubmitForm()`
|
||
- 字段自定义 slot:控件绑定用 `v-bind="slotProps.componentProps"`(不要再 `v-bind="slotProps"`)
|
||
- 联动优先 `dependencies.resolve(context)` 一次返回 `show/disabled/rules/componentProps...`
|
||
- 远程 options:打开时拉取后 `updateSchema` 注入;或用 `ApiSelect`/`ApiTreeSelect`
|
||
- 密码字段用 `InputPassword`(适配器组件名),不要 `Input` + `type="password"`
|
||
|
||
---
|
||
|
||
## Vxe Table(`useVbenVxeGrid` from `#/adapter/vxe-table`)
|
||
|
||
```ts
|
||
const [Grid, gridApi] = useVbenVxeGrid({
|
||
formOptions, // 搜索表单(Vben Form)
|
||
gridOptions: {
|
||
proxyConfig: {
|
||
ajax: {
|
||
query: async ({ page }, formValues) => {
|
||
// 返回适配器约定结构
|
||
return { items: [], total: 0 };
|
||
},
|
||
},
|
||
},
|
||
toolbarConfig: { search: true },
|
||
},
|
||
gridEvents,
|
||
});
|
||
```
|
||
|
||
强制约定:
|
||
|
||
- 远程列表走 `proxyConfig.ajax.query`,禁止自己 fetch 再赋 `data`
|
||
- 响应结构对齐适配器:`{ items, total }`(见 `apps/web-antd/src/adapter/vxe-table.ts`)
|
||
- 分页从 `{ page }` 取 `currentPage` / `pageSize`
|
||
- 刷新:`gridApi.query()` 保留页;`gridApi.reload()` 回第一页
|
||
- loading:`gridApi.setLoading(bool)`
|
||
- 工具栏插槽:`toolbar-actions`(左)/ `toolbar-tools`(右)
|
||
- 操作列:列上 `slots: { default: 'action' }`,页面 `<template #action="{ row }">`,**禁止**列 render 写 JSX
|
||
- 需要原生能力时用 `gridApi.grid`(如树展开 `setAllTreeExpand`)
|
||
|
||
---
|
||
|
||
## ApiComponent / ApiSelect
|
||
|
||
- 一般不直接用 `ApiComponent`;优先用适配器包装好的 `ApiSelect` / `ApiTreeSelect`
|
||
- 关键映射:`api` / `params` / `resultField` / `labelField` / `valueField` / `childrenField`
|
||
- `immediate: false` + `visibleEvent`:展开时再请求
|
||
- 同页多实例同数据源:用 TanStack Query/`useQuery` 包一层再传 `api`,避免 N 次请求
|
||
- `autoSelect`: `'first' | 'last' | 'one' | fn | false`(不要用在多选)
|
||
|
||
---
|
||
|
||
## TableAction(本仓库)
|
||
|
||
本仓库列表页已统一用本地封装:
|
||
|
||
```ts
|
||
import { TableAction } from '#/components/table-action';
|
||
```
|
||
|
||
- 字段习惯:`label`(不是官方 demo 的 `text`)、`drop-down-actions`、`popConfirm`、`auth`、`ifShow`
|
||
- 放在 `#toolbar-actions` 与 `#action` 插槽中
|
||
- 若改用官方 `VbenTableAction`(`@vben/common-ui` / `#/adapter/vxe-table`),注意其字段为 `text` / `dropdownActions`,并优先走适配器以自动注入权限
|
||
|
||
---
|
||
|
||
## EllipsisText
|
||
|
||
```vue
|
||
<EllipsisText :max-width="240" :line="2" :tooltip-when-ellipsis="true">
|
||
{{ text }}
|
||
</EllipsisText>
|
||
```
|
||
|
||
- 表格/卡片超长文案优先用它;需要展开收起开 `expand`
|
||
- `tooltipWhenEllipsis`:仅截断时显示提示,避免短文本也弹层
|
||
|
||
---
|
||
|
||
## VbenDescriptions
|
||
|
||
- 详情/预览优先 `:items="[{ label, content, span }]"` 数据驱动
|
||
- `span: 'filled'` 占满当前行剩余;`column` 可传断点对象
|
||
- `bordered` + `title` + `#extra` 做带操作的信息块
|
||
- `layout="vertical"` 标签在上;也可用 `VbenDescriptionsItem` 子组件(`items` 优先)
|
||
|
||
---
|
||
|
||
## VCropper
|
||
|
||
```ts
|
||
const blob = await cropperRef.value?.getCropImage('image/jpeg', 0.9, 'blob');
|
||
```
|
||
|
||
- Props:`img`(必填)、`width`/`height`、`aspectRatio`(如 `'1:1'` `'16:9'`)
|
||
- blob URL 用完/`onBeforeUnmount` 要 `URL.revokeObjectURL`,防泄漏
|
||
- 网络图导出需 CORS;头像场景常用固定比例 + 指定 `targetWidth/targetHeight`
|
||
|
||
---
|
||
|
||
## VbenTiptap(`@vben/plugins/tiptap`)
|
||
|
||
```vue
|
||
<VbenTiptap v-model="html" :image-upload="imageUpload" />
|
||
<VbenTiptapPreview :content="html" />
|
||
```
|
||
|
||
- 内容为 HTML 字符串;只读展示用 `VbenTiptapPreview`
|
||
- 上传必须配 `imageUpload.upload(file, onProgress) => Promise<url>`,走本项目上传接口
|
||
- 上传中不要拿 `getHTML()` 去保存(可能是临时 blob URL)
|
||
- 自定义 `extensions` 会覆盖默认扩展,且图片上传能力不可用——非必要不要自定义
|
||
|
||
---
|
||
|
||
## 与本仓库踩坑清单(必守)
|
||
|
||
1. Form 无 `setComponentProps` / 无 `resetFields`
|
||
2. Modal/Drawer 禁止覆盖 `#footer`;提交用 `lock/unlock`(5.7 基线,无需再判断版本)
|
||
3. 表格返回必须是 `{ items, total }`;刷新分清 `query` vs `reload`
|
||
4. 字段名 `fieldName` 用后端 snake_case(如 `created_at`、`nick_name`)
|
||
5. 状态枚举与后端一致:**0 正常 / 1 禁用**
|
||
6. 暗色主题:业务样式用 CSS 变量,禁止写死亮色色值(见 `Code-Standards.mdc`)
|
||
7. Tailwind 已是 v4:业务样式优先主题 CSS 变量 / 现有 utility;不要新建 v3 的 `tailwind.config.*`
|
||
8. 业务 CRUD 只改 `apps/web-antd`;不要往 `web-ele` / `web-naive` / `web-antdv-next` 等演示端塞业务代码
|