#
vincentlu
2025-12-12 4b0ea0e7958398dd72b7bf411157cd3eb49e0ab6
zy-acs-flow/src/map/tool.js
@@ -14,7 +14,7 @@
    ANIMATE_DURING_TIME,
    AGV_ANGLE_OFFSET_VAL,
} from './constants';
import { getRouteList } from './http';
import { getRouteList, fetchAreaList } from './http';
import PointRoute from "./PointRoute";
import shelf from '/map/shelf.svg';
@@ -31,7 +31,7 @@
import ctuLoadedFullBattery from '/map/agv/ctuLoadedFullBattery.svg';
let app, mapContainer, themeMode;
let selectedSprite, effectCircle, effectTicker;
let selectedSprite, effectOverlay, effectTicker;
let tooltip;
export function getApp() {
@@ -422,6 +422,38 @@
}
export const showSelectedEffect = (sprite) => {
    if (!sprite) {
        return;
    }
    // area
    if (sprite.data?.type === DEVICE_TYPE.AREA) {
        const { start, end } = sprite.data || {};
        const hasBounds = start && end;
        const minX = hasBounds ? Math.min(start.x, end.x) : 0;
        const maxX = hasBounds ? Math.max(start.x, end.x) : 0;
        const minY = hasBounds ? Math.min(start.y, end.y) : 0;
        const maxY = hasBounds ? Math.max(start.y, end.y) : 0;
        const width = hasBounds ? Math.abs(maxX - minX) : Math.abs(sprite.width);
        const height = hasBounds ? Math.abs(maxY - minY) : Math.abs(sprite.height);
        const centerX = hasBounds ? (minX + maxX) / 2 : sprite.x;
        const centerY = hasBounds ? (minY + maxY) / 2 : sprite.y;
        const color = DEVICE_SELECTED_EFFECT_COLOR[DEVICE_TYPE.AREA] || (themeMode === 'light' ? '#747d8c' : '#718093');
        const lineWidth = Math.max(2, 4 / Math.abs(mapContainer.scale.x || 1));
        effectOverlay = new PIXI.Graphics();
        effectOverlay.lineStyle(lineWidth, color, 1);
        effectOverlay.drawRect(-width / 2, -height / 2, width, height);
        effectOverlay.position.set(centerX, centerY);
        effectOverlay.zIndex = sprite.zIndex + 1;
        effectOverlay.blendMode = PIXI.BLEND_MODES.NORMAL;
        mapContainer.addChild(effectOverlay);
        selectedSprite = sprite;
        effectTicker = null;
        return;
    }
    // others
    if (!sprite?.texture || !sprite?.texture?.valid) {
        return;
    }
@@ -440,15 +472,15 @@
    const alpha = 1;
    effectCircle = new PIXI.Graphics();
    effectCircle.beginFill(color, alpha);
    effectCircle.drawCircle(0, 0, radius);
    effectCircle.endFill();
    effectCircle.position.set(sprite.x, sprite.y);
    effectCircle.zIndex = -1;
    effectCircle.blendMode = PIXI.BLEND_MODES.NORMAL;
    effectOverlay = new PIXI.Graphics();
    effectOverlay.beginFill(color, alpha);
    effectOverlay.drawCircle(0, 0, radius);
    effectOverlay.endFill();
    effectOverlay.position.set(sprite.x, sprite.y);
    effectOverlay.zIndex = -1;
    effectOverlay.blendMode = PIXI.BLEND_MODES.NORMAL;
    mapContainer.addChild(effectCircle);
    mapContainer.addChild(effectOverlay);
    selectedSprite = sprite;
@@ -467,8 +499,8 @@
                scalingUp = true;
            }
        }
        effectCircle.scale.set(pulseScale);
        effectCircle.position.set(sprite.x, sprite.y);
        effectOverlay.scale.set(pulseScale);
        effectOverlay.position.set(sprite.x, sprite.y);
    };
    app.ticker.add(effectTicker);
