编辑地图 搜索节点 节点滚动到中间

This commit is contained in:
yyy 2025-04-15 17:29:50 +08:00
parent ca0a561058
commit 9cec9dacb1
3 changed files with 405 additions and 351 deletions

View File

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

BIN
src/assets/search.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -1,303 +1,334 @@
<template> <template>
<div class="edit-map-page" @wheel="handleWheel"> <div class="edit-map-page" @wheel="handleWheel">
<div class="top-tool"> <div class="top-tool">
<div class="top-tool-list"> <div class="top-tool-list-container">
<div v-for="item in state.topToolList" :key="item.switchType" class="top-tool-item"> <div class="top-tool-list">
<el-popover <div v-for="item in state.topToolList" :key="item.switchType" class="top-tool-item">
placement="bottom" <el-popover
:width="170" placement="bottom"
trigger="click" :width="170"
v-if="item.switchType === 'move' || item.switchType === 'revolve'" trigger="click"
:disabled="state.currentItemIndex === -1" v-if="item.switchType === 'move' || item.switchType === 'revolve'"
> :disabled="state.currentItemIndex === -1"
<template #reference> >
<div <template #reference>
class="tool-item" <div
:class=" class="tool-item"
toolbarSwitchType === item.switchType :class="
? 'tool-active' toolbarSwitchType === item.switchType
: item.isActive ? 'tool-active'
? 'right-tool-active' : item.isActive
: '' ? 'right-tool-active'
" : ''
@click="toolbarClick(item)" "
> @click="toolbarClick(item)"
<Icon :icon="item.icon" :size="20" />
<div class="name"> {{ item.name }} </div>
</div>
</template>
<!-- 位置 -->
<el-form :model="state.moveForm" v-if="item.switchType === 'move'" class="mt-2">
<el-form-item label="X">
<el-input-number
v-model="state.moveForm.locationX"
:min="0"
:max="imgBgObj.width"
controls-position="right"
/>
</el-form-item>
<el-form-item label="Y">
<el-input-number
v-model="state.moveForm.locationY"
:min="0"
:max="imgBgObj.height"
controls-position="right"
/>
</el-form-item>
<div style="text-align: right">
<el-button
size="small"
style="width: 4rem; height: 1.875rem; background: #00329f"
color="#00329F"
@click="moveFormSubmit()"
>确认</el-button
> >
</div> <Icon :icon="item.icon" :size="20" />
</el-form> <div class="name"> {{ item.name }} </div>
<!-- 旋转 --> </div>
<el-form :model="state.rotationForm" v-if="item.switchType === 'revolve'" class="mt-2"> </template>
<el-form-item label="角度"> <!-- 位置 -->
<el-input-number <el-form :model="state.moveForm" v-if="item.switchType === 'move'" class="mt-2">
v-model="state.rotationForm.angle" <el-form-item label="X">
:min="0" <el-input-number
:max="259" v-model="state.moveForm.locationX"
controls-position="right" :min="0"
/> :max="imgBgObj.width"
</el-form-item> controls-position="right"
<div style="text-align: right">
<el-button
size="small"
style="width: 4rem; height: 1.875rem; background: #00329f"
color="#00329F"
@click="rotationFormSubmit"
>确认</el-button
>
</div>
</el-form>
</el-popover>
<el-popover
placement="bottom"
:width="170"
trigger="click"
v-else-if="item.switchType === 'grid'"
>
<template #reference>
<div
class="tool-item"
:class="
toolbarSwitchType === item.switchType
? 'tool-active'
: item.isActive
? 'right-tool-active'
: ''
"
@click="toolbarClick(item)"
>
<Icon :icon="item.icon" :size="20" />
<div class="name"> {{ item.name }} </div>
</div>
</template>
<!-- 网格 -->
<el-form :model="state.gridForm" v-if="item.switchType === 'grid'" class="mt-2">
<el-form-item label="网格">
<el-select v-model="state.gridForm.gridNum" placeholder="选择">
<el-option label="0.5米" value="10px" />
<el-option label="1米" value="20px" />
<el-option label="2米" value="40px" />
<el-option label="3米" value="60px" />
</el-select>
</el-form-item>
</el-form>
</el-popover>
<!-- 线库 -->
<el-popover
placement="bottom"
trigger="click"
v-else-if="item.switchType === 'lineLibrary'"
:popper-style="{ padding: '0rem' }"
>
<template #reference>
<div
class="tool-item"
:class="
toolbarSwitchType === 'lineLibrary' ||
toolbarSwitchType === 'createLineLibrary' ||
toolbarSwitchType === 'lineLibraryManage'
? 'tool-active'
: item.isActive
? 'right-tool-active'
: ''
"
@click="toolbarClick(item)"
>
<Icon :icon="item.icon" :size="20" />
<div class="name"> {{ item.name }} </div>
</div>
</template>
<div v-if="item.switchType === 'lineLibrary'" class="drop-down-menu">
<div
class="drop-down-menu-item"
:class="toolbarSwitchType === 'createLineLibrary' ? 'right-tool-active' : ''"
@click="toolbarClick({ switchType: 'createLineLibrary' })"
>生成线库</div
>
<div
class="drop-down-menu-item"
:class="toolbarSwitchType === 'lineLibraryManage' ? 'right-tool-active' : ''"
@click="toolbarClick({ switchType: 'lineLibraryManage' })"
>线库管理</div
>
</div>
</el-popover>
<!-- 区域 -->
<el-popover
placement="bottom"
trigger="click"
v-else-if="item.switchType === 'region'"
:popper-style="{ padding: '0rem' }"
>
<template #reference>
<div
class="tool-item"
:class="
toolbarSwitchType === 'region' ||
toolbarSwitchType === 'createRegion' ||
toolbarSwitchType === 'regionManage'
? 'tool-active'
: item.isActive
? 'right-tool-active'
: ''
"
@click="toolbarClick(item)"
>
<Icon :icon="item.icon" :size="20" />
<div class="name"> {{ item.name }} </div>
</div>
</template>
<div v-if="item.switchType === 'region'" class="drop-down-menu">
<div
class="drop-down-menu-item"
:class="toolbarSwitchType === 'createRegion' ? 'right-tool-active' : ''"
@click="toolbarClick({ switchType: 'createRegion' })"
>生成物料区域</div
>
<div
class="drop-down-menu-item"
:class="toolbarSwitchType === 'regionManage' ? 'right-tool-active' : ''"
@click="toolbarClick({ switchType: 'regionManage' })"
>物料区域管理</div
>
</div>
</el-popover>
<!-- 标记 -->
<el-popover
placement="bottom"
trigger="click"
v-else-if="item.switchType === 'marker'"
width="220"
:visible="state.popoverVisible"
>
<template #reference>
<div
class="tool-item"
:class="
toolbarSwitchType === item.switchType
? 'tool-active'
: item.isActive
? 'right-tool-active'
: ''
"
@click="toolbarClick(item)"
>
<Icon :icon="item.icon" :size="20" />
<div class="name"> {{ item.name }} </div>
</div>
</template>
<!-- 位置 -->
<el-form :model="state.markForm" class="mt-2" label-width="68">
<el-form-item label="标记车辆">
<el-select
v-model="state.markForm.macAddress"
placeholder="请选择"
@change="macAddressChange"
>
<el-option
v-for="item in state.mapMarkCarList"
:key="item.id"
:label="item.robotNo"
:value="item.macAddress"
/> />
</el-select> </el-form-item>
</el-form-item> <el-form-item label="Y">
<el-form-item label="标记属性" v-if="state.markForm.markProperty"> <el-input-number
<el-input v-model="state.markForm.markProperty" style="width: 15rem" disabled /> v-model="state.moveForm.locationY"
</el-form-item> :min="0"
<el-form-item label="原节点" v-if="state.markForm.originalNode"> :max="imgBgObj.height"
<el-input v-model="state.markForm.originalNode" style="width: 15rem" disabled /> controls-position="right"
</el-form-item> />
<div style="text-align: right"> </el-form-item>
<el-button <div style="text-align: right">
size="small" <el-button
style="width: 4rem; height: 1.875rem; background: #efefef" size="small"
@click="markFormCancel()" style="width: 4rem; height: 1.875rem; background: #00329f"
>取消</el-button color="#00329F"
@click="moveFormSubmit()"
>确认</el-button
>
</div>
</el-form>
<!-- 旋转 -->
<el-form
:model="state.rotationForm"
v-if="item.switchType === 'revolve'"
class="mt-2"
>
<el-form-item label="角度">
<el-input-number
v-model="state.rotationForm.angle"
:min="0"
:max="259"
controls-position="right"
/>
</el-form-item>
<div style="text-align: right">
<el-button
size="small"
style="width: 4rem; height: 1.875rem; background: #00329f"
color="#00329F"
@click="rotationFormSubmit"
>确认</el-button
>
</div>
</el-form>
</el-popover>
<el-popover
placement="bottom"
:width="170"
trigger="click"
v-else-if="item.switchType === 'grid'"
>
<template #reference>
<div
class="tool-item"
:class="
toolbarSwitchType === item.switchType
? 'tool-active'
: item.isActive
? 'right-tool-active'
: ''
"
@click="toolbarClick(item)"
> >
<el-button <Icon :icon="item.icon" :size="20" />
size="small" <div class="name"> {{ item.name }} </div>
style="width: 4rem; height: 1.875rem; background: #00329f" </div>
color="#00329F" </template>
@click="markFormSubmit()" <!-- 网格 -->
>确认</el-button <el-form :model="state.gridForm" v-if="item.switchType === 'grid'" class="mt-2">
<el-form-item label="网格">
<el-select v-model="state.gridForm.gridNum" placeholder="选择">
<el-option label="0.5米" value="10px" />
<el-option label="1米" value="20px" />
<el-option label="2米" value="40px" />
<el-option label="3米" value="60px" />
</el-select>
</el-form-item>
</el-form>
</el-popover>
<!-- 线库 -->
<el-popover
placement="bottom"
trigger="click"
v-else-if="item.switchType === 'lineLibrary'"
:popper-style="{ padding: '0rem' }"
>
<template #reference>
<div
class="tool-item"
:class="
toolbarSwitchType === 'lineLibrary' ||
toolbarSwitchType === 'createLineLibrary' ||
toolbarSwitchType === 'lineLibraryManage'
? 'tool-active'
: item.isActive
? 'right-tool-active'
: ''
"
@click="toolbarClick(item)"
>
<Icon :icon="item.icon" :size="20" />
<div class="name"> {{ item.name }} </div>
</div>
</template>
<div v-if="item.switchType === 'lineLibrary'" class="drop-down-menu">
<div
class="drop-down-menu-item"
:class="toolbarSwitchType === 'createLineLibrary' ? 'right-tool-active' : ''"
@click="toolbarClick({ switchType: 'createLineLibrary' })"
>生成线库</div
>
<div
class="drop-down-menu-item"
:class="toolbarSwitchType === 'lineLibraryManage' ? 'right-tool-active' : ''"
@click="toolbarClick({ switchType: 'lineLibraryManage' })"
>线库管理</div
> >
</div> </div>
</el-form> </el-popover>
</el-popover> <!-- 区域 -->
<div <el-popover
v-else placement="bottom"
class="tool-item" trigger="click"
:class=" v-else-if="item.switchType === 'region'"
toolbarSwitchType === item.switchType :popper-style="{ padding: '0rem' }"
? 'tool-active' >
: item.isActive <template #reference>
? 'right-tool-active' <div
: '' class="tool-item"
" :class="
@click="toolbarClick(item)" toolbarSwitchType === 'region' ||
> toolbarSwitchType === 'createRegion' ||
<Icon :icon="item.icon" :size="20" /> toolbarSwitchType === 'regionManage'
<div class="name"> {{ item.name }} </div> ? 'tool-active'
</div> : item.isActive
<!-- 分隔线 --> ? 'right-tool-active'
<div : ''
class="line" "
v-if=" @click="toolbarClick(item)"
item.switchType === 'save' || >
item.switchType === 'delete' || <Icon :icon="item.icon" :size="20" />
item.switchType === 'grid' || <div class="name"> {{ item.name }} </div>
(item.switchType === 'next' && </div>
</template>
<div v-if="item.switchType === 'region'" class="drop-down-menu">
<div
class="drop-down-menu-item"
:class="toolbarSwitchType === 'createRegion' ? 'right-tool-active' : ''"
@click="toolbarClick({ switchType: 'createRegion' })"
>生成物料区域</div
>
<div
class="drop-down-menu-item"
:class="toolbarSwitchType === 'regionManage' ? 'right-tool-active' : ''"
@click="toolbarClick({ switchType: 'regionManage' })"
>物料区域管理</div
>
</div>
</el-popover>
<!-- 标记 -->
<el-popover
placement="bottom"
trigger="click"
v-else-if="item.switchType === 'marker'"
width="220"
:visible="state.popoverVisible"
>
<template #reference>
<div
class="tool-item"
:class="
toolbarSwitchType === item.switchType
? 'tool-active'
: item.isActive
? 'right-tool-active'
: ''
"
@click="toolbarClick(item)"
>
<Icon :icon="item.icon" :size="20" />
<div class="name"> {{ item.name }} </div>
</div>
</template>
<!-- 位置 -->
<el-form :model="state.markForm" class="mt-2" label-width="68">
<el-form-item label="标记车辆">
<el-select
v-model="state.markForm.macAddress"
placeholder="请选择"
@change="macAddressChange"
>
<el-option
v-for="item in state.mapMarkCarList"
:key="item.id"
:label="item.robotNo"
:value="item.macAddress"
/>
</el-select>
</el-form-item>
<el-form-item label="标记属性" v-if="state.markForm.markProperty">
<el-input v-model="state.markForm.markProperty" style="width: 15rem" disabled />
</el-form-item>
<el-form-item label="原节点" v-if="state.markForm.originalNode">
<el-input v-model="state.markForm.originalNode" style="width: 15rem" disabled />
</el-form-item>
<div style="text-align: right">
<el-button
size="small"
style="width: 4rem; height: 1.875rem; background: #efefef"
@click="markFormCancel()"
>取消</el-button
>
<el-button
size="small"
style="width: 4rem; height: 1.875rem; background: #00329f"
color="#00329F"
@click="markFormSubmit()"
>确认</el-button
>
</div>
</el-form>
</el-popover>
<div
v-else
class="tool-item"
:class="
toolbarSwitchType === item.switchType
? 'tool-active'
: item.isActive
? 'right-tool-active'
: ''
"
@click="toolbarClick(item)"
>
<Icon :icon="item.icon" :size="20" />
<div class="name"> {{ item.name }} </div>
</div>
<!-- 分隔线 -->
<div
class="line"
v-if="
item.switchType === 'save' ||
item.switchType === 'delete' ||
item.switchType === 'grid' ||
(item.switchType === 'next' &&
(toolbarSwitchType === 'createLineLibrary' ||
toolbarSwitchType === 'createRegion' ||
toolbarSwitchType === 'drawRoute' ||
toolbarSwitchType === 'generateLine' ||
toolbarSwitchType === 'bulkDelete'))
"
></div>
<el-button
v-if="
item.switchType === 'next' &&
(toolbarSwitchType === 'createLineLibrary' || (toolbarSwitchType === 'createLineLibrary' ||
toolbarSwitchType === 'createRegion' || toolbarSwitchType === 'createRegion' ||
toolbarSwitchType === 'drawRoute' || toolbarSwitchType === 'drawRoute' ||
toolbarSwitchType === 'generateLine' || toolbarSwitchType === 'generateLine' ||
toolbarSwitchType === 'bulkDelete')) toolbarSwitchType === 'bulkDelete')
" "
></div> type="danger"
<el-button class="selection-area-btn"
v-if=" @click="clickDrawSelectionArea"
item.switchType === 'next' && >确定</el-button
(toolbarSwitchType === 'createLineLibrary' || >
toolbarSwitchType === 'createRegion' || </div>
toolbarSwitchType === 'drawRoute' || </div>
toolbarSwitchType === 'generateLine' || <div class="search-select">
toolbarSwitchType === 'bulkDelete') <img
" class="search-icon"
type="danger" v-if="!state.isSearchSelectVisible"
class="selection-area-btn" @click="toggleSelect"
@click="clickDrawSelectionArea" src="@/assets/search.png"
>确定</el-button />
<el-select
class="!w-160px"
v-else
v-model="state.searchSelectedOption"
@blur="toggleSelect"
placeholder="请选择"
@change="searchSelectChange"
filterable
> >
<el-option
v-for="item in state.haveSortNumMapPointInfo"
:key="item.sortNum"
:label="item.sortNum"
:value="item.sortNum"
/>
</el-select>
</div> </div>
</div> </div>
<div class="right-tool-list" v-if="state.isShowToolbar"> <div class="right-tool-list" v-if="state.isShowToolbar">
<div <div
v-for="item in state.rightToolList" v-for="item in state.rightToolList"
@ -340,6 +371,7 @@
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-popover> </el-popover>
<div v-else> <div v-else>
<Icon :icon="item.icon" :size="20" /> <Icon :icon="item.icon" :size="20" />
<div class="name"> {{ item.name }} </div> <div class="name"> {{ item.name }} </div>
@ -350,7 +382,7 @@
<div <div
class="map-container" class="map-container"
ref="mapContainer" ref="mapContainerRef"
:style="{ cursor: state.cursorStyle }" :style="{ cursor: state.cursorStyle }"
v-if="imgBgObj.width && imgBgObj.height" v-if="imgBgObj.width && imgBgObj.height"
> >
@ -840,6 +872,7 @@ const equipmentToolDialogRef = ref() //设备弹窗
const textFormToolDialogRef = ref() // const textFormToolDialogRef = ref() //
const editMapRouteDialogRef = ref() //线 const editMapRouteDialogRef = ref() //线
const mapBackgroundRef = ref() const mapBackgroundRef = ref()
const mapContainerRef = ref()
const inputBoxRef = ref() // const inputBoxRef = ref() //
const message = useMessage() // const message = useMessage() //
@ -1522,7 +1555,10 @@ const state = reactive({
}, },
routeWidthForm: { routeWidthForm: {
routeWidth: 3 routeWidth: 3
} },
isSearchSelectVisible: false, //
searchSelectedOption: '',
haveSortNumMapPointInfo: []
}) })
// //
const gradientBackground = computed(() => { const gradientBackground = computed(() => {
@ -2490,36 +2526,6 @@ const mapPointsToLine = (points, startPointId, endPointId) => {
locationY: newY locationY: newY
} }
}) })
// const dx = startPoint.locationX - endPoint.locationX
// const dy = startPoint.locationY - endPoint.locationY
// // 线
// if (dx === 0) {
// return points.map((point) => {
// if (point === endPoint || point === startPoint) {
// return point
// }
// return {
// ...point,
// locationX: endPoint.locationX
// }
// })
// }
// const slope = dy / dx
// const intercept = endPoint.locationY - slope * endPoint.locationX
// return points.map((point) => {
// if (point === endPoint || point === startPoint) {
// return point
// }
// const newY = slope * point.locationX + intercept
// return {
// ...point,
// locationY: newY
// }
// })
} }
//线 //线
@ -3088,6 +3094,11 @@ const getAllNodeList = async () => {
state.allMapPointInfo.push(item) state.allMapPointInfo.push(item)
}) })
//sortNumber
state.haveSortNumMapPointInfo = state.allMapPointInfo.filter((item) => {
return item.sortNum
})
} }
//线 //线
const getAllMapRoute = async () => { const getAllMapRoute = async () => {
@ -3384,6 +3395,37 @@ const computedCurveTextY = (item) => {
4 4
) )
} }
//
const toggleSelect = () => {
state.isSearchSelectVisible = !state.isSearchSelectVisible
state.searchSelectedOption = ''
}
const searchSelectChange = (sortNum) => {
const currentIndex = state.allMapPointInfo.findIndex((item) => item.sortNum === sortNum)
const currentItem = state.allMapPointInfo.find((item) => item.sortNum === sortNum)
//
const rect = mapContainerRef.value
const maxScrollLeft = rect.scrollWidth - rect.clientWidth
const maxScrollTop = rect.scrollHeight - rect.clientHeight
const scrollToX = Math.max(
0,
Math.min(currentItem.locationX - rect.clientWidth / 2, maxScrollLeft)
)
const scrollToY = Math.max(
0,
Math.min(currentItem.locationY - rect.clientHeight / 2, maxScrollTop)
)
rect.scrollTo({
left: scrollToX,
top: scrollToY,
behavior: 'smooth'
})
state.currentItemIndex = currentIndex
}
document.onmousedown = function (e) { document.onmousedown = function (e) {
// //
if (e.button == 2) { if (e.button == 2) {
@ -3498,15 +3540,12 @@ onUnmounted(() => {
position: relative; position: relative;
width: 100%; width: 100%;
overflow: auto; overflow: auto;
// height: 85vh;
height: calc(100vh - 120px); height: calc(100vh - 120px);
.map-bg { .map-bg {
background-size: contain; background-size: contain;
background-repeat: no-repeat; background-repeat: no-repeat;
position: absolute; position: absolute;
// top: 1.125rem;
// left: 1.125rem;
top: 0; top: 0;
left: 0; left: 0;
@ -3519,48 +3558,56 @@ onUnmounted(() => {
} }
.top-tool { .top-tool {
margin-bottom: 0.125rem; margin-bottom: 2px;
.top-tool-list {
.top-tool-list-container {
display: flex; display: flex;
align-items: center; align-items: center;
text-align: center; justify-content: space-between;
background-color: #fff; background-color: #fff;
padding: 0 0.75rem;
height: 60px;
box-shadow: rgba(0, 0, 0, 0.06) 0rem 0.125rem 0.1875rem; box-shadow: rgba(0, 0, 0, 0.06) 0rem 0.125rem 0.1875rem;
height: 60px;
padding: 0 0.75rem;
.top-tool-item { .top-tool-list {
display: flex; display: flex;
align-items: center; align-items: center;
text-align: center;
position: relative;
.tool-item { .top-tool-item {
cursor: pointer;
width: 44px;
height: 60px;
display: flex; display: flex;
flex-direction: column;
align-items: center; align-items: center;
justify-content: center;
.name { .tool-item {
cursor: pointer; cursor: pointer;
font-family: width: 44px;
PingFangSC, height: 60px;
PingFang SC; display: flex;
font-weight: 400; flex-direction: column;
font-size: 13px; align-items: center;
color: #0d162a; justify-content: center;
line-height: 1.25rem;
text-align: center; .name {
font-style: normal; cursor: pointer;
margin-top: 0.25rem; font-family:
PingFangSC,
PingFang SC;
font-weight: 400;
font-size: 13px;
color: #0d162a;
line-height: 1.25rem;
text-align: center;
font-style: normal;
margin-top: 0.25rem;
}
}
.line {
margin: 0 4px;
width: 0.0625rem;
height: 2.9375rem;
border: 0.0625rem solid #cccccc;
} }
}
.line {
margin: 0 4px;
width: 0.0625rem;
height: 2.9375rem;
border: 0.0625rem solid #cccccc;
} }
} }
} }
@ -3673,4 +3720,11 @@ onUnmounted(() => {
font-size: 0.875rem; font-size: 0.875rem;
user-select: none; user-select: none;
} }
.search-select {
.search-icon {
widows: 20px;
height: 20px;
}
}
</style> </style>