skyouc
2024-12-21 c635d78b479510ebe2556a420948effcd30a0731
zy-asrs-admin/src/components/loc/map/index.vue
@@ -1,772 +1,772 @@
<script setup>
import { ref, onMounted, watch } from 'vue';
import { get, post, postBlob } from '@/utils/request.js'
import { message, Modal } from 'ant-design-vue';
import { logout } from '@/config.js';
import { formatMessage } from '@/utils/localeUtils.js';
import {
    CompressOutlined,
} from "@ant-design/icons-vue";
import * as PIXI from 'pixi.js'
let width = 25;
let height = 25;
let pixiApp;
let pixiStageMap = new Map();
let objectsContainer;
let map = []
const pixiView = ref(null)
const mapFps = ref(0)
const currentLev = ref(1)
const levList = ref([])
const pointContainerWidth = ref(0)
const drawer = ref(false)
const drawerLocData = ref(null)
const drawerLocDetls = ref([])
const drawerLocDetlField = ref([])
const drawerOper = ref(false)
const drawerOperLocNo = ref(null)
const drawerOperMatnr = ref(null)
const drawerOperMaktx = ref(null)
onMounted(() => {
    createMap();
    init(currentLev.value);
})
watch(drawer, (newVal, oldVal) => {
    if (!drawer.value) {
        var rectangle = pixiStageMap.get(drawerLocData.value.locNo)
        updateColor(rectangle, rectangle.originColor);//恢复颜色
    }
})
function switchLev(lev) {
    currentLev.value = lev;
    init(lev);
}
function init(lev) {
    get('/api/locMap/getData/' + lev + '/auth', {}).then(resp => {
        let result = resp.data;
        if (result.code == 200) {
            let tmp = JSON.parse(result.data);
            let tmpMap = []
            tmp.forEach((item, index) => {
                let data2 = []
                item.forEach((val, idx) => {
                    val.searchStatus = false//搜索标记
                    val.rectangle = null;
                    data2.push(val)
                })
                pointContainerWidth.value = item.length * (40 + 1)
                tmpMap.push(data2)
            })
            createMapData(tmpMap);
        }
    })
    get('/api/locMap/getLev', {}).then(resp => {
        let result = resp.data;
        if (result.code == 200) {
            let tmp = result.data;
            levList.value = tmp;
        }
    })
}
function createMap() {
    //Create a Pixi Application
    pixiApp = new PIXI.Application({
        width: pixiView.value.offsetWidth,
        height: window.innerHeight * 0.75,
        backgroundColor: 0xF5F7F9FF,
        // resizeTo: window
    });
    //Add the canvas that Pixi automatically created for you to the HTML document
    pixiView.value.appendChild(pixiApp.view)
    // 创建一个容器来管理大批量的显示对象
    objectsContainer = new PIXI.Container();
    pixiApp.stage.addChild(objectsContainer);
    //*******************拖动画布*******************
    let stageOriginalPos;
    let mouseDownPoint;
    let touchBlank = false;
    pixiApp.renderer.plugins.interaction.on(
        'pointerdown',
        (event) => {
            const globalPos = event.data.global;
            // 记录下stage原来的位置
            stageOriginalPos = [pixiApp.stage.position._x, pixiApp.stage.position._y];
            // 记录下mouse down的位置
            mouseDownPoint = [globalPos.x, globalPos.y];
            if (!event.target) {
                // 点到了画布的空白位置
                touchBlank = true;
            }
        }
    );
    pixiApp.renderer.plugins.interaction.on(
        'pointermove',
        (event) => {
            const globalPos = event.data.global;
            if (touchBlank) {
                // 拖拽画布
                const dx = globalPos.x - mouseDownPoint[0];
                const dy = globalPos.y - mouseDownPoint[1];
                pixiApp.stage.position.set(
                    stageOriginalPos[0] + dx,
                    stageOriginalPos[1] + dy
                );
            }
        }
    );
    pixiApp.renderer.plugins.interaction.on(
        'pointerup',
        (event) => {
            touchBlank = false;
        }
    );
    //*******************拖动画布*******************
    //*******************缩放画布*******************
    pixiApp.view.addEventListener('wheel', (event) => {
        event.stopPropagation();
        event.preventDefault();
        // 因为画布是充满视窗的,所以clientX等于mouse point在renderer上的x坐标
        const globalPos = [event.clientX, event.clientY];
        const delta = event.deltaY;
        const oldZoom = pixiApp.stage.scale.x;
        let newZoom = oldZoom * 0.999 ** delta;
        // const oldStageMatrix = app.stage.localTransform.clone();
        // const oldStagePos = oldStageMatrix.applyInverse(pointerGlobalPos);
        const oldStagePos = globalPos;
        const dx = oldStagePos[0] * oldZoom - oldStagePos[0] * newZoom;
        const dy = oldStagePos[1] * oldZoom - oldStagePos[1] * newZoom;
        pixiApp.stage.setTransform(
            pixiApp.stage.position.x + dx,
            pixiApp.stage.position.y + dy,
            newZoom,
            newZoom,
            0,
            0,
            0,
            0,
            0
        );
    });
    //*******************缩放画布*******************
    //*******************FPS*******************
    var g_Time = 0;
    pixiApp.ticker.add((delta) => {
        var timeNow = (new Date()).getTime();
        var timeDiff = timeNow - g_Time;
        g_Time = timeNow;
        var fps = 1000 / timeDiff;
        mapFps.value = parseInt(fps)
    });
    //*******************FPS*******************
}
function createMapData(map) {
    objectsContainer.removeChildren();
    map.forEach((item, index) => {
        for (let idx = 0; idx < item.length; idx++) {
            let val = item[idx]
            if (val.value < 0) {
                continue;
            }
            let rectangle = getContainer(val.value, idx * width, index * height, map[index][idx].locSts);
            rectangle.on('click', (e) => {
                openLocDrawer(index, idx, map[index][idx], { x: e.data.originalEvent.offsetX, y: e.data.originalEvent.offsetY })
                updateColor(rectangle, 0x9900ff);
            });
            rectangle.locX = index;
            rectangle.locY = idx;
            pixiStageMap.set(map[index][idx].locNo, rectangle);
            objectsContainer.addChild(rectangle);
        }
    })
    //视角居中
    let containerWidth = (pixiApp.view.width - objectsContainer.width) / 2;
    let containerHeight = (pixiApp.view.height - objectsContainer.height) / 2;
    pixiApp.stage.position.set(containerWidth, containerHeight);
}
function containerAppViewCenter() {
    //视角居中
    let containerWidth = (pixiApp.view.width - objectsContainer.width) / 2;
    let containerHeight = (pixiApp.view.height - objectsContainer.height) / 2;
    pixiApp.stage.position.set(containerWidth, containerHeight);
}
function openLocDrawer(x, y, loc, e) {
    drawer.value = true;
    drawerLocData.value = loc;
    get('/api/locDetl/locNo/' + loc.locNo, {}).then(resp => {
        let result = resp.data;
        if (result.code == 200) {
            drawerLocDetls.value = result.data;
        }
    })
}
function getContainer(value, x, y, locSts) {
    let rectangle = new PIXI.Graphics();
    if (value === 0) {
        if (locSts === "F") {
            rectangle.beginFill(0xff0000);
            rectangle.originColor = 0xff0000;
        } else if (locSts === "O") {
            rectangle.beginFill(0x55aaff);
            rectangle.originColor = 0x55aaff;
        } else if (locSts === "D") {
            rectangle.beginFill(0xc2c934);
            rectangle.originColor = 0xc2c934;
        } else if (locSts === "P") {
            rectangle.beginFill(0xf1aa19);
            rectangle.originColor = 0xf1aa19;
        } else if (locSts === "R") {
            rectangle.beginFill(0x618593);
            rectangle.originColor = 0x618593;
        } else if (locSts === "S") {
            rectangle.beginFill(0xfa736f);
            rectangle.originColor = 0xfa736f;
        } else {
            rectangle.beginFill(0x86779d);
            rectangle.originColor = 0x86779d;
        }
    } else if (value === 3) {//母轨道
        rectangle.beginFill(0x00ff7f);
        rectangle.originColor = 0x00ff7f;
        rectangle.visible = false;
    } else if (value === 4) {//站点
        rectangle.beginFill(0xffff00);
        rectangle.originColor = 0xffff00;
        rectangle.visible = false;
    } else if (value === 5) {//充电桩
        rectangle.beginFill(0xffaa7f);
        rectangle.originColor = 0xffaa7f;
        rectangle.visible = false;
    } else if (value === 9) {//轨迹
        rectangle.beginFill(0xff0000);
        rectangle.originColor = 0xff0000;
    }
    // rectangle.lineStyle(1, 0xffffff, 1);
    rectangle.drawRect(0, 0, width, height);
    rectangle.x = x;
    rectangle.y = y;
    // 设置是否可以交互
    rectangle.interactive = true;
    rectangle.endFill();
    // 创建文本对象
    const style = new PIXI.TextStyle({
        fontSize: 14 * window.devicePixelRatio,
        fill: 'white',
        align: 'center', // 设置文本水平居中对齐
        verticalAlign: 'middle' // 设置文本垂直居中对齐
    });
    const text = new PIXI.Text(locSts, style);
    text.anchor.set(0.5); // 设置文本锚点为中心点
    text.position.set(rectangle.width / 2, rectangle.height / 2); // 将文本位置设置为Graphics对象的中心点
    // 将文本对象添加到Graphics对象中
    rectangle.addChild(text);
    return rectangle;
}
/**
* 更新颜色
*/
function updateColor(rectangle, color) {
    rectangle.clear()
    rectangle.beginFill(color);
    rectangle.drawRect(0, 0, width, height);
}
</script>
<script>
export default {
    name: '库位地图'
}
</script>
<template>
    <div style="position: relative;overflow: hidden;">
        <div ref="pixiView">
        </div>
        <!--输出操作和FPS-->
        <div style="position: absolute;top: 0px;right: 10px;user-select: none;">
            <div>FPS:{{ mapFps }}</div>
            <a-button @click="drawerOper = true">操作</a-button>
        </div>
        <!--输出操作和FPS-->
        <div style="position: absolute;bottom: 20px;left: 20px;user-select: none;">
            <a-button type="dashed" @click="containerAppViewCenter">
                <CompressOutlined />
            </a-button>
        </div>
        <div>
            <a-drawer v-model:open="drawer" placement="right" style="background: #f3f3f3;">
                <div style="margin-top: 10px;">
                    <a-tag>{{ formatMessage('locMap.locNo', '库位号') }}</a-tag>{{ drawerLocData.locNo }}
                </div>
                <div style="margin-top: 20px;">
                    <a-tag>{{ formatMessage('locMap.locSts', '库位状态') }}</a-tag>{{ drawerLocData.locSts }}.{{
                        drawerLocData.locSts$ }}
                </div>
                <div style="margin-top: 20px;">
                    <div v-for="(item, index) in drawerLocDetls" :key="index" style="margin-top: 20px;">
                        <a-card :title="item.matnr" :bordered="false" style="width: 300px">
                            <div>{{ formatMessage('locMap.batch', '批号') }}:{{ item.batch }}</div>
                            <div>{{ formatMessage('locMap.orderNo', '单据编号') }}:{{ item.orderNo }}</div>
                            <div>{{ formatMessage('locMap.anfme', '数量') }}:{{ item.anfme }}</div>
                            <div v-for="(field, index) in item.dynamicFieldsList" :key="index">{{
                                formatMessage('locMap.' + field.key, field.desc) }}:{{ field.value }}</div>
                        </a-card>
                    </div>
                </div>
            </a-drawer>
        </div>
        <div>
            <a-drawer v-model:open="drawerOper" placement="right">
                <div style="margin-top: 10px;">
                    <div>
                        {{ formatMessage('locMap.locNo', '库位号') }}
                    </div>
                    <div style="margin-top: 10px;">
                        <a-input v-model:value="drawerOperLocNo" :placeholder="formatMessage('locMap.locNo', '库位号')" />
                    </div>
                </div>
                <div style="margin-top: 20px;">
                    <div>
                        {{ formatMessage('locMap.matnr', '商品编号') }}
                    </div>
                    <div style="margin-top: 10px;">
                        <a-input v-model:value="drawerOperMatnr" :placeholder="formatMessage('locMap.matnr', '商品编号')" />
                    </div>
                </div>
                <div style="margin-top: 20px;">
                    <div>
                        {{ formatMessage('locMap.maktx', '商品名称') }}
                    </div>
                    <div style="margin-top: 10px;">
                        <a-input v-model:value="drawerOperMaktx" :placeholder="formatMessage('locMap.maktx', '商品名称')" />
                    </div>
                </div>
                <div style="margin-top: 20px;">
                    <a-button type="primary" @click="drawerOper = true">搜索</a-button>
                </div>
                <div style="margin-top: 50px;">
                    <div v-for="(lev, index) in levList" :key="index" style="margin-top: 10px;">
                        <a-button :type="currentLev == lev ? 'primary' : 'dashed'" @click="switchLev(lev)"
                            style="width: 100%;">{{ lev }}F</a-button>
                    </div>
                </div>
            </a-drawer>
        </div>
    </div>
