实时地图添加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>
<Dialog v-model="dialogVisible" :title="title" width="545px" style="padding: 0">
<el-form :model="formData" label-width="auto" ref="formRef" :rules="formRules">
<el-form-item label="车辆类型" prop="robotModelId" required>
<el-select
v-model="formData.robotModelId"
placeholder="请选择车辆类型"
required
:disabled="formData.id"
>
<el-option
:label="item.robotModelNumber"
:value="item.id"
v-for="item in carModelList"
:key="item.id"
/>
</el-select>
</el-form-item>
<el-form-item required label="车辆编号" prop="robotNo">
<el-input
v-model="formData.robotNo"
:disabled="false"
placeholder="请输入车辆编号"
maxlength="10"
show-word-limit
/>
</el-form-item>
<el-form-item required label="车辆状态" v-if="formData.id">
<el-select v-model="formData.robotTaskModel" placeholder="请选择车辆类型" required>
<el-option :label="'锁定'" :value="0" />
<el-option :label="'正常'" :value="1" />
</el-select>
</el-form-item>
<el-form-item required label="自动充电电量" prop="autoCharge">
<el-input-number
style="width: 100px"
v-model="formData.autoCharge"
:disabled="false"
:controls="false"
:precision="0"
:step="1"
type="number"
:min="0"
:max="99"
placeholder="请输入自动充电电量"
>
<template #append>%</template>
</el-input-number>
<span style="margin-left: 5px">%</span>
<span style="margin-left: 4px; color: #c60606; font-size: 12px"
>建议自动充电电量大于30%</span
>
</el-form-item>
<el-form-item required label="Mac地址" prop="macAddress">
<el-input
v-model="formData.macAddress"
:disabled="false"
placeholder="请输入Mac地址"
maxlength="25"
show-word-limit
/>
</el-form-item>
<el-form-item required label="选择范围" placeholder="请选择范围">
<el-cascader
:options="floorAreaList"
:props="props"
clearable
:collapse-tags="false"
v-model="floorAreaJsonData"
@change="floorAreaChange"
/>
</el-form-item>
<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-form-item label="车辆端口" prop="robotPort">
<el-input
v-model="formData.robotPort"
:disabled="false"
placeholder="请输入车辆端口"
maxlength="6"
show-word-limit
/>
</el-form-item>
<Dialog v-model="dialogVisible" :title="title" width="1000" style="padding: 0">
<el-form :model="formData" label-width="110" ref="formRef" :rules="formRules">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="车辆类型" prop="robotModelId" required>
<el-select
v-model="formData.robotModelId"
placeholder="请选择车辆类型"
required
:disabled="formData.id"
>
<el-option
:label="item.robotModelNumber"
:value="item.id"
v-for="item in carModelList"
:key="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item required label="车辆编号" prop="robotNo">
<el-input
v-model="formData.robotNo"
:disabled="false"
placeholder="请输入车辆编号"
maxlength="10"
show-word-limit
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item required label="车辆状态">
<el-select
v-model="formData.robotTaskModel"
placeholder="请选择车辆类型"
required
:disabled="!formData.id"
>
<el-option :label="'锁定'" :value="0" />
<el-option :label="'正常'" :value="1" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item required label="自动充电电量" prop="autoCharge">
<el-input-number
style="width: 130px"
v-model="formData.autoCharge"
:disabled="false"
:controls="false"
:precision="0"
:step="1"
type="number"
:min="0"
:max="99"
placeholder="请输入自动充电电量"
>
<template #append>%</template>
</el-input-number>
<span style="margin-left: 5px">%</span>
<span style="margin-left: 4px; color: #c60606; font-size: 12px"
>建议自动充电电量大于30%</span
>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item required label="Mac地址" prop="macAddress">
<el-input
v-model="formData.macAddress"
:disabled="false"
placeholder="请输入Mac地址"
maxlength="25"
show-word-limit
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item required label="选择范围" placeholder="请选择范围">
<el-cascader
:options="floorAreaList"
:props="props"
clearable
:collapse-tags="false"
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>
<template #footer>
@ -113,13 +233,60 @@ const formData = ref({
floorAreaJson: [],
autoCharge: undefined,
robotIp: undefined,
robotPort: undefined
robotPort: undefined,
cameraAddVOList: []
})
const carModelList = ref([])
const floorAreaList = ref([])
const props = { multiple: true }
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 res = await CarApi.robotGetAllModel()
carModelList.value = res
@ -181,8 +348,6 @@ const open = async (type, id) => {
} else {
title.value = '新建'
}
// getCanUseRobotList()
// getTaskNo()
}
defineExpose({ open }) // open
@ -267,7 +432,10 @@ const resetForm = () => {
robotNo: undefined, //
macAddress: undefined, //mac
floorAreaJson: [],
autoCharge: undefined
autoCharge: undefined,
robotIp: undefined,
robotPort: undefined,
cameraAddVOList: []
}
floorAreaJsonData.value = []
formRef.value?.resetFields()
@ -335,4 +503,18 @@ const resetForm = () => {
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>

View File

@ -215,7 +215,7 @@
</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-item-box"
@ -271,7 +271,7 @@
/>
</div>
</div>
</div>
</div> -->
<!-- 右下角的功能 -->
<div class="affix-container-right" v-if="!isAllBoard">

View File

@ -161,54 +161,70 @@
</div>
</div>
</template>
<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>
<div
v-if="item.type === 1 && legendObj.sortNumberShow && item.sortNum"
class="sort-num"
:style="getSortNumStyle(item, index)"
>
{{ 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>
<!-- 库位点 -->
<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>
</div>
</div>
@ -222,57 +238,75 @@
<div class="affix-container-left" v-if="!isAllBoard">
<div class="affix-container-left-box">
<div
class="affix-container-left-box-item-box"
class="legend-list"
:style="{
height: legendObj.legendShow ? '84px' : '0',
height: legendObj.legendShow ? '126px' : '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"
class="affix-container-left-box-item-img"
<div class="legend-item">
<div class="legend-item-left"> 行驶路线 </div>
<el-icon
class="legend-item-img"
size="20"
v-if="legendObj.driveLineShow"
@click="changDriveLineShow"
/>
<img
src="@/assets/imgs/indexPage/yanjing_yincang_o.png"
class="affix-container-left-box-item-img"
color="#1677FF"
><View
/></el-icon>
<el-icon
class="legend-item-img"
size="20"
v-if="!legendObj.driveLineShow"
@click="changDriveLineShow"
/>
color="#444444"
><Hide
/></el-icon>
</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"
class="affix-container-left-box-item-img"
<div class="legend-item">
<div class="legend-item-left"> 车辆 </div>
<el-icon
class="legend-item-img"
size="20"
v-if="legendObj.carShow"
@click="changCarShow"
/>
<img
src="@/assets/imgs/indexPage/yanjing_yincang_o.png"
class="affix-container-left-box-item-img"
color="#1677FF"
><View
/></el-icon>
<el-icon
class="legend-item-img"
size="20"
v-if="!legendObj.carShow"
@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
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"
class="affix-container-left-box-item-bottom-img"
:style="{
transform: legendObj.legendShow ? 'rotate(180deg)' : 'rotate(0deg)',
transition: 'all 0.3s ease-in-out'
}"
/>
<div class="legend-item-bottom" @click="legendObj.legendShow = !legendObj.legendShow">
<div class="legend-item-bottom-left"> 图例 </div>
<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>
</div>
</div>
@ -364,6 +398,61 @@ const nodeStyle = (item, index) => {
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) => {
let list = getIntDictOptions(DICT_TYPE.DEVICE_TYPE)
@ -457,6 +546,7 @@ const changeSizeRaio = (type) => {
const legendObj = reactive({
driveLineShow: true,
carShow: true,
sortNumberShow: true,
legendShow: true
})
//
@ -467,6 +557,11 @@ const changCarShow = () => {
const changDriveLineShow = () => {
legendObj.driveLineShow = !legendObj.driveLineShow
}
//sortNumber
const changeSortNumber = () => {
legendObj.sortNumberShow = !legendObj.sortNumberShow
}
const toggleFullScreen = () => {
var elem = document.getElementById('indexpage-container') //
if (!document.fullscreenElement) {
@ -1126,6 +1221,7 @@ onBeforeUnmount(() => {
position: absolute;
cursor: pointer;
}
.affix-container-left {
position: fixed;
right: 30px;
@ -1135,20 +1231,23 @@ onBeforeUnmount(() => {
.affix-container-left-box {
width: 144px;
}
.affix-container-left-box-item-box {
.legend-list {
width: 100%;
border-bottom: 1px solid #eeeeee;
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%;
padding: 0 18px;
height: 42px;
display: flex;
align-items: center;
justify-content: space-between;
}
.affix-container-left-box-item-left {
.legend-item-left {
font-family:
PingFangSC,
PingFang SC;
@ -1156,13 +1255,10 @@ onBeforeUnmount(() => {
font-size: 14px;
color: rgba(0, 0, 0, 0.88);
}
.affix-container-left-box-item-img {
.legend-item-img {
cursor: pointer;
flex-shrink: 0;
width: 20px;
height: 12px;
}
.affix-container-left-box-item-bottom {
.legend-item-bottom {
width: 100%;
height: 36px;
display: flex;
@ -1171,13 +1267,7 @@ onBeforeUnmount(() => {
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 {
.legend-item-bottom-left {
cursor: pointer;
flex-shrink: 0;
font-family:
@ -1207,4 +1297,17 @@ onBeforeUnmount(() => {
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>