kd-lane-chart-v3 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.
package/dist/esm/index.js CHANGED
@@ -2659,7 +2659,7 @@ function findLogTrendSeries(chart, paramId) {
2659
2659
  const type = sm && sm.subType || (Array.isArray(opt.type) ? opt.type[0] : opt.type);
2660
2660
  if (type !== "line") continue;
2661
2661
  const name = Array.isArray(sm.name) ? sm.name[0] : sm.name;
2662
- if (name === "helper" || name === "log-grid") continue;
2662
+ if (name === "helper" || name === "log-grid" || name === "trend") continue;
2663
2663
  const pid = Array.isArray(opt.paramId) ? opt.paramId[0] : opt.paramId;
2664
2664
  if (String(pid) === String(paramId)) return opt;
2665
2665
  }
@@ -2718,6 +2718,17 @@ function logTrendDataToPixel(chart, paramId, lg, depth) {
2718
2718
  const series = findLogTrendSeries(chart, paramId);
2719
2719
  const xAxisIndex = series ? axisIndexOf$1(series.xAxisIndex) : 0;
2720
2720
  const yAxisIndex = series ? axisIndexOf$1(series.yAxisIndex) : 0;
2721
+ try {
2722
+ const model = chart.getModel();
2723
+ const xAxis = model.getComponent("xAxis", xAxisIndex) && model.getComponent("xAxis", xAxisIndex).axis;
2724
+ const yAxis = model.getComponent("yAxis", yAxisIndex) && model.getComponent("yAxis", yAxisIndex).axis;
2725
+ if (xAxis && yAxis) {
2726
+ const x = xAxis.toGlobalCoord(xAxis.dataToCoord(lg, true));
2727
+ const y = yAxis.toGlobalCoord(yAxis.dataToCoord(depth, true));
2728
+ if (Number.isFinite(x) && Number.isFinite(y)) return [x, y];
2729
+ }
2730
+ } catch (e) {
2731
+ }
2721
2732
  try {
2722
2733
  const px = chart.convertToPixel({ xAxisIndex, yAxisIndex }, [lg, depth]);
2723
2734
  if (px && Number.isFinite(px[0]) && Number.isFinite(px[1])) return px;
@@ -3344,7 +3355,6 @@ const TREND_MIRROR_POINT_STYLE = {
3344
3355
  lineWidth: 1
3345
3356
  };
3346
3357
  const TREND_LINE_Z = 9999;
3347
- const TREND_POINT_Z = 99999;
3348
3358
  const DEPTH_EDGE_SNAP = 0.2;
3349
3359
  const TREND_CLICK_DELAY_MS = 240;
3350
3360
  const TREND_HIT_RADIUS = 14;
@@ -3699,7 +3709,7 @@ function listLineSeries(model) {
3699
3709
  const type = sm && sm.subType || seriesTypeName(opt);
3700
3710
  if (type !== "line") continue;
3701
3711
  const name = optionScalar(sm && sm.name) || seriesDisplayName(opt);
3702
- if (name === "helper" || name === "log-grid") continue;
3712
+ if (name === "helper" || name === "log-grid" || name === "trend") continue;
3703
3713
  out.push({
3704
3714
  seriesIndex: sm.seriesIndex,
3705
3715
  xAxisIndex: axisIndexOf(opt.xAxisIndex),
@@ -3738,20 +3748,27 @@ function findSeriesForDepth(chart, paramId, depth) {
3738
3748
  function eventOffsetInChart(chart, evt) {
3739
3749
  var _a, _b;
3740
3750
  const zr = chart.getZr();
3741
- const rect = (_b = (_a = chart.getDom()) == null ? void 0 : _a.getBoundingClientRect) == null ? void 0 : _b.call(_a);
3742
- let offsetX = 0;
3743
- let offsetY = 0;
3751
+ const clamp = (x, y) => ({
3752
+ offsetX: Math.max(0, Math.min(x, zr.getWidth())),
3753
+ offsetY: Math.max(0, Math.min(y, zr.getHeight()))
3754
+ });
3755
+ if (evt && evt.event && Number.isFinite(evt.offsetX) && Number.isFinite(evt.offsetY)) {
3756
+ return clamp(evt.offsetX, evt.offsetY);
3757
+ }
3744
3758
  const native = (evt == null ? void 0 : evt.event) || evt;
3759
+ let el = null;
3760
+ try {
3761
+ el = zr.painter && zr.painter.getViewportRoot && zr.painter.getViewportRoot();
3762
+ } catch (e) {
3763
+ }
3764
+ const rect = (_b = (_a = el || chart.getDom()) == null ? void 0 : _a.getBoundingClientRect) == null ? void 0 : _b.call(_a);
3745
3765
  if (rect && Number.isFinite(native == null ? void 0 : native.clientX) && Number.isFinite(native == null ? void 0 : native.clientY)) {
3746
- offsetX = native.clientX - rect.left;
3747
- offsetY = native.clientY - rect.top;
3748
- } else if (Number.isFinite(evt == null ? void 0 : evt.offsetX) && Number.isFinite(evt == null ? void 0 : evt.offsetY)) {
3749
- offsetX = evt.offsetX;
3750
- offsetY = evt.offsetY;
3766
+ return clamp(native.clientX - rect.left, native.clientY - rect.top);
3751
3767
  }
3752
- offsetX = Math.max(0, Math.min(offsetX, zr.getWidth()));
3753
- offsetY = Math.max(0, Math.min(offsetY, zr.getHeight()));
3754
- return { offsetX, offsetY };
3768
+ if (Number.isFinite(evt == null ? void 0 : evt.offsetX) && Number.isFinite(evt == null ? void 0 : evt.offsetY)) {
3769
+ return clamp(evt.offsetX, evt.offsetY);
3770
+ }
3771
+ return clamp(0, 0);
3755
3772
  }
3756
3773
  function isPointerInAnyGrid(chart, offsetX, offsetY) {
3757
3774
  const gridCount = modelGridCount(chartModel(chart));
@@ -3819,8 +3836,48 @@ function pixelToData(chart, paramId, offsetX, offsetY, loose = false) {
3819
3836
  }
3820
3837
  return null;
3821
3838
  }
3839
+ function axisPairToPixel(chart, xAxisIndex, yAxisIndex, value, depth) {
3840
+ try {
3841
+ const model = chartModel(chart);
3842
+ if (!model) return null;
3843
+ const xComp = model.getComponent("xAxis", xAxisIndex || 0);
3844
+ const yComp = model.getComponent("yAxis", yAxisIndex || 0);
3845
+ const xAxis = xComp && xComp.axis;
3846
+ const yAxis = yComp && yComp.axis;
3847
+ if (!xAxis || !yAxis) return null;
3848
+ const x = xAxis.toGlobalCoord(xAxis.dataToCoord(value, true));
3849
+ const y = yAxis.toGlobalCoord(yAxis.dataToCoord(depth, true));
3850
+ if (Number.isFinite(x) && Number.isFinite(y)) return [x, y];
3851
+ } catch (e) {
3852
+ }
3853
+ return null;
3854
+ }
3855
+ function axisPairFromPixel(chart, xAxisIndex, yAxisIndex, offsetX, offsetY) {
3856
+ try {
3857
+ const model = chartModel(chart);
3858
+ if (!model) return null;
3859
+ const xComp = model.getComponent("xAxis", xAxisIndex || 0);
3860
+ const yComp = model.getComponent("yAxis", yAxisIndex || 0);
3861
+ const xAxis = xComp && xComp.axis;
3862
+ const yAxis = yComp && yComp.axis;
3863
+ if (!xAxis || !yAxis) return null;
3864
+ const value = xAxis.coordToData(xAxis.toLocalCoord(offsetX), true);
3865
+ const depth = yAxis.coordToData(yAxis.toLocalCoord(offsetY), true);
3866
+ if (!Number.isFinite(value) || !Number.isFinite(depth)) return null;
3867
+ return {
3868
+ value,
3869
+ depth,
3870
+ xAxisIndex: xAxisIndex || 0,
3871
+ yAxisIndex: yAxisIndex || 0
3872
+ };
3873
+ } catch (e) {
3874
+ return null;
3875
+ }
3876
+ }
3822
3877
  function dataToPixel(chart, paramId, value, depth) {
3823
3878
  const finder = findSeriesForDepth(chart, paramId, depth);
3879
+ const live = axisPairToPixel(chart, finder.xAxisIndex, finder.yAxisIndex, value, depth);
3880
+ if (live) return live;
3824
3881
  const finders = [
3825
3882
  { xAxisIndex: finder.xAxisIndex, yAxisIndex: finder.yAxisIndex },
3826
3883
  { seriesIndex: finder.seriesIndex },
@@ -3871,49 +3928,62 @@ class TrendLineManager {
3871
3928
  this.dragSession = null;
3872
3929
  this._panLockedByHover = false;
3873
3930
  this._raf = null;
3874
- this._zoomRaf = null;
3875
- this._zoomPending = false;
3876
- this._zoomEndTimer = null;
3877
- this._graphicIds = /* @__PURE__ */ Object.create(null);
3878
- this._zrElCache = /* @__PURE__ */ new WeakMap();
3931
+ this._seriesIds = /* @__PURE__ */ Object.create(null);
3932
+ this._graphicCleared = /* @__PURE__ */ new WeakSet();
3879
3933
  this._ignoreClickUntil = 0;
3934
+ this._destroyed = false;
3880
3935
  }
3881
3936
  destroy() {
3937
+ this._destroyed = true;
3882
3938
  this._clearClickTimer();
3883
- this._endDrag();
3884
- Array.from(this.binds.keys()).forEach((laneId) => this.unbindChart(laneId));
3939
+ try {
3940
+ this._unbindDragWindow();
3941
+ } catch (e) {
3942
+ }
3943
+ this._panLockedByHover = false;
3944
+ Array.from(this.binds.keys()).forEach((laneId) => {
3945
+ try {
3946
+ this.unbindChart(laneId);
3947
+ } catch (e) {
3948
+ }
3949
+ });
3885
3950
  if (this._raf) {
3886
3951
  cancelAnimationFrame(this._raf);
3887
3952
  this._raf = null;
3888
3953
  }
3889
- if (this._zoomRaf) {
3890
- cancelAnimationFrame(this._zoomRaf);
3891
- this._zoomRaf = null;
3892
- }
3893
- if (this._zoomEndTimer) {
3894
- clearTimeout(this._zoomEndTimer);
3895
- this._zoomEndTimer = null;
3896
- }
3897
3954
  this.store = /* @__PURE__ */ Object.create(null);
3898
- this._graphicIds = /* @__PURE__ */ Object.create(null);
3955
+ this._seriesIds = /* @__PURE__ */ Object.create(null);
3956
+ this.binds.clear();
3957
+ this.host = null;
3958
+ }
3959
+ _alive() {
3960
+ return !this._destroyed && !!this.host;
3899
3961
  }
3900
3962
  /** 从模板 line.trendSegments 回显;已有 store 的曲线不覆盖,避免编辑中被模板刷新冲掉。8.21 */
3901
3963
  hydrateFromTemplate(template) {
3964
+ if (!this._alive()) return;
3965
+ const hasEcho = Array.isArray(this.host.lineListData) && this.host.lineListData.length;
3902
3966
  const lanes = template && template.lanes || [];
3967
+ const alive = /* @__PURE__ */ new Set();
3903
3968
  lanes.forEach((lane) => {
3904
3969
  (lane.lines || []).forEach((line) => {
3905
3970
  if (!lane.laneId || !line.paramId) return;
3906
3971
  const key = makeTrendKey(lane.laneId, line.paramId);
3972
+ alive.add(key);
3907
3973
  if (this.store[key]) return;
3908
- if (line.trendSegments && line.trendSegments.length) {
3909
- this.store[key] = { segments: this._prepareEchoSegments(line.paramId, line.trendSegments) };
3910
- } else {
3974
+ if (!hasEcho || !line.trendSegments || !line.trendSegments.length) {
3911
3975
  this.store[key] = { segments: [] };
3976
+ return;
3912
3977
  }
3978
+ this.store[key] = { segments: this._prepareEchoSegments(line.paramId, line.trendSegments) };
3913
3979
  });
3914
3980
  });
3981
+ Object.keys(this.store).forEach((key) => {
3982
+ if (!alive.has(key)) delete this.store[key];
3983
+ });
3915
3984
  }
3916
3985
  getChart(laneId) {
3986
+ if (!this._alive()) return null;
3917
3987
  const ref = this.host.getChartRefByLaneId(laneId);
3918
3988
  const chart = ref && ref.chart;
3919
3989
  if (!chart || chart.isDisposed()) return null;
@@ -3921,6 +3991,7 @@ class TrendLineManager {
3921
3991
  }
3922
3992
  /** isDraw 可编辑,或已有控制点(只读回显)的曲线。8.21 */
3923
3993
  listDrawableTargets() {
3994
+ if (!this._alive()) return [];
3924
3995
  const lanes = this.host.currentTemplate && this.host.currentTemplate.lanes || [];
3925
3996
  const list = [];
3926
3997
  lanes.forEach((lane) => {
@@ -3982,45 +4053,62 @@ class TrendLineManager {
3982
4053
  /** 捕获阶段监听 mousedown,保证拖控制点比图表其它逻辑先拿到。8.21 */
3983
4054
  bindChart(laneId, chart) {
3984
4055
  this.unbindChart(laneId);
3985
- if (!chart || chart.isDisposed()) return;
3986
- const onMouseDown = (e) => this.onMouseDown(e, laneId);
4056
+ if (!this._alive() || !chart || chart.isDisposed()) return;
3987
4057
  const onZrDown = (e) => this.onZrMouseDown(e, laneId);
3988
4058
  const onZrMove = (e) => this.onZrHover(e, laneId);
3989
4059
  const onDblClick = (e) => this.handleDblClick(e, laneId);
3990
- const dom = chart.getDom();
3991
- if (dom) dom.addEventListener("mousedown", onMouseDown, true);
3992
4060
  const zr = chart.getZr();
3993
4061
  zr.on("mousedown", onZrDown);
3994
4062
  zr.on("mousemove", onZrMove);
3995
4063
  zr.on("dblclick", onDblClick);
3996
4064
  const onZoom = () => {
4065
+ if (this.host && this.host._isPanning) return;
3997
4066
  if (this.host && typeof this.host.startChartZoom === "function") {
3998
4067
  this.host.startChartZoom();
3999
4068
  }
4000
- this.scheduleZoomRender();
4001
4069
  };
4002
4070
  if (typeof chart.on === "function") chart.on("datazoom", onZoom);
4003
- this.binds.set(laneId, { chart, onMouseDown, onZrDown, onZrMove, onDblClick, onZoom, dom });
4071
+ this.binds.set(laneId, { chart, onZrDown, onZrMove, onDblClick, onZoom, dom: chart.getDom() });
4004
4072
  this.updateCursor(laneId);
4005
4073
  }
4006
4074
  unbindChart(laneId) {
4007
4075
  const bind = this.binds.get(laneId);
4008
4076
  if (!bind) return;
4077
+ const session = this.dragSession;
4078
+ if (session && (session.chart === bind.chart || session.target && session.target.laneId === laneId)) {
4079
+ this._unbindDragWindow();
4080
+ }
4009
4081
  if (bind.dom && bind.onMouseDown) {
4010
4082
  bind.dom.removeEventListener("mousedown", bind.onMouseDown, true);
4011
4083
  }
4012
4084
  if (bind.dom && bind.onZoom) {
4013
4085
  bind.dom.removeEventListener("wheel", bind.onZoom);
4014
4086
  }
4015
- if (bind.chart && !bind.chart.isDisposed()) {
4016
- const zr = bind.chart.getZr();
4017
- if (bind.onZrDown) zr.off("mousedown", bind.onZrDown);
4018
- if (bind.onZrMove) zr.off("mousemove", bind.onZrMove);
4019
- if (bind.onDblClick) zr.off("dblclick", bind.onDblClick);
4020
- if (bind.onZoom) {
4021
- zr.off("mousewheel", bind.onZoom);
4022
- if (typeof bind.chart.off === "function") bind.chart.off("datazoom", bind.onZoom);
4087
+ const chart = bind.chart;
4088
+ try {
4089
+ if (chart && typeof chart.getZr === "function") {
4090
+ const zr = chart.getZr();
4091
+ if (zr) {
4092
+ if (bind.onZrDown) zr.off("mousedown", bind.onZrDown);
4093
+ if (bind.onZrMove) zr.off("mousemove", bind.onZrMove);
4094
+ if (bind.onDblClick) zr.off("dblclick", bind.onDblClick);
4095
+ if (bind.onZoom) zr.off("mousewheel", bind.onZoom);
4096
+ }
4097
+ }
4098
+ if (chart && bind.onZoom && typeof chart.off === "function") {
4099
+ chart.off("datazoom", bind.onZoom);
4023
4100
  }
4101
+ } catch (e) {
4102
+ }
4103
+ delete this._seriesIds[laneId];
4104
+ if (bind) {
4105
+ bind.chart = null;
4106
+ bind.dom = null;
4107
+ bind.onZrDown = null;
4108
+ bind.onZrMove = null;
4109
+ bind.onDblClick = null;
4110
+ bind.onZoom = null;
4111
+ bind.onMouseDown = null;
4024
4112
  }
4025
4113
  this.binds.delete(laneId);
4026
4114
  }
@@ -4041,10 +4129,11 @@ class TrendLineManager {
4041
4129
  /** 图表重绘后对齐叠加层;拖拽中跳过,避免和 _onDragMove 抢 setOption。8.21 */
4042
4130
  // 只排一帧,不再连刷两次 renderAll(会闪)。8.24
4043
4131
  scheduleRender() {
4044
- if (this.dragSession) return;
4132
+ if (!this._alive() || this.dragSession) return;
4045
4133
  if (this._raf) return;
4046
4134
  this._raf = requestAnimationFrame(() => {
4047
4135
  this._raf = null;
4136
+ if (!this._alive()) return;
4048
4137
  this.renderAll();
4049
4138
  });
4050
4139
  }
@@ -4057,385 +4146,338 @@ class TrendLineManager {
4057
4146
  }
4058
4147
  return false;
4059
4148
  }
4060
- /** 缩放过程用 ZR 就地改像素,结束后再整层重画,避免 replaceMerge 把线冲掉。8.22 */
4149
+ // 趋势线已是笛卡尔 series,dataZoom 带着走;这些入口留给旧调用方,不再像素跟帧。8.25
4061
4150
  scheduleZoomRender() {
4062
- if (this.dragSession || !this.hasAnyKnots()) return;
4063
- this._zoomPending = true;
4064
- if (this._zoomEndTimer) clearTimeout(this._zoomEndTimer);
4065
- this._zoomEndTimer = setTimeout(() => {
4066
- this._zoomPending = false;
4067
- if (this._zoomRaf) {
4068
- cancelAnimationFrame(this._zoomRaf);
4069
- this._zoomRaf = null;
4070
- }
4071
- this.scheduleRender();
4072
- }, 140);
4073
- if (this._zoomRaf) return;
4074
- const tick = () => {
4075
- if (this.dragSession) {
4076
- this._zoomRaf = null;
4077
- return;
4078
- }
4079
- this._patchAllPositions();
4080
- if (this._zoomPending) {
4081
- this._zoomRaf = requestAnimationFrame(tick);
4082
- } else {
4083
- this._zoomRaf = null;
4084
- }
4085
- };
4086
- this._zoomRaf = requestAnimationFrame(tick);
4087
4151
  }
4088
- /** 缩放帧:优先 ZR setShape,元素被冲掉时才回退 renderLane。8.22 */
4089
- _patchAllPositions() {
4090
- const lanes = this.host.currentTemplate && this.host.currentTemplate.lanes || [];
4091
- lanes.forEach((lane) => this._patchLanePositions(lane.laneId));
4152
+ beginPanFollow() {
4092
4153
  }
4093
- _patchLanePositions(laneId) {
4094
- const chart = this.getChart(laneId);
4095
- if (!chart) return;
4096
- const targets = this.listDrawableTargets().filter((t) => t.laneId === laneId);
4097
- const hasKnots = targets.some((t) => (t.segments || []).some((s) => (s.knots || []).length));
4098
- if (!hasKnots) return;
4099
- const patches = [];
4100
- targets.forEach((target) => {
4101
- patches.push(...this._buildPatches(chart, target));
4102
- });
4103
- if (!patches.length) return;
4104
- if (!this._applyPatchesViaZr(chart, patches) && !this._zoomPending) {
4105
- this.renderLane(laneId);
4106
- }
4154
+ tickPanFollow() {
4107
4155
  }
4108
- _buildPatches(chart, target) {
4109
- const patches = [];
4110
- const { paramId, key, segments, line } = target;
4111
- const canMirror = this._canMirror(line, segments);
4112
- segments.forEach((seg) => {
4113
- const knots = seg.knots || [];
4114
- for (let i = 0; i < knots.length - 1; i++) {
4115
- const p1 = knots[i];
4116
- const p2 = knots[i + 1];
4117
- const px1 = this._knotPixel(chart, target, p1);
4118
- const px2 = this._knotPixel(chart, target, p2);
4119
- if (!px1 || !px2) continue;
4120
- patches.push({
4121
- id: `trend_line_${key}_${seg.id}_${i}`,
4122
- shape: { x1: px1[0], y1: px1[1], x2: px2[0], y2: px2[1] }
4123
- });
4124
- if (canMirror) {
4125
- const mx1 = this._knotPixel(chart, target, p1, "mirror");
4126
- const mx2 = this._knotPixel(chart, target, p2, "mirror");
4127
- if (mx1 && mx2) {
4128
- patches.push({
4129
- id: `trend_mirror_${key}_${seg.id}_${i}`,
4130
- shape: { x1: mx1[0], y1: mx1[1], x2: mx2[0], y2: mx2[1] }
4131
- });
4132
- }
4133
- }
4134
- }
4135
- knots.forEach((knot, i) => {
4136
- const px = this._knotPixel(chart, target, knot);
4137
- if (px) {
4138
- patches.push({
4139
- id: `trend_point_${key}_${seg.id}_${i}`,
4140
- shape: { cx: px[0], cy: px[1], r: 4 }
4141
- });
4142
- }
4143
- if (canMirror) {
4144
- const mx = this._knotPixel(chart, target, knot, "mirror");
4145
- if (mx) {
4146
- patches.push({
4147
- id: `trend_mirror_point_${key}_${seg.id}_${i}`,
4148
- shape: { cx: mx[0], cy: mx[1], r: 4 }
4149
- });
4150
- }
4151
- }
4152
- });
4153
- });
4154
- return patches;
4156
+ endPanFollow() {
4155
4157
  }
4156
- _invalidateZrElCache(chart) {
4157
- if (chart && this._zrElCache) this._zrElCache.delete(chart);
4158
+ endFollowView() {
4158
4159
  }
4159
- _indexGraphicZrEls(chart, map) {
4160
- const zr = chart.getZr();
4161
- if (!zr || !zr.storage || typeof zr.storage.traverse !== "function") return;
4162
- zr.storage.traverse((el) => {
4163
- const elId = el.id ?? el.anid ?? el.name ?? el.__ecGraphicId;
4164
- if (elId == null) return;
4165
- map.set(String(elId), el);
4166
- });
4160
+ _patchAllPositions() {
4167
4161
  }
4168
- _findGraphicZrEl(chart, id) {
4169
- if (!chart || id == null) return null;
4170
- let map = this._zrElCache.get(chart);
4171
- if (!map) {
4172
- map = /* @__PURE__ */ new Map();
4173
- this._zrElCache.set(chart, map);
4174
- this._indexGraphicZrEls(chart, map);
4175
- }
4176
- const key = String(id);
4177
- if (map.has(key)) return map.get(key);
4178
- map.clear();
4179
- this._indexGraphicZrEls(chart, map);
4180
- if (map.has(key)) return map.get(key);
4181
- for (const [elId, el] of map) {
4182
- if (elId.endsWith(key)) return el;
4162
+ _isTrendSeriesOpt(series) {
4163
+ if (!series) return false;
4164
+ const name = Array.isArray(series.name) ? series.name[0] : series.name;
4165
+ if (name === "trend") return true;
4166
+ const id = String(series.id || "");
4167
+ return id.indexOf("trend_") === 0;
4168
+ }
4169
+ _findCurveSeriesInOption(option, paramId) {
4170
+ const list = option && option.series || [];
4171
+ for (let i = 0; i < list.length; i++) {
4172
+ const s = list[i];
4173
+ if (!s || this._isTrendSeriesOpt(s)) continue;
4174
+ const type = Array.isArray(s.type) ? s.type[0] : s.type;
4175
+ if (type && type !== "line") continue;
4176
+ const name = Array.isArray(s.name) ? s.name[0] : s.name;
4177
+ if (name === "helper" || name === "log-grid" || name === "推荐值") continue;
4178
+ if (String(s.paramId) === String(paramId)) return s;
4183
4179
  }
4184
4180
  return null;
4185
4181
  }
4186
- _graphicPixel(el) {
4187
- const cx = el.shape && el.shape.cx || 0;
4188
- const cy = el.shape && el.shape.cy || 0;
4189
- return [cx + (el.x || 0), cy + (el.y || 0)];
4182
+ _firstKnotDepth(target) {
4183
+ const segs = target && target.segments || [];
4184
+ for (let i = 0; i < segs.length; i++) {
4185
+ const knot = (segs[i].knots || [])[0];
4186
+ if (knot && Number.isFinite(Number(knot.depth))) return Number(knot.depth);
4187
+ }
4188
+ return 0;
4190
4189
  }
4191
- _applyKnotPixel(chart, target, knot, role, offsetX, offsetY) {
4192
- const data = this._pixelToTrend(chart, target, offsetX, offsetY, true);
4193
- if (!data) return false;
4194
- const range = this._drawValueRange(target, chart, data.depth);
4195
- let depth = snapDepthToEdge(data.depth, range.depthMin, range.depthMax, DEPTH_EDGE_SNAP);
4196
- if (data.value == null) return false;
4197
- const clickX = Math.max(range.valueMin, Math.min(range.valueMax, data.value));
4198
- const all = this._allKnots(target.key);
4199
- const idx = all.indexOf(knot);
4200
- if (idx > 0) depth = Math.max(depth, all[idx - 1].depth);
4201
- if (idx >= 0 && idx < all.length - 1) depth = Math.min(depth, all[idx + 1].depth);
4202
- knot.depth = parseFloat(depth.toFixed(3));
4203
- this._applyPointerValue(chart, target, knot, knot.depth, clickX, role);
4204
- return true;
4190
+ _curveAxisOf(chart, target) {
4191
+ const info = findSeriesForDepth(chart, target.paramId, this._firstKnotDepth(target));
4192
+ return {
4193
+ xAxisIndex: info && info.xAxisIndex != null ? info.xAxisIndex : 0,
4194
+ yAxisIndex: info && info.yAxisIndex != null ? info.yAxisIndex : 0
4195
+ };
4205
4196
  }
4206
- /** 控制点交给 ZR 拖,不再和 dataZoom 抢 DOM mousedown。8.22 */
4207
- _bindPointInteractions(chart, laneId) {
4208
- const targets = this.listDrawableTargets().filter((t) => t.laneId === laneId && t.editable);
4209
- targets.forEach((target) => {
4210
- const canMirror = this._canMirror(target.line, target.segments);
4211
- (target.segments || []).forEach((seg) => {
4212
- (seg.knots || []).forEach((knot, i) => {
4213
- this._bindOnePointEl(
4214
- chart,
4215
- target,
4216
- knot,
4217
- `trend_point_${target.key}_${seg.id}_${i}`,
4218
- "main"
4219
- );
4220
- if (canMirror && knot.mirrorValue != null && Number.isFinite(Number(knot.mirrorValue))) {
4221
- this._bindOnePointEl(
4222
- chart,
4223
- target,
4224
- knot,
4225
- `trend_mirror_point_${target.key}_${seg.id}_${i}`,
4226
- "mirror"
4227
- );
4228
- }
4197
+ _buildOneTrendSeries(id, data, lineStyle, pointStyle, xAxisIndex, yAxisIndex) {
4198
+ return {
4199
+ id,
4200
+ name: "trend",
4201
+ type: "line",
4202
+ data,
4203
+ xAxisIndex,
4204
+ yAxisIndex,
4205
+ clip: true,
4206
+ animation: false,
4207
+ silent: true,
4208
+ large: false,
4209
+ z: TREND_LINE_Z,
4210
+ zlevel: 0,
4211
+ symbol: "circle",
4212
+ symbolSize: 8,
4213
+ showSymbol: true,
4214
+ hoverAnimation: false,
4215
+ legendHoverLink: false,
4216
+ connectNulls: false,
4217
+ tooltip: { show: false },
4218
+ emphasis: { disabled: true },
4219
+ lineStyle: {
4220
+ color: lineStyle.stroke,
4221
+ width: lineStyle.lineWidth,
4222
+ type: lineStyle.lineDash || "solid"
4223
+ },
4224
+ itemStyle: {
4225
+ color: pointStyle.fill,
4226
+ borderColor: pointStyle.stroke,
4227
+ borderWidth: pointStyle.lineWidth
4228
+ }
4229
+ };
4230
+ }
4231
+ _knotPoint(x, depth) {
4232
+ return { value: [x, depth], symbol: "circle", symbolSize: 8 };
4233
+ }
4234
+ _segSeriesData(knots, role) {
4235
+ const raw = [];
4236
+ for (let i = 0; i < (knots || []).length; i++) {
4237
+ const knot = knots[i];
4238
+ const x = this._knotDrawX(knot, role);
4239
+ const depth = Number(knot && knot.depth);
4240
+ if (x == null || !Number.isFinite(depth)) continue;
4241
+ raw.push([x, depth]);
4242
+ }
4243
+ if (raw.length < 2) {
4244
+ return raw.map((p) => this._knotPoint(p[0], p[1]));
4245
+ }
4246
+ const data = [];
4247
+ for (let i = 0; i < raw.length - 1; i++) {
4248
+ const a = raw[i];
4249
+ const b = raw[i + 1];
4250
+ data.push(this._knotPoint(a[0], a[1]));
4251
+ const span = Math.abs(b[1] - a[1]);
4252
+ const n = Math.min(160, Math.max(0, Math.floor(span / 4)));
4253
+ for (let k = 1; k < n; k++) {
4254
+ const t = k / n;
4255
+ data.push({
4256
+ value: [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t],
4257
+ symbol: "none"
4229
4258
  });
4230
- });
4259
+ }
4260
+ }
4261
+ const last = raw[raw.length - 1];
4262
+ data.push(this._knotPoint(last[0], last[1]));
4263
+ return data;
4264
+ }
4265
+ /** 曲线仍 weakFilter;趋势线点太稀,滤掉窗外控制点后穿过视窗的线会断。8.25 */
4266
+ applyDataZoomSkipTrend(option) {
4267
+ if (!option) return;
4268
+ const raw = option.dataZoom;
4269
+ if (!raw) return;
4270
+ const list = Array.isArray(raw) ? raw : [raw];
4271
+ const indexes = [];
4272
+ const series = option.series || [];
4273
+ for (let i = 0; i < series.length; i++) {
4274
+ if (!this._isTrendSeriesOpt(series[i])) indexes.push(i);
4275
+ }
4276
+ if (!indexes.length) return;
4277
+ list.forEach((dz) => {
4278
+ if (dz) dz.seriesIndex = indexes;
4231
4279
  });
4232
4280
  }
4233
- _bindOnePointEl(chart, target, knot, id, role) {
4234
- const el = this._findGraphicZrEl(chart, id);
4235
- if (!el) return;
4236
- el.silent = false;
4237
- el.draggable = true;
4238
- el.cursor = "grab";
4239
- if (el.off && el.__trendDragBound) {
4240
- el.off("mousedown", el.__trendOnDown);
4241
- el.off("drag", el.__trendOnDrag);
4242
- el.off("dragend", el.__trendOnEnd);
4281
+ dataZoomSeriesIndex(chart) {
4282
+ if (!chart || chart.isDisposed()) return null;
4283
+ try {
4284
+ const list = chart.getModel().getSeries() || [];
4285
+ const indexes = [];
4286
+ for (let i = 0; i < list.length; i++) {
4287
+ const sm = list[i];
4288
+ const opt = sm && sm.option || {};
4289
+ const name = Array.isArray(sm.name) ? sm.name[0] : sm.name;
4290
+ if (this._isTrendSeriesOpt({ name, id: opt.id })) continue;
4291
+ if (sm.seriesIndex != null) indexes.push(sm.seriesIndex);
4292
+ }
4293
+ return indexes.length ? indexes : null;
4294
+ } catch (e) {
4295
+ return null;
4243
4296
  }
4244
- const onDown = (e) => {
4245
- this._clearClickTimer();
4246
- this.activeKey = target.key;
4247
- this.dragSession = { chart, target, knot, role, graphic: true };
4248
- this._setPanLocked(true);
4249
- if (e && typeof e.stop === "function") e.stop();
4250
- if (e && e.event) {
4251
- e.event.preventDefault();
4252
- e.event.stopPropagation();
4253
- }
4254
- };
4255
- const onDrag = () => {
4256
- if (!this.dragSession) {
4257
- this.dragSession = { chart, target, knot, role, graphic: true };
4258
- this._setPanLocked(true);
4259
- }
4260
- const [x, y] = this._graphicPixel(el);
4261
- if (!this._applyKnotPixel(chart, target, knot, role, x, y)) return;
4262
- el.x = 0;
4263
- el.y = 0;
4264
- this._patchLanePositions(target.laneId);
4265
- };
4266
- const onEnd = () => {
4267
- if (!this.dragSession) return;
4268
- const sessionTarget = this.dragSession.target;
4269
- this.dragSession = null;
4270
- this._setPanLocked(false, true);
4271
- this._ignoreClickUntil = Date.now() + 300;
4272
- this._emitChange(sessionTarget, { redraw: false });
4273
- };
4274
- el.__trendOnDown = onDown;
4275
- el.__trendOnDrag = onDrag;
4276
- el.__trendOnEnd = onEnd;
4277
- el.__trendDragBound = true;
4278
- el.on("mousedown", onDown);
4279
- el.on("drag", onDrag);
4280
- el.on("dragend", onEnd);
4281
4297
  }
4282
- _applyPatchesViaZr(chart, patches) {
4283
- if (!patches.length) return false;
4284
- let applied = 0;
4285
- patches.forEach(({ id, shape }) => {
4286
- const el = this._findGraphicZrEl(chart, id);
4287
- if (!el || typeof el.setShape !== "function") return;
4288
- el.setShape(shape);
4289
- applied += 1;
4298
+ /** 笛卡尔 [曲线值, 井深];对数道 X 已是 lg。和曲线共用轴,dataZoom 同一帧带走。8.25 */
4299
+ _buildTrendSeries(target, xAxisIndex, yAxisIndex) {
4300
+ const list = [];
4301
+ const { key, segments, line } = target;
4302
+ const canMirror = this._canMirror(line, segments);
4303
+ const doubleLine = this._useGammaStyle(line, segments);
4304
+ const mainLineStyle = doubleLine ? TREND_DOUBLE_LINE_STYLE : TREND_LINE_STYLE;
4305
+ const mainPointStyle = doubleLine ? TREND_DOUBLE_POINT_STYLE : TREND_POINT_STYLE;
4306
+ (segments || []).forEach((seg) => {
4307
+ const knots = seg.knots || [];
4308
+ if (!knots.length) return;
4309
+ list.push(
4310
+ this._buildOneTrendSeries(
4311
+ `trend_${key}_${seg.id}`,
4312
+ this._segSeriesData(knots, "main"),
4313
+ mainLineStyle,
4314
+ mainPointStyle,
4315
+ xAxisIndex,
4316
+ yAxisIndex
4317
+ )
4318
+ );
4319
+ if (!canMirror) return;
4320
+ const mirror = this._segSeriesData(knots, "mirror");
4321
+ if (!mirror.length) return;
4322
+ list.push(
4323
+ this._buildOneTrendSeries(
4324
+ `trend_mirror_${key}_${seg.id}`,
4325
+ mirror,
4326
+ TREND_MIRROR_LINE_STYLE,
4327
+ TREND_MIRROR_POINT_STYLE,
4328
+ xAxisIndex,
4329
+ yAxisIndex
4330
+ )
4331
+ );
4290
4332
  });
4291
- const zr = chart.getZr && chart.getZr();
4292
- if (applied > 0 && zr && typeof zr.refresh === "function") zr.refresh();
4293
- return applied > 0;
4333
+ return list;
4294
4334
  }
4295
- // 只 merge 新图形,不动已有线。8.24
4296
- _addGraphicElements(chart, elements) {
4297
- if (!elements.length) return;
4298
- this._invalidateZrElCache(chart);
4299
- chart.setOption(
4300
- {
4301
- graphic: elements.map((el) => ({ ...el, $action: "merge" }))
4302
- },
4303
- { silent: true, lazyUpdate: false }
4304
- );
4335
+ /** 刷 option 时给该道补上趋势 series,所有有控制点的泳道都会走。8.25 */
4336
+ appendSeriesToOption(option, lane) {
4337
+ if (!option || !lane) return option;
4338
+ if (!Array.isArray(option.series)) option.series = [];
4339
+ option.series = option.series.filter((s) => !this._isTrendSeriesOpt(s));
4340
+ const targets = this.listDrawableTargets().filter((t) => t.laneId === lane.laneId);
4341
+ const added = [];
4342
+ targets.forEach((target) => {
4343
+ const curve = this._findCurveSeriesInOption(option, target.paramId);
4344
+ if (!curve) return;
4345
+ const xAxisIndex = curve.xAxisIndex == null ? 0 : Number(curve.xAxisIndex);
4346
+ const yAxisIndex = curve.yAxisIndex == null ? 0 : Number(curve.yAxisIndex);
4347
+ added.push(...this._buildTrendSeries(target, xAxisIndex, yAxisIndex));
4348
+ });
4349
+ option.series.push(...added);
4350
+ this._seriesIds[lane.laneId] = added.map((s) => s.id);
4351
+ this.applyDataZoomSkipTrend(option);
4352
+ return option;
4305
4353
  }
4306
- // 按 id 删除,不 replaceMerge 整层 graphic。8.24
4307
- _removeGraphicIds(chart, ids) {
4308
- if (!ids.length) return;
4309
- this._invalidateZrElCache(chart);
4310
- chart.setOption(
4311
- {
4312
- graphic: ids.map((id) => ({ id, $action: "remove" }))
4313
- },
4314
- { silent: true, lazyUpdate: false }
4315
- );
4354
+ _clearLeftoverGraphic(chart) {
4355
+ if (!chart || chart.isDisposed() || this._graphicCleared.has(chart)) return;
4356
+ this._graphicCleared.add(chart);
4357
+ try {
4358
+ chart.setOption({ graphic: [] }, { replaceMerge: ["graphic"], silent: true, lazyUpdate: false });
4359
+ } catch (e) {
4360
+ }
4316
4361
  }
4317
4362
  renderAll(forceReplace = false) {
4318
4363
  const lanes = this.host.currentTemplate && this.host.currentTemplate.lanes || [];
4319
4364
  lanes.forEach((lane) => this.renderLane(lane.laneId, forceReplace));
4320
4365
  }
4321
- /** 已有线只改 ZR 形状;新点/新线段单独 merge 进去,绝不整层 graphic 替换。8.24 */
4366
+ /** 用笛卡尔 series 画趋势线;平移/缩放交给 dataZoom,不跟像素。8.25 */
4322
4367
  renderLane(laneId, forceReplace = false) {
4368
+ if (!this._alive()) return;
4323
4369
  const chart = this.getChart(laneId);
4324
4370
  if (!chart) return;
4371
+ this._clearLeftoverGraphic(chart);
4325
4372
  const targets = this.listDrawableTargets().filter((t) => t.laneId === laneId);
4326
- const elements = [];
4373
+ const next = [];
4327
4374
  targets.forEach((target) => {
4328
- elements.push(...this._buildElements(chart, target));
4375
+ const axis = this._curveAxisOf(chart, target);
4376
+ next.push(...this._buildTrendSeries(target, axis.xAxisIndex, axis.yAxisIndex));
4329
4377
  });
4330
- const hasKnots = targets.some((t) => (t.segments || []).some((s) => (s.knots || []).length));
4331
- if (hasKnots && !elements.length) return;
4332
- const nextIds = elements.map((el) => el.id);
4333
- const prevIds = this._graphicIds[laneId] || [];
4334
- const prevSet = new Set(prevIds);
4378
+ const nextIds = next.map((s) => s.id);
4335
4379
  const nextSet = new Set(nextIds);
4336
- const added = elements.filter((el) => !prevSet.has(el.id));
4337
- const kept = elements.filter((el) => prevSet.has(el.id));
4338
- const removedIds = prevIds.filter((id) => !nextSet.has(id));
4339
- if (forceReplace) {
4340
- this._removeGraphicIds(chart, prevIds);
4341
- this._addGraphicElements(chart, elements);
4342
- } else {
4343
- if (kept.length) {
4344
- this._applyPatchesViaZr(
4345
- chart,
4346
- kept.map((el) => ({ id: el.id, shape: el.shape }))
4347
- );
4380
+ const liveIds = this._chartTrendSeriesIds(chart);
4381
+ const prevIds = [.../* @__PURE__ */ new Set([...this._seriesIds[laneId] || [], ...liveIds])];
4382
+ const replace = !!(forceReplace && !this.dragSession);
4383
+ const patch = next.map((s) => ({
4384
+ ...s,
4385
+ data: s.data || [],
4386
+ $action: replace ? "replace" : "merge"
4387
+ }));
4388
+ prevIds.forEach((id) => {
4389
+ if (!nextSet.has(id)) patch.push({ id, $action: "remove" });
4390
+ });
4391
+ if (patch.length) {
4392
+ try {
4393
+ chart.setOption({ series: patch }, { silent: true, lazyUpdate: false });
4394
+ } catch (e) {
4348
4395
  }
4349
- if (removedIds.length) this._removeGraphicIds(chart, removedIds);
4350
- if (added.length) this._addGraphicElements(chart, added);
4351
4396
  }
4352
- this._graphicIds[laneId] = nextIds;
4353
- this._bindPointInteractions(chart, laneId);
4354
- this.updateCursor(laneId);
4355
- }
4356
- _buildElements(chart, target) {
4357
- const elements = [];
4358
- const { paramId, key, editable, segments, line } = target;
4359
- const canMirror = this._canMirror(line, segments);
4360
- const doubleLine = this._useGammaStyle(line, segments);
4361
- const mainLineStyle = doubleLine ? TREND_DOUBLE_LINE_STYLE : TREND_LINE_STYLE;
4362
- const mainPointStyle = doubleLine ? TREND_DOUBLE_POINT_STYLE : TREND_POINT_STYLE;
4363
- segments.forEach((seg) => {
4364
- const knots = seg.knots || [];
4365
- for (let i = 0; i < knots.length - 1; i++) {
4366
- const p1 = knots[i];
4367
- const p2 = knots[i + 1];
4368
- const px1 = this._knotPixel(chart, target, p1);
4369
- const px2 = this._knotPixel(chart, target, p2);
4370
- if (!px1 || !px2) continue;
4371
- elements.push({
4372
- type: "line",
4373
- id: `trend_line_${key}_${seg.id}_${i}`,
4374
- shape: { x1: px1[0], y1: px1[1], x2: px2[0], y2: px2[1] },
4375
- style: { ...mainLineStyle },
4376
- z: TREND_LINE_Z,
4377
- silent: true
4378
- });
4379
- if (canMirror) {
4380
- const mx1 = this._knotPixel(chart, target, p1, "mirror");
4381
- const mx2 = this._knotPixel(chart, target, p2, "mirror");
4382
- if (mx1 && mx2) {
4383
- elements.push({
4384
- type: "line",
4385
- id: `trend_mirror_${key}_${seg.id}_${i}`,
4386
- shape: { x1: mx1[0], y1: mx1[1], x2: mx2[0], y2: mx2[1] },
4387
- style: { ...TREND_MIRROR_LINE_STYLE },
4388
- z: TREND_LINE_Z - 1,
4389
- silent: true
4390
- });
4397
+ this._seriesIds[laneId] = nextIds;
4398
+ if (!this.dragSession) this._syncCachedOption(laneId);
4399
+ if (this.dragSession) {
4400
+ this.updateCursor(laneId);
4401
+ return;
4402
+ }
4403
+ const idsChanged = nextIds.length !== prevIds.length || nextIds.some((id, i) => id !== prevIds[i]);
4404
+ if (idsChanged) {
4405
+ const skip = this.dataZoomSeriesIndex(chart);
4406
+ if (skip && skip.length) {
4407
+ try {
4408
+ const dz = chart.getModel().getComponent("dataZoom", 0);
4409
+ const opt = dz && dz.option;
4410
+ const next2 = { seriesIndex: skip };
4411
+ if (opt) {
4412
+ if (opt.start != null) next2.start = opt.start;
4413
+ if (opt.end != null) next2.end = opt.end;
4414
+ if (opt.startValue != null) next2.startValue = opt.startValue;
4415
+ if (opt.endValue != null) next2.endValue = opt.endValue;
4391
4416
  }
4417
+ chart.setOption({ dataZoom: [next2] }, { silent: true, lazyUpdate: true });
4418
+ } catch (e) {
4392
4419
  }
4393
4420
  }
4394
- knots.forEach((knot, i) => {
4395
- const px = this._knotPixel(chart, target, knot);
4396
- if (!px) return;
4397
- elements.push({
4398
- type: "circle",
4399
- id: `trend_point_${key}_${seg.id}_${i}`,
4400
- shape: { cx: px[0], cy: px[1], r: 4 },
4401
- style: { ...mainPointStyle },
4402
- z: TREND_POINT_Z,
4403
- zlevel: 10,
4404
- silent: !editable,
4405
- draggable: !!editable,
4406
- cursor: editable ? "grab" : "default"
4407
- });
4408
- if (canMirror) {
4409
- const mx = this._knotPixel(chart, target, knot, "mirror");
4410
- if (mx) {
4411
- elements.push({
4412
- type: "circle",
4413
- id: `trend_mirror_point_${key}_${seg.id}_${i}`,
4414
- shape: { cx: mx[0], cy: mx[1], r: 4 },
4415
- style: { ...TREND_MIRROR_POINT_STYLE },
4416
- z: TREND_POINT_Z,
4417
- zlevel: 10,
4418
- silent: !editable,
4419
- draggable: !!editable,
4420
- cursor: editable ? "grab" : "default"
4421
- });
4422
- }
4423
- }
4424
- });
4425
- });
4426
- return elements;
4421
+ }
4422
+ this.updateCursor(laneId);
4423
+ }
4424
+ /** 把当前 store 写回 allOptions,避免随后 notMerge 用旧趋势 series 把点盖回去。8.25 */
4425
+ _syncCachedOption(laneId) {
4426
+ const host = this.host;
4427
+ if (!host || typeof host.getOptionsByLineId !== "function") return null;
4428
+ const option = host.getOptionsByLineId(laneId);
4429
+ if (!option || !Array.isArray(option.series)) return null;
4430
+ const lanes = host.currentTemplate && host.currentTemplate.lanes || [];
4431
+ const lane = lanes.find((l) => l.laneId === laneId);
4432
+ if (!lane) return null;
4433
+ this.appendSeriesToOption(option, lane);
4434
+ return option;
4435
+ }
4436
+ /** 删点后走整份 option 替换,ECharts 才会丢掉缩短后的旧 data。8.25 */
4437
+ _flushLane(laneId) {
4438
+ const option = this._syncCachedOption(laneId);
4439
+ if (option && this.host && typeof this.host.flushLaneChartOption === "function") {
4440
+ option.__kdTrendSynced = true;
4441
+ try {
4442
+ this.host.flushLaneChartOption(laneId, option);
4443
+ } finally {
4444
+ delete option.__kdTrendSynced;
4445
+ }
4446
+ this.updateCursor(laneId);
4447
+ return;
4448
+ }
4449
+ this.renderLane(laneId, true);
4450
+ }
4451
+ _chartTrendSeriesIds(chart) {
4452
+ if (!chart || chart.isDisposed()) return [];
4453
+ try {
4454
+ const list = chart.getModel().getSeries() || [];
4455
+ const ids = [];
4456
+ for (let i = 0; i < list.length; i++) {
4457
+ const sm = list[i];
4458
+ const opt = sm && sm.option || {};
4459
+ const name = Array.isArray(sm.name) ? sm.name[0] : sm.name;
4460
+ const id = opt.id || sm.id;
4461
+ if (this._isTrendSeriesOpt({ name, id }) && id) ids.push(id);
4462
+ }
4463
+ return ids;
4464
+ } catch (e) {
4465
+ return [];
4466
+ }
4427
4467
  }
4428
4468
  _findKnotAtPointer(chart, target, offsetX, offsetY, radius = TREND_HIT_RADIUS) {
4429
4469
  let best = null;
4430
4470
  let bestDist = radius;
4431
4471
  const canMirror = this._canMirror(target.line, target.segments);
4432
4472
  for (const seg of target.segments || []) {
4433
- for (const knot of seg.knots || []) {
4473
+ const knots = seg.knots || [];
4474
+ for (let i = 0; i < knots.length; i++) {
4475
+ const knot = knots[i];
4434
4476
  const px = this._knotPixel(chart, target, knot);
4435
4477
  if (px) {
4436
4478
  const dist = Math.hypot(px[0] - offsetX, px[1] - offsetY);
4437
4479
  if (dist <= bestDist) {
4438
- best = { knot, seg, role: "main" };
4480
+ best = { knot, seg, knotIndex: i, role: "main" };
4439
4481
  bestDist = dist;
4440
4482
  }
4441
4483
  }
@@ -4444,7 +4486,7 @@ class TrendLineManager {
4444
4486
  if (!mx) continue;
4445
4487
  const mirrorDist = Math.hypot(mx[0] - offsetX, mx[1] - offsetY);
4446
4488
  if (mirrorDist <= bestDist) {
4447
- best = { knot, seg, role: "mirror" };
4489
+ best = { knot, seg, knotIndex: i, role: "mirror" };
4448
4490
  bestDist = mirrorDist;
4449
4491
  }
4450
4492
  }
@@ -4453,6 +4495,11 @@ class TrendLineManager {
4453
4495
  }
4454
4496
  /** 单击空白处加点;延迟执行以便和双击断段区分。命中已有点则不加点。返回 true 表示已消费本次点击。8.21 */
4455
4497
  handleClick(e, laneId) {
4498
+ if (!this._alive()) return false;
4499
+ if (this.dragSession) {
4500
+ this._endDrag(true);
4501
+ return true;
4502
+ }
4456
4503
  const target = this.getTargetByLane(laneId);
4457
4504
  if (!target || !target.editable) return false;
4458
4505
  if (Date.now() < this._ignoreClickUntil) return true;
@@ -4465,6 +4512,7 @@ class TrendLineManager {
4465
4512
  this._clearClickTimer();
4466
4513
  this.clickTimer = setTimeout(() => {
4467
4514
  this.clickTimer = null;
4515
+ if (!this._alive()) return;
4468
4516
  this._addPoint(chart, target, offsetX, offsetY);
4469
4517
  }, TREND_CLICK_DELAY_MS);
4470
4518
  return true;
@@ -4486,7 +4534,7 @@ class TrendLineManager {
4486
4534
  }
4487
4535
  return true;
4488
4536
  }
4489
- /** 右键命中控制点时弹出「删除控制点」;先结束可能误触发的拖拽并重绘,避免线段被 replaceMerge 丢掉。8.21 */
4537
+ /** 右键命中控制点时弹出「删除控制点」。打开菜单时不要 setOption,否则会吞掉菜单点击。8.21 */
4490
4538
  handleContextMenu(e, laneId) {
4491
4539
  const target = this.getTargetByLane(laneId);
4492
4540
  if (!target || !target.editable) return false;
@@ -4500,14 +4548,23 @@ class TrendLineManager {
4500
4548
  this.host.showTrendKnotContextMenu(e, {
4501
4549
  key: target.key,
4502
4550
  knot: hit.knot,
4551
+ segId: hit.seg && hit.seg.id,
4552
+ knotIndex: hit.knotIndex,
4553
+ depth: hit.knot.depth,
4554
+ value: hit.knot.value,
4555
+ plotValue: hit.knot.plotValue,
4503
4556
  laneId
4504
4557
  });
4505
- this.renderLane(laneId);
4506
4558
  return true;
4507
4559
  }
4508
4560
  /** 指针靠近控制点时先停地应力平移,按下时才来得及拖点。8.22 */
4509
4561
  onZrHover(e, laneId) {
4510
- if (this.dragSession) return;
4562
+ if (!this._alive()) return;
4563
+ if (this.dragSession) {
4564
+ e.__ecRoamConsumed = true;
4565
+ if (typeof e.stop === "function") e.stop();
4566
+ return;
4567
+ }
4511
4568
  if (this.host && typeof this.host.isChartBusy === "function" && this.host.isChartBusy()) {
4512
4569
  return;
4513
4570
  }
@@ -4530,38 +4587,32 @@ class TrendLineManager {
4530
4587
  this.host.setInsideDataZoomPanEnabled(!locked);
4531
4588
  }
4532
4589
  }
4533
- /** zr 里拦住 dataZoom 的按下,否则图会跟着点一起拖。8.22 */
4590
+ /** 左键拖点与右键删点同一套命中(zr offset + _findKnotAtPointer)。8.25 */
4534
4591
  onZrMouseDown(e, laneId) {
4592
+ if (!this._alive()) return;
4535
4593
  if (e.which != null && e.which !== 1 && e.event && e.event.button !== 0) return;
4594
+ if (this.dragSession) {
4595
+ e.__ecRoamConsumed = true;
4596
+ if (typeof e.stop === "function") e.stop();
4597
+ this._endDrag(true);
4598
+ return;
4599
+ }
4536
4600
  const target = this.getTargetByLane(laneId);
4537
4601
  const chart = this.getChart(laneId);
4538
- if (!target || !target.editable || !chart || this.dragSession) return;
4602
+ if (!target || !target.editable || !chart) return;
4539
4603
  const { offsetX, offsetY } = eventOffsetInChart(chart, e);
4540
- if (!this._findKnotAtPointer(chart, target, offsetX, offsetY)) return;
4604
+ const hit = this._findKnotAtPointer(chart, target, offsetX, offsetY);
4605
+ if (!hit) return;
4606
+ e.__ecRoamConsumed = true;
4541
4607
  if (typeof e.stop === "function") e.stop();
4542
4608
  e.cancelBubble = true;
4543
4609
  if (e.event) {
4544
4610
  e.event.preventDefault();
4545
4611
  e.event.stopPropagation();
4546
4612
  }
4547
- }
4548
- /** 仅左键拖拽控制点。右键 mousedown 会先于 contextmenu,绝不能当拖拽处理。8.21 */
4549
- onMouseDown(nativeEvent, laneId) {
4550
- if (nativeEvent.button !== 0) return;
4551
- const target = this.getTargetByLane(laneId);
4552
- if (!target || !target.editable || this.dragSession) return;
4553
- const chart = this.getChart(laneId);
4554
- if (!chart) return;
4555
- const { offsetX, offsetY } = eventOffsetInChart(chart, nativeEvent);
4556
- const hit = this._findKnotAtPointer(chart, target, offsetX, offsetY);
4557
- if (!hit) return;
4558
- nativeEvent.stopPropagation();
4559
- nativeEvent.stopImmediatePropagation();
4560
- nativeEvent.preventDefault();
4561
4613
  this._clearClickTimer();
4562
4614
  this.activeKey = target.key;
4563
- this._setPanLocked(true);
4564
- this._startDrag(chart, target, hit.knot, nativeEvent, hit.role);
4615
+ this._startDrag(chart, target, hit.knot, e, hit.role);
4565
4616
  }
4566
4617
  /** 沿深度增大方向追加控制点;曲线值限制在该线 min/max 内。8.21 */
4567
4618
  _addPoint(chart, target, offsetX, offsetY) {
@@ -4657,6 +4708,7 @@ class TrendLineManager {
4657
4708
  }
4658
4709
  /** 清空全部趋势线控制点并重绘。8.22 */
4659
4710
  resetAllTrendLines() {
4711
+ if (this.dragSession) this._endDrag(false);
4660
4712
  const prev = this.listDrawableTargets().filter(
4661
4713
  (t) => (t.segments || []).some((s) => (s.knots || []).length)
4662
4714
  );
@@ -4670,25 +4722,39 @@ class TrendLineManager {
4670
4722
  segments: []
4671
4723
  });
4672
4724
  });
4673
- this.renderAll(true);
4725
+ this._flushTrendLanes(prev);
4674
4726
  }
4675
4727
  /** 清空指定曲线的控制点;不传 key 则清当前编辑曲线。8.21 */
4676
4728
  resetTrendLine(key) {
4677
4729
  const target = key ? this.listDrawableTargets().find((t) => t.key === key) : this.getActiveTarget() || this.listDrawableTargets().find((t) => t.editable);
4678
4730
  if (!target) return;
4731
+ if (this.dragSession) this._endDrag(false);
4679
4732
  this.store[target.key] = { segments: [] };
4680
- this.renderLane(target.laneId, true);
4733
+ this._flushLane(target.laneId);
4681
4734
  this._emitChange(target, { redraw: false });
4682
4735
  }
4736
+ /** 把 store 已空的泳道整份 option 刷进图,线段才会从画面上消失。8.25 */
4737
+ _flushTrendLanes(prev) {
4738
+ const laneIds = /* @__PURE__ */ new Set();
4739
+ (prev || []).forEach((t) => t.laneId && laneIds.add(t.laneId));
4740
+ Object.keys(this._seriesIds).forEach((id) => laneIds.add(id));
4741
+ this.binds.forEach((_, laneId) => {
4742
+ const chart = this.getChart(laneId);
4743
+ if (chart && this._chartTrendSeriesIds(chart).length) laneIds.add(laneId);
4744
+ });
4745
+ if (!laneIds.size) {
4746
+ this.renderAll(true);
4747
+ return;
4748
+ }
4749
+ laneIds.forEach((laneId) => this._flushLane(laneId));
4750
+ }
4683
4751
  /** 删除一个控制点。段内只剩 0~1 个点时整段丢掉(画不出线)。8.21 */
4684
4752
  deleteKnot(payload) {
4685
- if (!payload || !payload.key || !payload.knot) return;
4753
+ if (!payload || !payload.key) return;
4686
4754
  const bucket = this.store[payload.key];
4687
4755
  if (!bucket) return;
4688
4756
  bucket.segments = bucket.segments.map((seg) => {
4689
- const idx = seg.knots.findIndex(
4690
- (k) => k === payload.knot || Math.abs(k.depth - payload.knot.depth) < 0.05 && Math.abs(k.value - payload.knot.value) < 0.01
4691
- );
4757
+ const idx = this._findKnotIndex(seg, payload);
4692
4758
  if (idx < 0) return seg;
4693
4759
  const knots = seg.knots.filter((_, i) => i !== idx);
4694
4760
  if (knots.length <= 1) return { ...seg, knots: [] };
@@ -4696,9 +4762,32 @@ class TrendLineManager {
4696
4762
  }).filter((s) => s.knots.length > 0);
4697
4763
  const target = this.listDrawableTargets().find((t) => t.key === payload.key);
4698
4764
  const laneId = target && target.laneId || payload.laneId;
4699
- if (laneId) this.renderLane(laneId, true);
4765
+ if (laneId) this._flushLane(laneId);
4700
4766
  if (target) this._emitChange(target, { redraw: false });
4701
4767
  }
4768
+ _findKnotIndex(seg, payload) {
4769
+ const knots = seg && seg.knots || [];
4770
+ if (!knots.length || !payload) return -1;
4771
+ if (payload.knot) {
4772
+ const byRef = knots.indexOf(payload.knot);
4773
+ if (byRef >= 0) return byRef;
4774
+ }
4775
+ if (payload.segId && seg.id !== payload.segId) return -1;
4776
+ if (Number.isInteger(payload.knotIndex) && payload.knotIndex >= 0 && payload.knotIndex < knots.length) {
4777
+ return payload.knotIndex;
4778
+ }
4779
+ const depth = payload.knot != null ? payload.knot.depth : payload.depth;
4780
+ const value = payload.knot != null ? payload.knot.value : payload.value;
4781
+ const plotValue = payload.knot != null ? payload.knot.plotValue : payload.plotValue;
4782
+ return knots.findIndex((k) => {
4783
+ if (depth == null || Math.abs(Number(k.depth) - Number(depth)) >= 0.05) return false;
4784
+ if (value != null && Math.abs(Number(k.value) - Number(value)) < 0.01) return true;
4785
+ if (plotValue != null && k.plotValue != null && Math.abs(Number(k.plotValue) - Number(plotValue)) < 0.01) {
4786
+ return true;
4787
+ }
4788
+ return value == null;
4789
+ });
4790
+ }
4702
4791
  /** 返回 geosteering trendLinePoint 格式的线段数组;8137 带上 isDouble / calcType。8.22 */
4703
4792
  getTrendLineData(mainId = "demo") {
4704
4793
  const grouped = {};
@@ -4722,12 +4811,14 @@ class TrendLineManager {
4722
4811
  }
4723
4812
  const segs = this._prepareEchoSegments(paramId, segments);
4724
4813
  this.store[key] = { segments: segs };
4725
- if (found.line) found.line.trendSegments = segs;
4726
4814
  this.renderLane(found.laneId);
4727
4815
  }
4728
4816
  /** 把 geosteering 线段数组灌进对应曲线并画出叠加层。8.21 */
4729
4817
  setTrendLineList(lineList, overwrite = true) {
4730
- if (!Array.isArray(lineList) || !lineList.length) return;
4818
+ if (!Array.isArray(lineList) || !lineList.length) {
4819
+ this.resetAllTrendLines();
4820
+ return;
4821
+ }
4731
4822
  const grouped = fromTrendLineSavePayload(lineList);
4732
4823
  Object.entries(grouped).forEach(([paramId, segments]) => {
4733
4824
  this.setTrendLineData(paramId, segments, overwrite);
@@ -4763,33 +4854,58 @@ class TrendLineManager {
4763
4854
  const last = target.segments[target.segments.length - 1];
4764
4855
  return !!(last && !last._closed && last.knots && last.knots.length);
4765
4856
  }
4766
- /** 拖拽开始:在 window 上听 mousemove/mouseup,避免鼠标移出图表丢事件。8.21 */
4857
+ /** 拖拽开始:按下不刷图,避免 setOption 把这次按住掐掉。8.25 */
4767
4858
  _startDrag(chart, target, knot, nativeEvent, role = "main") {
4859
+ this._unbindDragWindow();
4768
4860
  const onMove = (ev) => this._onDragMove(ev);
4769
- const onUp = () => this._endDrag(true);
4861
+ const onUp = (ev) => {
4862
+ if (ev && ev.button != null && ev.button !== 0) return;
4863
+ this._endDrag(true);
4864
+ };
4865
+ const startStamp = nativeEvent && nativeEvent.timeStamp;
4866
+ const onClickEnd = (ev) => {
4867
+ if (ev && ev.button != null && ev.button !== 0) return;
4868
+ if (startStamp != null && ev.timeStamp === startStamp) return;
4869
+ this._endDrag(true);
4870
+ };
4871
+ const { offsetX, offsetY } = eventOffsetInChart(chart, nativeEvent);
4872
+ const px = this._knotPixel(chart, target, knot, role);
4770
4873
  this.dragSession = {
4771
4874
  chart,
4772
4875
  target,
4773
4876
  knot,
4774
4877
  role,
4775
4878
  onMove,
4776
- onUp
4879
+ onUp,
4880
+ onClickEnd,
4881
+ grabDX: px ? px[0] - offsetX : 0,
4882
+ grabDY: px ? px[1] - offsetY : 0
4777
4883
  };
4778
- if (this.host && typeof this.host.setInsideDataZoomPanEnabled === "function") {
4779
- this.host.setInsideDataZoomPanEnabled(false);
4780
- }
4781
4884
  window.addEventListener("mousemove", onMove);
4782
4885
  window.addEventListener("mouseup", onUp);
4886
+ window.addEventListener("mousedown", onClickEnd, true);
4783
4887
  this.updateCursor(target.laneId);
4784
- this._onDragMove(nativeEvent);
4785
4888
  }
4786
- /** 拖拽时夹在相邻点之间,不能越过前后控制点。8.21 */
4889
+ _unbindDragWindow() {
4890
+ const session = this.dragSession;
4891
+ if (!session) return;
4892
+ window.removeEventListener("mousemove", session.onMove);
4893
+ window.removeEventListener("mouseup", session.onUp);
4894
+ if (session.onClickEnd) {
4895
+ window.removeEventListener("mousedown", session.onClickEnd, true);
4896
+ }
4897
+ this.dragSession = null;
4898
+ }
4899
+ /** 拖拽时点跟着鼠标走;夹在相邻点之间,不能越过前后控制点。8.21 */
4787
4900
  _onDragMove(nativeEvent) {
4901
+ if (!this._alive()) return;
4788
4902
  const session = this.dragSession;
4789
4903
  if (!session) return;
4790
4904
  const { chart, target, knot, role } = session;
4791
- const { offsetX, offsetY } = eventOffsetInChart(chart, nativeEvent);
4792
- const data = this._pixelToTrend(chart, target, offsetX, offsetY, true);
4905
+ const pos = eventOffsetInChart(chart, nativeEvent);
4906
+ const offsetX = pos.offsetX + (session.grabDX || 0);
4907
+ const offsetY = pos.offsetY + (session.grabDY || 0);
4908
+ const data = this._pixelToDragTrend(chart, target, offsetX, offsetY);
4793
4909
  if (!data) return;
4794
4910
  const range = this._drawValueRange(target, chart, data.depth);
4795
4911
  let depth = snapDepthToEdge(data.depth, range.depthMin, range.depthMax, DEPTH_EDGE_SNAP);
@@ -4803,19 +4919,33 @@ class TrendLineManager {
4803
4919
  this._applyPointerValue(chart, target, knot, knot.depth, clickX, role);
4804
4920
  this.renderLane(target.laneId);
4805
4921
  }
4922
+ /** 拖点用轴 scale 反算,和画点同一套,避免点落在鼠标旁边。8.25 */
4923
+ _pixelToDragTrend(chart, target, offsetX, offsetY) {
4924
+ const finder = findSeriesForDepth(chart, target.paramId, this._firstKnotDepth(target));
4925
+ const fromAxis = axisPairFromPixel(
4926
+ chart,
4927
+ finder.xAxisIndex,
4928
+ finder.yAxisIndex,
4929
+ offsetX,
4930
+ offsetY
4931
+ );
4932
+ if (fromAxis) return fromAxis;
4933
+ return this._pixelToTrend(chart, target, offsetX, offsetY, true);
4934
+ }
4806
4935
  /** emit=false 只结束拖拽不抛事件(右键菜单用)。8.21 */
4807
4936
  _endDrag(emit) {
4808
4937
  const session = this.dragSession;
4809
4938
  if (!session) return;
4810
- window.removeEventListener("mousemove", session.onMove);
4811
- window.removeEventListener("mouseup", session.onUp);
4812
- this.dragSession = null;
4939
+ this._unbindDragWindow();
4940
+ if (!this._alive()) return;
4813
4941
  this._panLockedByHover = false;
4814
4942
  if (this.host && typeof this.host.setInsideDataZoomPanEnabled === "function") {
4815
4943
  this.host.setInsideDataZoomPanEnabled(true);
4816
4944
  }
4817
4945
  this._ignoreClickUntil = Date.now() + 300;
4818
4946
  this.updateCursor(session.target.laneId);
4947
+ this.renderLane(session.target.laneId, true);
4948
+ this._syncCachedOption(session.target.laneId);
4819
4949
  if (emit) this._emitChange(session.target, { redraw: false });
4820
4950
  }
4821
4951
  _clearClickTimer() {
@@ -4827,6 +4957,7 @@ class TrendLineManager {
4827
4957
  /** 抛 trend-change(拖拽过程中不抛,只在松开时抛);不写回响应式模板。8.22 */
4828
4958
  // redraw=false:本地已画过,避免再 scheduleRender 把线闪掉。8.24
4829
4959
  _emitChange(target, { redraw = true } = {}) {
4960
+ if (!this._alive() || !target) return;
4830
4961
  const segments = cloneSegments(this.store[target.key] && this.store[target.key].segments || []);
4831
4962
  this.host.$emit("trend-change", {
4832
4963
  paramId: target.paramId,
@@ -4838,8 +4969,12 @@ class TrendLineManager {
4838
4969
  /** 等 Vue / 图表 setOption 完成后再画叠加层。8.22 */
4839
4970
  // 只 nextTick 一次,不再叠 rAF 连刷。8.24
4840
4971
  _scheduleRenderAfterVue() {
4841
- if (this.host && typeof this.host.$nextTick === "function") {
4842
- this.host.$nextTick(() => this.scheduleRender());
4972
+ if (!this._alive()) return;
4973
+ if (typeof this.host.$nextTick === "function") {
4974
+ this.host.$nextTick(() => {
4975
+ if (!this._alive()) return;
4976
+ this.scheduleRender();
4977
+ });
4843
4978
  } else {
4844
4979
  this.scheduleRender();
4845
4980
  }
@@ -4956,6 +5091,15 @@ class TrendLineManager {
4956
5091
  return pixelToData(chart, target.paramId, offsetX, offsetY, loose);
4957
5092
  }
4958
5093
  _trendToPixel(chart, target, value, depth) {
5094
+ const finder = findSeriesForDepth(chart, target.paramId, depth);
5095
+ const live = axisPairToPixel(
5096
+ chart,
5097
+ finder.xAxisIndex,
5098
+ finder.yAxisIndex,
5099
+ value,
5100
+ depth
5101
+ );
5102
+ if (live) return live;
4959
5103
  const lane = this._laneById(target.laneId);
4960
5104
  if (isLaneLogScale(lane)) {
4961
5105
  return logTrendDataToPixel(chart, target.paramId, value, depth);
@@ -5183,11 +5327,16 @@ const _sfc_main$6 = {
5183
5327
  type: Boolean,
5184
5328
  default: true
5185
5329
  },
5186
- // 当前选中的泳道ID(用于在头部显示红色边框)
5330
+ // 当前选中的泳道ID(最后一次点选,兼容旧 emit)
5187
5331
  activeLaneId: {
5188
5332
  type: [String, Number],
5189
5333
  default: null
5190
5334
  },
5335
+ // 地应力可同时选中多道。8.25
5336
+ activeLaneIds: {
5337
+ type: Array,
5338
+ default: () => []
5339
+ },
5191
5340
  // 是否为地应力模式(只有为 true 时才启用泳道选中功能)
5192
5341
  isGeomechanical: {
5193
5342
  type: Boolean,
@@ -5260,6 +5409,10 @@ const _sfc_main$6 = {
5260
5409
  clearTimeout(this.resizeTimer);
5261
5410
  this.resizeTimer = null;
5262
5411
  }
5412
+ if (this._curveConfigCloseTimer) {
5413
+ clearTimeout(this._curveConfigCloseTimer);
5414
+ this._curveConfigCloseTimer = 0;
5415
+ }
5263
5416
  if (this.resizeObserver) {
5264
5417
  this.resizeObserver.disconnect();
5265
5418
  this.resizeObserver = null;
@@ -5288,6 +5441,7 @@ const _sfc_main$6 = {
5288
5441
  return;
5289
5442
  }
5290
5443
  this.$nextTick(() => {
5444
+ if (this._isUnmounted) return;
5291
5445
  const containerRef = this.$refs.containerRef;
5292
5446
  if (!containerRef) {
5293
5447
  return;
@@ -5667,10 +5821,11 @@ const _sfc_main$6 = {
5667
5821
  isLaneTrendDraw(lane) {
5668
5822
  return !!(lane && (lane.lines || []).some((line) => isLineDrawable(line)));
5669
5823
  },
5670
- // 获取泳道头部样式:地应力选中道,或 isDraw 所在道,红色边框
5824
+ // 获取泳道头部样式:地应力已选中的道都画红框。8.25
5671
5825
  getLaneHeaderStyle(lane) {
5672
5826
  const base = this.headerStyle;
5673
- const isActive = this.isGeomechanical && this.activeLaneId != null && lane && lane.laneId === this.activeLaneId || this.isLaneTrendDraw(lane);
5827
+ const ids = this.activeLaneIds || [];
5828
+ const isActive = this.isGeomechanical ? !!(lane && ids.some((id) => id === lane.laneId)) : this.isLaneTrendDraw(lane);
5674
5829
  if (!isActive) return base;
5675
5830
  return {
5676
5831
  ...base,
@@ -5683,7 +5838,10 @@ const _sfc_main$6 = {
5683
5838
  },
5684
5839
  closeCurveConfigDialog() {
5685
5840
  this.curveConfigClosing = true;
5686
- setTimeout(() => {
5841
+ if (this._curveConfigCloseTimer) clearTimeout(this._curveConfigCloseTimer);
5842
+ this._curveConfigCloseTimer = setTimeout(() => {
5843
+ this._curveConfigCloseTimer = 0;
5844
+ if (this._isUnmounted) return;
5687
5845
  this.curveConfigVisible = false;
5688
5846
  this.curveConfigClosing = false;
5689
5847
  }, 300);
@@ -6691,7 +6849,7 @@ function _sfc_render$6(_ctx, _cache, $props, $setup, $data, $options) {
6691
6849
  }, 8, ["modelValue", "onClose"])
6692
6850
  ], 544);
6693
6851
  }
6694
- const KdLaneContainerComponent = /* @__PURE__ */ _export_sfc(_sfc_main$6, [["render", _sfc_render$6], ["__scopeId", "data-v-9d96e270"]]);
6852
+ const KdLaneContainerComponent = /* @__PURE__ */ _export_sfc(_sfc_main$6, [["render", _sfc_render$6], ["__scopeId", "data-v-814fbe51"]]);
6695
6853
  const SLIDER_GROUP_ID = "depthSliderGroup";
6696
6854
  function pad2(n) {
6697
6855
  return n < 10 ? "0" + n : "" + n;
@@ -9298,11 +9456,86 @@ const _sfc_main$3 = {
9298
9456
  const model = this.chart.getModel();
9299
9457
  const list = model.queryComponents({ mainType: "graphic" }) || [];
9300
9458
  if (!list.length) return;
9301
- const graphic = list.map((m) => m.option).filter((item) => item && (item.id || item.elements || item.type));
9459
+ const graphic = list.map((m) => m.option).filter((item) => {
9460
+ if (!item || !(item.id || item.elements || item.type)) return false;
9461
+ const id = String(item.id || "");
9462
+ return id.indexOf("trend_") !== 0;
9463
+ });
9302
9464
  if (graphic.length) options.graphic = graphic;
9303
9465
  } catch (e) {
9304
9466
  }
9305
9467
  },
9468
+ // notMerge 时把笛卡尔趋势 series 带回去,避免先消失再画。8.25
9469
+ // 只拷绘制字段,禁止展开 model.option(会带上坐标系等内部引用,图表拆不掉)。8.25
9470
+ _copyTrendSeriesOption(opt, id) {
9471
+ if (!opt) return null;
9472
+ const ls = opt.lineStyle || {};
9473
+ const is = opt.itemStyle || {};
9474
+ return {
9475
+ id: opt.id || id,
9476
+ name: "trend",
9477
+ type: "line",
9478
+ data: Array.isArray(opt.data) ? opt.data.slice() : [],
9479
+ xAxisIndex: opt.xAxisIndex,
9480
+ yAxisIndex: opt.yAxisIndex,
9481
+ clip: opt.clip,
9482
+ animation: false,
9483
+ silent: true,
9484
+ large: false,
9485
+ z: opt.z,
9486
+ zlevel: 0,
9487
+ symbol: opt.symbol,
9488
+ symbolSize: opt.symbolSize,
9489
+ showSymbol: opt.showSymbol,
9490
+ hoverAnimation: false,
9491
+ legendHoverLink: false,
9492
+ connectNulls: false,
9493
+ tooltip: { show: false },
9494
+ emphasis: { disabled: true },
9495
+ lineStyle: {
9496
+ color: ls.color,
9497
+ width: ls.width,
9498
+ type: ls.type
9499
+ },
9500
+ itemStyle: {
9501
+ color: is.color,
9502
+ borderColor: is.borderColor,
9503
+ borderWidth: is.borderWidth
9504
+ }
9505
+ };
9506
+ },
9507
+ _preserveTrendSeries(options) {
9508
+ if (!options || !this.chart || this.chart.isDisposed()) return;
9509
+ if (options.__kdTrendSynced) return;
9510
+ const incoming = Array.isArray(options.series) ? options.series : [];
9511
+ const hasTrend = incoming.some((s) => {
9512
+ if (!s) return false;
9513
+ const name = Array.isArray(s.name) ? s.name[0] : s.name;
9514
+ if (name === "trend") return true;
9515
+ const id = String(s.id || "");
9516
+ return id.indexOf("trend_") === 0;
9517
+ });
9518
+ if (hasTrend) return;
9519
+ try {
9520
+ const list = this.chart.getModel().getSeries() || [];
9521
+ const trends = [];
9522
+ const seen = /* @__PURE__ */ new Set();
9523
+ for (let i = 0; i < list.length; i++) {
9524
+ const sm = list[i];
9525
+ const opt = sm && sm.option || {};
9526
+ const name = Array.isArray(sm.name) ? sm.name[0] : sm.name;
9527
+ const id = String(opt.id || sm.id || "");
9528
+ if (name !== "trend" && id.indexOf("trend_") !== 0) continue;
9529
+ if (!id || seen.has(id)) continue;
9530
+ seen.add(id);
9531
+ const copied = this._copyTrendSeriesOption(opt, id);
9532
+ if (copied) trends.push(copied);
9533
+ }
9534
+ if (!trends.length) return;
9535
+ options.series = incoming.concat(trends);
9536
+ } catch (e) {
9537
+ }
9538
+ },
9306
9539
  // dataZoom 写进即将下发的 option,不再二次 setOption 打断 Chrome 平移。8.24
9307
9540
  _applySavedDataZoomToOptions(options, savedState) {
9308
9541
  if (!savedState || !options) return;
@@ -9352,6 +9585,7 @@ const _sfc_main$3 = {
9352
9585
  const next = this.getOptionsWithTheme(options, skipClone);
9353
9586
  this._applySavedDataZoomToOptions(next, savedDataZoom);
9354
9587
  this._preserveGraphic(next);
9588
+ this._preserveTrendSeries(next);
9355
9589
  this.delegateMethod("setOption", next, {
9356
9590
  notMerge: !!notMerge,
9357
9591
  lazyUpdate: !!lazyUpdate
@@ -9518,6 +9752,7 @@ const _sfc_main$3 = {
9518
9752
  const next = this.getOptionsWithTheme(val);
9519
9753
  this._applySavedDataZoomToOptions(next, savedDataZoom);
9520
9754
  this._preserveGraphic(next);
9755
+ this._preserveTrendSeries(next);
9521
9756
  this.chart.setOption(next, {
9522
9757
  notMerge: this.skipClone || val !== oldVal
9523
9758
  });
@@ -9613,7 +9848,7 @@ const _hoisted_1$3 = { class: "echarts" };
9613
9848
  function _sfc_render$3(_ctx, _cache, $props, $setup, $data, $options) {
9614
9849
  return openBlock(), createElementBlock("div", _hoisted_1$3);
9615
9850
  }
9616
- const resizeEcharts = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["render", _sfc_render$3], ["__scopeId", "data-v-c1cce4a1"]]);
9851
+ const resizeEcharts = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["render", _sfc_render$3], ["__scopeId", "data-v-5b44c8a6"]]);
9617
9852
  const DRAW_POINT_MIN = 600;
9618
9853
  const DRAW_POINT_MAX = 1800;
9619
9854
  function getSeriesDrawThreshold(laneHeightPx) {
@@ -9994,6 +10229,7 @@ const _sfc_main$2 = {
9994
10229
  selectedParamId: null,
9995
10230
  // 当前选中的泳道ID(只有该泳道显示缺失点、区块、区块点)
9996
10231
  activeLaneId: null,
10232
+ activeLaneIds: [],
9997
10233
  currentMarkAreaIndex: -1,
9998
10234
  // 每个泳道的选中点对数组,每对包含两个点形成区块,与 markArea 通过 markAreaId 关联
9999
10235
  laneSelectedPointPairs: {},
@@ -10030,6 +10266,8 @@ const _sfc_main$2 = {
10030
10266
  this._isPanning = false;
10031
10267
  this._isZooming = false;
10032
10268
  this._zoomIdleTimer = 0;
10269
+ this._geoClickTimer = 0;
10270
+ this._laneZrHandlers = /* @__PURE__ */ Object.create(null);
10033
10271
  this._connectDetached = false;
10034
10272
  this._savedConnectGroups = null;
10035
10273
  this._tipHtmlBucket = "";
@@ -10039,7 +10277,7 @@ const _sfc_main$2 = {
10039
10277
  this._pointerRaf = 0;
10040
10278
  const pending = this._pendingPointer;
10041
10279
  if (!pending || !this.pointerInChartArea) return;
10042
- const { chart, x, y } = pending;
10280
+ const { chart, x, y, clientY } = pending;
10043
10281
  if (!chart || chart.isDisposed()) {
10044
10282
  this._pendingPointer = null;
10045
10283
  return;
@@ -10078,7 +10316,18 @@ const _sfc_main$2 = {
10078
10316
  clearTimeout(this._zoomIdleTimer);
10079
10317
  this._zoomIdleTimer = 0;
10080
10318
  }
10081
- if (this.trendLineManager) this.trendLineManager.destroy();
10319
+ if (this._geoClickTimer) {
10320
+ clearTimeout(this._geoClickTimer);
10321
+ this._geoClickTimer = 0;
10322
+ }
10323
+ Object.keys(this._laneZrHandlers || {}).forEach((laneId) => {
10324
+ this._unbindLaneZrEvents(laneId);
10325
+ });
10326
+ this._laneZrHandlers = /* @__PURE__ */ Object.create(null);
10327
+ if (this.trendLineManager) {
10328
+ this.trendLineManager.destroy();
10329
+ this.trendLineManager = null;
10330
+ }
10082
10331
  if (this._pointerRaf) {
10083
10332
  cancelAnimationFrame(this._pointerRaf);
10084
10333
  this._pointerRaf = 0;
@@ -10099,6 +10348,7 @@ const _sfc_main$2 = {
10099
10348
  this.attachChartConnect();
10100
10349
  this.unbindChartPointerSync();
10101
10350
  echarts.disConnect(this.group);
10351
+ this.chartRefMap = /* @__PURE__ */ Object.create(null);
10102
10352
  this.clearEcharts();
10103
10353
  if (this.resizeObserver) {
10104
10354
  this.resizeObserver.disconnect();
@@ -10118,6 +10368,7 @@ const _sfc_main$2 = {
10118
10368
  async laneWidthChange(widthMap) {
10119
10369
  this.laneWidthMap = widthMap || {};
10120
10370
  await this.$nextTick();
10371
+ if (this._isUnmounted) return;
10121
10372
  this.updateLanesChartOption();
10122
10373
  },
10123
10374
  // 曲线上方对数刻度:边控关则只藏尺。8.22对数轴
@@ -10182,43 +10433,77 @@ const _sfc_main$2 = {
10182
10433
  flex: "1"
10183
10434
  });
10184
10435
  }
10185
- const isDrawLane = this.isLaneTrendDrawById(laneId);
10186
- if (this.isGeomechanical || isDrawLane) {
10187
- const isActive = this.activeLaneId === laneId || isDrawLane;
10436
+ if (this.isGeomechanical) {
10437
+ style.boxSizing = "border-box";
10438
+ style.border = this.isLaneSelected(laneId) ? "1px solid #F86C59" : "1px solid transparent";
10439
+ } else if (this.isLaneTrendDrawById(laneId)) {
10188
10440
  style.boxSizing = "border-box";
10189
- style.border = isActive ? "1px solid #F86C59" : "1px solid transparent";
10441
+ style.border = "1px solid #F86C59";
10190
10442
  }
10191
10443
  return style;
10192
10444
  },
10445
+ // 按引用摘掉 click/dblclick,避免 zr.off(类型) 误拆趋势线 dblclick。8.25
10446
+ _unbindLaneZrEvents(laneId) {
10447
+ const rec = this._laneZrHandlers && this._laneZrHandlers[laneId];
10448
+ if (!rec) return;
10449
+ try {
10450
+ const chart = rec.chart;
10451
+ if (chart && typeof chart.getZr === "function" && !chart.isDisposed()) {
10452
+ const zr = chart.getZr();
10453
+ if (zr) {
10454
+ if (rec.onContext) zr.off("contextmenu", rec.onContext);
10455
+ if (rec.onClick) zr.off("click", rec.onClick);
10456
+ if (rec.onDbl) zr.off("dblclick", rec.onDbl);
10457
+ }
10458
+ }
10459
+ } catch (e) {
10460
+ }
10461
+ rec.chart = null;
10462
+ rec.onContext = null;
10463
+ rec.onClick = null;
10464
+ rec.onDbl = null;
10465
+ delete this._laneZrHandlers[laneId];
10466
+ },
10467
+ _bindLaneZrEvents(laneId, chart) {
10468
+ this._unbindLaneZrEvents(laneId);
10469
+ if (!chart || chart.isDisposed()) return;
10470
+ let zr;
10471
+ try {
10472
+ zr = chart.getZr();
10473
+ } catch (e) {
10474
+ return;
10475
+ }
10476
+ if (!zr) return;
10477
+ const onContext = (e) => this.handleChartRightClick(e, laneId);
10478
+ const onClick = (e) => this.handleChartClick(e, laneId);
10479
+ const onDbl = (e) => this.handleChartDblClick(e, laneId);
10480
+ zr.on("contextmenu", onContext);
10481
+ zr.on("click", onClick);
10482
+ zr.on("dblclick", onDbl);
10483
+ this._laneZrHandlers[laneId] = { chart, onContext, onClick, onDbl };
10484
+ },
10193
10485
  setChartRef(laneId, el) {
10194
10486
  if (el) {
10195
- if (this.chartRefMap[laneId] && this.chartRefMap[laneId].chart) {
10196
- this.chartRefMap[laneId].chart.getZr().off("contextmenu");
10197
- this.chartRefMap[laneId].chart.getZr().off("click");
10487
+ const prev = this.chartRefMap[laneId];
10488
+ if (prev && prev.chart && prev.chart !== el.chart) {
10489
+ this._unbindLaneZrEvents(laneId);
10198
10490
  }
10199
10491
  this.chartRefMap[laneId] = el;
10200
- el.chart && el.chart.getZr().on("contextmenu", (e) => this.handleChartRightClick(e, laneId));
10201
- el.chart && el.chart.getZr().on("click", (e) => this.handleChartClick(e, laneId));
10492
+ if (el.chart) this._bindLaneZrEvents(laneId, el.chart);
10202
10493
  if (el.chart && this.trendLineManager) {
10203
10494
  this.trendLineManager.bindChart(laneId, el.chart);
10204
10495
  }
10205
10496
  } else if (this.chartRefMap[laneId]) {
10206
10497
  if (this.trendLineManager) this.trendLineManager.unbindChart(laneId);
10207
- this.chartRefMap[laneId].chart && this.chartRefMap[laneId].chart.getZr().off("contextmenu");
10208
- this.chartRefMap[laneId].chart && this.chartRefMap[laneId].chart.getZr().off("click");
10498
+ this._unbindLaneZrEvents(laneId);
10209
10499
  delete this.chartRefMap[laneId];
10210
10500
  }
10211
10501
  },
10212
10502
  handleChartInited(laneId, chart) {
10213
- if (this.chartRefMap[laneId]) {
10214
- this.chartRefMap[laneId].chart.getZr().off("contextmenu");
10215
- this.chartRefMap[laneId].chart.getZr().off("click");
10216
- }
10217
- chart.getZr().on("contextmenu", (e) => this.handleChartRightClick(e, laneId));
10218
- chart.getZr().on("click", (e) => this.handleChartClick(e, laneId));
10503
+ this._bindLaneZrEvents(laneId, chart);
10219
10504
  if (this.trendLineManager) {
10220
10505
  this.trendLineManager.bindChart(laneId, chart);
10221
- this.applyLineListData();
10506
+ if ((this.lineListData || []).length) this.applyLineListData();
10222
10507
  this.trendLineManager.scheduleRender();
10223
10508
  }
10224
10509
  this.scheduleConnectAllCharts();
@@ -10230,14 +10515,24 @@ const _sfc_main$2 = {
10230
10515
  this.getConnectedCharts().forEach((chart) => {
10231
10516
  if (!chart || chart.isDisposed()) return;
10232
10517
  const laneId = this.findLaneIdByChart(chart);
10233
- chart.setOption({
10234
- dataZoom: [
10235
- this.buildInsideDataZoom(
10236
- !!enabled,
10237
- this.dataZoomFilterModeForLane(this.findLaneById(laneId))
10238
- )
10239
- ]
10240
- });
10518
+ const next = this.buildInsideDataZoom(
10519
+ !!enabled,
10520
+ this.dataZoomFilterModeForLane(this.findLaneById(laneId))
10521
+ );
10522
+ try {
10523
+ const dz = chart.getModel().getComponent("dataZoom", 0);
10524
+ const opt = dz && dz.option;
10525
+ if (opt) {
10526
+ if (opt.start != null) next.start = opt.start;
10527
+ if (opt.end != null) next.end = opt.end;
10528
+ if (opt.startValue != null) next.startValue = opt.startValue;
10529
+ if (opt.endValue != null) next.endValue = opt.endValue;
10530
+ }
10531
+ const skip = this.trendLineManager && this.trendLineManager.dataZoomSeriesIndex(chart);
10532
+ if (skip && skip.length) next.seriesIndex = skip;
10533
+ } catch (e) {
10534
+ }
10535
+ chart.setOption({ dataZoom: [next] }, { silent: true, lazyUpdate: false });
10241
10536
  });
10242
10537
  },
10243
10538
  getConnectedCharts() {
@@ -10249,7 +10544,7 @@ const _sfc_main$2 = {
10249
10544
  // 趋势线控制点右键菜单(删除控制点) 8.21
10250
10545
  showTrendKnotContextMenu(e, payload) {
10251
10546
  this.currentContextMenuType = "trendKnot";
10252
- this.currentPointInfo = payload;
10547
+ this.currentPointInfo = markRaw(payload);
10253
10548
  this.currentContextMenuItems = [{ label: "删除控制点", action: "deleteTrendKnot" }];
10254
10549
  this.calculateContextMenuPosition(e);
10255
10550
  this.contextMenuVisible = true;
@@ -10282,6 +10577,85 @@ const _sfc_main$2 = {
10282
10577
  resetAllTrendLines() {
10283
10578
  if (this.trendLineManager) this.trendLineManager.resetAllTrendLines();
10284
10579
  },
10580
+ isLaneSelected(laneId) {
10581
+ return laneId != null && (this.activeLaneIds || []).some((id) => id === laneId);
10582
+ },
10583
+ addLaneSelection(laneId) {
10584
+ if (laneId == null) return false;
10585
+ this.activeLaneId = laneId;
10586
+ if (this.isLaneSelected(laneId)) return false;
10587
+ this.activeLaneIds = this.activeLaneIds.concat([laneId]);
10588
+ return true;
10589
+ },
10590
+ removeLaneSelection(laneId) {
10591
+ if (!this.isLaneSelected(laneId)) return;
10592
+ this.activeLaneIds = (this.activeLaneIds || []).filter((id) => id !== laneId);
10593
+ if (this.activeLaneId === laneId) {
10594
+ this.activeLaneId = this.activeLaneIds.length ? this.activeLaneIds[this.activeLaneIds.length - 1] : null;
10595
+ }
10596
+ },
10597
+ // 退出编辑:清掉全部选中红框,不挂新监听。8.25
10598
+ clearActiveLane() {
10599
+ this._clearGeoClickTimer();
10600
+ this._geoSelectClickAt = 0;
10601
+ this._geoSelectLaneId = null;
10602
+ this._lastBlankClickTime = null;
10603
+ this._lastBlankClickLaneId = null;
10604
+ const ids = (this.activeLaneIds || []).slice();
10605
+ if (this.selectedPoints.length > 0 && this.selectedLaneId) {
10606
+ if (this.laneSelectedPointPairs[this.selectedLaneId]) {
10607
+ this.laneSelectedPointPairs[this.selectedLaneId].pop();
10608
+ }
10609
+ this.selectedPoints = [];
10610
+ this.selectedLaneId = null;
10611
+ this.selectedParamId = null;
10612
+ }
10613
+ this.activeLaneIds = [];
10614
+ this.activeLaneId = null;
10615
+ if (!ids.length) return;
10616
+ this.updateLanesChartOption().then(() => {
10617
+ if (this._isUnmounted) return;
10618
+ ids.forEach((id) => this.showSelectedPointScatter(id));
10619
+ });
10620
+ },
10621
+ // 切趋势线:只保留当前曲线所在道;单击其它道仍可再选中。8.25
10622
+ selectLaneByParamId(paramId) {
10623
+ if (!this.isGeomechanical) return;
10624
+ if (paramId == null || paramId === "") {
10625
+ this.clearActiveLane();
10626
+ return;
10627
+ }
10628
+ const lanes = this.currentTemplate && this.currentTemplate.lanes || [];
10629
+ let laneId = null;
10630
+ for (let i = 0; i < lanes.length; i++) {
10631
+ const lane = lanes[i];
10632
+ if ((lane.lines || []).some((line) => String(line.paramId) === String(paramId))) {
10633
+ laneId = lane.laneId;
10634
+ break;
10635
+ }
10636
+ }
10637
+ if (!laneId) return;
10638
+ const prevIds = (this.activeLaneIds || []).slice();
10639
+ if (prevIds.length === 1 && prevIds[0] === laneId) {
10640
+ this.activeLaneId = laneId;
10641
+ return;
10642
+ }
10643
+ if (this.selectedPoints.length > 0 && this.selectedLaneId && this.selectedLaneId !== laneId) {
10644
+ if (this.laneSelectedPointPairs[this.selectedLaneId]) {
10645
+ this.laneSelectedPointPairs[this.selectedLaneId].pop();
10646
+ }
10647
+ this.selectedPoints = [];
10648
+ this.selectedLaneId = null;
10649
+ this.selectedParamId = null;
10650
+ }
10651
+ this.activeLaneIds = [laneId];
10652
+ this.activeLaneId = laneId;
10653
+ this.updateLanesChartOption().then(() => {
10654
+ if (this._isUnmounted) return;
10655
+ prevIds.forEach((id) => this.showSelectedPointScatter(id));
10656
+ this.showSelectedPointScatter(laneId);
10657
+ });
10658
+ },
10285
10659
  // 根据 paramId 判断曲线是否可见(isUsed === '1')
10286
10660
  isLineVisibleByParamId(paramId) {
10287
10661
  const lanes = this.currentTemplate && this.currentTemplate.lanes || [];
@@ -10400,7 +10774,7 @@ const _sfc_main$2 = {
10400
10774
  const sm = seriesList[s];
10401
10775
  const option = sm.option || {};
10402
10776
  const name = this.unwrapEchartsValue(sm.name);
10403
- if (name === "helper" || name === "log-grid") continue;
10777
+ if (name === "helper" || name === "log-grid" || name === "trend") continue;
10404
10778
  const type = sm.subType || this.unwrapEchartsValue(option.type);
10405
10779
  if (type && type !== "line") continue;
10406
10780
  const tip = option.tooltip;
@@ -10561,6 +10935,7 @@ const _sfc_main$2 = {
10561
10935
  if (!series || !Array.isArray(series.data) || !series.data.length) return false;
10562
10936
  if (this.seriesDisplayName(series) === "helper") return false;
10563
10937
  if (this.seriesDisplayName(series) === "log-grid") return false;
10938
+ if (this.seriesDisplayName(series) === "trend") return false;
10564
10939
  const type = this.seriesTypeName(series);
10565
10940
  if (type && type !== "line") return false;
10566
10941
  if (this.seriesTooltipHidden(series)) return false;
@@ -10715,8 +11090,8 @@ const _sfc_main$2 = {
10715
11090
  el.style.transform = `translate3d(0, ${top}px, 0)`;
10716
11091
  if (!this.crosshairVisible) this.crosshairVisible = true;
10717
11092
  },
10718
- schedulePointerSync(chart, x, y) {
10719
- this._pendingPointer = { chart, x, y };
11093
+ schedulePointerSync(chart, x, y, clientY) {
11094
+ this._pendingPointer = { chart, x, y, clientY };
10720
11095
  if (this._pointerRaf) return;
10721
11096
  this._pointerRaf = requestAnimationFrame(this._flushPointerSync);
10722
11097
  },
@@ -10856,6 +11231,7 @@ const _sfc_main$2 = {
10856
11231
  },
10857
11232
  startChartPan() {
10858
11233
  if (!this.isGeomechanical || this._isPanning) return;
11234
+ if (this._chartPanLocked) return;
10859
11235
  this._isPanning = true;
10860
11236
  this.attachChartConnect();
10861
11237
  this.hideLaneTooltipsForPan();
@@ -10879,6 +11255,7 @@ const _sfc_main$2 = {
10879
11255
  this._zoomIdleTimer = 0;
10880
11256
  if (!this._isZooming) return;
10881
11257
  this._isZooming = false;
11258
+ if (!this._isPanning && this.trendLineManager) this.trendLineManager.endFollowView();
10882
11259
  this.restorePointerAfterInteract();
10883
11260
  },
10884
11261
  hideAllLaneTooltips(event) {
@@ -10955,7 +11332,8 @@ const _sfc_main$2 = {
10955
11332
  this.schedulePointerSync(
10956
11333
  chart,
10957
11334
  e.offsetX ?? ((_a = e.event) == null ? void 0 : _a.offsetX),
10958
- e.offsetY ?? ((_b = e.event) == null ? void 0 : _b.offsetY)
11335
+ e.offsetY ?? ((_b = e.event) == null ? void 0 : _b.offsetY),
11336
+ native.clientY
10959
11337
  );
10960
11338
  };
10961
11339
  zr.on("mousedown", onDown);
@@ -10995,11 +11373,13 @@ const _sfc_main$2 = {
10995
11373
  if (this.trendLineManager) this.trendLineManager.hydrateFromTemplate(template);
10996
11374
  this.applyLineListData();
10997
11375
  await this.$nextTick();
11376
+ if (this._isUnmounted) return;
10998
11377
  this.updateLanesChartOption();
10999
11378
  },
11000
11379
  // 线段变化
11001
11380
  async lineChange() {
11002
11381
  await this.$nextTick();
11382
+ if (this._isUnmounted) return;
11003
11383
  this.updateLanesChartOption();
11004
11384
  },
11005
11385
  // 初始化窗口大小范围以及windowSize默认值
@@ -11174,10 +11554,10 @@ const _sfc_main$2 = {
11174
11554
  ref.mergeOptions(option, true, false, true);
11175
11555
  },
11176
11556
  // 判断指定泳道是否应该显示tooltip内容
11177
- // 地应力模式下:如果没有选中泳道,所有泳道正常显示;如果有选中泳道,只有选中的泳道显示
11557
+ // 地应力:无选中时所有泳道显示;有选中时已选中的多道都显示。8.25
11178
11558
  shouldShowTooltipContent(laneId) {
11179
11559
  if (!this.showTooltip) return false;
11180
- if (this.isGeomechanical && this.activeLaneId && laneId !== this.activeLaneId) {
11560
+ if (this.isGeomechanical && (this.activeLaneIds || []).length && !this.isLaneSelected(laneId)) {
11181
11561
  return false;
11182
11562
  }
11183
11563
  return true;
@@ -11633,7 +12013,7 @@ const _sfc_main$2 = {
11633
12013
  return { data: [[x, group.min], [x, group.max]] };
11634
12014
  }
11635
12015
  const type = sm.subType || this.unwrapEchartsValue(opt.type);
11636
- if (name === "log-grid" || type !== "line") return {};
12016
+ if (name === "log-grid" || name === "trend" || type !== "line") return {};
11637
12017
  const paramId = this.unwrapEchartsValue(opt.paramId);
11638
12018
  if (paramId == null) return {};
11639
12019
  const cacheKey = `${gi}:${paramId}:${isLog ? 1 : 0}`;
@@ -11665,7 +12045,6 @@ const _sfc_main$2 = {
11665
12045
  } catch (e) {
11666
12046
  }
11667
12047
  });
11668
- if (this.trendLineManager) this.trendLineManager.scheduleZoomRender();
11669
12048
  },
11670
12049
  // 处理键盘按下事件
11671
12050
  handleKeyDown(e) {
@@ -12250,6 +12629,7 @@ const _sfc_main$2 = {
12250
12629
  },
12251
12630
  // 更新泳道图表选项
12252
12631
  async updateLanesChartOption(transferData) {
12632
+ if (this._isUnmounted) return;
12253
12633
  if (this.isUpdatingChart) {
12254
12634
  this.pendingChartUpdate = true;
12255
12635
  return;
@@ -12567,7 +12947,7 @@ const _sfc_main$2 = {
12567
12947
  if (rangeAreaSeries) {
12568
12948
  option.series.push(rangeAreaSeries);
12569
12949
  }
12570
- if (lineInfo.isUsed === "1" && this.isGeomechanical && lane.laneId === this.activeLaneId) {
12950
+ if (lineInfo.isUsed === "1" && this.isGeomechanical && this.isLaneSelected(lane.laneId)) {
12571
12951
  const boundaryPoints = this.findMissingDataBoundaryPoints(
12572
12952
  this.plotSeries(groupSeriesData, isLogScale)
12573
12953
  );
@@ -12720,12 +13100,17 @@ const _sfc_main$2 = {
12720
13100
  option.tooltip.extraCssText = "pointer-events: none !important;";
12721
13101
  option.tooltip.formatter = () => this.renderLaneTooltipHtml(laneId);
12722
13102
  }
13103
+ if (this.trendLineManager) this.trendLineManager.appendSeriesToOption(option, lane);
12723
13104
  this.updateOptionsByLineId(lane.laneId, option);
12724
13105
  }
12725
13106
  } catch (error) {
12726
13107
  console.error("updateLanesChartOption error:", error);
12727
13108
  } finally {
12728
13109
  this.isUpdatingChart = false;
13110
+ if (this._isUnmounted) {
13111
+ this.pendingChartUpdate = false;
13112
+ return;
13113
+ }
12729
13114
  this.scheduleConnectAllCharts();
12730
13115
  const lanesToRefresh = new Set(
12731
13116
  Object.keys(this.laneSelectedPointPairs || {})
@@ -12733,6 +13118,7 @@ const _sfc_main$2 = {
12733
13118
  if (this.selectedLaneId) lanesToRefresh.add(this.selectedLaneId);
12734
13119
  if (lanesToRefresh.size > 0) {
12735
13120
  this.$nextTick(() => {
13121
+ if (this._isUnmounted) return;
12736
13122
  lanesToRefresh.forEach((lid) => {
12737
13123
  this.showSelectedPointScatter(lid);
12738
13124
  });
@@ -12741,13 +13127,10 @@ const _sfc_main$2 = {
12741
13127
  if (this.pendingChartUpdate) {
12742
13128
  this.pendingChartUpdate = false;
12743
13129
  this.$nextTick(() => {
13130
+ if (this._isUnmounted) return;
12744
13131
  this.updateLanesChartOption(transferData);
12745
13132
  });
12746
13133
  }
12747
- if (this.trendLineManager) {
12748
- this.trendLineManager.scheduleZoomRender();
12749
- this.$nextTick(() => this.trendLineManager.scheduleZoomRender());
12750
- }
12751
13134
  }
12752
13135
  },
12753
13136
  /**
@@ -13040,85 +13423,172 @@ const _sfc_main$2 = {
13040
13423
  }
13041
13424
  return boundaryPoints;
13042
13425
  },
13043
- async handleChartClick(e, laneId) {
13044
- var _a, _b, _c;
13045
- const chartRef = this.getChartRefByLaneId(laneId);
13046
- if (!chartRef || !chartRef.chart) return;
13047
- if (this.trendLineManager && this.trendLineManager.handleClick(e, laneId)) {
13048
- return;
13049
- }
13050
- if (!this.isGeomechanical) return;
13051
- const wasActiveLane = this.activeLaneId === laneId;
13052
- if (this.activeLaneId !== laneId) {
13053
- const prevActiveLaneId = this.activeLaneId;
13054
- this.activeLaneId = laneId;
13055
- await this.updateLanesChartOption();
13056
- if (prevActiveLaneId) {
13057
- this.showSelectedPointScatter(prevActiveLaneId);
13426
+ // 只把点击附近的点转成像素。全量 convertToPixel 会堵住主线程,双击 500ms 对不上。8.25
13427
+ findClosestGeoLineHit(chart, option, pointInPixel, pixelThreshold, laneId) {
13428
+ var _a;
13429
+ const threshold = pixelThreshold || 10;
13430
+ const px = pointInPixel[0];
13431
+ const py = pointInPixel[1];
13432
+ const seriesList = option && option.series || [];
13433
+ let closest = null;
13434
+ let closestDistance = threshold;
13435
+ for (let i = 0; i < seriesList.length; i++) {
13436
+ const series2 = seriesList[i];
13437
+ if (!series2 || series2.type !== "line") continue;
13438
+ const name = Array.isArray(series2.name) ? series2.name[0] : series2.name;
13439
+ if (name === "helper" || name === "log-grid" || name === "trend") continue;
13440
+ const id = String(series2.id || "");
13441
+ if (id.indexOf("trend_") === 0) continue;
13442
+ let clickData;
13443
+ try {
13444
+ clickData = chart.convertFromPixel({ seriesIndex: i }, [px, py]);
13445
+ } catch (err) {
13446
+ continue;
13058
13447
  }
13059
- }
13060
- const chart = chartRef.chart;
13061
- const option = this.readChartOptionRefs(chart);
13062
- const pointInPixel = [
13063
- e.offsetX || ((_a = e.event) == null ? void 0 : _a.offsetX),
13064
- e.offsetY || ((_b = e.event) == null ? void 0 : _b.offsetY)
13065
- ];
13066
- let clickedPoint = null;
13067
- let closestPoint = null;
13068
- let closestDistance = 10;
13069
- const axisKey = this.currentToolBarConfig.axisTypeList && this.currentToolBarConfig.axisTypeList[this.formCache.axisType] || (this.formCache.axisType === "time" ? "time" : "depth");
13070
- for (let i = 0; i < option.series.length; i++) {
13071
- const series = option.series[i];
13072
- if (series.name === "helper" || series.name === "log-grid") continue;
13073
- if (series.type !== "line") continue;
13074
- const data = series.data || [];
13448
+ if (!clickData || clickData.length < 2) continue;
13449
+ let yTol = Infinity;
13450
+ try {
13451
+ const offset = chart.convertFromPixel({ seriesIndex: i }, [px, py + threshold]);
13452
+ if (offset && offset.length >= 2) {
13453
+ const span = Math.abs(offset[1] - clickData[1]);
13454
+ if (Number.isFinite(span) && span > 0) yTol = span * 1.25;
13455
+ }
13456
+ } catch (err) {
13457
+ yTol = Infinity;
13458
+ }
13459
+ const data = series2.data || [];
13075
13460
  for (let j = 0; j < data.length; j++) {
13076
- const item = data[j];
13077
- if (!item) continue;
13078
- let pointValue;
13079
- pointValue = Array.isArray(item) ? item : item.value || [];
13080
- if (!pointValue || !Array.isArray(pointValue) || pointValue.length < 2)
13081
- continue;
13082
- const point = chart.convertToPixel({ seriesIndex: i }, pointValue);
13461
+ const item2 = data[j];
13462
+ if (!item2) continue;
13463
+ const pointValue2 = Array.isArray(item2) ? item2 : item2.value || [];
13464
+ if (!pointValue2 || pointValue2.length < 2) continue;
13465
+ if (Number.isFinite(yTol) && Math.abs(pointValue2[1] - clickData[1]) > yTol) continue;
13466
+ const point = chart.convertToPixel({ seriesIndex: i }, pointValue2);
13083
13467
  if (!point) continue;
13084
- const distance = Math.sqrt(
13085
- Math.pow(point[0] - pointInPixel[0], 2) + Math.pow(point[1] - pointInPixel[1], 2)
13086
- );
13468
+ const distance = Math.hypot(point[0] - px, point[1] - py);
13087
13469
  if (distance < closestDistance) {
13088
13470
  closestDistance = distance;
13089
- const axisValue = pointValue[1];
13090
- const originalData = this.realTimeData.find(
13091
- (d) => d[axisKey] == axisValue
13092
- );
13093
- const paramId = item.paramId || series.paramId;
13094
- const lane = (this.currentTemplate && this.currentTemplate.lanes || []).find(
13095
- (l) => l.laneId === laneId
13096
- );
13097
- const originalX = (item.rowData && item.rowData[paramId]) ?? (originalData && originalData[paramId]);
13098
- option.xAxis && option.xAxis[series.xAxisIndex ?? 0] || {};
13099
- const useOriginal = originalX != null && Number(originalX) > 0;
13100
- const drawX = isLaneLogScale(lane) ? toLaneDrawX(
13101
- useOriginal ? originalX : pointValue[0],
13102
- lane
13103
- ) : pointValue[0];
13104
- closestPoint = {
13105
- ...item,
13106
- value: [drawX, pointValue[1], pointValue[2]],
13107
- seriesIndex: i,
13108
- dataIndex: j,
13109
- paramId,
13110
- lineId: item.lineId || series.lineId,
13111
- color: series.color,
13112
- laneId,
13113
- depth: (originalData == null ? void 0 : originalData.depth) || null,
13114
- timestamp: ((_c = originalData == null ? void 0 : originalData.timestamp) == null ? void 0 : _c.toString()) || null
13115
- };
13471
+ closest = { item: item2, series: series2, seriesIndex: i, dataIndex: j, pointValue: pointValue2, distance };
13116
13472
  }
13117
13473
  }
13118
13474
  }
13119
- if (closestPoint && closestDistance < 10) {
13120
- clickedPoint = closestPoint;
13475
+ if (!closest) return null;
13476
+ const axisKey = this.currentToolBarConfig.axisTypeList && this.currentToolBarConfig.axisTypeList[this.formCache.axisType] || (this.formCache.axisType === "time" ? "time" : "depth");
13477
+ const { item, series, seriesIndex, dataIndex, pointValue } = closest;
13478
+ const originalData = item && item.rowData || (this.realTimeData || []).find((d) => d[axisKey] == pointValue[1]);
13479
+ const paramId = item && item.paramId || series.paramId;
13480
+ const lane = (this.currentTemplate && this.currentTemplate.lanes || []).find(
13481
+ (l) => l.laneId === laneId
13482
+ );
13483
+ const originalX = (item.rowData && item.rowData[paramId]) ?? (originalData && originalData[paramId]);
13484
+ option.xAxis && option.xAxis[series.xAxisIndex ?? 0] || {};
13485
+ const useOriginal = originalX != null && Number(originalX) > 0;
13486
+ const drawX = isLaneLogScale(lane) ? toLaneDrawX(
13487
+ useOriginal ? originalX : pointValue[0],
13488
+ lane
13489
+ ) : pointValue[0];
13490
+ return {
13491
+ ...item,
13492
+ value: [drawX, pointValue[1], pointValue[2]],
13493
+ seriesIndex,
13494
+ dataIndex,
13495
+ paramId,
13496
+ lineId: item && item.lineId || series.lineId,
13497
+ color: series.color,
13498
+ laneId,
13499
+ depth: (originalData == null ? void 0 : originalData.depth) || null,
13500
+ timestamp: ((_a = originalData == null ? void 0 : originalData.timestamp) == null ? void 0 : _a.toString()) || null
13501
+ };
13502
+ },
13503
+ _clearGeoClickTimer() {
13504
+ if (this._geoClickTimer) {
13505
+ clearTimeout(this._geoClickTimer);
13506
+ this._geoClickTimer = 0;
13507
+ }
13508
+ },
13509
+ // 地应力:双击已选中泳道,只取消这一道,其它选中保留。8.25
13510
+ handleChartDblClick(e, laneId) {
13511
+ if (this._isUnmounted || !this.isGeomechanical) return;
13512
+ this._clearGeoClickTimer();
13513
+ if (!this.isLaneSelected(laneId)) return;
13514
+ if (this._geoSelectClickAt && this._geoSelectLaneId === laneId && Date.now() - this._geoSelectClickAt < 500) {
13515
+ return;
13516
+ }
13517
+ this.deselectGeoLane(laneId);
13518
+ },
13519
+ async deselectGeoLane(laneId) {
13520
+ const id = laneId != null ? laneId : this.activeLaneId;
13521
+ this._lastBlankClickTime = null;
13522
+ this._lastBlankClickLaneId = null;
13523
+ if (!id || !this.isLaneSelected(id)) return;
13524
+ if (this.selectedPoints.length > 0 && this.selectedLaneId === id) {
13525
+ if (this.laneSelectedPointPairs[this.selectedLaneId]) {
13526
+ this.laneSelectedPointPairs[this.selectedLaneId].pop();
13527
+ }
13528
+ this.selectedPoints = [];
13529
+ this.selectedLaneId = null;
13530
+ this.selectedParamId = null;
13531
+ }
13532
+ this.removeLaneSelection(id);
13533
+ await this.updateLanesChartOption();
13534
+ if (this._isUnmounted) return;
13535
+ this.showSelectedPointScatter(id);
13536
+ },
13537
+ async handleChartClick(e, laneId) {
13538
+ var _a, _b, _c, _d;
13539
+ if (this._isUnmounted) return;
13540
+ const clickAt = Date.now();
13541
+ const chartRef = this.getChartRefByLaneId(laneId);
13542
+ if (!chartRef || !chartRef.chart) return;
13543
+ const wasActiveLane = this.isLaneSelected(laneId);
13544
+ const trendConsumed = this.trendLineManager && this.trendLineManager.handleClick(e, laneId);
13545
+ if (!this.isGeomechanical) return;
13546
+ if (!wasActiveLane) {
13547
+ this._geoSelectClickAt = clickAt;
13548
+ this._geoSelectLaneId = laneId;
13549
+ this.addLaneSelection(laneId);
13550
+ await this.updateLanesChartOption();
13551
+ if (this._isUnmounted) return;
13552
+ this.showSelectedPointScatter(laneId);
13553
+ }
13554
+ if (trendConsumed) return;
13555
+ if (wasActiveLane) {
13556
+ this._clearGeoClickTimer();
13557
+ const offsetX = e.offsetX || ((_a = e.event) == null ? void 0 : _a.offsetX);
13558
+ const offsetY = e.offsetY || ((_b = e.event) == null ? void 0 : _b.offsetY);
13559
+ this._geoClickTimer = setTimeout(() => {
13560
+ this._geoClickTimer = 0;
13561
+ if (this._isUnmounted) return;
13562
+ this.processGeoPointOrBlank(laneId, offsetX, offsetY, clickAt, true);
13563
+ }, 280);
13564
+ return;
13121
13565
  }
13566
+ const pointInPixel = [
13567
+ e.offsetX || ((_c = e.event) == null ? void 0 : _c.offsetX),
13568
+ e.offsetY || ((_d = e.event) == null ? void 0 : _d.offsetY)
13569
+ ];
13570
+ await this.processGeoPointOrBlank(
13571
+ laneId,
13572
+ pointInPixel[0],
13573
+ pointInPixel[1],
13574
+ clickAt,
13575
+ false
13576
+ );
13577
+ },
13578
+ async processGeoPointOrBlank(laneId, offsetX, offsetY, clickAt, wasActiveLane) {
13579
+ if (this._isUnmounted) return;
13580
+ const chartRef = this.getChartRefByLaneId(laneId);
13581
+ if (!chartRef || !chartRef.chart) return;
13582
+ const chart = chartRef.chart;
13583
+ const option = this.readChartOptionRefs(chart);
13584
+ const closestPoint = this.findClosestGeoLineHit(
13585
+ chart,
13586
+ option,
13587
+ [offsetX, offsetY],
13588
+ 10,
13589
+ laneId
13590
+ );
13591
+ const clickedPoint = closestPoint || null;
13122
13592
  if (clickedPoint) {
13123
13593
  this._lastBlankClickTime = null;
13124
13594
  this._lastBlankClickLaneId = null;
@@ -13177,18 +13647,19 @@ const _sfc_main$2 = {
13177
13647
  this.showSelectedPointScatter(laneId);
13178
13648
  }
13179
13649
  if (wasActiveLane) {
13180
- const now = Date.now();
13650
+ const now = clickAt;
13181
13651
  if (this._lastBlankClickLaneId === laneId && this._lastBlankClickTime && now - this._lastBlankClickTime < 500) {
13182
13652
  this._lastBlankClickTime = null;
13183
13653
  this._lastBlankClickLaneId = null;
13184
- if (this.selectedPoints.length > 0 && this.selectedLaneId) {
13654
+ if (this.selectedPoints.length > 0 && this.selectedLaneId === laneId) {
13185
13655
  this.laneSelectedPointPairs[this.selectedLaneId].pop();
13186
13656
  this.selectedPoints = [];
13187
13657
  this.selectedLaneId = null;
13188
13658
  this.selectedParamId = null;
13189
13659
  }
13190
- this.activeLaneId = null;
13660
+ this.removeLaneSelection(laneId);
13191
13661
  await this.updateLanesChartOption();
13662
+ if (this._isUnmounted) return;
13192
13663
  } else {
13193
13664
  this._lastBlankClickTime = now;
13194
13665
  this._lastBlankClickLaneId = laneId;
@@ -13199,12 +13670,13 @@ const _sfc_main$2 = {
13199
13670
  }
13200
13671
  }
13201
13672
  await this.$nextTick();
13673
+ if (this._isUnmounted) return;
13202
13674
  this.showSelectedPointScatter(laneId);
13203
13675
  },
13204
13676
  // 收集某泳道所有需要展示 scatter 的点:已完成区块的两个点 + 进行中的第一个点
13205
- // 仅在地应力模式下的当前激活泳道才收集并显示
13677
+ // 地应力已选中的泳道都收集并显示。8.25
13206
13678
  collectScatterPointsForLane(laneId) {
13207
- if (!this.isGeomechanical || laneId !== this.activeLaneId) return [];
13679
+ if (!this.isGeomechanical || !this.isLaneSelected(laneId)) return [];
13208
13680
  const points = [];
13209
13681
  const pairs = this.laneSelectedPointPairs[laneId] || [];
13210
13682
  pairs.forEach((pair) => {
@@ -13333,15 +13805,9 @@ const _sfc_main$2 = {
13333
13805
  return;
13334
13806
  }
13335
13807
  if (!this.isGeomechanical) return;
13336
- if (this.activeLaneId !== laneId) {
13337
- const prevActiveLaneId = this.activeLaneId;
13338
- this.activeLaneId = laneId;
13808
+ if (!this.isLaneSelected(laneId)) {
13809
+ this.addLaneSelection(laneId);
13339
13810
  this.updateLanesChartOption();
13340
- if (prevActiveLaneId) {
13341
- this.$nextTick(() => {
13342
- this.showSelectedPointScatter(prevActiveLaneId);
13343
- });
13344
- }
13345
13811
  }
13346
13812
  const chart = chartRef.chart;
13347
13813
  const pointInPixel = [
@@ -13956,7 +14422,7 @@ const _sfc_main$2 = {
13956
14422
  for (let i = 0; i < currentMarkAreaData.length; i++) {
13957
14423
  const areaItem = currentMarkAreaData[i];
13958
14424
  if (!areaItem.data || areaItem.data.length < 2) continue;
13959
- if (lineInfo.laneId !== this.activeLaneId && areaItem.type === "markArea")
14425
+ if (!this.isLaneSelected(lineInfo.laneId) && areaItem.type === "markArea")
13960
14426
  continue;
13961
14427
  const hasTimestamp = areaItem.data[0].timestamp;
13962
14428
  const sortedData = hasTimestamp ? [...areaItem.data].sort(
@@ -14410,7 +14876,7 @@ const _sfc_main$2 = {
14410
14876
  }
14411
14877
  }
14412
14878
  seriesIndex++;
14413
- if (lineInfo.isUsed === "1" && this.isGeomechanical && lane.laneId === this.activeLaneId) {
14879
+ if (lineInfo.isUsed === "1" && this.isGeomechanical && this.isLaneSelected(lane.laneId)) {
14414
14880
  const boundaryPoints = this.findMissingDataBoundaryPoints(
14415
14881
  this.plotSeries(seriesData, isLogScale)
14416
14882
  );
@@ -14493,10 +14959,6 @@ const _sfc_main$2 = {
14493
14959
  } finally {
14494
14960
  this.isUpdatingChart = false;
14495
14961
  this.scheduleConnectAllCharts();
14496
- if (this.trendLineManager) {
14497
- this.trendLineManager.scheduleZoomRender();
14498
- this.$nextTick(() => this.trendLineManager.scheduleZoomRender());
14499
- }
14500
14962
  }
14501
14963
  },
14502
14964
  // 清空所有图表实例
@@ -14522,6 +14984,7 @@ const _sfc_main$2 = {
14522
14984
  this.markAreaLineData = [];
14523
14985
  this.laneSelectedPointPairs = {};
14524
14986
  this.activeLaneId = null;
14987
+ this.activeLaneIds = [];
14525
14988
  this.lineRange = {};
14526
14989
  this.pendingRequestData = null;
14527
14990
  this.initialRealTimeData = {};
@@ -15012,7 +15475,8 @@ function _sfc_render$2(_ctx, _cache, $props, $setup, $data, $options) {
15012
15475
  visibleData: $data.visibleData,
15013
15476
  headerData: $data.headerData,
15014
15477
  lineRange: $data.lineRange,
15015
- activeLaneId: $data.activeLaneId
15478
+ activeLaneId: $data.activeLaneId,
15479
+ activeLaneIds: $data.activeLaneIds
15016
15480
  }), createSlots({ _: 2 }, [
15017
15481
  renderList($props.headerSlotName, (lane) => {
15018
15482
  return {
@@ -15112,7 +15576,7 @@ function _sfc_render$2(_ctx, _cache, $props, $setup, $data, $options) {
15112
15576
  ])
15113
15577
  };
15114
15578
  })
15115
- ]), 1040, ["onTemplateChange", "onLineChange", "onLaneWidthChange", "onParamsChange", "visibleData", "headerData", "lineRange", "activeLaneId"])
15579
+ ]), 1040, ["onTemplateChange", "onLineChange", "onLaneWidthChange", "onParamsChange", "visibleData", "headerData", "lineRange", "activeLaneId", "activeLaneIds"])
15116
15580
  ], 512),
15117
15581
  withDirectives(createElementVNode("div", _hoisted_2$2, null, 512), [
15118
15582
  [vShow, $data.crosshairVisible]
@@ -15155,7 +15619,7 @@ function _sfc_render$2(_ctx, _cache, $props, $setup, $data, $options) {
15155
15619
  ], 40, _hoisted_3$1)) : (openBlock(), createElementBlock("div", {
15156
15620
  key: 1,
15157
15621
  class: "context-menu-item",
15158
- onClick: ($event) => $options.handleContextMenuItemClick(item)
15622
+ onClick: withModifiers(($event) => $options.handleContextMenuItemClick(item), ["stop"])
15159
15623
  }, [
15160
15624
  createElementVNode("span", null, toDisplayString(item.label), 1)
15161
15625
  ], 8, _hoisted_5))
@@ -15167,7 +15631,7 @@ function _sfc_render$2(_ctx, _cache, $props, $setup, $data, $options) {
15167
15631
  ])
15168
15632
  ], 544);
15169
15633
  }
15170
- const chartContainer = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["render", _sfc_render$2], ["__scopeId", "data-v-22f7e692"]]);
15634
+ const chartContainer = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["render", _sfc_render$2], ["__scopeId", "data-v-9a816c60"]]);
15171
15635
  const _sfc_main$1 = {
15172
15636
  name: "ParameterPanel",
15173
15637
  props: {
@@ -16313,6 +16777,12 @@ const _sfc_main = {
16313
16777
  this.$refs.chartContainer.resetAllTrendLines();
16314
16778
  }
16315
16779
  },
16780
+ // 退出编辑时清掉泳道选中。8.25
16781
+ clearActiveLane() {
16782
+ if (this.$refs.chartContainer && this.$refs.chartContainer.clearActiveLane) {
16783
+ this.$refs.chartContainer.clearActiveLane();
16784
+ }
16785
+ },
16316
16786
  // 新增:更新form数据缓存
16317
16787
  updateForm(content) {
16318
16788
  let key = cloneDeep(content.key);
@@ -16682,7 +17152,7 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
16682
17152
  ])])) : createCommentVNode("", true)
16683
17153
  ]);
16684
17154
  }
16685
- const FrameLayoutComponent = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-76b2334b"]]);
17155
+ const FrameLayoutComponent = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-51f78d28"]]);
16686
17156
  KdLaneContainerComponent.install = function(app) {
16687
17157
  app.component(KdLaneContainerComponent.name, KdLaneContainerComponent);
16688
17158
  };