返回笔记首页

SDD 规范驱动开发 — 管理系统 CRUD 实操

主题配置

核心价值:第一个模块 1.5 天(含建规范),后续每个模块平均 4 小时,边际成本趋近于零。

AI


核心思路

把一个 CRUD 模块拆成 3 层 Spec,AI 每次只生成对应层,互不污染:

Spec 文件 用途 维护频率
crud-template.spec.md 所有模块的通用骨架,写一次永久用 极低,有新规范才改
ui-token.spec.md Element Plus 组件库用法规范 低,升级时更新
{module}.spec.md 本次业务模块的字段、接口、权限 每个新模块写一份

每次跑 Prompt 生成 5 个文件:列表页 / 表单弹窗 / API 封装 / 类型定义 / 路由配置


Step 1 — 写 crud-template.spec.md(一次性,永久复用)

markdown
# crud-template.spec.md(通用模板,不要改)

## 列表页结构(固定)
顶部:搜索区(折叠/展开)+ 右侧操作按钮组(新增 / 导出)
中部:数据表格,支持列排序,固定操作列在最右
底部:分页器,每页 20 条,显示总数

## 表格必须包含的列(所有模块通用)
- 序号列(宽 60px,不排序)
- 业务数据列(由 module.spec.md 定义)
- 状态列(宽 100px,Tag 展示)
- 创建时间(宽 180px,默认降序排列)
- 操作列(固定右侧 120px:编辑 / 删除)

## 新增/编辑弹窗规范
- 统一用 el-dialog,宽 600px,移动端全屏
- 表单双列布局(label 在左,宽 80px)
- 校验时机:失焦触发,不是提交时
- 底部按钮:左对齐"取消",右对齐"确定"
- 提交期间:确定按钮 loading,禁止重复点击

## 删除确认
- 单条:el-popconfirm(不用 el-message-box)
- 批量:el-message-box,展示将删除的条数

## 接口规范(RESTful)
GET    /api/{module}        列表(分页 + 筛选)
POST   /api/{module}        新增
PUT    /api/{module}/:id    编辑
DELETE /api/{module}/:id    删除(软删除)

## 请求/响应结构(固定格式)
请求分页参数:{ page, pageSize, ...filters }
响应格式:{ code, data: { list, total }, message }

## 权限控制(按钮级别)
新增按钮:v-permission="'{module}:add'"
编辑按钮:v-permission="'{module}:edit'"
删除按钮:v-permission="'{module}:delete'"

## 公共组件(必须复用,不重复造轮子)
- SearchForm:搜索区表单
- PageTable:数据表格(含分页)
- StatusTag:状态 Tag 展示
- OperateButtons:操作按钮组(编辑/删除)

## 错误处理规范
- 接口错误:统一由 request.ts 拦截,ElMessage.error 提示
- 表单校验失败:高亮字段,不弹 Toast
- 网络超时:提示"网络异常,请稍后重试",不暴露技术细节

Step 2 — 写 ui-token.spec.md(Element Plus 用法规范)

markdown
# ui-token.spec.md(Element Plus 规范)

## 表单组件
- 输入框:el-input,clearable 默认开启
- 下拉:el-select,filterable 超过 10 条开启
- 日期:el-date-picker,format="YYYY-MM-DD",value-format="YYYY-MM-DD"
- 数字:el-input-number,controls-position="right"
- 开关:el-switch,active-value=1 inactive-value=0(对应后端 0/1)

