xt-element-ui 2.1.5 → 2.1.6

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 (30) hide show
  1. package/docs/components/base/xt-date-picker.md +127 -37
  2. package/docs/components/base/xt-form-schema.md +358 -0
  3. package/docs/components/base/xt-grid-box.md +4 -4
  4. package/docs/components/base/xt-input.md +204 -42
  5. package/docs/components/base/xt-list.md +458 -458
  6. package/docs/components/base/xt-text.md +1 -0
  7. package/docs/components/utils/config.md +285 -0
  8. package/docs/components/utils/format.md +445 -0
  9. package/lib/index.common.js +2303 -8625
  10. package/lib/index.css +1 -1
  11. package/lib/index.umd.js +2303 -8625
  12. package/lib/index.umd.min.js +1 -1
  13. package/package.json +4 -2
  14. package/src/components/xt-date-picker/component/Picker.vue +196 -0
  15. package/src/components/xt-date-picker/component/RangeDate.vue +136 -0
  16. package/src/components/xt-date-picker/index.vue +164 -144
  17. package/src/components/xt-flex-box/index.vue +1 -1
  18. package/src/components/xt-form-schema/index.js +8 -0
  19. package/src/components/xt-form-schema/index.vue +328 -0
  20. package/src/components/xt-grid-item/index.vue +2 -2
  21. package/src/components/xt-input/index.vue +224 -28
  22. package/src/components/xt-input/style/index.scss +10 -0
  23. package/src/components/xt-list/index.js +7 -7
  24. package/src/components/xt-list/index.vue +885 -885
  25. package/src/components/xt-transfer-tree/index.js +7 -7
  26. package/src/components/xt-transfer-tree/index.vue +493 -493
  27. package/src/index.js +5 -2
  28. package/src/components/xt-date-picker/SearchDate.vue +0 -45
  29. package/src/components/xt-date-picker/quarter.vue +0 -154
  30. package/src/components/xt-table/index copy.vue +0 -663
