--- name: mock-codegen description: 'Use when: generating mock data for development without a backend. Optional skill - activated when user confirms or explicitly requests it. Triggers on: 生成mock, mock数据, 模拟数据, mock生成, 前端mock, 联调前mock.' --- # Skill: Mock 数据生成(mock-codegen) > **⚡ 可选技能** — 默认不在标准工作流中强制执行。 > > 触发方式: > > - 执行完整流程时(prototype-scan → api-contract → page-codegen),AI 会在最后询问:**"是否需要生成 Mock 数据以便在联调前使用?"** > - 用户明确说"帮我生成 mock 数据"时直接执行 --- ## 触发 - 用户显式请求 Mock 数据生成 - 执行完整代码生成流程结束时,由 AI 确认后执行 - 接收 page-spec JSON 作为输入(来自 prototype-scan) --- ## Robot Admin Mock 约定 本项目采用**轻量级内联 Mock 模式**: - Mock 数据直接写在 `data.ts` 中,以函数或常量导出 - `useTableCrud` 支持传入本地数据数组代替真实 API 调用 - 不依赖 MSW / axios-mock-adapter 等重量级 mock 框架 - 通过环境变量 `VITE_APP_ENV=development` 判断是否启用 --- ## 步骤 ### 1. 确认是否需要 Mock 在完整流程末尾,AI 询问: ``` ✅ 页面代码已生成完成。 是否需要生成 Mock 数据(开发联调前使用)? - 输入 Y / 是 → 生成 Mock 并注入 data.ts - 输入 N / 否 → 跳过,等后端接口就绪后对接 ``` ### 2. 根据 page-spec 推导字段类型 从 `page-spec.columns` + `page-spec.form` 推导每个字段的 mock 策略: | 字段类型/名称特征 | Mock 策略 | | ------------------------------- | -------------------------------------- | | `id` / `*Id` | `Date.now() + index` 或递增数字 | | `name` / `*Name` | 中文姓名候选数组随机取 | | `status` | 从 page-spec tagMap 的 key 中随机取 | | `date` / `*Time` / `createTime` | `new Date().toISOString()` ± 随机天数 | | `phone` / `mobile` | `1${['3','5','7','8'][r]}${随机9位数}` | | `email` | `user${i}@example.com` | | `amount` / `price` / `*Fee` | `Math.floor(Math.random() * 10000)` | | `count` / `num` / `qty` | `Math.floor(Math.random() * 100)` | | `type` / `category` | 从 options 配置中随机取 value | | `remark` / `desc` / `note` | 候选文本数组随机取 | | `boolean` / `enabled` | `Math.random() > 0.5` | ### 3. 生成 getMockList 函数 在 `data.ts` 中追加以下代码(放在文件末尾,用区块注释隔开): ```typescript // ================= Mock 数据(联调前使用,对接真实接口后可删除)================= /** * * @description: 生成{{资源名}}模拟数据列表 * ? @param {number} count 生成条数,默认 30 * ! @return {ResourceItem[]} 模拟数据数组 */ export const getMockList = (count = 30): {{ResourceItem}}[] => { // 候选数据池 const names = ['张三', '李四', '王五', '赵六', '陈七', '周八', '吴九', '郑十'] const depts = ['技术部', '市场部', '运营部', '财务部', '人事部'] return Array.from({ length: count }, (_, i) => ({ id: i + 1, // ... 根据 page-spec 字段自动生成(每个字段对应上表策略) createTime: new Date(Date.now() - Math.random() * 30 * 24 * 3600 * 1000).toLocaleString(), updateTime: new Date().toLocaleString(), })) } /** * * @description: 生成分页 Mock 响应(模拟后端响应结构) * ? @param {number} page 当前页 * ? @param {number} pageSize 每页条数 * ! @return {object} 分页响应对象 */ export const getMockPageResult = (page = 1, pageSize = 20) => { const allData = getMockList(100) const start = (page - 1) * pageSize return { list: allData.slice(start, start + pageSize), total: allData.length, page, pageSize, } } ``` ### 4. 注入 Headless CRUD(可选) 如果页面使用了表格 CRUD,直接通过 `query` 注入 Mock 查询函数,不伪造接口配置: ```typescript import { useNaiveTableCrud } from '@robot-admin/request-core/naive' const tableCrud = useNaiveTableCrud({ autoLoad: 'mounted', defaultPageSize: 20, query: async ({ page, pageSize }) => { const result = getMockList(page, pageSize) return { items: result.list, total: result.total } }, columns: getTableColumns(), }) ``` ### 5. 简单赋值模式 不需要 CRUD 状态机的纯展示页可直接在 `onMounted` 中赋值: ```typescript // index.vue 中 const tableData = ref([]) const loading = ref(false) onMounted(async () => { loading.value = true try { // 开发 Mock 模式 —— 对接真实接口时替换为 API 调用 // const res = await getResourceListApi({ page: 1, pageSize: 20 }) // tableData.value = res.data.list tableData.value = getMockList(30) // 开发 Mock 用 } finally { loading.value = false } }) ``` --- ## 输出文件 | 文件 | 操作 | | ------------------------------------- | ---------------------------------------- | | `src/views///data.ts` | 追加 `getMockList` + `getMockPageResult` | | `src/views///index.vue` | 可选:修改 onMounted 注入 mock 数据 | --- ## 注意事项 1. **Mock 代码加注释**:所有 Mock 代码块前后加 `// ===== Mock 数据 =====` 注释,便于对接真实接口时批量删除 2. **不影响生产**:Mock 代码仅在开发时使用,不影响生产构建 3. **字段名必须一致**:Mock 数据的字段名必须与 `getTableColumns()` 中的 `key` 完全一致 4. **状态字段取合法值**:`status` 等字段的值必须来自 `STATUS_TAG_CONFIG` 的 key,否则渲染会出现空白 5. **数量建议 20-50 条**:足够测试分页逻辑,不超过内存限制