Files
lgp-admin-plus/.cursor/rules/Code-Standards.mdc

195 lines
12 KiB
Plaintext
Raw Normal View History

2026-08-10 12:38:09 +08:00
---
2026-08-10 14:42:27 +08:00
description: nl-admin 管理后台(Vben Admin 5.7 + Vue 3.5 + TS + Ant Design Vue 4)代码规范
2026-08-10 12:38:09 +08:00
alwaysApply: true
---
2026-08-10 14:42:27 +08:00
# 技术栈基线(升级后必守)
- **Vben Admin 5.7.0**(monorepo 根 `package.json` / `@vben/web-antd` 版本)
- **Vue 3.5** + **TypeScript** + **Ant Design Vue 4.x**;业务只改 `apps/web-antd`
- **Tailwind CSS v4**(CSS-first,主题在 `@vben/tailwind-config`);不要再引入 `tailwind.config.*` / PostCSS Tailwind 插件那套 v3 写法
- 格式化 / Lint 以仓库现有工具链为准:`oxfmt` + `oxlint`(配合 eslint / stylelint),**不要**再单独引入 Prettier 作为主格式化器
- 组件细则见同目录 `Vben-Components.mdc`(与 https://doc.vben.pro/components/ 对齐)
2026-08-10 12:38:09 +08:00
# 基础规范(所有项目通用)
1. 写代码的时候要补充详细的中文注释(每个方法是干嘛的,为什么要这样写),如果是工作区,则所有项目都适用该规则
2. 注意不要生成太多的空行,上一部分代码和下一部分代码中间的空行不要大于 2 行
3. 有封装好的方法、组件需要复用,不要重复造轮子
4. 小程序端的抽屉全部需要用 page-container 来防止用户意外退出页面(记得使用 v-if 而不是 v-show;本仓库为 PC 管理端,此项约束 uniapp)
5. 数据库的(created_at、updated_at、deleted_at)统一使用时间戳,不要使用字符串,并且我在查询器中一级格式化成字符串了,无需再次格式化
# 全局架构规范
## 目录结构规范
- 业务页面统一放在 `apps/web-antd/src/views/<端>/<模块>/`
- 当前已有端/模块:`system`(admin / role / menu / database)、`dashboard`、`code-generation`、`_core`
- 新增业务优先挂在已有端下;确需新端时再建目录,**不要**照搬其他项目的 `business` / `doctor` 等目录名
- 标准 CRUD 模块目录结构(以 `system/admin`、`system/role`、`system/menu` 为准):
```
views/<端>/<模块>/
├── index.vue # 列表页(Page + Grid + Modal)
├── api/index.ts # 本模块 API(CRUD)
├── config/
│ ├── table.ts # vxe-grid 列定义 + proxyConfig
│ ├── search.ts # 顶部搜索表单 schema
│ └── form.ts # 新增/编辑弹窗 form schema
├── components/
│ └── modal.vue # 新增/编辑弹窗
└── utils/ # 模块工具(可选)
```
- 非标准 CRUD(如 `system/database`、`code-generation`)可按功能拆组件,但仍须把请求收敛到模块 `api/`
- 全局复用组件放 `apps/web-antd/src/components/`
- 表单内可用组件放 `apps/web-antd/src/components/form/components/`,文件名 kebab-case,自动注册为 schema 的 `component` 名(PascalCase 引用)
## 强制复用封装(禁止重复造轮子)
- 表格统一用 `useVbenVxeGrid`(来自 `#/adapter/vxe-table`),**不要直接 new VxeGrid**
- 表单统一用 `useVbenForm`(来自 `#/adapter/form`),**不要直接写 `<Form>` + 手动校验**
- 弹窗统一用 `useVbenModal`(来自 `@vben/common-ui`),**不要用原生 `<Modal>`**
- 页面外壳统一用 `<Page>` 组件(来自 `@vben/common-ui`)
- HTTP 请求统一用 `requestClient`(来自 `#/api/request`),**禁止直接 axios / fetch**
- 行操作和工具栏按钮统一用 `<TableAction>` 组件(来自 `#/components/table-action`)
- 表单选择器/上传等:优先复用 `#/components/form/components/` 已有组件(当前有 upload / upload-image / avatar);没有再新增,禁止业务页内再造一份
## API 层规范
- API 文件统一放在 `<模块>/api/index.ts`
- 函数命名约定:`get{Entity}List` / `get{Entity}Option` / `get{Entity}Info` / `create{Entity}` / `update{Entity}` / `delete{Entity}`
- URL 使用 kebab-case,统一前缀变量 `const prefix = 'xxx/'`,**URL 末尾不补 `/`**
- GET 用 `params`,POST 用 `data`,例如:
```ts
import { requestClient } from '#/api/request';
const prefix = 'admin/';
export async function getAdminList(data: any) {
return requestClient.get<any>(`${prefix}list`, { params: data });
}
```
- 现有资源前缀参考:`admin/`、`role/`、`menu/`、`database/`、`code/`;登录后菜单接口为 `GET admin/menu`
## 字段命名对齐后端
- 数据库字段在前端 schema 的 `field` / `fieldName` 中保持 snake_case(如 `created_at`、`role_id`、`nick_name`),**不要转换为 camelCase**
- 时间戳字段(`created_at` 等)后端查询器已格式化为字符串,前端**无需再次格式化**
- TS 变量用 camelCase,常量用 UPPER_SNAKE_CASE,类型用 PascalCase
- 物理表前缀为 `nl_`(如 `nl_admin`、`nl_menu`),前端只对齐字段名,不必在 API 路径里写表前缀
## 表单校验
- 必填用字符串规则 `rules: 'required'`(选择项用 `'selectRequired'`),规则定义在 `#/adapter/form.ts` 的 `defineRules`
- 校验调用统一用 `formApi.validate().then(e => { if (e.valid) {...} })`
- 必填字段在 schema 中加 `rules: 'required'`,禁止散落 `validator` 写法
## 字典/枚举管理
- 模块内建 `config/constants.ts`,导出 `{ label, value }[]` 数组,禁止在 schema 中散落字面量
- 例如:
```ts
export const STATUS_OPTIONS = [
2026-08-10 13:10:42 +08:00
{ label: '启用', value: 0 },
{ label: '禁用', value: 1 },
2026-08-10 12:38:09 +08:00
];
```
## 路由规范
- 业务路由通过后端动态菜单驱动(库表 `nl_menu`,接口 `admin/menu`,`accessMode: 'backend'`)
- **不要**在前端 `router/routes/modules/` 静态注册业务路由(框架自带的 dashboard / core 除外)
- `defineOptions({ name: 'PascalCaseName' })` 必须与 `nl_menu.name` 字段一致(如 `SystemUser`、`SystemRole`、`SystemMenu`)
## Tab 用法
- 统一用 antd 原生 `Tabs` + `Tabs.TabPane`,**不要找 Vben 自封装的 Tabs**
- Tab 选中状态需要持久化时走 `localStorage`(模块内自定义 `TAB_STORAGE_KEY`)
## 中文注释
- 每个 `<script setup>` 顶部说明模块用途
- 每个主要函数(特别是有业务逻辑的)必须有中文注释
- 复杂的 schema 字段配置要注释意图
## 空行控制
- 上一部分代码和下一部分代码中间空行不超过 2 行
## 暗色模式适配(PC 管理端强制)
后台支持亮/暗主题切换,**新增或改样式时必须同时适配暗色**,禁止只按白底写死颜色。
### 优先用法(推荐)
- 颜色、边框、背景一律用主题 CSS 变量,例如:
- 文字:`hsl(var(--foreground))` / `hsl(var(--muted-foreground))`
- 背景:`hsl(var(--background))` / `hsl(var(--card, var(--background)))` / `hsl(var(--muted) / 0.25)`
- 边框:`hsl(var(--border))`
- 强调/选中:`hsl(var(--primary))`、`hsl(var(--primary) / 0.1)`
- 警告:`hsl(var(--warning))`、`hsl(var(--warning) / 0.12)`
- **禁止**业务样式写死 `#fff`、`#000`、`#0f172a`、`#f8fafc`、`#64748b` 等仅适合亮色的色值
- **禁止**用 `:global(.dark)` 改全局变量以免污染整站配色
- 优先「一套变量样式自动跟主题」,避免再写一套 `.dark .xxx { ... }`;仅当变量无法表达(如图片反色、特殊阴影)才用局部 `.dark .xxx`(且必须 `scoped`)
### 自检清单
- 弹窗 / 抽屉 / 卡片 / 预览对照区:在暗色下背景、文字、边框对比度可读
- 选中态、成功/导入高亮:用 `--primary` 透明度,不要写死浅绿底 `#f0faf8`
- 表格空态、分割线、次要文案:用 `--muted` / `--muted-foreground` / `--border`
- 改完后切换暗色主题肉眼过一遍,不要只测亮色
2026-08-10 14:42:27 +08:00
## Vben 组件使用规范(5.7 基线)
> 完整 API / 踩坑见 `Vben-Components.mdc`。下列为业务页强制约定;`lock` / `unlock` / Promise 版 `onBeforeClose` 在 5.7 已是默认能力,**不要再写「需 >5.5.x」这类过时版本门槛**。
2026-08-10 12:38:09 +08:00
### VbenModal(来自 @vben/common-ui)
- 创建弹窗必须用 `useVbenModal`,禁止直接用 antd `Modal` 或原生 `<dialog>`
- 抽离弹窗内容到子组件时,外层用 `connectedComponent` 参数连接,内外组件共享 `modalApi`
- 底部按钮扩展 slot 优先级:`prepend-footer`(取消按钮左侧)> `center-footer`(取消/确定之间)> `append-footer`(确定右侧)
- **严禁覆盖 `#footer` slot**,会丢失默认的取消/确定按钮和 loading 行为
2026-08-10 14:42:27 +08:00
- 提交防抖/防重复优先 `modalApi.lock()` / `unlock()`(锁定时确认按钮 loading,并禁止关闭);也可用 `setState({ confirmLoading: true })`,禁止自己写 button loading / disabled
2026-08-10 12:38:09 +08:00
- 拖拽开 `draggable: true`,需要拖出可视区时同时开 `overflow: true`
- 数据回填走 `modalApi.setData()` + `onOpenChange(isOpen) { const data = modalApi.getData() }`
2026-08-10 14:42:27 +08:00
- `animationType: 'slide' | 'scale'`(默认 slide);挂内容区时开 `appendToMain`,且 Page 必须 `auto-content-height`
2026-08-10 12:38:09 +08:00
### VbenDrawer(来自 @vben/common-ui)
- 创建抽屉必须用 `useVbenDrawer`,禁止直接用 antd `Drawer`
- 底部按钮扩展 slot 与 Modal 一致:`prepend-footer` / `center-footer` / `append-footer`
- **同样严禁覆盖 `#footer` slot**
2026-08-10 14:42:27 +08:00
- 关闭前校验用 `onBeforeClose`(支持 Promise / 返回 `false`),禁止自己拦截
- 提交防抖/防重复用 `drawerApi.lock()` / `unlock()`
2026-08-10 12:38:09 +08:00
### VbenVxeTable(来自 #/adapter/vxe-table)
- 表格必须用 `useVbenVxeGrid`,禁止直接 `new VxeGrid` 或 antd `Table`
- 远程数据加载走 `gridOptions.proxyConfig.ajax.query`,**禁止自己 fetch 后 `data = []` 赋值**
- query 接口返回结构必须匹配适配器配置:`{ items: [], total: number }`(在 `apps/web-antd/src/adapter/vxe-table.ts` 的 `response` 字段统一配置)
- 分页参数从 `{ page }` 解构取:`page.currentPage` / `page.pageSize`,禁止自己读 URL
- 刷新表格用 `gridApi.query()`(保留当前页)或 `gridApi.reload()`(回到第一页),禁止整页 `window.location.reload()`
- 工具栏按钮插 slot:`toolbar-actions`(标题左侧)、`toolbar-tools`(工具按钮左侧),禁止改 vxe-grid 内部模板
- 表格 loading 用 `gridApi.setLoading(bool)`,禁止自己遮罩
- 搜索表单由 `formOptions` 配置(底层是 Vben Form),开关走 `gridOptions.toolbarConfig.search = true`
- 自定义列渲染走 `slots: { default: 'action' }` + Grid 子组件内 `<template #action="{ row }">`,禁止 column 渲染函数里写 JSX
### VbenForm(来自 #/adapter/form)
- 表单必须用 `useVbenForm`,禁止直接 `<form>` 或 antd `Form`
- 动态修改 schema 必须用 `formApi.updateSchema([...])`,**vben form 没有 `setComponentProps` 这个方法**(这是已踩过的坑)
- 重置表单用 `formApi.resetForm()`,**严禁 `formApi.resetFields()`**(那是 antd Form 的 API,Vben Form 没有,会报 `resetFields is not a function`;已踩过坑)
- 取值/赋值用 `formApi.getValues()` / `formApi.setValues(obj)`,禁止自己 `v-model` 收集
- 校验用 `formApi.validate().then(e => { if (e.valid) {...} })` 或 `formApi.validateAndSubmitForm()`
- 必填规则用字符串 `'required'` / `'selectRequired'`,规则在 `#/adapter/form.ts` 的 `defineRules` 注册
- schema 中字段名(`fieldName`)必须与后端字段 snake_case 对齐,禁止前端 camelCase
2026-08-10 14:42:27 +08:00
- 业务表单密码字段用适配器组件名 `InputPassword`;禁止 `Input` + `type="password"`(框架自带页可用 `VbenInputPassword`)
2026-08-10 12:38:09 +08:00
- 单选/多选选项来自接口时,必须在弹窗/页面 `onMounted` 或 `onOpenChange` 里拉取后通过 `updateSchema` 注入,禁止写死常量
2026-08-11 09:39:29 +08:00
## 代码生成与 uniapp
- PC「代码生成」下载的 zip **同时包含** `view/`(本仓库 my-gen)与 `uniapp/`(移动端 `pages-sub/my-gen`)
- PC 侧只维护 Vben 模板与粘贴路径;uniapp 的 **C 端 UI / 三端兼容** 以 uniapp 与后端代码生成规则为准,禁止要求移动端照搬本仓库表格形态