topo-engine 0.1.7 → 0.1.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.
package/README.md CHANGED
@@ -11,7 +11,8 @@
11
11
  - 📐 **自研正交布局** — 主干沿最长路径横向铺开 + 分支带垂直直走,整图**横平竖直、0 交叉、0 重叠**,边缘路由为直线 / L 形折线
12
12
  - 🔍 **拓扑查询 API** — 节点 / 边 / 邻居 / 子树 / 主干链 / 路径 / 坐标 查询
13
13
  - 🎨 **样式控制** — 节点、边样式覆盖与批量重置
14
- - **动画与高亮**节点脉动、边流动、路径高亮、其余变暗、异常标注
14
+ - 🎨 **节点 / 电表染色** 按 **psrId(设备编码)** 染普通节点、按 **assetNo(资产编号)** 染计量箱内电表户(状态色、告警色场景)
15
+ - ✨ **动画与高亮** — 节点脉动、节点光晕(发光环,颜色可设)、边流动、路径高亮、其余变暗、异常标注
15
16
  - 📝 **文字标注** — 附加文字、用户名 full / short / hidden 切换
16
17
  - 🃏 **卡片盒** — 节点信息卡片(字段 + 按钮),适配"运行曲线/工单跳转"类场景
17
18
  - 🔗 **连接线 / 拓扑线** — 任意两节点动态连接线、路径拓扑高亮线
@@ -256,6 +257,25 @@ const dataUrl = ref('/data/topology.json');
256
257
 
257
258
  > 也可自行 `import { TopoApi }`,按 `new TopoApi(graph, model, layout, nodeByIdMap)` 构造后调用同一套方法;事件与查询见下。使用组件时无需关心内部构造。
258
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.setNodePulse(assetNo); // 该户电表格脉动
272
+ > topo.setText(assetNo, '异常'); // 表位上方标注
273
+ > topo.showCard(assetNo); // 客户档案卡片(定位在表位旁)
274
+ > topo.selectNode(assetNo); // 编程选中该户(联动 customer-select)
275
+ > topo.getNode(assetNo); // → 所在箱节点副本 { …, customer, customerIndex, targetKind:'customer' }
276
+ > topo.resolveRef(assetNo); // → { kind:'customer', boxId, index, customer, … } | null
277
+ > ```
278
+
259
279
  ### 查询
260
280
 
261
281
  | 方法 | 说明 |
@@ -282,6 +302,24 @@ const dataUrl = ref('/data/topology.json');
282
302
  `setNodeStyle(id, style)`、`batchSetNodeStyle(ids, style)`、`resetNodeStyle(id)`、`resetAllNodeStyles()`
283
303
  `setEdgeStyle(src, tgt, style)`、`batchSetEdgeStyle([[src,tgt],…], style)`、`resetEdgeStyle(src, tgt)`
284
304
 
305
+ ### 节点 / 箱内电表户染色(psrId / 资产编号)
306
+
307
+ ```javascript
308
+ // 普通节点:按 psrId(设备编码)整图单色换装(主体/引线/描边换成该色,白字细节保留)
309
+ topo.setNodeColor('8ff4786d548a7073a2517297e801518ff456732f4d', '#FF0000');
310
+ // 箱内电表户:按资产编号(assetNo,兼容 consNo/consId)染该户电表图标 + 户名
311
+ topo.setNodeColor('4230010007008084768', '#FF6600');
312
+ // 批量(数组 / 对象数组 / ref→color 对象均可)
313
+ topo.batchSetNodeColor({ '8ff4…': '#FF0000', '4230…': '#FF6600' });
314
+ // 清除 / 查询
315
+ topo.resetNodeColor('4230010007008084768');
316
+ topo.resetAllNodeColors();
317
+ topo.getNodeColor('4230010007008084768'); // → { kind, id, color } | null
318
+ ```
319
+
320
+ > 引用规则与 `addConnection` 一致:节点 id / **psrId** 命中普通节点;**assetNo** 命中计量箱内某户电表。
321
+ > 染色记录随主题切换 / 数据重渲染自动保留并重放(无需重新设置)。详见 [docs/API.md](docs/API.md)。
322
+
285
323
  ### 高亮 / 变暗
286
324
 
287
325
  ```javascript
@@ -299,6 +337,8 @@ topo.unhighlightAll();
299
337
 
