@qilitt-mickey/vue3-temp-skill 1.0.8

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 (37) hide show
  1. package/README.md +229 -0
  2. package/SKILL.md +621 -0
  3. package/bin/cli.js +579 -0
  4. package/package.json +46 -0
  5. package/references/advanced-ui.md +302 -0
  6. package/references/api-check.md +272 -0
  7. package/references/base-code-dict.md +48 -0
  8. package/references/build-optim.md +282 -0
  9. package/references/code-quality.md +235 -0
  10. package/references/crud-pages.md +316 -0
  11. package/references/data-compare.md +501 -0
  12. package/references/data-mapping.md +213 -0
  13. package/references/data-screen.md +79 -0
  14. package/references/data-writeback.md +104 -0
  15. package/references/detail-page.md +99 -0
  16. package/references/directives-advanced.md +93 -0
  17. package/references/download-export.md +68 -0
  18. package/references/feedback-loading.md +60 -0
  19. package/references/feedback-ui.md +111 -0
  20. package/references/file-management.md +132 -0
  21. package/references/flowchart-g6.md +244 -0
  22. package/references/form-advanced.md +137 -0
  23. package/references/graph-relation.md +253 -0
  24. package/references/http-api.md +188 -0
  25. package/references/layout-theme.md +540 -0
  26. package/references/mobile-h5.md +271 -0
  27. package/references/permission-auth.md +235 -0
  28. package/references/project-inventory.md +326 -0
  29. package/references/qrcode-barcode.md +92 -0
  30. package/references/rich-text.md +73 -0
  31. package/references/seamless-scroll.md +38 -0
  32. package/references/tree-table.md +111 -0
  33. package/references/ui-components.md +161 -0
  34. package/references/verify-captcha.md +96 -0
  35. package/references/vue-core.md +209 -0
  36. package/references/websocket-realtime.md +176 -0
  37. package/references/workflow-bpmn.md +206 -0
@@ -0,0 +1,316 @@
1
+ ---
2
+ skill: crud-pages
3
+ description: 规范查询列表页、表单页、详情/编辑页的标准开发流程,包含 useTableSearch Hook、表单校验、数据回显等核心模式。开发 CRUD 业务页面时调用。
4
+ scope: project
5
+ tags: [vue3, crud, list, form, detail, table, useTableSearch, page]
6
+ ---
7
+
8
+ # CRUD 页面开发规范
9
+
10
+ ## 查询列表页
11
+
12
+ ### 标准结构
13
+
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" });
23
+
24
+ // 查询表单
25
+ const formQuery = reactive<XxxQueryForm>({
26
+ keyword: "",
27
+ status: undefined,
28
+ dateRange: [],
29
+ });
30
+
31
+ // useTableSearch 返回表格相关的所有状态和方法
32
+ const {
33
+ content, // 表格数据
34
+ pagination, // 分页信息
35
+ loading, // 加载状态
36
+ handleSearch, // 查询
37
+ handleReset, // 重置
38
+ handlePageChange, // 翻页
39
+ handleSizeChange, // 切换每页条数
40
+ } = useTableSearch(formQuery, getXxxListApi);
41
+
42
+ // 初始化加载
43
+ handleSearch();
44
+ </script>
45
+
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>
64
+
65
+ <!-- 工具栏 -->
66
+ <ReTableBar>
67
+ <template #left>
68
+ <el-button type="primary" @click="handleAdd">新增</el-button>
69
+ </template>
70
+ </ReTableBar>
71
+
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
+ ```
102
+
103
+ ### useTableSearch 参数说明
104
+
105
+ ```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;
121
+ }
122
+ ```
123
+
124
+ ### 列表页约定
125
+
126
+ 1. 查询表单使用 `reactive()` 定义,不使用 `ref()`。
127
+ 2. 表格数据、分页、Loading 统一由 `useTableSearch` 管理,不要手动维护。
128
+ 3. 操作列使用 `ReTableOperate` 组件渲染按钮组。
129
+ 4. 工具栏使用 `ReTableBar` 组件包裹。
130
+ 5. 表格使用 `ReGrid` 组件(内置分页联动)。
131
+ 6. 删除操作必须弹出确认框。
132
+
133
+ ## 表单页
134
+
135
+ ### 新增/编辑表单
136
+
137
+ ```vue
138
+ <script setup lang="ts">
139
+ import type { FormInstance, FormRules } from "element-plus";
140
+ import { getXxxDetailApi, saveXxxApi } from "@/api/xxx";
141
+ import type { XxxForm } from "@/api/types/xxx";
142
+
143
+ defineOptions({ name: "XxxForm" });
144
+
145
+ const route = useRoute();
146
+ const router = useRouter();
147
+ const formRef = ref<FormInstance>();
148
+ const loading = ref(false);
149
+
150
+ const formData = reactive<XxxForm>({
151
+ name: "",
152
+ 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" }],
161
+ });
162
+
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
+ });
171
+
172
+ // 提交
173
+ async function handleSubmit() {
174
+ const valid = await formRef.value?.validate().catch(() => false);
175
+ if (!valid) return;
176
+
177
+ loading.value = true;
178
+ try {
179
+ await saveXxxApi(formData);
180
+ ElMessage.success("保存成功");
181
+ router.back();
182
+ } finally {
183
+ loading.value = false;
184
+ }
185
+ }
186
+
187
+ // 重置
188
+ function handleReset() {
189
+ formRef.value?.resetFields();
190
+ }
191
+ </script>
192
+
193
+ <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>
224
+ </template>
225
+ ```
226
+
227
+ ### 表单约定
228
+
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` 状态防止重复提交。
235
+
236
+ ## 详情/编辑页
237
+
238
+ ### 页面跳转传参
239
+
240
+ ```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" });
260
+ }
261
+ ```
262
+
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
+ ```
308
+
309
+ ## 常见反例
310
+
311
+ - 不使用 `useTableSearch`,手动管理 loading / pagination / content。
312
+ - 表单使用 `ref({})` 而非 `reactive({})` 定义。
313
+ - 提交时不调用 `validate()`,或调用后不处理 reject。
314
+ - 编辑页通过 `router.params` 传参(应使用 `query`)。
315
+ - 重置表单时手动清空字段而不使用 `resetFields()`。
316
+ - 操作列直接写 `el-button` 而不使用 `ReTableOperate`。