diff --git a/src/views/board/allBoard/index.vue b/src/views/board/allBoard/index.vue index b4f0d278..ab991176 100644 --- a/src/views/board/allBoard/index.vue +++ b/src/views/board/allBoard/index.vue @@ -314,7 +314,7 @@ import { ref, reactive, onMounted, onBeforeUnmount } from 'vue' import { dateFormatter } from '@/utils/formatTime' import * as ChartsApi from '@/api/boardCharts' import { DICT_TYPE, getIntDictOptions, getDictOptions } from '@/utils/dict' -import indexPage from '../../mapPage/realTimeMap/components/indexPage.vue' +import indexPage from './indexPage.vue' import * as MapApi from '@/api/map/map' const router = useRouter() // 路由对象 const indexPageRef = ref(null) diff --git a/src/views/board/allBoard/indexPage.vue b/src/views/board/allBoard/indexPage.vue index 7128abd2..c2f8e730 100644 --- a/src/views/board/allBoard/indexPage.vue +++ b/src/views/board/allBoard/indexPage.vue @@ -3,44 +3,90 @@ class="affix-container" id="indexpage-container" :style="{ - height: heightVal * radio + 'px', + height: heightVal + 'px', cursor: isDrag ? 'pointer' : 'default', - scale: isSizeRaio, + scale: isSizeRadio, transformOrigin: '0 0' }" >
- +
-
+
- -
@@ -54,13 +100,19 @@ :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' + width: legendObj.carShow + ? (carWidth / nowObject.showYamlJson.resolution / 100) * radio + 'px' + : '0', + height: legendObj.carShow + ? (carHeight / nowObject.showYamlJson.resolution / 100) * radio + 'px' + : '0', + transform: 'rotate(' + radianToDegree(item.data.pose2d.yaw) + 'deg)', + transition: 'all 0.2s linear', + zIndex: 9999 }" >
@@ -68,203 +120,107 @@ 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' }" + :style="{ + left: + (Number(item.locationX) - Number(item.locationWidePx) / 2) * Number(radio) + 'px', + top: + (Number(item.locationY) - Number(item.locationDeepPx) / 2) * Number(radio) + 'px', + width: Number(item.locationWidePx) * Number(radio) + 'px', + height: Number(item.locationDeepPx) * Number(radio) + 'px' + }" > - -
- -
- - -
-
-
- 库位名: -
-
- {{ item.showData.locationNo || '' }} -
-
-
-
- 所属线库: -
-
- {{ item.showData.laneName || '' }} -
-
-
-
- 所属区域: -
-
- {{ item.showData.areaName || '' }} -
-
-
-
-
- -
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- - + +
+ + + +
+
+
库位名:
+
+ {{ item.showData?.locationNo || '' }} +
+
+
+
所属线库:
+
+ {{ item.showData?.laneName || '' }} +
+
+
+
所属区域:
+
+ {{ item.showData?.areaName || '' }} +
+
+
+
+ + + + + + + +
-
+
行驶路线
车辆
-
图层
+
图例
+
- +
@@ -341,7 +291,6 @@
@@ -350,13 +299,12 @@
- + @@ -376,36 +324,47 @@ import storeDialog from '@/views/mapPage/realTimeMap/components/storeDialog.vue' import { color } from 'echarts' import { resetDragPosition } from '@/utils/drag' import carDialog from '@/views/mapPage/realTimeMap/components/carDialog.vue' - +import { is } from 'bpmn-js/lib/util/ModelUtil' +import JSONBigInt from 'json-bigint' +import { propTypes } from '@/utils/propTypes' const imgUrl = ref('') const carDialogRef = ref(null) const socketClient = ref(null) - -const emit = defineEmits(['transmitMapId', 'getWidth', 'getLeftPx']) -const storeDialogRef = ref(null) +const router = useRouter() // 路由对象 +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 +const nowObject = ref(null) // 地图当前对象 父组件传过来的 +const testCarList = ref([]) //小车数组 +const carWidth = ref(60) +const carHeight = ref(32) - // let data2 = JSON.parse(data['d0:65:78:c4:af:cc']) - // console.log(data2) +const nodeStyle = (item, index) => { + return { + verticalAlign: 'top', + objectFit: 'cover', + width: Number(item.locationWidePx) * Number(radio.value) + 'px', + height: Number(item.locationDeepPx) * Number(radio.value) + 'px' + } +} + +// 定义属性 +const props = defineProps({ + // 当前选中的链接 + isAllBoard: propTypes.bool.def(false) +}) +const convertActualToBrowser = (pointX, pointY) => { + let resolution = Number(nowObject.value.showYamlJson.resolution) + let origin = nowObject.value.showYamlJson.origin + + const y1 = Number(origin[1]) + Number(nowObject.value.showYamlJson.height) * resolution + let x = Math.max(Number(pointX) - Number(origin[0]), 0) + let y = Math.max(y1 - Number(pointY), 0) + + return { + x: x / resolution - carWidth.value / resolution / 100 / 2, + y: y / resolution - carHeight.value / resolution / 100 / 2 + } } //是否可以拖拽 const isDrag = ref(false) @@ -419,22 +378,47 @@ const changeIsDrag = () => { } }) } + +//点击选中线 如果选中就变成非选中 否则相反 +const handleChooseRoute = (val, i) => { + // console.log('============================', val) + if (lineList.value.length) { + if (val.isSelect) { + lineList.value.forEach((item, index) => { + if (index == i) { + item.isSelect = false + } else { + item.isSelect = false + } + }) + } else { + lineList.value.forEach((item, index) => { + if (index == i) { + item.isSelect = true + } else { + item.isSelect = false + } + }) + } + } +} + // 获取曲线的路径 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 + let startPointX = Number(curve.startPointX) * radio.value + let startPointY = Number(curve.startPointY) * radio.value + let endPointX = Number(curve.endPointX) * radio.value + let endPointY = Number(curve.endPointY) * 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 isSizeRadio = ref(1) const changeSizeRaio = (type) => { - if (type < 0 && isSizeRaio.value + type <= 0) { + if (type < 0 && isSizeRadio.value + type <= 0) { return } - isSizeRaio.value += type + isSizeRadio.value += type } //图层状态 const legendObj = reactive({ @@ -537,89 +521,93 @@ const toggleFullScreen = () => { } }) } -//获取地图区域 -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 + pointList.value = await MapApi.getPositionMapItemList({ positionMapId: positionMapId }) - // 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 - // } - // ] + pointList.value?.forEach((item) => { + item.formattedData = item.dataJson ? JSON.parse(item.dataJson) : '' + item.showData = item.dataJson ? JSON.parse(item.dataJson)[0] : null + item.imgUrl = formatTypeImg(item.type) + + if (item.type === 1) { + item.locationDeep = 40 + item.locationWide = 40 + } else if (item.type === 5 || item.type === 6) { + item.locationDeep = 150 + item.locationWide = 150 + } else if (item.type === 2) { + //库位点 + item.dataList = JSONBigInt({ storeAsString: true }).parse(item.dataJson) + item.locationDeep = item.dataList[0].locationDeep + item.locationWide = item.dataList[0].locationWide + } else if (item.type === 3) { + item.dataObj = JSONBigInt({ storeAsString: true }).parse(item.dataJson) + item.locationDeep = item.dataObj.locationDeep + item.locationWide = item.dataObj.locationWide + } else if (item.type === 4) { + item.dataObj = JSONBigInt({ storeAsString: true }).parse(item.dataJson) + item.locationDeep = item.dataObj.locationDeep + item.locationWide = item.dataObj.locationWide + } + //要将实际的cm改成px + if (item.locationWide && item.locationDeep) { + let pxObj = cmConversionPx(item.locationWide, item.locationDeep) + item.locationWidePx = pxObj.pWidth + item.locationDeepPx = pxObj.pHeight + } + }) } +//将节点实际宽高cm转换成px +const cmConversionPx = (cWidth, cHeight) => { + let pWidth = Number(cWidth) / Number(nowObject.value.showYamlJson.resolution) / 100 + let pHeight = Number(cHeight) / Number(nowObject.value.showYamlJson.resolution) / 100 + return { + pWidth, + pHeight + } +} + const draggableElement = ref(null) const resetPosition = () => { if (draggableElement.value) { resetDragPosition(draggableElement.value) } } +// 计算直线中间箭头的路径 +const getLineMidArrowPath = (item) => { + const midX = ((Number(item.startPointX) + Number(item.endPointX)) / 2) * radio.value + const midY = ((Number(item.startPointY) + Number(item.endPointY)) / 2) * radio.value + + let dx = item.endPointX * radio.value - item.startPointX * radio.value + let dy = item.endPointY * radio.value - item.startPointY * radio.value + let length = Math.sqrt(dx * dx + dy * dy) + + if (length === 0) { + return `M ${midX} ${midY} L ${midX} ${midY}` + } + + const unitDx = dx / length + const unitDy = dy / length + + const arrowLength = 1 + const startXArrow = midX - unitDx * arrowLength + const startYArrow = midY - unitDy * arrowLength + const endXArrow = midX + const endYArrow = midY + + return `M ${startXArrow} ${startYArrow} L ${endXArrow} ${endYArrow}` +} const calculateDistanceAndAngle = (point1, point2) => { // 计算水平和垂直方向的差值 const dx = point2.left - point1.left @@ -642,7 +630,7 @@ const calculateDistanceAndAngle = (point1, point2) => { } } -const formatteTypeImg = (type) => { +const formatTypeImg = (type) => { switch (type) { case 1: return '' @@ -669,10 +657,14 @@ const linkWebSocket = (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) + // console.log('收到消息:', message) + //心跳信息不处理 + if (message == 'ping' || message == 'pong') return + let jsonMsg = JSON.parse(message) + // 车辆信息 + if (jsonMsg.type == 'map_push') { + let data = JSON.parse(jsonMsg.content) + console.log('======车位点======', data) let dataList = [] for (let key in data) { dataList.push({ @@ -682,10 +674,68 @@ const linkWebSocket = (url) => { } // console.log('=====', dataList) testCarList.value = dataList + computedRatio() + } + //告警信息 + if (jsonMsg.type == 'agv_warn') { + ElMessage({ + message: () => + h( + 'div', + { + style: 'background-color: #C60606;' + }, + [ + h( + 'span', + { style: 'color: rgba(255,255,255,0.88);font-size: .875rem;' }, + `${JSON.parse(jsonMsg.content)}` + ), + h( + 'span', + { + onClick: () => lookError(), + style: + 'color: rgba(255,255,255,0.88);font-size: .875rem;cursor: pointer;text-decoration-line: underline;margin-left: 2rem;' + }, + '详情' + ) + ] + ), + showClose: false, + duration: 3000, // 让消息持续显示,直到用户关闭 + type: 'info', + customClass: 'indexpage-custom-message-style' + }) + } + // 规划路线信息 + if (jsonMsg.type == 'planning_move_pose') { + let data = JSON.parse(jsonMsg.content) + // console.log('======规划路线======', JSON.parse(data).data) + let dataList = JSON.parse(data).data + if(lineList.value.length > 0) { + console.log(lineList.value) + lineList.value = setIsSelect(lineList.value, dataList) + } } }) } } +const setIsSelect =(arr1, arr2) => { + for (let i = 0; i < arr1.length; i++) { + const element = arr1[i]; + const isExist = arr2.includes(element.id); + element.isSelect = isExist; + } + return arr1; +} +// 查看更多异常列表 +const lookError = () => { + // console.log('点击了') + router.push({ + path: '/carError' + }) +} // 发送websocket消息 const sendMessage = () => { socketClient.value.send('Hello, WebSocket!') @@ -694,14 +744,17 @@ const sendMessage = () => { const disconnect = () => { socketClient.value.disconnect() } +const emitParent = () => { + getMapData(nowObject.value) +} //获取扫描图 const getMapData = async (item) => { - console.log('============', JSON.parse(item.yamlJson)) + // console.log('============', JSON.parse(item.yamlJson)) + testCarList.value = [] nowObject.value = JSON.parse(JSON.stringify(item)) nowObject.value.showYamlJson = JSON.parse(item.yamlJson) - 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) + let websocketUrl = `${replaceHttpWithWs(import.meta.env.VITE_BASE_URL)}/infra/ws?type=map&floor=${nowObject.value.floor}&area=${nowObject.value.area}` + linkWebSocket(websocketUrl) getPositionMapListFun(nowObject.value.id) emit('transmitMapInfo', { id: item.id, @@ -716,6 +769,13 @@ const getMapData = async (item) => { computedRatio() getMapLineList() } +//偏航率斜率算旋转 +const radianToDegree = (radian) => { + // 将弧度转换为角度 + const degree = radian * (180 / Math.PI) + // 返回带有单位 'deg' 的 CSS 角度值 + return `${degree}` +} const heightVal = ref(0) const widthVal = ref(0) const radio = ref(1) @@ -725,22 +785,14 @@ const computedRatio = () => { //这段代码之后会删掉 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 + item.realX = convertActualToBrowser(item.data.pose2d.x, item.data.pose2d.y).x + item.realY = convertActualToBrowser(item.data.pose2d.x, item.data.pose2d.y).y }) - // console.log('当前数据', testCarList.value) } }) .catch((error) => { @@ -748,13 +800,11 @@ const computedRatio = () => { }) let width = getElementWidthByClass('indexpage-container') - emit('getWidth', width) getImageWidth(imgUrl.value, 'width').then((res) => { - // console.log(res) + console.log(res) let ratioVal = width / res radio.value = ratioVal - widthVal.value = res - + widthVal.value = res * radio.value if (pointList.value.length) { pointList.value.forEach((item) => { item.radio = radio.value @@ -767,21 +817,20 @@ const computedRatio = () => { } }) getImageWidth(imgUrl.value, 'height').then((res) => { - // console.log('高', res) - heightVal.value = res + heightVal.value = res * radio.value }) - // 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() @@ -853,7 +902,6 @@ const getLeftPx = () => { let indexpageContainer = document.getElementById('indexpage-container') // console.log('距离左边的距离', indexpageContainer.getBoundingClientRect().left) boxLeft.value = indexpageContainer.getBoundingClientRect().left + 6 - emit('getLeftPx', indexpageContainer.getBoundingClientRect().left) } // 封装监听元素宽度变化的方法 const observeWidthChange = (id, callback) => { @@ -880,7 +928,6 @@ const observeWidthChange = (id, callback) => { let stopObserving defineExpose({ getMapData }) // 提供 open 方法,用于打开弹窗 onMounted(() => { - carPointListFun() // getList() window.addEventListener('resize', computedRatio) stopObserving = observeWidthChange('indexpage-container', (newWidth) => { @@ -913,11 +960,19 @@ onUnmounted(() => { width: 100%; position: relative; } +.indexpage-container-active { + width: 100%; + position: relative; + cursor: grab; +} +.indexpage-container-active:active { + cursor: grabbing; +} .indexpage-container .affix-container-top { width: 100%; - height: 80px; + height: 5rem; background-color: #fff; - border-bottom: 1px solid #f5f5f5; + border-bottom: 0.0625rem solid #f5f5f5; } .indexpage-container-box-point { width: 100%; @@ -928,11 +983,12 @@ onUnmounted(() => { right: 0; } .indexpage-container-box-point-item { + box-sizing: border-box; position: absolute; cursor: pointer; } .scrollbar-flex-content { - padding: 2px 6px; + padding: 0.125rem 0.375rem; display: flex; align-items: center; flex-wrap: wrap; @@ -942,11 +998,11 @@ onUnmounted(() => { display: flex; align-items: center; justify-content: center; - width: 90px; - height: 30px; - margin: 10px 6px; + width: 5.625rem; + height: 1.875rem; + margin: 0.625rem 0.375rem; text-align: center; - border-radius: 4px; + border-radius: 0.25rem; background: var(--el-color-primary-light-9); color: var(--el-color-primary); position: relative; @@ -967,14 +1023,15 @@ onUnmounted(() => { width: 100%; height: auto; } -.indexpage-container-box-point-item-inner-popover-item { +.indexpage-popover-item { display: flex; font-family: PingFangSC, PingFang SC; font-weight: 400; - font-size: 14px; + font-size: 0.875rem; color: #0d162a; + margin-bottom: 0.5rem; } .line-box { position: absolute; @@ -985,24 +1042,21 @@ onUnmounted(() => { } .affix-container-left { position: fixed; - bottom: 26px; + bottom: 1.625rem; z-index: 999; - // overflow: hidden; } .affix-container-left-box { - width: 144px; - // height: 120px; - // background: #ffffff; + width: 9rem; } .affix-container-left-box-item-box { width: 100%; - border-bottom: 1px solid #eeeeee; + border-bottom: 0.0625rem solid #eeeeee; background: #ffffff; } .affix-container-left-box-item { width: 100%; - padding: 0 18px; - height: 42px; + padding: 0 1.125rem; + height: 2.625rem; display: flex; align-items: center; justify-content: space-between; @@ -1012,18 +1066,18 @@ onUnmounted(() => { PingFangSC, PingFang SC; font-weight: 400; - font-size: 14px; + font-size: 0.875rem; color: rgba(0, 0, 0, 0.88); } .affix-container-left-box-item-img { cursor: pointer; flex-shrink: 0; - width: 20px; - height: 12px; + width: 1.25rem; + height: 0.75rem; } .affix-container-left-box-item-bottom { width: 100%; - height: 36px; + height: 2.25rem; display: flex; align-items: center; justify-content: center; @@ -1033,8 +1087,8 @@ onUnmounted(() => { .affix-container-left-box-item-bottom-img { cursor: pointer; flex-shrink: 0; - width: 12px; - height: 7px; + width: 0.75rem; + height: 0.4375rem; } .affix-container-left-box-item-bottom-left { cursor: pointer; @@ -1043,24 +1097,24 @@ onUnmounted(() => { PingFangSC, PingFang SC; font-weight: 400; - font-size: 16px; + font-size: 1rem; color: #98a4bf; - margin-right: 2px; + margin-right: 0.125rem; } .affix-container-right { position: fixed; z-index: 999; - right: 40px; - bottom: 20px; + right: 2.5rem; + bottom: 1.25rem; display: flex; align-items: center; justify-content: flex-end; } .affix-container-right-item { - width: 28px; - height: 28px; + width: 1.75rem; + height: 1.75rem; cursor: pointer; flex-shrink: 0; - margin-left: 8px; + margin-left: 0.5rem; } diff --git a/src/views/board/device/createEditDialog.vue b/src/views/board/device/createEditDialog.vue index d1ea05ac..d15982cc 100644 --- a/src/views/board/device/createEditDialog.vue +++ b/src/views/board/device/createEditDialog.vue @@ -1,7 +1,7 @@