300
338
  ```javascript
301
339
  topo.setNodePulse('2', { color: '#00C8FF', duration: 1500 }); // 节点脉动
340
+ topo.setNodeGlow('2', { color: '#FF0000', spread: 18 }); // 节点光晕(发光环,颜色可设;ref 支持 psrId)
341
+ topo.removeNodeGlow('2');
302
342
  topo.setEdgeFlow('1', '2', { color: '#00C8FF', duration: 2000 }); // 边流动
303
343
  topo.removeAnimation('2');
304
344
  topo.removeAllAnimations();
package/dist/index.d.ts CHANGED
@@ -8,78 +8,96 @@
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
-
47
- // 边样式
48
- setEdgeStyle(srcId: string, tgtId: string, style: object): void;
49
- batchSetEdgeStyle(edgeIds: [string, string][], style: object): void;
50
- resetEdgeStyle(srcId: string, tgtId: string): void;
51
-
52
- // 高亮与标注
53
- highlightNode(nodeId: string, opts?: object): void;
54
- unhighlightNode(nodeId: string): void;
55
- highlightPath(nodeIds: string[], opts?: object): void;
52
+
53
+ // 节点 / 箱内电表户染色(ref:节点 id 或 psrId;电表户用客户 assetNo/consNo/consId)
54
+ setNodeColor(ref: string, color: string | null): boolean;
55
+ batchSetNodeColor(entries: any, color?: string): { total: number, ok: number, failed: Array<{ ref: string, reason: string }> };
56
+ resetNodeColor(ref: string): boolean;
57
+ resetAllNodeColors(): void;
58
+ getNodeColor(ref: string): { kind: 'node' | 'customer', id: string, index?: number, color: string | null } | null;
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;
56
69
  unhighlightAll(): void;
57
- dimOthers(keepIds: string[], opacity?: number): void;
58
-
59
- // 动画
60
- setNodePulse(nodeId: string, opts?: object): void;
61
- setEdgeFlow(srcId: string, tgtId: string, opts?: object): void;
62
- 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;
63
76
  removeAllAnimations(): void;
64
-
65
- // 文字标注
66
- setText(nodeId: string, text: string, opts?: object): void;
67
- removeText(nodeId: string): void;
77
+
78
+ // 节点光晕动画(ref 支持节点 id / psrId / assetNo)
79
+ setNodeGlow(ref: string, opts?: { color?: string; spread?: number; radius?: number; duration?: number; opacity?: number }): boolean;
80
+ removeNodeGlow(ref: string): boolean;
81
+ removeAllNodeGlows(): void;
82
+
83
+ // 文字标注(assetNo → 该户表位旁小标注)
84
+ setText(ref: string, text: string, opts?: object): void;
85
+ removeText(ref: string): void;
68
86
  removeAllTexts(): void;
69
87
  setDeviceText(cat: string, fontSize: number): void;
70
88
  changeUserName(mode: 'full' | 'short' | 'hidden'): void;
71
-
72
- // 卡片盒
73
- showCard(nodeId: string, content: object): void;
74
- hideCard(nodeId: string): void;
89
+
90
+ // 卡片盒(content 可省略:自动按设备/客户档案生成;assetNo 卡片定位在该户表位旁)
91
+ showCard(ref: string, content?: object): void;
92
+ hideCard(ref: string): void;
75
93
  hideAllCards(): void;
76
94
 
77
- // 连接线与拓扑线(端点支持节点 id 或箱内用户的客户档案编号 consNo/consId
95
+ // 连接线与拓扑线(端点/路径点支持节点 id、psrId 或箱内用户的客户编号 assetNo
78
96
  resolveUserRef(ref: string): string | null;
79
97
  addConnection(fromId: string, toId: string, opts?: object): string | false;
80
98
  removeConnection(fromId: string, toId: string): void;
81
99
  removeAllConnections(): void;
82
- addTopoLine(nodeIds: string[], opts?: object): string;
100
+ addTopoLine(refs: string[], opts?: object): string;
83
101
  removeTopoLine(lineId: string): void;
84
102
  removeAllTopoLines(): void;
85
103
 
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-d3774ed5] {
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-d3774ed5] {
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-d3774ed5] {
37
37
  width: 100%;
38
38
  height: 100%;
39
39
  }
40
- .topo-toolbar[data-v-c2cd8b2c] {
40
+ .topo-toolbar[data-v-d3774ed5] {
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-d3774ed5] {
56
56
  color: var(--tp-title);
57
57
  font-weight: 600;
58
58
  }
59
- .topo-meta[data-v-c2cd8b2c] {
59
+ .topo-meta[data-v-d3774ed5] {
60
60
  color: var(--tp-meta);
61
61
  }
62
- .btn[data-v-c2cd8b2c] {
62
+ .btn[data-v-d3774ed5] {
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-d3774ed5]: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-d3774ed5],
77
+ .topo-error[data-v-d3774ed5] {
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-d3774ed5] {
89
89
  color: var(--tp-error);
90
90
  }
91
91