修改实时地图小手

增加整体看板
This commit is contained in:
xhf 2025-02-25 14:42:44 +08:00
parent 4b54062344
commit 9690a8a347
4 changed files with 1563 additions and 119 deletions

View File

@ -4,7 +4,7 @@ NODE_ENV=development
VITE_DEV=true VITE_DEV=true
# 请求路径 # 请求路径
VITE_BASE_URL='http://192.168.0.172:48080' VITE_BASE_URL='http://192.168.0.74:48080'
# VITE_BASE_URL='http://192.168.0.189:48080' # VITE_BASE_URL='http://192.168.0.189:48080'
# 文件上传类型server - 后端上传, client - 前端直连上传,仅支持 S3 服务 # 文件上传类型server - 后端上传, client - 前端直连上传,仅支持 S3 服务

View File

@ -33,7 +33,9 @@
<el-col :span="4"> <el-col :span="4">
<div class="top-item"> <div class="top-item">
<div class="top-item-title"> 已取消 </div> <div class="top-item-title"> 已取消 </div>
<div class="top-item-num" style="color: #a6a6a6"> {{ data.cancelledNum || 0 }} </div> <div class="top-item-num" style="color: #a6a6a6">
{{ data.cancelledNum || 0 }}
</div>
</div> </div>
</el-col> </el-col>
<el-col :span="4"> <el-col :span="4">
@ -77,7 +79,9 @@
<el-col :span="4"> <el-col :span="4">
<div class="top-item"> <div class="top-item">
<div class="top-item-title"> 离线 </div> <div class="top-item-title"> 离线 </div>
<div class="top-item-num" style="color: #a6a6a6"> {{ data.statistics.offline || 0 }} </div> <div class="top-item-num" style="color: #a6a6a6">
{{ data.statistics.offline || 0 }}
</div>
</div> </div>
</el-col> </el-col>
<el-col :span="4"> <el-col :span="4">
@ -92,105 +96,207 @@
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
<!-- 底部列表和地图 --> <!-- 下部分 -->
<div class="bottom-box" > <div class="bottom-box">
<el-row :gutter="10"> <el-row :gutter="10">
<!-- 左边 -->
<el-col :span="6"> <el-col :span="6">
<div class="bottom-box-item" v-if="data"> <div class="bottom-box-item" v-if="data">
<div class="bottom-box-item-table"> <div class="bottom-box-item-table">
<div class="bottom-box-item-title"> <div class="bottom-box-item-title">
<div class="bottom-box-item-title-left"> 执行中 </div> <div class="bottom-box-item-title-left"> 执行中 </div>
<div class="bottom-box-item-title-right" @click="toManyTask('执行中')"> 查看更多 </div> <div class="bottom-box-item-title-right" @click="toManyTask('执行中')">
</div> 查看更多
<div> </div>
<el-table :data="data.underway" style="width: 100%"> </div>
<el-table-column prop="taskNo" label="任务编号" /> <div>
<el-table-column prop="skuNumber" label="车辆编号" /> <el-table :data="data.underway" style="width: 100%">
<el-table-column prop="startTime" label="开始时间" :formatter="dateFormatter" /> <el-table-column prop="taskNo" label="任务编号" />
<el-table-column prop="address" label="任务阶段" /> <el-table-column prop="skuNumber" label="车辆编号" />
</el-table> <el-table-column prop="startTime" label="开始时间" :formatter="dateFormatter" />
</div> <el-table-column prop="address" label="任务阶段" />
</el-table>
</div>
</div> </div>
</div> </div>
<div class="bottom-box-item" style="margin-top: 8px" v-if="data"> <div class="bottom-box-item" style="margin-top: 8px" v-if="data">
<div class="bottom-box-item-table"> <div class="bottom-box-item-table">
<div class="bottom-box-item-title"> <div class="bottom-box-item-title">
<div class="bottom-box-item-title-left"> 待执行 </div> <div class="bottom-box-item-title-left"> 待执行 </div>
<div class="bottom-box-item-title-right" @click="toManyTask('未开始')"> 查看更多 </div> <div class="bottom-box-item-title-right" @click="toManyTask('未开始')">
</div> 查看更多
<div> </div>
<el-table :data="data.pendingExecution" style="width: 100%"> </div>
<el-table-column prop="taskNo" label="任务编号" /> <div>
<el-table-column prop="priorilty" label="优先级" /> <el-table :data="data.pendingExecution" style="width: 100%">
<el-table-column prop="createTime" label="生成时间" :formatter="dateFormatter" /> <el-table-column prop="taskNo" label="任务编号" />
</el-table> <el-table-column prop="priorilty" label="优先级" />
</div> <el-table-column prop="createTime" label="生成时间" :formatter="dateFormatter" />
</el-table>
</div>
</div> </div>
</div> </div>
</el-col> </el-col>
<el-col :span="12"> 2 </el-col> <!-- 实时地图 -->
<el-col :span="6"> <el-col :span="12">
<div class="bottom-box-item" v-if="data"> <div style="margin-bottom: 10px">
<div class="bottom-box-item-table"> <el-cascader
<div class="bottom-box-item-title"> v-model="mapValue"
<div class="bottom-box-item-title-left"> 异常信息 </div> :options="list"
<div class="bottom-box-item-title-right"> 查看更多 </div> @change="handleChangeMap"
</div> style="width: 160px"
<div> />
<el-table :data="data.robotWarnMsgDOS" style="width: 100%"> </div>
<el-table-column prop="robotNo" label="车辆编号" /> <div style="width: 100%; padding-bottom: 120px" class="map-box-allBoard">
<el-table-column prop="warnMsg" label="异常信息" > <indexPage ref="indexPageRef" />
<template #default="scope"> </div>
<div class="warn-msg"> <div
<div class="warn-msg-color" :style="{ backgroundColor: computedBackgroundColor(scope.row.warnLevel) }"> style="position: fixed; bottom: 20px"
:style="{ width: widthVal + 'px', left: leftVal + 'px' }"
v-if="data&&data.deviceStatusInfoVOS"
>
<div
ref="scrollContainer"
class="scroll-container"
@mousedown="startDrag"
@mousemove="onDrag"
@mouseup="endDrag"
@mouseleave="endDrag"
>
<div class="content">
<div v-for="(n, i) in data.deviceStatusInfoVOS" :key="i" class="item" :class="{ noBoarder: i === data.deviceStatusInfoVOS.length - 1 }">
<div class="scroll-container-item-left">
<div class="scroll-container-item-left-title">{{ filterTypeFun(n.deviceType, typeList) }}</div>
<div class="scroll-container-item-left-img">
<img
:src="n.defaultImage"
alt=""
object-fit="contain"
style="width: 100%; height: 100%"
/>
</div>
</div>
<div class="scroll-container-item-right">
<div class="scroll-container-item-right-item">
<div class="scroll-container-item-right-item-title">
数量
</div> </div>
<div class="warn-msg-text"> <div class="scroll-container-item-right-item-num">
{{ scope.row.warnMsg || '' }} {{ n.totalNum || 0 }}
</div> </div>
</div> </div>
</template> <div class="scroll-container-item-right-item" style="margin-top: 3px;">
</el-table-column> <div class="scroll-container-item-right-item-title">
<el-table-column prop="createTime" label="发生时间" :formatter="dateFormatter"/> 正常数量
</div>
</el-table> <div class="scroll-container-item-right-item-num">
</div> {{ n.normalNum || 0 }}
</div>
</div>
<div class="scroll-container-item-right-item" style="margin-top: 3px;">
<div class="scroll-container-item-right-item-title">
异常数量
</div>
<div class="scroll-container-item-right-item-num" style="color: #c60606">
{{ n.abnormalNum || 0 }}
</div>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
<div class="bottom-box-item" style="margin-top: 8px" v-if="data"> </el-col>
<!-- 右边 -->
<el-col :span="6">
<div class="bottom-box-item" v-if="data">
<div class="bottom-box-item-table"> <div class="bottom-box-item-table">
<div class="bottom-box-item-title"> <div class="bottom-box-item-title">
<div class="bottom-box-item-title-left"> 车辆信息 </div> <div class="bottom-box-item-title-left"> 异常信息 </div>
<div class="bottom-box-item-title-right"> 查看更多 </div> <div class="bottom-box-item-title-right" @click="toManyWarnMsg"> 查看更多 </div>
</div>
<div>
<el-table :data="data.robotWarnMsgDOS" style="width: 100%">
<el-table-column prop="robotNo" label="车辆编号" />
<el-table-column prop="warnMsg" label="异常信息">
<template #default="scope">
<div class="warn-msg">
<div
class="warn-msg-color"
:style="{ backgroundColor: computedBackgroundColor(scope.row.warnLevel) }"
>
</div>
<div class="warn-msg-text">
{{ scope.row.warnMsg || '' }}
</div>
</div>
</template>
</el-table-column>
<el-table-column prop="createTime" label="发生时间" :formatter="dateFormatter" />
</el-table>
</div>
</div> </div>
<div> </div>
<el-table :data="data.robotElectricityLevelVOS" style="width: 100%"> <div class="bottom-box-item" style="margin-top: 8px" v-if="data">
<el-table-column prop="robotNo" label="车辆编号" /> <div class="bottom-box-item-table">
<el-table-column prop="doLock" label="车辆状态" > <div class="bottom-box-item-title">
<template #default="scope"> <div class="bottom-box-item-title-left"> 车辆信息 </div>
<span>{{ scope.row.doLock === 0 ? '正常' : '锁定' }}</span> <div class="bottom-box-item-title-right" @click="goCarBord"> 查看更多 </div>
</template> </div>
</el-table-column> <div>
<el-table-column prop="batSoc" label="电量百分比" > <el-table :data="data.robotElectricityLevelVOS" style="width: 100%">
<template #default="scope"> <el-table-column prop="robotNo" label="车辆编号" />
<div v-if="scope.row.batSoc === null"></div> <el-table-column prop="doLock" label="车辆状态">
<div class="battery-box-all" > <template #default="scope">
<span>{{ scope.row.doLock === 0 ? '正常' : '锁定' }}</span>
</template>
</el-table-column>
<el-table-column prop="batSoc" label="电量百分比">
<template #default="scope">
<div v-if="scope.row.batSoc === null"></div>
<div class="battery-box-all">
<div class="battery-box" v-if="scope.row.batSoc !== null"> <div class="battery-box" v-if="scope.row.batSoc !== null">
<img src="@/assets/imgs/allBoard/dianlianggreen.png" alt="" class="battery-box-img" v-if="scope.row.batSoc >=40"/> <img
<img src="@/assets/imgs/allBoard/dianliangyellow.png" alt="" class="battery-box-img" v-if="(scope.row.batSoc < 40) && (scope.row.batSoc >=20)"/> src="@/assets/imgs/allBoard/dianlianggreen.png"
<img src="@/assets/imgs/allBoard/dianliangred.png" alt="" class="battery-box-img" v-if="scope.row.batSoc < 20"/> alt=""
<div class="battery-box-inner"> class="battery-box-img"
<div class="battery-box-inner-inner " :class="scope.row.batSoc >=40 ? 'green-bg' : scope.row.batSoc < 40 && scope.row.batSoc >=20 ? 'yellow-bg' : 'red-bg'" :style="{width: scope.row.batSoc+'%'}"> v-if="scope.row.batSoc >= 40"
/>
</div> <img
</div> src="@/assets/imgs/allBoard/dianliangyellow.png"
alt=""
class="battery-box-img"
v-if="scope.row.batSoc < 40 && scope.row.batSoc >= 20"
/>
<img
src="@/assets/imgs/allBoard/dianliangred.png"
alt=""
class="battery-box-img"
v-if="scope.row.batSoc < 20"
/>
<div class="battery-box-inner">
<div
class="battery-box-inner-inner"
:class="
scope.row.batSoc >= 40
? 'green-bg'
: scope.row.batSoc < 40 && scope.row.batSoc >= 20
? 'yellow-bg'
: 'red-bg'
"
:style="{ width: scope.row.batSoc + '%' }"
>
</div>
</div>
</div>
<span class="battery-box-text" v-if="scope.row.batSoc !== null"
>{{ scope.row.batSoc || 0 }}%</span
>
</div> </div>
<span class="battery-box-text" v-if="scope.row.batSoc !== null">{{scope.row.batSoc || 0}}%</span> </template>
</div> </el-table-column>
</template> </el-table>
</el-table-column> </div>
</el-table>
</div>
</div> </div>
</div> </div>
</el-col> </el-col>
@ -201,18 +307,30 @@
<script setup> <script setup>
import { ref, reactive, onMounted, onBeforeUnmount } from 'vue' import { ref, reactive, onMounted, onBeforeUnmount } from 'vue'
import { dateFormatter } from '@/utils/formatTime' import { dateFormatter } from '@/utils/formatTime'
import * as ChartsApi from '@/api/boardCharts' import * as ChartsApi from '@/api/boardCharts'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions,getDictOptions } from '@/utils/dict'
import indexPage from './indexPage.vue'
import * as MapApi from '@/api/map/map'
const router = useRouter() // const router = useRouter() //
const indexPageRef = ref(null)
const typeList = ref([])
defineOptions({ name: 'BoardAllBoard' }) defineOptions({ name: 'BoardAllBoard' })
const data = ref(null) const data = ref(null)
// //
const getAllData = async () => { const getAllData = async () => {
let datas = await ChartsApi.bulletinBoardGet() let datas = await ChartsApi.bulletinBoardGet()
console.log(datas) console.log(datas)
data.value = datas data.value = datas
}
const widthVal = ref(0)
const leftVal = ref(0)
const getWidth = (val) => {
widthVal.value = val
console.log(widthVal.value)
}
const getLeftPx = (val) => {
leftVal.value = val
} }
// //
const computedBackgroundColor = (warnLevel) => { const computedBackgroundColor = (warnLevel) => {
switch (warnLevel) { switch (warnLevel) {
@ -229,6 +347,7 @@ const computedBackgroundColor = (warnLevel) => {
} }
} }
//
const toManyTask = (type) => { const toManyTask = (type) => {
// console.log(getIntDictOptions(DICT_TYPE.ROBOT_TASK_STATUS)) // console.log(getIntDictOptions(DICT_TYPE.ROBOT_TASK_STATUS))
// return // return
@ -240,8 +359,185 @@ const toManyTask = (type) => {
}) })
} }
//
const toManyWarnMsg = () => {
router.push({
path: '/carError'
})
}
//
const goCarBord = () => {
router.push({
path: '/board/carBoard',
})
}
const list = ref([]) //
const mapValue = ref([]) //
const mapInfo = ref(null) //
//
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 (mapValue.value.length) {
handleChangeMap(mapValue.value)
} else {
mapValue.value = [list.value[0].value, list.value[0].children[0].value]
mapInfo.value = list.value[0].children[0]
? JSON.parse(JSON.stringify(list.value[0].children[0]))
: {
mapId: '',
floor: '',
area: ''
}
indexPageRef.value.getMapData(mapInfo.value)
}
}
//
const handleChangeMap = async (e) => {
mapInfo.value = findChildrenByValues(list.value, e)
? findChildrenByValues(list.value, e)
: {
mapId: '',
floor: '',
area: ''
}
console.log(mapInfo.value)
indexPageRef.value.getMapData(mapInfo.value)
}
//
const findChildrenByValues = (tree, values) => {
if (!tree || tree.length === 0) {
return null
}
function traverse(node) {
if (node.children) {
for (let child of node.children) {
if (values.includes(child.value)) {
return child
}
let result = traverse(child)
if (result) {
return result
}
}
}
return null
}
for (let root of tree) {
if (values.includes(root.value)) {
if (root.children) {
for (let child of root.children) {
if (values.includes(child.value)) {
return child
}
let result = traverse(child)
if (result) {
return result
}
}
}
}
let result = traverse(root)
if (result) {
return result
}
}
return null
}
const scrollContainer = ref(null)
let isDragging = false
let startX = 0
let scrollLeft = 0
const startDrag = (e) => {
isDragging = true
const rect = scrollContainer.value.getBoundingClientRect()
startX = e.clientX - rect.left
scrollLeft = scrollContainer.value.scrollLeft
scrollContainer.value.style.cursor = 'grabbing'
}
const onDrag = (e) => {
if (!isDragging) return
const rect = scrollContainer.value.getBoundingClientRect()
const mouseX = e.clientX - rect.left
const dragDistance = (mouseX - startX) * 2.5 //
scrollContainer.value.scrollLeft = scrollLeft - dragDistance
}
const endDrag = () => {
isDragging = false
scrollContainer.value.style.cursor = 'grab'
}
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 getLeft = () => {
let indexpageContainer = document.getElementsByClassName('map-box-allBoard')[0]
// console.log('', indexpageContainer.getBoundingClientRect().left)
leftVal.value = indexpageContainer.getBoundingClientRect().left
}
const getWidthPx = () => {
let width = getElementWidthByClass('map-box-allBoard')
// console.log("pppppppppppppp",width)
widthVal.value = width
}
const getLeftWidth = () => {
nextTick(() => {
getWidthPx()
getLeft()
})
}
//type
const filterTypeFun = (type, list) => {
if (list.length) {
let obj = list.find(item => {
return item.value == type
})
return obj == undefined ? type : obj.label
} else {
return type
}
}
onMounted(() => { onMounted(() => {
typeList.value = getDictOptions(DICT_TYPE.DEVICE_TYPE)
getAllData() getAllData()
getList()
getLeftWidth()
window.addEventListener('resize', getLeftWidth)
})
onBeforeUnmount(() => {
window.removeEventListener('resize', getLeftWidth)
}) })
</script> </script>
@ -304,58 +600,57 @@ onMounted(() => {
color: #1677ff; color: #1677ff;
cursor: pointer; cursor: pointer;
} }
.bottom-box-item-table{ .bottom-box-item-table {
width: 100%; width: 100%;
} }
.battery-box{ .battery-box {
width: 22px; width: 22px;
height: 11px; height: 11px;
position: relative; position: relative;
} }
.battery-box-img{ .battery-box-img {
width: 100%; width: 100%;
height: 100%; height: 100%;
vertical-align: top; vertical-align: top;
} }
.battery-box-inner{ .battery-box-inner {
width: 17px; width: 17px;
height: 8px; height: 8px;
position: absolute; position: absolute;
left: 50%; left: 50%;
top: 50%; top: 50%;
transform: translateX(-50%) translateY(-50%); transform: translateX(-50%) translateY(-50%);
} }
.battery-box-inner-inner{ .battery-box-inner-inner {
height: 100%; height: 100%;
} }
.green-bg{ .green-bg {
background: #52C41A; background: #52c41a;
} }
.yellow-bg{ .yellow-bg {
background: #F1CD0B; background: #f1cd0b;
} }
.red-bg{ .red-bg {
background: #C60606; background: #c60606;
} }
.battery-box-text{ .battery-box-text {
font-family: PingFangSC, PingFang SC; font-family:
font-weight: 400; PingFangSC,
font-size: 12px; PingFang SC;
color: #0D162A; font-weight: 400;
margin-left: 3px; font-size: 12px;
color: #0d162a;
margin-left: 3px;
} }
.battery-box-all{ .battery-box-all {
display: flex; display: flex;
align-items: center; align-items: center;
flex-wrap: wrap; flex-wrap: wrap;
} }
.warn-msg{ .warn-msg {
display: flex; display: flex;
} }
.warn-msg-color{ .warn-msg-color {
width: 8px; width: 8px;
height: 8px; height: 8px;
flex-shrink: 0; flex-shrink: 0;
@ -363,13 +658,99 @@ margin-left: 3px;
margin-right: 4px; margin-right: 4px;
margin-top: 4px; margin-top: 4px;
} }
.warn-msg-text{ .warn-msg-text {
flex: 1; flex: 1;
font-family:
PingFangSC,
PingFang SC;
font-weight: 400;
font-size: 12px;
color: #0d162a;
margin-top: -3px;
}
/* 容器样式 */
.scroll-container {
width: 100%;
height: 104px;
overflow: hidden;
position: relative;
cursor: grab;
user-select: none;
background: #fff;
margin-top: 10px;
}
/* 隐藏滚动条 */
.scroll-container::-webkit-scrollbar {
display: none;
}
/* 内容布局 */
.content {
display: inline-flex;
height: 100%;
padding: 20px 0;
white-space: nowrap;
}
/* 单个项目样式 */
.item {
display: inline-flex;
align-items: center;
padding: 17px 30px;
background: #fff;
transition: transform 0.2s;
border-right: 2px solid #e9e9e9;
}
/* .item:hover {
transform: translateY(-3px);
} */
/* 拖拽时状态 */
.scroll-container:active {
cursor: grabbing;
}
.scroll-container-item-left-title {
font-family:
PingFangSC,
PingFang SC;
font-weight: 500;
font-size: 14px;
color: #0d162a;
margin-bottom: 14px;
flex-shrink: 0;
}
.scroll-container-item-left-img {
width: 40px;
height: 40px;
vertical-align: top;
flex-shrink: 0;
}
.scroll-container-item-right {
flex-shrink: 0;
margin-left: 16px;
}
.noBoarder{
border-right: none;
}
.scroll-container-item-right-item{
display: flex;
flex-shrink: 0;
}
.scroll-container-item-right-item-title{
font-family: PingFangSC, PingFang SC; font-family: PingFangSC, PingFang SC;
font-weight: 400; font-weight: 400;
font-size: 12px; font-size: 12px;
color: #0D162A; color: #0D162A;
margin-top: -3px; flex-shrink: 0;
}
.scroll-container-item-right-item-num{
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 12px;
color: #0D162A;
flex-shrink: 0;
} }
</style> </style>

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@
}" }"
> >
<div <div
class="indexpage-container" :class="isDrag ? 'indexpage-container-active' : 'indexpage-container'"
v-if="imgUrl" v-if="imgUrl"
v-drag="isDrag" v-drag="isDrag"
:style="{ scale: 1, transformOrigin: '0 0' }" :style="{ scale: 1, transformOrigin: '0 0' }"
@ -31,11 +31,11 @@
:x2="(Number(item.endPointX) + Number(item.endWidth) / 2) * radio" :x2="(Number(item.endPointX) + Number(item.endWidth) / 2) * radio"
:y2="(Number(item.endPointY) + Number(item.endHigh) / 2) * radio" :y2="(Number(item.endPointY) + Number(item.endHigh) / 2) * radio"
stroke="#00329F" stroke="#00329F"
stroke-width="5" :stroke-width="5*radio"
/> />
</template> </template>
<template v-else> <template v-else>
<path :d="getCurvePath(item)" :stroke="'#00329F'" stroke-width="5" fill="none" /> <path :d="getCurvePath(item)" :stroke="'#00329F'" :stroke-width="5*radio" fill="none" />
</template> </template>
</svg> </svg>
</div> </div>
@ -239,8 +239,8 @@
placement="top" placement="top"
v-if="item.type == 1" v-if="item.type == 1"
> >
<div style="width: 10px; height: 10px; border-radius: 50%; background: #1890ff"> <div :style="{ width: 10 * radio + 'px', height: 10 * radio + 'px', background: '#1890ff'}">
</div> </div>
</el-tooltip> </el-tooltip>
</div> </div>
</div> </div>
@ -899,6 +899,14 @@ onUnmounted(() => {
width: 100%; width: 100%;
position: relative; position: relative;
} }
.indexpage-container-active{
width: 100%;
position: relative;
cursor: grab;
}
.indexpage-container-active:active {
cursor: grabbing;
}
.indexpage-container .affix-container-top { .indexpage-container .affix-container-top {
width: 100%; width: 100%;
height: 80px; height: 80px;