## 校验规则格式(统一写法)
rules: {
  fieldName: [
    { required: true, message: '请输入XX', trigger: 'blur' },
    { min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
  ]
}

## 表格规范
- stripe: true(斑马纹)
- border: true(边框)
- highlight-current-row: true
- 空数据:empty-text="暂无数据"
- 加载:v-loading="loading"(不用骨架屏)

## 弹窗规范
- width="600px"
- :close-on-click-modal="false"(禁止点遮罩关闭)
- @close 时重置表单:formRef.value?.resetFields()

## 消息提示
- 成功:ElMessage.success('操作成功')
- 失败:ElMessage.error(err.message || '操作失败')
- 确认删除(批量):ElMessageBox.confirm
- 确认删除(单条):ElPopconfirm

## 按钮规范
- 主操作(新增/确定):type="primary"
- 次操作(导出/重置):无 type(默认样式)
- 危险操作(删除):type="danger"
- 按钮间距:gap: 8px

Step 3 — 写 module.spec.md(每个新模块 5 分钟,以"角色管理"为例)

只写业务差异,通用规范已在 crud-template.spec.md 里:

markdown
# role-module.spec.md(角色管理模块)

## 引用
遵循 @crud-template.spec.md 和 @ui-token.spec.md 的所有规范

## 模块基本信息
- 路由路径:/system/role
- API 前缀:/api/system/role
- 权限前缀:system:role

## 业务字段定义

| 字段名    | 类型     | 必填 | 说明                       | 表格展示 | 表单展示                   |
|-----------|----------|------|----------------------------|----------|--------------------------|
| roleName  | string   | 是   | 角色名称,2~20字            | 是       | 是                        |
| roleCode  | string   | 是   | 角色编码,大写+下划线        | 是       | 新增可编辑,编辑时 disabled |
| roleSort  | number   | 是   | 排序,默认0,越小越前        | 否       | 是                        |
| status    | 0 \| 1   | 是   | 0=禁用 1=启用               | 是(Tag)| 是(Switch)               |
| menuIds   | number[] | 否   | 绑定菜单ID列表              | 否       | 是(树形选择)              |
| remark    | string   | 否   | 备注,最多200字              | 否       | 是                        |

## 搜索条件
- roleName(el-input,模糊搜索)
- status(el-select:全部 / 启用 / 禁用)
- 创建时间范围(el-date-picker,type="daterange")

## 特殊交互(超出通用规范的部分)
1. roleCode 新增时可编辑,编辑时 disabled(通过 isEdit props 控制)
2. 编辑弹窗底部额外有"权限配置"区域
   - el-tree 树形菜单,支持全选/反选
   - 接口:GET /api/system/menu/tree 获取菜单树
3. status=禁用时,编辑/删除按钮仍可操作(不 disabled)

## 接口定义
| 方法   | 路径                    | 参数说明                                               |
|--------|-------------------------|------------------------------------------------------|
| GET    | /api/system/role        | { roleName?, status?, startTime?, endTime?, page, pageSize } |
| POST   | /api/system/role        | { roleName, roleCode, roleSort, status, menuIds?, remark? } |
| PUT    | /api/system/role/:id    | 同新增(roleCode 字段后端忽略)                         |
| DELETE | /api/system/role/:id    | 无 body                                              |
| GET    | /api/system/menu/tree   | 无参数,返回菜单树结构                                  |

Step 4 — 一次性 Prompt 生成 5 个文件

plain
请基于以下 Spec,为"角色管理"模块生成完整代码:

【通用规范】@crud-template.spec.md
【UI 规范】@ui-token.spec.md
【模块定义】@role-module.spec.md
【参考代码】@src/views/system/user/index.vue(风格最接近的已有模块)

需要生成的 5 个文件:
1. src/views/system/role/index.vue         ← 列表页(含搜索 + 表格 + 分页)
2. src/views/system/role/RoleDialog.vue    ← 新增/编辑弹窗(含权限配置树)
3. src/api/system/role.ts                  ← 接口封装
4. src/types/system/role.d.ts              ← TypeScript 类型定义
5. src/router/modules/system.ts            ← 新增路由(追加,不替换原有配置)

约束:
- 严格复用已有公共组件(SearchForm / PageTable / StatusTag / OperateButtons)
- 权限指令使用 v-permission,不自己实现权限逻辑
- 所有 CSS 使用 scoped + CSS 变量,不写内联样式
- roleCode 的 disabled 逻辑通过 isEdit prop 控制,不用 v-if 条件渲染两个 input
- 生成完成后列出每个文件的核心改动点,方便 diff 检查

Step 5 — 验证四件事

bash
npx tsc --noEmit              # 类型无误
npx eslint src --ext .vue,.ts # 规范符合
git diff --stat               # 确认只改了预期文件
# 浏览器跑通:列表 → 搜索 → 新增 → 编辑 → 删除 → 权限配置

下一个新模块怎么做(以"菜单管理"为例)

  1. 新建 menu-module.spec.md(5 分钟填字段表,只写业务差异)
  2. Step 4 的 Prompt 里把 @role-module.spec.md 换成 @menu-module.spec.md
  3. 参考代码换成 src/views/system/role/index.vue(刚生成的,风格最新)
  4. 跑 Prompt → 5 个文件生成 → 验证
plain
第 1 个模块:1.5 天(含建规范)
第 2~N 个模块:每个约 4 小时

收益(面试话术)

"我们项目有 12 个管理模块,传统方式每个需要 2~3 天。SDD 之后第一个模块 1.5 天(含建规范),后续每个平均 4 小时,节省约 60% 的时间。而且代码风格高度一致,Code Review 基本不在结构和样式上花时间,只看业务逻辑。"


Spec 目录结构建议

plain
docs/specs/
  crud-template.spec.md    ← 永久复用,轻易不改
  ui-token.spec.md         ← 升级 Element Plus 时更新
  modules/
    user-module.spec.md    ← 用户管理
    role-module.spec.md    ← 角色管理
    menu-module.spec.md    ← 菜单管理
    dept-module.spec.md    ← 部门管理
    ...