topo-engine 0.1.8 → 0.1.10

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/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
  - 🔍 **拓扑查询 API** — 节点 / 边 / 邻居 / 子树 / 主干链 / 路径 / 坐标 查询
13
13
  - 🎨 **样式控制** — 节点、边样式覆盖与批量重置
14
14
  - 🎨 **节点 / 电表染色** — 按 **psrId(设备编码)** 染普通节点、按 **assetNo(资产编号)** 染计量箱内电表户(状态色、告警色场景)
15
- - ✨ **动画与高亮** — 节点脉动、边流动、路径高亮、其余变暗、异常标注
15
+ - ✨ **动画与高亮** — 节点脉动、节点光晕(发光环,颜色可设)、边流动、路径高亮、其余变暗、异常标注
16
16
  - 📝 **文字标注** — 附加文字、用户名 full / short / hidden 切换
17
17
  - 🃏 **卡片盒** — 节点信息卡片(字段 + 按钮),适配"运行曲线/工单跳转"类场景
18
18
  - 🔗 **连接线 / 拓扑线** — 任意两节点动态连接线、路径拓扑高亮线
@@ -257,6 +257,26 @@ const dataUrl = ref('/data/topology.json');
257
257
 
258
258
  > 也可自行 `import { TopoApi }`,按 `new TopoApi(graph, model, layout, nodeByIdMap)` 构造后调用同一套方法;事件与查询见下。使用组件时无需关心内部构造。
259
259
 
260
+ > 📌 **引用统一(id / psrId / assetNo)**:所有目标入参(查询/定位/样式/高亮/动画/文字/卡片/连线…)都可传
261
+ > **节点内部 id**、**psrId(设备编码)** 或 **客户 assetNo(资产编号,兼容 consNo/consId)**:
262
+ > - 命中普通节点 → 作用在该节点;
263
+ > - 命中**箱内电表户** → 视觉/定位/动画**精确作用在该户表位**(光环/高亮环/脉动/文字/染色/卡片),
264
+ > 查询/拓扑按所在计量箱参与(`getNode(assetNo)` 返回箱节点并附 `customer` 信息),
265
+ > 定位聚焦表位中心(`locateNode(assetNo)`、`getNodePosition(assetNo)`)。
266
+ >
267
+ > ```js
268
+ > topo.locateNode(assetNo); // 定位到某户表位中心
269
+ > topo.highlightNode(assetNo, { color: '#FF0000' }); // 该户表位高亮环
270
+ > topo.setNodeGlow(assetNo, { color: '#FF6600' }); // 该户表位光晕
271
+ > topo.setNodeGlow(assetNo, { blink: true, color: '#FF6600' }); // 该户表位整体闪烁
272
+ > topo.setNodePulse(assetNo); // 该户电表格脉动
273
+ > topo.setText(assetNo, '异常'); // 表位上方标注
274
+ > topo.showCard(assetNo); // 客户档案卡片(定位在表位旁)
275
+ > topo.selectNode(assetNo); // 编程选中该户(联动 customer-select)
276
+ > topo.getNode(assetNo); // → 所在箱节点副本 { …, customer, customerIndex, targetKind:'customer' }
277
+ > topo.resolveRef(assetNo); // → { kind:'customer', boxId, index, customer, … } | null
278
+ > ```
279
+
260
280
  ### 查询
261
281
 
262
282
  | 方法 | 说明 |
@@ -318,6 +338,9 @@ topo.unhighlightAll();
318
338
 
