From 51a1786ef3e4e016d5f3f7bad8c2e2e8a84247a6 Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期日, 22 三月 2026 18:42:19 +0800
Subject: [PATCH] #
---
src/main/webapp/components/MapCanvas.js | 201 ++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 184 insertions(+), 17 deletions(-)
diff --git a/src/main/webapp/components/MapCanvas.js b/src/main/webapp/components/MapCanvas.js
index b5ced58..a4c0a7d 100644
--- a/src/main/webapp/components/MapCanvas.js
+++ b/src/main/webapp/components/MapCanvas.js
@@ -12,6 +12,7 @@
鍦坽{ item.loopNo }} |
绔欑偣: {{ item.stationCount || 0 }} |
浠诲姟: {{ item.taskCount || 0 }} |
+ 鎵嬪姩: {{ item.manualStationCount || 0 }} |
鎵胯浇: {{ formatLoadPercent(item.currentLoad) }}
</div>
</div>
@@ -49,7 +50,7 @@
</div>
</div>
`,
- props: ['lev', 'levList', 'crnParam', 'rgvParam', 'devpParam', 'stationTaskRange', 'highlightOnParamChange', 'viewportPadding', 'hudPadding'],
+ props: ['lev', 'levList', 'crnParam', 'rgvParam', 'devpParam', 'stationTaskRange', 'highlightOnParamChange', 'viewportPadding', 'hudPadding', 'traceOverlay'],
data() {
return {
map: [],
@@ -76,6 +77,8 @@
},
pixiShelfMap: new Map(),
pixiTrackMap: new Map(),
+ pixiCrnTextureMap: new Map(),
+ pixiRgvTextureMap: new Map(),
pixiDevpTextureMap: new Map(),
pixiCrnColorTextureMap: new Map(),
pixiDevpTextureMap: new Map(),
@@ -103,6 +106,8 @@
hoverRaf: null,
objectsContainer: null,
objectsContainer2: null,
+ traceOverlayContainer: null,
+ tracePulseTween: null,
tracksContainer: null,
tracksGraphics: null,
shelvesContainer: null,
@@ -119,6 +124,7 @@
},
shelfTooltipMinScale: 0.4,
containerResizeObserver: null,
+ resizeDebounceTimer: null,
timer: null,
adjustLabelTimer: null,
isSwitchingFloor: false,
@@ -126,6 +132,8 @@
loopList: [],
totalStationCount: 0,
taskStationCount: 0,
+ manualStationCount: 0,
+ occupiedStationCount: 0,
currentLoad: 0
},
showMapToolPanel: false,
@@ -173,10 +181,12 @@
if (this.hoverRaf) { cancelAnimationFrame(this.hoverRaf); this.hoverRaf = null; }
if (this.shelfCullRaf) { cancelAnimationFrame(this.shelfCullRaf); this.shelfCullRaf = null; }
+ if (this.resizeDebounceTimer) { clearTimeout(this.resizeDebounceTimer); this.resizeDebounceTimer = null; }
if (window.gsap && this.pixiApp && this.pixiApp.stage) { window.gsap.killTweensOf(this.pixiApp.stage.position); }
+ this.clearTraceOverlay();
if (this.pixiApp) { this.pixiApp.destroy(true, { children: true }); }
if (this.containerResizeObserver) { this.containerResizeObserver.disconnect(); this.containerResizeObserver = null; }
- window.removeEventListener('resize', this.resizeToContainer);
+ window.removeEventListener('resize', this.scheduleResizeToContainer);
if (this.wsReconnectTimer) { clearTimeout(this.wsReconnectTimer); this.wsReconnectTimer = null; }
if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) { try { this.ws.close(); } catch (e) {} }
},
@@ -232,6 +242,12 @@
window.gsap.fromTo(sprite, { alpha: 1 }, { alpha: 0.2, yoyo: true, repeat: 6, duration: 0.15 });
}
}
+ }
+ },
+ traceOverlay: {
+ deep: true,
+ handler() {
+ this.renderTraceOverlay();
}
}
},
@@ -377,11 +393,12 @@
this.pixiApp.view.style.height = '100%';
this.pixiApp.view.style.display = 'block';
this.resizeToContainer();
- window.addEventListener('resize', this.resizeToContainer);
+ window.addEventListener('resize', this.scheduleResizeToContainer);
this.graphicsCrnTrack = this.createTrackTexture(25, 25, 10);
this.graphicsRgvTrack = this.createTrackTexture(25, 25, 10);
this.objectsContainer = new PIXI.Container();
this.objectsContainer2 = new PIXI.Container();
+ this.traceOverlayContainer = new PIXI.Container();
this.tracksContainer = new PIXI.ParticleContainer(10000, { scale: true, position: true, rotation: false, uvs: false, alpha: false });
this.tracksGraphics = new PIXI.Graphics();
this.shelvesContainer = new PIXI.Container();
@@ -393,6 +410,7 @@
this.mapRoot.addChild(this.shelvesContainer);
this.mapRoot.addChild(this.objectsContainer);
this.mapRoot.addChild(this.objectsContainer2);
+ this.mapRoot.addChild(this.traceOverlayContainer);
this.pixiApp.renderer.roundPixels = true;
this.hoveredShelfCell = null;
this.hoverPointer = { x: 0, y: 0 };
@@ -490,9 +508,18 @@
startContainerResizeObserve() {
if (typeof ResizeObserver === 'undefined' || !this.$el) { return; }
this.containerResizeObserver = new ResizeObserver(() => {
- this.resizeToContainer();
+ this.scheduleResizeToContainer();
});
this.containerResizeObserver.observe(this.$el);
+ },
+ scheduleResizeToContainer() {
+ if (this.resizeDebounceTimer) {
+ clearTimeout(this.resizeDebounceTimer);
+ }
+ this.resizeDebounceTimer = setTimeout(() => {
+ this.resizeDebounceTimer = null;
+ this.resizeToContainer();
+ }, 80);
},
getViewportSize() {
if (!this.pixiApp || !this.pixiApp.renderer) { return { width: 0, height: 0 }; }
@@ -583,6 +610,7 @@
changeFloor(lev) {
this.currentLev = lev;
this.clearLoopStationHighlight();
+ this.clearTraceOverlay();
this.isSwitchingFloor = true;
this.hideShelfTooltip();
this.hoveredShelfCell = null;
@@ -608,6 +636,7 @@
},
createMapData(map) {
this.clearLoopStationHighlight();
+ this.clearTraceOverlay();
this.hideShelfTooltip();
this.hoveredShelfCell = null;
this.mapRowOffsets = [];
@@ -760,8 +789,7 @@
});
this.crnList.forEach((item) => {
- if (this.graphicsCrn == null) { this.graphicsCrn = this.createCrnTexture(item.width * 0.9, item.height * 0.9); }
- let sprite = new PIXI.Sprite(this.graphicsCrn);
+ let sprite = this.createCrnSprite(item.width * 0.9, item.height * 0.9);
const deviceNo = this.getDeviceNo(item.value);
const taskNo = this.getTaskNo(item.value);
const style = new PIXI.TextStyle({ fontFamily: 'Arial', fontSize: 12, fill: '#000000', stroke: '#ffffff', strokeThickness: 1 });
@@ -793,8 +821,7 @@
});
this.dualCrnList.forEach((item) => {
- if (this.graphicsCrn == null) { this.graphicsCrn = this.createCrnTexture(item.width * 0.9, item.height * 0.9); }
- let sprite = new PIXI.Sprite(this.graphicsCrn);
+ let sprite = this.createCrnSprite(item.width * 0.9, item.height * 0.9);
const deviceNo = this.getDeviceNo(item.value);
const taskNo = this.getTaskNo(item.value);
const style = new PIXI.TextStyle({ fontFamily: 'Arial', fontSize: 12, fill: '#000000', stroke: '#ffffff', strokeThickness: 1 });
@@ -826,8 +853,7 @@
});
this.rgvList.forEach((item) => {
- if (this.graphicsRgv == null) { this.graphicsRgv = this.createRgvTexture(item.width * 0.9, item.height * 0.9); }
- let sprite = new PIXI.Sprite(this.graphicsRgv);
+ let sprite = this.createRgvSprite(item.width * 0.9, item.height * 0.9);
const deviceNo = this.getDeviceNo(item.value);
const taskNo = this.getTaskNo(item.value);
const style = new PIXI.TextStyle({ fontFamily: 'Arial', fontSize: 12, fill: '#000000', stroke: '#ffffff', strokeThickness: 1 });
@@ -875,6 +901,7 @@
this.applyMapTransform(true);
this.map = map;
this.isSwitchingFloor = false;
+ this.renderTraceOverlay();
},
initWidth(map) {
let maxRow = map.length;
@@ -959,6 +986,8 @@
loopList: loopList,
totalStationCount: payload.totalStationCount || 0,
taskStationCount: payload.taskStationCount || 0,
+ manualStationCount: payload.manualStationCount || 0,
+ occupiedStationCount: payload.occupiedStationCount || 0,
currentLoad: typeof payload.currentLoad === 'number' ? payload.currentLoad : parseFloat(payload.currentLoad || 0)
};
if (this.hoverLoopNo != null) {
@@ -1197,6 +1226,28 @@
if (texture == undefined) {
texture = this.createTrackTexture(width, height, trackMask);
this.pixiTrackMap.set(idx, texture);
+ }
+ return new PIXI.Sprite(texture);
+ },
+ createCrnSprite(width, height) {
+ const w = Math.max(1, Math.round(width));
+ const h = Math.max(1, Math.round(height));
+ const key = w + "-" + h;
+ let texture = this.pixiCrnTextureMap.get(key);
+ if (texture == undefined) {
+ texture = this.createCrnTexture(w, h);
+ this.pixiCrnTextureMap.set(key, texture);
+ }
+ return new PIXI.Sprite(texture);
+ },
+ createRgvSprite(width, height) {
+ const w = Math.max(1, Math.round(width));
+ const h = Math.max(1, Math.round(height));
+ const key = w + "-" + h;
+ let texture = this.pixiRgvTextureMap.get(key);
+ if (texture == undefined) {
+ texture = this.createRgvTexture(w, h);
+ this.pixiRgvTextureMap.set(key, texture);
}
return new PIXI.Sprite(texture);
},
@@ -1804,6 +1855,129 @@
}
this.hoverLoopNo = null;
this.hoverLoopStationIdSet = new Set();
+ },
+ clearTraceOverlay() {
+ if (window.gsap && this.tracePulseTween) {
+ try { this.tracePulseTween.kill(); } catch (e) {}
+ }
+ this.tracePulseTween = null;
+ if (!this.traceOverlayContainer) { return; }
+ const children = this.traceOverlayContainer.removeChildren();
+ children.forEach((child) => {
+ if (child && typeof child.destroy === 'function') {
+ child.destroy({ children: true, texture: false, baseTexture: false });
+ }
+ });
+ },
+ normalizeTraceOverlay(trace) {
+ if (!trace) { return null; }
+ const taskNo = parseInt(trace.taskNo, 10);
+ return {
+ taskNo: isNaN(taskNo) ? null : taskNo,
+ status: trace.status || '',
+ currentStationId: this.parseStationTaskNo(trace.currentStationId),
+ finalTargetStationId: this.parseStationTaskNo(trace.finalTargetStationId),
+ blockedStationId: this.parseStationTaskNo(trace.blockedStationId),
+ passedStationIds: this.normalizeTraceStationIds(trace.passedStationIds),
+ pendingStationIds: this.normalizeTraceStationIds(trace.pendingStationIds),
+ latestAppendedPath: this.normalizeTraceStationIds(trace.latestIssuedSegmentPath || trace.latestAppendedPath)
+ };
+ },
+ normalizeTraceStationIds(list) {
+ if (!Array.isArray(list)) { return []; }
+ const result = [];
+ list.forEach((item) => {
+ const stationId = parseInt(item, 10);
+ if (!isNaN(stationId)) { result.push(stationId); }
+ });
+ return result;
+ },
+ getStationCenter(stationId) {
+ if (stationId == null || !this.pixiStaMap) { return null; }
+ const sprite = this.pixiStaMap.get(parseInt(stationId, 10));
+ if (!sprite) { return null; }
+ return {
+ x: sprite.x + sprite.width / 2,
+ y: sprite.y + sprite.height / 2
+ };
+ },
+ drawTracePairs(graphics, stationIds, color, width, alpha) {
+ if (!graphics || !Array.isArray(stationIds) || stationIds.length < 2) { return; }
+ graphics.lineStyle({ width: width, color: color, alpha: alpha, cap: PIXI.LINE_CAP.ROUND, join: PIXI.LINE_JOIN.ROUND });
+ for (let i = 1; i < stationIds.length; i++) {
+ const prev = this.getStationCenter(stationIds[i - 1]);
+ const curr = this.getStationCenter(stationIds[i]);
+ if (!prev || !curr) { continue; }
+ graphics.moveTo(prev.x, prev.y);
+ graphics.lineTo(curr.x, curr.y);
+ }
+ },
+ drawTraceMarker(container, stationId, options) {
+ const point = this.getStationCenter(stationId);
+ if (!container || !point) { return null; }
+ const marker = new PIXI.Container();
+ const ring = new PIXI.Graphics();
+ const fill = new PIXI.Graphics();
+ const radius = options && options.radius ? options.radius : 18;
+ const color = options && options.color != null ? options.color : 0x1d4ed8;
+ ring.lineStyle(3, color, 0.95);
+ ring.drawCircle(0, 0, radius);
+ fill.beginFill(color, 0.18);
+ fill.drawCircle(0, 0, Math.max(6, radius * 0.42));
+ fill.endFill();
+ marker.addChild(ring);
+ marker.addChild(fill);
+ marker.position.set(point.x, point.y);
+ container.addChild(marker);
+ return marker;
+ },
+ buildPendingTraceSequence(trace) {
+ const pending = this.normalizeTraceStationIds(trace && trace.pendingStationIds);
+ const currentStationId = this.parseStationTaskNo(trace && trace.currentStationId);
+ if (pending.length === 0) {
+ return currentStationId > 0 ? [currentStationId] : [];
+ }
+ if (currentStationId > 0 && pending[0] !== currentStationId) {
+ return [currentStationId].concat(pending);
+ }
+ return pending;
+ },
+ renderTraceOverlay() {
+ if (!this.traceOverlayContainer) { return; }
+ this.clearTraceOverlay();
+ const trace = this.normalizeTraceOverlay(this.traceOverlay);
+ if (!trace || !this.pixiStaMap || this.pixiStaMap.size === 0) { return; }
+
+ const graphics = new PIXI.Graphics();
+ this.drawTracePairs(graphics, trace.passedStationIds, 0x2563eb, 5, 0.95);
+ this.drawTracePairs(graphics, this.buildPendingTraceSequence(trace), 0xf97316, 4, 0.9);
+ this.drawTracePairs(graphics, trace.latestAppendedPath, 0xfacc15, 7, 0.88);
+ this.traceOverlayContainer.addChild(graphics);
+
+ const currentMarker = this.drawTraceMarker(this.traceOverlayContainer, trace.currentStationId, {
+ color: 0x2563eb,
+ radius: 20
+ });
+ if (currentMarker && window.gsap) {
+ this.tracePulseTween = window.gsap.to(currentMarker.scale, {
+ x: 1.18,
+ y: 1.18,
+ duration: 0.55,
+ repeat: -1,
+ yoyo: true,
+ ease: 'sine.inOut'
+ });
+ }
+
+ if (trace.blockedStationId > 0) {
+ const blockedMarker = this.drawTraceMarker(this.traceOverlayContainer, trace.blockedStationId, {
+ color: 0xdc2626,
+ radius: 22
+ });
+ if (blockedMarker) {
+ blockedMarker.alpha = 0.95;
+ }
+ }
},
handleLoopCardEnter(loopItem) {
if (!loopItem) { return; }
@@ -2646,13 +2820,6 @@
}
}
});
-
-
-
-
-
-
-
--
Gitblit v1.9.1