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

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.
Files changed (42) hide show
  1. package/README.md +66 -214
  2. package/SKILL.md +165 -527
  3. package/package.json +2 -2
  4. package/references/api-check.md +187 -128
  5. package/references/base-code-dict.md +49 -48
  6. package/references/chart-echarts.md +61 -0
  7. package/references/code-quality.md +44 -15
  8. package/references/core-kernel.md +305 -0
  9. package/references/crud-pages.md +87 -261
  10. package/references/data-compare.md +502 -501
  11. package/references/data-mapping.md +218 -213
  12. package/references/data-screen.md +94 -79
  13. package/references/data-writeback.md +104 -104
  14. package/references/detail-page.md +100 -99
  15. package/references/directives-advanced.md +27 -4
  16. package/references/download-export.md +70 -68
  17. package/references/feedback-loading.md +62 -60
  18. package/references/feedback-ui.md +125 -111
  19. package/references/file-management.md +28 -7
  20. package/references/flowchart-g6.md +249 -244
  21. package/references/form-advanced.md +264 -26
  22. package/references/graph-relation.md +12 -7
  23. package/references/http-api.md +138 -103
  24. package/references/icons.md +326 -0
  25. package/references/layout-theme.md +1 -1
  26. package/references/mobile-h5.md +20 -0
  27. package/references/particles.md +143 -0
  28. package/references/permission-auth.md +8 -31
  29. package/references/project-inventory.md +175 -120
  30. package/references/qrcode-barcode.md +107 -92
  31. package/references/rich-text.md +85 -73
  32. package/references/seamless-scroll.md +40 -38
  33. package/references/table-vxe.md +114 -0
  34. package/references/tree-table.md +2 -1
  35. package/references/ui-components.md +5 -1
  36. package/references/verify-captcha.md +110 -96
  37. package/references/websocket-realtime.md +2 -1
  38. package/references/wechat-js.md +58 -0
  39. package/references/workflow-bpmn.md +207 -206
  40. package/references/advanced-ui.md +0 -302
  41. package/references/build-optim.md +0 -282
  42. package/references/vue-core.md +0 -209
@@ -1,316 +1,142 @@
1
1
  ---
2
2
  skill: crud-pages
3
- description: 规范查询列表页、表单页、详情/编辑页的标准开发流程,包含 useTableSearch Hook、表单校验、数据回显等核心模式。开发 CRUD 业务页面时调用。
3
+ description: 查询列表页、表单页、标准 CRUD 流程。含 useTableSearch(onSearch/reset)、ReGrid 查询区、表格工具栏。务必按照本文件完整示例编写。
4
4
  scope: project
5
5
  tags: [vue3, crud, list, form, detail, table, useTableSearch, page]
6
6
  ---
7
7
 
8
8
  # CRUD 页面开发规范
9
9
 
10
- ## 查询列表页
11
-
12
- ### 标准结构
10
+ > 以下示例即规范真相。
11
+ > 须同时遵守 `core-kernel` / `http-api`。详情跳转见 `detail-page`,回显见 `data-writeback`。
13
12
 