@@ -479,18 +511,11 @@
        app.ticker.remove(effectTicker);
        effectTicker = null;
    }
    if (effectCircle) {
        mapContainer?.removeChild(effectCircle);
        effectCircle = null;
    if (effectOverlay) {
        mapContainer?.removeChild(effectOverlay);
        effectOverlay = null;
    }
    selectedSprite = null;
};
export const updateEffect = () => {
    if (!selectedSprite || !effectCircle) {
        return;
    }
    effectCircle.position.set(selectedSprite.x, selectedSprite.y);
};
export const showRoutes = (curZone, setShowRoutes, setLoading) => {
@@ -877,32 +902,110 @@
}
// zone operator -------------------------------
// area operator -------------------------------
let zoneDrawingCleanup = null;
let areaDrawingCleanup = null;
const AREA_COLOR = 0x3498db;
const AREA_BORDER_COLOR = 0x6c7a89;
export const startZoneDrawing = ({ promptText, onComplete } = {}) => {
    if (!mapContainer || !mapContainer.parent) {
        return;
const addAreaLabel = (draft, text, from, to) => {
    const centerX = (from.x + to.x) / 2;
    const centerY = (from.y + to.y) / 2;
    const label = new PIXI.Text(text, {
        fill: themeMode === 'dark' ? '#f1f2f6' : '#535353ff',
        fontSize: 20 / Math.abs(mapContainer.scale.x || 1),
        fontWeight: 'bold',
    });
    label.anchor.set(0.5);
    label.position.set(centerX, centerY);
    label.rotation = rotationParseNum(MAP_DEFAULT_ROTATION);
    label.scale.set(MAP_MIRROR ? -1 : 1, 1);
    draft.addChild(label);
};
export const loadAreas = (curZone, setCurSprite) => {
    if (!mapContainer) return;
    clearAreas();
    fetchAreaList(curZone).then((areas) => {
        areas.forEach((area) => {
            const { name, color, id } = area || {};
            const start = area?.start;
            const end = area?.end;
            if (!start || !end || !name || !id) {
                return;
            }
            const graphics = createAreaGraphic({ name, start, end, color, id }, setCurSprite);
            if (graphics) {
                mapContainer.addChild(graphics);
            }
        });
    });
};
export const clearAreas = () => {
    if (!mapContainer) return;
    for (let i = mapContainer.children.length - 1; i >= 0; i--) {
        const child = mapContainer.children[i];
        if (child?.data?.type === DEVICE_TYPE.AREA) {
            mapContainer.removeChild(child);
            child.destroy({ children: true, texture: false, baseTexture: false });
        }
    }
    mapContainer?.parent?.off('mousedown');
};
    if (zoneDrawingCleanup) {
        zoneDrawingCleanup();
const createAreaGraphic = ({ name, start, end, color, id }, setCurSprite) => {
    if (!mapContainer) return null;
    const from = start || { x: 0, y: 0 };
    const to = end || { x: 0, y: 0 };
    const areaColor = color || AREA_COLOR;
    const draft = new PIXI.Graphics();
    draft.name = id ? `area_${id}` : 'area_' + generateID();
    draft.zIndex = DEVICE_Z_INDEX.AREA;
    draft.lineStyle(2 / Math.abs(mapContainer.scale.x || 1), AREA_BORDER_COLOR, 0.9);
    draft.beginFill(areaColor, 0.18);
    draft.drawRect(
        Math.min(from.x, to.x),
        Math.min(from.y, to.y),
        Math.abs(from.x - to.x),
        Math.abs(from.y - to.y),
    );
    draft.endFill();
    addAreaLabel(draft, name, from, to);
    draft.data = { ...(draft.data || {}), type: DEVICE_TYPE.AREA, name, color: areaColor, id, start: from, end: to };
    draft.eventMode = 'static';
    draft.cursor = 'pointer';
    if (setCurSprite) {
        draft.off('click');
        draft.on('click', () => setCurSprite(draft));
    }
    return draft;
};
export const startAreaDrawing = ({ promptText, onComplete, onFinish, setCurSprite } = {}) => {
    if (!mapContainer || !mapContainer.parent) {
        return false;
    }
    if (areaDrawingCleanup) {
        areaDrawingCleanup();
    }
    const stage = mapContainer.parent;
    stage.off('mousedown');
    const draft = new PIXI.Graphics();
    draft.name = 'zone_' + generateID();
    draft.zIndex = DEVICE_Z_INDEX.ZONE;
    draft.name = 'area_' + generateID();
    draft.zIndex = DEVICE_Z_INDEX.AREA;
    let drawing = false;
    let startPoint = null;
    const areaColor = AREA_COLOR;
    const originalCursor = stage.cursor;
    stage.cursor = 'crosshair';
    const drawRect = (from, to) => {
        draft.clear();
        const strokeColor = themeMode === 'dark' ? 0x74b9ff : 0x2ecc71;
        draft.lineStyle(4 / Math.abs(mapContainer.scale.x || 1), strokeColor, 0.8);
        const strokeColor = areaColor;
        draft.lineStyle(2 / Math.abs(mapContainer.scale.x || 1), AREA_BORDER_COLOR, 0.9);
        draft.beginFill(strokeColor, 0.18);
        draft.drawRect(
            Math.min(from.x, to.x),
@@ -913,26 +1016,17 @@
        draft.endFill();
    };
    const addZoneLabel = (text) => {
        const bounds = draft.getBounds();
        const label = new PIXI.Text(text, {
            fill: themeMode === 'dark' ? '#f1f2f6' : '#2d3436',
            fontSize: 48 / Math.abs(mapContainer.scale.x || 1),
            fontWeight: 'bold',
            fontFamily: 'Microsoft YaHei',
        });
        label.anchor.set(0.5);
        label.position.set(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2);
        draft.addChild(label);
    };
    const cleanupListeners = () => {
        stage.off('pointermove', handleMove);
        stage.off('pointerup', handleUp);
        stage.off('pointerdown', handleDown);
        zoneDrawingCleanup = null;
        areaDrawingCleanup = null;
        stage.cursor = originalCursor;
        if (onFinish) {
            onFinish();
        }
    };
    zoneDrawingCleanup = cleanupListeners;
    areaDrawingCleanup = cleanupListeners;
    const handleDown = (event) => {
        if (event.button !== undefined && event.button !== 0) {
@@ -960,22 +1054,35 @@
        drawRect(startPoint, endPoint);
        cleanupListeners();
        const zoneName = prompt(promptText || 'Please enter zone name');
        if (!zoneName) {
        const areaName = prompt(promptText || 'Please enter area name');
        if (!areaName) {
            mapContainer.removeChild(draft);
            draft.destroy({ children: true, texture: false, baseTexture: false });
            return;
        }
        addZoneLabel(zoneName);
        draft.data = { ...(draft.data || {}), type: DEVICE_TYPE.ZONE, name: zoneName };
        addAreaLabel(draft, areaName, startPoint, endPoint);
        draft.data = { ...(draft.data || {}), type: DEVICE_TYPE.AREA, name: areaName, color: areaColor, start: startPoint, end: endPoint };
        draft.eventMode = 'static';
        draft.cursor = 'pointer';
        if (setCurSprite) {
            draft.off('click');
            draft.on('click', () => setCurSprite(draft));
        }
        if (onComplete) {
            onComplete({ name: zoneName, start: startPoint, end: endPoint, graphics: draft });
            onComplete({
                name: areaName,
                start: startPoint,
                end: endPoint,
                color: areaColor,
                graphics: draft
            });
        }
    };
    stage.on('pointerdown', handleDown);
    stage.on('pointermove', handleMove);
    stage.on('pointerup', handleUp);
};
    return true;
};