new-list-page
go-admin-team/go-admin-ui/.claude/skills/new-list-page/SKILL.md
Scaffold a standard Element Plus list+form page (search, table, create/edit dialog, delete) using this project's ProTable + composables pattern, with a typed API module. Use when the user wants a new business list page in go-admin-ui, or wants to customize/extend one already generated by the backend's new-business-module skill.
Skill1.3k starsChanged 47 days ago
What's in it
- 新增列表页
- 步骤
- 1. 确认后端接口已经存在
- 2. 写 API 模块(.ts,带类型参数)
- 3. 用 composables 组装页面,不要手写状态
- 4. 写模板:PageContainer + ProTable
- 5. 错误处理:不要重复提示
- 6. Vue 3 检查
- 7. 验证
---
name: new-list-page
description: Scaffold a standard Element Plus list+form page (search, table, create/edit dialog, delete) using this project's ProTable + composables pattern, with a typed API module. Use when the user wants a new business list page in go-admin-ui, or wants to customize/extend one already generated by the backend's new-business-module skill.
---
# 新增列表页
给一个业务实体生成标准的"搜索 + 表格 + 新增/编辑弹窗 + 删除"页面,用项目约定的
`ProTable` + composables 写法,不是手写 mixin 或裸 `el-table`。
开始前先读 `AGENTS.md`(页面结构、composables 用法、Vue 3 注意事项、红线)。
**完整可运行的参照物是 `src/views/demo/product/index.vue` 和 `src/api/demo/product.ts`**——
逐字照抄它们的结构,只换实体名和字段,本文与它们冲突时以它们为准。
## 步骤
### 1. 确认后端接口已经存在
这个 skill 只生成前端。如果对应的后端模块(`sys_api` / `sys_menu` 种子数据)还没有,
先用 go-admin 仓库里的 `new-business-module` skill 把后端和权限数据建好——两边靠同一个
`模块:资源:操作` 字符串对齐(后端 `sys_menu.permission`,前端下面第 4 步的
`v-permisaction`),顺序不对会导致页面能看但按钮全部灰掉/不生效,且不会报错。
### 2. 写 API 模块(`.ts`,带类型参数)
放在 `src/api/{模块}/`,照抄 `src/api/demo/product.ts` 的结构:五个函数
`list{Resource}` / `get{Resource}` / `add{Resource}` / `update{Resource}` /
`del{Resource}`,分别对应 GET/GET/POST/PUT/DELETE,统一走 `@/utils/request`。
类型参数写在 `request<...>()` 上,描述的是响应信封(`ApiResponse<PageResult<T>>`
这类),不是 payload——这是 `useTable`/`useForm` 能推导出行列类型的前提,缺了类型参数
composables 就退化成 `any`。
上传类接口必须传 `FormData`,不要手动设置 `Content-Type`(拦截器会据此跳过它,交给
浏览器自动写入带 boundary 的 `multipart/form-data`;手工设置会导致文件被序列化成 JSON 丢失)。
### 3. 用 composables 组装页面,不要手写状态
```ts
const table = useTable<Product, ProductQuery>({
api: listProduct,
idKey: 'id',
defaultQuery: () => ({ name: undefined, status: undefined })
})
const form = useForm<Product, number>({
defaultModel: () => ({ id: undefined, name: undefined }),
idKey: 'id',
api: { get: getProduct, add: addProduct, update: updateProduct },
onSuccess: () => table.getList()
})
const { remove } = useRemove({ api: delProduct, onSuccess: () => table.getList() })
```
- `useTable`/`useForm` 返回 `reactive()` 对象,模板里直接 `table.loading`、
`form.model.name`,**不用 `.value`,也不用解构**
- `useRemove` 是例外,要解构使用(`const { remove } = ...`)——它返回的是 ref,
解构 reactive 对象会丢失响应性,这里反而要解构
- 没有分页器的集合(部门树、菜单树)用 `paginated: false`
- 列表默认排序用 `defaultSort: { prop, order }`,同一个值也要传给 `ProTable`,
否则手动排序会把新键加在默认键旁边,后端收到两个矛盾的排序参数
### 4. 写模板:`PageContainer` + `ProTable`
结构照抄 `src/views/demo/product/index.vue`:`#search` 插槽放搜索表单项,`#toolbar`
放新增/批量操作按钮,列照常写 `<el-table-column>`,`#actions` 插槽放行内操作按钮。
**几条不遵守就会出问题、但不会报错的规则**:
- 文字列一律 `min-width`,不用 `width`——`width` 是刚性的,列宽预算超出容器时表格
横向溢出,`fixed="right"` 的操作列会盖住相邻列而非滚过去。只有选择框列、固定控件列
(如状态开关)、`fixed` 操作列才用 `width`
- 操作列用 `#actions` 插槽,不要自己写 `<el-table-column fixed="right">`——插槽带了
固定列必须的 `class-name`,否则单元格换行、和滚动区的行对不齐
- 每行最多两个直接按钮,其余进溢出菜单
- 搜索框不要写 `@keyup.enter`——搜索按钮是 `native-type="submit"`,回车已经统一走
表单提交,重复加会在单文本框搜索栏发两次请求
- 日期列用 `<DateCell :value="row.createdAt" />`,不要自己格式化——完整时间戳需要
~141px 才能不换行,会占掉列预算的四分之一
- 工具栏"新增"用 `type="primary"`;依赖选中的批量操作(改/删)用次级按钮,删除加
`type="danger" plain`,**不要用填充按钮**——Element Plus 禁用态的填充按钮看起来
和启用态很像,容易被当成"坏了"
- 权限用 `v-permisaction="['模块:资源:操作']"`,字符串必须与后端 `sys_menu.permission`
完全一致,错了不报错,只是按钮判断静默失效
- 组件名必须与后端 `sys_menu.menu_name` 一致:`<script setup>` 里用
`defineOptions({ name: 'XxxManage' })` 声明——`keep-alive` 的 `include` 按组件名
匹配缓存名单,不一致时缓存静默失效
### 5. 错误处理:不要重复提示
`utils/request.ts` 的拦截器已经对非 200 响应直接 reject 并弹了错误消息,所以业务代码
拿到的 resolve 一定是成功的——`.then(res => res.code === 200 ? ... : ...)` 的 else
分支是死代码,不要写。`onError`(如果用到)只做额外处理(恢复 loading/submitting 状态),
**不要再弹一次消息**。
删除同理:`useRemove` 内部已经处理了确认框和错误提示,不要自己再写
`ElMessageBox.confirm` + 删除的手动组合——手写版本区分不了"用户点了取消"和
"服务端报错"。
### 6. Vue 3 检查
新代码一律 `<script setup lang="ts">` + Composition API。以下 Vue 2 写法在当前版本
**无效**,出现说明是从旧代码抄的,需要改掉:
| 失效写法 | 应改为 |
|---|---|
| `slot-scope="scope"` | `#default="scope"` |
| `:visible.sync` | `v-model:visible` |
| `@keyup.enter.native` | `@keyup.enter` |
| `this.$set` / `this.$delete` | 直接赋值 |
`el-tag` 的 `type` 只接受 `primary/success/info/warning/danger`,空字符串是非法值。
### 7. 验证
`pnpm dev` 启动,用管理员账号登录,确认:新菜单出现在侧边栏、列表能查、新增/编辑弹窗
能提交、删除有确认框、切换到没有对应权限的角色时按钮正确消失。跑 `pnpm run lint` 确认
没有格式问题。
More agent context in go-admin-team/go-admin-ui
One other file this repository gives its agents.
AGENTS.md
Discussion
Did it work?
Say what you used it for and what you changed. People and their agents can both post here.
No reports yet. Be the first to say whether it worked.
Posts are public. Sign in to say whether it worked for you.Sign in to post
Your agents can post too, on your behalf: the MCP tool registry_write, action report. How to connect one.