14
- 查询列表页是后台管理系统最常见的页面类型,使用 `useTableSearch` Hook 统一处理。
15
-
16
- ```vue
17
- <script setup lang="ts">
18
- import { useTableSearch } from "@/hooks/useTableSearch";
19
- import { getXxxListApi } from "@/api/xxx";
20
- import type { XxxQueryForm, XxxListItem } from "@/api/types/xxx";
21
-
22
- defineOptions({ name: "XxxList" });
13
+ ## 查询列表页
23
14
 
24
- // 查询表单
25
- const formQuery = reactive<XxxQueryForm>({
26
- keyword: "",
27
- status: undefined,
28
- dateRange: [],
29
- });
15
+ ### useTableSearch 真实签名
30
16
 
31
- // useTableSearch 返回表格相关的所有状态和方法
32
- const {
33
- content, // 表格数据
34
- pagination, // 分页信息
35
- loading, // 加载状态
36
- handleSearch, // 查询
37
- handleReset, // 重置
38
- handlePageChange, // 翻页
39
- handleSizeChange, // 切换每页条数
40
- } = useTableSearch(formQuery, getXxxListApi);
17
+ ```typescript
18
+ useTableSearch(formQuery, searchFn, url)
19
+ // searchFn: (query: CombinedQueryData, url: string) => Promise<Result<T>>
20
+ // 返回含:content, tableData, pagination, loading, formRef, onSearch, reset, tableHeight, ...
21
+ ```
41
22
 
42
- // 初始化加载
43
- handleSearch();
44
- </script>
23
+ - 查询:`onSearch()`
24
+ - 重置:`reset()`(内部 `formRef.resetFields()`)
25
+ - 列表:`tableData.value.tableList`(或经 computed 断言)
26
+ - 分页:请求带 `pageNo` / `pageSize`;响应 `data.content` / `data.totalCount`
45
27
 
46
- <template>
47
- <div class="re-xxx-list">
48
- <!-- 查询区域 -->
49
- <el-form :model="formQuery" inline>
50
- <el-form-item label="关键词">
51
- <el-input v-model="formQuery.keyword" placeholder="请输入" clearable />
52
- </el-form-item>
53
- <el-form-item label="状态">
54
- <el-select v-model="formQuery.status" placeholder="请选择" clearable>
55
- <el-option label="启用" :value="1" />
56
- <el-option label="禁用" :value="0" />
57
- </el-select>
58
- </el-form-item>
59
- <el-form-item>
60
- <el-button type="primary" @click="handleSearch">查询</el-button>
61
- <el-button @click="handleReset">重置</el-button>
62
- </el-form-item>
63
- </el-form>
28
+ ### 完整列表示例
64
29
 
65
- <!-- 工具栏 -->
66
- <ReTableBar>
67
- <template #left>
68
- <el-button type="primary" @click="handleAdd">新增</el-button>
69
- </template>
70
- </ReTableBar>
30
+ 见 `core-kernel.md`「列表黄金样板」。本模块补充约定:
71
31
 
72
- <!-- 数据表格 -->
73
- <ReGrid
74
- v-loading="loading"
75
- :data="content"
76
- :pagination="pagination"
77
- @page-change="handlePageChange"
78
- @size-change="handleSizeChange"
79
- >
80
- <el-table-column prop="name" label="名称" />
81
- <el-table-column prop="status" label="状态">
82
- <template #default="{ row }">
83
- <el-tag :type="row.status === 1 ? 'success' : 'danger'">
84
- {{ row.status === 1 ? "启用" : "禁用" }}
85
- </el-tag>
86
- </template>
87
- </el-table-column>
88
- <el-table-column label="操作" width="200" fixed="right">
89
- <template #default="{ row }">
90
- <ReTableOperate
91
- :buttons="[
92
- { label: '编辑', type: 'primary', onClick: () => handleEdit(row) },
93
- { label: '删除', type: 'danger', onClick: () => handleDelete(row) },
94
- ]"
95
- />
96
- </template>
97
- </el-table-column>
98
- </ReGrid>
99
- </div>
100
- </template>
101
- ```
32
+ 1. 查询区用 **`ReGrid` + `ReGridItem`**,折叠与 `collapsed` / `showCollapse` / `collapsedToggle` 配套。
33
+ 2. 工具栏用 **`ReTableBar`**,行操作用 **`ReTableOperate`**,分页用 **`RePagination`**。
34
+ 3. 查询 / 重置按钮文案 `$t('查询')` / `$t('重置')`,点击绑定 **`onSearch` / `reset`**。
35
+ 4. `defineOptions({ name })` 与路由 `name` 一致(keep-alive / 标签)。
36
+ 5. 需要权限的按钮加 `v-auth` / `ReAuth`。
102
37
 
103
- ### useTableSearch 参数说明
38
+ ### API 配套
104
39
 
105
40
  ```typescript
106
- function useTableSearch<TQuery, TResult>(
107
- formQuery: TQuery, // 查询表单(reactive 对象)
108
- apiFn: (params: TQuery) => Promise<Result<TResult>>, // API 函数
109
- options?: {
110
- immediate?: boolean; // 是否立即加载(默认 true)
111
- defaultPageSize?: number; // 默认每页条数(默认 10)
112
- }
113
- ): {
114
- content: Ref<TResult["list"]>;
115
- pagination: { currentPage: number; pageSize: number; total: number };
116
- loading: Ref<boolean>;
117
- handleSearch: () => Promise<void>;
118
- handleReset: () => void;
119
- handlePageChange: (page: number) => void;
120
- handleSizeChange: (size: number) => void;
41
+ // src/api/customer.ts — 详见 http-api
42
+ export function getCustomerList(data: CustomerQuery, url: string) {
43
+ return http.request<Result<CustomerPage>>("post", url, { data });
121
44
  }
122
45
  ```
123
46
 
124
- ### 列表页约定
125
-
126
- 1. 查询表单使用 `reactive()` 定义,不使用 `ref()`。
127
- 2. 表格数据、分页、Loading 统一由 `useTableSearch` 管理,不要手动维护。
128
- 3. 操作列使用 `ReTableOperate` 组件渲染按钮组。
129
- 4. 工具栏使用 `ReTableBar` 组件包裹。
130
- 5. 表格使用 `ReGrid` 组件(内置分页联动)。
131
- 6. 删除操作必须弹出确认框。
132
-
133
- ## 表单页
47
+ ```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
+ );
53
+ onMounted(() => onSearch());
54
+ ```
134
55
 
