实时地图添加sortNum图例

This commit is contained in:
yyy 2025-04-22 09:20:31 +08:00
parent 4b11195c54
commit 830cf56ee0
6 changed files with 484 additions and 199 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 732 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 419 B

View File

@ -1,93 +1,213 @@
<template> <template>
<Dialog v-model="dialogVisible" :title="title" width="545px" style="padding: 0"> <Dialog v-model="dialogVisible" :title="title" width="1000" style="padding: 0">
<el-form :model="formData" label-width="auto" ref="formRef" :rules="formRules"> <el-form :model="formData" label-width="110" ref="formRef" :rules="formRules">
<el-form-item label="车辆类型" prop="robotModelId" required> <el-row :gutter="20">
<el-select <el-col :span="12">
v-model="formData.robotModelId" <el-form-item label="车辆类型" prop="robotModelId" required>
placeholder="请选择车辆类型" <el-select
required v-model="formData.robotModelId"
:disabled="formData.id" placeholder="请选择车辆类型"
> required
<el-option :disabled="formData.id"
:label="item.robotModelNumber" >
:value="item.id" <el-option
v-for="item in carModelList" :label="item.robotModelNumber"
:key="item.id" :value="item.id"
/> v-for="item in carModelList"
</el-select> :key="item.id"
</el-form-item> />
<el-form-item required label="车辆编号" prop="robotNo"> </el-select>
<el-input </el-form-item>
v-model="formData.robotNo" </el-col>
:disabled="false" <el-col :span="12">
placeholder="请输入车辆编号" <el-form-item required label="车辆编号" prop="robotNo">
maxlength="10" <el-input
show-word-limit v-model="formData.robotNo"
/> :disabled="false"
</el-form-item> placeholder="请输入车辆编号"
<el-form-item required label="车辆状态" v-if="formData.id"> maxlength="10"
<el-select v-model="formData.robotTaskModel" placeholder="请选择车辆类型" required> show-word-limit
<el-option :label="'锁定'" :value="0" /> />
<el-option :label="'正常'" :value="1" /> </el-form-item>
</el-select> </el-col>
</el-form-item> </el-row>
<el-form-item required label="自动充电电量" prop="autoCharge"> <el-row :gutter="20">
<el-input-number <el-col :span="12">
style="width: 100px" <el-form-item required label="车辆状态">
v-model="formData.autoCharge" <el-select
:disabled="false" v-model="formData.robotTaskModel"
:controls="false" placeholder="请选择车辆类型"
:precision="0" required
:step="1" :disabled="!formData.id"
type="number" >
:min="0" <el-option :label="'锁定'" :value="0" />
:max="99" <el-option :label="'正常'" :value="1" />
placeholder="请输入自动充电电量" </el-select>
> </el-form-item>
<template #append>%</template> </el-col>
</el-input-number> <el-col :span="12">
<span style="margin-left: 5px">%</span> <el-form-item required label="自动充电电量" prop="autoCharge">
<span style="margin-left: 4px; color: #c60606; font-size: 12px" <el-input-number
>建议自动充电电量大于30%</span style="width: 130px"
> v-model="formData.autoCharge"
</el-form-item> :disabled="false"
<el-form-item required label="Mac地址" prop="macAddress"> :controls="false"
<el-input :precision="0"
v-model="formData.macAddress" :step="1"
:disabled="false" type="number"
placeholder="请输入Mac地址" :min="0"
maxlength="25" :max="99"
show-word-limit placeholder="请输入自动充电电量"
/> >
</el-form-item> <template #append>%</template>
<el-form-item required label="选择范围" placeholder="请选择范围"> </el-input-number>
<el-cascader <span style="margin-left: 5px">%</span>
:options="floorAreaList" <span style="margin-left: 4px; color: #c60606; font-size: 12px"
:props="props" >建议自动充电电量大于30%</span
clearable >
:collapse-tags="false" </el-form-item>
v-model="floorAreaJsonData" </el-col>
@change="floorAreaChange" </el-row>
/> <el-row :gutter="20">
</el-form-item> <el-col :span="12">
<el-form-item label="车辆ip" prop="robotIp"> <el-form-item required label="Mac地址" prop="macAddress">
<el-input <el-input
v-model="formData.robotIp" v-model="formData.macAddress"
:disabled="false" :disabled="false"
placeholder="请输入车辆ip" placeholder="请输入Mac地址"
maxlength="20" maxlength="25"
show-word-limit show-word-limit
/> />
</el-form-item> </el-form-item>
<el-form-item label="车辆端口" prop="robotPort"> </el-col>
<el-input <el-col :span="12">
v-model="formData.robotPort" <el-form-item required label="选择范围" placeholder="请选择范围">
:disabled="false" <el-cascader
placeholder="请输入车辆端口" :options="floorAreaList"
maxlength="6" :props="props"
show-word-limit clearable
/> :collapse-tags="false"
</el-form-item> v-model="floorAreaJsonData"
@change="floorAreaChange"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="车辆ip" prop="robotIp">
<el-input
v-model="formData.robotIp"
:disabled="false"
placeholder="请输入车辆ip"
maxlength="20"
show-word-limit
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="车辆端口" prop="robotPort">
<el-input
v-model="formData.robotPort"
:disabled="false"
placeholder="请输入车辆端口"
maxlength="6"
show-word-limit
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-form-item label="摄像头配置">
<el-button type="primary" @click="addCameraConfig">添加</el-button>
</el-form-item>
</el-row>
<template v-if="formData.cameraAddVOList && formData.cameraAddVOList.length > 0">
<div v-for="(item, index) in formData.cameraAddVOList" :key="index" class="camera-item">
<el-row :gutter="20">
<el-col :span="8">
<el-form-item
label="摄像头位置"
required
:prop="`cameraAddVOList[${index}].cameraPosition`"
:rules="{ required: true, message: '摄像头位置不能为空', trigger: 'change' }"
>
<el-select v-model="item.cameraPosition" placeholder="请选择" style="width: 100%">
<el-option
v-for="(type, typeIndex) in cameraType"
:key="typeIndex"
:label="type.label"
:value="type.value"
:disabled="cameraPositionDisable(type)"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item
label="摄像头IP"
:prop="`cameraAddVOList[${index}].cameraIp`"
:rules="{ required: true, message: '摄像头IP不能为空', trigger: 'change' }"
>
<el-input
v-model="item.cameraIp"
placeholder="请输入摄像头IP"
maxlength="20"
show-word-limit
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item
label="摄像头端口"
:prop="`cameraAddVOList[${index}].cameraPort`"
:rules="{ required: true, message: '摄像头端口不能为空', trigger: 'change' }"
>
<el-input
v-model="item.cameraPort"
placeholder="请输入摄像头端口"
maxlength="10"
show-word-limit
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item
label="摄像头账号"
:prop="`cameraAddVOList[${index}].cameraAccount`"
:rules="{ required: true, message: '摄像头账号不能为空', trigger: 'change' }"
>
<el-input
v-model="item.cameraAccount"
placeholder="请输入摄像头账号"
maxlength="30"
show-word-limit
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item
label="摄像头密码"
:prop="`cameraAddVOList[${index}].cameraPassword`"
:rules="{ required: true, message: '摄像头密码不能为空', trigger: 'change' }"
>
<el-input
v-model="item.cameraPassword"
placeholder="请输入摄像头密码"
maxlength="40"
show-word-limit
/>
</el-form-item>
</el-col>
<el-col :span="8">
<div class="delete-btn">
<el-button type="danger" @click="deleteItem(index)">删除</el-button>
</div>
</el-col>
</el-row>
</div>
</template>
</el-form> </el-form>
<template #footer> <template #footer>
@ -113,13 +233,60 @@ const formData = ref({
floorAreaJson: [], floorAreaJson: [],
autoCharge: undefined, autoCharge: undefined,
robotIp: undefined, robotIp: undefined,
robotPort: undefined robotPort: undefined,
cameraAddVOList: []
}) })
const carModelList = ref([]) const carModelList = ref([])
const floorAreaList = ref([]) const floorAreaList = ref([])
const props = { multiple: true } const props = { multiple: true }
const floorAreaJsonData = ref([]) const floorAreaJsonData = ref([])
//
const cameraType = ref([
{
label: '上',
value: 0
},
{
label: '左',
value: 1
},
{
label: '右',
value: 2
},
{
label: '下',
value: 3
},
{
label: '后',
value: 4
}
])
const addCameraConfig = () => {
if (formData.value.cameraAddVOList.length === cameraType.value.length) {
message.warning('已达到最大摄像头数量')
return
}
formData.value.cameraAddVOList.push({
cameraPosition: '', //(0:, 1:, 2:, 3:, 4:)
cameraIp: '', //IP--20
cameraPort: '', //--10
cameraAccount: '', //--30", example = "24863
cameraPassword: '' //--40
})
}
//
const deleteItem = (index) => {
formData.value.cameraAddVOList.splice(index, 1)
}
//
const cameraPositionDisable = (typeItem) => {
return formData.value.cameraAddVOList.find((item) => item.cameraPosition === typeItem.value)
}
const getCarModelList = async () => { const getCarModelList = async () => {
const res = await CarApi.robotGetAllModel() const res = await CarApi.robotGetAllModel()
carModelList.value = res carModelList.value = res
@ -181,8 +348,6 @@ const open = async (type, id) => {
} else { } else {
title.value = '新建' title.value = '新建'
} }
// getCanUseRobotList()
// getTaskNo()
} }
defineExpose({ open }) // open defineExpose({ open }) // open
@ -267,7 +432,10 @@ const resetForm = () => {
robotNo: undefined, // robotNo: undefined, //
macAddress: undefined, //mac macAddress: undefined, //mac
floorAreaJson: [], floorAreaJson: [],
autoCharge: undefined autoCharge: undefined,
robotIp: undefined,
robotPort: undefined,
cameraAddVOList: []
} }
floorAreaJsonData.value = [] floorAreaJsonData.value = []
formRef.value?.resetFields() formRef.value?.resetFields()
@ -335,4 +503,18 @@ const resetForm = () => {
opacity: 1; opacity: 1;
} }
} }
.camera-item {
box-shadow: rgba(0, 0, 0, 0.03) 0px 0px 0px 1px;
padding: 16px 8px 0 0;
border-radius: 6px;
margin-bottom: 20px;
border: 1px solid #efefef;
.delete-btn {
width: 100%;
display: flex;
justify-content: flex-end;
}
}
</style> </style>

