@agile-team/wl-skills-kit 2.6.0 → 2.7.1
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/CHANGELOG.md +46 -0
- package/README.md +16 -1
- package/bin/wl-skills.js +48 -2
- package/docs/agent-pipeline-runbook.md +1 -1
- package/docs/ai/345/205/250/346/231/257/345/210/206/346/236/220.md +1 -1
- package/docs/mcp-tool-risk-matrix.md +2 -1
- package/docs//345/205/250/347/233/230/345/210/206/346/236/220/344/270/216/346/231/272/350/203/275/344/275/223/346/220/255/345/273/272/346/214/207/345/215/227.md +1 -1
- package/files/.github/copilot-instructions.md +1 -12
- package/files/.github/guides/architecture.md +1 -1
- package/files/.github/skills/ops/code-fix/USAGE.md +131 -0
- package/files/.github/skills/sync/dict-sync/USAGE.md +122 -0
- package/files/docs/jh-date-range.md +40 -8
- package/files/docs/jh-date.md +45 -18
- package/files/docs/jh-dept-picker.md +17 -0
- package/files/docs/jh-drag-row.md +44 -21
- package/files/docs/jh-file-upload.md +43 -0
- package/files/docs/jh-select.md +51 -14
- package/files/docs/jh-text.md +29 -11
- package/files/docs/jh-textarea.md +159 -0
- package/files/docs/jh-user-picker.md +17 -0
- package/files/docs/page-query-hook-best-practices.md +431 -362
- package/mcp/registry.js +368 -0
- package/mcp/server.js +65 -432
- package/package.json +9 -5
|
@@ -1,362 +1,431 @@
|
|
|
1
|
-
# 页面查询 Hook 最佳实践
|
|
2
|
-
|
|
3
|
-
本文档介绍如何使用 `AbstractPageQueryHook` 基类进行页面配置化开发,无需维护独立的 API 层。
|
|
4
|
-
|
|
5
|
-
## 核心理念
|
|
6
|
-
|
|
7
|
-
**配置化驱动**:通过在 `data.ts` 中配置 `API_CONFIG` 直接调用基类内置的 HTTP 方法,实现"零 API 层"开发模式。
|
|
8
|
-
|
|
9
|
-
## 基类概述
|
|
10
|
-
|
|
11
|
-
`AbstractPageQueryHook` 来自 `@jhlc/common-core
|
|
12
|
-
|
|
13
|
-
```typescript
|
|
14
|
-
import { AbstractPageQueryHook } from "@jhlc/common-core";
|
|
15
|
-
|
|
16
|
-
class
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
###
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
{
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
###
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
);
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
1
|
+
# 页面查询 Hook 最佳实践
|
|
2
|
+
|
|
3
|
+
本文档介绍如何使用 `AbstractPageQueryHook` 基类进行页面配置化开发,无需维护独立的 API 层。
|
|
4
|
+
|
|
5
|
+
## 核心理念
|
|
6
|
+
|
|
7
|
+
**配置化驱动**:通过在 `data.ts` 中配置 `API_CONFIG` 直接调用基类内置的 HTTP 方法,实现"零 API 层"开发模式。
|
|
8
|
+
|
|
9
|
+
## 基类概述
|
|
10
|
+
|
|
11
|
+
`AbstractPageQueryHook` 来自 `@jhlc/common-core`,提供完整的分页查询 + CRUD 操作:
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { AbstractPageQueryHook } from "@jhlc/common-core";
|
|
15
|
+
|
|
16
|
+
class MyPageHook extends AbstractPageQueryHook {
|
|
17
|
+
constructor() {
|
|
18
|
+
super({
|
|
19
|
+
url: { list: "/api/list", remove: "/api/remove" },
|
|
20
|
+
// page 默认值: { size: 20, current: 1 }
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// 必须实现以下三个抽象方法:
|
|
25
|
+
queryDef() { return []; } // 查询条件 BaseQueryItemDesc[]
|
|
26
|
+
columnsDef() { return []; } // 表格列 TableColumnDesc[]
|
|
27
|
+
toolbarDef() { return []; } // 工具栏 ActionButtonDesc[]
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 源码构造参数(UsePageQueryConstructorParam)
|
|
32
|
+
|
|
33
|
+
| 参数 | 说明 | 类型 | 默认值 |
|
|
34
|
+
| ---------------- | -------------------------- | ----------------------------------- | ---------------- |
|
|
35
|
+
| url | API 地址(list 必填) | `{ list, remove?, save?, update? }` | - |
|
|
36
|
+
| page | 分页初始值 | `ViewPage` | `{size:20, current:1}` |
|
|
37
|
+
| showSearchForm | 是否显示搜索表单 | `boolean` | - |
|
|
38
|
+
| constQueryParam | 查询条件常量(每次请求自动附加) | `() => Record<string, any>` | - |
|
|
39
|
+
| queryParam | 查询条件初始值 | `Q` | `{}` |
|
|
40
|
+
| requestMethod | 请求方式 | `RequestMethod` | `RequestMethod.get` |
|
|
41
|
+
|
|
42
|
+
### 核心属性(均为响应式 Ref)
|
|
43
|
+
|
|
44
|
+
| 属性 | 类型 | 说明 |
|
|
45
|
+
| ---------- | -------------------- | -------------- |
|
|
46
|
+
| list | `Ref<T[]>` | 数据列表 |
|
|
47
|
+
| page | `Ref<ViewPage>` | 分页对象 |
|
|
48
|
+
| queryParam | `Ref<Q>` | 查询条件对象 |
|
|
49
|
+
| columns | `TableColumnDesc[]` | 表格列(由 columnsDef 计算) |
|
|
50
|
+
| queryItems | `BaseQueryItemDesc[]`| 查询项(由 queryDef 计算) |
|
|
51
|
+
| toolbars | `ActionButtonDesc[]` | 工具栏(由 toolbarDef 计算) |
|
|
52
|
+
| tableRef | `Ref` | 表格组件引用 |
|
|
53
|
+
| summaryRow | `Ref<T>` | 汇总行 |
|
|
54
|
+
|
|
55
|
+
### 内置 HTTP 方法说明
|
|
56
|
+
|
|
57
|
+
所有内置方法的详细用法、参数说明、实战示例请参考:[request.md - AbstractPageQueryHook 基类内置方法](./request.md#abstractpagequeryhook-基类内置方法)
|
|
58
|
+
|
|
59
|
+
### 关键方法一览(源码)
|
|
60
|
+
|
|
61
|
+
| 方法 | 说明 |
|
|
62
|
+
| --------------------------------- | -------------------------------------- |
|
|
63
|
+
| `select()` | 执行查询(自动读取 page + queryParam) |
|
|
64
|
+
| `remove(id, index?, msg?)` | 删除(带确认弹窗,删后自动刷新) |
|
|
65
|
+
| `resetQuery()` | 重置查询条件并重新查询 |
|
|
66
|
+
| `saveBatch()` | 批量保存(自动区分新增/更新) |
|
|
67
|
+
| `getSelection()` | 获取表格选中行 |
|
|
68
|
+
| `exportExcel(option)` | 导出 Excel |
|
|
69
|
+
| `actionBatch(action, url, tip, ids?, auto?)` | 批量操作统一封装 |
|
|
70
|
+
| `postBatch(url, tip, ids?)` | 批量 POST |
|
|
71
|
+
| `putBatch(url, tip, ids?)` | 批量 PUT |
|
|
72
|
+
| `deleteBatch(url, tip, ids)` | 批量 DELETE |
|
|
73
|
+
| `unshiftEditRow()` | 在列表顶部插入可编辑行 |
|
|
74
|
+
| `openEdit(row)` | 开启行内编辑 |
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 快速开始
|
|
79
|
+
|
|
80
|
+
### 步骤 1:定义 API 配置
|
|
81
|
+
|
|
82
|
+
在 `data.ts` 中直接配置接口路径:
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// data.ts
|
|
86
|
+
import { AbstractPageQueryHook } from "@jhlc/common-core";
|
|
87
|
+
|
|
88
|
+
const API_CONFIG = {
|
|
89
|
+
list: "/mmsm/mmsmRsltLadleUse/list",
|
|
90
|
+
get: "/mmsm/mmsmRsltLadleUse/getById",
|
|
91
|
+
save: "/mmsm/mmsmRsltLadleUse/save",
|
|
92
|
+
update: "/mmsm/mmsmRsltLadleUse/update",
|
|
93
|
+
remove: "/mmsm/mmsmRsltLadleUse/remove"
|
|
94
|
+
};
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 步骤 2:实现抽象方法 + 编写 CRUD
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import { BusLogicDataType } from "@jhlc/types/src/logical-data";
|
|
101
|
+
|
|
102
|
+
class LadleUseQueryHook extends AbstractPageQueryHook {
|
|
103
|
+
constructor() {
|
|
104
|
+
super({ url: { list: API_CONFIG.list, remove: API_CONFIG.remove } });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 查询条件定义
|
|
108
|
+
queryDef() {
|
|
109
|
+
return [
|
|
110
|
+
{ name: "ladleNum", label: "钢包号", placeholder: "请输入钢包号" },
|
|
111
|
+
{ name: "useDate", label: "使用日期", logicType: BusLogicDataType.date },
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// 表格列定义
|
|
116
|
+
columnsDef() {
|
|
117
|
+
return [
|
|
118
|
+
{ name: "ladleNum", label: "钢包号", width: 120 },
|
|
119
|
+
{ name: "furnaceId", label: "炉号", width: 100 },
|
|
120
|
+
];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 工具栏定义
|
|
124
|
+
toolbarDef() {
|
|
125
|
+
return [
|
|
126
|
+
{ name: "add", onClick: () => this.handleAdd() },
|
|
127
|
+
{ name: "removeBatch", onClick: () => this.handleBatchDelete() },
|
|
128
|
+
];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 新增
|
|
132
|
+
async handleAdd(row?: any) {
|
|
133
|
+
await this.postAction(API_CONFIG.save, row);
|
|
134
|
+
this.select(); // 刷新列表
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// 编辑
|
|
138
|
+
async handleEdit(row: any) {
|
|
139
|
+
await this.putAction(API_CONFIG.update, row);
|
|
140
|
+
this.select();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// 单个删除(内置 remove 已有确认弹窗,直接调用即可)
|
|
144
|
+
handleDelete(row: any) {
|
|
145
|
+
this.remove(row.id);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// 批量删除
|
|
149
|
+
async handleBatchDelete() {
|
|
150
|
+
await this.deleteBatch(API_CONFIG.remove, "确定批量删除?", this.getSelection().map(i => i.id));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
> **注意**: 源码中刷新列表的方法是 `this.select()`,不是 `getTableList()`
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 标准页面配置示例
|
|
160
|
+
|
|
161
|
+
完整的 `data.ts` 配置示例:
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
import { AbstractPageQueryHook } from "@jhlc/common-core";
|
|
165
|
+
import { BusLogicDataType } from "@jhlc/types/src/logical-data";
|
|
166
|
+
import { BaseQueryItemDesc } from "@jhlc/common-core/src/components/form/base-query/type";
|
|
167
|
+
import { TableColumnDesc } from "@jhlc/common-core/src/components/table/base-table/type";
|
|
168
|
+
import { ActionButtonDesc } from "@jhlc/common-core/src/components/toolbar/type";
|
|
169
|
+
|
|
170
|
+
const API_CONFIG = {
|
|
171
|
+
list: "/mmsm/mmsmRsltLadleUse/list",
|
|
172
|
+
get: "/mmsm/mmsmRsltLadleUse/getById",
|
|
173
|
+
save: "/mmsm/mmsmRsltLadleUse/save",
|
|
174
|
+
update: "/mmsm/mmsmRsltLadleUse/update",
|
|
175
|
+
remove: "/mmsm/mmsmRsltLadleUse/remove",
|
|
176
|
+
exportExcel: "/mmsm/mmsmRsltLadleUse/export"
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export class LadleUseQueryHook extends AbstractPageQueryHook {
|
|
180
|
+
constructor() {
|
|
181
|
+
super({
|
|
182
|
+
url: { list: API_CONFIG.list, remove: API_CONFIG.remove },
|
|
183
|
+
// page 默认 { size: 20, current: 1 }
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// 查询条件定义(抽象方法必须实现)
|
|
188
|
+
queryDef(): BaseQueryItemDesc[] {
|
|
189
|
+
return [
|
|
190
|
+
{
|
|
191
|
+
name: "ladleNum",
|
|
192
|
+
label: "钢包号",
|
|
193
|
+
placeholder: "请输入钢包号",
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
name: "useDate",
|
|
197
|
+
startName: "useDateStart",
|
|
198
|
+
endName: "useDateEnd",
|
|
199
|
+
label: "使用日期",
|
|
200
|
+
defaultValue: "recentDay7",
|
|
201
|
+
component: () => ({ tag: "jh-date", type: "daterange" }),
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
name: "status",
|
|
205
|
+
label: "状态",
|
|
206
|
+
logicType: BusLogicDataType.dict,
|
|
207
|
+
logicValue: "ladle_status",
|
|
208
|
+
},
|
|
209
|
+
];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// 表格列定义(抽象方法必须实现)
|
|
213
|
+
columnsDef(): TableColumnDesc[] {
|
|
214
|
+
return [
|
|
215
|
+
{ type: "selection", width: 50 },
|
|
216
|
+
{ name: "ladleNum", label: "钢包号", width: 120 },
|
|
217
|
+
{ name: "furnaceId", label: "炉号", width: 100 },
|
|
218
|
+
{
|
|
219
|
+
name: "useDate", label: "使用日期", width: 120,
|
|
220
|
+
logicType: BusLogicDataType.date,
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
name: "status", label: "状态", width: 100,
|
|
224
|
+
logicType: BusLogicDataType.dict,
|
|
225
|
+
logicValue: "ladle_status",
|
|
226
|
+
},
|
|
227
|
+
];
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// 工具栏定义(抽象方法必须实现)
|
|
231
|
+
toolbarDef(): ActionButtonDesc[] {
|
|
232
|
+
return [
|
|
233
|
+
{ name: "add", onClick: () => this.handleAdd() },
|
|
234
|
+
{ name: "removeBatch", onClick: () => this.handleBatchDelete() },
|
|
235
|
+
{
|
|
236
|
+
name: "export", label: "导出",
|
|
237
|
+
onClick: () => this.exportExcel({
|
|
238
|
+
filename: "钢包使用记录.xlsx",
|
|
239
|
+
}),
|
|
240
|
+
},
|
|
241
|
+
];
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ========== CRUD 操作 ==========
|
|
245
|
+
|
|
246
|
+
// 新增
|
|
247
|
+
async handleAdd(row?: any) {
|
|
248
|
+
const res = await this.postAction(API_CONFIG.save, row);
|
|
249
|
+
if (res.success) {
|
|
250
|
+
this.msgSuccess("新增成功");
|
|
251
|
+
this.select(); // 刷新列表
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// 编辑
|
|
256
|
+
async handleEdit(row: any) {
|
|
257
|
+
const res = await this.putAction(API_CONFIG.update, row);
|
|
258
|
+
if (res.success) {
|
|
259
|
+
this.msgSuccess("更新成功");
|
|
260
|
+
this.select();
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// 删除(内置 remove 已带确认弹窗)
|
|
265
|
+
handleDelete(row: any) {
|
|
266
|
+
this.remove(row.id);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// 批量删除
|
|
270
|
+
async handleBatchDelete() {
|
|
271
|
+
const ids = this.getSelection().map(i => i.id);
|
|
272
|
+
await this.deleteBatch(API_CONFIG.remove, "确定批量删除?", ids);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// 详情查询
|
|
276
|
+
async getDetail(id: string) {
|
|
277
|
+
return await this.getAction(API_CONFIG.get, { id });
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export const createPage = () => new LadleUseQueryHook();
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## 何时需要独立的 API 层?
|
|
287
|
+
|
|
288
|
+
在以下场景中,建议创建独立的 `api/` 文件:
|
|
289
|
+
|
|
290
|
+
### ✅ 需要独立 API 层的场景
|
|
291
|
+
|
|
292
|
+
1. **复杂的数据转换**
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
// api/complex-data.ts
|
|
296
|
+
export async function fetchComplexData(params: any) {
|
|
297
|
+
const res = await request.post("/api/data", params);
|
|
298
|
+
// 复杂的数据转换逻辑
|
|
299
|
+
return transformData(res.data);
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
2. **多个页面共享同一接口**
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
// api/common.ts
|
|
307
|
+
export const CommonAPI = {
|
|
308
|
+
getDictData: (type: string) => request.get(`/dict/${type}`),
|
|
309
|
+
uploadFile: (file: File) => request.upload("/upload", file)
|
|
310
|
+
};
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
3. **需要组合多个接口调用**
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
// api/batch-operations.ts
|
|
317
|
+
export async function batchProcess(ids: string[]) {
|
|
318
|
+
const details = await Promise.all(
|
|
319
|
+
ids.map((id) => request.get(`/detail/${id}`))
|
|
320
|
+
);
|
|
321
|
+
return await request.post("/batch", { data: details });
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
4. **特殊的请求拦截或错误处理**
|
|
326
|
+
```typescript
|
|
327
|
+
// api/special-request.ts
|
|
328
|
+
export async function sensitiveOperation(data: any) {
|
|
329
|
+
return await request.post("/sensitive", data, {
|
|
330
|
+
headers: { "X-Custom-Token": getSpecialToken() }
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### ⛔ 不需要独立 API 层的场景
|
|
336
|
+
|
|
337
|
+
1. **标准 CRUD 操作** - 直接使用基类方法
|
|
338
|
+
2. **简单的列表查询** - 配置 `API_CONFIG.list`
|
|
339
|
+
3. **单页面独享的接口** - 写在 `data.ts` 的 `API_CONFIG` 中
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## 完整的方法参考
|
|
344
|
+
|
|
345
|
+
所有基类方法的详细文档请参考:
|
|
346
|
+
|
|
347
|
+
- [request.md - AbstractPageQueryHook 基类内置方法](./request.md#abstractpagequeryhook-基类内置方法)
|
|
348
|
+
- 方法签名
|
|
349
|
+
- 参数说明
|
|
350
|
+
- 实战示例
|
|
351
|
+
- 常见错误与解决方案
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## 常见问题
|
|
356
|
+
|
|
357
|
+
### 1. 删除操作应优先使用内置 `remove()`
|
|
358
|
+
|
|
359
|
+
源码内置的 `this.remove(id)` 已包含确认弹窗 + 成功提示 + 自动刷新,无需自己封装:
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
// ✅ 推荐:直接使用内置 remove
|
|
363
|
+
this.remove(row.id);
|
|
364
|
+
|
|
365
|
+
// ❌ 不推荐:自己封装确认弹窗 + deleteAction
|
|
366
|
+
await this.deleteAction(API_CONFIG.remove, { id: row.id });
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### 2. `actionBatch` / `deleteBatch` 如何使用?
|
|
370
|
+
|
|
371
|
+
批量操作的统一封装,自动处理确认、提示、刷新:
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
// 批量删除(源码内置自动调用 select() 刷新)
|
|
375
|
+
await this.deleteBatch(API_CONFIG.remove, "确定批量删除?", ids);
|
|
376
|
+
|
|
377
|
+
// 批量确认
|
|
378
|
+
await this.postBatch(API_CONFIG.confirm, "确定批量确认?", ids);
|
|
379
|
+
|
|
380
|
+
// 自定义批量操作
|
|
381
|
+
await this.actionBatch(
|
|
382
|
+
this.deleteAction, // 要执行的 HTTP 方法
|
|
383
|
+
API_CONFIG.remove, // 接口 URL
|
|
384
|
+
"确定删除?", // 确认提示文本
|
|
385
|
+
ids, // ID 列表(可省略,默认取选中行)
|
|
386
|
+
true // 是否自动显示成功提示
|
|
387
|
+
);
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### 3. 如何自定义请求头?
|
|
391
|
+
|
|
392
|
+
所有内置方法都支持 `headers` 参数:
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
await this.postAction(API_CONFIG.save, row, {}, { "X-Custom-Header": "value" });
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### 4. 如何处理文件上传?
|
|
399
|
+
|
|
400
|
+
使用 `postAction` 配合 `FormData`:
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
async handleUpload(file: File) {
|
|
404
|
+
const formData = new FormData()
|
|
405
|
+
formData.append('file', file)
|
|
406
|
+
|
|
407
|
+
const res = await this.postAction('/upload', formData, {}, {
|
|
408
|
+
'Content-Type': 'multipart/form-data'
|
|
409
|
+
})
|
|
410
|
+
|
|
411
|
+
return res
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
---
|
|
416
|
+
|
|
417
|
+
## 最佳实践总结
|
|
418
|
+
|
|
419
|
+
1. **简单 CRUD**:直接在 `data.ts` 配置 `API_CONFIG` + 基类方法
|
|
420
|
+
2. **复杂逻辑**:创建独立 `api/` 文件进行封装
|
|
421
|
+
3. **查阅方法**:所有 HTTP 方法详细说明见 [request.md](./request.md)
|
|
422
|
+
4. **刷新列表**:统一使用 `this.select()`,不是 `getTableList()`
|
|
423
|
+
5. **删除操作**:单个用 `this.remove(id)`,批量用 `this.deleteBatch(url, tip, ids)`
|
|
424
|
+
6. **抽象方法**:必须实现 `queryDef()`、`columnsDef()`、`toolbarDef()`
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
**相关文档**:
|
|
429
|
+
|
|
430
|
+
- [request.md - HTTP 请求方法完整文档](./request.md)
|
|
431
|
+
- [request.md - AbstractPageQueryHook 基类内置方法](./request.md#abstractpagequeryhook-基类内置方法)
|