1030 lines
30 KiB
Vue
1030 lines
30 KiB
Vue
<template>
|
||
<div
|
||
class="affix-container"
|
||
id="indexpage-container"
|
||
:style="{
|
||
height: heightVal * radio + 'px',
|
||
cursor: isDrag ? 'pointer' : 'default',
|
||
scale: isSizeRaio,
|
||
transformOrigin: 'center center'
|
||
}"
|
||
>
|
||
<div
|
||
class="indexpage-container"
|
||
v-if="imgUrl"
|
||
v-drag="isDrag"
|
||
:style="{ scale: 1, transformOrigin: '0 0' }"
|
||
ref="draggableElement"
|
||
>
|
||
<div class="indexpage-container-box">
|
||
<img :src="imgUrl" alt="" class="indexpage-container-box-img" />
|
||
|
||
<div class="indexpage-container-box-point">
|
||
<!-- 连线 -->
|
||
<div v-if="legendObj.driveLineShow">
|
||
<div class="line-box" v-for="(item, index) in lineList" :key="index">
|
||
<svg id="svg" :width="widthVal" :height="heightVal">
|
||
<template v-if="item.method == 0">
|
||
<line
|
||
:x1="(Number(item.startPointX) + Number(item.beginWidth) / 2) * radio"
|
||
:y1="(Number(item.startPointY) + Number(item.beginHigh) / 2) * radio"
|
||
:x2="(Number(item.endPointX) + Number(item.endWidth) / 2) * radio"
|
||
:y2="(Number(item.endPointY) + Number(item.endHigh) / 2) * radio"
|
||
stroke="#00329F"
|
||
stroke-width="5"
|
||
/>
|
||
</template>
|
||
<template v-else>
|
||
<path :d="getCurvePath(item)" :stroke="'#00329F'" stroke-width="5" fill="none" />
|
||
</template>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
<!-- 小车 -->
|
||
<div
|
||
class="indexpage-car-item"
|
||
v-for="(item, index) in testCarList"
|
||
@dblclick="carDbClick(item, index)"
|
||
:key="index"
|
||
:style="{
|
||
left: item.realX * radio + 'px',
|
||
top: item.realY * radio + 'px',
|
||
width: legendObj.carShow ? 40 * radio + 'px' : '0',
|
||
height: legendObj.carShow ? 22 * radio + 'px' : '0'
|
||
}"
|
||
>
|
||
<img
|
||
src="@/assets/imgs/indexPage/chache-4备份 7@2x.png"
|
||
alt=""
|
||
style="width: 100%; height: 100%"
|
||
/>
|
||
</div>
|
||
<div
|
||
class="indexpage-container-box-point-item"
|
||
v-for="(item, index) in pointList"
|
||
:key="index"
|
||
:style="{ left: item.locationX * radio + 'px', top: item.locationY * radio + 'px' }"
|
||
>
|
||
<!-- 库位点 -->
|
||
<div
|
||
class="indexpage-container-box-point-item-inner"
|
||
v-if="item.showData"
|
||
:style="{
|
||
width: item.showData.locationWide * radio + 'px',
|
||
height: item.showData.locationDeep * radio + 'px'
|
||
}"
|
||
>
|
||
<!-- 库位 2-->
|
||
<div v-if="item.type == 2" style="width: 100%; height: 100%">
|
||
<el-popover placement="top-start" trigger="hover" width="auto">
|
||
<template #reference>
|
||
<img
|
||
:src="item.imgUrl"
|
||
alt=""
|
||
style="width: 100%; height: 100%"
|
||
@dblclick="storeClick(item)"
|
||
/>
|
||
</template>
|
||
<div class="indexpage-container-box-point-item-inner-popover">
|
||
<div
|
||
class="indexpage-container-box-point-item-inner-popover-item"
|
||
style="margin-bottom: 8px"
|
||
>
|
||
<div class="indexpage-container-box-point-item-inner-popover-name">
|
||
库位名:
|
||
</div>
|
||
<div class="indexpage-container-box-point-item-inner-popover-value">
|
||
{{ item.showData.locationNo || '' }}
|
||
</div>
|
||
</div>
|
||
<div
|
||
class="indexpage-container-box-point-item-inner-popover-item"
|
||
style="margin-bottom: 8px"
|
||
>
|
||
<div class="indexpage-container-box-point-item-inner-popover-name">
|
||
所属线库:
|
||
</div>
|
||
<div class="indexpage-container-box-point-item-inner-popover-value">
|
||
{{ item.showData.laneName || '' }}
|
||
</div>
|
||
</div>
|
||
<div
|
||
class="indexpage-container-box-point-item-inner-popover-item"
|
||
style="margin-bottom: 8px"
|
||
>
|
||
<div class="indexpage-container-box-point-item-inner-popover-name">
|
||
所属区域:
|
||
</div>
|
||
<div class="indexpage-container-box-point-item-inner-popover-value">
|
||
{{ item.showData.areaName || '' }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</el-popover>
|
||
</div>
|
||
<!-- 设备点 -->
|
||
<div v-if="item.type == 3">
|
||
<img
|
||
:src="item.formattedData.mapImageUrl"
|
||
alt=""
|
||
style="width: 100%; height: 100%"
|
||
/>
|
||
</div>
|
||
</div>
|
||
<!-- 设备点 -->
|
||
<div
|
||
class="indexpage-container-box-point-item-inner"
|
||
v-if="item.type == 3"
|
||
:style="{
|
||
width: item.formattedData.locationWide * radio + 'px',
|
||
height: item.formattedData.locationDeep * radio + 'px'
|
||
}"
|
||
>
|
||
<div>
|
||
<img
|
||
:src="
|
||
item.formattedData.mapImageUrl
|
||
? item.formattedData.mapImageUrl
|
||
: 'https://api.znkjfw.com/admin-api/infra/file/4/get/设备点_png_179_1739327151877.png'
|
||
"
|
||
alt=""
|
||
style="width: 100%; height: 100%"
|
||
/>
|
||
</div>
|
||
</div>
|
||
<!-- 停车点 -->
|
||
<div
|
||
class="indexpage-container-box-point-item-inner"
|
||
v-if="item.type == 4"
|
||
:style="{
|
||
width: item.formattedData.locationWide * radio + 'px',
|
||
height: item.formattedData.locationDeep * radio + 'px'
|
||
}"
|
||
>
|
||
<div>
|
||
<img
|
||
:src="
|
||
item.formattedData.mapImageUrl
|
||
? item.formattedData.mapImageUrl
|
||
: 'https://api.znkjfw.com/admin-api/infra/file/4/get/停车场-01_png_179_1739326933020.png'
|
||
"
|
||
alt=""
|
||
style="width: 100%; height: 100%"
|
||
/>
|
||
</div>
|
||
</div>
|
||
<!-- 路径点 -->
|
||
<div
|
||
class="indexpage-container-box-point-item-inner"
|
||
v-if="item.type == 5"
|
||
:style="{
|
||
width: 40 * radio + 'px',
|
||
height: 40 * radio + 'px'
|
||
}"
|
||
>
|
||
<div>
|
||
<img
|
||
:src="
|
||
item.formattedData.mapImageUrl
|
||
? item.formattedData.mapImageUrl
|
||
: 'https://api.znkjfw.com/admin-api/infra/file/4/get/区域_png_179_1739327151876.png'
|
||
"
|
||
alt=""
|
||
style="width: 100%; height: 100%"
|
||
/>
|
||
</div>
|
||
</div>
|
||
<!-- 等待点 -->
|
||
<div
|
||
class="indexpage-container-box-point-item-inner"
|
||
v-if="item.type == 6"
|
||
:style="{
|
||
width: 40 * radio + 'px',
|
||
height: 40 * radio + 'px'
|
||
}"
|
||
>
|
||
<div>
|
||
<img
|
||
:src="
|
||
item.formattedData.mapImageUrl
|
||
? item.formattedData.mapImageUrl
|
||
: 'https://api.znkjfw.com/admin-api/infra/file/4/get/等待点_png_179_1739326991439.png'
|
||
"
|
||
alt=""
|
||
style="width: 100%; height: 100%"
|
||
/>
|
||
</div>
|
||
</div>
|
||
<div
|
||
v-if="item.type == 1"
|
||
style="width: 10px; height: 10px; border-radius: 50%; background: #1890ff"
|
||
>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 左下角图例 -->
|
||
<div class="affix-container-left" :style="{ left: boxLeft + 'px' }">
|
||
<div class="affix-container-left-box">
|
||
<div
|
||
class="affix-container-left-box-item-box"
|
||
:style="{
|
||
height: legendObj.legendShow ? '84px' : '0',
|
||
overflow: 'hidden',
|
||
transition: 'all 0.3s ease-in-out'
|
||
}"
|
||
>
|
||
<div class="affix-container-left-box-item">
|
||
<div class="affix-container-left-box-item-left"> 行驶路线 </div>
|
||
<img
|
||
src="@/assets/imgs/indexPage/yanjing_xianshi_o.png"
|
||
alt=""
|
||
class="affix-container-left-box-item-img"
|
||
v-if="legendObj.driveLineShow"
|
||
@click="changDriveLineShow"
|
||
/>
|
||
<img
|
||
src="@/assets/imgs/indexPage/yanjing_yincang_o.png"
|
||
alt=""
|
||
class="affix-container-left-box-item-img"
|
||
v-if="!legendObj.driveLineShow"
|
||
@click="changDriveLineShow"
|
||
/>
|
||
</div>
|
||
<div class="affix-container-left-box-item">
|
||
<div class="affix-container-left-box-item-left"> 车辆 </div>
|
||
<img
|
||
src="@/assets/imgs/indexPage/yanjing_xianshi_o.png"
|
||
alt=""
|
||
class="affix-container-left-box-item-img"
|
||
v-if="legendObj.carShow"
|
||
@click="changCarShow"
|
||
/>
|
||
<img
|
||
src="@/assets/imgs/indexPage/yanjing_yincang_o.png"
|
||
alt=""
|
||
class="affix-container-left-box-item-img"
|
||
v-if="!legendObj.carShow"
|
||
@click="changCarShow"
|
||
/>
|
||
</div>
|
||
</div>
|
||
<div
|
||
class="affix-container-left-box-item-bottom"
|
||
@click="legendObj.legendShow = !legendObj.legendShow"
|
||
>
|
||
<div class="affix-container-left-box-item-bottom-left"> 图例 </div>
|
||
<img
|
||
src="@/assets/imgs/indexPage/zhankai@2x.png"
|
||
alt=""
|
||
class="affix-container-left-box-item-bottom-img"
|
||
:style="{
|
||
transform: legendObj.legendShow ? 'rotate(180deg)' : 'rotate(0deg)',
|
||
transition: 'all 0.3s ease-in-out'
|
||
}"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 右下角的功能 -->
|
||
<div class="affix-container-right">
|
||
<!-- 拖拽 -->
|
||
<div class="affix-container-right-item" @click="changeIsDrag">
|
||
<img src="@/assets/imgs/indexPage/编组 12.png" alt="" style="width: 100%; height: 100%" />
|
||
</div>
|
||
<!-- 放大 -->
|
||
<div class="affix-container-right-item">
|
||
<img
|
||
src="@/assets/imgs/indexPage/编组 14.png"
|
||
alt=""
|
||
style="width: 100%; height: 100%"
|
||
@click="changeSizeRaio(0.2)"
|
||
/>
|
||
</div>
|
||
<!-- 缩小 -->
|
||
<div class="affix-container-right-item">
|
||
<img
|
||
src="@/assets/imgs/indexPage/编组 15.png"
|
||
alt=""
|
||
style="width: 100%; height: 100%"
|
||
@click="changeSizeRaio(-0.2)"
|
||
/>
|
||
</div>
|
||
<!-- 全屏 -->
|
||
<div class="affix-container-right-item">
|
||
<img
|
||
src="@/assets/imgs/indexPage/编组 22.png"
|
||
alt=""
|
||
style="width: 100%; height: 100%"
|
||
@click="toggleFullScreen"
|
||
/>
|
||
</div>
|
||
</div>
|
||
<storeDialog ref="storeDialogRef" @success="getList" />
|
||
<carDialog ref="carDialogRef" />
|
||
</template>
|
||
|
||
<script setup>
|
||
import {
|
||
ref,
|
||
defineComponent,
|
||
reactive,
|
||
nextTick,
|
||
onMounted,
|
||
onBeforeUnmount,
|
||
onUnmounted
|
||
} from 'vue'
|
||
import * as MapApi from '@/api/map/map'
|
||
import WebSocketClient from '../webSocket.js'
|
||
import storeDialog from './storeDialog.vue'
|
||
import { color } from 'echarts'
|
||
import { resetDragPosition } from '@/utils/drag'
|
||
import carDialog from './carDialog.vue'
|
||
import { is } from 'bpmn-js/lib/util/ModelUtil'
|
||
const imgUrl = ref('')
|
||
const carDialogRef = ref(null)
|
||
const socketClient = ref(null)
|
||
|
||
const emit = defineEmits(['transmitMapId'])
|
||
const storeDialogRef = ref(null)
|
||
const list = ref([])
|
||
const nowObject = ref(null)
|
||
const testCarList = ref([])
|
||
const carPointListFun = () => {
|
||
let testJson = {
|
||
type: 'map_push',
|
||
content:
|
||
'{"d0:65:78:c4:af:cc":"{\\"id\\":1,\\"macAddress\\":\\"d0:65:78:c4:af:cc\\",\\"robotModelNumber\\":\\"A-1\\",\\"pose2d\\":{\\"y\\":\\"1\\",\\"x\\":\\"2\\",\\"yaw\\":\\"30\\",\\"floor\\":\\"1\\",\\"area\\":\\"A区\\",\\"bat_soc\\":\\"40\\"}}"}'
|
||
}
|
||
let data = JSON.parse(testJson.content)
|
||
// console.log("============",data)
|
||
let dataList = []
|
||
for (let key in data) {
|
||
dataList.push({
|
||
macAddress: key,
|
||
data: JSON.parse(data[key])
|
||
})
|
||
}
|
||
console.log('=====', dataList)
|
||
testCarList.value = dataList
|
||
|
||
// let data2 = JSON.parse(data['d0:65:78:c4:af:cc'])
|
||
// console.log(data2)
|
||
}
|
||
//是否可以拖拽
|
||
const isDrag = ref(false)
|
||
const changeIsDrag = () => {
|
||
nextTick(() => {
|
||
isDrag.value = !isDrag.value
|
||
console.log(isDrag.value)
|
||
if (!isDrag.value) {
|
||
//还原位置
|
||
resetPosition()
|
||
}
|
||
})
|
||
}
|
||
// 获取曲线的路径
|
||
const getCurvePath = (curve) => {
|
||
let startPointX = (Number(curve.startPointX) + Number(curve.beginWidth) / 2) * radio.value
|
||
let startPointY = (Number(curve.startPointY) + Number(curve.beginHigh) / 2) * radio.value
|
||
let endPointX = (Number(curve.endPointX) + Number(curve.endWidth) / 2) * radio.value
|
||
let endPointY = (Number(curve.endPointY) + Number(curve.endHigh) / 2) * radio.value
|
||
|
||
return `M ${startPointX} ${startPointY} C ${curve.beginControlX * radio.value} ${curve.beginControlY * radio.value}, ${curve.endControlX * radio.value} ${curve.endControlY * radio.value}, ${endPointX} ${endPointY}`
|
||
}
|
||
//放大缩小
|
||
const isSizeRaio = ref(1)
|
||
const changeSizeRaio = (type) => {
|
||
if (type < 0 && isSizeRaio.value + type <= 0) {
|
||
return
|
||
}
|
||
isSizeRaio.value += type
|
||
}
|
||
//图例状态
|
||
const legendObj = reactive({
|
||
driveLineShow: true,
|
||
carShow: true,
|
||
legendShow: true
|
||
})
|
||
// 车辆是否显示
|
||
const changCarShow = () => {
|
||
legendObj.carShow = !legendObj.carShow
|
||
}
|
||
// 车辆行驶路线是否展示
|
||
const changDriveLineShow = () => {
|
||
legendObj.driveLineShow = !legendObj.driveLineShow
|
||
}
|
||
const toggleFullScreen = () => {
|
||
var elem = document.getElementById('indexpage-container') // 获取元素
|
||
if (!document.fullscreenElement) {
|
||
// 检查是否已经是全屏模式
|
||
if (elem.requestFullscreen) {
|
||
// 支持requestFullscreen API的标准方式
|
||
elem.requestFullscreen().catch((err) => {
|
||
alert(`无法进入全屏模式: ${err.message}`) // 处理错误情况
|
||
})
|
||
} else if (elem.mozRequestFullScreen) {
|
||
// 旧版Firefox的API名称(已废弃)
|
||
elem.mozRequestFullScreen()
|
||
} else if (elem.webkitRequestFullscreen) {
|
||
// WebKit/Safari/Chrome的API名称(已废弃)
|
||
elem.webkitRequestFullscreen()
|
||
} else if (elem.msRequestFullscreen) {
|
||
// IE/Edge的API名称(已废弃)
|
||
elem.msRequestFullscreen()
|
||
}
|
||
isDrag.value = true
|
||
} else {
|
||
// 退出全屏模式
|
||
if (document.exitFullscreen) {
|
||
// 标准API退出全屏模式
|
||
document.exitFullscreen()
|
||
} else if (document.mozCancelFullScreen) {
|
||
// 旧版Firefox的API名称(已废弃)
|
||
document.mozCancelFullScreen()
|
||
} else if (document.webkitExitFullscreen) {
|
||
// WebKit/Safari/Chrome的API名称(已废弃)
|
||
document.webkitExitFullscreen()
|
||
} else if (document.msExitFullscreen) {
|
||
// IE/Edge的API名称(已废弃)
|
||
document.msExitFullscreen()
|
||
}
|
||
console.log('退出全屏')
|
||
isDrag.value = false
|
||
resetPosition()
|
||
}
|
||
// 监听全屏状态变化事件
|
||
document.addEventListener('fullscreenchange', function () {
|
||
if (!document.fullscreenElement) {
|
||
console.log('已退出全屏模式')
|
||
// 在这里可以添加退出全屏后的逻辑
|
||
isDrag.value = false
|
||
resetPosition()
|
||
} else {
|
||
console.log('已进入全屏模式')
|
||
// 在这里可以添加进入全屏后的逻辑
|
||
isDrag.value = true
|
||
}
|
||
})
|
||
|
||
// 监听旧版浏览器的全屏状态变化事件
|
||
document.addEventListener('mozfullscreenchange', function () {
|
||
if (!document.mozFullScreenElement) {
|
||
console.log('已退出全屏模式 (Firefox旧版)')
|
||
isDrag.value = false
|
||
resetPosition()
|
||
} else {
|
||
console.log('已进入全屏模式 (Firefox旧版)')
|
||
isDrag.value = true
|
||
}
|
||
})
|
||
|
||
document.addEventListener('webkitfullscreenchange', function () {
|
||
if (!document.webkitFullscreenElement) {
|
||
console.log('已退出全屏模式 (WebKit旧版)')
|
||
isDrag.value = false
|
||
resetPosition()
|
||
} else {
|
||
console.log('已进入全屏模式 (WebKit旧版)')
|
||
isDrag.value = true
|
||
}
|
||
})
|
||
|
||
document.addEventListener('msfullscreenchange', function () {
|
||
if (!document.msFullscreenElement) {
|
||
console.log('已退出全屏模式 (IE/Edge旧版)')
|
||
isDrag.value = false
|
||
resetPosition()
|
||
} else {
|
||
console.log('已进入全屏模式 (IE/Edge旧版)')
|
||
isDrag.value = true
|
||
}
|
||
})
|
||
}
|
||
//获取地图区域
|
||
const getList = async () => {
|
||
let data = await MapApi.getPositionMapGetMap()
|
||
let mapList = []
|
||
for (let key in data) {
|
||
mapList.push({
|
||
floor: key,
|
||
label: key + '层',
|
||
value: key,
|
||
children: data[key]
|
||
})
|
||
}
|
||
if (mapList.length) {
|
||
mapList.forEach((item) => {
|
||
if (item.children.length) {
|
||
item.children.forEach((child) => {
|
||
child.label = child.area
|
||
child.value = child.id
|
||
})
|
||
}
|
||
})
|
||
}
|
||
|
||
list.value = mapList
|
||
console.log(list.value, data)
|
||
//默认取第一个
|
||
if (data[1][0]) {
|
||
getMapData(data[1][0])
|
||
}
|
||
}
|
||
//库位双击
|
||
const storeClick = async (item) => {
|
||
console.log(item)
|
||
let storeData = await MapApi.houseLocationGetByMapItemId({
|
||
mapId: item.positionMapId,
|
||
mapItemId: item.id
|
||
})
|
||
console.log(storeData)
|
||
storeDialogRef.value.open(JSON.parse(JSON.stringify(storeData)))
|
||
}
|
||
const lineList = ref([])
|
||
const pointList = ref([])
|
||
const getPositionMapListFun = async (positionMapId) => {
|
||
// console.log(positionMapId)
|
||
let data = await MapApi.getPositionMapItemList({ positionMapId: positionMapId })
|
||
// console.log(data)
|
||
if (data && data.length > 0) {
|
||
data.forEach((item) => {
|
||
// console.log(JSON.parse(item.dataJson))
|
||
item.formattedData = item.dataJson ? JSON.parse(item.dataJson) : ''
|
||
item.showData = item.dataJson ? JSON.parse(item.dataJson)[0] : null
|
||
item.imgUrl = formatteTypeImg(item.type)
|
||
})
|
||
}
|
||
console.log(data)
|
||
pointList.value = data
|
||
|
||
// console.log(pointList.value)
|
||
// let lineStyle = calculateDistanceAndAngle(
|
||
// {
|
||
// left: pointList.value[0].locationX,
|
||
// top: pointList.value[0].locationY
|
||
// },
|
||
// {
|
||
// left: pointList.value[1].locationX,
|
||
// top: pointList.value[1].locationY
|
||
// }
|
||
// )
|
||
// console.log(lineStyle)
|
||
// lineList.value = [
|
||
// {
|
||
// ...lineStyle,
|
||
// color: '#1677ff',
|
||
// height: 2
|
||
// }
|
||
// ]
|
||
}
|
||
const draggableElement = ref(null)
|
||
const resetPosition = () => {
|
||
if (draggableElement.value) {
|
||
resetDragPosition(draggableElement.value)
|
||
}
|
||
}
|
||
const calculateDistanceAndAngle = (point1, point2) => {
|
||
// 计算水平和垂直方向的差值
|
||
const dx = point2.left - point1.left
|
||
const dy = point2.top - point1.top
|
||
|
||
// 计算两点之间的长度(使用勾股定理)
|
||
const distance = Math.sqrt(dx * dx + dy * dy)
|
||
|
||
// 计算两点连线与水平轴正方向的夹角(弧度)
|
||
const angleInRadians = Math.atan2(dy, dx)
|
||
|
||
// 将弧度转换为角度
|
||
const angleInDegrees = angleInRadians * (180 / Math.PI)
|
||
|
||
return {
|
||
distance: distance,
|
||
angle: angleInDegrees,
|
||
left: point1.left,
|
||
top: point1.top
|
||
}
|
||
}
|
||
|
||
const formatteTypeImg = (type) => {
|
||
switch (type) {
|
||
case 1:
|
||
return ''
|
||
case 2:
|
||
return 'https://api.znkjfw.com/admin-api/infra/file/4/get/库位库存_png_179_1739326653035.png'
|
||
case 3:
|
||
return 'https://api.znkjfw.com/admin-api/infra/file/4/get/设备点_png_179_1739327151877.png'
|
||
case 4:
|
||
return 'https://api.znkjfw.com/admin-api/infra/file/4/get/停车场-01_png_179_1739326933020.png'
|
||
case 5:
|
||
return 'https://api.znkjfw.com/admin-api/infra/file/4/get/区域_png_179_1739327151876.png'
|
||
case 6:
|
||
return 'https://api.znkjfw.com/admin-api/infra/file/4/get/等待点_png_179_1739326991439.png'
|
||
default:
|
||
return ''
|
||
}
|
||
}
|
||
const replaceHttpWithWs = (str) => {
|
||
return str.replace(/^http/, 'ws')
|
||
}
|
||
// 连接websocket
|
||
const linkWebSocket = (url) => {
|
||
socketClient.value = new WebSocketClient(url)
|
||
if (socketClient.value) {
|
||
// 监听消息
|
||
socketClient.value.onMessage((message) => {
|
||
console.log('收到消息:', message)
|
||
if (message.type == 'map_push') {
|
||
let data = JSON.parse(testJson.content)
|
||
// console.log("============",data)
|
||
let dataList = []
|
||
for (let key in data) {
|
||
dataList.push({
|
||
macAddress: key,
|
||
data: JSON.parse(data[key])
|
||
})
|
||
}
|
||
console.log('=====', dataList)
|
||
testCarList.value = dataList
|
||
}
|
||
})
|
||
}
|
||
}
|
||
// 发送websocket消息
|
||
const sendMessage = () => {
|
||
socketClient.value.send('Hello, WebSocket!')
|
||
}
|
||
// 断开连接
|
||
const disconnect = () => {
|
||
socketClient.value.disconnect()
|
||
}
|
||
//获取扫描图
|
||
const getMapData = async (item) => {
|
||
// console.log("============",item)
|
||
nowObject.value = JSON.parse(JSON.stringify(item))
|
||
let websoketUrl = `${replaceHttpWithWs(import.meta.env.VITE_BASE_URL)}/infra/ws?type=map&floor=${nowObject.value.floor}&area=${nowObject.value.area}`
|
||
console.log(websoketUrl)
|
||
linkWebSocket(websoketUrl)
|
||
getPositionMapListFun(nowObject.value.id)
|
||
emit('transmitMapInfo', {
|
||
id: item.id,
|
||
floor: item.floor,
|
||
area: item.area
|
||
})
|
||
let data = await MapApi.getPositionMapdDwnloadPngBase64({
|
||
floor: item.floor,
|
||
area: item.area
|
||
})
|
||
imgUrl.value = data
|
||
computedRatio()
|
||
getMapLineList()
|
||
}
|
||
const heightVal = ref(0)
|
||
const widthVal = ref(0)
|
||
const radio = ref(1)
|
||
const computedRatio = () => {
|
||
nextTick(() => {
|
||
if (imgUrl.value) {
|
||
//这段代码之后会删掉 yaml会给原始宽高
|
||
getImageSize(imgUrl.value)
|
||
.then(({ width, height }) => {
|
||
// console.log("原始地图的宽高",JSON.parse(nowObject.value.yamlJson))
|
||
if (testCarList.value.length) {
|
||
testCarList.value.forEach((item) => {
|
||
item.originWidth = width
|
||
item.originHeight = height
|
||
item.origin = JSON.parse(nowObject.value.yamlJson).origin
|
||
item.realX = convertToFrontendCoordinates(item.origin, width, height, [
|
||
item.data.pose2d.x,
|
||
item.data.pose2d.y
|
||
]).left
|
||
item.realY = convertToFrontendCoordinates(item.origin, width, height, [
|
||
item.data.pose2d.x,
|
||
item.data.pose2d.y
|
||
]).top
|
||
})
|
||
console.log('当前数据', testCarList.value)
|
||
}
|
||
})
|
||
.catch((error) => {
|
||
console.error(error.message)
|
||
})
|
||
|
||
let width = getElementWidthByClass('indexpage-container')
|
||
getImageWidth(imgUrl.value, 'width').then((res) => {
|
||
// console.log(res)
|
||
let ratioVal = width / res
|
||
radio.value = ratioVal
|
||
widthVal.value = res
|
||
if (pointList.value.length) {
|
||
pointList.value.forEach((item) => {
|
||
item.radio = radio.value
|
||
})
|
||
}
|
||
if (testCarList.value.length) {
|
||
testCarList.value.forEach((item) => {
|
||
item.radio = radio.value
|
||
})
|
||
}
|
||
})
|
||
getImageWidth(imgUrl.value, 'height').then((res) => {
|
||
console.log('高', res)
|
||
heightVal.value = res
|
||
})
|
||
// console.log(width)
|
||
}
|
||
})
|
||
}
|
||
|
||
const getMapLineList = () => {
|
||
MapApi.mapLineListGet({ positionMapId: nowObject.value.id }).then((res) => {
|
||
console.log(res)
|
||
lineList.value = res
|
||
})
|
||
}
|
||
|
||
const getImageSize = (url) => {
|
||
return new Promise((resolve, reject) => {
|
||
const img = new Image()
|
||
img.src = url
|
||
// 图片加载成功时触发
|
||
img.onload = () => {
|
||
const width = img.width
|
||
const height = img.height
|
||
resolve({ width, height })
|
||
}
|
||
// 图片加载失败时触发
|
||
img.onerror = () => {
|
||
reject(new Error(`Failed to load image from URL 获取图片尺寸失败: ${url}`))
|
||
}
|
||
})
|
||
}
|
||
const convertToFrontendCoordinates = (origin, width, height, target) => {
|
||
// 提取原点的 x 和 y 坐标
|
||
const [originX, originY] = origin
|
||
// 提取目标点的 x 和 y 坐标
|
||
const [targetX, targetY] = target
|
||
|
||
// 计算目标点相对于原点在 x 方向上的偏移量
|
||
const offsetX = targetX - originX
|
||
// 计算目标点相对于原点在 y 方向上的偏移量
|
||
const offsetY = targetY - originY
|
||
|
||
// 计算前端页面中的 left 坐标
|
||
const left = offsetX
|
||
// 计算前端页面中的 top 坐标,需要考虑坐标系的转换
|
||
const top = height - offsetY
|
||
|
||
return {
|
||
top,
|
||
left,
|
||
origin
|
||
}
|
||
}
|
||
const getImageWidth = (imageUrl, name) => {
|
||
return new Promise((resolve, reject) => {
|
||
const img = new Image()
|
||
img.onload = function () {
|
||
resolve(img[name])
|
||
}
|
||
img.onerror = function () {
|
||
reject(new Error('图片加载失败'))
|
||
}
|
||
img.src = imageUrl
|
||
})
|
||
}
|
||
|
||
const getElementWidthByClass = (className) => {
|
||
const element = document.querySelector(`.${className}`)
|
||
if (element) {
|
||
// return window.getComputedStyle(element).width
|
||
const widthWithUnit = window.getComputedStyle(element).width
|
||
return widthWithUnit.slice(0, -2)
|
||
}
|
||
return null
|
||
}
|
||
//小车双击
|
||
const carDbClick = (item, index) => {
|
||
console.log(item)
|
||
carDialogRef.value.open(JSON.parse(JSON.stringify(item)))
|
||
}
|
||
const boxLeft = ref(0)
|
||
//获取元素距离左边的距离
|
||
const getLeftPx = () => {
|
||
let indexpageContainer = document.getElementById('indexpage-container')
|
||
console.log('距离左边的距离', indexpageContainer.getBoundingClientRect().left)
|
||
boxLeft.value = indexpageContainer.getBoundingClientRect().left + 6
|
||
}
|
||
// 封装监听元素宽度变化的方法
|
||
const observeWidthChange = (id, callback) => {
|
||
const targetElement = document.getElementById(id)
|
||
if (!targetElement) {
|
||
console.error(`未找到 ID 为 ${id} 的元素`)
|
||
return
|
||
}
|
||
|
||
const resizeObserver = new ResizeObserver((entries) => {
|
||
for (const entry of entries) {
|
||
const newWidth = entry.contentRect.width
|
||
callback(newWidth)
|
||
}
|
||
})
|
||
|
||
resizeObserver.observe(targetElement)
|
||
|
||
return () => {
|
||
resizeObserver.disconnect()
|
||
}
|
||
}
|
||
|
||
let stopObserving
|
||
defineExpose({ getMapData }) // 提供 open 方法,用于打开弹窗
|
||
onMounted(() => {
|
||
carPointListFun()
|
||
// getList()
|
||
window.addEventListener('resize', computedRatio)
|
||
stopObserving = observeWidthChange('indexpage-container', (newWidth) => {
|
||
// 在这里执行宽度变化时的自定义操作
|
||
// console.log(`元素宽度已变为: ${newWidth}px`);
|
||
// 你可以添加更多的操作,比如更新组件的状态等
|
||
getLeftPx()
|
||
computedRatio()
|
||
})
|
||
})
|
||
onBeforeUnmount(() => {
|
||
window.removeEventListener('resize', computedRatio)
|
||
})
|
||
// 组件卸载时停止监听
|
||
onUnmounted(() => {
|
||
if (stopObserving) {
|
||
stopObserving()
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.affix-container {
|
||
width: 100%;
|
||
position: relative;
|
||
overflow: hidden;
|
||
background: rgba(0, 0, 0, 0.8);
|
||
}
|
||
.indexpage-container {
|
||
width: 100%;
|
||
position: relative;
|
||
}
|
||
.indexpage-container .affix-container-top {
|
||
width: 100%;
|
||
height: 80px;
|
||
background-color: #fff;
|
||
border-bottom: 1px solid #f5f5f5;
|
||
}
|
||
.indexpage-container-box-point {
|
||
width: 100%;
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
bottom: 0;
|
||
right: 0;
|
||
}
|
||
.indexpage-container-box-point-item {
|
||
position: absolute;
|
||
cursor: pointer;
|
||
}
|
||
.scrollbar-flex-content {
|
||
padding: 2px 6px;
|
||
display: flex;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
}
|
||
.scrollbar-demo-item {
|
||
flex-shrink: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 90px;
|
||
height: 30px;
|
||
margin: 10px 6px;
|
||
text-align: center;
|
||
border-radius: 4px;
|
||
background: var(--el-color-primary-light-9);
|
||
color: var(--el-color-primary);
|
||
position: relative;
|
||
}
|
||
.scrollbar-demo-item {
|
||
cursor: pointer;
|
||
border: none;
|
||
}
|
||
|
||
:focus-visible {
|
||
outline: none;
|
||
}
|
||
.indexpage-container-box {
|
||
width: 100%;
|
||
position: relative;
|
||
}
|
||
.indexpage-container-box-img {
|
||
width: 100%;
|
||
height: auto;
|
||
}
|
||
.indexpage-container-box-point-item-inner-popover-item {
|
||
display: flex;
|
||
font-family:
|
||
PingFangSC,
|
||
PingFang SC;
|
||
font-weight: 400;
|
||
font-size: 14px;
|
||
color: #0d162a;
|
||
}
|
||
.line-box {
|
||
position: absolute;
|
||
}
|
||
.indexpage-car-item {
|
||
position: absolute;
|
||
cursor: pointer;
|
||
}
|
||
.affix-container-left {
|
||
position: fixed;
|
||
bottom: 26px;
|
||
z-index: 999;
|
||
// overflow: hidden;
|
||
}
|
||
.affix-container-left-box {
|
||
width: 144px;
|
||
// height: 120px;
|
||
// background: #ffffff;
|
||
}
|
||
.affix-container-left-box-item-box {
|
||
width: 100%;
|
||
border-bottom: 1px solid #eeeeee;
|
||
background: #ffffff;
|
||
}
|
||
.affix-container-left-box-item {
|
||
width: 100%;
|
||
padding: 0 18px;
|
||
height: 42px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
.affix-container-left-box-item-left {
|
||
font-family:
|
||
PingFangSC,
|
||
PingFang SC;
|
||
font-weight: 400;
|
||
font-size: 14px;
|
||
color: rgba(0, 0, 0, 0.88);
|
||
}
|
||
.affix-container-left-box-item-img {
|
||
cursor: pointer;
|
||
flex-shrink: 0;
|
||
width: 20px;
|
||
height: 12px;
|
||
}
|
||
.affix-container-left-box-item-bottom {
|
||
width: 100%;
|
||
height: 36px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
cursor: pointer;
|
||
background: #ffffff;
|
||
}
|
||
.affix-container-left-box-item-bottom-img {
|
||
cursor: pointer;
|
||
flex-shrink: 0;
|
||
width: 12px;
|
||
height: 7px;
|
||
}
|
||
.affix-container-left-box-item-bottom-left {
|
||
cursor: pointer;
|
||
flex-shrink: 0;
|
||
font-family:
|
||
PingFangSC,
|
||
PingFang SC;
|
||
font-weight: 400;
|
||
font-size: 16px;
|
||
color: #98a4bf;
|
||
margin-right: 2px;
|
||
}
|
||
.affix-container-right {
|
||
position: fixed;
|
||
z-index: 999;
|
||
right: 40px;
|
||
bottom: 20px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: flex-end;
|
||
}
|
||
.affix-container-right-item {
|
||
width: 28px;
|
||
height: 28px;
|
||
cursor: pointer;
|
||
flex-shrink: 0;
|
||
margin-left: 8px;
|
||
}
|
||
</style>
|