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