@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.
- package/README.md +66 -214
- package/SKILL.md +165 -527
- package/package.json +2 -2
- package/references/api-check.md +187 -128
- package/references/base-code-dict.md +49 -48
- package/references/chart-echarts.md +61 -0
- package/references/code-quality.md +44 -15
- package/references/core-kernel.md +305 -0
- package/references/crud-pages.md +87 -261
- package/references/data-compare.md +502 -501
- package/references/data-mapping.md +218 -213
- package/references/data-screen.md +94 -79
- package/references/data-writeback.md +104 -104
- package/references/detail-page.md +100 -99
- package/references/directives-advanced.md +27 -4
- package/references/download-export.md +70 -68
- package/references/feedback-loading.md +62 -60
- package/references/feedback-ui.md +125 -111
- package/references/file-management.md +28 -7
- package/references/flowchart-g6.md +249 -244
- package/references/form-advanced.md +264 -26
- package/references/graph-relation.md +12 -7
- package/references/http-api.md +138 -103
- package/references/icons.md +326 -0
- package/references/layout-theme.md +1 -1
- package/references/mobile-h5.md +20 -0
- package/references/particles.md +143 -0
- package/references/permission-auth.md +8 -31
- package/references/project-inventory.md +175 -120
- package/references/qrcode-barcode.md +107 -92
- package/references/rich-text.md +85 -73
- package/references/seamless-scroll.md +40 -38
- package/references/table-vxe.md +114 -0
- package/references/tree-table.md +2 -1
- package/references/ui-components.md +5 -1
- package/references/verify-captcha.md +110 -96
- package/references/websocket-realtime.md +2 -1
- package/references/wechat-js.md +58 -0
- package/references/workflow-bpmn.md +207 -206
- package/references/advanced-ui.md +0 -302
- package/references/build-optim.md +0 -282
- package/references/vue-core.md +0 -209
|
@@ -1,501 +1,502 @@
|
|
|
1
|
-
# 数据变更对比
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
|
24
|
-
|
|
25
|
-
| `
|
|
26
|
-
| `
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
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
|
-
v-
|
|
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
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
| `
|
|
379
|
-
|
|
380
|
-
| `
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
v-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
</
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
store
|
|
423
|
-
store.
|
|
424
|
-
store.
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
- [ ]
|
|
478
|
-
- [ ]
|
|
479
|
-
- [ ]
|
|
480
|
-
- [ ]
|
|
481
|
-
- [ ]
|
|
482
|
-
- [ ]
|
|
483
|
-
- [ ]
|
|
484
|
-
- [ ]
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
|
493
|
-
|
|
494
|
-
| `src/directives/compare/
|
|
495
|
-
| `src/
|
|
496
|
-
| `src/
|
|
497
|
-
| `src/
|
|
498
|
-
| `src/
|
|
499
|
-
| `
|
|
500
|
-
|
|
501
|
-
|
|
1
|
+
# 数据变更对比
|
|
2
|
+
|
|
3
|
+
> 按本文件示例编写,复用 `v-compare` / `compareStore` / `ChangeSummaryService` 等已有能力,勿另起一套对比实现。
|
|
4
|
+
|
|
5
|
+
编辑页对比「当前值 vs 编辑前原值」:字段高亮、悬停原值提示、变更汇总弹窗、表格行增删改矩阵。
|
|
6
|
+
|
|
7
|
+
> 示例:以本文件完整规范为准。
|
|
8
|
+
> 核心落点:`src/directives/compare/`、`src/store/modules/compareStore.ts`、`src/utils/changeSummaryService.ts`、
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 一、架构总览
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
页面 provide(oldDataInfo, compareEnabled)
|
|
16
|
+
└─ 子表单 inject → v-compare / compare-config
|
|
17
|
+
├─ 高亮 .is-compare-changed + tooltip「原值:…」
|
|
18
|
+
└─ useCompareStore.addOrUpdateRecord
|
|
19
|
+
└─ ChangeSummaryDialog ← ChangeSummaryService.getChangeSummaryData()
|
|
20
|
+
└─ 定位:scrollIntoView + data-compare-* DOM 回退
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
| 层级 | 职责 |
|
|
24
|
+
|------|------|
|
|
25
|
+
| `v-compare` | 读 DOM/Vue 当前值,与 `oldValue` 比(映射后显示值),写样式与 Store |
|
|
26
|
+
| `useCompareStore` | 按模块聚合变更项;去重键 = module+label+context+tableFirstValue+tableRowOrder |
|
|
27
|
+
| `ChangeSummaryService` | 读 Store;元素引用失效时用 `data-compare-label/module` 修复 |
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
**对比的是映射后的显示文案**,不是原始 code(除非未配置 map)。空值统一显示为 `空`(`''` / `null` / `undefined` 视为相等)。
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 二、页面编排(必须先做)
|
|
35
|
+
|
|
36
|
+
### 1. 父页 provide
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
const formData = reactive<DataInfo>({ /* 当前编辑数据 */ })
|
|
40
|
+
const oldDataInfo = ref<DataInfo | null>(null) // 编辑前快照(接口返回后赋值)
|
|
41
|
+
const compareEnabled = ref(true) // true = 修改模式启用对比;新增模式设 false
|
|
42
|
+
|
|
43
|
+
provide('oldDataInfo', oldDataInfo)
|
|
44
|
+
provide('compareDisabled', compareEnabled) // 历史命名:变量叫 Disabled,语义却是「是否启用」
|
|
45
|
+
|
|
46
|
+
// 打开编辑:拉取原数据
|
|
47
|
+
async function fetchDetail(id: string) {
|
|
48
|
+
const res = await getDetailApi({ id })
|
|
49
|
+
Object.assign(formData, res.data)
|
|
50
|
+
oldDataInfo.value = cloneDeep(res.data)
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
> **命名陷阱**:示例里 inject 的 key 是 `compareDisabled`,但 `enabled: compareDisabled` 时 **true 表示对比已启用**。新代码建议本地命名为 `compareEnabled`,provide key 仍用 `compareDisabled` 以兼容现有子组件。
|
|
55
|
+
|
|
56
|
+
### 2. 子表单 inject
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
const oldFormInfos = inject('oldDataInfo') as Ref<DataInfo | null>
|
|
60
|
+
const compareDisabled = inject<Ref<boolean>>('compareDisabled', ref(false))
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 3. 变更汇总入口
|
|
64
|
+
|
|
65
|
+
```vue
|
|
66
|
+
<el-button :disabled="!compareDisabled" @click="showChangeSummary = true">
|
|
67
|
+
变更汇总
|
|
68
|
+
</el-button>
|
|
69
|
+
<ChangeSummaryDialog
|
|
70
|
+
v-model="showChangeSummary"
|
|
71
|
+
:module-order="['基本信息', '项目成员', '项目列表']"
|
|
72
|
+
/>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
离开编辑页时建议 `useCompareStoreHook().clearAllRecords()`。
|
|
76
|
+
|
|
77
|
+
### 4. 嵌套弹窗
|
|
78
|
+
|
|
79
|
+
弹窗内再 `provide('oldDataInfo', localOld)` / `provide('compareDisabled', …)`,与主表单同一套指令即可(见 `MultiFormDialog.vue`)。
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 三、指令 API
|
|
84
|
+
|
|
85
|
+
### 绑定值 `CompareBindingValue`
|
|
86
|
+
|
|
87
|
+
| 字段 | 说明 |
|
|
88
|
+
|------|------|
|
|
89
|
+
| `oldValue` | 编辑前原值(必填语义) |
|
|
90
|
+
| `value` | **覆盖当前显示值**(树/级联等用名称字段,不用 code) |
|
|
91
|
+
| `title` | 模块分组名(汇总按此分组;运行时优先于 `module`) |
|
|
92
|
+
| `label` | 字段中文名(汇总列 + DOM `data-compare-label`) |
|
|
93
|
+
| `enabled` | `false` 跳过对比(推荐开关) |
|
|
94
|
+
| `showInSummary` | `false` 只高亮,不进汇总 |
|
|
95
|
+
| `map` | 配合 `.map`:对象映射 `{ 1: '是' }` |
|
|
96
|
+
| `mapArr` | 配合 `.mapArr`:选项映射 / 多选顺序无关比较 |
|
|
97
|
+
| `format` | `(v) => string` 自定义显示 |
|
|
98
|
+
| `tableFirstLabel` | 表格模式:业务主键列名 |
|
|
99
|
+
| `tableFirstValue` | 表格模式:该行业务主键值 |
|
|
100
|
+
| `tableRowStatus` | `I` 新增 / `U` 修改 / `D`\|`B` 删除(强制记入汇总) |
|
|
101
|
+
| `tableRowOrder` | 行序号(与页面顺序一致) |
|
|
102
|
+
|
|
103
|
+
### 修饰符 / 参数
|
|
104
|
+
|
|
105
|
+
| 写法 | 作用 |
|
|
106
|
+
|------|------|
|
|
107
|
+
| `v-compare:fieldKey` | `arg` 为字段名(可读性;逻辑不依赖 arg) |
|
|
108
|
+
| `.map` | 使用 `map` 对象做显示映射 |
|
|
109
|
+
| `.mapArr` | 使用 `mapArr`;多选按集合比较(忽略顺序) |
|
|
110
|
+
| `.table` | 表格行上下文,汇总走矩阵视图 |
|
|
111
|
+
|
|
112
|
+
### 行为摘要
|
|
113
|
+
|
|
114
|
+
- 变更 → 加 `.is-compare-changed`,悬停全局 tooltip「原值:xxx」
|
|
115
|
+
- 写入 Store;改回原值 → `removeRecordByLabel`
|
|
116
|
+
- DOM 标记:`data-v-compare`、`data-compare-label`、`data-compare-module`(定位回退用)
|
|
117
|
+
- 全局恢复:`window.restoreCompareValue(el, originalValue)`
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 四、控件用法对照(逐类)
|
|
122
|
+
|
|
123
|
+
### 1. 普通输入 `el-input`
|
|
124
|
+
|
|
125
|
+
指令挂在组件根上即可。
|
|
126
|
+
|
|
127
|
+
```vue
|
|
128
|
+
<el-input
|
|
129
|
+
v-model="formInfos.projectName"
|
|
130
|
+
v-compare:projectName="{
|
|
131
|
+
oldValue: oldFormInfos?.projectName,
|
|
132
|
+
title: props.item.title,
|
|
133
|
+
label: '项目名称',
|
|
134
|
+
enabled: compareDisabled,
|
|
135
|
+
}"
|
|
136
|
+
/>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`showInSummary: false` → 仍高亮,但不进汇总弹窗。
|
|
140
|
+
|
|
141
|
+
### 2. 文本域 `el-input type="textarea"`
|
|
142
|
+
|
|
143
|
+
同 `el-input`,挂在输入组件上。
|
|
144
|
+
|
|
145
|
+
### 3. 日期/时间 `el-date-picker` / `el-time-picker`
|
|
146
|
+
|
|
147
|
+
**必须包一层 `div` 挂指令**(picker 根节点才稳定可读 DOM)。
|
|
148
|
+
|
|
149
|
+
```vue
|
|
150
|
+
<div
|
|
151
|
+
v-compare:planStart="{
|
|
152
|
+
oldValue: oldFormInfos?.planStart,
|
|
153
|
+
title: props.item.title,
|
|
154
|
+
label: '计划开始时间',
|
|
155
|
+
enabled: compareDisabled,
|
|
156
|
+
}"
|
|
157
|
+
class="!w-[100%]"
|
|
158
|
+
>
|
|
159
|
+
<el-date-picker v-model="formInfos.planStart" type="date" class="!w-[100%]" />
|
|
160
|
+
</div>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
适用:`date` / `datetime` / `month` / `year` / `daterange` / `datetimerange` / `time`。
|
|
164
|
+
范围值会按分隔符拼成字符串再比。
|
|
165
|
+
|
|
166
|
+
### 4. 本地下拉 `el-select`(单选)→ `.map` + `composeOptions`
|
|
167
|
+
|
|
168
|
+
```vue
|
|
169
|
+
<el-select
|
|
170
|
+
v-model="formInfos.projectStatus"
|
|
171
|
+
v-compare:projectStatus.map="{
|
|
172
|
+
oldValue: oldFormInfos?.projectStatus,
|
|
173
|
+
title: props.item.title,
|
|
174
|
+
label: '项目状态',
|
|
175
|
+
map: composeOptions(localSelectOptions.projectStatus, 'value', 'label'),
|
|
176
|
+
enabled: compareDisabled,
|
|
177
|
+
}"
|
|
178
|
+
>
|
|
179
|
+
<el-option v-for="s in localSelectOptions.projectStatus" :key="s.value" :label="s.label" :value="s.value" />
|
|
180
|
+
</el-select>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
`composeOptions(list, codeKey, labelKey)` → `{ [code]: label }`(见 `src/utils/utils.ts`)。
|
|
184
|
+
|
|
185
|
+
异步选项列表加载完后同样用 `composeOptions(saleUnitList)`(默认 key 为 `code`/`value`)。
|
|
186
|
+
|
|
187
|
+
### 5. 多选下拉 `el-select multiple` → `.mapArr`
|
|
188
|
+
|
|
189
|
+
```vue
|
|
190
|
+
<el-select
|
|
191
|
+
v-model="formInfos.multiSelectStatus"
|
|
192
|
+
multiple
|
|
193
|
+
v-compare:multiSelectStatus.mapArr="{
|
|
194
|
+
oldValue: oldFormInfos?.multiSelectStatus,
|
|
195
|
+
title: props.item.title,
|
|
196
|
+
label: '多选状态',
|
|
197
|
+
mapArr: composeOptions(localSelectOptions.projectStatus, 'value', 'label'),
|
|
198
|
+
enabled: compareDisabled,
|
|
199
|
+
}"
|
|
200
|
+
/>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
比较时忽略选项顺序;显示为逗号拼接的中文标签。
|
|
204
|
+
|
|
205
|
+
### 6. `ReSelectQuery`(异步分页选择)→ props 桥接
|
|
206
|
+
|
|
207
|
+
模板不能直接写 `v-compare` 时,用组件内置 props(内部 `withDirectives`)。
|
|
208
|
+
|
|
209
|
+
```vue
|
|
210
|
+
<!-- 单选:oldValue 用名称字段 -->
|
|
211
|
+
<ReSelectQuery
|
|
212
|
+
v-model="formInfos.asyncSelectUser"
|
|
213
|
+
:compare-config="{
|
|
214
|
+
oldValue: oldFormInfos?.asyncSelectUserName,
|
|
215
|
+
label: '异步单选用户',
|
|
216
|
+
title: props.item.title,
|
|
217
|
+
enabled: compareDisabled,
|
|
218
|
+
}"
|
|
219
|
+
compare-key="asyncSelectUser"
|
|
220
|
+
:edit-data="formInfos.single1BackData"
|
|
221
|
+
url="..."
|
|
222
|
+
value="userCode"
|
|
223
|
+
label="userName"
|
|
224
|
+
/>
|
|
225
|
+
|
|
226
|
+
<!-- 多选 -->
|
|
227
|
+
<ReSelectQuery
|
|
228
|
+
v-model="formInfos.asyncMultiSelectUser"
|
|
229
|
+
multiple
|
|
230
|
+
:compare-config="{
|
|
231
|
+
oldValue: oldFormInfos?.asyncMultiSelectUserNames,
|
|
232
|
+
label: '异步多选用户',
|
|
233
|
+
title: props.item.title,
|
|
234
|
+
enabled: compareDisabled,
|
|
235
|
+
}"
|
|
236
|
+
compare-key="asyncMultiSelectUser"
|
|
237
|
+
:compare-modifiers="{ mapArr: true }"
|
|
238
|
+
/>
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
| Prop | 说明 |
|
|
242
|
+
|------|------|
|
|
243
|
+
| `compareConfig` | 同指令 binding value |
|
|
244
|
+
| `compareKey` | 同 `v-compare:key` 的 arg |
|
|
245
|
+
| `compareModifiers` | 如 `{ mapArr: true }` |
|
|
246
|
+
|
|
247
|
+
### 7. `el-tree-select` → 用 `value` 覆盖显示名
|
|
248
|
+
|
|
249
|
+
对比名称字符串,不要对比节点 code。
|
|
250
|
+
|
|
251
|
+
```vue
|
|
252
|
+
<el-tree-select
|
|
253
|
+
v-model="formInfos.value3"
|
|
254
|
+
v-compare:value3="{
|
|
255
|
+
oldValue: oldFormInfos?.value3Name,
|
|
256
|
+
value: formInfos.value3Name,
|
|
257
|
+
title: props.item.title,
|
|
258
|
+
label: '单选树',
|
|
259
|
+
enabled: compareDisabled,
|
|
260
|
+
}"
|
|
261
|
+
:cache-data="formInfos.value3Text"
|
|
262
|
+
...
|
|
263
|
+
/>
|
|
264
|
+
|
|
265
|
+
<!-- 多选树 -->
|
|
266
|
+
v-compare:multiple1.mapArr="{
|
|
267
|
+
oldValue: oldFormInfos?.multiple1Name,
|
|
268
|
+
value: formInfos.multiple1Name,
|
|
269
|
+
title, label, enabled: compareDisabled,
|
|
270
|
+
}"
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
需在业务里维护 `*Name` / `*Text` 回写字段(与 data-writeback 模块配合)。
|
|
274
|
+
|
|
275
|
+
### 8. `ReCascader` → 同 ReSelectQuery 桥接 + `value` 名称
|
|
276
|
+
|
|
277
|
+
```vue
|
|
278
|
+
<ReCascader
|
|
279
|
+
v-model="formInfos.singleBack"
|
|
280
|
+
:compare-config="{
|
|
281
|
+
oldValue: oldFormInfos?.singleBackName,
|
|
282
|
+
value: formInfos.singleBackName,
|
|
283
|
+
label: '级联器单选',
|
|
284
|
+
title: props.item.title,
|
|
285
|
+
enabled: compareDisabled,
|
|
286
|
+
}"
|
|
287
|
+
compare-key="singleBack"
|
|
288
|
+
@change="getSingleBackName"
|
|
289
|
+
/>
|
|
290
|
+
|
|
291
|
+
<!-- 多选加 modifiers -->
|
|
292
|
+
:compare-modifiers="{ mapArr: true }"
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### 9. `el-checkbox` / `el-switch` → `.map` 布尔/枚举文案
|
|
296
|
+
|
|
297
|
+
```vue
|
|
298
|
+
<el-checkbox
|
|
299
|
+
v-model="formInfos.isUrgent"
|
|
300
|
+
true-value="1"
|
|
301
|
+
false-value="0"
|
|
302
|
+
v-compare:isUrgent.map="{
|
|
303
|
+
oldValue: oldFormInfos?.isUrgent,
|
|
304
|
+
title: props.item.title,
|
|
305
|
+
label: '是否紧急',
|
|
306
|
+
map: { 1: '是', 0: '否' },
|
|
307
|
+
enabled: compareDisabled,
|
|
308
|
+
}"
|
|
309
|
+
>
|
|
310
|
+
紧急项目
|
|
311
|
+
</el-checkbox>
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
`el-switch` 同理,读取 `active-value` / `inactive-value`。
|
|
315
|
+
|
|
316
|
+
### 10. `el-radio-group` / `el-checkbox-group` → `.map` / `.mapArr`
|
|
317
|
+
|
|
318
|
+
```vue
|
|
319
|
+
<el-radio-group
|
|
320
|
+
v-model="formInfos.approvalStatus"
|
|
321
|
+
v-compare:approvalStatus.map="{
|
|
322
|
+
oldValue: oldFormInfos?.approvalStatus,
|
|
323
|
+
title: props.item.title,
|
|
324
|
+
label: '审批状态',
|
|
325
|
+
map: composeOptions(options, 'value', 'label'),
|
|
326
|
+
enabled: compareDisabled,
|
|
327
|
+
}"
|
|
328
|
+
>
|
|
329
|
+
<el-radio v-for="o in options" :key="o.value" :value="o.value">{{ o.label }}</el-radio>
|
|
330
|
+
</el-radio-group>
|
|
331
|
+
|
|
332
|
+
<el-checkbox-group
|
|
333
|
+
v-model="formInfos.tags"
|
|
334
|
+
v-compare:tags.mapArr="{
|
|
335
|
+
oldValue: oldFormInfos?.tags,
|
|
336
|
+
title: props.item.title,
|
|
337
|
+
label: '标签',
|
|
338
|
+
mapArr: composeOptions(tagOptions, 'value', 'label'),
|
|
339
|
+
enabled: compareDisabled,
|
|
340
|
+
}"
|
|
341
|
+
>
|
|
342
|
+
...
|
|
343
|
+
</el-checkbox-group>
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### 11. `el-input-number` / `el-rate` → `format`
|
|
347
|
+
|
|
348
|
+
```vue
|
|
349
|
+
<el-input-number
|
|
350
|
+
v-model="formInfos.completionRate"
|
|
351
|
+
v-compare:completionRate="{
|
|
352
|
+
oldValue: oldFormInfos?.completionRate,
|
|
353
|
+
title: props.item.title,
|
|
354
|
+
label: '完成率',
|
|
355
|
+
enabled: compareDisabled,
|
|
356
|
+
format: (v) => (v === null || v === undefined || v === '' ? '未设置' : `${v}%`),
|
|
357
|
+
}"
|
|
358
|
+
/>
|
|
359
|
+
|
|
360
|
+
<el-rate
|
|
361
|
+
v-model="formInfos.projectRating"
|
|
362
|
+
v-compare:projectRating="{
|
|
363
|
+
oldValue: oldFormInfos?.projectRating,
|
|
364
|
+
title: props.item.title,
|
|
365
|
+
label: '项目评分',
|
|
366
|
+
enabled: compareDisabled,
|
|
367
|
+
format: (v) => (v == null || v === '' ? '未评分' : `${v}分`),
|
|
368
|
+
}"
|
|
369
|
+
/>
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
## 五、表格行对比(`.table`)
|
|
375
|
+
|
|
376
|
+
### 行标志约定
|
|
377
|
+
|
|
378
|
+
| `flag` / `tableRowStatus` | 含义 | 行样式(`getRowClassByFlag`) | 汇总 |
|
|
379
|
+
|---------------------------|------|------------------------------|------|
|
|
380
|
+
| `I` | 新增 | `row-added` | 记入;类型「新增」 |
|
|
381
|
+
| `U` | 修改 | `row-modified` | 按字段 diff |
|
|
382
|
+
| `D` / `B` | 删除 | `row-deleted` | **强制记入**(值相等也保留) |
|
|
383
|
+
|
|
384
|
+
```vue
|
|
385
|
+
<el-table :data="formInfos.projectList" :row-class-name="({ row }) => getRowClassByFlag(row?.flag)">
|
|
386
|
+
<el-table-column label="项目公司">
|
|
387
|
+
<template #default="{ row, $index }">
|
|
388
|
+
<el-input
|
|
389
|
+
v-model="row.companyManager"
|
|
390
|
+
:disabled="row.flag === 'D' || row.flag === 'B'"
|
|
391
|
+
v-compare:companyManager.table="{
|
|
392
|
+
oldValue: oldFormInfos?.projectList?.[$index]?.companyManager,
|
|
393
|
+
title: props.item.title,
|
|
394
|
+
label: '项目公司',
|
|
395
|
+
tableFirstLabel: '名称',
|
|
396
|
+
tableFirstValue: row.name,
|
|
397
|
+
tableRowStatus: row.flag,
|
|
398
|
+
tableRowOrder: $index,
|
|
399
|
+
enabled: compareDisabled,
|
|
400
|
+
}"
|
|
401
|
+
/>
|
|
402
|
+
</template>
|
|
403
|
+
</el-table-column>
|
|
404
|
+
</el-table>
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
要点:
|
|
408
|
+
|
|
409
|
+
- 每个可编辑列都挂 `.table`,共用同一 `tableFirstValue` / `tableRowOrder`
|
|
410
|
+
- `tableFirstLabel` + `tableFirstValue` 决定汇总矩阵「业务名称」列
|
|
411
|
+
- 新增行 `oldValue` 可为空;删除行即使字段未改也会进汇总
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
## 六、变更汇总
|
|
416
|
+
|
|
417
|
+
### Store
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
import { useCompareStoreHook } from '@/store/modules/compareStore'
|
|
421
|
+
|
|
422
|
+
const store = useCompareStoreHook()
|
|
423
|
+
store.getChangeSummaryData // 按 module 分组
|
|
424
|
+
store.getStatsNum // { total, modules }
|
|
425
|
+
store.clearAllRecords()
|
|
426
|
+
store.clearModuleRecords(module)
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### Service
|
|
430
|
+
|
|
431
|
+
```typescript
|
|
432
|
+
import { ChangeSummaryService } from '@/utils/changeSummaryService'
|
|
433
|
+
|
|
434
|
+
const data = ChangeSummaryService.getChangeSummaryData()
|
|
435
|
+
// 自动修复失效 element;失败则 DOM 回退扫描 [data-v-compare]
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
### Dialog
|
|
439
|
+
|
|
440
|
+
```vue
|
|
441
|
+
<ChangeSummaryDialog
|
|
442
|
+
v-model="visible"
|
|
443
|
+
:module-order="moduleTitles" // 可选:模块展示顺序
|
|
444
|
+
/>
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
- 普通字段:模块标题 + 表(字段 / 原值 / 新值 / 定位)
|
|
448
|
+
- `.table` 项:按行折叠为矩阵(变更类型 + 各列旧→新)
|
|
449
|
+
- 定位:`scrollIntoView` + 临时类 `locate-highlight`;引用失效时用
|
|
450
|
+
`[data-v-compare][data-compare-label="…"][data-compare-module="…"]`
|
|
451
|
+
(`escapeAttrSelector` 防特殊字符)
|
|
452
|
+
|
|
453
|
+
可将变更汇总弹窗抽到业务模块或 `components/`。
|
|
454
|
+
|
|
455
|
+
---
|
|
456
|
+
|
|
457
|
+
## 七、封装组件内挂指令(TSX/render)
|
|
458
|
+
|
|
459
|
+
```typescript
|
|
460
|
+
import { resolveDirective, withDirectives } from 'vue'
|
|
461
|
+
|
|
462
|
+
const compareDirective = resolveDirective('compare')
|
|
463
|
+
return withDirectives(vnode, [[
|
|
464
|
+
compareDirective,
|
|
465
|
+
compareConfig,
|
|
466
|
+
compareKey,
|
|
467
|
+
compareModifiers || {},
|
|
468
|
+
]])
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
`ReSelectQuery` / `ReCascader` 已实现;新封装选择器应同样暴露三件套 props。
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
## 八、生成检查清单
|
|
476
|
+
|
|
477
|
+
- [ ] 父页 `provide` 了 `oldDataInfo` + 启用开关;编辑拉取后写入 `oldDataInfo`
|
|
478
|
+
- [ ] 新增模式 `enabled: false`;修改模式 `enabled: true`
|
|
479
|
+
- [ ] 每个对比字段有稳定 `title`(模块)+ `label`(字段名)
|
|
480
|
+
- [ ] 码值类控件用了 `.map` / `.mapArr` + `composeOptions`(或等价 map)
|
|
481
|
+
- [ ] 日期类指令挂在外层 `div`,不是直接挂在 picker 不稳定节点
|
|
482
|
+
- [ ] 树/级联/异步选择对比的是 **名称**(`oldValue`/`value` 用 `*Name`)
|
|
483
|
+
- [ ] `ReSelectQuery`/`ReCascader` 用 `compare-config` + `compare-key`,多选加 `compare-modifiers.mapArr`
|
|
484
|
+
- [ ] 表格列用 `.table` + `tableFirst*` + `tableRowStatus` + `tableRowOrder` + `getRowClassByFlag`
|
|
485
|
+
- [ ] 接了汇总弹窗;离开页 `clearAllRecords`
|
|
486
|
+
- [ ] 未使用文档里的错误写法:`newValue`(不存在)、数值比较数组语法(那是错误旧文档)
|
|
487
|
+
|
|
488
|
+
---
|
|
489
|
+
|
|
490
|
+
## 九、关联资产
|
|
491
|
+
|
|
492
|
+
| 路径 | 说明 |
|
|
493
|
+
|------|------|
|
|
494
|
+
| `src/directives/compare/index.ts` | 指令实现 |
|
|
495
|
+
| `src/directives/compare/types.ts` | 类型 |
|
|
496
|
+
| `src/directives/compare/styles.css` | 高亮与 tooltip |
|
|
497
|
+
| `src/store/modules/compareStore.ts` | 变更 Store |
|
|
498
|
+
| `src/utils/changeSummaryService.ts` | 汇总读取/修复 |
|
|
499
|
+
| `src/utils/utils.ts` | `composeOptions` / `getRowClassByFlag` / `escapeAttrSelector` |
|
|
500
|
+
| `ReSelectQuery` / `ReCascader` | 指令 props 桥接 |
|
|
501
|
+
|
|
502
|
+
相关模块:`data-writeback`(名称回写)、`directives-advanced`(其它指令)、`crud-pages` / `detail-page`(编辑页骨架)。
|