| <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>  |