topo-engine 0.1.9 → 0.1.11

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.
@@ -6660,11 +6660,11 @@ var parseGradient$1 = /* @__PURE__ */ function() {
6660
6660
  }
6661
6661
  }
6662
6662
  function matchPositioning() {
6663
- var location = matchCoordinates();
6664
- if (location.x || location.y) {
6663
+ var location2 = matchCoordinates();
6664
+ if (location2.x || location2.y) {
6665
6665
  return {
6666
6666
  type: "position",
6667
- value: location
6667
+ value: location2
6668
6668
  };
6669
6669
  }
6670
6670
  }
@@ -26313,8 +26313,10 @@ class TopoApi {
26313
26313
  }
26314
26314
  // ------------------------------------------------------------
26315
26315
  // 光晕 / 脉动 动画 —— 节点 & 箱内电表户
26316
- // setNodeGlow(ref, { color, spread/radius, duration, opacity })
26316
+ // setNodeGlow(ref, { color, spread/radius, duration, opacity, width, intensity, blink })
26317
26317
  // ref = 节点 id/psrId → 图元光环;assetNo → 该户表位光环
26318
+ // 强度栈:blur 柔光底衬 + 实心光块 + 外扩散环×2 + 霓虹主环(shadowBlur) + 亮芯环
26319
+ // intensity = 强度倍率(默认 1);blink = true/0~1 → 图元「整体闪烁」
26318
26320
  // setNodePulse(ref, opts) → 节点透明度呼吸;assetNo → 该户电表格淡入淡出
26319
26321
  // ------------------------------------------------------------
26320
26322
  /** 是否有需要逐帧推进的动画(节点光晕 / 户光晕 / 户脉动) */
@@ -26412,6 +26414,9 @@ class TopoApi {
26412
26414
  _glowSpread: void 0,
26413
26415
  _glowOpacity: void 0,
26414
26416
  _glowDuration: void 0,
26417
+ _glowWidth: void 0,
26418
+ _glowLevel: void 0,
26419
+ _glowBlink: void 0,
26415
26420
  _glowT: void 0
26416
26421
  }
26417
26422
  }]);
@@ -26419,9 +26424,32 @@ class TopoApi {
26419
26424
  }
26420
26425
  }
26421
26426
  /**
26422
- * 节点 / 箱内电表户光晕(发光环)动画。
26427
+ * 归一化「整体闪烁」参数:false / 未给 → 0(只画光环);true → 0.85;数字 → clamp(0,1)。
26428
+ * 数值含义 = 图元最暗时被压掉的透明度幅度(1 → 图元完全隐去,0.5 → 最暗降到 50%)。
26429
+ */
26430
+ _normBlink(v) {
26431
+ if (v === true) return 0.85;
26432
+ if (v === false || v == null) return 0;
26433
+ const n = Number(v);
26434
+ return Number.isFinite(n) ? Math.max(0, Math.min(1, n)) : 0;
26435
+ }
26436
+ /** 归一化强度倍率(intensity/level/strength):默认 1,范围 0.2~3 */
26437
+ _normLevel(v) {
26438
+ const n = Number(v);
26439
+ return Number.isFinite(n) && n > 0 ? Math.max(0.2, Math.min(3, n)) : 1;
26440
+ }
26441
+ /**
26442
+ * 节点 / 箱内电表户光晕(发光环 + 霓虹辉光)动画。
26423
26443
  * @param {string} ref 节点 id / psrId(图元光环)或资产编号 assetNo(该户表位光环)
26424
- * @param {object} [opts] - { color, spread/radius, duration, opacity }
26444
+ * @param {object} [opts] - { color, spread/radius, duration, opacity, width, intensity/level/strength, blink/flash }
26445
+ * 渲染栈:blur 柔光底衬 + 贴合图元的实心光块(面光)→ 图元 → 外扩散环 ×2 →
26446
+ * 霓虹主环(粗线 + canvas shadowBlur 辉光)→ 偏白亮芯环(刺眼高光)。
26447
+ * intensity:强度倍率,默认 1(线宽/辉光/底光同时放大);想“更夸张”给 1.5~2.5,想收敛给 0.5~0.7。
26448
+ * spread:光环外扩半径,默认按图元大小自适应(短边 ×0.7,16~46px)。
26449
+ * blink:图元「整体闪烁」开关/强度(默认 false 只画光环;true = 0.85;数字 0~1)。
26450
+ * 开启后图元本体透明度随相位「亮 ↔ 暗」交替(与光环相位互补,始终有强视觉元素),
26451
+ * 图元变暗的瞬间底衬光块最亮 —— 整块明暗交替比细线醒目得多。
26452
+ * width:主环初始线宽 (px),默认 5.6(节点)/ 3.6(户表位),随相位收细。
26425
26453
  * @returns {boolean} 是否命中并启动
26426
26454
  */
26427
26455
  setNodeGlow(ref, opts = {}) {
@@ -26431,11 +26459,17 @@ class TopoApi {
26431
26459
  console.warn(`[TopoApi] setNodeGlow: 无法解析目标 ${ref}(节点用 id/psrId,电表户用 assetNo)`);
26432
26460
  return false;
26433
26461
  }
26462
+ const width = parseFloat(opts.width);
26463
+ const spread = opts.spread != null ? opts.spread : opts.radius;
26434
26464
  const cfg = {
26435
26465
  color: opts.color || "#00C8FF",
26436
- spread: opts.spread != null ? opts.spread : opts.radius != null ? opts.radius : 14,
26437
- opacity: opts.opacity != null ? opts.opacity : 0.9,
26466
+ spread: Number.isFinite(parseFloat(spread)) ? parseFloat(spread) : null,
26467
+ // null 按图元大小自适应
26468
+ opacity: opts.opacity != null ? opts.opacity : 0.95,
26438
26469
  duration: opts.duration || 1600,
26470
+ width: Number.isFinite(width) && width > 0 ? width : null,
26471
+ level: this._normLevel(opts.intensity != null ? opts.intensity : opts.level != null ? opts.level : opts.strength),
26472
+ blink: this._normBlink(opts.blink != null ? opts.blink : opts.flash),
26439
26473
  _start: performance.now(),
26440
26474
  t: 0
26441
26475
  };
@@ -26445,9 +26479,12 @@ class TopoApi {
26445
26479
  id: t.id,
26446
26480
  style: {
26447
26481
  _glowColor: cfg.color,
26448
- _glowSpread: cfg.spread,
26482
+ _glowSpread: cfg.spread == null ? void 0 : cfg.spread,
26449
26483
  _glowOpacity: cfg.opacity,
26450
26484
  _glowDuration: cfg.duration,
26485
+ _glowWidth: cfg.width == null ? void 0 : cfg.width,
26486
+ _glowLevel: cfg.level,
26487
+ _glowBlink: cfg.blink || void 0,
26451
26488
  _glowT: 0
26452
26489
  }
26453
26490
  }]);
@@ -26499,6 +26536,9 @@ class TopoApi {
26499
26536
  _glowSpread: void 0,
26500
26537
  _glowOpacity: void 0,
26501
26538
  _glowDuration: void 0,
26539
+ _glowWidth: void 0,
26540
+ _glowLevel: void 0,
26541
+ _glowBlink: void 0,
26502
26542
  _glowT: void 0
26503
26543
  }
26504
26544
  });