View File

@ -215,7 +215,7 @@
</div> </div>
</div> </div>
<!-- 左下角图层 --> <!-- 左下角图层 -->
<div class="affix-container-left" :style="{ left: boxLeft + 'px' }" v-if="!isAllBoard"> <!-- <div class="affix-container-left" :style="{ left: boxLeft + 'px' }" v-if="!isAllBoard">
<div class="affix-container-left-box"> <div class="affix-container-left-box">
<div <div
class="affix-container-left-box-item-box" class="affix-container-left-box-item-box"
@ -271,7 +271,7 @@
/> />
</div> </div>
</div> </div>
</div> </div> -->
<!-- 右下角的功能 --> <!-- 右下角的功能 -->
<div class="affix-container-right" v-if="!isAllBoard"> <div class="affix-container-right" v-if="!isAllBoard">

View File

@ -161,54 +161,70 @@
</div> </div>
</div> </div>
</template> </template>
<div <div>
v-if="item.type === 1" <div
:style="{ v-if="item.type === 1 && legendObj.sortNumberShow && item.sortNum"
width: Number(item.locationWidePx) * Number(radio) + 'px', class="sort-num"
height: Number(item.locationDeepPx) * Number(radio) + 'px', :style="getSortNumStyle(item, index)"
backgroundColor: '#000', >
borderRadius: '50%' {{ item.sortNum }}
}" </div>
> <div
v-if="item.type !== 1 && legendObj.sortNumberShow && item.sortNum"
class="sort-num-location"
:style="getSortNumLocationStyle(item, index)"
>
{{ item.sortNum }}
</div>
<div
v-if="item.type === 1"
:style="{
width: Number(item.locationWidePx) * Number(radio) + 'px',
height: Number(item.locationDeepPx) * Number(radio) + 'px',
backgroundColor: '#000',
borderRadius: '50%'
}"
>
</div>
<!-- 库位点 -->
<img
v-else-if="item.type === 2"
src="https://api.znkjfw.com/admin-api/infra/file/4/get/库位库存_png_179_1744098544821.png"
:style="nodeStyle(item, index)"
@dblclick="storeClick(item)"
/>
<!-- 设备点 -->
<img
v-else-if="item.type === 3"
:src="
item.formattedData.mapImageUrl ||
'https://api.znkjfw.com/admin-api/infra/file/4/get/设备 (4)_png_179_1744102025024.png'
"
style="background: #fff"
:style="nodeStyle(item, index)"
/>
<!-- 停车点 -->
<img
v-else-if="item.type === 4"
src="https://api.znkjfw.com/admin-api/infra/file/4/get/停车位_png_179_1744098982069.png"
style="background: #fff"
:style="nodeStyle(item, index)"
/>
<!-- 区域变更点 -->
<img
v-else-if="item.type === 5"
src="https://api.znkjfw.com/admin-api/infra/file/4/get/区域变更 (1)_png_179_1744100605191.png"
style="background: #fff"
:style="nodeStyle(item, index)"
/>
<!-- 等待点 -->
<img
v-else-if="item.type === 6"
src="https://api.znkjfw.com/admin-api/infra/file/4/get/等待_png_179_1744102070670.png"
style="background: #fff"
:style="nodeStyle(item, index)"
/>
</div> </div>
<!-- 库位点 -->
<img
v-else-if="item.type === 2"
src="https://api.znkjfw.com/admin-api/infra/file/4/get/库位库存_png_179_1744098544821.png"
:style="nodeStyle(item, index)"
@dblclick="storeClick(item)"
/>
<!-- 设备点 -->
<img
v-else-if="item.type === 3"
:src="
item.formattedData.mapImageUrl ||
'https://api.znkjfw.com/admin-api/infra/file/4/get/设备 (4)_png_179_1744102025024.png'
"
style="background: #fff"
:style="nodeStyle(item, index)"
/>
<!-- 停车点 -->
<img
v-else-if="item.type === 4"
src="https://api.znkjfw.com/admin-api/infra/file/4/get/停车位_png_179_1744098982069.png"
style="background: #fff"
:style="nodeStyle(item, index)"
/>
<!-- 区域变更点 -->
<img
v-else-if="item.type === 5"
src="https://api.znkjfw.com/admin-api/infra/file/4/get/区域变更 (1)_png_179_1744100605191.png"
style="background: #fff"
:style="nodeStyle(item, index)"
/>
<!-- 等待点 -->
<img
v-else-if="item.type === 6"
src="https://api.znkjfw.com/admin-api/infra/file/4/get/等待_png_179_1744102070670.png"
style="background: #fff"
:style="nodeStyle(item, index)"
/>
</el-tooltip> </el-tooltip>
</div> </div>
</div> </div>
@ -222,57 +238,75 @@
<div class="affix-container-left" v-if="!isAllBoard"> <div class="affix-container-left" v-if="!isAllBoard">
<div class="affix-container-left-box"> <div class="affix-container-left-box">
<div <div
class="affix-container-left-box-item-box" class="legend-list"
:style="{ :style="{
height: legendObj.legendShow ? '84px' : '0', height: legendObj.legendShow ? '126px' : '0',
overflow: 'hidden', overflow: 'hidden',
transition: 'all 0.3s ease-in-out' transition: 'all 0.3s ease-in-out'
}" }"
> >
<div class="affix-container-left-box-item"> <div class="legend-item">
<div class="affix-container-left-box-item-left"> 行驶路线 </div> <div class="legend-item-left"> 行驶路线 </div>
<img <el-icon
src="@/assets/imgs/indexPage/yanjing_xianshi_o.png" class="legend-item-img"
class="affix-container-left-box-item-img" size="20"
v-if="legendObj.driveLineShow" v-if="legendObj.driveLineShow"
@click="changDriveLineShow" @click="changDriveLineShow"
/> color="#1677FF"
<img ><View
src="@/assets/imgs/indexPage/yanjing_yincang_o.png" /></el-icon>
class="affix-container-left-box-item-img" <el-icon
class="legend-item-img"
size="20"
v-if="!legendObj.driveLineShow" v-if="!legendObj.driveLineShow"
@click="changDriveLineShow" @click="changDriveLineShow"
/> color="#444444"
><Hide
/></el-icon>
</div> </div>
<div class="affix-container-left-box-item"> <div class="legend-item">
<div class="affix-container-left-box-item-left"> 车辆 </div> <div class="legend-item-left"> 车辆 </div>
<img <el-icon
src="@/assets/imgs/indexPage/yanjing_xianshi_o.png" class="legend-item-img"
class="affix-container-left-box-item-img" size="20"
v-if="legendObj.carShow" v-if="legendObj.carShow"
@click="changCarShow" @click="changCarShow"
/> color="#1677FF"
<img ><View
src="@/assets/imgs/indexPage/yanjing_yincang_o.png" /></el-icon>
class="affix-container-left-box-item-img" <el-icon
class="legend-item-img"
size="20"
v-if="!legendObj.carShow" v-if="!legendObj.carShow"
@click="changCarShow" @click="changCarShow"
/> color="#444444"
><Hide
/></el-icon>
</div>
<div class="legend-item">
<div class="legend-item-left"> 节点ID </div>
<el-icon
class="legend-item-img"
size="20"
v-if="legendObj.sortNumberShow"
@click="changeSortNumber"
color="#1677FF"
><View
/></el-icon>
<el-icon
class="legend-item-img"
size="20"
v-if="!legendObj.sortNumberShow"
@click="changeSortNumber"
color="#444444"
><Hide
/></el-icon>
</div> </div>
</div> </div>
<div <div class="legend-item-bottom" @click="legendObj.legendShow = !legendObj.legendShow">
class="affix-container-left-box-item-bottom" <div class="legend-item-bottom-left"> 图例 </div>
@click="legendObj.legendShow = !legendObj.legendShow" <el-icon size="22" v-if="legendObj.legendShow" color="#98A4BF"><CaretTop /></el-icon>
> <el-icon size="22" v-else color="#98A4BF"><CaretBottom /></el-icon>
<div class="affix-container-left-box-item-bottom-left"> 图例 </div>
<img
src="@/assets/imgs/indexPage/zhankai@2x.png"
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>
</div> </div>
@ -364,6 +398,61 @@ const nodeStyle = (item, index) => {
borderRadius: '3px' borderRadius: '3px'
} }
} }
//sortNum
const getSortNumStyle = (item, index) => {
let leftNum = 0
if (item.sortNum.toString().length === 1) {
leftNum = 3
} else if (item.sortNum.toString().length === 2) {
leftNum = 7
} else if (item.sortNum.toString().length === 3) {
leftNum = 10
} else if (item.sortNum.toString().length === 4) {
leftNum = 14
} else if (item.sortNum.toString().length === 5) {
leftNum = 18
} else if (item.sortNum.toString().length === 6) {
leftNum = 21
} else if (item.sortNum.toString().length === 7) {
leftNum = 25
} else if (item.sortNum.toString().length === 8) {
leftNum = 28
} else if (item.sortNum.toString().length === 9) {
leftNum = 31
}
return {
left: Number(item.locationWidePx) / 2 - leftNum + 'px',
top: 6 + 'px'
}
}
//sortNum
const getSortNumLocationStyle = (item, index) => {
let leftNum = 0
if (item.sortNum.toString().length === 1) {
leftNum = 3
} else if (item.sortNum.toString().length === 2) {
leftNum = 7
} else if (item.sortNum.toString().length === 3) {
leftNum = 10
} else if (item.sortNum.toString().length === 4) {
leftNum = 14
} else if (item.sortNum.toString().length === 5) {
leftNum = 18
} else if (item.sortNum.toString().length === 6) {
leftNum = 21
} else if (item.sortNum.toString().length === 7) {
leftNum = 25
} else if (item.sortNum.toString().length === 8) {
leftNum = 28
} else if (item.sortNum.toString().length === 9) {
leftNum = 31
}
return {
left: Number(item.locationWidePx) / 2 - leftNum + 'px',
top: Number(item.locationDeepPx) / 2 - 2 + 'px'
}
}
// //
const filterTypeFun = (deviceType) => { const filterTypeFun = (deviceType) => {
let list = getIntDictOptions(DICT_TYPE.DEVICE_TYPE) let list = getIntDictOptions(DICT_TYPE.DEVICE_TYPE)
@ -457,6 +546,7 @@ const changeSizeRaio = (type) => {
const legendObj = reactive({ const legendObj = reactive({
driveLineShow: true, driveLineShow: true,
carShow: true, carShow: true,
sortNumberShow: true,
legendShow: true legendShow: true
}) })
// //
@ -467,6 +557,11 @@ const changCarShow = () => {
const changDriveLineShow = () => { const changDriveLineShow = () => {
legendObj.driveLineShow = !legendObj.driveLineShow legendObj.driveLineShow = !legendObj.driveLineShow
} }
//sortNumber
const changeSortNumber = () => {
legendObj.sortNumberShow = !legendObj.sortNumberShow
}
const toggleFullScreen = () => { const toggleFullScreen = () => {
var elem = document.getElementById('indexpage-container') // var elem = document.getElementById('indexpage-container') //
if (!document.fullscreenElement) { if (!document.fullscreenElement) {
@ -1126,6 +1221,7 @@ onBeforeUnmount(() => {
position: absolute; position: absolute;
cursor: pointer; cursor: pointer;
} }
.affix-container-left { .affix-container-left {
position: fixed; position: fixed;
right: 30px; right: 30px;
@ -1135,20 +1231,23 @@ onBeforeUnmount(() => {
.affix-container-left-box { .affix-container-left-box {
width: 144px; width: 144px;
} }
.affix-container-left-box-item-box {
.legend-list {
width: 100%; width: 100%;
border-bottom: 1px solid #eeeeee; border-bottom: 1px solid #eeeeee;
background: #ffffff; background: #ffffff;
.legend-item {
width: 100%;
padding: 0 18px;
height: 42px;
display: flex;
align-items: center;
justify-content: space-between;
}
} }
.affix-container-left-box-item {
width: 100%; .legend-item-left {
padding: 0 18px;
height: 42px;
display: flex;
align-items: center;
justify-content: space-between;
}
.affix-container-left-box-item-left {
font-family: font-family:
PingFangSC, PingFangSC,
PingFang SC; PingFang SC;
@ -1156,13 +1255,10 @@ onBeforeUnmount(() => {
font-size: 14px; font-size: 14px;
color: rgba(0, 0, 0, 0.88); color: rgba(0, 0, 0, 0.88);
} }
.affix-container-left-box-item-img { .legend-item-img {
cursor: pointer; cursor: pointer;
flex-shrink: 0;
width: 20px;
height: 12px;
} }
.affix-container-left-box-item-bottom { .legend-item-bottom {
width: 100%; width: 100%;
height: 36px; height: 36px;
display: flex; display: flex;
@ -1171,13 +1267,7 @@ onBeforeUnmount(() => {
cursor: pointer; cursor: pointer;
background: #ffffff; background: #ffffff;
} }
.affix-container-left-box-item-bottom-img { .legend-item-bottom-left {
cursor: pointer;
flex-shrink: 0;
width: 12px;
height: 7px;
}
.affix-container-left-box-item-bottom-left {
cursor: pointer; cursor: pointer;
flex-shrink: 0; flex-shrink: 0;
font-family: font-family:
@ -1207,4 +1297,17 @@ onBeforeUnmount(() => {
margin-left: 8px; margin-left: 8px;
} }
} }
.sort-num {
position: absolute;
font-size: 0.75rem;
user-select: none;
color: #000;
}
.sort-num-location {
position: absolute;
font-size: 0.75rem;
user-select: none;
color: #000;
}
</style> </style>