|
|
@@ -6,9 +6,44 @@
|
|
|
import * as echarts from 'echarts';
|
|
|
import echartsResize from '@/mixins/echartsResize.js';
|
|
|
|
|
|
+// 模块级共享定时器:所有实例订阅同一个 tick,保证扫描线完全同步
|
|
|
+let sharedEpoch = 0;
|
|
|
+let sharedTimer = null;
|
|
|
+let sharedListeners = new Set();
|
|
|
+let sharedBatchId = 0; // 批次ID,切换分页时递增
|
|
|
+
|
|
|
+function joinSharedTimer(listener, batchId) {
|
|
|
+ // 新批次:重置定时器和 epoch
|
|
|
+ if (batchId !== sharedBatchId) {
|
|
|
+ sharedBatchId = batchId;
|
|
|
+ if (sharedTimer) { clearInterval(sharedTimer); sharedTimer = null; }
|
|
|
+ sharedListeners.clear();
|
|
|
+ sharedEpoch = Math.floor(Date.now() / 1000);
|
|
|
+ }
|
|
|
+ if (!sharedTimer) {
|
|
|
+ sharedEpoch = Math.floor(Date.now() / 1000);
|
|
|
+ sharedTimer = setInterval(() => {
|
|
|
+ const elapsed = Math.floor(Date.now() / 1000) - sharedEpoch;
|
|
|
+ sharedListeners.forEach(fn => fn(elapsed));
|
|
|
+ }, 1000);
|
|
|
+ }
|
|
|
+ sharedListeners.add(listener);
|
|
|
+ // 立即触发一次,避免 mounted 到首次 tick 之间的闪跳
|
|
|
+ listener(Math.floor(Date.now() / 1000) - sharedEpoch);
|
|
|
+}
|
|
|
+
|
|
|
+function leaveSharedTimer(listener) {
|
|
|
+ sharedListeners.delete(listener);
|
|
|
+ if (sharedListeners.size === 0 && sharedTimer) {
|
|
|
+ clearInterval(sharedTimer);
|
|
|
+ sharedTimer = null;
|
|
|
+ sharedEpoch = 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
const COLORS = {
|
|
|
GREEN_LIGHT: '#8dc453', GREEN_DARK: '#73a542', YELLOW: '#fbd249', RED: '#ff7575', STRIPE_GREEN: '#a3d76e',
|
|
|
- TEXT_DARK: '#1e2638', AXIS_LINE: '#758599', DIVIDER_LINE: '#111827', MARK_BLUE: '#4da8ff', TEXT_LIGHT: '#d1d5db'
|
|
|
+ TEXT_DARK: '#1e2638', AXIS_LINE: '#758599', DIVIDER_LINE: '#111827', MARK_BLUE: '#00E5FF', TEXT_LIGHT: '#d1d5db'
|
|
|
};
|
|
|
|
|
|
// 绘制条纹图案用于绿闪/预警
|
|
|
@@ -96,7 +131,8 @@ export default {
|
|
|
if (!this.autoScan) {
|
|
|
if (this.$_chart) this.updateScanLine();
|
|
|
} else {
|
|
|
- this.internalTime = val;
|
|
|
+ // 页切换时 currentTime 变化,重新加入共享定时器触发 epoch 重置
|
|
|
+ this.startAutoScan();
|
|
|
}
|
|
|
},
|
|
|
autoScan(val) {
|
|
|
@@ -118,14 +154,19 @@ export default {
|
|
|
},
|
|
|
startAutoScan() {
|
|
|
this.stopAutoScan();
|
|
|
- this._scanTimer = setInterval(() => {
|
|
|
- const max = this.cycleLength || 140;
|
|
|
- this.internalTime = this.internalTime >= max ? 0 : this.internalTime + 1;
|
|
|
+ // 统一视觉周期:所有行在 VISUAL_PERIOD 秒内完成一次扫描
|
|
|
+ const VISUAL_PERIOD = 120;
|
|
|
+ this._scanListener = (elapsed) => {
|
|
|
+ const realMax = this.getMaxTime();
|
|
|
+ const offset = this.currentTime || 0; // 动态读取,不用闭包捕获
|
|
|
+ const ratio = ((offset + elapsed) % VISUAL_PERIOD) / VISUAL_PERIOD;
|
|
|
+ this.internalTime = ratio * realMax;
|
|
|
if (this.$_chart) this.updateScanLine();
|
|
|
- }, 1000);
|
|
|
+ };
|
|
|
+ joinSharedTimer(this._scanListener, this.currentTime || 0);
|
|
|
},
|
|
|
stopAutoScan() {
|
|
|
- if (this._scanTimer) { clearInterval(this._scanTimer); this._scanTimer = null; }
|
|
|
+ if (this._scanListener) { leaveSharedTimer(this._scanListener); this._scanListener = null; }
|
|
|
},
|
|
|
initChart() {
|
|
|
const chartDom = this.$refs.chartRef;
|
|
|
@@ -153,13 +194,13 @@ export default {
|
|
|
label: {
|
|
|
show: this.showScanLineLabel,
|
|
|
position: 'start',
|
|
|
- formatter: `${this.activeTime}/${realMaxTime}`,
|
|
|
+ formatter: `${Math.round(this.activeTime)}/${realMaxTime}`,
|
|
|
color: '#fff', backgroundColor: COLORS.MARK_BLUE,
|
|
|
padding: [Math.round(4 * s), Math.round(8 * s)],
|
|
|
borderRadius: 2, fontSize: Math.max(10, Math.round(10 * s)),
|
|
|
offset: [0, Math.round(1 * s)]
|
|
|
},
|
|
|
- lineStyle: { color: COLORS.MARK_BLUE, type: 'solid', width: Math.max(1, Math.round(2 * s)), z: 100 },
|
|
|
+ lineStyle: { color: COLORS.MARK_BLUE, type: 'solid', width: Math.max(2, Math.round(5 * s)), z: 100 },
|
|
|
data: [{ xAxis: this.activeTime }]
|
|
|
}
|
|
|
}]
|
|
|
@@ -198,12 +239,12 @@ export default {
|
|
|
animation: false,
|
|
|
label: {
|
|
|
show: this.showScanLineLabel,
|
|
|
- position: 'start', formatter: `${this.activeTime}/${realMaxTime}`,
|
|
|
+ position: 'start', formatter: `${Math.round(this.activeTime)}/${realMaxTime}`,
|
|
|
color: '#fff', backgroundColor: COLORS.MARK_BLUE, padding: [Math.round(4 * s), Math.round(8 * s)],
|
|
|
borderRadius: 2, fontSize: Math.max(10, Math.round(10 * s)),
|
|
|
offset: [0, Math.round(1 * s)]
|
|
|
},
|
|
|
- lineStyle: { color: COLORS.MARK_BLUE, type: 'solid', width: Math.max(1, Math.round(2 * s)), z: 100 },
|
|
|
+ lineStyle: { color: COLORS.MARK_BLUE, type: 'solid', width: Math.max(2, Math.round(5 * s)), z: 100 },
|
|
|
data: [ { xAxis: this.activeTime } ]
|
|
|
}
|
|
|
}]
|