@@ -28701,6 +28741,8 @@ const THEMES = {
28701
28741
  cyan: "#00C8FF",
28702
28742
  gold: "#FFD700",
28703
28743
  gray: "#8B8B8B",
28744
+ // 开关/熔丝(Kxx)标签文字:画在红色箱体(red)里的文字色,随主题切换
28745
+ switchText: "#FFFFFF",
28704
28746
  // 计量箱(JX):深色主题黑底白字;浅色主题白底深框深字(随画布配套)
28705
28747
  meterFill: "#000000",
28706
28748
  meterText: "#FFFFFF",
@@ -28729,6 +28771,9 @@ const THEMES = {
28729
28771
  cyan: "#0087B8",
28730
28772
  gold: "#B8860B",
28731
28773
  gray: "#9C9C9C",
28774
+ // 开关标签:浅色主题下箱体仍是深红 red(#C62828),此处保持浅色字
28775
+ // (白字对比度 ≈5.6:1;若改成深字,请连 red 一起调浅,否则只剩 ≈2.3:1 会发糊)
28776
+ switchText: "#FFFFFF",
28732
28777
  meterFill: "#FFFFFF",
28733
28778
  // 浅色主题:白底 + 深框深字(随白画布配套)
28734
28779
  meterText: "#3A3A3A",
@@ -28932,6 +28977,19 @@ function withAlpha(color2, a2) {
28932
28977
  }
28933
28978
  const LW = 1.8;
28934
28979
  const LW_HI = 2.2;
28980
+ function mixWhite(color2, k) {
28981
+ if (typeof color2 !== "string") return color2;
28982
+ const m = color2.match(/^#([0-9a-f]{6}|[0-9a-f]{3})$/i);
28983
+ if (!m) return color2;
28984
+ let hex2 = m[1];
28985
+ if (hex2.length === 3) hex2 = hex2.split("").map((h) => h + h).join("");
28986
+ const n = parseInt(hex2, 16);
28987
+ const mix = (v) => Math.round(v + (255 - v) * Math.max(0, Math.min(1, k)));
28988
+ const r = mix(n >> 16 & 255), g = mix(n >> 8 & 255), b = mix(n & 255);
28989
+ return `#${(r << 16 | g << 8 | b).toString(16).padStart(6, "0")}`;
28990
+ }
28991
+ const clampNum = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
28992
+ const NO_HIT = { pointerEvents: "none" };
28935
28993
  function drawHighlight(attributes, group) {
28936
28994
  if (!attributes.highlight) return;
28937
28995
  const [w, h] = attributes.size;
@@ -29025,7 +29083,7 @@ function _wrapLinesFixed(str2, chars2) {
29025
29083
  if (line) lines.push(line);
29026
29084
  return lines;
29027
29085
  }
29028
- function _drawWrappedTextFixed(group, str2, x, y, fontSize2, fill, chars2, lineGap = 1.3, weight = 400, align = "center") {
29086
+ function _drawWrappedTextFixed(group, str2, x, y, fontSize2, fill, chars2, lineGap = 1.3, weight = 400, align = "center", extra = null) {
29029
29087
  if (!str2) return;
29030
29088
  const lines = _wrapLinesFixed(str2, chars2);
29031
29089
  if (!lines.length) return;
@@ -29034,6 +29092,7 @@ function _drawWrappedTextFixed(group, str2, x, y, fontSize2, fill, chars2, lineG
29034
29092
  group.appendChild(
29035
29093
  new Text({
29036
29094
  style: {
29095
+ ...extra || {},
29037
29096
  text: lines[i],
29038
29097
  x,
29039
29098
  y: y + lineHeight2 * i,
@@ -29107,7 +29166,7 @@ function drawBreaker(attributes, group) {
29107
29166
  }
29108
29167
  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 } }));
29109
29168
  if (attributes.labelText) {
29110
- _drawWrappedText(group, attributes.labelText, 0, 0.6, 11.5, "#FFFFFF", bw - 8, 1.15, 700);
29169
+ _drawWrappedText(group, attributes.labelText, 0, 0.6, 11.5, C2.switchText, bw - 8, 1.15, 700);
29111
29170
  }
29112
29171
  }
29113
29172
  function drawCableTerminal(attributes, group) {
@@ -29154,20 +29213,33 @@ function cellFxAt(attributes, i) {
29154
29213
  return fx && typeof fx === "object" ? fx[i] || null : null;
29155
29214
  }
29156
29215
  function drawCellGlowRings(group, cx, cy, baseR, g, t) {
29157
- const color2 = g.color || "#00C8FF";
29158
- const spread = g.spread || 10;
29159
- const maxOp = g.opacity == null ? 0.8 : g.opacity;
29160
- const ease2 = 1 - (1 - t) * (1 - t);
29161
- const alpha = maxOp * Math.pow(1 - t, 1.4);
29162
- if (alpha <= 0.02) return;
29163
- const r = baseR + spread * ease2;
29164
- const lw = Math.max(1, 3.2 * (1 - t) + 0.6);
29165
- group.appendChild(new Circle({ style: { cx, cy, r, stroke: color2, lineWidth: lw, strokeOpacity: alpha, fill: "none", lineCap: "round" } }));
29166
- group.appendChild(new Circle({ style: { cx, cy, r: r + spread * 0.6, stroke: color2, lineWidth: Math.max(0.6, lw * 0.55), strokeOpacity: alpha * 0.35, fill: "none", lineCap: "round" } }));
29216
+ glowStack(group, cx, cy, baseR, {
29217
+ color: g.color,
29218
+ t: t == null ? g.t : t,
29219
+ spread: g.spread,
29220
+ opacity: g.opacity,
29221
+ width: g.width,
29222
+ level: g.level
29223
+ });
29224
+ }
29225
+ function cellBlinkOpacity(g) {
29226
+ if (!g || !g.blink) return 1;
29227
+ return 1 - g.blink * glowBlinkK(g.t == null ? 0 : g.t);
29228
+ }
29229
+ function drawCellGlowBackdrop(group, cx, cy, meterW, meterH, g) {
29230
+ if (!g) return;
29231
+ glowBackdrop(group, cx, cy, meterW, meterH, {
29232
+ color: g.color,
29233
+ t: g.t,
29234
+ spread: g.spread != null ? g.spread : 14,
29235
+ level: g.level,
29236
+ blink: g.blink
29237
+ });
29167
29238
  }
29168
29239
  function drawCellHighlight(group, cx, cy, baseR, hl) {
29169
29240
  group.appendChild(new Circle({
29170
29241
  style: {
29242
+ ...NO_HIT,
29171
29243
  cx,
29172
29244
  cy,
29173
29245
  r: baseR + 3,
@@ -29183,7 +29255,7 @@ function drawCellNote(group, cx, cy, baseR, note) {
29183
29255
  const color2 = note.color || C2.gold;
29184
29256
  const top = !note.position || note.position === "top";
29185
29257
  const y = top ? cy - baseR - 5 - size / 2 : cy + baseR + 6 + size / 2;
29186
- _drawWrappedTextFixed(group, note.text, cx, y, size, color2, 6, 1.15, 600, "center");
29258
+ _drawWrappedTextFixed(group, note.text, cx, y, size, color2, 6, 1.15, 600, "center", NO_HIT);
29187
29259
  }
29188
29260
  function drawMeterBox(attributes, group) {
29189
29261
  const [w, h] = attributes.size;
@@ -29205,6 +29277,8 @@ function drawMeterBox(attributes, group) {
29205
29277
  const meterH = 26;
29206
29278
  const cx = 0;
29207
29279
  const cy = -1;
29280
+ const baseR = Math.max(12, meterH / 2 + 2);
29281
+ if (fx && fx.glow) drawCellGlowBackdrop(cellGroup, cx, cy, meterW, meterH, fx.glow);
29208
29282
  if (sel || hover) {
29209
29283
  const ring = new Rect({
29210
29284
  style: {
@@ -29221,9 +29295,10 @@ function drawMeterBox(attributes, group) {
29221
29295
  markCell(ring);
29222
29296
  cellGroup.appendChild(ring);
29223
29297
  }
29298
+ const faceStart = cellGroup.children.length;
29224
29299
  drawMeterFace(cellGroup, cx, cy, meterW, meterH, lineColor, markCell, 0);
29300
+ const faceEnd = cellGroup.children.length;
29225
29301
  if (fx) {
29226
- const baseR = Math.max(12, meterH / 2 + 2);
29227
29302
  if (fx.hl) drawCellHighlight(cellGroup, cx, cy, baseR, fx.hl);
29228
29303
  if (fx.note && fx.note.text) drawCellNote(cellGroup, cx, cy, baseR, fx.note);
29229
29304
  if (fx.glow) drawCellGlowRings(cellGroup, cx, cy, baseR, fx.glow, fx.glow.t || 0);
@@ -29235,6 +29310,13 @@ function drawMeterBox(attributes, group) {
29235
29310
  if (fx && fx.pulse && fx.pulse.opacity != null && fx.pulse.opacity < 1) {
29236
29311
  cellGroup.style.opacity = fx.pulse.opacity;
29237
29312
  }
29313
+ const blinkOp0 = fx && fx.glow ? cellBlinkOpacity(fx.glow) : 1;
29314
+ if (blinkOp0 < 1) {
29315
+ for (let k = faceStart; k < faceEnd && k < cellGroup.children.length; k++) {
29316
+ const s2 = cellGroup.children[k].style;
29317
+ s2.opacity = (s2.opacity == null ? 1 : s2.opacity) * blinkOp0;
29318
+ }
29319
+ }
29238
29320
  group.appendChild(cellGroup);
29239
29321
  } else if (attributes._meterBoxMode === "grid" && attributes._customers) {
29240
29322
  const frameColor = boxColor || C2.meterText;
@@ -29260,6 +29342,7 @@ function drawMeterBox(attributes, group) {
29260
29342
  shape.__custBox = nodeId;
29261
29343
  shape.__custIndex = i;
29262
29344
  };
29345
+ const hitLayer = new Group({});
29263
29346
  for (let i = 0; i < customers.length; i++) {
29264
29347
  const row2 = Math.floor(i / cols);
29265
29348
  const col = i % cols;
@@ -29271,6 +29354,7 @@ function drawMeterBox(attributes, group) {
29271
29354
  const lineColor = sel ? C2.gold : hover ? C2.cyan : baseColor;
29272
29355
  const fx = cellFxAt(attributes, i);
29273
29356
  const cellGroup = new Group({});
29357
+ if (fx && fx.glow) drawCellGlowBackdrop(cellGroup, cx, cy, meterW, meterH, fx.glow);
29274
29358
  if (sel || hover) {
29275
29359
  cellGroup.appendChild(new Circle({
29276
29360
  style: {
@@ -29284,7 +29368,9 @@ function drawMeterBox(attributes, group) {
29284
29368
  }
29285
29369
  }));
29286
29370
  }
29371
+ const faceStart = cellGroup.children.length;
29287
29372
  drawMeterFace(cellGroup, cx, cy, meterW, meterH, lineColor, markCell, i);
29373
+ const faceEnd = cellGroup.children.length;
29288
29374
  if (fx) {
29289
29375
  if (fx.hl) drawCellHighlight(cellGroup, cx, cy, hitR, fx.hl);
29290
29376
  if (fx.note && fx.note.text) drawCellNote(cellGroup, cx, cy, hitR, fx.note);
@@ -29296,9 +29382,19 @@ function drawMeterBox(attributes, group) {
29296
29382
  _drawWrappedTextFixed(cellGroup, name, cx, cy + meterH / 2 + 6.5, 6.5, lineColor, 4, 1.15, 500);
29297
29383
  }
29298
29384
  }
29299
- const hit = new Circle({ style: { cx, cy, r: hitR, fill: "rgba(255,255,255,0.01)", stroke: "none", cursor: "pointer" } });
29300
- markCell(hit, i);
29301
- cellGroup.appendChild(hit);
29385
+ const hitCell = new Rect({
29386
+ style: {
29387
+ x: cx - cellW / 2 - METER_GAP / 2,
29388
+ y: cy - cellW / 2 - METER_GAP / 2,
29389
+ width: cellW + METER_GAP,
29390
+ height: cellH + METER_GAP,
29391
+ fill: "rgba(255,255,255,0.01)",
29392
+ stroke: "none",
29393
+ cursor: "pointer"
29394
+ }
29395
+ });
29396
+ markCell(hitCell, i);
29397
+ hitLayer.appendChild(hitCell);
29302
29398
  if (showName) {
29303
29399
  const name = customers[i].realConsName || customers[i].consName || "";
29304
29400
  if (name) {
@@ -29313,14 +29409,22 @@ function drawMeterBox(attributes, group) {
29313
29409
  }
29314
29410
  });
29315
29411
  markCell(hitName, i);
29316
- cellGroup.appendChild(hitName);
29412
+ hitLayer.appendChild(hitName);
29317
29413
  }
29318
29414
  }
29319
29415
  if (fx && fx.pulse && fx.pulse.opacity != null && fx.pulse.opacity < 1) {
29320
29416
  cellGroup.style.opacity = fx.pulse.opacity;
29321
29417
  }
29418
+ const blinkOp = fx && fx.glow ? cellBlinkOpacity(fx.glow) : 1;
29419
+ if (blinkOp < 1) {
29420
+ for (let k = faceStart; k < faceEnd && k < cellGroup.children.length; k++) {
29421
+ const s2 = cellGroup.children[k].style;
29422
+ s2.opacity = (s2.opacity == null ? 1 : s2.opacity) * blinkOp;
29423
+ }
29424
+ }
29322
29425
  group.appendChild(cellGroup);
29323
29426
  }
29427
+ group.appendChild(hitLayer);
29324
29428
  } else {
29325
29429
  const s2 = Math.min(w, h) || 21;
29326
29430
  const ink = boxColor || C2.meterText;
@@ -29408,35 +29512,134 @@ const DRAWERS = {
29408
29512
  other: drawUnknown
29409
29513
  };
29410
29514
  const _pulseState = /* @__PURE__ */ new Map();
29411
- function drawGlow(attributes, group) {
29412
- const color2 = attributes._glowColor;
29413
- if (!color2) return;
29414
- const t = attributes._glowT == null ? 0 : attributes._glowT;
29415
- const [w, h] = attributes.size || [40, 40];
29416
- const baseR = Math.max(w, h) / 2 + 3;
29417
- const spread = attributes._glowSpread || 14;
29418
- const maxOp = attributes._glowOpacity == null ? 0.9 : attributes._glowOpacity;
29419
- const ease2 = 1 - (1 - t) * (1 - t);
29420
- const alpha = maxOp * Math.pow(1 - t, 1.4);
29515
+ function pushGlowRing(group, cx, cy, r, lineWidth, strokeOpacity, color2, blur) {
29516
+ if (strokeOpacity <= 0.015 || lineWidth <= 0) return;
29517
+ const style = {
29518
+ ...NO_HIT,
29519
+ cx,
29520
+ cy,
29521
+ r,
29522
+ stroke: color2,
29523
+ lineWidth,
29524
+ strokeOpacity,
29525
+ fill: "none",
29526
+ lineCap: "round"
29527
+ };
29528
+ if (blur > 0.5) {
29529
+ style.shadowColor = color2;
29530
+ style.shadowBlur = blur;
29531
+ }
29532
+ group.appendChild(new Circle({ style }));
29533
+ }
29534
+ function glowStack(group, cx, cy, baseR, g) {
29535
+ const color2 = g.color || "#00C8FF";
29536
+ const level = g.level > 0 ? g.level : 1;
29537
+ const tt = clampNum(g.t == null ? 0 : g.t, 0, 1);
29538
+ const spread = g.spread != null ? g.spread : 14;
29539
+ const maxOp = g.opacity == null ? 0.95 : g.opacity;
29540
+ const alpha = Math.min(1, maxOp * Math.pow(1 - tt, 1.1) * Math.min(1.5, level));
29421
29541
  if (alpha <= 0.02) return;
29422
- const r = baseR + spread * ease2;
29423
- const lw = Math.max(1.2, 4.4 * (1 - t) + 0.8);
29424
- group.appendChild(new Circle({
29425
- style: { cx: 0, cy: 0, r, stroke: color2, lineWidth: lw, strokeOpacity: alpha, fill: "none", lineCap: "round" }
29542
+ const r = baseR + spread * (1 - (1 - tt) * (1 - tt));
29543
+ const lw = Math.max(1.3, (g.width || 3.6) * level * (0.34 + 0.66 * (1 - tt)));
29544
+ pushGlowRing(group, cx, cy, r + spread * 1.1, lw * 0.4, alpha * 0.26, color2, 0);
29545
+ pushGlowRing(group, cx, cy, r + spread * 0.55, lw * 0.62, alpha * 0.5, color2, lw * 2.5);
29546
+ pushGlowRing(group, cx, cy, r, lw, alpha, color2, lw * 6);
29547
+ pushGlowRing(group, cx, cy, r, Math.max(1.1, lw * 0.34), Math.min(1, alpha * 1.05), mixWhite(color2, 0.62), lw * 2);
29548
+ }
29549
+ function glowBackdrop(group, x, y, w, h, g) {
29550
+ const color2 = g.color || "#00C8FF";
29551
+ const level = g.level > 0 ? g.level : 1;
29552
+ const tt = clampNum(g.t == null ? 0 : g.t, 0, 1);
29553
+ const spread = g.spread != null ? g.spread : Math.max(14, Math.min(w, h) * 0.7);
29554
+ const blink = g.blink || 0;
29555
+ const fade = Math.pow(1 - tt, 1.1);
29556
+ const bk = glowBlinkK(tt);
29557
+ const base = Math.min(0.6, level * (0.26 * fade + 0.5 * blink * bk));
29558
+ const aura = Math.min(0.45, level * (0.2 * fade + 0.42 * blink * bk));
29559
+ if (base <= 0.02 && aura <= 0.02) return;
29560
+ const rad2 = Math.max(3, Math.min(w, h) * 0.16);
29561
+ const blurPx = clampNum(Math.min(w, h) * 0.28, 4, 18) * level;
29562
+ group.appendChild(new Rect({
29563
+ style: {
29564
+ ...NO_HIT,
29565
+ x: x - w / 2 - spread * 0.85,
29566
+ y: y - h / 2 - spread * 0.85,
29567
+ width: w + spread * 1.7,
29568
+ height: h + spread * 1.7,
29569
+ radius: rad2 + 4,
29570
+ fill: color2,
29571
+ fillOpacity: aura,
29572
+ stroke: "none",
29573
+ filter: `blur(${blurPx.toFixed(1)}px)`
29574
+ }
29426
29575
  }));
29427
- group.appendChild(new Circle({
29576
+ group.appendChild(new Rect({
29428
29577
  style: {
29429
- cx: 0,
29430
- cy: 0,
29431
- r: r + spread * 0.6,
29432
- stroke: color2,
29433
- lineWidth: Math.max(0.8, lw * 0.55),
29434
- strokeOpacity: alpha * 0.35,
29435
- fill: "none",
29436
- lineCap: "round"
29578
+ ...NO_HIT,
29579
+ x: x - w / 2 - 2.5,
29580
+ y: y - h / 2 - 2.5,
29581
+ width: w + 5,
29582
+ height: h + 5,
29583
+ radius: rad2,
29584
+ fill: color2,
29585
+ fillOpacity: base,
29586
+ stroke: "none"
29437
29587
  }
29438
29588
  }));
29439
29589
  }
29590
+ function glowBlinkK(t) {
29591
+ return 0.5 - 0.5 * Math.cos((t || 0) * Math.PI * 2);
29592
+ }
29593
+ function glowParams(attributes) {
29594
+ const color2 = attributes._glowColor;
29595
+ if (!color2) return null;
29596
+ const [w, h] = attributes.size || [40, 40];
29597
+ const t = clampNum(attributes._glowT == null ? 0 : attributes._glowT, 0, 1);
29598
+ const level = attributes._glowLevel > 0 ? attributes._glowLevel : 1;
29599
+ const spread = attributes._glowSpread != null ? attributes._glowSpread : clampNum(Math.min(w, h) * 0.7, 16, 46);
29600
+ return {
29601
+ color: color2,
29602
+ t,
29603
+ w,
29604
+ h,
29605
+ level,
29606
+ baseR: Math.max(w, h) / 2 + 3,
29607
+ spread,
29608
+ maxOp: attributes._glowOpacity == null ? 0.95 : attributes._glowOpacity,
29609
+ lw0: attributes._glowWidth || 5.6,
29610
+ // 主环初始线宽(可调,默认 5.6)
29611
+ blink: attributes._glowBlink || 0
29612
+ // 整体闪烁强度(0=关;setNodeGlow blink 参数)
29613
+ };
29614
+ }
29615
+ function drawGlowBackdrop(attributes, group) {
29616
+ const p = glowParams(attributes);
29617
+ if (!p) return;
29618
+ glowBackdrop(group, 0, 0, p.w, p.h, {
29619
+ color: p.color,
29620
+ t: p.t,
29621
+ spread: p.spread,
29622
+ level: p.level,
29623
+ blink: p.blink
29624
+ });
29625
+ }
29626
+ function nodeBlinkOpacity(attributes) {
29627
+ const blink = attributes._glowBlink;
29628
+ if (!blink) return 1;
29629
+ return 1 - blink * glowBlinkK(attributes._glowT == null ? 0 : attributes._glowT);
29630
+ }
29631
+ function drawGlow(attributes, group) {
29632
+ const p = glowParams(attributes);
29633
+ if (!p) return;
29634
+ glowStack(group, 0, 0, p.baseR, {
29635
+ color: p.color,
29636
+ t: p.t,
29637
+ spread: p.spread,
29638
+ opacity: p.maxOp,
29639
+ width: p.lw0,
29640
+ level: p.level
29641
+ });
29642
+ }
29440
29643
  class SvgSymbolNode extends g6.BaseNode {
29441
29644
  drawKeyShape(attributes, container) {
29442
29645
  const [w, h] = attributes.size;
@@ -29466,7 +29669,10 @@ class SvgSymbolNode extends g6.BaseNode {
29466
29669
  const target = rot ? new Group({ style: { transform: `rotate(${rot}deg)` } }) : key;
29467
29670
  if (rot) key.appendChild(target);
29468
29671
  const draw = DRAWERS[attributes.cat] || drawUnknown;
29672
+ drawGlowBackdrop(attributes, target);
29673
+ const symStart = (target.children || []).length;
29469
29674
  draw(attributes, target);
29675
+ const symEnd = (target.children || []).length;
29470
29676
  drawGlow(attributes, target);
29471
29677
  drawHighlight(attributes, target);
29472
29678
  drawAnnotation(attributes, target);
@@ -29484,6 +29690,15 @@ class SvgSymbolNode extends g6.BaseNode {
29484
29690
  for (const child of items) child.style.opacity = op;
29485
29691
  }
29486
29692
  }
29693
+ const blinkOp = nodeBlinkOpacity(attributes);
29694
+ if (blinkOp < 1) {
29695
+ const kids = target.children || [];
29696
+ for (let i = symStart; i < symEnd && i < kids.length; i++) {
29697
+ const s2 = kids[i].style;
29698
+ const base = s2.opacity == null ? 1 : s2.opacity;
29699
+ s2.opacity = base * blinkOp;
29700
+ }
29701
+ }
29487
29702
  return key;
29488
29703
  }
29489
29704
  }
@@ -29505,10 +29720,58 @@ const _hoisted_4$2 = { class: "topo-meta" };
29505
29720
  const _hoisted_5$2 = ["title"];
29506
29721
  const _hoisted_6$2 = {
29507
29722
  key: 1,
29723
+ class: "topo-legend"
29724
+ };
29725
+ const _hoisted_7$2 = { class: "topo-legend-body" };
29726
+ const _hoisted_8$2 = {
29727
+ class: "topo-legend-icon",
29728
+ viewBox: "0 0 32 20",
29729
+ width: "32",
29730
+ height: "20",
29731
+ "aria-hidden": "true"
29732
+ };
29733
+ const _hoisted_9$2 = ["stroke"];
29734
+ const _hoisted_10$2 = ["fill", "stroke"];
29735
+ const _hoisted_11$2 = ["stroke"];
29736
+ const _hoisted_12$2 = ["stroke"];
29737
+ const _hoisted_13$2 = ["stroke"];
29738
+ const _hoisted_14$2 = ["fill"];
29739
+ const _hoisted_15$1 = ["stroke"];
29740
+ const _hoisted_16$1 = ["stroke"];
29741
+ const _hoisted_17$1 = ["stroke"];
29742
+ const _hoisted_18$1 = ["fill"];
29743
+ const _hoisted_19$1 = ["fill"];
29744
+ const _hoisted_20$1 = ["fill", "stroke"];
29745
+ const _hoisted_21$1 = ["stroke"];
29746
+ const _hoisted_22$1 = ["fill"];
29747
+ const _hoisted_23$1 = ["stroke"];
29748
+ const _hoisted_24$1 = ["stroke"];
29749
+ const _hoisted_25$1 = ["stroke"];
29750
+ const _hoisted_26$1 = ["stroke"];
29751
+ const _hoisted_27$1 = ["fill", "stroke"];
29752
+ const _hoisted_28$1 = ["fill"];
29753
+ const _hoisted_29$1 = ["stroke"];
29754
+ const _hoisted_30$1 = ["fill"];
29755
+ const _hoisted_31$1 = ["fill", "stroke"];
29756
+ const _hoisted_32$1 = ["fill"];
29757
+ const _hoisted_33$1 = ["stroke"];
29758
+ const _hoisted_34$1 = ["fill"];
29759
+ const _hoisted_35$1 = ["fill"];
29760
+ const _hoisted_36$1 = ["fill", "stroke"];
29761
+ const _hoisted_37$1 = ["stroke", "stroke-width"];
29762
+ const _hoisted_38$1 = ["fill"];
29763
+ const _hoisted_39 = ["stroke"];
29764
+ const _hoisted_40 = ["fill"];
29765
+ const _hoisted_41 = ["stroke"];
29766
+ const _hoisted_42 = ["fill"];
29767
+ const _hoisted_43 = ["fill"];
29768
+ const _hoisted_44 = { class: "topo-legend-label" };
29769
+ const _hoisted_45 = {
29770
+ key: 2,
29508
29771
  class: "topo-loading"
29509
29772
  };
29510
- const _hoisted_7$2 = {
29511
- key: 2,
29773
+ const _hoisted_46 = {
29774
+ key: 3,
29512
29775
  class: "topo-error"
29513
29776
  };
29514
29777
  const _sfc_main$2 = {
@@ -29520,10 +29783,14 @@ const _sfc_main$2 = {
29520
29783
  customerData: { type: Array, default: null },
29521
29784
  /** 主题:'dark'(默认,黑底 SCADA 风)| 'light'(白底);支持 v-model:theme 双向绑定 */
29522
29785
  theme: { type: String, default: "light" },
29523
- /** 左上角工具条(标题/统计/主题/适应/导出)是否显示,默认不显示 */
29524
- toolbar: { type: Boolean, default: false }
29786
+ /** 左上角工具条(标题/统计/主题/图例/适应/导出)是否显示,默认不显示 */
29787
+ toolbar: { type: Boolean, default: false },
29788
+ /** 是否显示左下角图例面板,默认 false(关闭);支持 v-model:legend */
29789
+ legend: { type: Boolean, default: false },
29790
+ /** 自定义图例项(可选):[{ label, shape, color?, text?, key? }];不传则按当前图内容自动生成 */
29791
+ legendItems: { type: Array, default: null }
29525
29792
  },
29526
- emits: ["node-select", "customer-select", "loaded", "top-distances", "update:theme"],
29793
+ emits: ["node-select", "customer-select", "loaded", "top-distances", "update:theme", "update:legend"],
29527
29794
  setup(__props, { expose: __expose, emit: __emit }) {
29528
29795
  const props = __props;
29529
29796
  const emit = __emit;
@@ -29533,6 +29800,49 @@ const _sfc_main$2 = {
29533
29800
  const title = vue.ref("");
29534
29801
  const stats = vue.ref({ total: 0, depth: 0, leaves: 0, byCat: {} });
29535
29802
  const edgeCount = vue.ref(0);
29803
+ const legendCollapsed = vue.ref(false);
29804
+ const hasCustomerData = vue.ref(false);
29805
+ const hasSwitch = vue.ref(false);
29806
+ const hasFuse = vue.ref(false);
29807
+ const LEGEND_DEFS = [
29808
+ { cat: "transformer", label: "配电变压器", shape: "transformer" },
29809
+ { cat: "switch", label: "开关 / 断路器", shape: "switch" },
29810
+ { cat: "fuse", label: "低压熔丝", shape: "fuse" },
29811
+ { cat: "bus", label: "低压母线", shape: "bus" },
29812
+ { cat: "cableTerminal", label: "电缆终端头", shape: "cableTerminal" },
29813
+ { cat: "junction", label: "密母分接点", shape: "junction" },
29814
+ { cat: "meterBox", label: "计量箱", shape: "meterBox" },
29815
+ { cat: "consumer", label: "用户接入点", shape: "consumer" },
29816
+ { cat: "line", label: "低压导线点", shape: "line" },
29817
+ { cat: "station", label: "低压配电箱", shape: "station" },
29818
+ { cat: "other", label: "其它设备", shape: "other" }
29819
+ ];
29820
+ const legendPalette = vue.computed(() => THEMES[themeName()] || THEMES.dark);
29821
+ function legendColor(item, role) {
29822
+ if (item && item.color) return item.color;
29823
+ return legendPalette.value[role] || "#888888";
29824
+ }
29825
+ const legendEntries = vue.computed(() => {
29826
+ const custom = props.legendItems;
29827
+ if (Array.isArray(custom) && custom.length) {
29828
+ return custom.map((it, i) => ({ key: it.key || `custom-${i}`, ...it }));
29829
+ }
29830
+ const byCat = stats.value.byCat || {};
29831
+ const include = (d2) => {
29832
+ if (d2.cat === "switch") return hasSwitch.value;
29833
+ if (d2.cat === "fuse") return hasFuse.value;
29834
+ return !!byCat[d2.cat];
29835
+ };
29836
+ const list = LEGEND_DEFS.filter(include).map((d2) => ({ ...d2, key: d2.cat }));
29837
+ if (list.length) {
29838
+ list.push({ key: "edge-trunk", label: "主干导线", shape: "edge", role: "trunkEdge" });
29839
+ list.push({ key: "edge-branch", label: "分支导线", shape: "edge", role: "branchEdge" });
29840
+ }
29841
+ if (hasCustomerData.value) {
29842
+ list.push({ key: "meterUser", label: "客户电表", shape: "meterUser" });
29843
+ }
29844
+ return list;
29845
+ });
29536
29846
  let graph = null;
29537
29847
  let model = null;
29538
29848
  let selectedId = null;
@@ -29581,6 +29891,56 @@ const _sfc_main$2 = {
29581
29891
  function cellFromShape(shape) {
29582
29892
  return shape && shape.__custBox != null && shape.__custIndex != null ? { nodeId: shape.__custBox, index: shape.__custIndex } : null;
29583
29893
  }
29894
+ function cellIndexAtPoint(nodeId, evt) {
29895
+ const st = styleOf(nodeId);
29896
+ if (!st || st._meterBoxMode !== "grid" || !Array.isArray(st._customers) || !st._customers.length) return null;
29897
+ const c = evt && (evt.canvas || evt.viewport) || {};
29898
+ if (c.x == null || c.y == null || st.x == null || st.y == null) return null;
29899
+ let lx = c.x - st.x;
29900
+ let ly = c.y - st.y;
29901
+ const rot = (st.rotate || 0) * Math.PI / 180;
29902
+ if (rot) {
29903
+ const cos = Math.cos(-rot);
29904
+ const sin = Math.sin(-rot);
29905
+ const nx = lx * cos - ly * sin;
29906
+ const ny = lx * sin + ly * cos;
29907
+ lx = nx;
29908
+ ly = ny;
29909
+ }
29910
+ const cols = st._meterBoxCols || Math.min(st._customers.length, MAX_COLS);
29911
+ const cellW = METER_CELL_W;
29912
+ const cellH = st._meterBoxShowName ? METER_CELL_H : METER_CELL_W;
29913
+ const rows = st._meterBoxRows || Math.ceil(st._customers.length / cols);
29914
+ const gridW = cols * cellW + (cols - 1) * METER_GAP;
29915
+ const gridH = rows * cellH + (rows - 1) * METER_GAP;
29916
+ const col = Math.floor((lx + gridW / 2) / (cellW + METER_GAP));
29917
+ const row2 = Math.floor((ly + gridH / 2) / (cellH + METER_GAP));
29918
+ if (col < 0 || col >= cols || row2 < 0 || row2 >= rows) return null;
29919
+ const i = row2 * cols + col;
29920
+ return i < st._customers.length ? i : null;
29921
+ }
29922
+ const DEBUG_CELL = typeof location !== "undefined" && /[?&]debugCell\b/.test(location.search);
29923
+ function dbgCell(tag, detail) {
29924
+ if (DEBUG_CELL) console.log("[cell-debug]", tag, detail);
29925
+ }
29926
+ function nodeIdOfShape(shape) {
29927
+ if (!shape || !nodeByIdMap) return null;
29928
+ let el = shape;
29929
+ for (let hops = 0; el && hops < 16; hops++) {
29930
+ if (el.id && nodeByIdMap.has(el.id)) return el.id;
29931
+ el = el.parentElement;
29932
+ }
29933
+ return null;
29934
+ }
29935
+ function resolveCell(evt, shape, nodeIdHint, allowBboxFallback = true) {
29936
+ const direct = cellFromShape(shape);
29937
+ if (direct) return direct;
29938
+ let nid = nodeIdHint || nodeIdOfShape(shape);
29939
+ if (!nid && allowBboxFallback) nid = hitTestNodeAtCanvas(evt);
29940
+ if (!nid) return null;
29941
+ const idx = cellIndexAtPoint(nid, evt);
29942
+ return idx == null ? null : { nodeId: nid, index: idx };
29943
+ }
29584
29944
  function neighborsOf(id2) {
29585
29945
  const upstream = [];
29586
29946
  const downstream = [];
@@ -29772,6 +30132,9 @@ const _sfc_main$2 = {
29772
30132
  title.value = String(model.title || "").replace(/\.+$/, "");
29773
30133
  stats.value = model.stats;
29774
30134
  edgeCount.value = model.edges.length;
30135
+ hasCustomerData.value = model.nodes.some((n) => Array.isArray(n._customers) && n._customers.length);
30136
+ hasSwitch.value = model.nodes.some((n) => n.cat === "switch" && !n.isFuse);
30137
+ hasFuse.value = model.nodes.some((n) => n.cat === "switch" && n.isFuse);
29775
30138
  layoutData = computeOrthogonalLayout(model, {
29776
30139
  nodeGap: 26,
29777
30140
  // 主干相邻节点边界间距
@@ -29818,14 +30181,21 @@ const _sfc_main$2 = {
29818
30181
  graph.on("node:click", (evt) => {
29819
30182
  const id2 = evt.target.id;
29820
30183
  if (!id2) return;
29821
- const cell = cellFromShape(evt.originalTarget);
29822
- if (cell && customersOf(id2)) {
30184
+ const cell = resolveCell(evt, evt.originalTarget, id2);
30185
+ dbgCell("node:click", {
30186
+ id: id2,
30187
+ shape: evt.originalTarget && evt.originalTarget.nodeName,
30188
+ mark: cellFromShape(evt.originalTarget),
30189
+ cell,
30190
+ canvas: evt.canvas
30191
+ });
30192
+ if (cell && customersOf(id2) && cell.nodeId === id2) {
29823
30193
  selectCustomerWithInfo(id2, cell.index);
29824
30194
  return;
29825
30195
  }
29826
30196
  routeNodeTap(id2);
29827
30197
  });
29828
- function hitTestNodeAtCanvas(evt) {
30198
+ function hitTestNodeAtCanvas2(evt) {
29829
30199
  const c = evt && (evt.canvas || evt.viewport) || {};
29830
30200
  const cx = c.x, cy = c.y;
29831
30201
  if (cx == null || cy == null) return null;
@@ -29875,8 +30245,14 @@ const _sfc_main$2 = {
29875
30245
  api._emit("edge:click", { edgeId: id2, source: src, target: tgt });
29876
30246
  });
29877
30247
  graph.on("canvas:click", (evt) => {
29878
- const hitId = hitTestNodeAtCanvas(evt);
30248
+ const hitId = hitTestNodeAtCanvas2(evt);
29879
30249
  if (hitId) {
30250
+ const cell = resolveCell(evt, null, hitId);
30251
+ dbgCell("canvas:click", { hitId, cell, canvas: evt.canvas });
30252
+ if (cell && cell.nodeId === hitId && customersOf(hitId)) {
30253
+ selectCustomerWithInfo(hitId, cell.index);
30254
+ return;
30255
+ }
29880
30256
  routeNodeTap(hitId);
29881
30257
  return;
29882
30258
  }
@@ -29898,7 +30274,7 @@ const _sfc_main$2 = {
29898
30274
  const gCanvas = graph.getCanvas && graph.getCanvas();
29899
30275
  const doc = gCanvas && gCanvas.document;
29900
30276
  if (doc) {
29901
- doc.addEventListener("pointermove", (e2) => setCellHover(cellFromShape(e2 && e2.target)));
30277
+ doc.addEventListener("pointermove", (e2) => setCellHover(resolveCell(e2, e2 && e2.target, null, false)));
29902
30278
  }
29903
30279
  hoverLeaveHandler = () => setCellHover(null);
29904
30280
  container.value.addEventListener("pointerleave", hoverLeaveHandler);
@@ -30024,6 +30400,11 @@ const _sfc_main$2 = {
30024
30400
  title: __props.theme === "light" ? "切换到深色主题" : "切换到浅色主题",
30025
30401
  onClick: _cache[0] || (_cache[0] = ($event) => emit("update:theme", __props.theme === "light" ? "dark" : "light"))
30026
30402
  }, vue.toDisplayString(__props.theme === "light" ? "🌙 深色" : "☀️ 浅色"), 9, _hoisted_5$2),
30403
+ vue.createElementVNode("button", {
30404
+ class: "btn",
30405
+ title: "显示 / 隐藏图例",
30406
+ onClick: _cache[1] || (_cache[1] = ($event) => emit("update:legend", !__props.legend))
30407
+ }, vue.toDisplayString(__props.legend ? "🗺 图例 ✓" : "🗺 图例"), 1),
30027
30408
  vue.createElementVNode("button", {
30028
30409
  class: "btn",
30029
30410
  onClick: resetView
@@ -30033,12 +30414,334 @@ const _sfc_main$2 = {
30033
30414
  onClick: exportPng
30034
30415
  }, "导出 PNG")
30035
30416
  ])) : vue.createCommentVNode("", true),
30036
- loading.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_6$2, "正在解析台区拓扑…")) : error.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_7$2, vue.toDisplayString(error.value), 1)) : vue.createCommentVNode("", true)
30417
+ __props.legend && !loading.value && !error.value && legendEntries.value.length ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_6$2, [
30418
+ vue.createElementVNode("div", {
30419
+ class: "topo-legend-title",
30420
+ onClick: _cache[2] || (_cache[2] = ($event) => legendCollapsed.value = !legendCollapsed.value)
30421
+ }, [
30422
+ vue.createElementVNode("span", {
30423
+ class: vue.normalizeClass(["topo-legend-arrow", { open: !legendCollapsed.value }])
30424
+ }, "▸", 2),
30425
+ _cache[3] || (_cache[3] = vue.createTextVNode(" 图例 ", -1))
30426
+ ]),
30427
+ vue.withDirectives(vue.createElementVNode("div", _hoisted_7$2, [
30428
+ (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(legendEntries.value, (it) => {
30429
+ return vue.openBlock(), vue.createElementBlock("div", {
30430
+ key: it.key || it.label,
30431
+ class: "topo-legend-row"
30432
+ }, [
30433
+ (vue.openBlock(), vue.createElementBlock("svg", _hoisted_8$2, [
30434
+ it.shape === "transformer" ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 0 }, [
30435
+ vue.createElementVNode("line", {
30436
+ x1: "2",
30437
+ y1: "10",
30438
+ x2: "8",
30439
+ y2: "10",
30440
+ stroke: legendColor(it, "trunkLine"),
30441
+ "stroke-width": "1.6",
30442
+ "stroke-linecap": "round"
30443
+ }, null, 8, _hoisted_9$2),
30444
+ vue.createElementVNode("circle", {
30445
+ cx: "12",
30446
+ cy: "10",
30447
+ r: "5",
30448
+ fill: legendColor(it, "orange"),
30449
+ stroke: legendColor(it, "trunkLine"),
30450
+ "stroke-width": "1.2"
30451
+ }, null, 8, _hoisted_10$2),
30452
+ vue.createElementVNode("circle", {
30453
+ cx: "22",
30454
+ cy: "10",
30455
+ r: "5",
30456
+ fill: "none",
30457
+ stroke: legendColor(it, "red"),
30458
+ "stroke-width": "2"
30459
+ }, null, 8, _hoisted_11$2),
30460
+ vue.createElementVNode("line", {
30461
+ x1: "27",
30462
+ y1: "10",
30463
+ x2: "31",
30464
+ y2: "10",
30465
+ stroke: legendColor(it, "red"),
30466
+ "stroke-width": "1.6",
30467
+ "stroke-linecap": "round"
30468
+ }, null, 8, _hoisted_12$2)
30469
+ ], 64)) : it.shape === "switch" ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 1 }, [
30470
+ vue.createElementVNode("line", {
30471
+ x1: "2",
30472
+ y1: "10",
30473
+ x2: "7",
30474
+ y2: "10",
30475
+ stroke: legendColor(it, "red"),
30476
+ "stroke-width": "1.6",
30477
+ "stroke-linecap": "round"
30478
+ }, null, 8, _hoisted_13$2),
30479
+ vue.createElementVNode("rect", {
30480
+ x: "7",
30481
+ y: "5",
30482
+ width: "18",
30483
+ height: "10",
30484
+ rx: "2",
30485
+ fill: legendColor(it, "red")
30486
+ }, null, 8, _hoisted_14$2),
30487
+ vue.createElementVNode("line", {
30488
+ x1: "25",
30489
+ y1: "10",
30490
+ x2: "30",
30491
+ y2: "10",
30492
+ stroke: legendColor(it, "red"),
30493
+ "stroke-width": "1.6",
30494
+ "stroke-linecap": "round"
30495
+ }, null, 8, _hoisted_15$1)
30496
+ ], 64)) : it.shape === "fuse" ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 2 }, [
30497
+ vue.createElementVNode("line", {
30498
+ x1: "2",
30499
+ y1: "10",
30500
+ x2: "6",
30501
+ y2: "10",
30502
+ stroke: legendColor(it, "red"),
30503
+ "stroke-width": "1.6",
30504
+ "stroke-linecap": "round"
30505
+ }, null, 8, _hoisted_16$1),
30506
+ vue.createElementVNode("rect", {
30507
+ x: "6",
30508
+ y: "5",
30509
+ width: "20",
30510
+ height: "10",
30511
+ rx: "2",
30512
+ fill: "none",
30513
+ stroke: legendColor(it, "red"),
30514
+ "stroke-width": "1.6"
30515
+ }, null, 8, _hoisted_17$1),
30516
+ vue.createElementVNode("circle", {
30517
+ cx: "12",
30518
+ cy: "10",
30519
+ r: "2.2",
30520
+ fill: legendColor(it, "red")
30521
+ }, null, 8, _hoisted_18$1),
30522
+ vue.createElementVNode("rect", {
30523
+ x: "16",
30524
+ y: "7",
30525
+ width: "7",
30526
+ height: "6",
30527
+ fill: legendColor(it, "red")
30528
+ }, null, 8, _hoisted_19$1)
30529
+ ], 64)) : it.shape === "bus" ? (vue.openBlock(), vue.createElementBlock("rect", {
30530
+ key: 3,
30531
+ x: "5",
30532
+ y: "8",
30533
+ width: "22",
30534
+ height: "4",
30535
+ rx: "2",
30536
+ fill: legendColor(it, "white"),
30537
+ "fill-opacity": "0.5",
30538
+ stroke: legendColor(it, "white"),
30539
+ "stroke-width": "1.2"
30540
+ }, null, 8, _hoisted_20$1)) : it.shape === "cableTerminal" ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 4 }, [
30541
+ vue.createElementVNode("line", {
30542
+ x1: "3",
30543
+ y1: "10",
30544
+ x2: "9",
30545
+ y2: "10",
30546
+ stroke: legendColor(it, "red"),
30547
+ "stroke-width": "1.6",
30548
+ "stroke-linecap": "round"
30549
+ }, null, 8, _hoisted_21$1),
30550
+ vue.createElementVNode("polygon", {
30551
+ points: "9,4 20,10 9,16",
30552
+ fill: legendColor(it, "red")
30553
+ }, null, 8, _hoisted_22$1)
30554
+ ], 64)) : it.shape === "junction" ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 5 }, [
30555
+ vue.createElementVNode("line", {
30556
+ x1: "10",
30557
+ y1: "10",
30558
+ x2: "22",
30559
+ y2: "10",
30560
+ stroke: legendColor(it, "red"),
30561
+ "stroke-width": "1.8",
30562
+ "stroke-linecap": "round"
30563
+ }, null, 8, _hoisted_23$1),
30564
+ vue.createElementVNode("line", {
30565
+ x1: "16",
30566
+ y1: "4",
30567
+ x2: "16",
30568
+ y2: "16",
30569
+ stroke: legendColor(it, "red"),
30570
+ "stroke-width": "1.8",
30571
+ "stroke-linecap": "round"
30572
+ }, null, 8, _hoisted_24$1),
30573
+ vue.createElementVNode("line", {
30574
+ x1: "11",
30575
+ y1: "5",
30576
+ x2: "21",
30577
+ y2: "15",
30578
+ stroke: legendColor(it, "red"),
30579
+ "stroke-width": "1.8",
30580
+ "stroke-linecap": "round"
30581
+ }, null, 8, _hoisted_25$1),
30582
+ vue.createElementVNode("line", {
30583
+ x1: "21",
30584
+ y1: "5",
30585
+ x2: "11",
30586
+ y2: "15",
30587
+ stroke: legendColor(it, "red"),
30588
+ "stroke-width": "1.8",
30589
+ "stroke-linecap": "round"
30590
+ }, null, 8, _hoisted_26$1)
30591
+ ], 64)) : it.shape === "meterBox" ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 6 }, [
30592
+ vue.createElementVNode("rect", {
30593
+ x: "10",
30594
+ y: "2",
30595
+ width: "16",
30596
+ height: "16",
30597
+ rx: "3",
30598
+ fill: legendColor(it, "meterFill"),
30599
+ stroke: legendColor(it, "meterText"),
30600
+ "stroke-width": "1.4"
30601
+ }, null, 8, _hoisted_27$1),
30602
+ vue.createElementVNode("text", {
30603
+ x: "18",
30604
+ y: "13.5",
30605
+ "text-anchor": "middle",
30606
+ "font-size": "8",
30607
+ "font-weight": "700",
30608
+ fill: legendColor(it, "meterText")
30609
+ }, "JX", 8, _hoisted_28$1)
30610
+ ], 64)) : it.shape === "consumer" ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 7 }, [
30611
+ vue.createElementVNode("circle", {
30612
+ cx: "16",
30613
+ cy: "10",
30614
+ r: "7",
30615
+ fill: "none",
30616
+ stroke: legendColor(it, "white"),
30617
+ "stroke-width": "2"
30618
+ }, null, 8, _hoisted_29$1),
30619
+ vue.createElementVNode("text", {
30620
+ x: "16",
30621
+ y: "13",
30622
+ "text-anchor": "middle",
30623
+ "font-size": "9",
30624
+ "font-weight": "700",
30625
+ fill: legendColor(it, "white")
30626
+ }, "J", 8, _hoisted_30$1)
30627
+ ], 64)) : it.shape === "line" ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 8 }, [
30628
+ vue.createElementVNode("circle", {
30629
+ cx: "16",
30630
+ cy: "10",
30631
+ r: "4.6",
30632
+ fill: legendColor(it, "dotFill"),
30633
+ stroke: legendColor(it, "cyan"),
30634
+ "stroke-width": "1.6"
30635
+ }, null, 8, _hoisted_31$1),
30636
+ vue.createElementVNode("circle", {
30637
+ cx: "16",
30638
+ cy: "10",
30639
+ r: "1.5",
30640
+ fill: legendColor(it, "cyan")
30641
+ }, null, 8, _hoisted_32$1)
30642
+ ], 64)) : it.shape === "meterUser" ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 9 }, [
30643
+ vue.createElementVNode("rect", {
30644
+ x: "12",
30645
+ y: "2",
30646
+ width: "8",
30647
+ height: "15",
30648
+ rx: "2",
30649
+ fill: "none",
30650
+ stroke: legendColor(it, "meterText"),
30651
+ "stroke-width": "1.2"
30652
+ }, null, 8, _hoisted_33$1),
30653
+ vue.createElementVNode("rect", {
30654
+ x: "13.5",
30655
+ y: "4",
30656
+ width: "5",
30657
+ height: "2.2",
30658
+ rx: "1",
30659
+ fill: legendColor(it, "meterText"),
30660
+ "fill-opacity": "0.6"
30661
+ }, null, 8, _hoisted_34$1),
30662
+ vue.createElementVNode("circle", {
30663
+ cx: "16",
30664
+ cy: "11",
30665
+ r: "1.4",
30666
+ fill: legendColor(it, "meterText")
30667
+ }, null, 8, _hoisted_35$1)
30668
+ ], 64)) : it.shape === "station" ? (vue.openBlock(), vue.createElementBlock("rect", {
30669
+ key: 10,
30670
+ x: "6",
30671
+ y: "4",
30672
+ width: "20",
30673
+ height: "12",
30674
+ rx: "2",
30675
+ fill: legendColor(it, "stationFill"),
30676
+ stroke: legendColor(it, "white"),
30677
+ "stroke-width": "1.4"
30678
+ }, null, 8, _hoisted_36$1)) : it.shape === "edge" ? (vue.openBlock(), vue.createElementBlock("line", {
30679
+ key: 11,
30680
+ x1: "3",
30681
+ y1: "10",
30682
+ x2: "29",
30683
+ y2: "10",
30684
+ stroke: legendColor(it, it.role || "trunkEdge"),
30685
+ "stroke-width": it.role === "branchEdge" ? 1.2 : 2.4,
30686
+ "stroke-linecap": "round"
30687
+ }, null, 8, _hoisted_37$1)) : it.shape === "square" ? (vue.openBlock(), vue.createElementBlock("rect", {
30688
+ key: 12,
30689
+ x: "10",
30690
+ y: "4",
30691
+ width: "12",
30692
+ height: "12",
30693
+ rx: "2",
30694
+ fill: it.color || legendColor(it, "cyan")
30695
+ }, null, 8, _hoisted_38$1)) : it.shape === "ring" ? (vue.openBlock(), vue.createElementBlock("circle", {
30696
+ key: 13,
30697
+ cx: "16",
30698
+ cy: "10",
30699
+ r: "6",
30700
+ fill: "none",
30701
+ stroke: it.color || legendColor(it, "cyan"),
30702
+ "stroke-width": "2.4"
30703
+ }, null, 8, _hoisted_39)) : it.shape === "triangle" ? (vue.openBlock(), vue.createElementBlock("polygon", {
30704
+ key: 14,
30705
+ points: "10,16 22,16 16,4",
30706
+ fill: it.color || legendColor(it, "red")
30707
+ }, null, 8, _hoisted_40)) : it.shape === "hline" ? (vue.openBlock(), vue.createElementBlock("line", {
30708
+ key: 15,
30709
+ x1: "3",
30710
+ y1: "10",
30711
+ x2: "29",
30712
+ y2: "10",
30713
+ stroke: it.color || legendColor(it, "cyan"),
30714
+ "stroke-width": "2.4",
30715
+ "stroke-linecap": "round"
30716
+ }, null, 8, _hoisted_41)) : it.shape === "text" ? (vue.openBlock(), vue.createElementBlock("text", {
30717
+ key: 16,
30718
+ x: "16",
30719
+ y: "14",
30720
+ "text-anchor": "middle",
30721
+ "font-size": "11",
30722
+ "font-weight": "700",
30723
+ fill: it.color || legendColor(it, "white")
30724
+ }, vue.toDisplayString(it.text || "JX"), 9, _hoisted_42)) : (vue.openBlock(), vue.createElementBlock("circle", {
30725
+ key: 17,
30726
+ cx: "16",
30727
+ cy: "10",
30728
+ r: "5",
30729
+ fill: it.color || legendColor(it, "cyan")
30730
+ }, null, 8, _hoisted_43))
30731
+ ])),
30732
+ vue.createElementVNode("span", _hoisted_44, vue.toDisplayString(it.label), 1)
30733
+ ]);
30734
+ }), 128))
30735
+ ], 512), [
30736
+ [vue.vShow, !legendCollapsed.value]
30737
+ ])
30738
+ ])) : vue.createCommentVNode("", true),
30739
+ loading.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_45, "正在解析台区拓扑…")) : error.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_46, vue.toDisplayString(error.value), 1)) : vue.createCommentVNode("", true)
30037
30740
  ], 8, _hoisted_1$2);
30038
30741
  };
30039
30742
  }
30040
30743
  };
30041
- const TopoGraph = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-d3774ed5"]]);
30744
+ const TopoGraph = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-0736fa17"]]);
30042
30745
  const _hoisted_1$1 = ["data-theme"];
30043
30746
  const _hoisted_2$1 = { class: "panel-head" };
30044
30747
  const _hoisted_3$1 = {
@@ -30411,7 +31114,7 @@ const _sfc_main = {
30411
31114
  }
30412
31115
  };
30413
31116
  const DistanceTop10 = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-438b6017"]]);
30414
- const VERSION = "0.1.9";
31117
+ const VERSION = "0.1.11";
30415
31118
  const DESCRIPTION = "配电台区单线图拓扑成图引擎";
30416
31119
  exports.DESCRIPTION = DESCRIPTION;
30417
31120
  exports.DistanceTop10 = DistanceTop10;