</template>
<style>
* {
    margin: 0;
    padding: 0;
}
.pointContainer {
    display: flex;
    justify-content: center;
    /*margin-top: 1px;*/
}
.pointBox {
    background: #bababa;
    width: 40px;
    height: 40px;
    /*margin-right: 1px;*/
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 14px;
    user-select: none;
    color: #fff;
}
.pointBox:hover {
    background: #00ff7f;
}
.pointBoxEmpty {
    background: #c2c934;
}
.pointBoxOut {
    background: #f1aa19;
}
.pointBoxOutYy {
    background: #618593;
}
.pointBoxInYy {
    background: #fa736f;
}
.pointBoxGreen {
    background: #00ff7f;
}
.pointBoxBlue {
    background: #55aaff;
}
.pointBoxRed {
    background: #ff0000;
}
.pointBoxStart {
    background: #ffaa00;
}
.pointBoxEnd {
    background: #ff55ff;
}
.pointBoxStation {
    background: #ffff00;
}
.chargeStation {
    background: #ffaa7f;
}
.pointBoxDefault {
    background: #86779d;
}
.pointBoxSelected {
    background: #00ff7f !important;
}
.pointBoxSearch {
    background: #9900ff;
}
.crnLine {
    width: auto;
    height: 2px;
    margin: 10px 0;
    background: #000;
    position: relative;
}
.popBox {
    position: absolute;
}
/*卡片样式start*/
.apple-card {
    width: 190px;
    height: 254px;
    margin: 0 auto;
    background-color: #011522;
    border-radius: 8px;
    z-index: 1;
    animation: fadeInOut 0.5s 1;
}
.apple-card .tools {
    display: flex;
    align-items: center;
    padding: 9px;
}
.apple-card .circle {
    padding: 0 4px;
}
.apple-card .box {
    display: inline-block;
    align-items: center;
    width: 10px;
    height: 10px;
    padding: 1px;
    border-radius: 50%;
}
.apple-card .red {
    background-color: #ff605c;
    position: relative;
}
.apple-card .red:hover {
    background-color: #ff0300;
}
.apple-card .red:hover::before {
    content: "x";
    font-size: 11px;
    color: #fff;
    width: 10px;
    height: 10px;
    display: flex;
    justify-content: center;
    align-items: center;
    position: absolute;
    animation: fadeInOut 0.5s 1;
}
.apple-card .yellow {
    background-color: #ffbd44;
}
.apple-card .green {
    background-color: #00ca4e;
}
.apple-card .card-content {
    color: #fff;
    padding: 10px;
}
/*卡片样式end*/
/*滑动卡片start*/
.hoverCard {
    width: 150px;
    height: 224px;
    border-radius: 20px;
    background: #f5f5f5;
    position: relative;
    padding: 1.8rem;
    border: 2px solid #c3c6ce;
    transition: 0.5s ease-out;
    overflow: visible;
    margin-top: 30px;
}
.hoverCard .card-details {
    color: black;
    height: 100%;
    gap: .5em;
    display: grid;
    place-content: center;
}
.hoverCard .card-button {
    transform: translate(-50%, 125%);
    width: 60%;
    border-radius: 1rem;
    border: none;
    background-color: #008bf8;
    color: #fff;
    font-size: 1rem;
    padding: .5rem 1rem;
    position: absolute;
    left: 50%;
    bottom: 0;
    opacity: 0;
    transition: 0.3s ease-out;
}
.hoverCard .text-body {
    color: rgb(134, 134, 134);
}
/*Text*/
.hoverCard .text-title {
    font-size: 1.5em;
    font-weight: bold;
}
/*Hover*/
.hoverCard:hover {
    border-color: #008bf8;
    box-shadow: 0 4px 18px 0 rgba(0, 0, 0, 0.25);
}
.hoverCard:hover .card-button {
    transform: translate(-50%, 50%);
    opacity: 1;
}
/*滑动卡片end*/
/*楼层控制start*/
.floorSelect {
    --text: #414856;
    --radio: #7C96B2;
    --radio-checked: #4F29F0;
    --radio-size: 20px;
    --width: 150px;
    --height: 200px;
    --border-radius: 10px;
    width: var(--width);
    height: var(--height);
    border-radius: var(--border-radius);
    color: var(--text);
    position: relative;
    box-shadow: 0 10px 30px rgba(65, 72, 86, 0.05);
    display: grid;
    grid-template-columns: auto var(--radio-size);
    align-items: center;
}
.floorSelect label {
    cursor: pointer;
}
.floorSelect input[type="radio"] {
    -webkit-appearance: none;
    -moz-appearance: none;
    position: relative;
    height: var(--radio-size);
    width: var(--radio-size);
    outline: none;
    margin: 0;
    cursor: pointer;
    border: 2px solid var(--radio);
    background: transparent;
    border-radius: 50%;
    display: grid;
    justify-self: end;
    justify-items: center;
    align-items: center;
    overflow: hidden;
    transition: border .5s ease;
}
.floorSelect input[type="radio"]::before,
.floorSelect input[type="radio"]::after {
    content: "";
    display: flex;
    justify-self: center;
    border-radius: 50%;
}
.floorSelect input[type="radio"]::before {
    position: absolute;
    width: 100%;
    height: 100%;
    z-index: 1;
    opacity: var(--opacity, 1);
}
.floorSelect input[type="radio"]::after {
    position: relative;
    width: calc(100% /2);
    height: calc(100% /2);
    background: var(--radio-checked);
    top: var(--y, 100%);
    transition: top 0.5s cubic-bezier(0.48, 1.97, 0.5, 0.63);
}
.floorSelect input[type="radio"]:checked {
    --radio: var(--radio-checked);
}
.floorSelect input[type="radio"]:checked::after {
    --y: 0%;
    animation: stretch-animate .3s ease-out .17s;
}
.floorSelect input[type="radio"]:checked::before {
    --opacity: 0;
}
.floorSelect input[type="radio"]:checked~input[type="radio"]::after {
    --y: -100%;
}
.floorSelect input[type="radio"]:not(:checked)::before {
    --opacity: 1;
    transition: opacity 0s linear .5s;
}
@keyframes stretch-animate {
    0% {
        transform: scale(1, 1);
    }
    28% {
        transform: scale(1.15, 0.85);
    }
    50% {
        transform: scale(0.9, 1.1);
    }
    100% {
        transform: scale(1, 1);
    }
}
/*楼层控制end*/
/*搜索start*/
.search-input {
    line-height: 28px;
    border: 2px solid transparent;
    border-bottom-color: #777;
    padding: .2rem 0;
    outline: none;
    background-color: transparent;
    color: #0d0c22;
    transition: .3s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.search-input:focus,
.search-input:hover {
    outline: none;
    padding: .2rem 1rem;
    border-radius: 1rem;
    border-color: #7a9cc6;
}
.search-input::placeholder {
    color: #777;
}
.search-input:focus::placeholder {
    opacity: 0;
    transition: opacity .3s;
}
/*搜索end*/
@keyframes fadeInOut {
    0% {
        opacity: 0;
    }
    100% {
        opacity: 1;
    }
}
</style>
<script setup>
import { ref, onMounted, watch } from 'vue';
import { get, post, postBlob } from '@/utils/request.js'
import { message, Modal } from 'ant-design-vue';
import { logout } from '@/config.js';
import { formatMessage } from '@/utils/localeUtils.js';
import {
    CompressOutlined,
} from "@ant-design/icons-vue";
import * as PIXI from 'pixi.js'
let width = 25;
let height = 25;
let pixiApp;
let pixiStageMap = new Map();
let objectsContainer;
let map = []
const pixiView = ref(null)
const mapFps = ref(0)
const currentLev = ref(1)
const levList = ref([])
const pointContainerWidth = ref(0)
const drawer = ref(false)
const drawerLocData = ref(null)
const drawerLocDetls = ref([])
const drawerLocDetlField = ref([])
const drawerOper = ref(false)
const drawerOperLocNo = ref(null)
const drawerOperMatnr = ref(null)
const drawerOperMaktx = ref(null)
onMounted(() => {
    createMap();
    init(currentLev.value);
})
watch(drawer, (newVal, oldVal) => {
    if (!drawer.value) {
        var rectangle = pixiStageMap.get(drawerLocData.value.locNo)
        updateColor(rectangle, rectangle.originColor);//恢复颜色
    }
})
function switchLev(lev) {
    currentLev.value = lev;
    init(lev);
}
function init(lev) {
    get('/api/locMap/getData/' + lev + '/auth', {}).then(resp => {
        let result = resp.data;
        if (result.code == 200) {
            let tmp = JSON.parse(result.data);
            let tmpMap = []
            tmp.forEach((item, index) => {
                let data2 = []
                item.forEach((val, idx) => {
                    val.searchStatus = false//搜索标记
                    val.rectangle = null;
                    data2.push(val)
                })
                pointContainerWidth.value = item.length * (40 + 1)
                tmpMap.push(data2)
            })
            createMapData(tmpMap);
        }
    })
    get('/api/locMap/getLev', {}).then(resp => {
        let result = resp.data;
        if (result.code == 200) {
            let tmp = result.data;
            levList.value = tmp;
        }
    })
}
function createMap() {
    //Create a Pixi Application
    pixiApp = new PIXI.Application({
        width: pixiView.value.offsetWidth,
        height: window.innerHeight * 0.75,
        backgroundColor: 0xF5F7F9FF,
        // resizeTo: window
    });
    //Add the canvas that Pixi automatically created for you to the HTML document
    pixiView.value.appendChild(pixiApp.view)
    // 创建一个容器来管理大批量的显示对象
    objectsContainer = new PIXI.Container();
    pixiApp.stage.addChild(objectsContainer);
    //*******************拖动画布*******************
    let stageOriginalPos;
    let mouseDownPoint;
    let touchBlank = false;
    pixiApp.renderer.plugins.interaction.on(
        'pointerdown',
        (event) => {
            const globalPos = event.data.global;
            // 记录下stage原来的位置
            stageOriginalPos = [pixiApp.stage.position._x, pixiApp.stage.position._y];
            // 记录下mouse down的位置
            mouseDownPoint = [globalPos.x, globalPos.y];
            if (!event.target) {
                // 点到了画布的空白位置
                touchBlank = true;
            }
        }
    );
    pixiApp.renderer.plugins.interaction.on(
        'pointermove',
        (event) => {
            const globalPos = event.data.global;
            if (touchBlank) {
                // 拖拽画布
                const dx = globalPos.x - mouseDownPoint[0];
                const dy = globalPos.y - mouseDownPoint[1];
                pixiApp.stage.position.set(
                    stageOriginalPos[0] + dx,
                    stageOriginalPos[1] + dy
                );
            }
        }
    );
    pixiApp.renderer.plugins.interaction.on(
        'pointerup',
        (event) => {
            touchBlank = false;
        }
    );
    //*******************拖动画布*******************
    //*******************缩放画布*******************
    pixiApp.view.addEventListener('wheel', (event) => {
        event.stopPropagation();
        event.preventDefault();
        // 因为画布是充满视窗的,所以clientX等于mouse point在renderer上的x坐标
        const globalPos = [event.clientX, event.clientY];
        const delta = event.deltaY;
        const oldZoom = pixiApp.stage.scale.x;
        let newZoom = oldZoom * 0.999 ** delta;
        // const oldStageMatrix = app.stage.localTransform.clone();
        // const oldStagePos = oldStageMatrix.applyInverse(pointerGlobalPos);
        const oldStagePos = globalPos;
        const dx = oldStagePos[0] * oldZoom - oldStagePos[0] * newZoom;
        const dy = oldStagePos[1] * oldZoom - oldStagePos[1] * newZoom;
        pixiApp.stage.setTransform(
            pixiApp.stage.position.x + dx,
            pixiApp.stage.position.y + dy,
            newZoom,
            newZoom,
            0,
            0,
            0,
            0,
            0
        );
    });
    //*******************缩放画布*******************
    //*******************FPS*******************
    var g_Time = 0;
    pixiApp.ticker.add((delta) => {
        var timeNow = (new Date()).getTime();
        var timeDiff = timeNow - g_Time;
        g_Time = timeNow;
        var fps = 1000 / timeDiff;
        mapFps.value = parseInt(fps)
    });
    //*******************FPS*******************
}
function createMapData(map) {
    objectsContainer.removeChildren();
    map.forEach((item, index) => {
        for (let idx = 0; idx < item.length; idx++) {
            let val = item[idx]
            if (val.value < 0) {
                continue;
            }
            let rectangle = getContainer(val.value, idx * width, index * height, map[index][idx].locSts);
            rectangle.on('click', (e) => {
                openLocDrawer(index, idx, map[index][idx], { x: e.data.originalEvent.offsetX, y: e.data.originalEvent.offsetY })
                updateColor(rectangle, 0x9900ff);
            });
            rectangle.locX = index;
            rectangle.locY = idx;
            pixiStageMap.set(map[index][idx].locNo, rectangle);
            objectsContainer.addChild(rectangle);
        }
    })
    //视角居中
    let containerWidth = (pixiApp.view.width - objectsContainer.width) / 2;
    let containerHeight = (pixiApp.view.height - objectsContainer.height) / 2;
    pixiApp.stage.position.set(containerWidth, containerHeight);
}
function containerAppViewCenter() {
    //视角居中
    let containerWidth = (pixiApp.view.width - objectsContainer.width) / 2;
    let containerHeight = (pixiApp.view.height - objectsContainer.height) / 2;
    pixiApp.stage.position.set(containerWidth, containerHeight);
}
function openLocDrawer(x, y, loc, e) {
    drawer.value = true;
    drawerLocData.value = loc;
    get('/api/locDetl/locNo/' + loc.locNo, {}).then(resp => {
        let result = resp.data;
        if (result.code == 200) {
            drawerLocDetls.value = result.data;
        }
    })
}
function getContainer(value, x, y, locSts) {
    let rectangle = new PIXI.Graphics();
    if (value === 0) {
        if (locSts === "F") {
            rectangle.beginFill(0xff0000);
            rectangle.originColor = 0xff0000;
        } else if (locSts === "O") {
            rectangle.beginFill(0x55aaff);
            rectangle.originColor = 0x55aaff;
        } else if (locSts === "D") {
            rectangle.beginFill(0xc2c934);
            rectangle.originColor = 0xc2c934;
        } else if (locSts === "P") {
            rectangle.beginFill(0xf1aa19);
            rectangle.originColor = 0xf1aa19;
        } else if (locSts === "R") {
            rectangle.beginFill(0x618593);
            rectangle.originColor = 0x618593;
        } else if (locSts === "S") {
            rectangle.beginFill(0xfa736f);
            rectangle.originColor = 0xfa736f;
        } else {
            rectangle.beginFill(0x86779d);
            rectangle.originColor = 0x86779d;
        }
    } else if (value === 3) {//母轨道
        rectangle.beginFill(0x00ff7f);
        rectangle.originColor = 0x00ff7f;
        rectangle.visible = false;
    } else if (value === 4) {//站点
        rectangle.beginFill(0xffff00);
        rectangle.originColor = 0xffff00;
        rectangle.visible = false;
    } else if (value === 5) {//充电桩
        rectangle.beginFill(0xffaa7f);
        rectangle.originColor = 0xffaa7f;
        rectangle.visible = false;
    } else if (value === 9) {//轨迹
        rectangle.beginFill(0xff0000);
        rectangle.originColor = 0xff0000;
    }
    // rectangle.lineStyle(1, 0xffffff, 1);
    rectangle.drawRect(0, 0, width, height);
    rectangle.x = x;
    rectangle.y = y;
    // 设置是否可以交互
    rectangle.interactive = true;
    rectangle.endFill();
    // 创建文本对象
    const style = new PIXI.TextStyle({
        fontSize: 14 * window.devicePixelRatio,
        fill: 'white',
        align: 'center', // 设置文本水平居中对齐
        verticalAlign: 'middle' // 设置文本垂直居中对齐
    });
    const text = new PIXI.Text(locSts, style);
    text.anchor.set(0.5); // 设置文本锚点为中心点
    text.position.set(rectangle.width / 2, rectangle.height / 2); // 将文本位置设置为Graphics对象的中心点
    // 将文本对象添加到Graphics对象中
    rectangle.addChild(text);
    return rectangle;
}
/**
* 更新颜色
*/
function updateColor(rectangle, color) {
    rectangle.clear()
    rectangle.beginFill(color);
    rectangle.drawRect(0, 0, width, height);
}
</script>
<script>
export default {
    name: '库位地图'
}
</script>
<template>
    <div style="position: relative;overflow: hidden;">
        <div ref="pixiView">
        </div>
        <!--输出操作和FPS-->
        <div style="position: absolute;top: 0px;right: 10px;user-select: none;">
            <div>FPS:{{ mapFps }}</div>
            <a-button @click="drawerOper = true">操作</a-button>
        </div>
        <!--输出操作和FPS-->
        <div style="position: absolute;bottom: 20px;left: 20px;user-select: none;">
            <a-button type="dashed" @click="containerAppViewCenter">
                <CompressOutlined />
            </a-button>
        </div>
        <div>
            <a-drawer v-model:open="drawer" placement="right" style="background: #f3f3f3;">
                <div style="margin-top: 10px;">
                    <a-tag>{{ formatMessage('locMap.locNo', '库位号') }}</a-tag>{{ drawerLocData.locNo }}
                </div>
                <div style="margin-top: 20px;">
                    <a-tag>{{ formatMessage('locMap.locSts', '库位状态') }}</a-tag>{{ drawerLocData.locSts }}.{{
                        drawerLocData.locSts$ }}
                </div>
                <div style="margin-top: 20px;">
                    <div v-for="(item, index) in drawerLocDetls" :key="index" style="margin-top: 20px;">
                        <a-card :title="item.matnr" :bordered="false" style="width: 300px">
                            <div>{{ formatMessage('locMap.batch', '批号') }}:{{ item.batch }}</div>
                            <div>{{ formatMessage('locMap.orderNo', '单据编号') }}:{{ item.orderNo }}</div>
                            <div>{{ formatMessage('locMap.anfme', '数量') }}:{{ item.anfme }}</div>
                            <div v-for="(field, index) in item.dynamicFieldsList" :key="index">{{
                                formatMessage('locMap.' + field.key, field.desc) }}:{{ field.value }}</div>
                        </a-card>
                    </div>
                </div>
            </a-drawer>
        </div>
        <div>
            <a-drawer v-model:open="drawerOper" placement="right">
                <div style="margin-top: 10px;">
                    <div>
                        {{ formatMessage('locMap.locNo', '库位号') }}
                    </div>
                    <div style="margin-top: 10px;">
                        <a-input v-model:value="drawerOperLocNo" :placeholder="formatMessage('locMap.locNo', '库位号')" />
                    </div>
                </div>
                <div style="margin-top: 20px;">
                    <div>
                        {{ formatMessage('locMap.matnr', '商品编号') }}
                    </div>
                    <div style="margin-top: 10px;">
                        <a-input v-model:value="drawerOperMatnr" :placeholder="formatMessage('locMap.matnr', '商品编号')" />
                    </div>
                </div>
                <div style="margin-top: 20px;">
                    <div>
                        {{ formatMessage('locMap.maktx', '商品名称') }}
                    </div>
                    <div style="margin-top: 10px;">
                        <a-input v-model:value="drawerOperMaktx" :placeholder="formatMessage('locMap.maktx', '商品名称')" />
                    </div>
                </div>
                <div style="margin-top: 20px;">
                    <a-button type="primary" @click="drawerOper = true">搜索</a-button>
                </div>
                <div style="margin-top: 50px;">
                    <div v-for="(lev, index) in levList" :key="index" style="margin-top: 10px;">
                        <a-button :type="currentLev == lev ? 'primary' : 'dashed'" @click="switchLev(lev)"
                            style="width: 100%;">{{ lev }}F</a-button>
                    </div>
                </div>
            </a-drawer>
        </div>
    </div>
</template>
<style>
* {
    margin: 0;
    padding: 0;
}
.pointContainer {
    display: flex;
    justify-content: center;
    /*margin-top: 1px;*/
}
.pointBox {
    background: #bababa;
    width: 40px;
    height: 40px;
    /*margin-right: 1px;*/
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 14px;
    user-select: none;
    color: #fff;
}
.pointBox:hover {
    background: #00ff7f;
}
.pointBoxEmpty {
    background: #c2c934;
}
.pointBoxOut {
    background: #f1aa19;
}
.pointBoxOutYy {
    background: #618593;
}
.pointBoxInYy {
    background: #fa736f;
}
.pointBoxGreen {
    background: #00ff7f;
}
.pointBoxBlue {
    background: #55aaff;
}
.pointBoxRed {
    background: #ff0000;
}
.pointBoxStart {
    background: #ffaa00;
}
.pointBoxEnd {
    background: #ff55ff;
}
.pointBoxStation {
    background: #ffff00;
}
.chargeStation {
    background: #ffaa7f;
}
.pointBoxDefault {
    background: #86779d;
}
.pointBoxSelected {
    background: #00ff7f !important;
}
.pointBoxSearch {
    background: #9900ff;
}
.crnLine {
    width: auto;
    height: 2px;
    margin: 10px 0;
    background: #000;
    position: relative;
}
.popBox {
    position: absolute;
}
/*卡片样式start*/
.apple-card {
    width: 190px;
    height: 254px;
    margin: 0 auto;
    background-color: #011522;
    border-radius: 8px;
    z-index: 1;
    animation: fadeInOut 0.5s 1;
}
.apple-card .tools {
    display: flex;
    align-items: center;
    padding: 9px;
}
.apple-card .circle {
    padding: 0 4px;
}
.apple-card .box {
    display: inline-block;
    align-items: center;
    width: 10px;
    height: 10px;
    padding: 1px;
    border-radius: 50%;
}
.apple-card .red {
    background-color: #ff605c;
    position: relative;
}
.apple-card .red:hover {
    background-color: #ff0300;
}
.apple-card .red:hover::before {
    content: "x";
    font-size: 11px;
    color: #fff;
    width: 10px;
    height: 10px;
    display: flex;
    justify-content: center;
    align-items: center;
    position: absolute;
    animation: fadeInOut 0.5s 1;
}
.apple-card .yellow {
    background-color: #ffbd44;
}
.apple-card .green {
    background-color: #00ca4e;
}
.apple-card .card-content {
    color: #fff;
    padding: 10px;
}
/*卡片样式end*/
/*滑动卡片start*/
.hoverCard {
    width: 150px;
    height: 224px;
    border-radius: 20px;
    background: #f5f5f5;
    position: relative;
    padding: 1.8rem;
    border: 2px solid #c3c6ce;
    transition: 0.5s ease-out;
    overflow: visible;
    margin-top: 30px;
}
.hoverCard .card-details {
    color: black;
    height: 100%;
    gap: .5em;
    display: grid;
    place-content: center;
}
.hoverCard .card-button {
    transform: translate(-50%, 125%);
    width: 60%;
    border-radius: 1rem;
    border: none;
    background-color: #008bf8;
    color: #fff;
    font-size: 1rem;
    padding: .5rem 1rem;
    position: absolute;
    left: 50%;
    bottom: 0;
    opacity: 0;
    transition: 0.3s ease-out;
}
.hoverCard .text-body {
    color: rgb(134, 134, 134);
}
/*Text*/
.hoverCard .text-title {
    font-size: 1.5em;
    font-weight: bold;
}
/*Hover*/
.hoverCard:hover {
    border-color: #008bf8;
    box-shadow: 0 4px 18px 0 rgba(0, 0, 0, 0.25);
}
.hoverCard:hover .card-button {
    transform: translate(-50%, 50%);
    opacity: 1;
}
/*滑动卡片end*/
/*楼层控制start*/
.floorSelect {
    --text: #414856;
    --radio: #7C96B2;
    --radio-checked: #4F29F0;
    --radio-size: 20px;
    --width: 150px;
    --height: 200px;
    --border-radius: 10px;
    width: var(--width);
    height: var(--height);
    border-radius: var(--border-radius);
    color: var(--text);
    position: relative;
    box-shadow: 0 10px 30px rgba(65, 72, 86, 0.05);
    display: grid;
    grid-template-columns: auto var(--radio-size);
    align-items: center;
}
.floorSelect label {
    cursor: pointer;
}
.floorSelect input[type="radio"] {
    -webkit-appearance: none;
    -moz-appearance: none;
    position: relative;
    height: var(--radio-size);
    width: var(--radio-size);
    outline: none;
    margin: 0;
    cursor: pointer;
    border: 2px solid var(--radio);
    background: transparent;
    border-radius: 50%;
    display: grid;
    justify-self: end;
    justify-items: center;
    align-items: center;
    overflow: hidden;
    transition: border .5s ease;
}
.floorSelect input[type="radio"]::before,
.floorSelect input[type="radio"]::after {
    content: "";
    display: flex;
    justify-self: center;
    border-radius: 50%;
}
.floorSelect input[type="radio"]::before {
    position: absolute;
    width: 100%;
    height: 100%;
    z-index: 1;
    opacity: var(--opacity, 1);
}
.floorSelect input[type="radio"]::after {
    position: relative;
    width: calc(100% /2);
    height: calc(100% /2);
    background: var(--radio-checked);
    top: var(--y, 100%);
    transition: top 0.5s cubic-bezier(0.48, 1.97, 0.5, 0.63);
}
.floorSelect input[type="radio"]:checked {
    --radio: var(--radio-checked);
}
.floorSelect input[type="radio"]:checked::after {
    --y: 0%;
    animation: stretch-animate .3s ease-out .17s;
}
.floorSelect input[type="radio"]:checked::before {
    --opacity: 0;
}
.floorSelect input[type="radio"]:checked~input[type="radio"]::after {
    --y: -100%;
}
.floorSelect input[type="radio"]:not(:checked)::before {
    --opacity: 1;
    transition: opacity 0s linear .5s;
}
@keyframes stretch-animate {
    0% {
        transform: scale(1, 1);
    }
    28% {
        transform: scale(1.15, 0.85);
    }
    50% {
        transform: scale(0.9, 1.1);
    }
    100% {
        transform: scale(1, 1);
    }
}
/*楼层控制end*/
/*搜索start*/
.search-input {
    line-height: 28px;
    border: 2px solid transparent;
    border-bottom-color: #777;
    padding: .2rem 0;
    outline: none;
    background-color: transparent;
    color: #0d0c22;
    transition: .3s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.search-input:focus,
.search-input:hover {
    outline: none;
    padding: .2rem 1rem;
    border-radius: 1rem;
    border-color: #7a9cc6;
}
.search-input::placeholder {
    color: #777;
}
.search-input:focus::placeholder {
    opacity: 0;
    transition: opacity .3s;
}
/*搜索end*/
@keyframes fadeInOut {
    0% {
        opacity: 0;
    }
    100% {
        opacity: 1;
    }
}
</style>