@qilitt-mickey/vue3-temp-skill 1.1.2 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,9 +6,10 @@ Vue 3 企业级中后台项目开发规范技能包 — 让 AI 按团队规范
6
6
 
7
7
  外置 AI Skill:`/vue3-temp-skill <需求>` → 自动命中模块 → 按项目约定生成 → 自查。
8
8
 
9
- **设计要点(v1.1)**:
9
+ **设计要点**:
10
10
 
11
- - **始终加载** `core-kernel` + `project-inventory` + `code-quality`
11
+ - **始终加载** `core-kernel` + `project-inventory`(生成前)
12
+ - **生成后强制闸门** `code-quality`:重读清单 → 对照落盘文件 → `pnpm type-check` → 最终回复必须含「代码质量审查」结果块(仅列入命中模块不算完成)
12
13
  - **Vite / 脚本 / 入口不单独成 Skill**(工程摘要在 core-kernel)
13
14
  - **组件 / Hook 分核心与附加**,专项能力按需命中
14
15
  - 按 `references/` 内完整示例编写;复用已有封装;选型表指定库勿擅自替换(新依赖须待确认)
@@ -30,13 +31,18 @@ Vue 3 企业级中后台项目开发规范技能包 — 让 AI 按团队规范
30
31
 
31
32
  ## 模块一览
32
33
 
33
- ### 始终加载
34
+ ### 始终加载(生成前)
34
35
 
35
36
  | 模块 | 覆盖 |
36
37
  |------|------|
37
38
  | core-kernel | 硬约束、壳、CRUD 积木、工程摘要 |
38
39
  | project-inventory | 库精确版本 / 组件 / Hook / 选型对照 / **第九节安装表** |
39
- | code-quality | 生成后审查 |
40
+
41
+ ### 生成后强制闸门
42
+
43
+ | 模块 | 覆盖 |
44
+ |------|------|
45
+ | code-quality | 清单自查 + `pnpm type-check` + 最终回复「代码质量审查」结果块 |
40
46
 
41
47
  ### 标准业务面
42
48
 
@@ -85,11 +91,12 @@ npx @qilitt-mickey/vue3-temp-skill@latest install all
85
91
 
86
92
  ## 生成工作流(AI)
87
93
 
88
- 1. 解析需求 → 命中路由表
89
- 2. 读 core-kernel + inventory + code-quality
94
+ 1. 解析需求 → 命中**功能**路由表
95
+ 2. 读 core-kernel + inventory(生成前)
90
96
  3. **addon**:查业务 `package.json`,缺失则按 inventory 第九节精确版本征得确认后 `pnpm add`(含 `-D` 插件)
91
- 4. 读功能模块;仓库有实现则对齐,用 Skill 示例
92
- 5. 生成 → 审查 → 修正输出
97
+ 4. 读功能模块;仓库有 Re*/Hook/utils 则复用,页面写法用 Skill 示例
98
+ 5. 生成落盘
99
+ 6. **强制闸门 code-quality**:重读清单 → 自查刚改文件 → `pnpm type-check` → 修正 → 最终回复输出「代码质量审查」结果块(缺块 = 未完成)
93
100
 
94
101
  ## 源码位置
95
102
 
