ocpview-plus 1.2.7 → 1.2.8

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);
317
330
 
318
- const parentId = current[this.myConfig.parentField];
319
- if (!parentId) break;
331
+ autoExpandParents.add(cid); // 选中节点本身 + 全部祖先都加入展开集合
320
332
 
321
- current = this.data.find(
322
- item => item[this.myConfig.idField] === parentId
323
- );
333
+ const pid = current[parentField];
334
+ if (pid == null) break; // 注意:不要写 !pid,避免 0 被误判
335
+
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,7 +798,70 @@ export default {
742
798
  updateNode(this.data)
743
799
 
744
800
  },
745
- setNodes(data, name, value) {
801
+
802
+ setNodes(data = [], name, value) {
803
+ let tempName = name;
804
+ if (name === 'readOnly') {
805
+ tempName = 'disabled';
806
+ } else if (name === 'selected') {
807
+ tempName = (this.myConfig.multiSelect && this.myConfig.showCheckBox) ? 'checked' : 'selected';
808
+ }
809
+
810
+ const responseId = this.myConfig.idField || this.myConfig.responseId;
811
+
812
+ // 统一字符串,避免 1 / "1" 不匹配
813
+ const idSet = new Set(
814
+ (data || []).map(item => String(item?.[responseId]))
815
+ );
816
+
817
+ // 确认真实渲染源
818
+ const source = Array.isArray(this.data)
819
+ ? this.data
820
+ : (Array.isArray(this.treeData) ? this.treeData : []);
821
+
822
+ let hitCount = 0;
823
+
824
+ const updateNode = (nodes) => {
825
+ if (!Array.isArray(nodes)) return;
826
+
827
+ nodes.forEach(node => {
828
+ const exists = idSet.has(String(node?.[responseId]));
829
+ if (exists) hitCount++;
830
+
831
+ let newValue;
832
+
833
+ // 只有“选中=true”时,才清空其他节点(单选常见需求)
834
+ if (name === 'selected' && value === true) {
835
+ newValue = exists;
836
+ } else if (exists) {
837
+ // 其他场景只改命中节点
838
+ newValue = value;
839
+ } else {
840
+ newValue = node[tempName];
841
+ }
842
+
843
+ if (node[tempName] !== newValue) {
844
+ // Vue2: 动态键必须用 $set 才响应
845
+ // this.$set(node, tempName, newValue);
846
+ node[tempName] = newValue;
847
+ }
848
+
849
+ if (node.children && node.children.length > 0) {
850
+ updateNode(node.children);
851
+ }
852
+ });
853
+ };
854
+
855
+ updateNode(source);
856
+
857
+ // 兜底触发视图更新
858
+ if (source === this.data) this.data = [...source];
859
+ if (source === this.treeData) this.treeData = [...source];
860
+
861
+ console.log('[setNodes]', { tempName, responseId, targetIds: [...idSet], hitCount });
862
+ return hitCount;
863
+ },
864
+ setNodes2(data, name, value) {
746
865
  let tempName = name;
747
866
  if (name === 'readOnly') {
748
867
  tempName = 'disabled';
@@ -753,7 +872,7 @@ export default {
753
872
  tempName = 'selected';
754
873
  }
755
874
  }
756
- let responseId = this.myConfig.responseId;
875
+ let responseId = this.myConfig.idField || this.myConfig.responseId;
757
876
  // 把目标列表变成 Set,提高查找效率
758
877
  const idSet = new Set(
759
878
  data.map(item => item[responseId])
@@ -802,14 +921,12 @@ export default {
802
921
  return expandSet;
803
922
  },
804
923
  updateNode(data, flag) {
805
-
806
924
  const idField = this.myConfig.idField
807
925
  const parentField = this.myConfig.parentField
808
926
  const targetId = data[idField]
809
927
 
810
928
  // ① 保存当前展开状态
811
929
  const expandSet = this.saveExpandState()
812
-
813
930
  // ② 数据更新
814
931
  const index = this.data.findIndex(
815
932
  el => el[idField] === targetId
@@ -843,7 +960,6 @@ export default {
843
960
  this.data.splice(index, 1)
844
961
  }
845
962
  }
846
-
847
963
  // ③ 重建树(带展开状态)
848
964
  this.treeData = this.toTreeData(expandSet, targetId)
849
965
  },