centaline-data-driven-v3 0.1.40 → 0.1.42

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.
@@ -1,19 +1,23 @@
1
1
  <template>
2
2
  <div id="ct-tree" class="ct-tree" oncontextmenu="event.preventDefault()" style="overflow:auto;" v-loading="loading"
3
3
  :style="{ height: props.treeHeight - 10 + 'px' }">
4
- <el-tree class="tree-line" :props="defaultProps" :load="loadNode" :expand-on-click-node="false"
5
- @node-click="handleNodeClick" lazy ref="refTree" @node-contextmenu="rightClick" node-key="code"
6
- current-node-key="currentNodeKey">
7
-
4
+ <el-tree class="tree-line" :props="defaultProps" :expand-on-click-node="false" @node-click="handleNodeClick"
5
+ :filter-node-method="filterNode" lazy :load="loadNode" ref="refTree" @node-contextmenu="rightClick"
6
+ node-key="code" current-node-key="currentNodeKey" :default-expanded-keys="defaultExpandedKeys">
8
7
  <template #default="{ node, data }">
9
- <span>
10
- <span style="margin-left: 10px" :ref="(el: refItem) => handleSetInputMap(el, data)">{{
8
+ <span class="node_content">
9
+ <el-icon class="node-icon" :class="{ 'is-leaf': node.isLeaf }">
10
+ <Document v-if="node.isLeaf" :size="14" />
11
+ <Folder v-else-if="!node.expanded" :size="14" />
12
+ <FolderOpened v-else :size="14" />
13
+ </el-icon>
14
+ <span style="" :ref="(el: refItem) => handleSetInputMap(el, data)">{{
11
15
  data.name }}</span>
12
16
  <span @click.stop>
13
17
  <el-popover placement="right" :virtual-ref="nodeRef" virtual-triggering
14
- :visible="((data.code == currentData.code && menuVisible && model.actionRouter?.length > 0))">
18
+ :visible="((data.code == currentData.code && menuVisible && allowedRoutes?.length > 0))">
15
19
  <div class="box-menu">
16
- <template v-for="(item, index) in model.actionRouter">
20
+ <template v-for="(item, index) in allowedRoutes">
17
21
  <div class="opertion" @click="routerClickHandler(item)">
18
22
  <div>
19
23
  <img v-if="item.imgUrl" :src="item.imgUrl" alt="" />
@@ -22,22 +26,21 @@
22
26
  </div>
23
27
  </template>
24
28
  </div>
25
-
26
29
  </el-popover>
27
30
  </span>
28
31
  </span>
29
32
  </template>
30
33
  </el-tree>
31
-
32
-
33
34
  </div>
34
35
  </template>
36
+
35
37
  <script setup lang="ts">
36
- import { ref, nextTick, ComponentPublicInstance, onBeforeUnmount } from 'vue'
38
+ import { ref, nextTick, ComponentPublicInstance, onBeforeUnmount, computed, watch } from 'vue'
37
39
  import common from '../../../utils/common'
38
40
  import { RouterClickHandler } from '../../../utils/mixins';
39
41
  import Enum from '../../../utils/Enum'
40
42
  import Tree from '../../../loader/src/Tree';
43
+
41
44
  type refItem = Element | ComponentPublicInstance | null;
42
45
 
43
46
  const emit = defineEmits(['loaded'])
@@ -54,8 +57,15 @@ const props = defineProps({
54
57
  apiParam: {
55
58
  type: Object,
56
59
  default: () => { },
60
+ },
61
+ // 新增:是否默认展开两级
62
+ expandTwoLevels: {
63
+ type: Boolean,
64
+ default: true
57
65
  }
58
66
  })
67
+
68
+ const lazy = ref(true)
59
69
  const refTree = ref()
60
70
  const rootNode = ref();
61
71
  const rootResolve = ref();
@@ -69,15 +79,30 @@ const defaultProps = ref({
69
79
  })
70
80
  const searchStatus = ref([])
71
81
  const screenPara = ref(props.apiParam);
72
- const menuVisible = ref(false)//右键菜单显示控制
73
- const currentData = ref({ code: "" })// 当前节点所对应的数据
74
- const currentNode = ref(null)//当前节点对应的 Node
75
- const loading = ref(false)//数据理否查询完成
82
+ const menuVisible = ref(false)
83
+ const currentData = ref({ code: "" })
84
+ const currentNode = ref(null)
85
+ const loading = ref(false)
76
86
 
77
87
  const model = ref(null)
78
88
  const nodeRef = ref()
79
89
  const retryCount = ref(0);
80
90
  const treeNodeRefMap = ref({});
91
+ const allowedRoutes = ref([]);
92
+
93
+ // 默认展开的节点key数组
94
+ const defaultExpandedKeys = ref([])
95
+ // 是否已设置默认展开
96
+ const hasSetDefaultExpand = ref(false)
97
+ // 是否是搜索状态
98
+ const isSearching = ref(false)
99
+
100
+ // 全量数据缓存
101
+ const fullTreeData = ref([])
102
+
103
+ // 新增:缓存所有节点的Map,用于快速查找
104
+ const nodeCodeMap = ref(new Map())
105
+
81
106
  /** 动态设置Input Ref */
82
107
  const handleSetInputMap = (el: refItem, item) => {
83
108
  if (el) {
@@ -93,11 +118,100 @@ onBeforeUnmount(() => {
93
118
  }
94
119
  })
95
120
 
96
- //搜索(查询条件调用)
121
+ // 递归获取前两级节点的key
122
+ function getFirstTwoLevelKeys(nodes, level = 0, result = []) {
123
+ if (!nodes || nodes.length === 0 || level >= 1) {
124
+ return result;
125
+ }
126
+
127
+ nodes.forEach(node => {
128
+ if (node.code) {
129
+ result.push(node.code);
130
+ }
131
+
132
+ if (level < 1 && node.children && node.children.length > 0) {
133
+ getFirstTwoLevelKeys(node.children, level + 1, result);
134
+ }
135
+ });
136
+
137
+ return result;
138
+ }
139
+
140
+ // 递归获取所有节点的key(用于搜索时全展开)
141
+ function getAllNodeKeys(nodes, result = []) {
142
+ if (!nodes || nodes.length === 0) {
143
+ return result;
144
+ }
145
+
146
+ nodes.forEach(node => {
147
+ if (node.code) {
148
+ result.push(node.code);
149
+ }
150
+
151
+ if (node.children && node.children.length > 0) {
152
+ getAllNodeKeys(node.children, result);
153
+ }
154
+ });
155
+
156
+ return result;
157
+ }
158
+
159
+ // 设置默认展开节点
160
+ function setDefaultExpandedNodes() {
161
+ if (!props.expandTwoLevels || !fullTreeData.value.length) {
162
+ return;
163
+ }
164
+
165
+ if (isSearching.value) {
166
+ const allKeys = getAllNodeKeys(fullTreeData.value);
167
+ defaultExpandedKeys.value = allKeys;
168
+ } else {
169
+ defaultExpandedKeys.value = getFirstTwoLevelKeys(fullTreeData.value);
170
+ }
171
+
172
+ hasSetDefaultExpand.value = true;
173
+ }
174
+
175
+ // 新增:构建节点Map,便于快速查找
176
+ function buildNodeCodeMap(nodes) {
177
+ const map = new Map();
178
+
179
+ function traverse(nodeList) {
180
+ nodeList.forEach(node => {
181
+ if (node.code) {
182
+ map.set(node.code, node);
183
+ }
184
+
185
+ if (node.children && node.children.length > 0) {
186
+ traverse(node.children);
187
+ }
188
+ });
189
+ }
190
+
191
+ traverse(nodes);
192
+ return map;
193
+ }
194
+
195
+ // 新增:复制节点(深拷贝)
196
+ function cloneNode(node) {
197
+ const cloned = { ...node };
198
+ cloned.children = [];
199
+ return cloned;
200
+ }
201
+
202
+ // 搜索(查询条件调用)
97
203
  function search(m) {
204
+ const hasValidSearchConditions = checkHasValidSearchConditions(m);
205
+
206
+ isSearching.value = hasValidSearchConditions;
98
207
  screenPara.value = m;
208
+
209
+ if (!lazy.value) {
210
+ filterFullTreeData(screenPara.value);
211
+ return;
212
+ }
213
+
99
214
  if (searchStatus.value.length == 0) {
100
- // 重置计数器
101
215
  retryCount.value = 0
102
216
  if (qrtimer.value) clearTimeout(qrtimer.value);
103
217
  searchStatus.value.push('a');
@@ -116,47 +230,465 @@ function search(m) {
116
230
  }, 200);
117
231
  }
118
232
  }
119
- //加载节点(点击节点加号时调用)
233
+
234
+ // 检查是否有有效的搜索条件
235
+ function checkHasValidSearchConditions(filter) {
236
+ if (!filter) return false;
237
+
238
+ let searchConditions = [];
239
+
240
+ if (filter?.searchData?.fields) {
241
+ searchConditions = filter.searchData.fields;
242
+ } else if (filter?.fields) {
243
+ searchConditions = filter.fields;
244
+ }
245
+
246
+ const validConditions = filterValidConditions(searchConditions);
247
+ return validConditions && validConditions.length > 0;
248
+ }
249
+
250
+
251
+
252
+ // 过滤掉 operation 为 1 的条件
253
+ function filterValidConditions(conditions) {
254
+ if (!conditions || !Array.isArray(conditions)) return [];
255
+
256
+ return conditions.filter(condition => {
257
+ return condition.operation !== 1;
258
+ });
259
+ }
260
+
261
+ // 判断节点是否匹配所有搜索条件
262
+ function nodeMatchesAllConditions(node, conditions) {
263
+ const validConditions = filterValidConditions(conditions);
264
+
265
+ if (!validConditions || validConditions.length === 0) return true;
266
+
267
+ return validConditions.every(condition => {
268
+ const { fieldName1, operation, searchValue1, searchValue2, searchDataType } = condition;
269
+ let nodeValue = node[fieldName1];
270
+
271
+ if (nodeValue === undefined) {
272
+ const mappedField = Object.keys(node).find(key =>
273
+ key.toLowerCase() === fieldName1.toLowerCase()
274
+ );
275
+ if (mappedField) {
276
+ nodeValue = node[mappedField];
277
+ } else {
278
+ return false;
279
+ }
280
+ }
281
+
282
+ const strNodeValue = String(nodeValue || '').toLowerCase();
283
+ const strSearchValue = String(searchValue1 || '').toLowerCase();
284
+
285
+ switch (operation) {
286
+ case 2:
287
+ return strNodeValue === strSearchValue;
288
+ case 3:
289
+ return strNodeValue !== strSearchValue;
290
+ case 4:
291
+ return Number(nodeValue) > Number(searchValue1);
292
+ case 5:
293
+ return Number(nodeValue) >= Number(searchValue1);
294
+ case 6:
295
+ return Number(nodeValue) < Number(searchValue1);
296
+ case 7:
297
+ return Number(nodeValue) <= Number(searchValue1);
298
+ case 8:
299
+ return strNodeValue.includes(strSearchValue);
300
+ case 9:
301
+ return strNodeValue.startsWith(strSearchValue);
302
+ case 10:
303
+ return strNodeValue.endsWith(strSearchValue);
304
+ case 11:
305
+ return !strNodeValue.includes(strSearchValue);
306
+ case 12:
307
+ try {
308
+ const regex = new RegExp(searchValue1, 'i');
309
+ return regex.test(nodeValue);
310
+ } catch (e) {
311
+ console.warn('正则表达式错误:', e);
312
+ return false;
313
+ }
314
+ case 13:
315
+ if (!searchValue1) return false;
316
+ try {
317
+ const values = searchValue1.split(',').map(v => v.trim().toLowerCase());
318
+ return values.includes(strNodeValue);
319
+ } catch (e) {
320
+ console.warn('解析IN条件错误:', e);
321
+ return false;
322
+ }
323
+ case 14:
324
+ if (!searchValue1) return true;
325
+ try {
326
+ const values = searchValue1.split(',').map(v => v.trim().toLowerCase());
327
+ return !values.includes(strNodeValue);
328
+ } catch (e) {
329
+ console.warn('解析NotIN条件错误:', e);
330
+ return false;
331
+ }
332
+ default:
333
+ return false;
334
+ }
335
+ });
336
+ }
337
+
338
+ // 修改:构建完整的过滤树,保持原始层级结构
339
+ function buildFilteredTreeWithOriginalHierarchy(matchedNodes) {
340
+ if (!matchedNodes || matchedNodes.length === 0) {
341
+ return [];
342
+ }
343
+
344
+ const resultMap = new Map();
345
+ const rootNodes = [];
346
+
347
+ // 第一步:创建所有匹配节点的副本
348
+ matchedNodes.forEach(node => {
349
+ if (!resultMap.has(node.code)) {
350
+ const clonedNode = cloneNode(node);
351
+ resultMap.set(node.code, clonedNode);
352
+ }
353
+ });
354
+
355
+ // 第二步:为每个匹配节点建立完整的父子关系链
356
+ matchedNodes.forEach(matchedNode => {
357
+ // 从全量数据中查找该节点的完整路径
358
+ const fullNode = nodeCodeMap.value.get(matchedNode.code);
359
+ if (!fullNode) return;
360
+
361
+ // 递归向上查找父节点链
362
+ function findParentChain(currentNode, chain = []) {
363
+ chain.unshift(currentNode.code);
364
+
365
+ // 在全量数据中查找父节点
366
+ const parentCode = findParentCodeInFullTree(currentNode.code);
367
+ if (parentCode) {
368
+ const parentNode = nodeCodeMap.value.get(parentCode);
369
+ if (parentNode) {
370
+ findParentChain(parentNode, chain);
371
+ }
372
+ }
373
+
374
+ return chain;
375
+ }
376
+
377
+ const parentChain = findParentChain(fullNode);
378
+
379
+ // 构建完整的树结构
380
+ for (let i = 0; i < parentChain.length; i++) {
381
+ const currentCode = parentChain[i];
382
+
383
+ // 如果节点已经存在,跳过
384
+ if (resultMap.has(currentCode)) {
385
+ continue;
386
+ }
387
+
388
+ // 从全量数据中获取节点
389
+ const originalNode = nodeCodeMap.value.get(currentCode);
390
+ if (!originalNode) continue;
391
+
392
+ // 创建节点副本
393
+ const clonedNode = cloneNode(originalNode);
394
+ resultMap.set(currentCode, clonedNode);
395
+
396
+ // 如果是根节点,添加到根节点数组
397
+ if (i === 0) {
398
+ // 检查是否已经存在于根节点中
399
+ const existingRoot = rootNodes.find(n => n.code === currentCode);
400
+ if (!existingRoot) {
401
+ rootNodes.push(clonedNode);
402
+ }
403
+ }
404
+
405
+ // 连接到父节点
406
+ if (i > 0) {
407
+ const parentCode = parentChain[i - 1];
408
+ const parentNode = resultMap.get(parentCode);
409
+
410
+ if (parentNode) {
411
+ // 检查是否已经包含该子节点
412
+ const existingChild = parentNode.children.find(child => child.code === currentCode);
413
+ if (!existingChild) {
414
+ parentNode.children.push(clonedNode);
415
+ }
416
+ }
417
+ }
418
+ }
419
+
420
+ // 最后,确保当前匹配节点连接到其父节点
421
+ const parentCode = findParentCodeInFullTree(matchedNode.code);
422
+ if (parentCode && resultMap.has(parentCode)) {
423
+ const parentNode = resultMap.get(parentCode);
424
+ const currentNode = resultMap.get(matchedNode.code);
425
+
426
+ if (parentNode && currentNode) {
427
+ // 检查是否已经包含该子节点
428
+ const existingChild = parentNode.children.find(child => child.code === matchedNode.code);
429
+ if (!existingChild) {
430
+ parentNode.children.push(currentNode);
431
+ }
432
+ }
433
+ }
434
+ });
435
+
436
+ // 第三步:清理根节点,确保它们没有重复的父节点
437
+ const finalRootNodes = [];
438
+ const usedNodes = new Set();
439
+
440
+ function isNodeUsedInTree(node) {
441
+ for (const root of rootNodes) {
442
+ if (isNodeInChildren(root, node.code)) {
443
+ return true;
444
+ }
445
+ }
446
+ return false;
447
+ }
448
+
449
+ rootNodes.forEach(rootNode => {
450
+ // 如果这个节点已经是其他节点的子节点,则不作为根节点
451
+ if (!isNodeUsedInTree(rootNode)) {
452
+ finalRootNodes.push(rootNode);
453
+ }
454
+ });
455
+
456
+ return finalRootNodes;
457
+ }
458
+
459
+ // 新增:在全量树中查找父节点code
460
+ function findParentCodeInFullTree(nodeCode) {
461
+ function findParent(nodes, targetCode, parentCode = null) {
462
+ for (const node of nodes) {
463
+ if (node.code === targetCode) {
464
+ return parentCode;
465
+ }
466
+
467
+ if (node.children && node.children.length > 0) {
468
+ const found = findParent(node.children, targetCode, node.code);
469
+ if (found !== null) {
470
+ return found;
471
+ }
472
+ }
473
+ }
474
+ return null;
475
+ }
476
+
477
+ return findParent(fullTreeData.value, nodeCode);
478
+ }
479
+
480
+ // 新增:检查节点是否在子树中
481
+ function isNodeInChildren(parentNode, targetCode) {
482
+ if (parentNode.code === targetCode) {
483
+ return false; // 不检查自身
484
+ }
485
+
486
+ if (!parentNode.children || parentNode.children.length === 0) {
487
+ return false;
488
+ }
489
+
490
+ for (const child of parentNode.children) {
491
+ if (child.code === targetCode) {
492
+ return true;
493
+ }
494
+
495
+ if (isNodeInChildren(child, targetCode)) {
496
+ return true;
497
+ }
498
+ }
499
+
500
+ return false;
501
+ }
502
+
503
+ // 修改:过滤全量树数据,保持原始层级
504
+ function filterFullTreeData(filter) {
505
+ if (!fullTreeData.value.length) {
506
+ return;
507
+ }
508
+
509
+ loading.value = true;
510
+ try {
511
+ let searchConditions = [];
512
+
513
+ if (filter?.searchData?.fields) {
514
+ searchConditions = filter.searchData.fields;
515
+ } else if (filter?.fields) {
516
+ searchConditions = filter.fields;
517
+ }
518
+
519
+ const validConditions = filterValidConditions(searchConditions);
520
+
521
+ if (!validConditions || validConditions.length === 0) {
522
+ refTree.value.store.setData(fullTreeData.value);
523
+ loading.value = false;
524
+ isSearching.value = false;
525
+ hasSetDefaultExpand.value = false;
526
+ defaultExpandedKeys.value = [];
527
+
528
+ // 先收起所有节点
529
+ if (refTree.value && refTree.value.store) {
530
+ Object.values(refTree.value.store.nodesMap).forEach((node: any) => {
531
+ if (node) {
532
+ node.expanded = false;
533
+ }
534
+ });
535
+ }
536
+
537
+ setDefaultExpandedNodes();
538
+ return;
539
+ }
540
+
541
+ // 递归查找所有匹配的节点
542
+ const findMatchedNodes = (nodes, result = []) => {
543
+ nodes.forEach(node => {
544
+ const nodeMatches = nodeMatchesAllConditions(node, validConditions);
545
+
546
+ if (nodeMatches) {
547
+ result.push(node);
548
+ }
549
+
550
+ if (node.children && node.children.length > 0) {
551
+ findMatchedNodes(node.children, result);
552
+ }
553
+ });
554
+ return result;
555
+ };
556
+
557
+ const matchedNodes = findMatchedNodes(fullTreeData.value);
558
+
559
+ // 构建保持原始层级的过滤树
560
+ let filteredData = [];
561
+ if (matchedNodes.length > 0) {
562
+ filteredData = buildFilteredTreeWithOriginalHierarchy(matchedNodes);
563
+ }
564
+
565
+ // 设置过滤后的数据
566
+ refTree.value.store.setData(filteredData);
567
+
568
+ // 搜索状态时展开所有节点
569
+ if (filteredData.length > 0 && isSearching.value) {
570
+ const allKeys = getAllNodeKeys(filteredData);
571
+ defaultExpandedKeys.value = allKeys;
572
+
573
+ nextTick(() => {
574
+ if (refTree.value && refTree.value.store) {
575
+ Object.values(refTree.value.store.nodesMap).forEach((node: any) => {
576
+ if (node) {
577
+ node.expanded = true;
578
+ }
579
+ });
580
+ }
581
+ });
582
+
583
+ // 默认选中第一个可选节点
584
+ const findFirstSelectableNode = (nodes) => {
585
+ for (const node of nodes) {
586
+ if (!node.children || node.children.length === 0) {
587
+ return node;
588
+ }
589
+ const childResult = findFirstSelectableNode(node.children);
590
+ if (childResult) return childResult;
591
+ }
592
+ return nodes[0];
593
+ };
594
+
595
+ const firstNode = findFirstSelectableNode(filteredData);
596
+ if (firstNode && firstNode.code) {
597
+ nextTick(() => {
598
+ refTree.value.setCurrentKey(firstNode.code);
599
+ handleNodeClick(firstNode, true);
600
+ });
601
+ }
602
+ }
603
+
604
+ } catch (error) {
605
+ console.error('过滤树数据失败:', error);
606
+ } finally {
607
+ loading.value = false;
608
+ }
609
+ }
610
+
611
+ // 修改:加载节点,保存节点Map
120
612
  function loadNode(node, resolve) {
613
+
614
+ if (!lazy.value) {
615
+ const nodeData = node.data;
616
+ resolve(nodeData.children)
617
+ return;
618
+ }
619
+
121
620
  if (node.level === 0) {
122
621
  loading.value = true;
123
622
  rootNode.value = node
124
623
  rootResolve.value = resolve;
624
+
125
625
  return SearchTree(screenPara.value || undefined).then(data => {
126
626
  loading.value = false;
127
- resolve(data.rows);
128
- load(data, true)
627
+
628
+
629
+ const { tree, hasAnyLeaf } = buildDeptTreeByField(data.rows, data.source.content.columns || [], {
630
+ pathKey: 'deptPath',
631
+ separator: '.'
632
+ });
633
+
634
+ // 缓存全量数据并构建节点Map
635
+ if (hasAnyLeaf) {
636
+ fullTreeData.value = tree;
637
+ nodeCodeMap.value = buildNodeCodeMap(tree);
638
+ resolve(tree);
639
+ if (props.expandTwoLevels) {
640
+ nextTick(() => {
641
+ hasSetDefaultExpand.value = false;
642
+ setDefaultExpandedNodes();
643
+ });
644
+ }
645
+ }
646
+ else {
647
+ resolve(data.rows);
648
+ }
649
+ lazy.value = !hasAnyLeaf;
650
+
651
+ load(data, true);
652
+ }).catch(error => {
653
+ loading.value = false;
654
+ console.error('加载树数据失败:', error);
129
655
  });
130
656
  }
131
- if (node.level >= 1) {
657
+
658
+ if (node.level >= 1 && lazy.value) {
132
659
  let fields = model.value.searchData("code", node.data.code, 9, 3);
133
660
  let filter = {
134
661
  "searchData": {
135
- // 先保留searchData的其他属性
136
662
  ...(screenPara.value?.searchData || {}),
137
- // 再合并fields数组(将两个数组合并,而不是覆盖)
138
663
  fields: [
139
664
  ...fields.fields,
140
665
  ...(screenPara.value?.searchData?.fields || [])
141
666
  ]
142
667
  }
143
- };
668
+ };
669
+
144
670
  return SearchTree(filter).then(data => {
145
671
  loading.value = false;
146
672
  resolve(data.rows);
147
- load(data, false)
673
+ load(data, false);
674
+ }).catch(error => {
675
+ loading.value = false;
676
+ console.error('加载子节点失败:', error);
148
677
  });
149
678
  }
150
679
  }
151
- //加载数据
152
- async function load(data, firstLoad) {
680
+
681
+ // 加载数据
682
+ async function load(data, flagRootNode) {
153
683
  model.value = data;
154
684
  if (searchStatus.value.length > 0) {
155
685
  searchStatus.value = searchStatus.value.slice(1)
156
686
  }
687
+
157
688
  loading.value = false;
158
689
  menuVisible.value = false;
159
- if (firstLoad) {
690
+
691
+ if (flagRootNode) {
160
692
  if (model.value.rows && model.value.rows[0]) {
161
693
  handleNodeClick(model.value.rows[0], true);
162
694
  }
@@ -167,17 +699,77 @@ async function load(data, firstLoad) {
167
699
  });
168
700
  }
169
701
  }
170
- //查询数据
171
- function SearchTree(m) {
172
702
 
703
+ // 查询数据
704
+ function SearchTree(m) {
173
705
  return new Promise((resolve, reject) => {
174
706
  if (typeof props.api !== 'undefined') {
175
-
176
- resolve(Tree.loadSearchTreeApi(props.api, m));
707
+ Tree.loadSearchTreeApi(props.api, m)
708
+ .then(resolve)
709
+ .catch(reject);
710
+ } else {
711
+ reject(new Error('API未定义'));
177
712
  }
178
713
  });
179
714
  }
180
- //节点击点事件
715
+
716
+ // 重置树到全量数据
717
+ function resetTree() {
718
+ isSearching.value = false;
719
+ if (fullTreeData.value.length > 0) {
720
+ refTree.value.store.setData(fullTreeData.value);
721
+ hasSetDefaultExpand.value = false;
722
+ defaultExpandedKeys.value = [];
723
+
724
+ if (fullTreeData.value[0] && fullTreeData.value[0].code) {
725
+ nextTick(() => {
726
+ if (refTree.value && refTree.value.store) {
727
+ Object.values(refTree.value.store.nodesMap).forEach((node: any) => {
728
+ if (node) {
729
+ node.expanded = false;
730
+ }
731
+ });
732
+ }
733
+
734
+ refTree.value.setCurrentKey(fullTreeData.value[0].code);
735
+ handleNodeClick(fullTreeData.value[0], true);
736
+ setDefaultExpandedNodes();
737
+ });
738
+ }
739
+ }
740
+ }
741
+
742
+ // 清除搜索条件
743
+ function clearSearch() {
744
+ screenPara.value = {};
745
+ isSearching.value = false;
746
+
747
+ defaultExpandedKeys.value = [];
748
+ hasSetDefaultExpand.value = false;
749
+
750
+ if (fullTreeData.value.length > 0) {
751
+ refTree.value.store.setData(fullTreeData.value);
752
+
753
+ nextTick(() => {
754
+ if (refTree.value && refTree.value.store) {
755
+ Object.values(refTree.value.store.nodesMap).forEach((node: any) => {
756
+ if (node) {
757
+ node.expanded = false;
758
+ }
759
+ });
760
+ }
761
+
762
+ setDefaultExpandedNodes();
763
+
764
+ if (fullTreeData.value[0] && fullTreeData.value[0].code) {
765
+ refTree.value.setCurrentKey(fullTreeData.value[0].code);
766
+ handleNodeClick(fullTreeData.value[0], true);
767
+ }
768
+ });
769
+ }
770
+ }
771
+
772
+ //节点点击事件
181
773
  function handleNodeClick(data, formType) {
182
774
  closeMenu();
183
775
  if (data && formType) {
@@ -194,33 +786,41 @@ function handleNodeClick(data, formType) {
194
786
  })
195
787
  model.value.requestAction(rowRouter.action, SearchData, function (rowdata) {
196
788
  var newdata = common.deepClone(data);
789
+ delete newdata.children;
790
+
197
791
  if (rowdata) {
198
- newdata.rowRouter = rowdata
792
+ newdata.rowRouter = rowdata;
199
793
  }
200
794
  newdata.title = model.value.source?.content?.title || '';
201
795
  emit('loaded', newdata);
202
796
  });
203
797
  }
204
798
  }
799
+
205
800
  //右键菜单
206
- function rightClick(event, object, Node) { // event、object该节点所对应的对象、节点对应的 Node、节点组件本身
801
+ function rightClick(event, object, Node) {
207
802
  nodeRef.value = treeNodeRefMap.value[`node_Ref_${object.code}`]
208
803
 
209
804
  currentData.value = object
210
805
  currentNode.value = Node
211
806
  let actionIndex = 0;
212
- if (model.value.actionRouter) {
213
- model.value.actionRouter.forEach(v => {
214
- if (currentData.value[v.rightField] == '1') {
215
- menuVisible.value = true //显示增删改的div
216
- actionIndex += 1;
217
- }
218
- })
219
- }
807
+
808
+ allowedRoutes.value = (model.value.actionRouter || []).filter(v => {
809
+ const val = currentData.value[v.rightField]
810
+ const hit = val == '1' || val === undefined || val === null;
811
+ return hit;
812
+ })
813
+
814
+ allowedRoutes.value?.forEach(() => {
815
+ menuVisible.value = true
816
+ actionIndex += 1
817
+ })
818
+
220
819
  if (menuVisible.value) {
221
- document.addEventListener('click', closeMenu) // 监听事件鼠标点击事件,若点击则隐藏菜单
820
+ document.addEventListener('click', closeMenu)
222
821
  }
223
822
  }
823
+
224
824
  //关闭菜单
225
825
  function closeMenu() {
226
826
  menuVisible.value = false;
@@ -228,6 +828,7 @@ function closeMenu() {
228
828
  currentData.value = { ...currentData.value, code: "" }
229
829
  document.removeEventListener('click', closeMenu)
230
830
  }
831
+
231
832
  // 菜单路由
232
833
  function routerClickHandler(field) {
233
834
  var submitData = {};
@@ -238,48 +839,48 @@ function routerClickHandler(field) {
238
839
 
239
840
  RouterClickHandler(field, submitData, null, model.value, 'tree', function (newData) {
240
841
  switch (field.actionType) {
241
- case Enum.ActionType.Delete://删除
242
- case Enum.ActionType.CloseTabThenDelete://删除
243
- removeNode(newData);//删除树节点
842
+ case Enum.ActionType.Delete:
843
+ case Enum.ActionType.CloseTabThenDelete:
844
+ removeNode(newData);
244
845
  break;
245
- case Enum.ActionType.New://新增
246
- case Enum.ActionType.CloseTabThenNew://新增
247
- addNode(newData)//新增树节点
846
+ case Enum.ActionType.New:
847
+ case Enum.ActionType.CloseTabThenNew:
848
+ addNode(newData)
248
849
  break;
249
- case Enum.ActionType.Update://修改
250
- case Enum.ActionType.CloseTabThenUpdate://修改
251
- updateNode(newData)//修改树节点
850
+ case Enum.ActionType.Update:
851
+ case Enum.ActionType.CloseTabThenUpdate:
852
+ updateNode(newData)
252
853
  break;
253
854
  default:
254
855
  break;
255
856
  }
256
-
257
857
  })
258
858
  }
859
+
259
860
  //删除树节点
260
861
  function removeNode(newData) {
261
862
  getNextClickNode()
262
863
  refTree.value.remove(currentNode.value);
263
864
  }
865
+
264
866
  //新增树节点
265
867
  function addNode(newData) {
266
868
  refTree.value.append(newData, currentNode.value)
267
869
  handleNodeClick(newData, true)
268
870
  }
871
+
269
872
  //修改树节点
270
873
  function updateNode(newData) {
271
-
272
874
  for (let key in newData) {
273
-
274
875
  if (typeof newData[key] === 'object') {
275
876
  currentData.value[key] = { ...currentData.value[key], ...newData[key] }
276
877
  } else {
277
878
  currentData.value[key] = newData[key];
278
879
  }
279
-
280
880
  }
281
881
  handleNodeClick(newData, true)
282
882
  }
883
+
283
884
  function getNextClickNode() {
284
885
  const node = refTree.value.getNode(currentData.value.code);
285
886
  let data = {};
@@ -292,9 +893,106 @@ function getNextClickNode() {
292
893
  }
293
894
  handleNodeClick(data, true)
294
895
  }
295
- // //把属性和方法暴露
896
+
897
+ /** 过滤节点(全量数据模式下使用) */
898
+ function filterNode(value, data) {
899
+ if (!value) return true
900
+ return data.name.toLowerCase().includes(value.toLowerCase())
901
+ }
902
+
903
+ function buildDeptTreeByField(flatList, fieldDefine, opt = {}) {
904
+ const {
905
+ pathKey = 'path',
906
+ separator = '.',
907
+ sortKey = 'path'
908
+ } = opt;
909
+
910
+ const keepFields = fieldDefine
911
+ .filter(f => f.visible && f.isDataField)
912
+ .sort((a, b) => a.trOrder - b.trOrder)
913
+ .map(f => f.fieldName);
914
+
915
+ const createNode = (tplItem, path) => {
916
+ const node = { children: [] };
917
+
918
+ for (const key in tplItem) {
919
+ if (key !== 'children') {
920
+ node[key] = tplItem[key];
921
+ }
922
+ }
923
+
924
+ node[pathKey] = path;
925
+ return node;
926
+ };
927
+
928
+ const root = [];
929
+ const nodeMap = new Map();
930
+
931
+ for (const item of flatList) {
932
+
933
+ if (!item[pathKey]) {
934
+ break;
935
+ }
936
+ const path = item[pathKey];
937
+ const levels = path.split(separator);
938
+
939
+ let curPath = '';
940
+ let parentPath = '';
941
+
942
+ levels.forEach((lvl, idx) => {
943
+ curPath = idx === 0 ? lvl : `${parentPath}${separator}${lvl}`;
944
+ let curNode = nodeMap.get(curPath);
945
+
946
+ if (!curNode) {
947
+ curNode = createNode(item, curPath);
948
+ nodeMap.set(curPath, curNode);
949
+
950
+ if (idx === 0) {
951
+ root.push(curNode);
952
+ } else {
953
+ const pNode = nodeMap.get(parentPath);
954
+ if (pNode) {
955
+ pNode.children.push(curNode);
956
+ }
957
+ }
958
+ }
959
+ parentPath = curPath;
960
+ });
961
+
962
+ const lastNode = nodeMap.get(path);
963
+ for (const key in item) {
964
+ if (key !== 'children') {
965
+ lastNode[key] = item[key];
966
+ }
967
+ }
968
+ }
969
+
970
+ for (const node of nodeMap.values()) {
971
+ if (node.children && node.children.length > 0) {
972
+ node.children.sort((a, b) => {
973
+ const aVal = a[sortKey] || '';
974
+ const bVal = b[sortKey] || '';
975
+ return String(aVal).localeCompare(String(bVal));
976
+ });
977
+ }
978
+ node.isLeaf = node.children.length === 0;
979
+ }
980
+
981
+ root.sort((a, b) => {
982
+ const aVal = a[sortKey] || '';
983
+ const bVal = b[sortKey] || '';
984
+ return String(aVal).localeCompare(String(bVal));
985
+ });
986
+
987
+ const hasAnyLeaf = [...nodeMap.values()].some(n => n.isLeaf == false);
988
+ return { tree: root, hasAnyLeaf };
989
+ }
990
+
991
+ // 暴露方法
296
992
  defineExpose({
297
- search
993
+ search,
994
+ resetTree,
995
+ clearSearch
298
996
  })
299
997
  </script>
300
998
 
@@ -310,7 +1008,6 @@ defineExpose({
310
1008
 
311
1009
  .ct-tree::-webkit-scrollbar-thumb {
312
1010
  background-color: #bebebe;
313
- /* 设置滚动条滑块颜色 */
314
1011
  height: 5px;
315
1012
  border-radius: 4px;
316
1013
  display: block;
@@ -319,7 +1016,6 @@ defineExpose({
319
1016
  .ct-tree::-webkit-scrollbar {
320
1017
  width: 6px;
321
1018
  height: 6px;
322
- /* 设置滚动条高度 */
323
1019
  background-color: #f1f1f1;
324
1020
  display: block;
325
1021
  }
@@ -347,121 +1043,14 @@ defineExpose({
347
1043
  padding-right: 2px !important;
348
1044
  }
349
1045
 
350
- /* 竖线 */
351
- .el-tree-node::before {
352
- content: "";
353
- height: 100%;
354
- width: 1px;
355
- position: absolute;
356
- left: 27px;
357
- top: -1px;
358
- border-width: 1px;
359
- border-left: 1px dashed #52627C;
360
- }
361
-
362
- .tree-line .el-tree-node:before:last-child {
363
- top: -35px;
364
- }
365
-
366
- .el-tree-node__children .el-tree-node::before {
367
- content: "";
368
- height: 100%;
369
- width: 1px;
370
- position: absolute;
371
- left: 27px;
372
- top: -2px;
373
- border-width: 1px;
374
- border-left: 1px dashed #52627C;
375
- }
376
-
377
- /* 当前最后一个接点的竖线高度 */
378
- .el-tree-node:last-child::before {
379
- height: 15px;
380
- /*可以自己调整 */
381
- }
382
-
383
- /* 横线 */
384
- .el-tree-node::after {
385
- content: "";
386
- width: 12px;
387
- height: 20px;
388
- position: absolute;
389
- left: 27px;
390
- top: 12px;
391
- right: 2px;
392
- border-width: 1px;
393
- border-top: 1px dashed #52627C;
394
- }
395
-
396
- .el-tree:first-child .el-tree-node:before {
397
- /* height: 46px;可以自己调整 */
398
- }
399
-
400
- .el-tree-node__expand-icon {
401
- font-size: 16px;
402
- }
403
-
404
- /* 去掉顶层虚线 */
405
- .tree>.el-tree-node:first-child::after {
406
- border-top: none;
1046
+ .node_content {
1047
+ display: flex;
1048
+ align-items: center;
407
1049
  }
408
1050
 
409
- .el-tree>.el-tree-node:first-child:before {
410
- /* border-left: none; */
411
- }
412
-
413
- .tree>.is-leaf {
414
- color: transparent;
415
- }
416
-
417
- .el-tree .el-tree-node__expand-icon.expanded {
418
- -webkit-transform: rotate(0deg);
419
- transform: rotate(0deg);
420
- }
421
-
422
- .el-tree .el-tree-node__expand-icon.expanded {
423
- -webkit-transform: rotate(0deg);
424
- transform: rotate(0deg);
425
- }
426
-
427
-
428
- .el-tree .el-tree-node__expand-icon {
429
- background: url("@/assets/images/node-collapse.png") no-repeat;
430
- content: '';
431
- display: block;
432
- width: 12px;
433
- height: 12px;
434
- font-size: 12px;
435
- background-size: 10px;
436
-
437
- color: transparent;
438
- background-position: left 6px top 6px;
439
- }
440
-
441
- /*
442
- //有子节点 且已展开*/
443
- .el-tree .el-tree-node__expand-icon.expanded {
444
- background: url("@/assets/images/node-expand.png") no-repeat;
445
- content: '';
446
- display: block;
447
- width: 12px;
448
- height: 12px;
449
- font-size: 12px;
450
- background-size: 10px;
451
-
452
- color: transparent;
453
- background-position: left 6px top 6px;
454
- }
455
-
456
- /* //没有子节点*/
457
- .el-tree .el-tree-node__expand-icon.is-leaf::before {
458
- background: transparent no-repeat 0 3px;
459
- content: '';
460
- display: block;
461
- width: 12px;
462
- height: 12px;
463
- font-size: 12px;
464
- background-size: 10px;
1051
+ .node-icon {
1052
+ margin-right: 5px;
1053
+ color: var(--el-color-warning);
465
1054
  }
466
1055
 
467
1056
  .box-menu {
@@ -481,5 +1070,6 @@ defineExpose({
481
1070
 
482
1071
  .box-menu .opertion:hover {
483
1072
  color: var(--btnHoverRed);
1073
+ background-color: var(--el-fill-color-light);
484
1074
  }
485
- </style>
1075
+ </style>