topo-engine 0.1.7 → 0.1.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.
@@ -25024,8 +25024,8 @@ function injectCustomers(model, customerMap) {
25024
25024
  node._customers = customers;
25025
25025
  if (customers.length === 1) {
25026
25026
  node._meterBoxMode = "single";
25027
- node._meterBoxW = 30;
25028
- node._meterBoxH = 38;
25027
+ node._meterBoxW = 22;
25028
+ node._meterBoxH = 30;
25029
25029
  } else {
25030
25030
  const showName = customers.length <= SHOW_NAME_LIMIT;
25031
25031
  const cols = Math.min(customers.length, MAX_COLS);
@@ -25098,17 +25098,26 @@ class TopoApi {
25098
25098
  this._nodeLabels = /* @__PURE__ */ new Map();
25099
25099
  this._originalNodeStyles = /* @__PURE__ */ new Map();
25100
25100
  this._originalEdgeStyles = /* @__PURE__ */ new Map();
25101
+ this._nodeColorOverrides = /* @__PURE__ */ new Map();
25102
+ this._custColorOverrides = /* @__PURE__ */ new Map();
25101
25103
  this._bindConnGraphEvents();
25102
25104
  }
25103
25105
  /** 数据刷新(图重新渲染后调用) */
25104
25106
  update(graph, model, layout, nodeByIdMap) {
25107
+ const colorSnapshot = {
25108
+ node: new Map(this._nodeColorOverrides),
25109
+ cust: new Map(this._custColorOverrides)
25110
+ };
25105
25111
  this.destroy();
25106
25112
  this._graph = graph;
25107
25113
  this._model = model;
25108
25114
  this._layout = layout;
25109
25115
  this._nodeByIdMap = nodeByIdMap;
25110
25116
  this._selectedId = null;
25117
+ this._nodeColorOverrides = colorSnapshot.node;
25118
+ this._custColorOverrides = colorSnapshot.cust;
25111
25119
  this._bindConnGraphEvents();
25120
+ this._applyColorOverrides();
25112
25121
  }
25113
25122
  /** 销毁,释放所有资源 */
25114
25123
  destroy() {
@@ -25121,6 +25130,8 @@ class TopoApi {
25121
25130
  this._originalNodeStyles.clear();
25122
25131
  this._originalEdgeStyles.clear();
25123
25132
  this._nodeLabels.clear();
25133
+ this._nodeColorOverrides.clear();
25134
+ this._custColorOverrides.clear();
25124
25135
  this._graph = null;
25125
25136
  this._model = null;
25126
25137
  this._layout = null;
@@ -25504,6 +25515,230 @@ class TopoApi {
25504
25515
  this._originalNodeStyles.clear();
25505
25516
  }
25506
25517
  // ============================================================
25518
+ // 三·五、节点 / 箱内电表户染色(支持 psrId / 资产编号 / 节点 id)
25519
+ //
25520
+ // 普通节点(变压器/开关/熔丝/导线点/用户接入点…)→ 图元“整图单色换装”,
25521
+ // 主体/引线/描边换成指定色,白字与浅色细节保留可读;
25522
+ // 箱内电表户(计量箱 grid/single 里的每只电表)→ 该户电表图标与户名染色。
25523
+ // 引用规则与 addConnection 完全一致:
25524
+ // 节点:节点内部 id 或 psrId(设备编码);电表户:客户档案 assetNo(资产编号,
25525
+ // 兼容 consNo / consId)。染色记录随数据重渲染(主题切换/换数据)自动保留重放。
25526
+ // ============================================================
25527
+ /**
25528
+ * 把当前记录的染色覆盖(节点 / 电表户)重新写进图 style(update 后调用)。
25529
+ * 仅做 updateNodeData 合并,不主动 render —— 由随后的一次 render 统一生效。
25530
+ * 顺带清理指向已不存在节点 / 表位的过期记录(换数据后自动丢弃)。
25531
+ */
25532
+ _applyColorOverrides() {
25533
+ if (!this._graph) return;
25534
+ const updates = [];
25535
+ if (this._nodeColorOverrides.size) {
25536
+ const stale = [];
25537
+ for (const [id2, color2] of this._nodeColorOverrides) {
25538
+ if (!this._graph.getNodeData(id2)) {
25539
+ stale.push(id2);
25540
+ continue;
25541
+ }
25542
+ updates.push({ id: id2, style: { color: color2 || "" } });
25543
+ }
25544
+ for (const id2 of stale) this._nodeColorOverrides.delete(id2);
25545
+ }
25546
+ if (this._custColorOverrides.size) {
25547
+ const byBox = /* @__PURE__ */ new Map();
25548
+ const stale = [];
25549
+ for (const [key, color2] of this._custColorOverrides) {
25550
+ const sep = key.indexOf("#cust");
25551
+ if (sep < 0) {
25552
+ stale.push(key);
25553
+ continue;
25554
+ }
25555
+ const boxId = key.slice(0, sep);
25556
+ if (!this._graph.getNodeData(boxId)) {
25557
+ stale.push(key);
25558
+ continue;
25559
+ }
25560
+ if (!byBox.has(boxId)) byBox.set(boxId, /* @__PURE__ */ new Map());
25561
+ byBox.get(boxId).set(Number(key.slice(sep + 5)), color2);
25562
+ }
25563
+ for (const [boxId, cellMap] of byBox) {
25564
+ const cur = this._safeNodeStyle(boxId, {});
25565
+ const customers = cur._customers;
25566
+ if (!Array.isArray(customers) || !customers.length) continue;
25567
+ const arr = new Array(customers.length).fill(null);
25568
+ let any = false;
25569
+ for (const [idx, color2] of cellMap) {
25570
+ if (idx >= 0 && idx < arr.length && color2) {
25571
+ arr[idx] = color2;
25572
+ any = true;
25573
+ }
25574
+ }
25575
+ updates.push({ id: boxId, style: { _cellColors: any ? arr : null } });
25576
+ }
25577
+ for (const key of stale) this._custColorOverrides.delete(key);
25578
+ }
25579
+ if (updates.length) {
25580
+ try {
25581
+ this._graph.updateNodeData(updates);
25582
+ } catch (e2) {
25583
+ console.error("[TopoApi] _applyColorOverrides error:", e2);
25584
+ }
25585
+ }
25586
+ }
25587
+ /**
25588
+ * 写入某个解析端点的染色(setNodeColor / batchSetNodeColor 共用)。
25589
+ * @param {{kind:'node',id:string}|{kind:'customer',boxId:string,index:number,node:object}} e
25590
+ * @param {string|null} color 颜色;null/''/undefined = 清除该处染色
25591
+ */
25592
+ _writeColorEndpoint(e2, color2) {
25593
+ if (!this._graph) return;
25594
+ color2 = color2 == null ? null : String(color2).trim() || null;
25595
+ if (e2.kind === "node") {
25596
+ const id2 = e2.id;
25597
+ if (color2) this._nodeColorOverrides.set(id2, color2);
25598
+ else this._nodeColorOverrides.delete(id2);
25599
+ this._updateNodes([{ id: id2, style: { color: color2 || "" } }]);
25600
+ return;
25601
+ }
25602
+ const key = `${e2.boxId}#cust${e2.index}`;
25603
+ if (color2) this._custColorOverrides.set(key, color2);
25604
+ else this._custColorOverrides.delete(key);
25605
+ const cur = this._safeNodeStyle(e2.boxId, {});
25606
+ const customers = cur._customers;
25607
+ if (!Array.isArray(customers) || !customers.length) return;
25608
+ const arr = new Array(customers.length).fill(null);
25609
+ let any = false;
25610
+ for (const [k, c] of this._custColorOverrides) {
25611
+ if (!k.startsWith(`${e2.boxId}#cust`)) continue;
25612
+ const idx = Number(k.slice(k.indexOf("#cust") + 5));
25613
+ if (idx >= 0 && idx < arr.length && c) {
25614
+ arr[idx] = c;
25615
+ any = true;
25616
+ }
25617
+ }
25618
+ this._updateNodes([{ id: e2.boxId, style: { _cellColors: any ? arr : null } }]);
25619
+ }
25620
+ /**
25621
+ * 给节点 / 箱内电表户设置颜色。
25622
+ * @param {string} ref 节点内部 id / 节点 psrId(普通节点、计量箱本体)
25623
+ * 或 客户资产编号 assetNo(兼容 consNo/consId,箱内电表户)
25624
+ * @param {string} [color] 颜色('#RRGGBB' / 'red' 等);传 null/''/undefined 清除该处染色
25625
+ * @returns {boolean} 是否命中并设置成功
25626
+ */
25627
+ setNodeColor(ref2, color2) {
25628
+ this._assertReady();
25629
+ const e2 = this._parseEndpoint(ref2);
25630
+ if (!e2) {
25631
+ console.warn(`[TopoApi] setNodeColor: 无法解析目标 ${ref2}(普通节点请用 psrId/节点id,电表户请用 assetNo/consNo/consId)`);
25632
+ return false;
25633
+ }
25634
+ this._writeColorEndpoint(e2, color2);
25635
+ return true;
25636
+ }
25637
+ /**
25638
+ * 批量设置颜色。三种入参形式:
25639
+ * 1) 数组:[ ['8ff4…', '#FF0000'], ['4230…资产编号', '#00FF00'], … ]
25640
+ * 2) 数组:[{ ref / psrId / assetNo / nodeId, color? }](color 缺省用第 2 参)
25641
+ * 3) 对象:{ 'psrId或assetNo': '#FF0000', … }
25642
+ * @param {*} entries
25643
+ * @param {string} [color] 未在单项里给色时的兜底颜色
25644
+ * @returns {{total:number, ok:number, failed:Array<{ref:string, reason:string}>}}
25645
+ */
25646
+ batchSetNodeColor(entries, color2) {
25647
+ this._assertReady();
25648
+ const failed = [];
25649
+ let total = 0;
25650
+ const applyRef = (ref2, c) => {
25651
+ total++;
25652
+ const e2 = this._parseEndpoint(ref2);
25653
+ if (!e2) {
25654
+ failed.push({ ref: String(ref2), reason: "未命中任何节点/电表户" });
25655
+ return;
25656
+ }
25657
+ this._writeColorEndpoint(e2, c);
25658
+ };
25659
+ if (Array.isArray(entries)) {
25660
+ for (const item of entries) {
25661
+ if (Array.isArray(item)) {
25662
+ applyRef(item[0], item[1] != null ? item[1] : color2);
25663
+ } else if (item && typeof item === "object") {
25664
+ const ref2 = item.ref != null ? item.ref : item.psrId != null ? item.psrId : item.assetNo != null ? item.assetNo : item.nodeId;
25665
+ if (ref2 == null) {
25666
+ total++;
25667
+ failed.push({ ref: JSON.stringify(item), reason: "缺少 ref/psrId/assetNo/nodeId" });
25668
+ continue;
25669
+ }
25670
+ applyRef(ref2, item.color != null ? item.color : color2);
25671
+ }
25672
+ }
25673
+ } else if (entries && typeof entries === "object") {
25674
+ for (const [ref2, c] of Object.entries(entries)) applyRef(ref2, c);
25675
+ }
25676
+ return { total, ok: total - failed.length, failed };
25677
+ }
25678
+ /**
25679
+ * 清除某处染色(返回节点 / 电表户到主题默认配色)。
25680
+ * @param {string} ref 同 setNodeColor 的引用
25681
+ */
25682
+ resetNodeColor(ref2) {
25683
+ this._assertReady();
25684
+ const e2 = this._parseEndpoint(ref2);
25685
+ if (!e2) {
25686
+ console.warn(`[TopoApi] resetNodeColor: 无法解析目标 ${ref2}`);
25687
+ return false;
25688
+ }
25689
+ this._writeColorEndpoint(e2, null);
25690
+ return true;
25691
+ }
25692
+ /** 清除全部染色(所有节点与电表户回到主题默认配色) */
25693
+ resetAllNodeColors() {
25694
+ this._assertReady();
25695
+ const nodeUpdates = [];
25696
+ for (const [id2] of this._nodeColorOverrides) {
25697
+ if (this._graph.getNodeData(id2)) nodeUpdates.push({ id: id2, style: { color: "" } });
25698
+ }
25699
+ const boxes = /* @__PURE__ */ new Set();
25700
+ for (const [key] of this._custColorOverrides) {
25701
+ const sep = key.indexOf("#cust");
25702
+ if (sep >= 0) boxes.add(key.slice(0, sep));
25703
+ }
25704
+ for (const boxId of boxes) {
25705
+ if (this._graph.getNodeData(boxId)) {
25706
+ nodeUpdates.push({ id: boxId, style: { _cellColors: null } });
25707
+ }
25708
+ }
25709
+ if (nodeUpdates.length) this._updateNodes(nodeUpdates);
25710
+ this._nodeColorOverrides.clear();
25711
+ this._custColorOverrides.clear();
25712
+ }
25713
+ /**
25714
+ * 查询某处当前生效的染色。
25715
+ * @param {string} ref 同 setNodeColor 的引用
25716
+ * @returns {{ kind: 'node'|'customer', id: string, index?: number, color: string|null } | null}
25717
+ */
25718
+ getNodeColor(ref2) {
25719
+ this._assertReady();
25720
+ const e2 = this._parseEndpoint(ref2);
25721
+ if (!e2) return null;
25722
+ if (e2.kind === "node") {
25723
+ const color3 = this._nodeColorOverrides.get(e2.id);
25724
+ if (color3) return { kind: "node", id: e2.id, color: color3 };
25725
+ const cur2 = this._safeNodeStyle(e2.id, {});
25726
+ return { kind: "node", id: e2.id, color: cur2.color || null };
25727
+ }
25728
+ const key = `${e2.boxId}#cust${e2.index}`;
25729
+ const color2 = this._custColorOverrides.get(key);
25730
+ if (color2) return { kind: "customer", id: key, boxId: e2.boxId, index: e2.index, color: color2 };
25731
+ const cur = this._safeNodeStyle(e2.boxId, {});
25732
+ const arr = cur._cellColors;
25733
+ return {
25734
+ kind: "customer",
25735
+ id: key,
25736
+ boxId: e2.boxId,
25737
+ index: e2.index,
25738
+ color: Array.isArray(arr) && arr[e2.index] || null
25739
+ };
25740
+ }
25741
+ // ============================================================
25507
25742
  // 四、边样式
25508
25743
  // ============================================================
25509
25744
  /** 设置边样式 */
@@ -27899,6 +28134,9 @@ function buildGraphData(model, layout) {
27899
28134
  size: [w, h],
27900
28135
  // 自定义字段(进 attributes,供 svg-symbol 节点绘制使用)
27901
28136
  cat: n.cat,
28137
+ // labelText:节点标签文字(canvasNode 用固定每行 N 字换行绘制)。
28138
+ // 禁用 G6 内置 label(label:false),避免其单行画出长名不换行。
28139
+ label: false,
27902
28140
  labelText: n.cat === "bus" || n.cat === "cableTerminal" || n.cat === "consumer" ? "" : n.shortName || "",
27903
28141
  rotate: layout.stationRotate && layout.stationRotate.get(n.id) || 0,
27904
28142
  highlight: 0,
@@ -28035,21 +28273,56 @@ function setNodePalette(themeName) {
28035
28273
  if (key in C2) C2[key] = t[key];
28036
28274
  }
28037
28275
  }
28276
+ function nodeColor(attributes) {
28277
+ return attributes && attributes.color || null;
28278
+ }
28279
+ function cellColorAt(attributes, i) {
28280
+ const arr = attributes && attributes._cellColors;
28281
+ return Array.isArray(arr) ? arr[i] || null : null;
28282
+ }
28283
+ function localPalette(attributes) {
28284
+ const c = nodeColor(attributes);
28285
+ if (!c) return C2;
28286
+ return {
28287
+ ...C2,
28288
+ red: c,
28289
+ orange: c,
28290
+ cyan: c,
28291
+ gold: c,
28292
+ trunkLine: c,
28293
+ dotFill: withAlpha(c, 0.18)
28294
+ };
28295
+ }
28296
+ function withAlpha(color2, a2) {
28297
+ if (typeof color2 !== "string") return color2;
28298
+ const m = color2.match(/^#([0-9a-f]{6}|[0-9a-f]{3})$/i);
28299
+ if (!m) return color2;
28300
+ let hex2 = m[1];
28301
+ if (hex2.length === 3) hex2 = hex2.split("").map((h) => h + h).join("");
28302
+ const n = parseInt(hex2, 16);
28303
+ const r = n >> 16 & 255, g = n >> 8 & 255, b = n & 255;
28304
+ return `rgba(${r},${g},${b},${a2})`;
28305
+ }
28306
+ const LW = 1.8;
28307
+ const LW_HI = 2.2;
28038
28308
  function drawHighlight(attributes, group) {
28039
28309
  if (!attributes.highlight) return;
28040
28310
  const [w, h] = attributes.size;
28311
+ const color2 = attributes.highlightColor || C2.gold;
28312
+ const lw = attributes.highlightWidth || 2;
28313
+ const dash = attributes.highlightDash || [5, 4];
28041
28314
  group.appendChild(
28042
28315
  new Rect({
28043
28316
  style: {
28044
- x: -w / 2 + 1,
28045
- y: -h / 2 + 1,
28046
- width: w - 2,
28047
- height: h - 2,
28048
- radius: 4,
28317
+ x: -w / 2 - 3,
28318
+ y: -h / 2 - 3,
28319
+ width: w + 6,
28320
+ height: h + 6,
28321
+ radius: 6,
28049
28322
  fill: "none",
28050
- stroke: C2.gold,
28051
- lineWidth: 1.6,
28052
- lineDash: [4, 3]
28323
+ stroke: color2,
28324
+ lineWidth: lw,
28325
+ lineDash: dash
28053
28326
  }
28054
28327
  })
28055
28328
  );
@@ -28061,15 +28334,16 @@ function drawAnnotation(attributes, group) {
28061
28334
  const pos = attributes.annotationPos || "top";
28062
28335
  const offset = attributes.annotationOffset || 4;
28063
28336
  const y = pos === "top" ? -h / 2 - offset - 6 : h / 2 + offset + 6;
28064
- _drawWrappedText(group, label, 0, y, attributes.annotationSize || 12, attributes.annotationColor || C2.white, w + 10, 1.25);
28337
+ _drawWrappedText(group, label, 0, y, attributes.annotationSize || 12, attributes.annotationColor || C2.white, w + 10, 1.25, 600);
28065
28338
  }
28066
28339
  function drawLabelUnder(attributes, group) {
28067
28340
  const label = attributes.labelText || "";
28068
28341
  if (!label) return;
28069
28342
  const [w, h] = attributes.size;
28070
- _drawWrappedText(group, label, 0, h / 2 + 8, 11, C2.white, w + 4, 1.3);
28343
+ const x0 = w / 2 + 6;
28344
+ _drawWrappedTextFixed(group, label, x0, -h / 2, 8, C2.white, 4, 1.3, 500, "left");
28071
28345
  }
28072
- function text(attributes, group, str2, x, y, size, fill) {
28346
+ function text(attributes, group, str2, x, y, size, fill, weight = 400) {
28073
28347
  if (!str2) return;
28074
28348
  group.appendChild(
28075
28349
  new Text({
@@ -28079,6 +28353,7 @@ function text(attributes, group, str2, x, y, size, fill) {
28079
28353
  y,
28080
28354
  fontSize: size,
28081
28355
  fill,
28356
+ fontWeight: weight,
28082
28357
  fontFamily: FONT,
28083
28358
  textAlign: "center",
28084
28359
  textBaseline: "middle"
@@ -28109,7 +28384,44 @@ function _wrapLines(str2, fontSize2, maxWidth) {
28109
28384
  if (line) lines.push(line);
28110
28385
  return lines;
28111
28386
  }
28112
- function _drawWrappedText(group, str2, x, y, fontSize2, fill, maxWidth, lineGap = 1.3) {
28387
+ function _wrapLinesFixed(str2, chars2) {
28388
+ if (!str2) return [];
28389
+ const lines = [];
28390
+ let line = "";
28391
+ for (const ch of str2) {
28392
+ line += ch;
28393
+ if (line.length >= chars2) {
28394
+ lines.push(line);
28395
+ line = "";
28396
+ }
28397
+ }
28398
+ if (line) lines.push(line);
28399
+ return lines;
28400
+ }
28401
+ function _drawWrappedTextFixed(group, str2, x, y, fontSize2, fill, chars2, lineGap = 1.3, weight = 400, align = "center") {
28402
+ if (!str2) return;
28403
+ const lines = _wrapLinesFixed(str2, chars2);
28404
+ if (!lines.length) return;
28405
+ const lineHeight2 = fontSize2 * lineGap;
28406
+ for (let i = 0; i < lines.length; i++) {
28407
+ group.appendChild(
28408
+ new Text({
28409
+ style: {
28410
+ text: lines[i],
28411
+ x,
28412
+ y: y + lineHeight2 * i,
28413
+ fontSize: fontSize2,
28414
+ fill,
28415
+ fontWeight: weight,
28416
+ fontFamily: FONT,
28417
+ textAlign: align === "left" ? "left" : "center",
28418
+ textBaseline: "middle"
28419
+ }
28420
+ })
28421
+ );
28422
+ }
28423
+ }
28424
+ function _drawWrappedText(group, str2, x, y, fontSize2, fill, maxWidth, lineGap = 1.3, weight = 400) {
28113
28425
  if (!str2) return;
28114
28426
  const lines = _wrapLines(str2, fontSize2, maxWidth);
28115
28427
  if (!lines.length) return;
@@ -28124,6 +28436,7 @@ function _drawWrappedText(group, str2, x, y, fontSize2, fill, maxWidth, lineGap
28124
28436
  y: y - totalH / 2 + lineHeight2 * (i + 0.5),
28125
28437
  fontSize: fontSize2,
28126
28438
  fill,
28439
+ fontWeight: weight,
28127
28440
  fontFamily: FONT,
28128
28441
  textAlign: "center",
28129
28442
  textBaseline: "middle"
@@ -28133,77 +28446,122 @@ function _drawWrappedText(group, str2, x, y, fontSize2, fill, maxWidth, lineGap
28133
28446
  }
28134
28447
  }
28135
28448
  function drawTransformer(attributes, group) {
28136
- const lw = 1.4;
28137
- group.appendChild(new Line({ style: { x1: -24, y1: 0, x2: -15, y2: 0, stroke: C2.trunkLine, lineWidth: lw } }));
28138
- group.appendChild(new Circle({ style: { cx: -5, cy: 0, r: 10, fill: C2.orange, stroke: C2.trunkLine, lineWidth: 1.2 } }));
28139
- group.appendChild(new Circle({ style: { cx: 10, cy: 0, r: 10, fill: "none", stroke: C2.red, lineWidth: 1.2 } }));
28140
- group.appendChild(new Line({ style: { x1: 20, y1: 0, x2: 29, y2: 0, stroke: C2.red, lineWidth: lw } }));
28449
+ const P = localPalette(attributes);
28450
+ const lw = LW;
28451
+ group.appendChild(new Line({ style: { x1: -26, y1: 0, x2: -14, y2: 0, stroke: P.trunkLine, lineWidth: lw, lineCap: "round" } }));
28452
+ group.appendChild(new Circle({ style: { cx: -4.5, cy: 0, r: 10, fill: P.orange, stroke: P.trunkLine, lineWidth: 1.4 } }));
28453
+ group.appendChild(new Circle({ style: { cx: -7.2, cy: -3.2, r: 3.2, fill: withAlpha(C2.white, 0.25), stroke: "none" } }));
28454
+ group.appendChild(new Circle({ style: { cx: 10, cy: 0, r: 10, fill: withAlpha(P.red, 0.06), stroke: P.red, lineWidth: LW_HI } }));
28455
+ group.appendChild(new Line({ style: { x1: 20, y1: 0, x2: 30, y2: 0, stroke: P.red, lineWidth: lw, lineCap: "round" } }));
28141
28456
  }
28142
28457
  function drawFuse(attributes, group) {
28143
- const lw = 1.4;
28144
- group.appendChild(new Line({ style: { x1: -26, y1: 0, x2: -19, y2: 0, stroke: C2.red, lineWidth: lw } }));
28145
- group.appendChild(new Rect({ style: { x: -19, y: -7, width: 38, height: 14, radius: 1, fill: "none", stroke: C2.red, lineWidth: lw } }));
28146
- group.appendChild(new Circle({ style: { cx: -13, cy: 0, r: 3.6, fill: C2.red } }));
28147
- group.appendChild(new Rect({ style: { x: -8, y: -2.4, width: 5, height: 4.8, fill: C2.red } }));
28148
- group.appendChild(new Rect({ style: { x: -1, y: -5.4, width: 13, height: 10.8, fill: C2.red } }));
28149
- group.appendChild(new Line({ style: { x1: 19, y1: 0, x2: 26, y2: 0, stroke: C2.red, lineWidth: lw } }));
28458
+ const P = localPalette(attributes);
28459
+ const lw = LW;
28460
+ group.appendChild(new Line({ style: { x1: -28, y1: 0, x2: -19, y2: 0, stroke: P.red, lineWidth: lw, lineCap: "round" } }));
28461
+ group.appendChild(new Line({ style: { x1: 19, y1: 0, x2: 28, y2: 0, stroke: P.red, lineWidth: lw, lineCap: "round" } }));
28462
+ group.appendChild(new Rect({ style: { x: -19, y: -7.5, width: 38, height: 15, radius: 2, fill: withAlpha(P.red, 0.05), stroke: P.red, lineWidth: lw } }));
28463
+ group.appendChild(new Circle({ style: { cx: -13, cy: 0, r: 3.4, fill: P.red, stroke: "none" } }));
28464
+ group.appendChild(new Rect({ style: { x: -8.2, y: -2.4, width: 5, height: 4.8, radius: 0.6, fill: P.red } }));
28465
+ group.appendChild(new Rect({ style: { x: -1.4, y: -5.4, width: 13, height: 10.8, radius: 0.8, fill: P.red } }));
28466
+ group.appendChild(new Rect({ style: { x: 5, y: -1.2, width: 8, height: 2.4, fill: C2.white, opacity: 0.55 } }));
28150
28467
  }
28151
28468
  function drawBreaker(attributes, group) {
28152
- const lw = 1.4;
28153
- group.appendChild(new Line({ style: { x1: -28, y1: 0, x2: -21, y2: 0, stroke: C2.red, lineWidth: lw } }));
28154
- group.appendChild(new Rect({ style: { x: -21, y: -9, width: 42, height: 18, radius: 2, fill: "none", stroke: C2.red, lineWidth: lw } }));
28155
- group.appendChild(new Rect({ style: { x: -21, y: -9, width: 42, height: 7, fill: "rgba(255,0,0,0.25)" } }));
28156
- _drawWrappedText(group, attributes.labelText, 0, 0.5, 10, C2.white, 36, 1.2);
28157
- group.appendChild(new Line({ style: { x1: 21, y1: 0, x2: 28, y2: 0, stroke: C2.red, lineWidth: lw } }));
28469
+ const P = localPalette(attributes);
28470
+ const lw = LW;
28471
+ const [nw] = attributes.size;
28472
+ const bw = 42;
28473
+ const bh = 20, r = 4;
28474
+ const bx = -bw / 2, by = -bh / 2;
28475
+ const xEdge = (nw || 60) / 2;
28476
+ const xBox = bw / 2;
28477
+ if (xEdge > xBox) {
28478
+ group.appendChild(new Line({ style: { x1: -xEdge, y1: 0, x2: -xBox, y2: 0, stroke: P.red, lineWidth: lw, lineCap: "round" } }));
28479
+ group.appendChild(new Line({ style: { x1: xBox, y1: 0, x2: xEdge, y2: 0, stroke: P.red, lineWidth: lw, lineCap: "round" } }));
28480
+ }
28481
+ group.appendChild(new Rect({ style: { x: bx, y: by, width: bw, height: bh, radius: r, fill: P.red, stroke: withAlpha(C2.white, 0.22), lineWidth: 1 } }));
28482
+ if (attributes.labelText) {
28483
+ _drawWrappedText(group, attributes.labelText, 0, 0.6, 11.5, "#FFFFFF", bw - 8, 1.15, 700);
28484
+ }
28158
28485
  }
28159
28486
  function drawCableTerminal(attributes, group) {
28160
- group.appendChild(new Polygon({ style: { points: [[1, -8], [-7, 8], [9, 8]], fill: C2.red, stroke: C2.red, lineWidth: 1 } }));
28487
+ const P = localPalette(attributes);
28488
+ const lw = 1.6;
28489
+ group.appendChild(new Line({ style: { x1: -11, y1: 0, x2: -5.5, y2: 0, stroke: P.red, lineWidth: lw, lineCap: "round" } }));
28490
+ group.appendChild(new Polygon({
28491
+ style: {
28492
+ points: [[-5, -9.5], [11, 0], [-5, 9.5]],
28493
+ fill: P.red,
28494
+ stroke: withAlpha(C2.white, 0.3),
28495
+ lineWidth: 1
28496
+ }
28497
+ }));
28161
28498
  }
28162
28499
  function drawStationTerminal(attributes, group) {
28163
- group.appendChild(new Line({ style: { x1: -9, y1: 0, x2: -1, y2: 0, stroke: C2.red, lineWidth: 1.4 } }));
28164
- group.appendChild(new Polygon({ style: { points: [[0, -5], [0, 5], [9, 0]], fill: "none", stroke: C2.red, lineWidth: 1.4 } }));
28500
+ const P = localPalette(attributes);
28501
+ const lw = LW;
28502
+ group.appendChild(new Line({ style: { x1: -12, y1: 0, x2: -4, y2: 0, stroke: P.red, lineWidth: lw, lineCap: "round" } }));
28503
+ group.appendChild(new Polygon({
28504
+ style: {
28505
+ points: [[0, -6], [0, 6], [10, 0]],
28506
+ fill: withAlpha(P.red, 0.08),
28507
+ stroke: P.red,
28508
+ lineWidth: lw
28509
+ }
28510
+ }));
28511
+ }
28512
+ function drawMeterFace(group, cx, cy, meterW, meterH, lineColor, markCell, cellIdx) {
28513
+ const r = Math.max(2, meterW * 0.16);
28514
+ const body = new Rect({ style: { x: cx - meterW / 2, y: cy - meterH / 2, width: meterW, height: meterH, radius: r, fill: withAlpha(lineColor, 0.06), stroke: lineColor, lineWidth: 1.2, cursor: "pointer" } });
28515
+ if (markCell) markCell(body, cellIdx);
28516
+ group.appendChild(body);
28517
+ const winW = meterW * 0.66, winH = Math.max(2.4, meterH * 0.17);
28518
+ const win = new Rect({ style: { x: cx - winW / 2, y: cy - meterH * 0.31, width: winW, height: winH, radius: winH / 2, fill: lineColor, opacity: 0.5, stroke: "none" } });
28519
+ if (markCell) markCell(win, cellIdx);
28520
+ group.appendChild(win);
28521
+ const dot2 = new Circle({ style: { cx, cy: cy + meterH * 0.12, r: 1.6, fill: lineColor, opacity: 0.85, stroke: "none" } });
28522
+ if (markCell) markCell(dot2, cellIdx);
28523
+ group.appendChild(dot2);
28165
28524
  }
28166
28525
  function drawMeterBox(attributes, group) {
28167
28526
  const [w, h] = attributes.size;
28527
+ const boxColor = nodeColor(attributes);
28168
28528
  if (attributes._meterBoxMode === "single") {
28169
28529
  const nodeId = attributes._nodeId || "";
28170
28530
  const sel = attributes._custSel === 0;
28171
28531
  const hover = attributes._custHover === 0;
28172
- const lineColor = sel ? C2.gold : hover ? C2.cyan : C2.white;
28532
+ const baseColor = cellColorAt(attributes, 0) || boxColor || C2.white;
28533
+ const lineColor = sel ? C2.gold : hover ? C2.cyan : baseColor;
28173
28534
  const markCell = (shape) => {
28174
28535
  if (!nodeId) return;
28175
28536
  shape.__custBox = nodeId;
28176
28537
  shape.__custIndex = 0;
28177
28538
  };
28539
+ const meterW = 20;
28540
+ const meterH = 26;
28541
+ drawMeterFace(group, 0, -1, meterW, meterH, lineColor, markCell, 0);
28178
28542
  if (sel || hover) {
28179
- const ring = new Circle({
28543
+ const ring = new Rect({
28180
28544
  style: {
28181
- cx: 0,
28182
- cy: -3,
28183
- r: 13,
28184
- fill: sel ? C2.gold : C2.cyan,
28185
- fillOpacity: sel ? 0.3 : 0.18,
28545
+ x: -meterW / 2 - 4,
28546
+ y: -meterH / 2 - 4,
28547
+ width: meterW + 8,
28548
+ height: meterH + 8,
28549
+ radius: 5,
28550
+ fill: "none",
28186
28551
  stroke: sel ? C2.gold : C2.cyan,
28187
- lineWidth: sel ? 1.6 : 1.1
28552
+ lineWidth: sel ? 2 : 1.3
28188
28553
  }
28189
28554
  });
28190
28555
  markCell(ring);
28191
28556
  group.appendChild(ring);
28192
28557
  }
28193
- const meterCircle = new Circle({ style: { cx: 0, cy: -3, r: 11, fill: "none", stroke: lineColor, lineWidth: sel ? 1.8 : 1.6, cursor: "pointer" } });
28194
- markCell(meterCircle);
28195
- group.appendChild(meterCircle);
28196
- const stub = new Line({ style: { x1: 0, y1: -14, x2: 0, y2: -7, stroke: lineColor, lineWidth: 1.6, cursor: "pointer" } });
28197
- markCell(stub);
28198
- group.appendChild(stub);
28199
28558
  const name = attributes._customers && attributes._customers[0] ? attributes._customers[0].realConsName || attributes._customers[0].consName || "" : "";
28200
28559
  if (name) {
28201
- const nameText = new Text({ style: { text: name, x: 0, y: 12, fontSize: 8, fill: sel ? C2.gold : hover ? C2.cyan : C2.white, fontFamily: FONT, textAlign: "center", textBaseline: "middle", cursor: "pointer" } });
28202
- markCell(nameText);
28203
- group.appendChild(nameText);
28560
+ _drawWrappedTextFixed(group, name, 0, meterH / 2 + 7, 7, lineColor, 4, 1.15, 500);
28204
28561
  }
28205
28562
  } else if (attributes._meterBoxMode === "grid" && attributes._customers) {
28206
- group.appendChild(new Rect({ style: { x: -w / 2, y: -h / 2, width: w, height: h, fill: C2.meterFill, stroke: C2.meterText, lineWidth: 0.9, radius: 2 } }));
28563
+ const frameColor = boxColor || C2.meterText;
28564
+ group.appendChild(new Rect({ style: { x: -w / 2, y: -h / 2, width: w, height: h, fill: withAlpha(frameColor, 0.04), stroke: frameColor, lineWidth: 1, radius: 4 } }));
28207
28565
  const cols = attributes._meterBoxCols || 5;
28208
28566
  const customers = attributes._customers;
28209
28567
  const showName = attributes._meterBoxShowName;
@@ -28214,11 +28572,12 @@ function drawMeterBox(attributes, group) {
28214
28572
  const gridH = rows * cellH + (rows - 1) * METER_GAP;
28215
28573
  const startX = -gridW / 2;
28216
28574
  const startY = -gridH / 2;
28217
- const meterR = 9;
28575
+ const meterW = Math.min(13, cellW * 0.5);
28576
+ const meterH = meterW * 1.5;
28577
+ const hitR = Math.max(9, meterH * 0.4);
28218
28578
  const custSel = attributes._custSel == null ? -1 : attributes._custSel;
28219
28579
  const custHover = attributes._custHover == null ? -1 : attributes._custHover;
28220
28580
  const nodeId = attributes._nodeId || "";
28221
- const hitR = meterR + 2.5;
28222
28581
  const markCell = (shape, i) => {
28223
28582
  if (!nodeId) return;
28224
28583
  shape.__custBox = nodeId;
@@ -28231,7 +28590,8 @@ function drawMeterBox(attributes, group) {
28231
28590
  const cy = startY + row2 * (cellH + METER_GAP) + cellW / 2;
28232
28591
  const sel = i === custSel;
28233
28592
  const hover = i === custHover;
28234
- const lineColor = sel ? C2.gold : hover ? C2.cyan : C2.meterText;
28593
+ const baseColor = cellColorAt(attributes, i) || C2.meterText;
28594
+ const lineColor = sel ? C2.gold : hover ? C2.cyan : baseColor;
28235
28595
  if (sel || hover) {
28236
28596
  group.appendChild(new Circle({
28237
28597
  style: {
@@ -28241,49 +28601,69 @@ function drawMeterBox(attributes, group) {
28241
28601
  fill: sel ? C2.gold : C2.cyan,
28242
28602
  fillOpacity: sel ? 0.3 : 0.18,
28243
28603
  stroke: sel ? C2.gold : C2.cyan,
28244
- lineWidth: sel ? 1.6 : 1.1
28604
+ lineWidth: sel ? 1.7 : 1.1
28245
28605
  }
28246
28606
  }));
28247
28607
  }
28248
- group.appendChild(new Circle({ style: { cx, cy, r: meterR, fill: "none", stroke: lineColor, lineWidth: sel ? 1.2 : 0.8 } }));
28249
- group.appendChild(new Line({ style: { x1: cx, y1: cy - meterR, x2: cx, y2: cy - meterR + 4, stroke: lineColor, lineWidth: 0.8 } }));
28608
+ drawMeterFace(group, cx, cy, meterW, meterH, lineColor, markCell, i);
28250
28609
  const hit = new Circle({ style: { cx, cy, r: hitR, fill: "rgba(255,255,255,0.01)", stroke: "none", cursor: "pointer" } });
28251
28610
  markCell(hit, i);
28252
28611
  group.appendChild(hit);
28253
28612
  if (showName) {
28254
28613
  const name = customers[i].realConsName || customers[i].consName || "";
28255
28614
  if (name) {
28256
- const nameText = new Text({ style: { text: name, x: cx, y: cy + meterR + 6, fontSize: 7, fill: sel ? C2.gold : hover ? C2.cyan : C2.meterText, fontFamily: FONT, textAlign: "center", textBaseline: "middle", cursor: "pointer" } });
28257
- markCell(nameText, i);
28258
- group.appendChild(nameText);
28615
+ _drawWrappedTextFixed(group, name, cx, cy + meterH / 2 + 6.5, 6.5, lineColor, 4, 1.15, 500);
28616
+ const hitName = new Circle({
28617
+ style: {
28618
+ cx,
28619
+ cy: cy + meterH / 2 + 6.5 + 4,
28620
+ r: Math.max(8, Math.min(14, Math.ceil(name.length / 4) * 4 + 4)),
28621
+ fill: "rgba(255,255,255,0.01)",
28622
+ stroke: "none",
28623
+ cursor: "pointer"
28624
+ }
28625
+ });
28626
+ markCell(hitName, i);
28627
+ group.appendChild(hitName);
28259
28628
  }
28260
28629
  }
28261
28630
  }
28262
28631
  } else {
28263
- group.appendChild(new Rect({ style: { x: -9.5, y: -9.5, width: 19, height: 19, fill: C2.meterFill, stroke: C2.meterText, lineWidth: 0.9 } }));
28264
- text(attributes, group, "JX", 0, 0.5, 7.5, C2.meterText);
28632
+ const s2 = Math.min(w, h) || 21;
28633
+ const ink = boxColor || C2.meterText;
28634
+ group.appendChild(new Rect({ style: { x: -s2 / 2, y: -s2 / 2, width: s2, height: s2, radius: Math.max(2, s2 * 0.16), fill: C2.meterFill, stroke: ink, lineWidth: 1.4 } }));
28635
+ group.appendChild(new Rect({ style: { x: -s2 / 2 + 2, y: -s2 / 2 + 2, width: s2 - 4, height: 2, radius: 1, fill: withAlpha(ink, 0.35), stroke: "none" } }));
28636
+ text(attributes, group, "JX", 0, s2 * 0.06, Math.max(8, s2 * 0.4), ink, 700);
28265
28637
  }
28266
28638
  }
28267
28639
  function drawConsumer(attributes, group) {
28268
- group.appendChild(new Circle({ style: { cx: 0, cy: 0, r: 12.5, fill: "none", stroke: C2.white, lineWidth: 1.8 } }));
28269
- text(attributes, group, "J", 0, 0.5, 13, C2.white);
28640
+ const ink = nodeColor(attributes) || C2.white;
28641
+ group.appendChild(new Circle({ style: { cx: 0, cy: 0, r: 13, fill: "none", stroke: ink, lineWidth: LW_HI } }));
28642
+ text(attributes, group, "J", 0, 0.8, 14.5, ink, 700);
28270
28643
  }
28271
28644
  function drawBus(attributes, group) {
28272
28645
  const [w] = attributes.size;
28273
- group.appendChild(new Rect({ style: { x: -w / 2, y: -3, width: w, height: 6, radius: 2, fill: C2.busFill, stroke: C2.white, lineWidth: 1.4 } }));
28646
+ const ink = nodeColor(attributes) || C2.white;
28647
+ const hh = Math.max(4.5, Math.min(6, w / 10));
28648
+ group.appendChild(new Rect({ style: { x: -w / 2, y: -hh / 2, width: w, height: hh, radius: hh / 2, fill: withAlpha(ink, 0.5), stroke: ink, lineWidth: 1.2 } }));
28649
+ group.appendChild(new Rect({ style: { x: -w / 2 + 1, y: -0.7, width: w - 2, height: 1.4, fill: withAlpha(C2.canvasBg || "#000", 0.18), stroke: "none" } }));
28274
28650
  }
28275
28651
  function drawLineDot(attributes, group) {
28276
- group.appendChild(new Circle({ style: { cx: 0, cy: 0, r: 4, fill: C2.dotFill, stroke: C2.cyan, lineWidth: 1.4 } }));
28652
+ const P = localPalette(attributes);
28653
+ group.appendChild(new Circle({ style: { cx: 0, cy: 0, r: 4.6, fill: P.dotFill, stroke: P.cyan, lineWidth: 1.6 } }));
28654
+ group.appendChild(new Circle({ style: { cx: 0, cy: 0, r: 1.5, fill: P.cyan, stroke: "none" } }));
28277
28655
  }
28278
28656
  function drawJunction(attributes, group) {
28279
- const lw = 1.4;
28280
- group.appendChild(new Line({ style: { x1: -5, y1: 0, x2: 5, y2: 0, stroke: C2.red, lineWidth: lw } }));
28281
- group.appendChild(new Line({ style: { x1: 0, y1: -5, x2: 0, y2: 5, stroke: C2.red, lineWidth: lw } }));
28282
- group.appendChild(new Line({ style: { x1: -3.5, y1: -3.5, x2: 3.5, y2: 3.5, stroke: C2.red, lineWidth: lw } }));
28283
- group.appendChild(new Line({ style: { x1: 3.5, y1: -3.5, x2: -3.5, y2: 3.5, stroke: C2.red, lineWidth: lw } }));
28657
+ const P = localPalette(attributes);
28658
+ const lw = LW;
28659
+ group.appendChild(new Line({ style: { x1: -5.5, y1: 0, x2: 5.5, y2: 0, stroke: P.red, lineWidth: lw, lineCap: "round" } }));
28660
+ group.appendChild(new Line({ style: { x1: 0, y1: -5.5, x2: 0, y2: 5.5, stroke: P.red, lineWidth: lw, lineCap: "round" } }));
28661
+ group.appendChild(new Line({ style: { x1: -3.9, y1: -3.9, x2: 3.9, y2: 3.9, stroke: P.red, lineWidth: lw, lineCap: "round" } }));
28662
+ group.appendChild(new Line({ style: { x1: 3.9, y1: -3.9, x2: -3.9, y2: 3.9, stroke: P.red, lineWidth: lw, lineCap: "round" } }));
28284
28663
  }
28285
28664
  function drawUnknown(attributes, group) {
28286
- group.appendChild(new Circle({ style: { cx: 0, cy: 0, r: 8, fill: C2.unknownFill, stroke: C2.unknownStroke, lineWidth: 1.2 } }));
28665
+ group.appendChild(new Circle({ style: { cx: 0, cy: 0, r: 9, fill: C2.unknownFill, stroke: C2.unknownStroke, lineWidth: 1.4 } }));
28666
+ text(attributes, group, "?", 0, 0.8, 13, C2.unknownStroke, 700);
28287
28667
  }
28288
28668
  function drawStation(attributes, group) {
28289
28669
  const [w, h] = attributes.size;
@@ -28294,16 +28674,30 @@ function drawStation(attributes, group) {
28294
28674
  y: -h / 2,
28295
28675
  width: w,
28296
28676
  height: h,
28297
- radius: 2,
28677
+ radius: 5,
28298
28678
  fill: C2.stationFill,
28299
28679
  stroke: C2.white,
28300
- lineWidth: 1.2,
28680
+ lineWidth: 1.5,
28681
+ opacity: 0.95
28682
+ }
28683
+ })
28684
+ );
28685
+ group.appendChild(
28686
+ new Rect({
28687
+ style: {
28688
+ x: -w / 2 + 2,
28689
+ y: -h / 2 + 2,
28690
+ width: w - 4,
28691
+ height: 2.4,
28692
+ radius: 1.2,
28693
+ fill: withAlpha(C2.white, 0.28),
28694
+ stroke: "none",
28301
28695
  opacity: 0.9
28302
28696
  }
28303
28697
  })
28304
28698
  );
28305
28699
  if (attributes._stationTitle) {
28306
- _drawWrappedText(group, attributes._stationTitle, 0, -h / 2 + 13, 11, C2.stationText, w - 12, 1.35);
28700
+ _drawWrappedText(group, attributes._stationTitle, 0, -h / 2 + 15, 12, C2.stationText, w - 12, 1.35, 600);
28307
28701
  }
28308
28702
  }
28309
28703
  const DRAWERS = {
@@ -28320,7 +28714,6 @@ const DRAWERS = {
28320
28714
  station: drawStation,
28321
28715
  other: drawUnknown
28322
28716
  };
28323
- const LABEL_UNDER = /* @__PURE__ */ new Set(["transformer", "fuse"]);
28324
28717
  const _pulseState = /* @__PURE__ */ new Map();
28325
28718
  class SvgSymbolNode extends BaseNode {
28326
28719
  drawKeyShape(attributes, container) {
@@ -28354,7 +28747,9 @@ class SvgSymbolNode extends BaseNode {
28354
28747
  draw(attributes, target);
28355
28748
  drawHighlight(attributes, target);
28356
28749
  drawAnnotation(attributes, target);
28357
- if (LABEL_UNDER.has(attributes.cat)) drawLabelUnder(attributes, target);
28750
+ if (attributes.labelText && !attributes._stationTitle && attributes.cat !== "bus" && attributes.cat !== "cableTerminal" && attributes.cat !== "consumer" && attributes.cat !== "switch" && attributes.cat !== "meterBox") {
28751
+ drawLabelUnder(attributes, target);
28752
+ }
28358
28753
  const pulse = _pulseState.get(attributes._nodeId || "");
28359
28754
  if (pulse) {
28360
28755
  const items = rot ? [target] : key.children || [];
@@ -29267,7 +29662,7 @@ const _sfc_main = {
29267
29662
  }
29268
29663
  };
29269
29664
  const DistanceTop10 = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-438b6017"]]);
29270
- const VERSION = "0.1.7";
29665
+ const VERSION = "0.1.8";
29271
29666
  const DESCRIPTION = "配电台区单线图拓扑成图引擎";
29272
29667
  export {
29273
29668
  DESCRIPTION,