ocpview-plus 1.2.7 → 1.2.9

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.
@@ -300,81 +300,140 @@ export default {
300
300
  })
301
301
  ]);
302
302
  },
303
- toTreeData (expandMap = new Set(), selectedId = null) {
303
+ toTreeData(expandMap = new Set(), selectedId = null) {
304
304
  let pos = {};
305
305
  let tree = [];
306
306
  let i = 0;
307
- let autoExpandId = this._autoExpandId;
307
+
308
+ const idField = this.myConfig.idField;
309
+ const parentField = this.myConfig.parentField;
310
+ const rootId = this.rootNode[idField];
311
+
312
+ const focusId = (selectedId !== null && selectedId !== undefined)
313
+ ? selectedId
314
+ : this._autoExpandId;
315
+
308
316
  let autoExpandParents = new Set();
309
317
 
310
- if (autoExpandId) {
311
- let current = this.data.find(
312
- item => item[this.myConfig.idField] === autoExpandId
313
- );
318
+ // 根据 focusId 反查父链(并防环)
319
+ if (focusId !== null && focusId !== undefined) {
320
+ let current = this.data.find(item => item[idField] === focusId);
321
+ const visited = new Set();
314
322
 
315
323
  while (current) {
316
- autoExpandParents.add(current[this.myConfig.idField]);
324
+ const cid = current[idField];
325
+ if (visited.has(cid)) {
326
+ console.warn('[toTreeData] parent chain cycle detected:', cid);
327
+ break;
328
+ }
329
+ visited.add(cid);
330
+
331
+ autoExpandParents.add(cid); // 选中节点本身 + 全部祖先都加入展开集合
317
332
 
318
- const parentId = current[this.myConfig.parentField];
319
- if (!parentId) break;
333
+ const pid = current[parentField];
334
+ if (pid == null) break; // 注意:不要写 !pid,避免 0 被误判
320
335
 
321
- current = this.data.find(
322
- item => item[this.myConfig.idField] === parentId
323
- );
336
+ current = this.data.find(item => item[idField] === pid);
324
337
  }
325
338
  }
326
339
 
340
+
327
341
  let data = this.$Method.copy(this.data);
328
342
  data.forEach(el => {
329
- this.setSelected(el);
343
+ this.setSelected(el); // 保留你原逻辑
330
344
  });
331
345
  let tempData = this.$Method.copy(data);
346
+
347
+ // 统一状态赋值(避免到处重复写漏)
348
+ const applyNodeState = (node) => {
349
+ const nid = node[idField];
350
+
351
+ // 展开:外部传入 expandMap 或者 focusId 的祖先链
352
+ if (expandMap.has(nid) || autoExpandParents.has(nid)) {
353
+ node.expand = true;
354
+ }
355
+
356
+ // 选中:focusId 命中
357
+ node.selected = (focusId !== null && focusId !== undefined && nid === focusId);
358
+ };
359
+
360
+ // 防死循环控制
361
+ let guard = 0;
362
+ const maxGuard = data.length * 5 + 20;
363
+ let movedInRound = 0;
364
+
332
365
  while (data.length !== 0) {
333
- // 解决找不到节点,而导致的死循环
334
- const index = tempData.findIndex(item => item[this.myConfig.idField] === data[i][this.myConfig.parentField]);
366
+ const current = data[i];
367
+ const currentId = current[idField];
368
+ const parentId = current[parentField];
335
369
 
336
- const currentId = data[i][this.myConfig.idField]
337
- if (data[i][this.myConfig.parentField] === this.rootNode[this.myConfig.idField] || index < 0) {
370
+ // 父是否存在于原始数据(判断孤儿)
371
+ const index = tempData.findIndex(item => item[idField] === parentId);
338
372
 
339
- if (expandMap.has(currentId)) {
340
- data[i].expand = true
341
- }
373
+ if (parentId === rootId || index < 0) {
374
+ applyNodeState(current);
342
375
 
343
- data[i].selected = (currentId === selectedId)
344
- tree.push(data[i]);
345
- pos[data[i][this.myConfig.idField]] = [tree.length - 1];
376
+ tree.push(current);
377
+ pos[currentId] = [tree.length - 1];
346
378
  data.splice(i, 1);
347
379
  i--;
380
+ movedInRound++;
348
381
  } else {
349
- let posArr = pos[data[i][this.myConfig.parentField]];
382
+ const posArr = pos[parentId];
350
383
  if (posArr) {
351
384
  let obj = tree[posArr[0]];
352
- for (var j = 1; j < posArr.length; j++) {
385
+ for (let j = 1; j < posArr.length; j++) {
353
386
  obj = obj.children[posArr[j]];
354
387
  }
355
- if (expandMap.has(currentId)) {
356
- data[i].expand = true;
357
- }
358
- data[i].selected = (currentId === selectedId)
359
- obj.children.push(data[i]);
360
- pos[data[i][this.myConfig.idField]] = posArr.concat([obj.children.length - 1]);
388
+
389
+ applyNodeState(current);
390
+
391
+ obj.children.push(current);
392
+ pos[currentId] = posArr.concat([obj.children.length - 1]);
361
393
  data.splice(i, 1);
362
394
  i--;
395
+ movedInRound++;
363
396
  }
364
397
  }
398
+
365
399
  i++;
366
400
  if (i > data.length - 1) {
401
+ // 一轮下来没挂上任何节点 -> 环/坏链路,兜底退出
402
+ if (movedInRound === 0 && data.length > 0) {
403
+ console.warn('[toTreeData] unresolved/cyclic nodes, force attach to root level:', data);
404
+
405
+ data.forEach(node => {
406
+ applyNodeState(node);
407
+ const nid = node[idField];
408
+ tree.push(node);
409
+ pos[nid] = [tree.length - 1];
410
+ });
411
+
412
+ data = [];
413
+ break;
414
+ }
415
+
416
+ movedInRound = 0;
367
417
  i = 0;
418
+
419
+ guard++;
420
+ if (guard > maxGuard) {
421
+ console.warn('[toTreeData] safety break triggered');
422
+ break;
423
+ }
368
424
  }
369
425
  }
426
+
427
+
370
428
  if (this.myConfig.showRootNode) {
371
- let root = {
372
- title: ' ' + this.myConfig.rootNode.name,
429
+ let root = {
430
+ title: ' ' + this.myConfig.rootNode.name,
373
431
  expand: true,
374
- disabled :this.myConfig.readOnly,
432
+ disabled: this.myConfig.readOnly,
375
433
  children: tree,
376
- selected:false
434
+ selected: false
377
435
  };
436
+
378
437
  if (this.myConfig.rootNode.disabled !== undefined) {
379
438
  root.disabled = this.myConfig.rootNode.disabled;
380
439
  }
@@ -386,18 +445,15 @@ export default {
386
445
  root = this.setIcon(root);
387
446
  }
388
447
  }
389
- if (this.myConfig.nodeRender) {
390
- root.render = this.myConfig.nodeRender;
391
- } else{
392
- root.render = this.iconRender;
393
- }
448
+
449
+ root.render = this.myConfig.nodeRender ? this.myConfig.nodeRender : this.iconRender;
450
+
394
451
  this._autoExpandId = null;
395
452
  return [root];
396
453
  } else {
397
454
  this._autoExpandId = null;
398
455
  return tree;
399
456
  }
400
-
401
457
  },
402
458
  setSelected (reTemp) {
403
459
  if (this.myConfig.expand) {
@@ -742,6 +798,7 @@ export default {
742
798
  updateNode(this.data)
743
799
 
744
800
  },
801
+
745
802
  setNodes(data, name, value) {
746
803
  let tempName = name;
747
804
  if (name === 'readOnly') {
@@ -754,6 +811,100 @@ export default {
754
811
  }
755
812
  }
756
813
  let responseId = this.myConfig.responseId;
814
+ this.$refs.tree.flatState.forEach(el => {
815
+ let index = data.findIndex(
816
+ el2 => el.node[responseId] === el2[responseId]
817
+ );
818
+ if (index > -1) {
819
+ if (el.node[tempName] !== value) {
820
+ let obj ={};
821
+ obj[tempName] = value;
822
+ Object.assign(el.node,obj);
823
+ }
824
+ } else {
825
+ if (el.node[tempName] !== !value) {
826
+ let obj ={};
827
+ obj[tempName] = !value;
828
+ Object.assign(el.node,obj);
829
+ }
830
+ }
831
+ });
832
+ },
833
+
834
+ setNodese(data = [], name, value) {
835
+ let tempName = name;
836
+ if (name === 'readOnly') {
837
+ tempName = 'disabled';
838
+ } else if (name === 'selected') {
839
+ tempName = (this.myConfig.multiSelect && this.myConfig.showCheckBox) ? 'checked' : 'selected';
840
+ }
841
+
842
+ const responseId = this.myConfig.idField || this.myConfig.responseId;
843
+
844
+ // 统一字符串,避免 1 / "1" 不匹配
845
+ const idSet = new Set(
846
+ (data || []).map(item => String(item?.[responseId]))
847
+ );
848
+
849
+ // 确认真实渲染源
850
+ const source = Array.isArray(this.data)
851
+ ? this.data
852
+ : (Array.isArray(this.treeData) ? this.treeData : []);
853
+
854
+ let hitCount = 0;
855
+
856
+ const updateNode = (nodes) => {
857
+ if (!Array.isArray(nodes)) return;
858
+
859
+ nodes.forEach(node => {
860
+ const exists = idSet.has(String(node?.[responseId]));
861
+ if (exists) hitCount++;
862
+
863
+ let newValue;
864
+
865
+ // 只有“选中=true”时,才清空其他节点(单选常见需求)
866
+ if (name === 'selected' && value === true) {
867
+ newValue = exists;
868
+ } else if (exists) {
869
+ // 其他场景只改命中节点
870
+ newValue = value;
871
+ } else {
872
+ newValue = node[tempName];
873
+ }
874
+
875
+ if (node[tempName] !== newValue) {
876
+ // Vue2: 动态键必须用 $set 才响应
877
+ // this.$set(node, tempName, newValue);
878
+ node[tempName] = newValue;
879
+ }
880
+
881
+ if (node.children && node.children.length > 0) {
882
+ updateNode(node.children);
883
+ }
884
+ });
885
+ };
886
+
887
+ updateNode(source);
888
+
889
+ // 兜底触发视图更新
890
+ if (source === this.data) this.data = [...source];
891
+ if (source === this.treeData) this.treeData = [...source];
892
+
893
+ console.log('[setNodes]', { tempName, responseId, targetIds: [...idSet], hitCount });
894
+ return hitCount;
895
+ },
896
+ setNodes2(data, name, value) {
897
+ let tempName = name;
898
+ if (name === 'readOnly') {
899
+ tempName = 'disabled';
900
+ } else if (name === 'selected') {
901
+ if (this.myConfig.multiSelect && this.myConfig.showCheckBox) {
902
+ tempName ='checked';
903
+ } else {
904
+ tempName = 'selected';
905
+ }
906
+ }
907
+ let responseId = this.myConfig.idField || this.myConfig.responseId;
757
908
  // 把目标列表变成 Set,提高查找效率
758
909
  const idSet = new Set(
759
910
  data.map(item => item[responseId])
@@ -802,14 +953,12 @@ export default {
802
953
  return expandSet;
803
954
  },
804
955
  updateNode(data, flag) {
805
-
806
956
  const idField = this.myConfig.idField
807
957
  const parentField = this.myConfig.parentField
808
958
  const targetId = data[idField]
809
959
 
810
960
  // ① 保存当前展开状态
811
961
  const expandSet = this.saveExpandState()
812
-
813
962
  // ② 数据更新
814
963
  const index = this.data.findIndex(
815
964
  el => el[idField] === targetId
@@ -843,7 +992,6 @@ export default {
843
992
  this.data.splice(index, 1)
844
993
  }
845
994
  }
846
-
847
995
  // ③ 重建树(带展开状态)
848
996
  this.treeData = this.toTreeData(expandSet, targetId)
849
997
  },