135
- ### 新增/编辑表单
56
+ ## 表单页(新增 / 编辑)
136
57
 
137
58
  ```vue
138
59
  <script setup lang="ts">
139
60
  import type { FormInstance, FormRules } from "element-plus";
140
- import { getXxxDetailApi, saveXxxApi } from "@/api/xxx";
141
- import type { XxxForm } from "@/api/types/xxx";
61
+ import type { Customer } from "@/api/customer";
62
+ import { saveCustomer } from "@/api/customer";
63
+ import { message } from "@/utils/message";
64
+ import { checkReeorPosition } from "@/utils/utils";
142
65
 
143
- defineOptions({ name: "XxxForm" });
66
+ defineOptions({ name: "CustomerForm" });
144
67
 
145
- const route = useRoute();
146
- const router = useRouter();
147
68
  const formRef = ref<FormInstance>();
148
- const loading = ref(false);
149
-
150
- const formData = reactive<XxxForm>({
151
- name: "",
69
+ const submitLoading = ref(false);
70
+ const formData = reactive<Customer>({
71
+ id: "",
152
72
  code: "",
153
- status: 1,
154
- remark: "",
155
- });
156
-
157
- const rules = reactive<FormRules<XxxForm>>({
158
- name: [{ required: true, message: "请输入名称", trigger: "blur" }],
159
- code: [{ required: true, message: "请输入编码", trigger: "blur" }],
160
- status: [{ required: true, message: "请选择状态", trigger: "change" }],
73
+ name: "",
161
74
  });
162
75
 
163
- // 编辑模式加载数据
164
- onMounted(async () => {
165
- const id = route.query.id as string;
166
- if (id) {
167
- const { data } = await getXxxDetailApi(id);
168
- Object.assign(formData, data);
169
- }
170
- });
76
+ const rules: FormRules<Customer> = {
77
+ code: [{ required: true, message: $t("请输入编号"), trigger: "blur" }],
78
+ name: [{ required: true, message: $t("请输入名称"), trigger: "blur" }],
79
+ };
171
80
 
172
- // 提交
173
- async function handleSubmit() {
81
+ async function onSubmit() {
174
82
  const valid = await formRef.value?.validate().catch(() => false);
175
- if (!valid) return;
176
-
177
- loading.value = true;
83
+ if (!valid) {
84
+ checkReeorPosition();
85
+ return;
86
+ }
87
+ submitLoading.value = true;
178
88
  try {
179
- await saveXxxApi(formData);
180
- ElMessage.success("保存成功");
181
- router.back();
89
+ const res = await saveCustomer(formData);
90
+ if (res.status === 200) {
91
+ message($t("保存成功"), { type: "success" });
92
+ } else {
93
+ message(res.statusText || $t("保存失败"), { type: "error" });
94
+ }
182
95
  } finally {
183
- loading.value = false;
96
+ submitLoading.value = false;
184
97
  }
185
98
  }
186
-
187
- // 重置
188
- function handleReset() {
189
- formRef.value?.resetFields();
190
- }
191
99
  </script>
192
100
 
193
101
  <template>
194
- <div class="re-xxx-form">
195
- <el-form
196
- ref="formRef"
197
- :model="formData"
198
- :rules="rules"
199
- label-width="100px"
200
- >
201
- <el-form-item label="名称" prop="name">
202
- <el-input v-model="formData.name" placeholder="请输入" />
203
- </el-form-item>
204
- <el-form-item label="编码" prop="code">
205
- <el-input v-model="formData.code" placeholder="请输入" />
206
- </el-form-item>
207
- <el-form-item label="状态" prop="status">
208
- <el-radio-group v-model="formData.status">
209
- <el-radio :value="1">启用</el-radio>
210
- <el-radio :value="0">禁用</el-radio>
211
- </el-radio-group>
212
- </el-form-item>
213
- <el-form-item label="备注" prop="remark">
214
- <el-input v-model="formData.remark" type="textarea" :rows="3" />
215
- </el-form-item>
216
- <el-form-item>
217
- <el-button type="primary" :loading="loading" @click="handleSubmit">
218
- 保存
219
- </el-button>
220
- <el-button @click="handleReset">重置</el-button>
221
- </el-form-item>
222
- </el-form>
223
- </div>
102
+ <el-form ref="formRef" :model="formData" :rules="rules" label-position="top">
103
+ <el-form-item :label="$t('编号')" prop="code">
104
+ <el-input v-model="formData.code" />
105
+ </el-form-item>
106
+ <el-form-item :label="$t('名称')" prop="name">
107
+ <el-input v-model="formData.name" />
108
+ </el-form-item>
109
+ <el-button type="primary" :loading="submitLoading" @click="onSubmit">
110
+ {{ $t("提交") }}
111
+ </el-button>
112
+ </el-form>
224
113
  </template>
225
114
  ```
