@truenewx/tnxvue3 3.0.0-alpha.32 → 3.0.0-alpha.34
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truenewx/tnxvue3",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.34",
|
|
4
4
|
"description": "互联网技术解决方案:Vue3扩展支持",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"vue-router": "~4.4.0"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@truenewx/tnxcore": "3.0.0-alpha.
|
|
27
|
+
"@truenewx/tnxcore": "3.0.0-alpha.20",
|
|
28
28
|
"@element-plus/icons-vue": "2.3.1",
|
|
29
29
|
"async-validator": "4.2.5",
|
|
30
30
|
"mitt": "3.0.1"
|
|
@@ -64,6 +64,8 @@
|
|
|
64
64
|
],
|
|
65
65
|
"no-undef": "warn",
|
|
66
66
|
"no-useless-escape": "warn",
|
|
67
|
+
"no-empty": "off",
|
|
68
|
+
"vue/no-mutating-props": "warn",
|
|
67
69
|
"vue/no-v-model-argument": "off",
|
|
68
70
|
"vue/multi-word-component-names": "off"
|
|
69
71
|
}
|
|
@@ -3,37 +3,39 @@
|
|
|
3
3
|
<el-table ref="table"
|
|
4
4
|
class="tnxel-edit-table"
|
|
5
5
|
:class="{'padding-none': !padding}"
|
|
6
|
-
:data="
|
|
6
|
+
:data="tableDataList"
|
|
7
|
+
:row-key="rowKey"
|
|
8
|
+
:row-class-name="tableRowClassName"
|
|
7
9
|
:highlight-current-row="selectable"
|
|
8
10
|
border
|
|
9
11
|
@current-change="onSelectRow"
|
|
10
12
|
>
|
|
11
13
|
<slot></slot>
|
|
12
14
|
<el-table-column label="排序" align="center" width="60px" v-if="sortable">
|
|
13
|
-
<template #default="
|
|
15
|
+
<template #default="{row, $index}">
|
|
14
16
|
<tnxel-button
|
|
15
17
|
type="primary" link
|
|
16
18
|
icon="Top"
|
|
17
|
-
:class="{'text-transparent':
|
|
18
|
-
@click="sortUp(
|
|
19
|
+
:class="{'text-transparent': !isSortableRow(row, $index,true)}"
|
|
20
|
+
@click="sortUp(row, $index)"
|
|
19
21
|
/>
|
|
20
22
|
<tnxel-button
|
|
21
23
|
type="primary" link
|
|
22
24
|
icon="Bottom"
|
|
23
25
|
class="ms-0"
|
|
24
|
-
:class="{'text-transparent':
|
|
25
|
-
@click="sortDown(
|
|
26
|
+
:class="{'text-transparent': !isSortableRow(row, $index, false)}"
|
|
27
|
+
@click="sortDown(row, $index)"
|
|
26
28
|
/>
|
|
27
29
|
</template>
|
|
28
30
|
</el-table-column>
|
|
29
31
|
<el-table-column label="移除" align="center" width="60px" v-if="removable">
|
|
30
|
-
<template #default="
|
|
32
|
+
<template #default="{row, $index}">
|
|
31
33
|
<tnxel-button
|
|
32
34
|
type="primary" link
|
|
33
35
|
icon="CircleClose"
|
|
34
|
-
:tooltip="
|
|
35
|
-
@click="
|
|
36
|
-
|
|
36
|
+
:tooltip="row.removeTip"
|
|
37
|
+
@click="removeRow(row, $index)"
|
|
38
|
+
v-if="isRemovableRow(row, $index)"/>
|
|
37
39
|
</template>
|
|
38
40
|
</el-table-column>
|
|
39
41
|
</el-table>
|
|
@@ -61,7 +63,7 @@ export default {
|
|
|
61
63
|
default: true,
|
|
62
64
|
},
|
|
63
65
|
removable: {
|
|
64
|
-
type: Boolean,
|
|
66
|
+
type: [Boolean, Function],
|
|
65
67
|
default: true,
|
|
66
68
|
},
|
|
67
69
|
addText: {
|
|
@@ -76,9 +78,11 @@ export default {
|
|
|
76
78
|
}
|
|
77
79
|
}
|
|
78
80
|
},
|
|
81
|
+
rowKey: [Function, String],
|
|
82
|
+
rowClassName: [Function, String],
|
|
79
83
|
selectable: Boolean, // 可否选择行
|
|
80
84
|
padding: Boolean, // 单元格中是否有边距
|
|
81
|
-
sortable: Boolean, // 可否调整行的顺序
|
|
85
|
+
sortable: [Boolean, Function], // 可否调整行的顺序
|
|
82
86
|
rules: [String, Object], // 加载字段校验规则的URL地址,或规则集对象
|
|
83
87
|
rulesApp: { // 加载字段校验规则的应用名称
|
|
84
88
|
type: String,
|
|
@@ -86,13 +90,30 @@ export default {
|
|
|
86
90
|
},
|
|
87
91
|
rulesLoaded: Function, // 规则集加载后的附加处理函数,仅在rule为字符串类型的URL地址时有效
|
|
88
92
|
},
|
|
93
|
+
emits: ['sorted', 'removed'],
|
|
89
94
|
data() {
|
|
90
95
|
return {
|
|
91
96
|
id: window.tnx.util.string.uuid32(),
|
|
92
97
|
selectedRow: null,
|
|
93
98
|
validationRules: {},
|
|
99
|
+
indexes: {}, // 附加索引映射集,key:row,value:一级节点为单个索引下标,多级节点为其在各级节点中的索引下标集合
|
|
94
100
|
}
|
|
95
101
|
},
|
|
102
|
+
computed: {
|
|
103
|
+
tableDataList() {
|
|
104
|
+
for (let rowIndex = 0; rowIndex < this.data.length; rowIndex++) {
|
|
105
|
+
let row = this.data[rowIndex];
|
|
106
|
+
this.indexes[this.getRowKey(row, rowIndex)] = rowIndex;
|
|
107
|
+
if (this.rowKey && row.children) {
|
|
108
|
+
for (let j = 0; j < row.children.length; j++) {
|
|
109
|
+
let child = row.children[j];
|
|
110
|
+
this.indexes[this.getRowKey(child)] = [rowIndex, j];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return this.data;
|
|
115
|
+
},
|
|
116
|
+
},
|
|
96
117
|
watch: {
|
|
97
118
|
data(newData, oldData) {
|
|
98
119
|
if (!oldData) {
|
|
@@ -121,8 +142,11 @@ export default {
|
|
|
121
142
|
});
|
|
122
143
|
},
|
|
123
144
|
methods: {
|
|
145
|
+
getRowKey(row, rowIndex) {
|
|
146
|
+
return this.rowKey ? row[this.rowKey] : (rowIndex + '');
|
|
147
|
+
},
|
|
124
148
|
initElements() {
|
|
125
|
-
this.focusRowFirstInput(0);
|
|
149
|
+
this.focusRowFirstInput(this.tableDataList[0]);
|
|
126
150
|
// 为富文本输入框添加默认title
|
|
127
151
|
let table = this.$refs.table.$el;
|
|
128
152
|
let textareas = table.getElementsByTagName('textarea');
|
|
@@ -280,23 +304,39 @@ export default {
|
|
|
280
304
|
this.selectedRow = row;
|
|
281
305
|
}
|
|
282
306
|
},
|
|
283
|
-
|
|
284
|
-
let
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
307
|
+
tableRowClassName(data) {
|
|
308
|
+
let classNames = [this.getRowClassName(data.row, data.$index)];
|
|
309
|
+
if (typeof this.rowClassName === 'function') {
|
|
310
|
+
classNames.push(this.rowClassName(data));
|
|
311
|
+
} else {
|
|
312
|
+
classNames.push(this.rowClassName);
|
|
313
|
+
}
|
|
314
|
+
return classNames.join(' ');
|
|
315
|
+
},
|
|
316
|
+
getRowClassName(row, rowIndex) {
|
|
317
|
+
let index = this.indexes[this.getRowKey(row, rowIndex)];
|
|
318
|
+
return 'tnxel-edit-table__row--' + (Array.isArray(index) ? index.join('_') : index);
|
|
319
|
+
},
|
|
320
|
+
focusRowFirstInput(row) {
|
|
321
|
+
if (row) {
|
|
322
|
+
let table = this.$refs.table.$el;
|
|
323
|
+
let rowClassName = this.getRowClassName(row);
|
|
324
|
+
let rowDoms = table.getElementsByClassName(rowClassName);
|
|
325
|
+
if (rowDoms.length) {
|
|
326
|
+
let rowDom = rowDoms[0];
|
|
327
|
+
if (rowDom) {
|
|
328
|
+
let cells = rowDom.getElementsByClassName('cell');
|
|
329
|
+
for (let cell of cells) {
|
|
330
|
+
let input = cell.querySelector('input:first-child');
|
|
331
|
+
if (input) {
|
|
332
|
+
input.focus();
|
|
299
333
|
return true;
|
|
334
|
+
} else {
|
|
335
|
+
let textarea = cell.querySelector('textarea:first-child');
|
|
336
|
+
if (textarea) {
|
|
337
|
+
textarea.focus();
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
300
340
|
}
|
|
301
341
|
}
|
|
302
342
|
}
|
|
@@ -306,33 +346,102 @@ export default {
|
|
|
306
346
|
},
|
|
307
347
|
toAddRow() {
|
|
308
348
|
let row = this.newRow();
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
this.
|
|
312
|
-
this.
|
|
313
|
-
|
|
349
|
+
if (row) {
|
|
350
|
+
let rowIndex = this.data.length;
|
|
351
|
+
this.indexes[this.getRowKey(row, rowIndex)] = rowIndex;
|
|
352
|
+
this.data.push(row);
|
|
353
|
+
this.$nextTick(() => {
|
|
354
|
+
setTimeout(() => {
|
|
355
|
+
this.bindElementRules();
|
|
356
|
+
this.focusRowFirstInput(row);
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
}
|
|
314
360
|
},
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
this.focusRowFirstInput(index - 1);
|
|
361
|
+
isRemovableRow(row, rowIndex) {
|
|
362
|
+
if (typeof this.removable === 'function') {
|
|
363
|
+
let index = this.indexes[this.getRowKey(row, rowIndex)];
|
|
364
|
+
let removable = this.removable(row, index);
|
|
365
|
+
if (removable !== undefined) {
|
|
366
|
+
return removable;
|
|
322
367
|
}
|
|
368
|
+
// 自定义函数如果返回undefined,则使用默认判断逻辑
|
|
369
|
+
}
|
|
370
|
+
return !!this.removable;
|
|
371
|
+
},
|
|
372
|
+
removeRow(row, rowIndex) {
|
|
373
|
+
this.locateRow(row, rowIndex, (dataList, index) => {
|
|
374
|
+
delete this.indexes[this.getRowKey(row, rowIndex)];
|
|
375
|
+
dataList.splice(index, 1);
|
|
376
|
+
this.$emit('removed', row, rowIndex);
|
|
377
|
+
this.$nextTick(() => {
|
|
378
|
+
setTimeout(() => {
|
|
379
|
+
let next = dataList[index] || dataList[index - 1];
|
|
380
|
+
this.focusRowFirstInput(next);
|
|
381
|
+
});
|
|
382
|
+
});
|
|
323
383
|
});
|
|
324
384
|
},
|
|
325
|
-
|
|
326
|
-
if (
|
|
327
|
-
|
|
328
|
-
|
|
385
|
+
locateRow(row, rowIndex, func) {
|
|
386
|
+
if (typeof rowIndex === 'function') {
|
|
387
|
+
func = rowIndex;
|
|
388
|
+
rowIndex = undefined;
|
|
329
389
|
}
|
|
390
|
+
let indexes = this.indexes[this.getRowKey(row, rowIndex)];
|
|
391
|
+
if (!Array.isArray(indexes)) {
|
|
392
|
+
indexes = [indexes];
|
|
393
|
+
}
|
|
394
|
+
let dataList = this.tableDataList;
|
|
395
|
+
for (let i = 0; i < indexes.length - 1; i++) {
|
|
396
|
+
dataList = dataList[indexes[i]].children;
|
|
397
|
+
}
|
|
398
|
+
let index = indexes[indexes.length - 1];
|
|
399
|
+
return func(dataList, index);
|
|
330
400
|
},
|
|
331
|
-
|
|
332
|
-
if (
|
|
333
|
-
let
|
|
334
|
-
this.
|
|
401
|
+
isSortableRow(row, rowIndex, up) {
|
|
402
|
+
if (typeof this.sortable === 'function') {
|
|
403
|
+
let index = this.indexes[this.getRowKey(row, rowIndex)];
|
|
404
|
+
let sortable = this.sortable(row, index, up);
|
|
405
|
+
if (sortable !== undefined) {
|
|
406
|
+
return sortable;
|
|
407
|
+
}
|
|
408
|
+
// 自定义函数如果返回undefined,则使用默认判断逻辑
|
|
409
|
+
}
|
|
410
|
+
if (this.sortable) {
|
|
411
|
+
if (up) {
|
|
412
|
+
let index = this.indexes[this.getRowKey(row, rowIndex)];
|
|
413
|
+
if (Array.isArray(index)) {
|
|
414
|
+
index = index[index.length - 1];
|
|
415
|
+
}
|
|
416
|
+
return index > 0;
|
|
417
|
+
} else {
|
|
418
|
+
return this.locateRow(row, rowIndex, (dataList, index) => {
|
|
419
|
+
return index < dataList.length - 1;
|
|
420
|
+
});
|
|
421
|
+
}
|
|
335
422
|
}
|
|
423
|
+
return false;
|
|
424
|
+
},
|
|
425
|
+
sortUp(row, rowIndex) {
|
|
426
|
+
this.locateRow(row, rowIndex, (dataList, index) => {
|
|
427
|
+
if (index > 0) {
|
|
428
|
+
let deleted = dataList.splice(index, 1);
|
|
429
|
+
dataList.splice(index - 1, 0, deleted[0]);
|
|
430
|
+
this.$emit('sorted', row, rowIndex, true);
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
},
|
|
434
|
+
sortDown(row, rowIndex) {
|
|
435
|
+
this.locateRow(row, rowIndex, (dataList, index) => {
|
|
436
|
+
if (index < dataList.length - 1) {
|
|
437
|
+
let deleted = dataList.splice(index, 1);
|
|
438
|
+
dataList.splice(index + 1, 0, deleted[0]);
|
|
439
|
+
this.$emit('sorted', row, rowIndex, false);
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
},
|
|
443
|
+
toggleRowExpansion(row, expanded) {
|
|
444
|
+
this.$refs.table.toggleRowExpansion(row, expanded);
|
|
336
445
|
},
|
|
337
446
|
}
|
|
338
447
|
}
|
|
@@ -351,6 +460,12 @@ export default {
|
|
|
351
460
|
height: 32px;
|
|
352
461
|
}
|
|
353
462
|
|
|
463
|
+
.tnxel-edit-table-container .el-table.padding-none .el-table__body-wrapper .el-input-group__append,
|
|
464
|
+
.tnxel-edit-table-container .el-table.padding-none .el-table__body-wrapper .el-input-group__prepend {
|
|
465
|
+
border-radius: 0;
|
|
466
|
+
padding: 0 16px;
|
|
467
|
+
}
|
|
468
|
+
|
|
354
469
|
.tnxel-edit-table-container .el-table.padding-none .el-table__body-wrapper .el-input--small .el-input__wrapper,
|
|
355
470
|
.tnxel-edit-table-container .el-table.padding-none .el-table__body-wrapper .el-input--small .el-select__wrapper {
|
|
356
471
|
height: 24px;
|
|
@@ -377,6 +492,45 @@ export default {
|
|
|
377
492
|
text-align: left;
|
|
378
493
|
}
|
|
379
494
|
|
|
495
|
+
.tnxel-edit-table-container .el-table .cell {
|
|
496
|
+
display: flex;
|
|
497
|
+
align-items: center;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
.tnxel-edit-table-container .el-table .is-center .cell {
|
|
501
|
+
justify-content: center;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.tnxel-edit-table-container .el-table .el-table__placeholder {
|
|
505
|
+
width: 20px;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
.tnxel-edit-table-container .el-table .el-table__indent + .el-table__placeholder {
|
|
509
|
+
width: 16px;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
.tnxel-edit-table-container .el-table .is-center .el-table__placeholder {
|
|
513
|
+
width: 16px;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
.tnxel-edit-table-container .el-table .is-center .el-table__indent + .el-table__placeholder {
|
|
517
|
+
width: 24px;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
.tnxel-edit-table-container .el-table [class*=el-table__row--level] .el-table__expand-icon {
|
|
521
|
+
margin-left: 4px;
|
|
522
|
+
margin-right: 4px;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
.tnxel-edit-table-container .el-table [class*=el-table__row--level] .is-center .el-table__expand-icon {
|
|
526
|
+
margin-left: 0;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
.tnxel-edit-table-container .tnxel-edit-table .el-table__expand-icon + div {
|
|
530
|
+
width: auto;
|
|
531
|
+
flex-grow: 1;
|
|
532
|
+
}
|
|
533
|
+
|
|
380
534
|
.tnxel-edit-table-container .el-table .is-error {
|
|
381
535
|
box-shadow: 0 0 0 1px var(--el-color-danger) inset;
|
|
382
536
|
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="tnxel-input-number" :class="containerClassObject">
|
|
3
3
|
<el-input-number ref="input" class="flex-grow-1" :class="{'rounded-end-0': suffix, 'text-start': !controls}"
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
4
|
+
v-model="model"
|
|
5
|
+
:min="min" :max="max"
|
|
6
|
+
:controls="controls" controls-position="right"
|
|
7
|
+
:placeholder="placeholderText" :disabled="disabled"
|
|
8
|
+
:step="step || Math.pow(10, -this.scale)" step-strictly
|
|
9
|
+
:precision="scale"
|
|
10
|
+
:value-on-clear="null"
|
|
11
|
+
:size="size"
|
|
12
|
+
@change="onChange"
|
|
13
|
+
@blur="$emit('blur', $event)"/>
|
|
14
14
|
<div class="el-input-group__append" v-if="suffix">{{ suffix }}</div>
|
|
15
15
|
</div>
|
|
16
16
|
</template>
|
|
@@ -41,7 +41,7 @@ export default {
|
|
|
41
41
|
required: Boolean,
|
|
42
42
|
size: String,
|
|
43
43
|
},
|
|
44
|
-
emits: ['update:modelValue', 'blur'],
|
|
44
|
+
emits: ['update:modelValue', 'blur', 'change'],
|
|
45
45
|
data() {
|
|
46
46
|
return {
|
|
47
47
|
model: typeof this.modelValue === 'string' ? Number(this.modelValue) : this.modelValue,
|
|
@@ -103,7 +103,11 @@ export default {
|
|
|
103
103
|
this.$refs.input.focus();
|
|
104
104
|
},
|
|
105
105
|
onChange() {
|
|
106
|
-
this.validateRequired(false)
|
|
106
|
+
if (this.validateRequired(false)) {
|
|
107
|
+
this.$nextTick(() => {
|
|
108
|
+
this.$emit('change', this.modelValue);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
107
111
|
},
|
|
108
112
|
}
|
|
109
113
|
}
|
package/src/tnxvue.js
CHANGED
|
@@ -38,6 +38,42 @@ export default build('tnxvue', () => {
|
|
|
38
38
|
window.tnx.app.page.stopCache(router, from.path);
|
|
39
39
|
}
|
|
40
40
|
},
|
|
41
|
+
/**
|
|
42
|
+
* 深度监听指定对象,在created()中调用才能生效
|
|
43
|
+
* @param vm 页面vue实例
|
|
44
|
+
* @param target 要监听的对象
|
|
45
|
+
* @param handler 处理函数
|
|
46
|
+
*/
|
|
47
|
+
deepWatch(vm, target, handler) {
|
|
48
|
+
vm.$watch(() => JSON.stringify(target), (newValue, oldValue) => {
|
|
49
|
+
if (newValue !== oldValue) {
|
|
50
|
+
const newObject = JSON.parse(newValue);
|
|
51
|
+
const oldObject = JSON.parse(oldValue);
|
|
52
|
+
this.deepCompare(newObject, oldObject, '', handler);
|
|
53
|
+
}
|
|
54
|
+
}, {deep: true});
|
|
55
|
+
},
|
|
56
|
+
deepCompare(object1, object2, path = '', handler) {
|
|
57
|
+
if (object1) {
|
|
58
|
+
const keys = Object.keys(object1);
|
|
59
|
+
keys.forEach(key => {
|
|
60
|
+
const fullPath = path ? `${path}.${key}` : key;
|
|
61
|
+
if (object2) {
|
|
62
|
+
if (Array.isArray(object1[key]) && Array.isArray(object2[key])) {
|
|
63
|
+
object1[key].forEach((item, index) => {
|
|
64
|
+
this.deepCompare(item, object2[key][index], `${fullPath}[${index}]`, handler);
|
|
65
|
+
});
|
|
66
|
+
} else if (typeof object1[key] === 'object' && typeof object2[key] === 'object') {
|
|
67
|
+
this.deepCompare(object1[key], object2[key], fullPath, handler);
|
|
68
|
+
} else if (object1[key] !== object2[key]) {
|
|
69
|
+
handler(object1[key], object2[key], fullPath);
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
handler(object1[key], undefined, fullPath);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
},
|
|
41
77
|
createVueInstance(rootComponent, router, rootProps) {
|
|
42
78
|
let vm = Vue.createApp(rootComponent, rootProps);
|
|
43
79
|
vm.use(this);
|