@@ -1,494 +1,494 @@
1
- <template>
2
- <div class="xt-transfer-tree">
3
- <div class="xt-transfer-tree__panel xt-transfer-tree__source">
4
- <div class="xt-transfer-tree__header">
5
- <span class="xt-transfer-tree__title">{{ leftTitle }}</span>
6
- <span class="xt-transfer-tree__count">{{ leftCheckedCount }}/{{ leftTotalCount }}</span>
7
- </div>
8
- <div class="xt-transfer-tree__search" v-if="filterable">
9
- <el-input
10
- v-model="leftFilterText"
11
- placeholder="搜索"
12
- prefix-icon="el-icon-search"
13
- size="small"
14
- />
15
- </div>
16
- <div class="xt-transfer-tree__body">
17
- <el-tree
18
- ref="leftTree"
19
- :data="sourceData"
20
- :props="treeProps"
21
- :default-expand-all="defaultExpandAll"
22
- :filter-node-method="filterLeftNode"
23
- :show-checkbox="showCheckbox"
24
- :check-strictly="!cascade"
25
- :default-checked-keys="leftCheckedKeys"
26
- @check-change="handleLeftCheckChange"
27
- @node-click="handleLeftNodeClick"
28
- >
29
- <span class="xt-transfer-tree__node" slot-scope="{ node, data }">
30
- <span>{{ node.label }}</span>
31
- <span v-if="data.children && data.children.length" class="xt-transfer-tree__node-count">
32
- ({{ data.children.length }})
33
- </span>
34
- </span>
35
- </el-tree>
36
- </div>
37
- </div>
38
-
39
- <div class="xt-transfer-tree__buttons">
40
- <el-button
41
- type="primary"
42
- :disabled="!leftCheckedCount"
43
- @click="transferToRight"
44
- :size="buttonSize"
45
- >
46
- <i class="el-icon-arrow-right"></i>
47
- </el-button>
48
- <el-button
49
- type="primary"
50
- :disabled="!rightCheckedCount"
51
- @click="transferToLeft"
52
- :size="buttonSize"
53
- >
54
- <i class="el-icon-arrow-left"></i>
55
- </el-button>
56
- </div>
57
-
58
- <div class="xt-transfer-tree__panel xt-transfer-tree__target">
59
- <div class="xt-transfer-tree__header">
60
- <span class="xt-transfer-tree__title">{{ rightTitle }}</span>
61
- <span class="xt-transfer-tree__count">{{ rightCheckedCount }}/{{ rightTotalCount }}</span>
62
- </div>
63
- <div class="xt-transfer-tree__search" v-if="filterable">
64
- <el-input
65
- v-model="rightFilterText"
66
- placeholder="搜索"
67
- prefix-icon="el-icon-search"
68
- size="small"
69
- />
70
- </div>
71
- <div class="xt-transfer-tree__body">
72
- <el-tree
73
- ref="rightTree"
74
- :data="targetData"
75
- :props="treeProps"
76
- :default-expand-all="defaultExpandAll"
77
- :filter-node-method="filterRightNode"
78
- :show-checkbox="showCheckbox"
79
- :check-strictly="!cascade"
80
- :default-checked-keys="rightCheckedKeys"
81
- @check-change="handleRightCheckChange"
82
- @node-click="handleRightNodeClick"
83
- >
84
- <span class="xt-transfer-tree__node" slot-scope="{ node, data }">
85
- <span>{{ node.label }}</span>
86
- <span v-if="data.children && data.children.length" class="xt-transfer-tree__node-count">
87
- ({{ data.children.length }})
88
- </span>
89
- </span>
90
- </el-tree>
91
- </div>
92
- </div>
93
- </div>
94
- </template>
95
-
96
- <script>
97
- export default {
98
- name: 'XtTransferTree',
99
-
100
- props: {
101
- data: {
102
- type: Array,
103
- default: () => []
104
- },
105
- value: {
106
- type: Array,
107
- default: () => []
108
- },
109
- leftTitle: {
110
- type: String,
111
- default: '待选择'
112
- },
113
- rightTitle: {
114
- type: String,
115
- default: '已选择'
116
- },
117
- treeProps: {
118
- type: Object,
119
- default: () => ({
120
- label: 'label',
121
- children: 'children',
122
- value: 'id'
123
- })
124
- },
125
- defaultExpandAll: {
126
- type: Boolean,
127
- default: true
128
- },
129
- filterable: {
130
- type: Boolean,
131
- default: false
132
- },
133
- showCheckbox: {
134
- type: Boolean,
135
- default: true
136
- },
137
- cascade: {
138
- type: Boolean,
139
- default: true
140
- },
141
- transferMode: {
142
- type: String,
143
- default: 'single',
144
- validator: (val) => ['single', 'multiple', 'parent-child'].includes(val)
145
- },
146
- buttonSize: {
147
- type: String,
148
- default: 'small'
149
- }
150
- },
151
-
152
- data() {
153
- return {
154
- leftFilterText: '',
155
- rightFilterText: '',
156
- leftCheckedKeys: [],
157
- rightCheckedKeys: []
158
- }
159
- },
160
-
161
- computed: {
162
- sourceData() {
163
- const valueSet = new Set(this.value)
164
- return this.filterTreeData(this.data, valueSet, true)
165
- },
166
-
167
- targetData() {
168
- const valueSet = new Set(this.value)
169
- return this.filterTreeData(this.data, valueSet, false)
170
- },
171
-
172
- leftTotalCount() {
173
- return this.countTreeNodes(this.sourceData)
174
- },
175
-
176
- rightTotalCount() {
177
- return this.countTreeNodes(this.targetData)
178
- },
179
-
180
- leftCheckedCount() {
181
- return this.leftCheckedKeys.length
182
- },
183
-
184
- rightCheckedCount() {
185
- return this.rightCheckedKeys.length
186
- }
187
- },
188
-
189
- watch: {
190
- leftFilterText(val) {
191
- this.$refs.leftTree && this.$refs.leftTree.filter(val)
192
- },
193
- rightFilterText(val) {
194
- this.$refs.rightTree && this.$refs.rightTree.filter(val)
195
- }
196
- },
197
-
198
- methods: {
199
- filterTreeData(data, valueSet, exclude) {
200
- return data
201
- .map(node => {
202
- const isTarget = valueSet.has(node[this.treeProps.value])
203
- if (exclude && isTarget) return null
204
- if (!exclude && !isTarget) return null
205
-
206
- const result = { ...node }
207
- if (node.children && node.children.length) {
208
- const filteredChildren = this.filterTreeData(node.children, valueSet, exclude)
209
- if (filteredChildren.length) {
210
- result.children = filteredChildren
211
- } else {
212
- delete result.children
213
- }
214
- }
215
- return result
216
- })
217
- .filter(Boolean)
218
- },
219
-
220
- countTreeNodes(data) {
221
- let count = 0
222
- data.forEach(node => {
223
- count++
224
- if (node.children) {
225
- count += this.countTreeNodes(node.children)
226
- }
227
- })
228
- return count
229
- },
230
-
231
- getAllKeys(node, keys = []) {
232
- keys.push(node[this.treeProps.value])
233
- if (node.children) {
234
- node.children.forEach(child => this.getAllKeys(child, keys))
235
- }
236
- return keys
237
- },
238
-
239
- getParentKeys(data, key, parentKeys = []) {
240
- for (const node of data) {
241
- if (node[this.treeProps.value] === key) {
242
- return parentKeys
243
- }
244
- if (node.children) {
245
- const result = this.getParentKeys(node.children, key, [...parentKeys, node[this.treeProps.value]])
246
- if (result.length > 0) return result
247
- }
248
- }
249
- return []
250
- },
251
-
252
- filterLeftNode(value, data) {
253
- if (!value) return true
254
- return String(data[this.treeProps.label]).toLowerCase().indexOf(value.toLowerCase()) !== -1
255
- },
256
-
257
- filterRightNode(value, data) {
258
- if (!value) return true
259
- return String(data[this.treeProps.label]).toLowerCase().indexOf(value.toLowerCase()) !== -1
260
- },
261
-
262
- handleLeftCheckChange(data, checked) {
263
- const key = data[this.treeProps.value]
264
- if (checked) {
265
- if (!this.leftCheckedKeys.includes(key)) {
266
- this.leftCheckedKeys.push(key)
267
- }
268
- if (this.cascade && data.children) {
269
- data.children.forEach(child => {
270
- const childKey = child[this.treeProps.value]
271
- if (!this.leftCheckedKeys.includes(childKey)) {
272
- this.leftCheckedKeys.push(childKey)
273
- }
274
- })
275
- }
276
- } else {
277
- this.leftCheckedKeys = this.leftCheckedKeys.filter(k => k !== key)
278
- if (this.cascade && data.children) {
279
- data.children.forEach(child => {
280
- this.leftCheckedKeys = this.leftCheckedKeys.filter(k => k !== child[this.treeProps.value])
281
- })
282
- }
283
- }
284
- },
285
-
286
- handleRightCheckChange(data, checked) {
287
- const key = data[this.treeProps.value]
288
- if (checked) {
289
- if (!this.rightCheckedKeys.includes(key)) {
290
- this.rightCheckedKeys.push(key)
291
- }
292
- if (this.cascade && data.children) {
293
- data.children.forEach(child => {
294
- const childKey = child[this.treeProps.value]
295
- if (!this.rightCheckedKeys.includes(childKey)) {
296
- this.rightCheckedKeys.push(childKey)
297
- }
298
- })
299
- }
300
- } else {
301
- this.rightCheckedKeys = this.rightCheckedKeys.filter(k => k !== key)
302
- if (this.cascade && data.children) {
303
- data.children.forEach(child => {
304
- this.rightCheckedKeys = this.rightCheckedKeys.filter(k => k !== child[this.treeProps.value])
305
- })
306
- }
307
- }
308
- },
309
-
310
- handleLeftNodeClick(data) {
311
- if (!this.showCheckbox) {
312
- const key = data[this.treeProps.value]
313
- const keysToTransfer = this.getTransferKeys(data)
314
- this.transferKeys(keysToTransfer, true)
315
- }
316
- },
317
-
318
- handleRightNodeClick(data) {
319
- if (!this.showCheckbox) {
320
- const key = data[this.treeProps.value]
321
- const keysToTransfer = this.getTransferKeys(data)
322
- this.transferKeys(keysToTransfer, false)
323
- }
324
- },
325
-
326
- getTransferKeys(data) {
327
- const key = data[this.treeProps.value]
328
- switch (this.transferMode) {
329
- case 'parent-child':
330
- return this.getAllKeys(data)
331
- case 'multiple':
332
- return [key]
333
- default:
334
- return [key]
335
- }
336
- },
337
-
338
- transferToRight() {
339
- const keys = [...this.leftCheckedKeys]
340
- this.transferKeys(keys, true)
341
- this.leftCheckedKeys = []
342
- },
343
-
344
- transferToLeft() {
345
- const keys = [...this.rightCheckedKeys]
346
- this.transferKeys(keys, false)
347
- this.rightCheckedKeys = []
348
- },
349
-
350
- transferKeys(keys, toRight) {
351
- const currentValue = [...this.value]
352
- const newKeys = new Set(currentValue)
353
-
354
- if (toRight) {
355
- keys.forEach(k => newKeys.add(k))
356
- if (this.cascade) {
357
- keys.forEach(k => {
358
- const parentKeys = this.getParentKeys(this.data, k)
359
- parentKeys.forEach(pk => newKeys.add(pk))
360
- })
361
- }
362
- } else {
363
- keys.forEach(k => newKeys.delete(k))
364
- if (this.cascade) {
365
- keys.forEach(k => {
366
- const allChildKeys = []
367
- this.findAllChildKeys(this.data, k, allChildKeys)
368
- allChildKeys.forEach(ck => newKeys.delete(ck))
369
- })
370
- }
371
- }
372
-
373
- const newValue = Array.from(newKeys)
374
- this.$emit('update:value', newValue)
375
- this.$emit('change', {
376
- value: newValue,
377
- addedKeys: toRight ? keys : [],
378
- removedKeys: toRight ? [] : keys
379
- })
380
- },
381
-
382
- findAllChildKeys(data, parentKey, childKeys) {
383
- for (const node of data) {
384
- if (node[this.treeProps.value] === parentKey && node.children) {
385
- node.children.forEach(child => {
386
- childKeys.push(child[this.treeProps.value])
387
- if (child.children) {
388
- this.findAllChildKeys([child], child[this.treeProps.value], childKeys)
389
- }
390
- })
391
- }
392
- if (node.children) {
393
- this.findAllChildKeys(node.children, parentKey, childKeys)
394
- }
395
- }
396
- },
397
-
398
- clearSelection() {
399
- this.leftCheckedKeys = []
400
- this.rightCheckedKeys = []
401
- }
402
- }
403
- }
404
- </script>
405
-
406
- <style lang="scss" scoped>
407
- .xt-transfer-tree {
408
- display: flex;
409
- align-items: stretch;
410
- width: 100%;
411
- height: 100%;
412
- }
413
-
414
- .xt-transfer-tree__panel {
415
- flex: 1;
416
- display: flex;
417
- flex-direction: column;
418
- border: 1px solid #ebeef5;
419
- border-radius: 4px;
420
- overflow: hidden;
421
- }
422
-
423
- .xt-transfer-tree__source {
424
- margin-right: 8px;
425
- }
426
-
427
- .xt-transfer-tree__target {
428
- margin-left: 8px;
429
- }
430
-
431
- .xt-transfer-tree__header {
432
- display: flex;
433
- align-items: center;
434
- justify-content: space-between;
435
- padding: 12px;
436
- background: #f5f7fa;
437
- border-bottom: 1px solid #ebeef5;
438
- }
439
-
440
- .xt-transfer-tree__title {
441
- font-size: 14px;
442
- font-weight: 600;
443
- color: #303133;
444
- }
445
-
446
- .xt-transfer-tree__count {
447
- font-size: 12px;
448
- color: #909399;
449
- }
450
-
451
- .xt-transfer-tree__search {
452
- padding: 8px 12px;
453
- border-bottom: 1px solid #ebeef5;
454
- }
455
-
456
- .xt-transfer-tree__body {
457
- flex: 1;
458
- overflow-y: auto;
459
- padding: 8px;
460
- }
461
-
462
- .xt-transfer-tree__buttons {
463
- display: flex;
464
- flex-direction: column;
465
- justify-content: center;
466
- align-items: center;
467
- padding: 8px;
468
- gap: 8px;
469
- }
470
-
471
- .xt-transfer-tree__node {
472
- display: flex;
473
- align-items: center;
474
- gap: 4px;
475
- }
476
-
477
- .xt-transfer-tree__node-count {
478
- font-size: 12px;
479
- color: #909399;
480
- }
481
-
482
- .xt-transfer-tree__body::-webkit-scrollbar {
483
- width: 6px;
484
- }
485
-
486
- .xt-transfer-tree__body::-webkit-scrollbar-thumb {
487
- background: #c1c1c1;
488
- border-radius: 3px;
489
- }
490
-
491
- .xt-transfer-tree__body::-webkit-scrollbar-track {
492
- background: transparent;
493
- }
1
+ <template>
2
+ <div class="xt-transfer-tree">
3
+ <div class="xt-transfer-tree__panel xt-transfer-tree__source">
4
+ <div class="xt-transfer-tree__header">
5
+ <span class="xt-transfer-tree__title">{{ leftTitle }}</span>
6
+ <span class="xt-transfer-tree__count">{{ leftCheckedCount }}/{{ leftTotalCount }}</span>
7
+ </div>
8
+ <div class="xt-transfer-tree__search" v-if="filterable">
9
+ <el-input
10
+ v-model="leftFilterText"
11
+ placeholder="搜索"
12
+ prefix-icon="el-icon-search"
13
+ size="small"
14
+ />
15
+ </div>
16
+ <div class="xt-transfer-tree__body">
17
+ <el-tree
18
+ ref="leftTree"
19
+ :data="sourceData"
20
+ :props="treeProps"
21
+ :default-expand-all="defaultExpandAll"
22
+ :filter-node-method="filterLeftNode"
23
+ :show-checkbox="showCheckbox"
24
+ :check-strictly="!cascade"
25
+ :default-checked-keys="leftCheckedKeys"
26
+ @check-change="handleLeftCheckChange"
27
+ @node-click="handleLeftNodeClick"
28
+ >
29
+ <span class="xt-transfer-tree__node" slot-scope="{ node, data }">
30
+ <span>{{ node.label }}</span>
31
+ <span v-if="data.children && data.children.length" class="xt-transfer-tree__node-count">
32
+ ({{ data.children.length }})
33
+ </span>
34
+ </span>
35
+ </el-tree>
36
+ </div>
37
+ </div>
38
+
39
+ <div class="xt-transfer-tree__buttons">
40
+ <el-button
41
+ type="primary"
42
+ :disabled="!leftCheckedCount"
43
+ @click="transferToRight"
44
+ :size="buttonSize"
45
+ >
46
+ <i class="el-icon-arrow-right"></i>
47
+ </el-button>
48
+ <el-button
49
+ type="primary"
50
+ :disabled="!rightCheckedCount"
51
+ @click="transferToLeft"
52
+ :size="buttonSize"
53
+ >
54
+ <i class="el-icon-arrow-left"></i>
55
+ </el-button>
56
+ </div>
57
+
58
+ <div class="xt-transfer-tree__panel xt-transfer-tree__target">
59
+ <div class="xt-transfer-tree__header">
60
+ <span class="xt-transfer-tree__title">{{ rightTitle }}</span>
61
+ <span class="xt-transfer-tree__count">{{ rightCheckedCount }}/{{ rightTotalCount }}</span>
62
+ </div>
63
+ <div class="xt-transfer-tree__search" v-if="filterable">
64
+ <el-input
65
+ v-model="rightFilterText"
66
+ placeholder="搜索"
67
+ prefix-icon="el-icon-search"
68
+ size="small"
69
+ />
70
+ </div>
71
+ <div class="xt-transfer-tree__body">
72
+ <el-tree
73
+ ref="rightTree"
74
+ :data="targetData"
75
+ :props="treeProps"
76
+ :default-expand-all="defaultExpandAll"
77
+ :filter-node-method="filterRightNode"
78
+ :show-checkbox="showCheckbox"
79
+ :check-strictly="!cascade"
80
+ :default-checked-keys="rightCheckedKeys"
81
+ @check-change="handleRightCheckChange"
82
+ @node-click="handleRightNodeClick"
83
+ >
84
+ <span class="xt-transfer-tree__node" slot-scope="{ node, data }">
85
+ <span>{{ node.label }}</span>
86
+ <span v-if="data.children && data.children.length" class="xt-transfer-tree__node-count">
87
+ ({{ data.children.length }})
88
+ </span>
89
+ </span>
90
+ </el-tree>
91
+ </div>
92
+ </div>
93
+ </div>
94
+ </template>
95
+
96
+ <script>
97
+ export default {
98
+ name: 'XtTransferTree',
99
+
100
+ props: {
101
+ data: {
102
+ type: Array,
103
+ default: () => []
104
+ },
105
+ value: {
106
+ type: Array,
107
+ default: () => []
108
+ },
109
+ leftTitle: {
110
+ type: String,
111
+ default: '待选择'
112
+ },
113
+ rightTitle: {
114
+ type: String,
115
+ default: '已选择'
116
+ },
117
+ treeProps: {
118
+ type: Object,
119
+ default: () => ({
120
+ label: 'label',
121
+ children: 'children',
122
+ value: 'id'
123
+ })
124
+ },
125
+ defaultExpandAll: {
126
+ type: Boolean,
127
+ default: true
128
+ },
129
+ filterable: {
130
+ type: Boolean,
131
+ default: false
132
+ },
133
+ showCheckbox: {
134
+ type: Boolean,
135
+ default: true
136
+ },
137
+ cascade: {
138
+ type: Boolean,
139
+ default: true
140
+ },
141
+ transferMode: {
142
+ type: String,
143
+ default: 'single',
144
+ validator: (val) => ['single', 'multiple', 'parent-child'].includes(val)
145
+ },
146
+ buttonSize: {
147
+ type: String,
148
+ default: 'small'
149
+ }
150
+ },
151
+
152
+ data() {
153
+ return {
154
+ leftFilterText: '',
155
+ rightFilterText: '',
156
+ leftCheckedKeys: [],
157
+ rightCheckedKeys: []
158
+ }
159
+ },
160
+
161
+ computed: {
162
+ sourceData() {
163
+ const valueSet = new Set(this.value)
164
+ return this.filterTreeData(this.data, valueSet, true)
165
+ },
166
+
167
+ targetData() {
168
+ const valueSet = new Set(this.value)
169
+ return this.filterTreeData(this.data, valueSet, false)
170
+ },
171
+
172
+ leftTotalCount() {
173
+ return this.countTreeNodes(this.sourceData)
174
+ },
175
+
176
+ rightTotalCount() {
177
+ return this.countTreeNodes(this.targetData)
178
+ },
179
+
180
+ leftCheckedCount() {
181
+ return this.leftCheckedKeys.length
182
+ },
183
+
184
+ rightCheckedCount() {
185
+ return this.rightCheckedKeys.length
186
+ }
187
+ },
188
+
189
+ watch: {
190
+ leftFilterText(val) {
191
+ this.$refs.leftTree && this.$refs.leftTree.filter(val)
192
+ },
193
+ rightFilterText(val) {
194
+ this.$refs.rightTree && this.$refs.rightTree.filter(val)
195
+ }
196
+ },
197
+
198
+ methods: {
199
+ filterTreeData(data, valueSet, exclude) {
200
+ return data
201
+ .map(node => {
202
+ const isTarget = valueSet.has(node[this.treeProps.value])
203
+ if (exclude && isTarget) return null
204
+ if (!exclude && !isTarget) return null
205
+
206
+ const result = { ...node }
207
+ if (node.children && node.children.length) {
208
+ const filteredChildren = this.filterTreeData(node.children, valueSet, exclude)
209
+ if (filteredChildren.length) {
210
+ result.children = filteredChildren
211
+ } else {
212
+ delete result.children
213
+ }
214
+ }
215
+ return result
216
+ })
217
+ .filter(Boolean)
218
+ },
219
+
220
+ countTreeNodes(data) {
221
+ let count = 0
222
+ data.forEach(node => {
223
+ count++
224
+ if (node.children) {
225
+ count += this.countTreeNodes(node.children)
226
+ }
227
+ })
228
+ return count
229
+ },
230
+
231
+ getAllKeys(node, keys = []) {
232
+ keys.push(node[this.treeProps.value])
233
+ if (node.children) {
234
+ node.children.forEach(child => this.getAllKeys(child, keys))
235
+ }
236
+ return keys
237
+ },
238
+
239
+ getParentKeys(data, key, parentKeys = []) {
240
+ for (const node of data) {
241
+ if (node[this.treeProps.value] === key) {
242
+ return parentKeys
243
+ }
244
+ if (node.children) {
245
+ const result = this.getParentKeys(node.children, key, [...parentKeys, node[this.treeProps.value]])
246
+ if (result.length > 0) return result
247
+ }
248
+ }
249
+ return []
250
+ },
251
+
252
+ filterLeftNode(value, data) {
253
+ if (!value) return true
254
+ return String(data[this.treeProps.label]).toLowerCase().indexOf(value.toLowerCase()) !== -1
255
+ },
256
+
257
+ filterRightNode(value, data) {
258
+ if (!value) return true
259
+ return String(data[this.treeProps.label]).toLowerCase().indexOf(value.toLowerCase()) !== -1
260
+ },
261
+
262
+ handleLeftCheckChange(data, checked) {
263
+ const key = data[this.treeProps.value]
264
+ if (checked) {
265
+ if (!this.leftCheckedKeys.includes(key)) {
266
+ this.leftCheckedKeys.push(key)
267
+ }
268
+ if (this.cascade && data.children) {
269
+ data.children.forEach(child => {
270
+ const childKey = child[this.treeProps.value]
271
+ if (!this.leftCheckedKeys.includes(childKey)) {
272
+ this.leftCheckedKeys.push(childKey)
273
+ }
274
+ })
275
+ }
276
+ } else {
277
+ this.leftCheckedKeys = this.leftCheckedKeys.filter(k => k !== key)
278
+ if (this.cascade && data.children) {
279
+ data.children.forEach(child => {
280
+ this.leftCheckedKeys = this.leftCheckedKeys.filter(k => k !== child[this.treeProps.value])
281
+ })
282
+ }
283
+ }
284
+ },
285
+
286
+ handleRightCheckChange(data, checked) {
287
+ const key = data[this.treeProps.value]
288
+ if (checked) {
289
+ if (!this.rightCheckedKeys.includes(key)) {
290
+ this.rightCheckedKeys.push(key)
291
+ }
292
+ if (this.cascade && data.children) {
293
+ data.children.forEach(child => {
294
+ const childKey = child[this.treeProps.value]
295
+ if (!this.rightCheckedKeys.includes(childKey)) {
296
+ this.rightCheckedKeys.push(childKey)
297
+ }
298
+ })
299
+ }
300
+ } else {
301
+ this.rightCheckedKeys = this.rightCheckedKeys.filter(k => k !== key)
302
+ if (this.cascade && data.children) {
303
+ data.children.forEach(child => {
304
+ this.rightCheckedKeys = this.rightCheckedKeys.filter(k => k !== child[this.treeProps.value])
305
+ })
306
+ }
307
+ }
308
+ },
309
+
310
+ handleLeftNodeClick(data) {
311
+ if (!this.showCheckbox) {
312
+ const key = data[this.treeProps.value]
313
+ const keysToTransfer = this.getTransferKeys(data)
314
+ this.transferKeys(keysToTransfer, true)
315
+ }
316
+ },
317
+
318
+ handleRightNodeClick(data) {
319
+ if (!this.showCheckbox) {
320
+ const key = data[this.treeProps.value]
321
+ const keysToTransfer = this.getTransferKeys(data)
322
+ this.transferKeys(keysToTransfer, false)
323
+ }
324
+ },
325
+
326
+ getTransferKeys(data) {
327
+ const key = data[this.treeProps.value]
328
+ switch (this.transferMode) {
329
+ case 'parent-child':
330
+ return this.getAllKeys(data)
331
+ case 'multiple':
332
+ return [key]
333
+ default:
334
+ return [key]
335
+ }
336
+ },
337
+
338
+ transferToRight() {
339
+ const keys = [...this.leftCheckedKeys]
340
+ this.transferKeys(keys, true)
341
+ this.leftCheckedKeys = []
342
+ },
343
+
344
+ transferToLeft() {
345
+ const keys = [...this.rightCheckedKeys]
346
+ this.transferKeys(keys, false)
347
+ this.rightCheckedKeys = []
348
+ },
349
+
350
+ transferKeys(keys, toRight) {
351
+ const currentValue = [...this.value]
352
+ const newKeys = new Set(currentValue)
353
+
354
+ if (toRight) {
355
+ keys.forEach(k => newKeys.add(k))
356
+ if (this.cascade) {
357
+ keys.forEach(k => {
358
+ const parentKeys = this.getParentKeys(this.data, k)
359
+ parentKeys.forEach(pk => newKeys.add(pk))
360
+ })
361
+ }
362
+ } else {
363
+ keys.forEach(k => newKeys.delete(k))
364
+ if (this.cascade) {
365
+ keys.forEach(k => {
366
+ const allChildKeys = []
367
+ this.findAllChildKeys(this.data, k, allChildKeys)
368
+ allChildKeys.forEach(ck => newKeys.delete(ck))
369
+ })
370
+ }
371
+ }
372
+
373
+ const newValue = Array.from(newKeys)
374
+ this.$emit('update:value', newValue)
375
+ this.$emit('change', {
376
+ value: newValue,
377
+ addedKeys: toRight ? keys : [],
378
+ removedKeys: toRight ? [] : keys
379
+ })
380
+ },
381
+
382
+ findAllChildKeys(data, parentKey, childKeys) {
383
+ for (const node of data) {
384
+ if (node[this.treeProps.value] === parentKey && node.children) {
385
+ node.children.forEach(child => {
386
+ childKeys.push(child[this.treeProps.value])
387
+ if (child.children) {
388
+ this.findAllChildKeys([child], child[this.treeProps.value], childKeys)
389
+ }
390
+ })
391
+ }
392
+ if (node.children) {
393
+ this.findAllChildKeys(node.children, parentKey, childKeys)
394
+ }
395
+ }
396
+ },
397
+
398
+ clearSelection() {
399
+ this.leftCheckedKeys = []
400
+ this.rightCheckedKeys = []
401
+ }
402
+ }
403
+ }
404
+ </script>
405
+
406
+ <style lang="scss" scoped>
407
+ .xt-transfer-tree {
408
+ display: flex;
409
+ align-items: stretch;
410
+ width: 100%;
411
+ height: 100%;
412
+ }
413
+
414
+ .xt-transfer-tree__panel {
415
+ flex: 1;
416
+ display: flex;
417
+ flex-direction: column;
418
+ border: 1px solid #ebeef5;
419
+ border-radius: 4px;
420
+ overflow: hidden;
421
+ }
422
+
423
+ .xt-transfer-tree__source {
424
+ margin-right: 8px;
425
+ }
426
+
427
+ .xt-transfer-tree__target {
428
+ margin-left: 8px;
429
+ }
430
+
431
+ .xt-transfer-tree__header {
432
+ display: flex;
433
+ align-items: center;
434
+ justify-content: space-between;
435
+ padding: 12px;
436
+ background: #f5f7fa;
437
+ border-bottom: 1px solid #ebeef5;
438
+ }
439
+
440
+ .xt-transfer-tree__title {
441
+ font-size: 14px;
442
+ font-weight: 600;
443
+ color: #303133;
444
+ }
445
+
446
+ .xt-transfer-tree__count {
447
+ font-size: 12px;
448
+ color: #909399;
449
+ }
450
+
451
+ .xt-transfer-tree__search {
452
+ padding: 8px 12px;
453
+ border-bottom: 1px solid #ebeef5;
454
+ }
455
+
456
+ .xt-transfer-tree__body {
457
+ flex: 1;
458
+ overflow-y: auto;
459
+ padding: 8px;
460
+ }
461
+
462
+ .xt-transfer-tree__buttons {
463
+ display: flex;
464
+ flex-direction: column;
465
+ justify-content: center;
466
+ align-items: center;
467
+ padding: 8px;
468
+ gap: 8px;
469
+ }
470
+
471
+ .xt-transfer-tree__node {
472
+ display: flex;
473
+ align-items: center;
474
+ gap: 4px;
475
+ }
476
+
477
+ .xt-transfer-tree__node-count {
478
+ font-size: 12px;
479
+ color: #909399;
480
+ }
481
+
482
+ .xt-transfer-tree__body::-webkit-scrollbar {
483
+ width: 6px;
484
+ }
485
+
486
+ .xt-transfer-tree__body::-webkit-scrollbar-thumb {
487
+ background: #c1c1c1;
488
+ border-radius: 3px;
489
+ }
490
+
491
+ .xt-transfer-tree__body::-webkit-scrollbar-track {
492
+ background: transparent;
493
+ }
494
494
  </style>