156 lines
8.5 KiB
Plaintext
156 lines
8.5 KiB
Plaintext
---
|
||
description: 萧康云医管理后台(Vben Admin v5 + Vue 3 + TS + Ant Design Vue)代码规范
|
||
alwaysApply: true
|
||
---
|
||
|
||
# 基础规范(所有项目通用)
|
||
|
||
1. 写代码的时候要补充详细的中文注释(每个方法是干嘛的,为什么要这样写),如果是工作区,则所有项目都适用该规则
|
||
2. 注意不要生成太多的空行,上一部分代码和下一部分代码中间的空行不要大于 2 行
|
||
3. 有封装好的方法、组件需要复用,不要重复造轮子
|
||
4. 小程序端的抽屉全部需要用 page-container 来防止用户意外退出页面(记得使用 v-if 而不是 v-show)
|
||
5. 数据库的(created_at、updated_at、deleted_at)统一使用时间戳,不要使用字符串,并且我在查询器中一级格式化成字符串了,无需再次格式化
|
||
|
||
# 全局架构规范
|
||
|
||
## 目录结构规范
|
||
|
||
- 业务页面统一放在 `apps/web-antd/src/views/<端>/<模块>/`(端:system / business / doctor 等)
|
||
- 标准 CRUD 模块目录结构:
|
||
|
||
```
|
||
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/ # 模块工具(可选)
|
||
```
|
||
|
||
- 全局复用组件放 `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/*-picker.vue`,不要重写
|
||
|
||
## 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 = 'oa-robot/';
|
||
export async function getOaRobotList(data: any) {
|
||
return requestClient.get<any>(`${prefix}list`, { params: data });
|
||
}
|
||
```
|
||
|
||
## 字段命名对齐后端
|
||
|
||
- 数据库字段在前端 schema 的 `field` / `fieldName` 中保持 snake_case(如 `created_at`、`store_id`、`platform_code`),**不要转换为 camelCase**
|
||
- 时间戳字段(`created_at` 等)后端查询器已格式化为字符串,前端**无需再次格式化**
|
||
- TS 变量用 camelCase,常量用 UPPER_SNAKE_CASE,类型用 PascalCase
|
||
|
||
## 表单校验
|
||
|
||
- 必填用字符串规则 `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 OA_MESSAGE_TYPE_OPTIONS = [
|
||
{ label: '文本', value: 'text' },
|
||
{ label: 'Markdown', value: 'markdown' },
|
||
];
|
||
```
|
||
|
||
## 路由规范
|
||
|
||
- 业务路由通过后端动态菜单驱动(`xk_menu` 表),**不要在前端 `router/routes/modules/` 静态注册业务路由**
|
||
- `defineOptions({ name: 'PascalCaseName' })` 必须与 `xk_menu.name` 字段一致
|
||
|
||
## Tab 用法
|
||
|
||
- 统一用 antd 原生 `Tabs` + `Tabs.TabPane`,**不要找 Vben 自封装的 Tabs**
|
||
- Tab 选中状态持久化走 `localStorage`(参考 `system-config/index.vue` 的 `TAB_STORAGE_KEY` 模式)
|
||
|
||
## 抽屉用法(小程序端,admin 不涉及)
|
||
|
||
- 小程序端的抽屉用 `page-container` + `v-if`(不是 `v-show`)防止用户意外退出页面
|
||
|
||
## 中文注释
|
||
|
||
- 每个 `<script setup>` 顶部说明模块用途
|
||
- 每个主要函数(特别是有业务逻辑的)必须有中文注释
|
||
- 复杂的 schema 字段配置要注释意图
|
||
|
||
## 空行控制
|
||
|
||
- 上一部分代码和下一部分代码中间空行不超过 2 行
|
||
|
||
## Vben 组件使用规范
|
||
|
||
### VbenModal(来自 @vben/common-ui)
|
||
|
||
- 创建弹窗必须用 `useVbenModal`,禁止直接用 antd `Modal` 或原生 `<dialog>`
|
||
- 抽离弹窗内容到子组件时,外层用 `connectedComponent` 参数连接,内外组件共享 `modalApi`
|
||
- 底部按钮扩展 slot 优先级:`prepend-footer`(取消按钮左侧)> `center-footer`(取消/确定之间)> `append-footer`(确定右侧)
|
||
- **严禁覆盖 `#footer` slot**,会丢失默认的取消/确定按钮和 loading 行为
|
||
- 表单提交 loading 走 `modalApi.setState({ confirmLoading: true })`,禁止自己写 button loading
|
||
- 提交防抖/防重复用 `modalApi.lock()` / `unlock()`(>5.5.3),禁止手动 disabled
|
||
- 拖拽开 `draggable: true`,需要拖出可视区时同时开 `overflow: true`
|
||
- 数据回填走 `modalApi.setData()` + `onOpenChange(isOpen) { const data = modalApi.getData() }`
|
||
|
||
### VbenDrawer(来自 @vben/common-ui)
|
||
|
||
- 创建抽屉必须用 `useVbenDrawer`,禁止直接用 antd `Drawer`
|
||
- 底部按钮扩展 slot 与 Modal 一致:`prepend-footer` / `center-footer` / `append-footer`
|
||
- **同样严禁覆盖 `#footer` slot**
|
||
- 关闭前校验用 `onBeforeClose`(>5.5.2 支持 Promise),禁止自己拦截
|
||
- 提交防抖/防重复用 `drawerApi.lock()` / `unlock()`(>5.5.3)
|
||
- 小程序端的「抽屉」概念与此无关,小程序端按现有规则用 `page-container`
|
||
|
||
### 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
|
||
- 密码/敏感字段用 `VbenInputPassword` 组件,禁止用 `VbenInput` 配 `type="password"`
|
||
- 单选/多选选项来自接口时,必须在弹窗/页面 `onMounted` 或 `onOpenChange` 里拉取后通过 `updateSchema` 注入,禁止写死常量
|
||
|