package/SKILL.md CHANGED
@@ -8,11 +8,12 @@ tags: [vue3, typescript, element-plus, vant, pinia, vite, unocss, crud, componen
8
8
  # Vue 3 项目开发规范技能包
9
9
 
10
10
  > 用户通过 `/vue3-temp-skill` + 自然语言描述需求,AI 应当:
11
- > 1. 分析需求,按下方**模块路由表**命中模块(可多模块)
12
- > 2. **始终加载** `core-kernel` + `code-quality`(含资产索引)
11
+ > 1. 分析需求,按下方**模块路由表**命中功能模块(可多模块)
12
+ > 2. **始终加载** `core-kernel` + `project-inventory`(生成前必读)
13
13
  > 3. 加载命中的功能模块详细规范(`references/`)
14
14
  > 4. **以本 Skill 内示例为准编写代码**(复用已有封装;选型表指定的库勿擅自替换)
15
- > 5. 生成完毕后执行质量审查;有问题就地修正
15
+ > 5. **生成完成后强制执行 `code-quality` 闸门**(见工作流第 6 步):重读清单 → 对照刚改文件自查 → 跑 `pnpm type-check` → 不通过就地修正 → 最终回复必须含「代码质量审查」结果块
16
+ > ⚠️ 仅在「命中模块」里写出 `code-quality` **不算**执行审查;没有审查结果块 = 任务未完成
16
17
 
17
18
  ---
18
19
 
@@ -39,7 +40,8 @@ tags: [vue3, typescript, element-plus, vant, pinia, vite, unocss, crud, componen
39
40
  - 选型表已指定三方库(如图谱、流程图、富文本)→ **用指定库,勿擅自换成同类其他库**;确需新库须「待确认」
40
41
  3. **按示例编写**:按本包 `references/` 中的**完整示例**与约定写法编写(命名、结构、调用方式以示例为准)。
41
42
  4. **无则上报**:清单与 Skill 均无对应能力时,标注「待确认」新依赖,未经用户同意不得引入。
42
- 5. **真相源优先级**:`仓库现有实现` > `本 Skill references 示例` > 通用 Vue 习惯。
43
+ 5. **真相源优先级**:`仓库已有可复用封装(Re* / Hook / utils)` > `本 Skill references 完整示例` > 通用 Vue 习惯。
44
+ **禁止**把仓库里的 views 演示页当作规范来源(业务项目可能没有演示页);列表布局、弹窗写法等以 Skill 内黄金样板为准。
43
45
  命名以代码为准:列表方法 **`onSearch` / `reset`**;响应类型全局 **`Result<T>`**(`types/global.d.ts`);HTTP 为 **`import { http } from '@/utils/http'`**。
44
46
  6. **依赖 / 插件安装(addon 必做)**:命中专项能力时,先读业务项目 `package.json`。
45
47
  - 已装齐 → 直接写代码
@@ -50,13 +52,20 @@ tags: [vue3, typescript, element-plus, vant, pinia, vite, unocss, crud, componen
50
52
 
51
53
  ## 模块路由表(AI 内部匹配用)
52
54
 
53
- ### 始终加载(无需关键词)
55
+ ### 始终加载(生成前必读)
54
56
 
55
57
  | 模块 | 文件 | 职责 |
56
58
  |------|------|------|
57
59
  | **core-kernel** | `references/core-kernel.md` | 硬约束、应用壳、CRUD 积木、工程摘要、资产分层 |
58
- | **code-quality** | `references/code-quality.md` | 生成后审查清单 |
59
- | **project-inventory** | `references/project-inventory.md` | 库/组件/Hook/工具/选型对照(随 core 一并查阅) |
60
+ | **project-inventory** | `references/project-inventory.md` | 库/组件/Hook/工具/选型对照 |
61
+
62
+ ### 生成后强制闸门(不是「命中模块」,写完代码后必须单独执行)
63
+
64
+ | 模块 | 文件 | 职责 |
65
+ |------|------|------|
66
+ | **code-quality** | `references/code-quality.md` | **写完/改完代码之后**再执行:对照清单自查 + `pnpm type-check` + 输出审查结果块;失败则修正后重跑 |
67
+
68
+ > `code-quality` **不要**只放进「命中模块」列表交差。它是闸门:生成前可预览清单,但**正式审查必须在落盘之后**,并在最终回复中可见。
60
69
 
61
70
  ### 标准业务面(按需)
62
71
 
@@ -103,8 +112,8 @@ tags: [vue3, typescript, element-plus, vant, pinia, vite, unocss, crud, componen
103
112
 
104
113
  ### 匹配规则
105
114
 
106
- 1. 单模块清晰 → 该模块 + 始终加载三件套。
107
- 2. 多模块组合 → 同时加载(例:带权限客户列表 = `crud-pages` + `permission-auth` + `http-api`)。
115
+ 1. 单模块清晰 → 该模块 + 始终加载(core-kernel + project-inventory);结束后跑 code-quality 闸门。
116
+ 2. 多模块组合 → 同时加载功能模块(例:带权限客户列表 = `crud-pages` + `permission-auth` + `http-api`),结束后同样强制 code-quality。
108
117
  3. 兜底 → 仅始终加载,并询问是否专项能力。
109
118
  4. 业务开发默认**不**改 `vite.config.ts` / `build/**`;仅当用户明确要求改构建时,参考 `core-kernel` 工程摘要后再动。
110
119
  5. **按用户说的效果匹配**(不要求用户懂代码/指令名):
@@ -173,7 +182,7 @@ export function getXxxListApi(data: XxxQuery) {
173
182
  ### 列表页
174
183
 
175
184
  ```typescript
176
- const { content, pagination, loading, onSearch, reset, formRef, tableData, ... } =
185
+ const { content, pagination, loading, onSearch, reset, formRef, tableData, tableHeight, ... } =
177
186
  useTableSearch(formQuery, getXxxListApi, `${import.meta.env.VITE_API_BASE_URL}/console/xxx/list`);
178
187
 
179
188
  onSearch(); // 查询
@@ -182,6 +191,10 @@ reset(); // 重置(配合 formRef.resetFields)
182
191
 
183
192
  - 方法名必须是 **`onSearch` / `reset`**,不是 handleSearch/handleReset。
184
193
  - 配合 `ReGrid` / `ReGridItem` / `ReTableBar` / `RePagination` / `ReTableOperate`。
194
+ - **布局必须按 `core-kernel`「列表黄金样板」完整输出**(技能自包含,不依赖仓库 views 演示页):
195
+ - `el-card ref="content"` + 固定 `height`(含 `headerHeight`),禁止只用 `minHeight` / `ref` 挂 `div`
196
+ - 查询区 `bg-bg_color p-5` + `ReGrid` `:gap="[0, 20]"` + 表单项 `w-[100%] !mr-0`;与 `ReTableBar` 展示区分开
197
+ - 表格绑 `tableHeight`;分页放在 `ReTableBar` 内,避免无数据时贴查询区
185
198
 
186
199
  ### 路由
187
200
 
@@ -200,12 +213,13 @@ reset(); // 重置(配合 formRef.resetFields)
200
213
 
201
214
  ### 1. 需求分析
202
215
 
203
- 提取字段、查询、交互;按路由表列出模块 ID
216
+ 提取字段、查询、交互;按路由表列出**功能**模块 ID(不含把 `code-quality` 当功能命中)。
204
217
 
205
- ### 2. 加载规范
218
+ ### 2. 加载规范(生成前)
206
219
 
207
- - 必读:`core-kernel.md`、`project-inventory.md`、`code-quality.md`
220
+ - 必读:`core-kernel.md`、`project-inventory.md`
208
221
  - 加读:命中的功能模块 md
222
+ - 可顺带浏览 `code-quality.md` 约束,但**不能替代**第 6 步正式审查
209
223
 
210
224
  ### 3. 依赖与插件探查(命中 addon 时必做)
211
225
 
@@ -221,15 +235,40 @@ reset(); // 重置(配合 formRef.resetFields)
221
235
 
222
236
  ### 5. 生成代码
223
237
 
224
- 完整可运行:类型、API、页面、路由片段(如需要)、样式约定。
238
+ 完整可运行:类型、API、页面、路由片段(如需要)、样式约定。
239
+ **本步结束后不得直接结束对话**,必须进入第 6 步。
240
+
241
+ ### 6. 自动质量审查闸门(强制 · 生成后)
225
242
 
226
- ### 6. 自动质量审查(必须)
243
+ > 目标:杜绝「命中了 code-quality 但没真正审查」的假完成。
227
244
 
228
- 见下方清单;不通过则修正后再输出。
245
+ 按顺序执行(缺一不可):
246
+
247
+ 1. **重读** `references/code-quality.md`(生成后再读一次,不要只靠记忆)。
248
+ 2. **对照刚写入/修改的文件**逐项过清单(尤其:选型、HTTP/`Result`、列表布局、权限、无多余 console)。
249
+ 3. **跑命令**(在业务项目根目录):优先 `pnpm type-check`;若项目有 `pnpm lint` 且改动含 `.vue/.ts`,尽量一并跑。失败则修代码后重跑,直到通过或明确阻塞原因。
250
+ 4. **就地修正**:审查发现的问题必须改完再进入第 7 步;禁止带着已知规范问题收工。
251
+ 5. **写入最终回复的「代码质量审查」结果块**(格式见下);无此块视为任务未完成。
252
+
253
+ #### 最终回复必含结构
254
+
255
+ ```markdown
256
+ ## 命中模块
257
+ - <功能模块…>(勿把 code-quality 仅列在此处代替审查)
258
+
259
+ ## 代码质量审查
260
+ - 闸门:已执行 code-quality(生成后)
261
+ - 清单:选型 / 类型 / 组件 / API / 安全 / 业务 / 列表布局(如适用) / 风格 → 通过 | 已修正:…
262
+ - 命令:`pnpm type-check` → 通过 | 失败原因…
263
+ - 待确认:…
264
+
265
+ ## 变更说明
266
+ - …
267
+ ```
229
268
 
230
269
  ### 7. 输出
231
270
 
232
- 说明命中了哪些模块;列出待确认项(接口路径、字段类型、是否允许新依赖 / 是否已安装 addon)。
271
+ 在完成第 6 步结果块之后,再说明变更与待确认项(接口路径、字段类型、是否允许新依赖 / 是否已安装 addon)。
233
272
 
234
273
  ---
235
274
 
@@ -247,6 +286,7 @@ reset(); // 重置(配合 formRef.resetFields)
247
286
  **3. API** — `@/utils/http`;方法跟后端(允许 GET/POST);封装在 `src/api/`;`VITE_API_BASE_URL`;无虚构 `/api` / 无 `crypto: true`
248
287
  **4. 安全** — 无危险 v-html;无硬编码密钥
249
288
  **5. 业务** — `onSearch`/`reset`;提交 loading;权限;未二次 Layout
289
+ **5b. 列表布局** — `el-card`+`content`+固定 height;查询/展示分区;`tableHeight`;分页在 ReTableBar 内
250
290
  **6. 风格** — 无多余 console;`$t('中文')`;`--vts-*` / UnoCSS;图标走 `svg-icon` / `icons` 约定
251
291
 
252
292
  完整项见 `references/code-quality.md`。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qilitt-mickey/vue3-temp-skill",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Vue 3 企业级中后台项目开发规范技能包 — core-kernel 架构、按需功能模块",
5
5
  "bin": {
6
6
  "vue3-temp-skill": "./bin/cli.js"
@@ -1,14 +1,37 @@
1
1
  ---
2
2
  skill: code-quality
3
- description: AI 生成代码的质量审查清单。在生成或修改任何 Vue/TypeScript 代码后,必须对照此清单逐项自查,确保代码符合项目规范。适用于代码审查、AI 代码自检、Code Review 场景。
3
+ description: 生成后强制质量闸门。写完/改完 Vue/TS 代码后必须重读本清单、对照落盘文件自查、跑 type-check,并在最终回复输出「代码质量审查」结果块。仅把本模块列入命中模块不算完成。
4
4
  scope: project
5
- tags: [code-review, quality, checklist, lint, typescript, vue3, security, performance]
5
+ tags: [code-review, quality, checklist, lint, typescript, vue3, security, performance, gate]
6
6
  ---
7
7
 
8
- # 代码质量审查清单
8
+ # 代码质量审查清单(生成后强制闸门)
9
+
10
+ > **何时执行**:任意 `/vue3-temp-skill` 任务在**文件已写入或修改之后**,结束对话前必须执行本闸门。
11
+ > **不算完成**:只在「命中模块」里写 `code-quality`;或只跑了 type-check 却未对照本清单;或最终回复无「代码质量审查」结果块。
12
+ > **须按** Skill 功能模块示例 + 本清单审查;不通过必须修正后再收工。
13
+
14
+ ## 强制执行协议(AI 必须按序做)
15
+
16
+ 1. **重读本文件**(生成后再读,禁止仅凭开场记忆勾选)。
17
+ 2. **列出本轮改动文件路径**,逐文件对照下方清单相关项。
18
+ 3. **执行命令**(业务项目根目录):
19
+ - 必跑:`pnpm type-check`(若无该脚本则用 `pnpm exec vue-tsc --noEmit`)
20
+ - 建议:`pnpm lint`(存在且改动含 `.vue/.ts/.tsx` 时)
21
+ 4. **失败则修代码 → 重跑命令**,直到通过或写出明确阻塞(缺依赖、环境等)。
22
+ 5. **在最终回复追加结果块**(缺此块 = 未完成):
23
+
24
+ ```markdown
25
+ ## 代码质量审查
26
+ - 闸门:已执行 code-quality(生成后)
27
+ - 改动文件:`path/a`、`path/b`、…
28
+ - 清单:选型 ✅ / 类型 ✅ / 组件 ✅ / API ✅ / 安全 ✅ / 业务 ✅ / 列表布局 ✅或N/A / 风格 ✅
29
+ - 已修正:…(无则写「无」)
30
+ - 命令:`pnpm type-check` → 通过 | 失败:…
31
+ - 待确认:…
32
+ ```
9
33
 
10
- > **使用说明**:生成或修改代码后逐项对照。不通过必须修正后再输出。
11
- > 须按 Skill 模块内示例与本清单审查。
34
+ ---
12
35
 
13
36
  ## 〇、项目选型(最高优先级)
14
37
 
@@ -22,6 +45,7 @@ tags: [code-review, quality, checklist, lint, typescript, vue3, security, perfor
22
45
  - [ ] HTTP 为 `import { http } from '@/utils/http'` + 全局 `Result<T>`
23
46
  - [ ] 业务路由未二次挂 Layout
24
47
  - [ ] 使用对应 `references/<模块>.md` 示例与约定写法
48
+ - [ ] **查询列表布局**:`el-card ref="content"` + 固定 `height`(含 `headerHeight`);查询区 `bg-bg_color p-5` + `ReGrid` `gap`/`cols`;表格绑 `tableHeight`;分页在 `ReTableBar` 内
25
49
 
26
50
 
27
51
  ## 一、TypeScript 类型检查
@@ -226,6 +250,18 @@ const fullName = computed(() => `${user.firstName} ${user.lastName}`);
226
250
  - [ ] 路由跳转使用了正确的参数传递方式(`query` 而非 `params`)。
227
251
  - [ ] 分页切换正确触发了数据加载。
228
252
 
253
+ ### 查询列表页布局(命中 crud-pages / 列表需求时必查)
254
+
255
+ - [ ] `ref="content"` 挂在 **`el-card`**(组件),不是原生 `div`。
256
+ - [ ] 卡片样式为固定 **`height: calc(100vh - 2 * var(--vts-margin) - ${headerHeight}px)`**,不是 `minHeight`。
257
+ - [ ] 查询 `el-form`:`inline` + `class="bg-bg_color p-5"`;`ReGrid` 有 `:gap="[0, 20]"` 与响应式 `cols`。
258
+ - [ ] 表单项有 `class="w-[100%] !mr-0"`;操作按钮用 `<ReGridItem suffix>`。
259
+ - [ ] 折叠事件为 `@should-show-collapse`(不是 `@show-collapse`)。
260
+ - [ ] 展示区用 `ReTableBar class="bg-bg_color"`,与查询区视觉分离。
261
+ - [ ] 表格 `:style="{ height: \`${tableHeight}px\` }"`;`paginationRef` 在表格下方且同属 `ReTableBar` `#default`。
262
+ - [ ] `RePagination` 使用 `:pagination` + `@handle-current-change` / `@handle-size-change`。
263
+ - [ ] `ReTableOperate` 使用 `:list`(不是 `:buttons`)。
264
+
229
265
  ## 八、代码风格检查
230
266
 
231
267
  ### 必须通过
@@ -240,25 +276,23 @@ const fullName = computed(() => `${user.firstName} ${user.lastName}`);
240
276
 
241
277
  ## 审查流程
242
278
 
243
- 1. **生成代码后**:逐项对照以上清单。
244
- 2. **发现问题**:立即修正,不要输出有问题的代码。
245
- 3. **不确定项**:标注 `// TODO: 需要确认` 注释提醒开发者。
246
- 4. **严重问题**:如果存在安全漏洞或类型错误,必须修正后再输出。
279
+ 1. **生成/改码落盘之后**(不是生成前):执行上方「强制执行协议」。
280
+ 2. **发现问题**:立即修正并重跑 `pnpm type-check`,不要带着已知问题收工。
281
+ 3. **不确定项**:写入结果块「待确认」,必要时在代码加 `// TODO: 需要确认`。
282
+ 4. **严重问题**(安全 / 类型错误 / 列表布局硬约束违反):必须修正后再输出最终回复。
283
+ 5. **最终回复**:必须含「代码质量审查」结果块;禁止只用「命中模块含 code-quality」代替。
247
284
 
248
285
  ## 快速自检命令
249
286
 
250
287
  完成代码修改后,运行以下命令验证:
251
288
 
252
289
  ```bash
253
- # TypeScript 类型检查
254
- npx vue-tsc --noEmit
290
+ # 必跑:TypeScript 类型检查(优先用项目脚本)
291
+ pnpm type-check
255
292
 
256
- # ESLint 检查
257
- npx eslint src/ --ext .vue,.ts,.tsx
258
-
259
- # Stylelint 检查
260
- npx stylelint "src/**/*.{css,scss,vue}"
261
-
262
- # 全量检查(推荐)
293
+ # 建议:全量检查
263
294
  pnpm lint
295
+
296
+ # 无 type-check 脚本时的回退
297
+ pnpm exec vue-tsc --noEmit
264
298
  ```
@@ -28,6 +28,7 @@ tags: [vue3, kernel, conventions, layout, router, http, crud, inventory]
28
28
  | 8 | i18n:`$t('中文key')`,key 即中文文案 |
29
29
  | 9 | 禁止用 `v-html` 渲染不可信内容 |
30
30
  | 10 | 新三方依赖必须用户确认;已有封装须复用,选型表指定库勿擅自替换 |
31
+ | 11 | **查询列表页布局硬约束**(见下方黄金样板):`el-card` + `ref="content"` + **固定 `height`**(非 `minHeight`);查询区与表格区分区;表格用 `tableHeight`;分页放在 `ReTableBar` 内 |
31
32
  | 11 | addon 缺失时按 `project-inventory` 第九节**精确版本**安装;禁止 `@latest` / 无版本号;Vite 插件与 `build/plugins/*` 成套核对 |
32
33
 
33
34
  ---
@@ -116,8 +117,23 @@ ReAuth、ReDialog、ReGrid、ReGridItem、RePagination、ReTableBar、ReTableOpe
116
117
 
117
118
  ### 列表黄金样板
118
119
 
120
+ > **布局真相源即本样板**(技能自包含,不依赖业务仓库是否存在演示页)。生成查询列表页必须完整按下方结构输出,禁止简化成「表单 + 裸表格 + 裸分页」——否则会出现字段挤在一起、查询与展示区分不清、无数据时分页贴查询区、页面高度不饱满。
121
+
122
+ #### 布局硬约束(违反即不合格)
123
+
124
+ | # | 必须 | 说明 |
125
+ |---|------|------|
126
+ | L1 | `el-card` + `ref="content"` | `useTableSearch` 通过 `content.$el` 算高度;`ref` 必须挂在 **组件**(`el-card`)上,禁止挂在原生 `div` |
127
+ | L2 | 卡片用固定 **`height`** | `height: calc(100vh - 2 * var(--vts-margin) - ${headerHeight}px)`;**禁止**只写 `minHeight`(否则 `tableHeight` 算不准,表格无法撑满) |
128
+ | L3 | 查询区与展示区分开 | 查询:`el-form` + `class="bg-bg_color p-5"`;展示:`ReTableBar class="bg-bg_color"`;两区视觉分离 |
129
+ | L4 | `ReGrid` 间距与栅格 | `:gap="[0, 20]"` + `:cols="{ xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }"`;表单项 `class="w-[100%] !mr-0"`;事件为 `@should-show-collapse` |
130
+ | L5 | 操作按钮用 `suffix` | `<ReGridItem suffix>`,勿用随意 `index=99` 冒充后缀列 |
131
+ | L6 | 表格动态高度 | `:style="{ height: \`${tableHeight}px\` }"`;分页 **必须在** `ReTableBar` 的 `#default` 内、表格下方,保证空数据时仍占满剩余高度 |
132
+ | L7 | `RePagination` 真实 API | `:pagination="pagination"` + `@handle-current-change` / `@handle-size-change`;**禁止**虚构的 `v-model:current-page` |
133
+
119
134
  ```vue
120
135
  <script setup lang="ts">
136
+ import type { OperateButton } from "@/components/ReTableOperate";
121
137
  import type { Customer, CustomerQuery } from "@/api/customer";
122
138
  import { getCustomerList } from "@/api/customer";
123
139
  import { ReGrid } from "@/components/ReGrid";
@@ -159,6 +175,15 @@ const {
159
175
 
160
176
  const tableList = computed(() => tableData.value.tableList as Customer[]);
161
177
 
178
+ const operateButtons: OperateButton[] = [
179
+ {
180
+ label: $t("删除"),
181
+ type: "danger",
182
+ icon: "i-ep:delete",
183
+ onClick: (row) => handleDelete(row as Customer),
184
+ },
185
+ ];
186
+
162
187
  onMounted(() => {
163
188
  onSearch();
164
189
  });
@@ -167,91 +192,147 @@ function handleDelete(row: Customer) {
167
192
  message($t("删除成功"), { type: "success" });
168
193
  onSearch();
169
194
  }
195
+
196
+ function handleCurrentChange(cur: number) {
197
+ pagination.currentPage = cur;
198
+ onSearch();
199
+ }
200
+
201
+ function handleSizeChange(size: number) {
202
+ pagination.pageSize = size;
203
+ onSearch();
204
+ }
170
205
  </script>
171
206
 
172
207
  <template>
173
- <div
174
- ref="content"
175
- :style="{ minHeight: `calc(100vh - 2 * var(--vts-margin) - ${headerHeight}px)` }"
176
- class="bg-[var(--el-bg-color)] p-2"
177
- >
178
- <el-form ref="formRef" :model="formQuery" @keyup.enter="onSearch">
179
- <ReGrid ref="gridRef" :collapsed="collapsed" @show-collapse="showCollapse = $event">
180
- <ReGridItem :index="0">
181
- <el-form-item :label="$t('关键词')" prop="keyword">
182
- <el-input v-model="formQuery.keyword" clearable :placeholder="$t('请输入')" />
183
- </el-form-item>
184
- </ReGridItem>
185
- <ReGridItem :index="1">
186
- <el-form-item :label="$t('状态')" prop="status">
187
- <el-select v-model="formQuery.status" clearable :placeholder="$t('请选择')">
188
- <el-option :label="$t('启用')" :value="1" />
189
- <el-option :label="$t('禁用')" :value="0" />
190
- </el-select>
191
- </el-form-item>
192
- </ReGridItem>
193
- <ReGridItem :index="99" :collapsed="false">
194
- <el-form-item>
195
- <el-button type="primary" :loading="loading" @click="onSearch">{{ $t("查询") }}</el-button>
196
- <el-button @click="reset()">{{ $t("重置") }}</el-button>
197
- <el-button
198
- v-if="showCollapse"
199
- text
200
- type="primary"
201
- @click="collapsedToggle"
202
- >
203
- {{ collapsed ? $t("展开") : $t("收起") }}
204
- <component :is="collapsed ? downBold : upBold" class="ml-1" />
205
- </el-button>
206
- </el-form-item>
207
- </ReGridItem>
208
- </ReGrid>
209
- </el-form>
210
-
211
- <ReTableBar
212
- :columns="[]"
213
- @refresh="onSearch"
214
- >
215
- <template #buttons>
216
- <el-button type="primary" v-auth="['admin']">{{ $t("新增") }}</el-button>
217
- </template>
218
- </ReTableBar>
219
-
220
- <el-table
221
- ref="multipleTableRef"
222
- v-loading="loading"
223
- :element-loading-svg="svgLoading"
224
- :data="tableList"
225
- :height="tableHeight"
226
- border
227
- row-key="id"
208
+ <div>
209
+ <!-- L1/L2:ref=content 必须在 el-card;用 height 非 minHeight -->
210
+ <el-card
211
+ ref="content"
212
+ shadow="never"
213
+ :style="{
214
+ height: `calc(100vh - 2 * var(--vts-margin) - ${headerHeight}px)`,
215
+ }"
228
216
  >
229
- <el-table-column prop="code" :label="$t('编号')" min-width="120" />
230
- <el-table-column prop="name" :label="$t('名称')" min-width="140" />
231
- <el-table-column :label="$t('操作')" width="160" fixed="right">
232
- <template #default="{ row }">
233
- <ReTableOperate
234
- :buttons="[
235
- { label: $t('编辑'), onClick: () => {} },
236
- { label: $t('删除'), type: 'danger', onClick: () => handleDelete(row) },
237
- ]"
238
- />
217
+ <!-- L3:查询区独立背景与内边距 -->
218
+ <div>
219
+ <el-form
220
+ ref="formRef"
221
+ :inline="true"
222
+ :model="formQuery"
223
+ class="bg-bg_color p-5"
224
+ >
225
+ <!-- L4:gap + cols;事件名 should-show-collapse -->
226
+ <ReGrid
227
+ ref="gridRef"
228
+ :collapsed="collapsed"
229
+ :gap="[0, 20]"
230
+ :cols="{ xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }"
231
+ style="max-height: 300px; overflow-y: auto;"
232
+ @should-show-collapse="(val) => (showCollapse = val)"
233
+ >
234
+ <ReGridItem index="0">
235
+ <el-form-item :label="$t('关键词')" prop="keyword" class="w-[100%] !mr-0">
236
+ <el-input
237
+ v-model="formQuery.keyword"
238
+ clearable
239
+ :placeholder="$t('请输入')"
240
+ />
241
+ </el-form-item>
242
+ </ReGridItem>
243
+ <ReGridItem index="1">
244
+ <el-form-item :label="$t('状态')" prop="status" class="w-[100%] !mr-0">
245
+ <el-select v-model="formQuery.status" clearable :placeholder="$t('请选择')">
246
+ <el-option :label="$t('启用')" :value="1" />
247
+ <el-option :label="$t('禁用')" :value="0" />
248
+ </el-select>
249
+ </el-form-item>
250
+ </ReGridItem>
251
+ <!-- L5:操作列用 suffix -->
252
+ <ReGridItem suffix>
253
+ <div class="flex flex-end">
254
+ <el-button
255
+ type="primary"
256
+ size="small"
257
+ :loading="loading"
258
+ @click="pagination.currentPage = 1; pagination.pageSize = 10; onSearch()"
259
+ >
260
+ {{ $t("查询") }}
261
+ </el-button>
262
+ <el-button size="small" @click="reset">
263
+ {{ $t("重置") }}
264
+ </el-button>
265
+ <el-button
266
+ v-if="showCollapse"
267
+ type="primary"
268
+ link
269
+ class="!font-size-12"
270
+ @click="collapsedToggle"
271
+ >
272
+ {{ collapsed ? $t("展开") : $t("收起") }}
273
+ <el-icon class="el-icon--right">
274
+ <component :is="collapsed ? downBold : upBold" />
275
+ </el-icon>
276
+ </el-button>
277
+ </div>
278
+ </ReGridItem>
279
+ </ReGrid>
280
+ </el-form>
281
+ </div>
282
+
283
+ <!-- L3/L6:展示区在 ReTableBar 内;表格+分页同槽,保证空数据也撑满 -->
284
+ <ReTableBar class="bg-bg_color" :columns="[]" @refresh="onSearch">
285
+ <template #title>
286
+ <span class="truncate font-size-16 font-bold">{{ $t("展示列表") }}</span>
287
+ </template>
288
+ <template #buttons>
289
+ <el-button type="primary" size="small" v-auth="['admin']">
290
+ {{ $t("新增") }}
291
+ </el-button>
292
+ </template>
293
+ <template #default="{ size }">
294
+ <div class="vl-parent">
295
+ <el-table
296
+ ref="multipleTableRef"
297
+ v-loading.lock="loading"
298
+ :element-loading-text="$t('加载中')"
299
+ :element-loading-spinner="svgLoading"
300
+ element-loading-svg-view-box="0, 0, 20, 20"
301
+ :style="{ height: `${tableHeight}px` }"
302
+ :size="size"
303
+ :data="tableList"
304
+ :header-cell-style="{
305
+ background: 'var(--el-fill-color-light)',
306
+ color: 'var(--el-text-color-primary)',
307
+ }"
308
+ row-key="id"
309
+ >
310
+ <el-table-column prop="code" :label="$t('编号')" min-width="120" />
311
+ <el-table-column prop="name" :label="$t('名称')" min-width="140" />
312
+ <el-table-column :label="$t('操作')" width="160" fixed="right">
313
+ <template #default="{ row, $index }">
314
+ <ReTableOperate :list="operateButtons" :row="row" :index="$index" />
315
+ </template>
316
+ </el-table-column>
317
+ </el-table>
318
+
319
+ <!-- L7:真实分页 API;ref=paginationRef 参与 tableHeight 计算 -->
320
+ <RePagination
321
+ ref="paginationRef"
322
+ :size="size"
323
+ :pagination="pagination"
324
+ @handle-current-change="handleCurrentChange"
325
+ @handle-size-change="handleSizeChange"
326
+ />
327
+ </div>
239
328
  </template>
240
- </el-table-column>
241
- </el-table>
242
-
243
- <RePagination
244
- ref="paginationRef"
245
- v-model:current-page="pagination.currentPage"
246
- v-model:page-size="pagination.pageSize"
247
- :total="pagination.total"
248
- @change="onSearch"
249
- />
329
+ </ReTableBar>
330
+ </el-card>
250
331
  </div>
251
332
  </template>
252
333
  ```
253
334
 
254
- `useTableSearch(formQuery, searchFn, url)`:`searchFn` 签名为 `(query, url) => Promise<Result<T>>`,分页字段为 `pageNo` / `pageSize`,列表数据取 `data.content`,总数 `data.totalCount`。
335
+ `useTableSearch(formQuery, searchFn, url)`:`searchFn` 签名为 `(query, url) => Promise<Result<T>>`,分页字段为 `pageNo` / `pageSize`,列表数据取 `data.content`,总数 `data.totalCount`。返回值中的 `content` / `tableHeight` / `paginationRef` / `formRef` 必须按样板绑定,否则动态高度失效。
255
336
 
256
337
  ---
257
338
 
@@ -1,14 +1,15 @@
1
1
  ---
2
2
  skill: crud-pages
3
- description: 查询列表页、表单页、标准 CRUD 流程。含 useTableSearch(onSearch/reset)、ReGrid 查询区、表格工具栏。务必按照本文件完整示例编写。
3
+ description: 查询列表页、表单页、标准 CRUD 流程。含 useTableSearch(onSearch/reset)、ReGrid 查询区、表格工具栏与动态高度布局。务必按照本文件与 core-kernel 完整示例编写。
4
4
  scope: project
5
- tags: [vue3, crud, list, form, detail, table, useTableSearch, page]
5
+ tags: [vue3, crud, list, form, detail, table, useTableSearch, page, layout]
6
6
  ---
7
7
 
8
8
  # CRUD 页面开发规范
9
9
 
10
10
  > 以下示例即规范真相。
11
11
  > 须同时遵守 `core-kernel` / `http-api`。详情跳转见 `detail-page`,回显见 `data-writeback`。
12
+ > **查询列表页完整模板以 `core-kernel.md`「列表黄金样板」为准**(技能内完整示例,勿到仓库里找 views 演示页对照)。
12
13
 
13
14
  ## 查询列表页
14
15
 
@@ -17,23 +18,40 @@ tags: [vue3, crud, list, form, detail, table, useTableSearch, page]
17
18
  ```typescript
18
19
  useTableSearch(formQuery, searchFn, url)
19
20
  // searchFn: (query: CombinedQueryData, url: string) => Promise<Result<T>>
20
- // 返回含:content, tableData, pagination, loading, formRef, onSearch, reset, tableHeight, ...
21
+ // 返回含:content, tableData, pagination, loading, formRef, onSearch, reset,
22
+ // tableHeight, paginationRef, gridRef, showCollapse, collapsed,
23
+ // collapsedToggle, upBold, downBold, multipleTableRef, ...
21
24
  ```
22
25
 
23
26
  - 查询:`onSearch()`
24
27
  - 重置:`reset()`(内部 `formRef.resetFields()`)
25
28
  - 列表:`tableData.value.tableList`(或经 computed 断言)
26
29
  - 分页:请求带 `pageNo` / `pageSize`;响应 `data.content` / `data.totalCount`
30
+ - **高度**:`tableHeight` 由 ResizeObserver 根据 `content` / `formRef` / `paginationRef` 计算;三者必须按黄金样板绑定
31
+
32
+ ### 布局硬约束(生成列表页必须全部满足)
33
+
34
+ | 区域 | 正确写法 | 禁止 |
35
+ |------|---------|------|
36
+ | 外层容器 | `<el-card ref="content" shadow="never" :style="{ height: \`calc(100vh - 2 * var(--vts-margin) - ${headerHeight}px)\` }">` | `ref` 挂原生 `div`;只用 `minHeight`;漏 `headerHeight` |
37
+ | 查询区 | `el-form` `:inline="true"` + `class="bg-bg_color p-5"` | 无内边距、无背景、字段横向挤成一团 |
38
+ | 栅格 | `ReGrid` `:gap="[0, 20]"` + `:cols="{ xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }"`;`@should-show-collapse` | 无 `gap`;事件写成 `@show-collapse` |
39
+ | 表单项 | `el-form-item` 加 `class="w-[100%] !mr-0"`;`ReGridItem` 用 `index="0"` 字符串 | 表单项无宽度、彼此贴死 |
40
+ | 查询按钮 | `<ReGridItem suffix>` + `flex flex-end` | 用 `index="99"` 冒充后缀 |
41
+ | 展示区 | `ReTableBar class="bg-bg_color"`,与查询区分离 | 查询与表格糊成一块、无标题栏 |
42
+ | 表格高度 | `:style="{ height: \`${tableHeight}px\` }"` | 不绑 `tableHeight`(空数据时分页贴查询区) |
43
+ | 分页 | 放在 `ReTableBar` `#default` 内、表格下方;`RePagination` + `:pagination` + `@handle-current-change` / `@handle-size-change` | 分页在卡片外;虚构 `v-model:current-page` |
27
44
 
28
45
  ### 完整列表示例
29
46
 
30
47
  见 `core-kernel.md`「列表黄金样板」。本模块补充约定:
31
48
 
32
49
  1. 查询区用 **`ReGrid` + `ReGridItem`**,折叠与 `collapsed` / `showCollapse` / `collapsedToggle` 配套。
33
- 2. 工具栏用 **`ReTableBar`**,行操作用 **`ReTableOperate`**,分页用 **`RePagination`**。
34
- 3. 查询 / 重置按钮文案 `$t('查询')` / `$t('重置')`,点击绑定 **`onSearch` / `reset`**。
50
+ 2. 工具栏用 **`ReTableBar`**,行操作用 **`ReTableOperate`**(`:list` 非 `:buttons`),分页用 **`RePagination`**。
51
+ 3. 查询 / 重置按钮文案 `$t('查询')` / `$t('重置')`(或模板一致的「搜索」),点击绑定 **`onSearch` / `reset`**。
35
52
  4. `defineOptions({ name })` 与路由 `name` 一致(keep-alive / 标签)。
36
53
  5. 需要权限的按钮加 `v-auth` / `ReAuth`。
54
+ 6. 从 `useApp()` 取 **`headerHeight`**、`svgLoading`(及按需 `filterValue` / `baseCodeGet`)。
37
55
 
38
56
  ### API 配套
39
57
 
@@ -45,11 +63,12 @@ export function getCustomerList(data: CustomerQuery, url: string) {
45
63
  ```
46
64
 
47
65
  ```typescript
48
- const { onSearch, reset, tableData, loading, pagination, ... } = useTableSearch(
49
- formQuery,
50
- getCustomerList,
51
- `${import.meta.env.VITE_API_BASE_URL}/console/customer/list`,
52
- );
66
+ const { onSearch, reset, tableData, loading, pagination, content, tableHeight, ... } =
67
+ useTableSearch(
68
+ formQuery,
69
+ getCustomerList,
70
+ `${import.meta.env.VITE_API_BASE_URL}/console/customer/list`,
71
+ );
53
72
  onMounted(() => onSearch());
54
73
  ```
55
74
 
@@ -140,3 +159,11 @@ async function onSubmit() {
140
159
  - 提交无 loading
141
160
  - 业务路由二次 Layout
142
161
  - ReSelectQuery 写成不存在的 `:api` / `label-key` API(真实 props 见 form-advanced)
162
+ - **`ref="content"` 挂在 `div` 上**(`content.$el` 为空 → `tableHeight` 恒为 0)
163
+ - 卡片只用 **`minHeight`**,不用固定 **`height`**(页面不饱满、表格高度算错)
164
+ - 查询表单无 `p-5` / 无 `gap` / 表单项无 `w-[100%] !mr-0`(字段挤在一起)
165
+ - 查询区与 `ReTableBar` 无分区(条件与展示糊成一块)
166
+ - 表格不绑 `tableHeight`,分页放在卡片外(无数据时分页直接贴查询条件)
167
+ - `RePagination` 写成 `v-model:current-page`(组件真实 API 为 `:pagination` + handle 事件)
168
+ - `ReTableOperate` 写成 `:buttons`(真实 prop 为 `:list`)
169
+ - `ReGrid` 监听 `@show-collapse`(真实事件为 `@should-show-collapse`)
@@ -203,7 +203,7 @@ function parseAddress(text: string) {
203
203
  ## ReDialog 通用弹窗(必用)
204
204
 
205
205
  > 落点:`src/components/ReDialog`。基于 `el-dialog` 封装:居中、可拖拽、`destroy-on-close`、默认禁止点遮罩关闭、可选全屏切换、内容区 `el-scrollbar`。
206
- > 样板参考:`src/views/table/base/index.vue`、`src/views/form/moreForm/MultiFormDialog.vue`。
206
+ > 写法以**本模块下方黄金样板**为准(技能自包含,勿依赖仓库 views 演示页)。
207
207
 
208
208
  ### Props / 插槽
209
209
 
@@ -154,7 +154,7 @@ export default {
154
154
 
155
155
  ## 五、ReIconPicker 图标选择器
156
156
 
157
- 路径:`src/components/ReIconPicker`。演示页:`src/views/form/iconPicker`(`/form/icon-picker`)。
157
+ 路径:`src/components/ReIconPicker`。用法以本模块示例为准。
158
158
 
159
159
  ### 默认图标源
160
160
 
@@ -221,7 +221,7 @@
221
221
  | **useApp** | 全局应用状态(布局、字典、登出等) | `baseCodeGet(codeType)`、`filterValue()`、`headerHeight`、`svgLoading` |
222
222
  | **useDetail** | 详情页跳转与参数传递 | `toDetail({ id, mark, text })`、`initToDetail()`、`getParameter()` |
223
223
  | **useFormCheck** | 多表单校验聚合 | 追踪各字段校验状态,emit `form-validate` 事件 |
224
- | **useTableSearch** | 列表页搜索/分页/Loading | `content`、`tableData`、`pagination`、`loading`、`onSearch()`、`reset()`、`formRef` |
224
+ | **useTableSearch** | 列表页搜索/分页/Loading/动态表高 | `content`(须绑 `el-card`)、`tableHeight`、`paginationRef`、`tableData`、`pagination`、`loading`、`onSearch()`、`reset()`、`formRef`、`gridRef`、`collapsed` / `showCollapse` / `collapsedToggle` |
225
225
  | **useTags** | 多标签页管理 | 右键菜单(刷新/关闭/关闭其他/全屏) |
226
226
  | **useTheme** | 主题切换(暗色/亮色) | 主题色生成、布局预设、缓存重置 |
227
227
  | **useWebSocket** | WebSocket 通信 | `subscribe()`、`send()`、`sendRaw()`、`isConnected` |