226
115
 
227
116
  ### 表单约定
228
117
 
229
- 1. 表单数据使用 `reactive()` 定义,配合泛型 `FormRules<T>` 获得类型校验。
230
- 2. 校验规则使用 `FormRules<T>` 类型,确保 rule 的 `field` 与表单字段对应。
231
- 3. 提交前必须调用 `formRef.validate()`,不要跳过校验直接提交。
232
- 4. 编辑模式通过 `route.query.id` 判断,有 id 则加载详情并回填。
233
- 5. 重置使用 `formRef.resetFields()`,不要手动清空字段。
234
- 6. 提交按钮使用 `loading` 状态防止重复提交。
118
+ 1. `reactive` + `FormRules` + `formRef.validate()`
119
+ 2. 提交按钮必须有 **loading** 防重复
120
+ 3. 编辑回填:`Object.assign(formData, detail)`(见 `data-writeback`)
121
+ 4. 远程下拉:`ReSelectQuery`(`url` + `value` + `label`,回写用 `edit-data`),见 `form-advanced`
235
122
 
236
- ## 详情/编辑页
237
-
238
- ### 页面跳转传参
123
+ ## 路由片段
239
124
 
240
125
  ```typescript
241
- // 列表页跳转到详情页
242
- function handleView(row: XxxListItem) {
243
- router.push({
244
- path: "/xxx/detail",
245
- query: { id: row.id },
246
- });
247
- }
248
-
249
- // 列表页跳转到编辑页
250
- function handleEdit(row: XxxListItem) {
251
- router.push({
252
- path: "/xxx/edit",
253
- query: { id: row.id },
254
- });
255
- }
256
-
257
- // 列表页跳转到新增页
258
- function handleAdd() {
259
- router.push({ path: "/xxx/add" });
126
+ {
127
+ path: "/customer/list",
128
+ name: "CustomerList",
129
+ component: () => import("@/views/customer/list/index.vue"),
130
+ meta: { title: "客户列表", roles: ["admin", "common"] },
260
131
  }
261
132
  ```
262
133
 
263
- ### 详情页数据回显
264
-
265
- ```typescript
266
- // 编辑页加载数据并回填
267
- onMounted(async () => {
268
- const id = route.query.id as string;
269
- if (!id) return;
270
-
271
- loading.value = true;
272
- try {
273
- const { data } = await getXxxDetailApi(id);
274
- // 使用 Object.assign 保持 reactive 响应性
275
- Object.assign(formData, data);
276
- } finally {
277
- loading.value = false;
278
- }
279
- });
280
- ```
281
-
282
- ### 多表单校验
283
-
284
- 当一个页面有多个表单区域时,需要统一校验:
285
-
286
- ```typescript
287
- async function handleSubmitAll() {
288
- const results = await Promise.all([
289
- formRef1.value?.validate().catch(() => false),
290
- formRef2.value?.validate().catch(() => false),
291
- formRef3.value?.validate().catch(() => false),
292
- ]);
293
-
294
- if (results.some(r => r === false)) {
295
- ElMessage.warning("请完善表单信息");
296
- return;
297
- }
298
-
299
- // 合并数据提交
300
- const submitData = {
301
- ...formData1,
302
- ...formData2,
303
- ...formData3,
304
- };
305
- await saveApi(submitData);
306
- }
307
- ```
134
+ 禁止业务模块再挂 Layout。
308
135
 
309
136
  ## 常见反例
310
137
 
311
- - 不使用 `useTableSearch`,手动管理 loading / pagination / content。
312
- - 表单使用 `ref({})` 而非 `reactive({})` 定义。
313
- - 提交时不调用 `validate()`,或调用后不处理 reject。
314
- - 编辑页通过 `router.params` 传参(应使用 `query`)。
315
- - 重置表单时手动清空字段而不使用 `resetFields()`。
316
- - 操作列直接写 `el-button` 而不使用 `ReTableOperate`。
138
+ - 使用 `handleSearch` / `handleReset`(错误命名)
139
+ - 不用 `useTableSearch` 手写一套分页 loading
140
+ - 提交无 loading
141
+ - 业务路由二次 Layout
142
+ - ReSelectQuery 写成不存在的 `:api` / `label-key` API(真实 props 见 form-advanced)