|  |  |  | 
|---|
|  |  |  | <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: 0px;left: 0px;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> | 
|---|