@agile-team/wl-skills-kit 2.4.2 → 2.5.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 +44 -1
- package/README.md +68 -45
- package/bin/wl-skills.js +196 -12
- package/files/.github/copilot-instructions.md +361 -322
- package/files/.github/guides/architecture.md +1 -1
- package/files/.github/guides/usage.md +32 -10
- package/files/.github/skills/_registry.md +18 -16
- package/files/.github/skills/core/page-codegen/SKILL.md +200 -97
- package/files/.github/skills/core/page-codegen/USAGE.md +33 -12
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-DETAIL-TABS.md +80 -48
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-FORM-ROUTE.md +183 -55
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-LIST.md +110 -21
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-MASTER-DETAIL.md +29 -9
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-RECORD-FORM.md +93 -48
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-TREE-LIST.md +49 -29
- package/files/.github/skills/core/prototype-scan/SKILL.md +16 -0
- package/files/.github/skills/sync/menu-sync/SKILL.md +27 -13
- package/mcp/server.js +279 -195
- package/mcp/tools/menuSync.js +416 -96
- package/mcp/tools/projectTools.js +336 -124
- package/package.json +5 -2
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
> 见 SKILL.md 主文件(约束 + 按钮规则 + Mock 规范等共用规则)。
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
> 复杂表单(多 Tab、多子表、独立布局)使用独立路由而非弹窗。
|
|
7
6
|
> 表单页 `data.ts` **不继承 `AbstractPageQueryHook`**,改为导出 `useXxx` Composable。
|
|
8
7
|
> 需在 `pages.ts` 单独注册路由,路径规则见"FORM_ROUTE 表单页"章节。
|
|
@@ -74,7 +73,9 @@ export function use[PageName]Form(tabsRef: any) {
|
|
|
74
73
|
<div class="page-header">
|
|
75
74
|
<span class="page-title">[页面标题]</span>
|
|
76
75
|
<span class="page-tag page-tag--add">新增</span>
|
|
77
|
-
<el-checkbox v-model="onlyRequired" class="only-required-check"
|
|
76
|
+
<el-checkbox v-model="onlyRequired" class="only-required-check"
|
|
77
|
+
>只看必填项</el-checkbox
|
|
78
|
+
>
|
|
78
79
|
</div>
|
|
79
80
|
<div class="page-toolbar">
|
|
80
81
|
<el-button type="primary" @click="handleSave">保存</el-button>
|
|
@@ -118,6 +119,10 @@ onMounted(() => {
|
|
|
118
119
|
import { getAction, postAction } from "@jhlc/common-core/src/api/action";
|
|
119
120
|
import { ElMessage, ElMessageBox } from "element-plus";
|
|
120
121
|
import { useRouter } from "vue-router";
|
|
122
|
+
import type { TableColumnDesc } from "@/types/page";
|
|
123
|
+
import { defineColumns, renderOps } from "@agile-team/wk-skills-ui/runtime";
|
|
124
|
+
|
|
125
|
+
export const DETAIL_TABLE_CID = "[pageAbbr]-[base36Timestamp]-sub1";
|
|
121
126
|
|
|
122
127
|
export const API_CONFIG = {
|
|
123
128
|
getById: "/sale/[业务名]/getById",
|
|
@@ -130,6 +135,25 @@ export const OPTS = {
|
|
|
130
135
|
// [字段名]: [{ label: "显示文本", value: "值" }]
|
|
131
136
|
};
|
|
132
137
|
|
|
138
|
+
export function createDetailColumns(removeRecord: (row: any) => void): TableColumnDesc<any>[] {
|
|
139
|
+
return defineColumns([
|
|
140
|
+
{ type: "index", label: "序号", width: 60, align: "center" },
|
|
141
|
+
{ label: "[明细字段]", name: "[detailField]", cid: `${DETAIL_TABLE_CID}-[detailField]`, minWidth: 120 },
|
|
142
|
+
{
|
|
143
|
+
label: "操作",
|
|
144
|
+
name: "_action",
|
|
145
|
+
cid: `${DETAIL_TABLE_CID}-action`,
|
|
146
|
+
width: 100,
|
|
147
|
+
fixed: "right",
|
|
148
|
+
align: "center",
|
|
149
|
+
defaultSlot: ({ row }: any) =>
|
|
150
|
+
renderOps([
|
|
151
|
+
{ type: "del", onClick: () => removeRecord(row) }
|
|
152
|
+
])
|
|
153
|
+
}
|
|
154
|
+
] as any) as TableColumnDesc<any>[];
|
|
155
|
+
}
|
|
156
|
+
|
|
133
157
|
export interface [PageName]Form {
|
|
134
158
|
id: string;
|
|
135
159
|
// ...所有字段
|
|
@@ -148,6 +172,16 @@ export function use[PageName]Detail() {
|
|
|
148
172
|
const loading = ref(false);
|
|
149
173
|
const form = reactive<[PageName]Form>(createMockData());
|
|
150
174
|
|
|
175
|
+
function addRecord() {
|
|
176
|
+
form.[列表字段].push({ id: `temp_${Date.now()}` });
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function removeRecord(row: any) {
|
|
180
|
+
form.[列表字段] = form.[列表字段].filter((item: any) => item.id !== row.id);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const detailColumns = createDetailColumns(removeRecord);
|
|
184
|
+
|
|
151
185
|
async function loadDetail(id: string) {
|
|
152
186
|
loading.value = true;
|
|
153
187
|
try {
|
|
@@ -170,7 +204,7 @@ export function use[PageName]Detail() {
|
|
|
170
204
|
|
|
171
205
|
function handleCancel() { router.back(); }
|
|
172
206
|
|
|
173
|
-
return { loading, form, loadDetail, handleSave, handleCancel };
|
|
207
|
+
return { loading, form, detailColumns, addRecord, loadDetail, handleSave, handleCancel };
|
|
174
208
|
}
|
|
175
209
|
```
|
|
176
210
|
|
|
@@ -182,7 +216,12 @@ export function use[PageName]Detail() {
|
|
|
182
216
|
<!-- 标题栏 -->
|
|
183
217
|
<div class="title-bar">
|
|
184
218
|
<span class="customer-name">{{ form.[标题字段] }}</span>
|
|
185
|
-
<el-tag
|
|
219
|
+
<el-tag
|
|
220
|
+
type="warning"
|
|
221
|
+
effect="plain"
|
|
222
|
+
size="small"
|
|
223
|
+
>{{ form.[状态字段] }}</el-tag
|
|
224
|
+
>
|
|
186
225
|
</div>
|
|
187
226
|
|
|
188
227
|
<!-- 工具栏 -->
|
|
@@ -221,16 +260,13 @@ export function use[PageName]Detail() {
|
|
|
221
260
|
<!-- 子表格 Section(如跟进记录) -->
|
|
222
261
|
<div class="form-section">
|
|
223
262
|
<div class="section-title">[表格标题]</div>
|
|
224
|
-
<
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
</template>
|
|
232
|
-
</el-table-column>
|
|
233
|
-
</el-table>
|
|
263
|
+
<BaseTable
|
|
264
|
+
render-type="agGrid"
|
|
265
|
+
:cid="DETAIL_TABLE_CID"
|
|
266
|
+
:data="form.[列表字段]"
|
|
267
|
+
:columns="detailColumns"
|
|
268
|
+
:height="300"
|
|
269
|
+
/>
|
|
234
270
|
<div class="add-row-btn" @click="addRecord">+ 新增行</div>
|
|
235
271
|
</div>
|
|
236
272
|
</el-form>
|
|
@@ -239,10 +275,18 @@ export function use[PageName]Detail() {
|
|
|
239
275
|
|
|
240
276
|
<script setup lang="ts">
|
|
241
277
|
import { useRoute } from "vue-router";
|
|
242
|
-
import { use[PageName]Detail, OPTS } from "./data";
|
|
278
|
+
import { use[PageName]Detail, OPTS, DETAIL_TABLE_CID } from "./data";
|
|
243
279
|
|
|
244
280
|
const route = useRoute();
|
|
245
|
-
const {
|
|
281
|
+
const {
|
|
282
|
+
loading,
|
|
283
|
+
form,
|
|
284
|
+
detailColumns,
|
|
285
|
+
addRecord,
|
|
286
|
+
loadDetail,
|
|
287
|
+
handleSave,
|
|
288
|
+
handleCancel
|
|
289
|
+
} = use[PageName]Detail();
|
|
246
290
|
|
|
247
291
|
onMounted(() => {
|
|
248
292
|
const id = route.query.id as string;
|
|
@@ -264,15 +308,37 @@ onMounted(() => {
|
|
|
264
308
|
flex-direction: column;
|
|
265
309
|
overflow: hidden;
|
|
266
310
|
|
|
267
|
-
.title-bar
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
.
|
|
271
|
-
|
|
272
|
-
|
|
311
|
+
.title-bar {
|
|
312
|
+
/* 标题 + 状态 Tag,灰色背景 */
|
|
313
|
+
}
|
|
314
|
+
.page-toolbar {
|
|
315
|
+
/* 按钮行,白底,底部边框 */
|
|
316
|
+
}
|
|
317
|
+
.detail-form {
|
|
318
|
+
flex: 1;
|
|
319
|
+
overflow-y: auto;
|
|
320
|
+
padding: 0 16px 16px;
|
|
321
|
+
}
|
|
322
|
+
.header-info {
|
|
323
|
+
padding: 12px 0 4px;
|
|
324
|
+
border-bottom: 1px solid #f0f2f5;
|
|
325
|
+
}
|
|
326
|
+
.form-section {
|
|
327
|
+
margin-top: 16px;
|
|
328
|
+
.section-title {
|
|
329
|
+
border-left: 3px solid var(--el-color-primary);
|
|
330
|
+
padding-left: 10px;
|
|
331
|
+
font-weight: 600;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
.add-row-btn {
|
|
335
|
+
color: #409eff;
|
|
336
|
+
cursor: pointer;
|
|
337
|
+
margin-top: 8px;
|
|
338
|
+
}
|
|
339
|
+
.el-form-item {
|
|
340
|
+
margin-bottom: 10px;
|
|
273
341
|
}
|
|
274
|
-
.add-row-btn { color: #409eff; cursor: pointer; margin-top: 8px; }
|
|
275
|
-
.el-form-item { margin-bottom: 10px; }
|
|
276
342
|
}
|
|
277
343
|
```
|
|
278
344
|
|
|
@@ -284,26 +350,27 @@ onMounted(() => {
|
|
|
284
350
|
> 典型页面:精整实绩(抛丸/倒棱/矫直/酸洗/剥皮/检验/包装)、加热管理(装炉/出炉)、剔钢操作。
|
|
285
351
|
>
|
|
286
352
|
> 项目中已有两种落地方式:
|
|
353
|
+
>
|
|
287
354
|
> - **配置驱动模板组件**:`FinishingAchievementTemplate`(7 个精整页面共用)
|
|
288
355
|
> - **独立页面编排**:`mmwr-heating-management`、`mmwr-steel-stripping-operations`
|
|
289
356
|
|
|
290
357
|
### D-0 核心特征
|
|
291
358
|
|
|
292
|
-
| 特征
|
|
293
|
-
|
|
294
|
-
| **多 AbstractPageQueryHook 实例** | 每个表格区域一个实例,各自管理 `list/page/queryParam/columns`
|
|
295
|
-
| **主从联动**
|
|
296
|
-
| **可拖拽分隔**
|
|
297
|
-
| **Tab 切换**
|
|
298
|
-
| **操作区**
|
|
299
|
-
| **懒加载**
|
|
359
|
+
| 特征 | 说明 |
|
|
360
|
+
| --------------------------------- | --------------------------------------------------------------- |
|
|
361
|
+
| **多 AbstractPageQueryHook 实例** | 每个表格区域一个实例,各自管理 `list/page/queryParam/columns` |
|
|
362
|
+
| **主从联动** | 选中上表行 → 调用下表实例的 `selectByPlan(row)` 驱动查询 |
|
|
363
|
+
| **可拖拽分隔** | `<jh-drag-row :top-height="N">` 上下分隔,可嵌套 |
|
|
364
|
+
| **Tab 切换** | `<el-tabs type="border-card">` 或 `<jh-tabs>` 切换录入/查询视角 |
|
|
365
|
+
| **操作区** | 在上下表之间放置 `BaseForm` + 按钮,或 `BaseToolbar` |
|
|
366
|
+
| **懒加载** | Tab 切换时才加载对应数据,避免首次全量查询 |
|
|
300
367
|
|
|
301
368
|
### D-1 判断何时使用配置驱动 vs 独立编排
|
|
302
369
|
|
|
303
|
-
| 条件
|
|
304
|
-
|
|
305
|
-
| 3+ 页面布局完全相同,仅 API/工序代码/列不同
|
|
306
|
-
| 页面布局有显著差异(不同 Tab 结构、不同表数量) | 独立页面,在 data.ts 中定义多个 `createXxxPage()`
|
|
370
|
+
| 条件 | 方式 |
|
|
371
|
+
| ----------------------------------------------- | ------------------------------------------------------------ |
|
|
372
|
+
| 3+ 页面布局完全相同,仅 API/工序代码/列不同 | 提取 `src/components/template/XxxTemplate/`,页面仅传 config |
|
|
373
|
+
| 页面布局有显著差异(不同 Tab 结构、不同表数量) | 独立页面,在 data.ts 中定义多个 `createXxxPage()` |
|
|
307
374
|
|
|
308
375
|
### D-2 配置驱动模板组件结构(参考 FinishingAchievementTemplate)
|
|
309
376
|
|
|
@@ -321,17 +388,27 @@ src/views/.../[page-name]/
|
|
|
321
388
|
```
|
|
322
389
|
|
|
323
390
|
**types.ts 要点**:
|
|
391
|
+
|
|
324
392
|
```typescript
|
|
325
393
|
export interface XxxTemplateConfig {
|
|
326
|
-
api: Record<string, string>;
|
|
327
|
-
processCode: string;
|
|
328
|
-
query?: {
|
|
329
|
-
|
|
330
|
-
|
|
394
|
+
api: Record<string, string>; // 各表格 API 端点
|
|
395
|
+
processCode: string; // 工序标识,用于查询参数
|
|
396
|
+
query?: {
|
|
397
|
+
plan?: {
|
|
398
|
+
items: BaseQueryItemDesc<any>[];
|
|
399
|
+
defaultParams?: Record<string, any>;
|
|
400
|
+
};
|
|
401
|
+
};
|
|
402
|
+
columns?: {
|
|
403
|
+
planColumns: TableColumnDesc<any>[];
|
|
404
|
+
detailColumns: TableColumnDesc<any>[];
|
|
405
|
+
};
|
|
406
|
+
ui?: Partial<UiConfig>; // 可选 UI 覆盖(Tab 标题、区域标题等)
|
|
331
407
|
}
|
|
332
408
|
```
|
|
333
409
|
|
|
334
410
|
**页面 data.ts 要点**(仅配置,不写逻辑):
|
|
411
|
+
|
|
335
412
|
```typescript
|
|
336
413
|
import type { XxxTemplateConfig } from "@/components/template/XxxTemplate/types";
|
|
337
414
|
|
|
@@ -345,6 +422,7 @@ export const xxxConfig: XxxTemplateConfig = {
|
|
|
345
422
|
### D-3 独立编排页面结构(参考 mmwr-steel-stripping-operations)
|
|
346
423
|
|
|
347
424
|
**data.ts 要点**(多个 createPage 工厂函数):
|
|
425
|
+
|
|
348
426
|
```typescript
|
|
349
427
|
// 上表
|
|
350
428
|
export function createEntryPage() {
|
|
@@ -375,6 +453,7 @@ export function createEntryBottomPage(rejectForm: any) {
|
|
|
375
453
|
```
|
|
376
454
|
|
|
377
455
|
**index.vue 要点**:
|
|
456
|
+
|
|
378
457
|
```vue
|
|
379
458
|
<template>
|
|
380
459
|
<div class="app-container app-page-container [page-class]">
|
|
@@ -383,12 +462,21 @@ export function createEntryBottomPage(rejectForm: any) {
|
|
|
383
462
|
<jh-drag-row :top-height="420">
|
|
384
463
|
<template #top>
|
|
385
464
|
<BaseQuery :form="..." :items="..." @select="..." @reset="..." />
|
|
386
|
-
<BaseTable
|
|
465
|
+
<BaseTable
|
|
466
|
+
ref="..."
|
|
467
|
+
:data="..."
|
|
468
|
+
:columns="..."
|
|
469
|
+
highlight-current-row
|
|
470
|
+
@current-change="handleRowClick"
|
|
471
|
+
/>
|
|
387
472
|
<jh-pagination ... />
|
|
388
473
|
</template>
|
|
389
474
|
<template #bottom>
|
|
390
475
|
<BaseToolbar v-if="selectedRow" :items="..." />
|
|
391
|
-
<el-empty
|
|
476
|
+
<el-empty
|
|
477
|
+
v-if="!selectedRow"
|
|
478
|
+
description="请先在上方列表中选择一行数据"
|
|
479
|
+
/>
|
|
392
480
|
<BaseTable v-else ref="..." :data="..." :columns="..." />
|
|
393
481
|
<jh-pagination ... />
|
|
394
482
|
</template>
|
|
@@ -402,16 +490,26 @@ export function createEntryBottomPage(rejectForm: any) {
|
|
|
402
490
|
</template>
|
|
403
491
|
|
|
404
492
|
<script setup lang="ts">
|
|
405
|
-
import {
|
|
493
|
+
import {
|
|
494
|
+
createEntryPage,
|
|
495
|
+
createEntryBottomPage,
|
|
496
|
+
createQueryPage,
|
|
497
|
+
} from "./data";
|
|
406
498
|
|
|
407
499
|
const activeTab = ref("entry");
|
|
408
500
|
const selectedRow = ref(null);
|
|
409
501
|
|
|
410
502
|
const EntryPage = createEntryPage();
|
|
411
|
-
const { tableRef, page, queryParam, list, queryItems, columns, select } =
|
|
503
|
+
const { tableRef, page, queryParam, list, queryItems, columns, select } =
|
|
504
|
+
EntryPage;
|
|
412
505
|
|
|
413
506
|
const BottomPage = createEntryBottomPage();
|
|
414
|
-
const {
|
|
507
|
+
const {
|
|
508
|
+
list: bottomList,
|
|
509
|
+
columns: bottomColumns,
|
|
510
|
+
select: bottomSelect,
|
|
511
|
+
selectByPlan,
|
|
512
|
+
} = BottomPage;
|
|
415
513
|
|
|
416
514
|
const handleRowClick = (row: any) => {
|
|
417
515
|
selectedRow.value = row;
|
|
@@ -419,7 +517,9 @@ const handleRowClick = (row: any) => {
|
|
|
419
517
|
};
|
|
420
518
|
|
|
421
519
|
onMounted(() => select());
|
|
422
|
-
watch(activeTab, (tab) => {
|
|
520
|
+
watch(activeTab, (tab) => {
|
|
521
|
+
if (tab === "query") QueryPage.select();
|
|
522
|
+
});
|
|
423
523
|
</script>
|
|
424
524
|
```
|
|
425
525
|
|
|
@@ -427,14 +527,42 @@ watch(activeTab, (tab) => { if (tab === "query") QueryPage.select(); });
|
|
|
427
527
|
|
|
428
528
|
```scss
|
|
429
529
|
.[page-class] {
|
|
430
|
-
.section-header {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
.
|
|
437
|
-
|
|
530
|
+
.section-header {
|
|
531
|
+
display: flex;
|
|
532
|
+
align-items: center;
|
|
533
|
+
gap: 6px;
|
|
534
|
+
margin: 8px 0;
|
|
535
|
+
}
|
|
536
|
+
.section-header .title-bar {
|
|
537
|
+
width: 3px;
|
|
538
|
+
height: 14px;
|
|
539
|
+
background: var(--el-color-primary);
|
|
540
|
+
border-radius: 1px;
|
|
541
|
+
}
|
|
542
|
+
.section-header .section-title {
|
|
543
|
+
font-size: 14px;
|
|
544
|
+
font-weight: 600;
|
|
545
|
+
margin: 0;
|
|
546
|
+
}
|
|
547
|
+
.empty-tip {
|
|
548
|
+
padding: 40px 0;
|
|
549
|
+
}
|
|
550
|
+
.operation-area {
|
|
551
|
+
padding: 8px 0;
|
|
552
|
+
}
|
|
553
|
+
.operation-buttons {
|
|
554
|
+
display: flex;
|
|
555
|
+
gap: 8px;
|
|
556
|
+
margin: 8px 0;
|
|
557
|
+
}
|
|
558
|
+
.results-container {
|
|
559
|
+
display: flex;
|
|
560
|
+
gap: 16px; /* 左右分栏时 */
|
|
561
|
+
}
|
|
562
|
+
.results-container .section {
|
|
563
|
+
flex: 1;
|
|
564
|
+
min-width: 0;
|
|
565
|
+
}
|
|
438
566
|
}
|
|
439
567
|
```
|
|
440
568
|
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
> 见 SKILL.md 主文件(约束 + 按钮规则 + Mock 规范等共用规则)。
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
#### data.ts
|
|
7
6
|
|
|
8
7
|
```typescript
|
|
@@ -14,7 +13,10 @@ import {
|
|
|
14
13
|
TableColumnDesc,
|
|
15
14
|
BusLogicDataType
|
|
16
15
|
} from "@/types/page";
|
|
17
|
-
import {
|
|
16
|
+
import { ElMessage } from "element-plus";
|
|
17
|
+
import { defineColumns, renderOps } from "@agile-team/wk-skills-ui/runtime";
|
|
18
|
+
|
|
19
|
+
export const TABLE_CID = "[pageAbbr]-[base36Timestamp]";
|
|
18
20
|
|
|
19
21
|
export const API_CONFIG = {
|
|
20
22
|
list: "/[服务缩写]/[资源名]/list",
|
|
@@ -88,18 +90,24 @@ export function createPage(editModalRef?: any) {
|
|
|
88
90
|
label: "新增",
|
|
89
91
|
plain: true,
|
|
90
92
|
onClick: () => editModalRef?.value?.open()
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
label: "导出",
|
|
96
|
+
plain: true,
|
|
97
|
+
onClick: () => ElMessage.info("导出逻辑待业务确认")
|
|
91
98
|
}
|
|
92
99
|
];
|
|
93
100
|
}
|
|
94
101
|
|
|
95
102
|
columnsDef(): TableColumnDesc<any>[] {
|
|
96
|
-
return [
|
|
97
|
-
{ type: "selection" },
|
|
98
|
-
{ type: "index" },
|
|
103
|
+
return defineColumns([
|
|
104
|
+
{ type: "selection", width: 55, fixed: "left", align: "center", headerAlign: "center" },
|
|
105
|
+
{ type: "index", label: "序号", width: 60, align: "center" },
|
|
99
106
|
// 普通列
|
|
100
107
|
{
|
|
101
108
|
label: "[列名]",
|
|
102
109
|
name: "[fieldName]",
|
|
110
|
+
cid: `${TABLE_CID}-[fieldName]`,
|
|
103
111
|
minWidth: 120,
|
|
104
112
|
sortable: true,
|
|
105
113
|
filterable: true
|
|
@@ -108,6 +116,7 @@ export function createPage(editModalRef?: any) {
|
|
|
108
116
|
{
|
|
109
117
|
label: "[状态名]",
|
|
110
118
|
name: "[statusField]",
|
|
119
|
+
cid: `${TABLE_CID}-[statusField]`,
|
|
111
120
|
minWidth: 120,
|
|
112
121
|
logicType: BusLogicDataType.dict,
|
|
113
122
|
logicValue: "[dictCode]",
|
|
@@ -117,22 +126,18 @@ export function createPage(editModalRef?: any) {
|
|
|
117
126
|
// 操作列(如需要行内编辑/删除按钮)
|
|
118
127
|
{
|
|
119
128
|
label: "操作",
|
|
129
|
+
name: "_action",
|
|
130
|
+
cid: `${TABLE_CID}-action`,
|
|
120
131
|
width: 150,
|
|
121
132
|
fixed: "right",
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
onClick: (
|
|
127
|
-
|
|
128
|
-
{
|
|
129
|
-
name: "remove",
|
|
130
|
-
label: "删除",
|
|
131
|
-
onClick: (row: any) => this.remove(row.id)
|
|
132
|
-
}
|
|
133
|
-
]
|
|
133
|
+
align: "center",
|
|
134
|
+
defaultSlot: ({ row }: any) =>
|
|
135
|
+
renderOps([
|
|
136
|
+
{ type: "edit", onClick: () => editModalRef?.value?.open(row.id) },
|
|
137
|
+
{ type: "del", onClick: () => this.remove(row.id) }
|
|
138
|
+
])
|
|
134
139
|
}
|
|
135
|
-
];
|
|
140
|
+
] as any) as TableColumnDesc<any>[];
|
|
136
141
|
}
|
|
137
142
|
})();
|
|
138
143
|
|
|
@@ -152,7 +157,14 @@ export function createPage(editModalRef?: any) {
|
|
|
152
157
|
@reset="select"
|
|
153
158
|
/>
|
|
154
159
|
<BaseToolbar :items="toolbars" />
|
|
155
|
-
<BaseTable
|
|
160
|
+
<BaseTable
|
|
161
|
+
ref="tableRef"
|
|
162
|
+
render-type="agGrid"
|
|
163
|
+
:cid="TABLE_CID"
|
|
164
|
+
:data="list"
|
|
165
|
+
:columns="columns"
|
|
166
|
+
showToolbar
|
|
167
|
+
/>
|
|
156
168
|
<jh-pagination
|
|
157
169
|
v-show="page.total && page.total > 0"
|
|
158
170
|
:total="page.total || 0"
|
|
@@ -165,7 +177,7 @@ export function createPage(editModalRef?: any) {
|
|
|
165
177
|
</template>
|
|
166
178
|
|
|
167
179
|
<script setup lang="ts">
|
|
168
|
-
import { createPage } from "./data";
|
|
180
|
+
import { createPage, TABLE_CID } from "./data";
|
|
169
181
|
|
|
170
182
|
const Page = createPage();
|
|
171
183
|
const {
|
|
@@ -176,7 +188,7 @@ const {
|
|
|
176
188
|
queryItems,
|
|
177
189
|
columns,
|
|
178
190
|
toolbars,
|
|
179
|
-
select
|
|
191
|
+
select,
|
|
180
192
|
} = Page;
|
|
181
193
|
|
|
182
194
|
onMounted(() => select());
|
|
@@ -193,4 +205,81 @@ onMounted(() => select());
|
|
|
193
205
|
// 页面特有样式(无特殊需求可留空)
|
|
194
206
|
```
|
|
195
207
|
|
|
208
|
+
#### mock/[页面kebab-name].ts
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
import type { MockMethod } from "vite-plugin-mock";
|
|
212
|
+
|
|
213
|
+
const dataPool = Array.from({ length: 23 }).map((_, index) => ({
|
|
214
|
+
id: String(index + 1),
|
|
215
|
+
name: `模拟数据${index + 1}`,
|
|
216
|
+
status: index % 2 === 0 ? "1" : "0",
|
|
217
|
+
remark: "由 vite-plugin-mock 提供,关闭 ENV_MOCK 后直接走真实接口",
|
|
218
|
+
}));
|
|
219
|
+
|
|
220
|
+
function pageList(query: any) {
|
|
221
|
+
const current = Number(query?.current || query?.pageNum || 1);
|
|
222
|
+
const size = Number(query?.size || query?.pageSize || 10);
|
|
223
|
+
const start = (current - 1) * size;
|
|
224
|
+
return {
|
|
225
|
+
code: 2000,
|
|
226
|
+
message: "success",
|
|
227
|
+
data: {
|
|
228
|
+
records: dataPool.slice(start, start + size),
|
|
229
|
+
total: dataPool.length,
|
|
230
|
+
current,
|
|
231
|
+
size,
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export default [
|
|
237
|
+
{
|
|
238
|
+
url: "/dev-api/[服务缩写]/[资源名]/list",
|
|
239
|
+
method: "get",
|
|
240
|
+
response: ({ query }: any) => pageList(query),
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
url: "/dev-api/[服务缩写]/[资源名]/remove",
|
|
244
|
+
method: "post",
|
|
245
|
+
response: ({ body, query }: any) => {
|
|
246
|
+
const id = body?.id || query?.id;
|
|
247
|
+
const ids = body?.ids || (id ? [id] : []);
|
|
248
|
+
ids.forEach((itemId: string) => {
|
|
249
|
+
const index = dataPool.findIndex((item) => item.id === String(itemId));
|
|
250
|
+
if (index >= 0) dataPool.splice(index, 1);
|
|
251
|
+
});
|
|
252
|
+
return { code: 2000, message: "删除成功", data: null };
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
url: "/dev-api/[服务缩写]/[资源名]/save",
|
|
257
|
+
method: "post",
|
|
258
|
+
response: ({ body }: any) => {
|
|
259
|
+
const row = { id: String(Date.now()), ...body };
|
|
260
|
+
dataPool.unshift(row);
|
|
261
|
+
return { code: 2000, message: "新增成功", data: row };
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
url: "/dev-api/[服务缩写]/[资源名]/update",
|
|
266
|
+
method: "post",
|
|
267
|
+
response: ({ body }: any) => {
|
|
268
|
+
const index = dataPool.findIndex((item) => item.id === String(body?.id));
|
|
269
|
+
if (index >= 0) Object.assign(dataPool[index], body);
|
|
270
|
+
return { code: 2000, message: "更新成功", data: dataPool[index] || body };
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
url: "/dev-api/[服务缩写]/[资源名]/getById",
|
|
275
|
+
method: "get",
|
|
276
|
+
response: ({ query }: any) => ({
|
|
277
|
+
code: 2000,
|
|
278
|
+
message: "success",
|
|
279
|
+
data: dataPool.find((item) => item.id === String(query?.id)) || null,
|
|
280
|
+
}),
|
|
281
|
+
},
|
|
282
|
+
] as MockMethod[];
|
|
283
|
+
```
|
|
284
|
+
|
|
196
285
|
---
|
|
@@ -2,18 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
> 见 SKILL.md 主文件(约束 + 按钮规则 + Mock 规范等共用规则)。
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
#### data.ts(额外部分)
|
|
7
6
|
|
|
8
7
|
在标准 createPage 基础上,增加 createBottomPage:
|
|
9
8
|
|
|
10
9
|
```typescript
|
|
11
10
|
// ... 同模板 A 的 imports 和 API_CONFIG(增加从表相关 URL)
|
|
11
|
+
import { defineColumns, renderOps } from "@agile-team/wk-skills-ui/runtime";
|
|
12
|
+
|
|
13
|
+
export const TABLE_CID = "[pageAbbr]-[base36Timestamp]";
|
|
14
|
+
export const BOTTOM_TABLE_CID = `${TABLE_CID}-sub1`;
|
|
15
|
+
|
|
12
16
|
export const API_CONFIG = {
|
|
13
17
|
list: "/[服务缩写]/[主资源]/list",
|
|
14
18
|
remove: "/[服务缩写]/[主资源]/remove",
|
|
15
19
|
// ...标准 CRUD
|
|
16
|
-
bottomList: "/[服务缩写]/[从资源]/list" // 从表查询
|
|
20
|
+
bottomList: "/[服务缩写]/[从资源]/list", // 从表查询
|
|
17
21
|
} as const;
|
|
18
22
|
|
|
19
23
|
export function createPage(/* refs */) {
|
|
@@ -24,7 +28,7 @@ export function createPage(/* refs */) {
|
|
|
24
28
|
export function handleRowDblclick(
|
|
25
29
|
row: any,
|
|
26
30
|
bottomSelect: Function,
|
|
27
|
-
BottomPage: any
|
|
31
|
+
BottomPage: any,
|
|
28
32
|
) {
|
|
29
33
|
BottomPage.queryParam.value.mainId = row.id;
|
|
30
34
|
BottomPage.tableRef.value.loading();
|
|
@@ -51,10 +55,16 @@ export function createBottomPage() {
|
|
|
51
55
|
return [];
|
|
52
56
|
}
|
|
53
57
|
columnsDef(): TableColumnDesc<any>[] {
|
|
54
|
-
return [
|
|
55
|
-
{ type: "index" }
|
|
58
|
+
return defineColumns([
|
|
59
|
+
{ type: "index", label: "序号", width: 60, align: "center" },
|
|
60
|
+
{
|
|
61
|
+
label: "[从表字段]",
|
|
62
|
+
name: "[fieldName]",
|
|
63
|
+
cid: `${BOTTOM_TABLE_CID}-[fieldName]`,
|
|
64
|
+
minWidth: 120,
|
|
65
|
+
},
|
|
56
66
|
// 从表字段
|
|
57
|
-
];
|
|
67
|
+
] as any) as TableColumnDesc<any>[];
|
|
58
68
|
}
|
|
59
69
|
})();
|
|
60
70
|
return (Page as any).create() as any;
|
|
@@ -77,6 +87,8 @@ export function createBottomPage() {
|
|
|
77
87
|
<BaseToolbar :items="toolbars" />
|
|
78
88
|
<BaseTable
|
|
79
89
|
ref="tableRef"
|
|
90
|
+
render-type="agGrid"
|
|
91
|
+
:cid="TABLE_CID"
|
|
80
92
|
:data="list"
|
|
81
93
|
:columns="columns"
|
|
82
94
|
showToolbar
|
|
@@ -97,6 +109,8 @@ export function createBottomPage() {
|
|
|
97
109
|
<BaseToolbar :items="bottomToolbars" />
|
|
98
110
|
<BaseTable
|
|
99
111
|
ref="bottomTableRef"
|
|
112
|
+
render-type="agGrid"
|
|
113
|
+
:cid="BOTTOM_TABLE_CID"
|
|
100
114
|
:data="bottomList"
|
|
101
115
|
:columns="bottomColumns"
|
|
102
116
|
showToolbar
|
|
@@ -107,7 +121,13 @@ export function createBottomPage() {
|
|
|
107
121
|
</template>
|
|
108
122
|
|
|
109
123
|
<script setup lang="ts">
|
|
110
|
-
import {
|
|
124
|
+
import {
|
|
125
|
+
createPage,
|
|
126
|
+
createBottomPage,
|
|
127
|
+
handleRowDblclick,
|
|
128
|
+
TABLE_CID,
|
|
129
|
+
BOTTOM_TABLE_CID,
|
|
130
|
+
} from "./data";
|
|
111
131
|
|
|
112
132
|
const Page = createPage();
|
|
113
133
|
const {
|
|
@@ -118,7 +138,7 @@ const {
|
|
|
118
138
|
queryItems,
|
|
119
139
|
columns,
|
|
120
140
|
toolbars,
|
|
121
|
-
select
|
|
141
|
+
select,
|
|
122
142
|
} = Page;
|
|
123
143
|
|
|
124
144
|
const BottomPage = createBottomPage();
|
|
@@ -127,7 +147,7 @@ const {
|
|
|
127
147
|
list: bottomList,
|
|
128
148
|
columns: bottomColumns,
|
|
129
149
|
select: bottomSelect,
|
|
130
|
-
toolbars: bottomToolbars
|
|
150
|
+
toolbars: bottomToolbars,
|
|
131
151
|
} = BottomPage;
|
|
132
152
|
|
|
133
153
|
onMounted(() => select());
|