319
339
  ```javascript
320
340
  topo.setNodePulse('2', { color: '#00C8FF', duration: 1500 }); // 节点脉动
341
+ topo.setNodeGlow('2', { color: '#FF0000' }); // 节点光晕(多层光环 + shadowBlur 辉光 + blur 面光底衬;ref 支持 psrId)
342
+ topo.setNodeGlow('2', { color: '#FF0000', intensity: 2, spread: 30, blink: true, duration: 800 }); // 更夸张:加强 + 更大扩散 + 图元整体闪烁
343
+ topo.removeNodeGlow('2');
321
344
  topo.setEdgeFlow('1', '2', { color: '#00C8FF', duration: 2000 }); // 边流动
322
345
  topo.removeAnimation('2');
323
346
  topo.removeAllAnimations();
package/dist/index.d.ts CHANGED
@@ -8,40 +8,46 @@
8
8
  /// <reference lib="es2015.promise" />
9
9
 
10
10
  // 核心 API
11
+ // ref:全库统一引用 —— 节点内部 id / psrId(设备编码)命中普通节点;
12
+ // 客户 assetNo(资产编号,兼容 consNo/consId)命中「计量箱内电表户」。
11
13
  export class TopoApi {
12
14
  constructor(graph: any, model: any, layout: any, nodeByIdMap: Map<string, any>);
13
-
14
- // 查询 API
15
- getNode(nodeId: string): any;
15
+
16
+ // 统一引用解析
17
+ resolveRef(ref: string): any; // { kind:'node', id, node } | { kind:'customer', boxId, boxNode, index, customer, key, assetNo, name } | null
18
+ getCustomer(ref: string): { node: any, customer: any, index: number, count: number, boxId: string } | null;
19
+
20
+ // 查询 API(目标参数均支持 id/psrId/assetNo;assetNo 命中后按确认语义返回)
21
+ getNode(ref: string): any; // 电表户 → 所在计量箱节点副本(附 customer/customerIndex/customerCount/targetKind)
16
22
  getAllNodes(): any[];
17
23
  findNodes(filter: object): any[];
18
24
  getSelectedId(): string | null;
19
- getNeighbors(nodeId: string): { upstream: any[], downstream: any[] };
20
- getStreamNodes(nodeId: string, direction: 'upstream' | 'downstream'): any[];
25
+ getNeighbors(ref: string): { upstream: any[], downstream: any[] };
26
+ getStreamNodes(ref: string, direction: 'upstream' | 'downstream'): any[];
21
27
  getTrunkNodes(): any[];
22
- getSubtreeNodes(nodeId: string): any[];
23
- getPath(fromId: string, toId: string): any[] | null;
24
- getEdge(srcId: string, tgtId: string): any;
28
+ getSubtreeNodes(ref: string): any[];
29
+ getPath(fromRef: string, toRef: string): any[] | null;
30
+ getEdge(srcRef: string, tgtRef: string): any;
25
31
  getEdges(): any[];
26
- getStreamEdges(nodeId: string, direction: 'upstream' | 'downstream'): any[];
32
+ getStreamEdges(ref: string, direction: 'upstream' | 'downstream'): any[];
27
33
  getMainEdge(): any[];
28
- getNodePosition(nodeId: string): { x: number, y: number } | null;
29
- getEdgePath(srcId: string, tgtId: string): number[][] | null;
34
+ getNodePosition(ref: string): { x: number, y: number } | null; // assetNo → 表位中心
35
+ getEdgePath(srcRef: string, tgtRef: string): number[][] | null;
30
36
  getGraphBounds(): { minX: number, minY: number, maxX: number, maxY: number };
31
37
  getStats(): any;
32
-
38
+
33
39
  // 视图控制
34
40
  fitView(padding?: number): void;
35
41
  zoomTo(ratio: number, center?: { x: number, y: number }): void;
36
42
  zoomIn(step?: number): void;
37
43
  zoomOut(step?: number): void;
38
- locateNode(nodeId: string, zoom?: number): void;
44
+ locateNode(ref: string, zoom?: number): void; // assetNo → 聚焦表位
39
45
  getViewport(): { zoom: number, center: { x: number, y: number } };
40
-
41
- // 节点样式
42
- setNodeStyle(nodeId: string, style: object): void;
43
- batchSetNodeStyle(nodeIds: string[], style: object): void;
44
- resetNodeStyle(nodeId: string): void;
46
+
47
+ // 节点样式(assetNo → 作用于所在计量箱节点)
48
+ setNodeStyle(ref: string, style: object): void;
49
+ batchSetNodeStyle(refs: string[], style: object): void;
50
+ resetNodeStyle(ref: string): void;
45
51
  resetAllNodeStyles(): void;
46
52
 
47
53
  // 节点 / 箱内电表户染色(ref:节点 id 或 psrId;电表户用客户 assetNo/consNo/consId)
@@ -50,43 +56,65 @@ export class TopoApi {
50
56
  resetNodeColor(ref: string): boolean;
51
57
  resetAllNodeColors(): void;
52
58
  getNodeColor(ref: string): { kind: 'node' | 'customer', id: string, index?: number, color: string | null } | null;
53
-
54
- // 边样式
55
- setEdgeStyle(srcId: string, tgtId: string, style: object): void;
56
- batchSetEdgeStyle(edgeIds: [string, string][], style: object): void;
57
- resetEdgeStyle(srcId: string, tgtId: string): void;
58
-
59
- // 高亮与标注
60
- highlightNode(nodeId: string, opts?: object): void;
61
- unhighlightNode(nodeId: string): void;
62
- highlightPath(nodeIds: string[], opts?: object): void;
59
+
60
+ // 边样式(端点均可传节点 id / psrId / assetNo)
61
+ setEdgeStyle(srcRef: string, tgtRef: string, style: object): void;
62
+ batchSetEdgeStyle(edgeRefs: [string, string][], style: object): void;
63
+ resetEdgeStyle(srcRef: string, tgtRef: string): void;
64
+
65
+ // 高亮与标注(assetNo → 该户表位高亮环 + 可选文字)
66
+ highlightNode(ref: string, opts?: object): void;
67
+ unhighlightNode(ref: string): void;
68
+ highlightPath(refs: string[], opts?: object): void;
63
69
  unhighlightAll(): void;
64
- dimOthers(keepIds: string[], opacity?: number): void;
65
-
66
- // 动画
67
- setNodePulse(nodeId: string, opts?: object): void;
68
- setEdgeFlow(srcId: string, tgtId: string, opts?: object): void;
69
- removeAnimation(targetId: string): void;
70
+ dimOthers(keepRefs: string[], opacity?: number): void;
71
+
72
+ // 动画(ref 支持节点 id/psrId;assetNo → 该户电表格)
73
+ setNodePulse(ref: string, opts?: { duration?: number; min?: number; max?: number }): void;
74
+ setEdgeFlow(srcRef: string, tgtRef: string, opts?: object): void;
75
+ removeAnimation(ref: string): void;
70
76
  removeAllAnimations(): void;
71
-
72
- // 文字标注
73
- setText(nodeId: string, text: string, opts?: object): void;
74
- removeText(nodeId: string): void;
77
+
78
+ // 节点光晕动画(ref 支持节点 id / psrId / assetNo)
79
+ // 渲染栈:blur 柔光底衬 + 实心光块 → 图元 → 外扩散环 ×2 → 霓虹主环(shadowBlur) → 亮芯环
80
+ // intensity: 强度倍率(默认 1;1.5~2.5 更夸张,0.5~0.7 更收敛)
81
+ // spread: 光环外扩半径,默认按图元大小自适应(短边 ×0.7,16~46px)
82
+ // width: 主环初始线宽 (px),默认 5.6(户表位 3.6)
83
+ // blink: 图元「整体闪烁」——false 默认只画光环;true = 0.85;数字 0~1 为强度
84
+ setNodeGlow(ref: string, opts?: {
85
+ color?: string;
86
+ spread?: number;
87
+ radius?: number;
88
+ duration?: number;
89
+ opacity?: number;
90
+ width?: number;
91
+ intensity?: number;
92
+ level?: number;
93
+ strength?: number;
94
+ blink?: boolean | number;
95
+ flash?: boolean | number;
96
+ }): boolean;
97
+ removeNodeGlow(ref: string): boolean;
98
+ removeAllNodeGlows(): void;
99
+
100
+ // 文字标注(assetNo → 该户表位旁小标注)
101
+ setText(ref: string, text: string, opts?: object): void;
102
+ removeText(ref: string): void;
75
103
  removeAllTexts(): void;
76
104
  setDeviceText(cat: string, fontSize: number): void;
77
105
  changeUserName(mode: 'full' | 'short' | 'hidden'): void;
78
-
79
- // 卡片盒
80
- showCard(nodeId: string, content: object): void;
81
- hideCard(nodeId: string): void;
106
+
107
+ // 卡片盒(content 可省略:自动按设备/客户档案生成;assetNo 卡片定位在该户表位旁)
108
+ showCard(ref: string, content?: object): void;
109
+ hideCard(ref: string): void;
82
110
  hideAllCards(): void;
83
111
 
84
- // 连接线与拓扑线(端点支持节点 id 或箱内用户的客户档案编号 consNo/consId
112
+ // 连接线与拓扑线(端点/路径点支持节点 id、psrId 或箱内用户的客户编号 assetNo
85
113
  resolveUserRef(ref: string): string | null;
86
114
  addConnection(fromId: string, toId: string, opts?: object): string | false;
87
115
  removeConnection(fromId: string, toId: string): void;
88
116
  removeAllConnections(): void;
89
- addTopoLine(nodeIds: string[], opts?: object): string;
117
+ addTopoLine(refs: string[], opts?: object): string;
90
118
  removeTopoLine(lineId: string): void;
91
119
  removeAllTopoLines(): void;
92
120
 
package/dist/style.css CHANGED
@@ -1,6 +1,6 @@
1
1
 
2
2
  /* —— 主题变量:dark 为默认值,light 由 [data-theme='light'] 覆盖 —— */
3
- .topo-wrap[data-v-c2cd8b2c] {
3
+ .topo-wrap[data-v-a4a4c398] {
4
4
  --tp-bg: #050505;
5
5
  --tp-toolbar-bg: rgba(20, 20, 20, 0.88);
6
6
  --tp-toolbar-border: #333;
@@ -19,7 +19,7 @@
19
19
  overflow: hidden;
20
20
  background: var(--tp-bg);
21
21
  }
22
- .topo-wrap[data-theme='light'][data-v-c2cd8b2c] {
22
+ .topo-wrap[data-theme='light'][data-v-a4a4c398] {
23
23
  --tp-bg: #ffffff;
24
24
  --tp-toolbar-bg: rgba(255, 255, 255, 0.92);
25
25
  --tp-toolbar-border: #d9d9d9;
@@ -33,11 +33,11 @@
33
33
  --tp-text: #555;
34
34
  --tp-error: #d32f2f;
35
35
  }
36
- .topo-container[data-v-c2cd8b2c] {
36
+ .topo-container[data-v-a4a4c398] {
37
37
  width: 100%;
38
38
  height: 100%;
39
39
  }
40
- .topo-toolbar[data-v-c2cd8b2c] {
40
+ .topo-toolbar[data-v-a4a4c398] {
41
41
  position: absolute;
42
42
  top: 10px;
43
43
  left: 12px;
@@ -52,14 +52,14 @@
52
52
  color: var(--tp-text);
53
53
  z-index: 10;
54
54
  }
55
- .topo-title[data-v-c2cd8b2c] {
55
+ .topo-title[data-v-a4a4c398] {
56
56
  color: var(--tp-title);
57
57
  font-weight: 600;
58
58
  }
59
- .topo-meta[data-v-c2cd8b2c] {
59
+ .topo-meta[data-v-a4a4c398] {
60
60
  color: var(--tp-meta);
61
61
  }
62
- .btn[data-v-c2cd8b2c] {
62
+ .btn[data-v-a4a4c398] {
63
63
  margin-left: 4px;
64
64
  padding: 3px 10px;
65
65
  border: 1px solid var(--tp-btn-border);
@@ -69,12 +69,12 @@
69
69
  cursor: pointer;
70
70
  font-size: 12px;
71
71
  }
72
- .btn[data-v-c2cd8b2c]:hover {
72
+ .btn[data-v-a4a4c398]:hover {
73
73
  border-color: var(--tp-accent);
74
74
  color: var(--tp-accent);
75
75
  }
76
- .topo-loading[data-v-c2cd8b2c],
77
- .topo-error[data-v-c2cd8b2c] {
76
+ .topo-loading[data-v-a4a4c398],
77
+ .topo-error[data-v-a4a4c398] {
78
78
  position: absolute;
79
79
  inset: 0;
80
80
  display: flex;
@@ -85,7 +85,7 @@
85
85
  z-index: 5;
86
86
  background: var(--tp-overlay-bg);
87
87
  }
88
- .topo-error[data-v-c2cd8b2c] {
88
+ .topo-error[data-v-a4a4c398] {
89
89
  color: var(--tp-error);
90
90
  }
91
91