3778 lines
122 KiB
Vue
3778 lines
122 KiB
Vue
<template>
|
||
<div class="edit-map-page" @wheel="handleWheel">
|
||
<div class="top-tool">
|
||
<div class="top-tool-list-container">
|
||
<div class="top-tool-list">
|
||
<div v-for="item in state.topToolList" :key="item.switchType" class="top-tool-item">
|
||
<el-popover
|
||
placement="bottom"
|
||
:width="170"
|
||
trigger="click"
|
||
v-if="item.switchType === 'move' || item.switchType === 'revolve'"
|
||
:disabled="state.currentItemIndex === -1"
|
||
>
|
||
<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.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>
|
||
</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)"
|
||
>
|
||
<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 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 === 'createRegion' ||
|
||
toolbarSwitchType === 'drawRoute' ||
|
||
toolbarSwitchType === 'generateLine' ||
|
||
toolbarSwitchType === 'bulkDelete')
|
||
"
|
||
type="danger"
|
||
class="selection-area-btn"
|
||
@click="clickDrawSelectionArea"
|
||
>确定</el-button
|
||
>
|
||
</div>
|
||
</div>
|
||
<div class="search-select">
|
||
<img
|
||
class="search-icon"
|
||
v-if="!state.isSearchSelectVisible"
|
||
@click="toggleSelect"
|
||
src="@/assets/search.png"
|
||
/>
|
||
<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 class="right-tool-list" v-if="state.isShowToolbar">
|
||
<div
|
||
v-for="item in state.rightToolList"
|
||
:key="item.switchType"
|
||
class="tool-item"
|
||
:class="
|
||
toolbarSwitchType === item.switchType
|
||
? 'tool-active'
|
||
: item.isActive
|
||
? 'right-tool-active'
|
||
: ''
|
||
"
|
||
@click="toolbarClick(item)"
|
||
>
|
||
<el-popover
|
||
placement="bottom"
|
||
:width="170"
|
||
trigger="click"
|
||
v-if="item.switchType === 'routeWidth'"
|
||
>
|
||
<template #reference>
|
||
<div @click="toolbarClick(item)">
|
||
<Icon :icon="item.icon" :size="20" />
|
||
<div class="name"> {{ item.name }} </div>
|
||
</div>
|
||
</template>
|
||
<!-- 网格 -->
|
||
<el-form
|
||
:model="state.routeWidthForm"
|
||
v-if="item.switchType === 'routeWidth'"
|
||
class="mt-2"
|
||
>
|
||
<el-form-item label="路线宽度">
|
||
<el-select v-model="state.routeWidthForm.routeWidth" placeholder="选择">
|
||
<el-option label="1" :value="1" />
|
||
<el-option label="2" :value="2" />
|
||
<el-option label="3" :value="3" />
|
||
<el-option label="4" :value="4" />
|
||
<el-option label="5" :value="5" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-form>
|
||
</el-popover>
|
||
|
||
<div v-else>
|
||
<Icon :icon="item.icon" :size="20" />
|
||
<div class="name"> {{ item.name }} </div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
class="map-container"
|
||
ref="mapContainerRef"
|
||
:style="{ cursor: state.cursorStyle }"
|
||
v-if="imgBgObj.width && imgBgObj.height"
|
||
>
|
||
<!-- <map-scale-tool :stepLength="50" :width="imgBgObj.width" :height="imgBgObj.height"> -->
|
||
<div
|
||
class="map-bg"
|
||
:style="{
|
||
backgroundImage: `url(${imgBgObj.imgUrl})`,
|
||
transform: `scale(${state.imageChangeMultiple})`,
|
||
transformOrigin: '0 0',
|
||
width: `${imgBgObj.width}px`,
|
||
height: `${imgBgObj.height}px`
|
||
}"
|
||
@mousedown="startDrawSelection"
|
||
@mousemove="updateDrawSelection"
|
||
@mouseup="endDrawSelection"
|
||
>
|
||
<div
|
||
ref="mapBackgroundRef"
|
||
class="map-box-inner"
|
||
@click="mapClick"
|
||
:style="gradientBackground"
|
||
v-if="interfaceRefreshed"
|
||
>
|
||
<VueDragResizeRotate
|
||
v-for="(item, index) in state.allMapPointInfo"
|
||
:key="index + Number(item.locationX)"
|
||
:parent="true"
|
||
:x="Number(item.locationX) - Number(item.locationWidePx) / 2"
|
||
:y="Number(item.locationY) - Number(item.locationDeepPx) / 2"
|
||
:w="Number(item.locationWidePx)"
|
||
:h="Number(item.locationDeepPx)"
|
||
:r="item.angle"
|
||
@rotatestop="(degree) => rotateEnd(degree, item, index)"
|
||
@dragstop="(x, y) => dragEnd(x, y, item, index)"
|
||
@resizestop="(x, y, width, height) => resizeEnd(x, y, width, height, item, index)"
|
||
@activated="() => activatedHandle(item, index)"
|
||
@deactivated="deactivatedHandle"
|
||
:draggable="!state.prohibitedOperation && item.draggable"
|
||
:resizable="!state.prohibitedOperation && item.resizable"
|
||
:rotatable="!state.prohibitedOperation && item.rotatable"
|
||
:lock-aspect-ratio="item.lockAspectRatio"
|
||
:scaleRatio="state.imageChangeMultiple"
|
||
style="border: none; z-index: 999"
|
||
>
|
||
<!-- 节点合集 -->
|
||
<div
|
||
@mousedown="startFromPoint(index, $event)"
|
||
:style="{ width: item.locationWidePx + 'px', height: item.locationDeepPx + 'px' }"
|
||
>
|
||
<!-- 1 路径点 -->
|
||
<el-tooltip effect="dark" placement="top" trigger="click">
|
||
<template #content>
|
||
<div v-if="item.type === 1">
|
||
<div>序号:{{ item.sortNum || '节点未保存' }}</div>
|
||
</div>
|
||
<div v-else-if="item.type === 2">
|
||
<div>序号:{{ item.sortNum || '节点未保存' }}</div>
|
||
<div class="item-tooltip-name" v-if="item.laneId && item.laneName">
|
||
所属线库:{{ item.laneName }}
|
||
</div>
|
||
<div class="item-tooltip-name" v-if="item.areaId && item.areaName">
|
||
所属区域:{{ item.areaName }}
|
||
</div>
|
||
<div class="item-tooltip-name" v-if="item.areaId && item.skuInfo">
|
||
物料信息:{{ item.skuInfo }}
|
||
</div>
|
||
</div>
|
||
<div v-else-if="item.type === 3">
|
||
<div>序号:{{ item.sortNum || '节点未保存' }}</div>
|
||
<div class="item-tooltip-name" v-if="item.deviceType">
|
||
设备类型:{{ getDeviceTypeName(item.deviceType) }}
|
||
</div>
|
||
<div class="item-tooltip-name" v-if="item.deviceNo">
|
||
设备编号:{{ item.deviceNo }}
|
||
</div>
|
||
</div>
|
||
<div v-else>
|
||
<div>序号:{{ item.sortNum || '节点未保存' }}</div>
|
||
<div class="item-tooltip-name">
|
||
节点类型:{{
|
||
item.type == 4
|
||
? '停车点'
|
||
: item.type == 5
|
||
? '区域变更点'
|
||
: item.type == 6
|
||
? '等待点'
|
||
: ''
|
||
}}
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<div>
|
||
<div
|
||
v-if="
|
||
item.type === 1 &&
|
||
item.layerSelectionShow &&
|
||
state.isShowSortNum &&
|
||
item.sortNum
|
||
"
|
||
class="sort-num"
|
||
:style="getSortNumStyle(item, index)"
|
||
>
|
||
{{ item.sortNum }}
|
||
</div>
|
||
<div
|
||
v-if="
|
||
item.type !== 1 &&
|
||
item.layerSelectionShow &&
|
||
state.isShowSortNum &&
|
||
item.sortNum
|
||
"
|
||
class="sort-num-location"
|
||
:style="getSortNumLocationStyle(item, index)"
|
||
>
|
||
{{ item.sortNum }}
|
||
</div>
|
||
<div
|
||
class="sort-num-location"
|
||
:style="getLocationNumberStyle(item, index)"
|
||
v-if="
|
||
toolbarSwitchType === 'createLineLibrary' &&
|
||
item.type === 2 &&
|
||
item.locationNumber
|
||
"
|
||
>
|
||
{{ item.locationNumber }}
|
||
</div>
|
||
<div
|
||
v-if="item.type === 1 && item.layerSelectionShow"
|
||
:style="{
|
||
width: item.locationWidePx + 'px',
|
||
height: item.locationDeepPx + 'px',
|
||
backgroundColor: state.currentItemIndex === index ? '#5ecc62' : '#000',
|
||
borderRadius: '50%',
|
||
zIndex: 999
|
||
}"
|
||
>
|
||
</div>
|
||
<!-- 2 库位点 -->
|
||
<img
|
||
v-if="item.type === 2 && item.layerSelectionShow"
|
||
src="https://api.znkjfw.com/admin-api/infra/file/4/get/库位库存_png_179_1744098544821.png"
|
||
alt=""
|
||
:style="binLocationStyle(item, index)"
|
||
/>
|
||
<!-- 3 设备点 -->
|
||
<img
|
||
v-if="item.type === 3 && item.layerSelectionShow"
|
||
:src="
|
||
item.mapImageUrl ||
|
||
'https://api.znkjfw.com/admin-api/infra/file/4/get/设备 (4)_png_179_1744102025024.png'
|
||
"
|
||
alt=""
|
||
style="background: #fff"
|
||
:style="nodeStyle(item, index)"
|
||
/>
|
||
<!-- 4 停车点 -->
|
||
<img
|
||
v-if="item.type === 4 && item.layerSelectionShow"
|
||
src="https://api.znkjfw.com/admin-api/infra/file/4/get/停车位_png_179_1744098982069.png"
|
||
alt=""
|
||
style="background: #fff"
|
||
:style="nodeStyle(item, index)"
|
||
/>
|
||
<!-- 5 区域变更点 -->
|
||
<img
|
||
v-if="item.type === 5 && item.layerSelectionShow"
|
||
src="https://api.znkjfw.com/admin-api/infra/file/4/get/区域变更 (1)_png_179_1744100605191.png"
|
||
alt=""
|
||
style="background: #fff"
|
||
:style="nodeStyle(item, index)"
|
||
/>
|
||
<!-- 6 等待点 -->
|
||
<img
|
||
v-if="item.type === 6 && item.layerSelectionShow"
|
||
src="https://api.znkjfw.com/admin-api/infra/file/4/get/等待_png_179_1744102070670.png"
|
||
alt=""
|
||
style="background: #fff"
|
||
:style="nodeStyle(item, index)"
|
||
/>
|
||
</div>
|
||
</el-tooltip>
|
||
</div>
|
||
<div
|
||
class="node-text"
|
||
v-if="item.type === 7 && item.layerSelectionShow"
|
||
:style="{
|
||
position: 'absolute',
|
||
color: item.fontColor,
|
||
fontSize: item.fontSize + 'px',
|
||
fontFamily: item.fontFamily,
|
||
border: state.currentItemIndex === index ? '.0625rem dashed #000' : 'none'
|
||
}"
|
||
>
|
||
{{ item.text }}
|
||
</div>
|
||
</VueDragResizeRotate>
|
||
|
||
<div v-if="imgBgObj.width && imgBgObj.height">
|
||
<svg
|
||
:width="imgBgObj.width"
|
||
:height="imgBgObj.height"
|
||
style="position: absolute; top: 0; left: 0; z-index: 9"
|
||
id="svgId"
|
||
>
|
||
<!-- 实时绘制当前直线 -->
|
||
<line
|
||
v-if="state.isDrawing && toolbarSwitchType === 'clickDrawRoute'"
|
||
:x1="Number(state.startDrawPoint.locationX)"
|
||
:y1="Number(state.startDrawPoint.locationY)"
|
||
:x2="Number(state.currentDrawX)"
|
||
:y2="Number(state.currentDrawY)"
|
||
stroke="#2d72d9"
|
||
:stroke-width="state.routeWidthForm.routeWidth"
|
||
/>
|
||
<template v-if="state.mapRouteList.length > 0">
|
||
<template v-for="(curve, index) in state.mapRouteList" :key="index">
|
||
<!-- 直线 -->
|
||
<template v-if="curve.method === 0">
|
||
<line
|
||
:x1="Number(curve.startPointX)"
|
||
:y1="Number(curve.startPointY)"
|
||
:x2="Number(curve.endPointX)"
|
||
:y2="Number(curve.endPointY)"
|
||
:stroke="curve.isSelected ? '#f48924' : '#2d72d9'"
|
||
:stroke-width="state.routeWidthForm.routeWidth"
|
||
@click="(e) => handleChooseRoute(curve, index, 'line', e)"
|
||
/>
|
||
<text
|
||
style="user-select: none"
|
||
:x="(Number(curve.startPointX) + Number(curve.endPointX)) / 2"
|
||
:y="(Number(curve.startPointY) + Number(curve.endPointY) - 18) / 2"
|
||
font-size="11"
|
||
text-anchor="middle"
|
||
fill="black"
|
||
v-if="curve.isSelected"
|
||
@click="(e) => handleChooseRoute(curve, index, 'line', e)"
|
||
>
|
||
{{ calculateRouteLength(curve, 'line') }}米
|
||
</text>
|
||
</template>
|
||
|
||
<template v-else>
|
||
<path
|
||
id="curvePath"
|
||
:d="getCurvePath(curve)"
|
||
:stroke="curve.isSelected ? '#f48924' : '#2d72d9'"
|
||
:stroke-width="state.routeWidthForm.routeWidth"
|
||
fill="none"
|
||
@click="handleChooseRoute(curve, index)"
|
||
/>
|
||
<text
|
||
style="user-select: none"
|
||
:x="computedCurveTextX(curve)"
|
||
:y="computedCurveTextY(curve)"
|
||
font-size="11"
|
||
text-anchor="middle"
|
||
fill="black"
|
||
v-if="curve.isSelected"
|
||
@click="handleChooseRoute(curve, index)"
|
||
>
|
||
{{ calculateRouteLength(curve, 'curve') }}米
|
||
</text>
|
||
|
||
<!-- 第一条控制线 -->
|
||
<line
|
||
v-if="state.currentDragTarget.index == index"
|
||
:x1="Number(curve.startPointX)"
|
||
:y1="Number(curve.startPointY)"
|
||
:x2="curve.beginControlX"
|
||
:y2="curve.beginControlY"
|
||
:stroke="curve.isSelected ? '#f48924' : '#2d72d9'"
|
||
stroke-dasharray="4"
|
||
stroke-width="2"
|
||
/>
|
||
<!-- 第二条控制线 -->
|
||
<line
|
||
v-if="state.currentDragTarget.index == index"
|
||
:x1="Number(curve.endPointX)"
|
||
:y1="Number(curve.endPointY)"
|
||
:x2="curve.endControlX"
|
||
:y2="curve.endControlY"
|
||
:stroke="curve.isSelected ? '#f48924' : '#2d72d9'"
|
||
stroke-dasharray="4"
|
||
stroke-width="2"
|
||
/>
|
||
<circle
|
||
v-if="state.currentDragTarget.index == index"
|
||
id="startCircle"
|
||
:cx="curve.beginControlX"
|
||
:cy="curve.beginControlY"
|
||
r="5"
|
||
:fill="curve.isSelected ? '#f48924' : '#2d72d9'"
|
||
@mousedown="startDrag(curve, index, 'start')"
|
||
/>
|
||
<circle
|
||
v-if="state.currentDragTarget.index == index"
|
||
id="endCircle"
|
||
:cx="curve.endControlX"
|
||
:cy="curve.endControlY"
|
||
r="5"
|
||
:fill="curve.isSelected ? '#f48924' : '#2d72d9'"
|
||
@mousedown="startDrag(curve, index, 'end')"
|
||
/>
|
||
</template>
|
||
</template>
|
||
</template>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 框选区域 -->
|
||
<div
|
||
v-for="(box, index) in state.allDrawSelectionAreaBox"
|
||
:key="index"
|
||
:style="{
|
||
position: 'absolute',
|
||
left: `${box.x}px`,
|
||
top: `${box.y}px`,
|
||
width: `${box.width}px`,
|
||
height: `${box.height}px`,
|
||
border: '.125rem dashed #007bff',
|
||
backgroundColor: 'rgba(0, 123, 255, 0.1)',
|
||
zIndex: 999
|
||
}"
|
||
></div>
|
||
<!-- 当前框选区域 -->
|
||
<div
|
||
v-if="state.drawSelectionAreaShow"
|
||
:style="{
|
||
position: 'absolute',
|
||
left: `${state.drawSelectionAreaBox.x}px`,
|
||
top: `${state.drawSelectionAreaBox.y}px`,
|
||
width: `${state.drawSelectionAreaBox.width}px`,
|
||
height: `${state.drawSelectionAreaBox.height}px`,
|
||
border: '.125rem dashed #007bff',
|
||
backgroundColor: 'rgba(0, 123, 255, 0.1)',
|
||
zIndex: 9999
|
||
}"
|
||
></div>
|
||
|
||
<!-- 测距 绘制点和连线 -->
|
||
<template v-if="state.measureDistancesPoints.length > 0">
|
||
<!-- 测距的线 -->
|
||
<div v-if="state.measureDistancesPoints.length === 2" :style="rangingLineStyle"></div>
|
||
<!-- 显示距离信息 -->
|
||
<div v-if="state.measureDistancesNum !== null" :style="rangingTextStyle">
|
||
{{ state.measureDistancesNum.toFixed(2) }}米
|
||
</div>
|
||
<!-- 测距的点 -->
|
||
<div
|
||
v-for="(point, index) in state.measureDistancesPoints"
|
||
:key="index"
|
||
:style="getRangingPointStyle(point)"
|
||
></div>
|
||
</template>
|
||
|
||
<!-- 文字输入区域 -->
|
||
<input
|
||
v-if="state.showInputBox"
|
||
ref="inputBoxRef"
|
||
v-model="state.inputBoxValue"
|
||
@keyup.enter="handleInputEnd"
|
||
@blur="handleInputEnd"
|
||
:style="{
|
||
fontSize: state.inputBoxStyle.fontSize + 'px',
|
||
fontFamily: state.inputBoxStyle.fontFamily,
|
||
color: state.inputBoxStyle.fontColor,
|
||
left: state.inputBoxStyle.locationX + 'px',
|
||
top: state.inputBoxStyle.locationY + 'px'
|
||
}"
|
||
:maxlength="30"
|
||
class="input-box-class"
|
||
/>
|
||
</div>
|
||
<!-- </map-scale-tool> -->
|
||
</div>
|
||
|
||
<div class="actual-location" v-if="state.actualLocation.x && state.actualLocation.y">
|
||
<div>X:{{ state.actualLocation.x }}</div>
|
||
<div>Y:{{ state.actualLocation.y }}</div>
|
||
</div>
|
||
|
||
<!-- 节点编辑 -->
|
||
<editNodeProperties
|
||
v-if="imgBgObj.positionMapId"
|
||
ref="editNodePropertiesRef"
|
||
:positionMapId="imgBgObj.positionMapId"
|
||
:imgBgObj="imgBgObj"
|
||
@submitNodeSuccess="submitNodeSuccess"
|
||
@addEventListener="addEventListener"
|
||
/>
|
||
<!-- 文字输入弹窗 -->
|
||
<textFormToolDialog
|
||
ref="textFormToolDialogRef"
|
||
v-if="state.textFormToolShow"
|
||
:inputBoxStyle="state.inputBoxStyle"
|
||
@textFormSuccess="textFormSuccess"
|
||
/>
|
||
<!-- 图层选择 -->
|
||
<layerSelectionToolDialog
|
||
v-if="state.isShowLayer"
|
||
ref="layerSelectionToolDialogRef"
|
||
@layerSelectionSuccess="layerSelectionSuccess"
|
||
/>
|
||
<!-- 设备弹窗选择 -->
|
||
<equipmentToolDialog
|
||
ref="equipmentToolDialogRef"
|
||
:positionMapId="imgBgObj.positionMapId"
|
||
@addEventListener="addEventListener"
|
||
/>
|
||
<!-- 区域选择 -->
|
||
<itemAreaSettingDialog
|
||
ref="itemAreaSettingDialogRef"
|
||
:positionMapId="imgBgObj.positionMapId"
|
||
@addEventListener="addEventListener"
|
||
@itemAreaSettingSubmitSuccess="itemAreaSettingSubmitSuccess"
|
||
/>
|
||
<!-- 线库设置 -->
|
||
<lineLibrarySettingDialog
|
||
ref="lineLibrarySettingDialogRef"
|
||
:positionMapId="imgBgObj.positionMapId"
|
||
@addEventListener="addEventListener"
|
||
@submitLineLibraryFormSuccess="submitLineLibraryFormSuccess"
|
||
/>
|
||
<!-- 编辑地图路线 -->
|
||
<editMapRouteDialog
|
||
v-if="imgBgObj.positionMapId"
|
||
ref="editMapRouteDialogRef"
|
||
@editMapRouteDialogSubmit="editMapRouteDialogSubmit"
|
||
@addEventListener="addEventListener"
|
||
:imgBgObj="imgBgObj"
|
||
/>
|
||
<!-- 线库管理 -->
|
||
<lineLibraryManagementDialog
|
||
ref="lineLibraryManagementDialogRef"
|
||
:positionMapId="imgBgObj.positionMapId"
|
||
@addEventListener="addEventListener"
|
||
@lineLibraryManagementDelete="lineLibraryManagementDelete"
|
||
@lineLibraryManagementEdit="lineLibraryManagementEdit"
|
||
/>
|
||
<!-- 区域管理 -->
|
||
<itemAreaManagementDialog
|
||
ref="itemAreaManagementDialogRef"
|
||
:positionMapId="imgBgObj.positionMapId"
|
||
@addEventListener="addEventListener"
|
||
@itemAreaManagementDelete="itemAreaManagementDelete"
|
||
@itemAreaManagementEdit="itemAreaManagementEdit"
|
||
/>
|
||
<!-- 生成直线 选择开始点和结束点 -->
|
||
<GenerateStraightLinesDialog
|
||
ref="GenerateStraightLinesDialogRef"
|
||
@GenerateStraightLinesSubmit="GenerateStraightLinesSubmit"
|
||
/>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import JSONBigInt from 'json-bigint'
|
||
import { ElLoading } from 'element-plus'
|
||
import { ref, defineComponent, reactive, nextTick, onMounted } from 'vue'
|
||
import editMapRouteDialog from './components-tool/editMapRouteDialog.vue'
|
||
import editNodeProperties from './components-tool/editNodeProperties.vue'
|
||
import textFormToolDialog from './components-tool/textFormToolDialog.vue'
|
||
import equipmentToolDialog from './components-tool/equipmentToolDialog.vue'
|
||
import itemAreaSettingDialog from './components-tool/itemAreaSettingDialog.vue'
|
||
import lineLibrarySettingDialog from './components-tool/lineLibrarySettingDialog.vue'
|
||
import layerSelectionToolDialog from './components-tool/layerSelectionToolDialog.vue'
|
||
import itemAreaManagementDialog from './components-tool/itemAreaManagementDialog.vue'
|
||
import lineLibraryManagementDialog from './components-tool/lineLibraryManagementDialog.vue'
|
||
import GenerateStraightLinesDialog from './components-tool/GenerateStraightLinesDialog.vue'
|
||
import mapScaleTool from './components-tool/map-scale-tool.vue'
|
||
|
||
import * as MapApi from '@/api/map/map'
|
||
import cursorCollection from './cursorCollection'
|
||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||
|
||
defineOptions({ name: 'editMapPageRealTimeMap' })
|
||
|
||
const GenerateStraightLinesDialogRef = ref() //生成直线的
|
||
const lineLibraryManagementDialogRef = ref() //线库管理
|
||
const itemAreaManagementDialogRef = ref() //区域管理
|
||
const lineLibrarySettingDialogRef = ref() //线库设置
|
||
const itemAreaSettingDialogRef = ref() //物料区域设置
|
||
const equipmentToolDialogRef = ref() //设备弹窗
|
||
const textFormToolDialogRef = ref() //文字输入弹窗
|
||
const editMapRouteDialogRef = ref() //编辑地图路线的弹窗
|
||
const mapBackgroundRef = ref()
|
||
const mapContainerRef = ref()
|
||
const inputBoxRef = ref() //文字输入框
|
||
|
||
const message = useMessage() // 消息弹窗
|
||
|
||
//其他节点的样式
|
||
const nodeStyle = (item, index) => {
|
||
return {
|
||
verticalAlign: 'top',
|
||
width: item.locationWidePx + 'px',
|
||
height: item.locationDeepPx + 'px',
|
||
border: state.currentItemIndex === index ? '.0625rem dashed #000' : 'none',
|
||
borderRadius: '.1875rem'
|
||
}
|
||
}
|
||
//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 getLocationNumberStyle = (item, index) => {
|
||
let leftNum = 0
|
||
if (item.locationNumber.toString().length === 1) {
|
||
leftNum = 3
|
||
} else if (item.locationNumber.toString().length === 2) {
|
||
leftNum = 7
|
||
} else if (item.locationNumber.toString().length === 3) {
|
||
leftNum = 10
|
||
} else if (item.locationNumber.toString().length === 4) {
|
||
leftNum = 14
|
||
} else if (item.locationNumber.toString().length === 5) {
|
||
leftNum = 18
|
||
} else if (item.locationNumber.toString().length === 6) {
|
||
leftNum = 21
|
||
} else if (item.locationNumber.toString().length === 7) {
|
||
leftNum = 25
|
||
} else if (item.locationNumber.toString().length === 8) {
|
||
leftNum = 28
|
||
} else if (item.locationNumber.toString().length === 9) {
|
||
leftNum = 31
|
||
}
|
||
return {
|
||
left: Number(item.locationWidePx) / 2 - leftNum + 'px',
|
||
top: Number(item.locationDeepPx) / 2 - 2 + 'px'
|
||
}
|
||
}
|
||
//库位的样式
|
||
const binLocationStyle = (item, index) => {
|
||
let laneId
|
||
let areaId
|
||
if (state.currentItemIndex !== -1) {
|
||
laneId = state.allMapPointInfo[state.currentItemIndex].laneId
|
||
areaId = state.allMapPointInfo[state.currentItemIndex].areaId
|
||
}
|
||
return {
|
||
verticalAlign: 'top',
|
||
width: item.locationWidePx + 'px',
|
||
height: item.locationDeepPx + 'px',
|
||
border:
|
||
state.currentItemIndex === index
|
||
? '.0625rem dashed #000'
|
||
: state.noLocationNumberList.includes(index)
|
||
? '2px dashed red'
|
||
: laneId && item.laneId === laneId
|
||
? '2px dashed #ff6a00'
|
||
: areaId && item.areaId === areaId
|
||
? '2px dashed #00aeff'
|
||
: 'none'
|
||
}
|
||
}
|
||
|
||
// 缩放停止
|
||
const interfaceRefreshed = ref(true)
|
||
const resizeEnd = (locationX, locationY, w, h, item, index) => {
|
||
interfaceRefreshed.value = false
|
||
nextTick(() => {
|
||
let x = Number(locationX) + Number(item.locationWidePx) / 2
|
||
let y = Number(locationY) + Number(item.locationDeepPx) / 2
|
||
let width = Number(w) * imgBgObj.resolution * 100 //实际的宽高cm
|
||
let height = Number(h) * imgBgObj.resolution * 100
|
||
|
||
let actualPoint = disposeEventPoint(x, y)
|
||
|
||
state.allMapPointInfo[index].locationX = x
|
||
state.allMapPointInfo[index].locationY = y
|
||
state.allMapPointInfo[index].locationWide = width
|
||
state.allMapPointInfo[index].locationDeep = height
|
||
state.allMapPointInfo[index].locationWidePx = w
|
||
state.allMapPointInfo[index].locationDeepPx = h
|
||
state.allMapPointInfo[index].actualLocationX = actualPoint.actualLocationX
|
||
state.allMapPointInfo[index].actualLocationY = actualPoint.actualLocationY
|
||
//更改路线里的
|
||
state.mapRouteList.forEach((route) => {
|
||
if (item.id === route.startingPointId) {
|
||
route.startPointX = x
|
||
route.startPointY = y
|
||
route.beginHigh = Number(item.locationDeepPx)
|
||
route.beginWidth = Number(item.locationWidePx)
|
||
route.actualStartPointX = actualPoint.actualLocationX
|
||
route.actualStartPointY = actualPoint.actualLocationY
|
||
}
|
||
if (item.id === route.endPointId) {
|
||
route.endPointX = x
|
||
route.endPointY = y
|
||
route.endHigh = Number(item.locationDeepPx)
|
||
route.endWidth = Number(item.locationWidePx)
|
||
route.actualEndPointX = actualPoint.actualLocationX
|
||
route.actualEndPointY = actualPoint.actualLocationY
|
||
}
|
||
})
|
||
addEditHistory()
|
||
})
|
||
}
|
||
|
||
// 拖拽停止
|
||
const dragEnd = (locationX, locationY, item, index) => {
|
||
let x = Number(locationX) + Number(item.locationWidePx) / 2
|
||
let y = Number(locationY) + Number(item.locationDeepPx) / 2
|
||
|
||
if (x === item.locationX && y === item.locationY) return
|
||
let actualPoint = disposeEventPoint(x, y)
|
||
state.allMapPointInfo[index].locationX = x
|
||
state.allMapPointInfo[index].locationY = y
|
||
state.allMapPointInfo[index].actualLocationX = actualPoint.actualLocationX
|
||
state.allMapPointInfo[index].actualLocationY = actualPoint.actualLocationY
|
||
//更改路线里的
|
||
state.mapRouteList.forEach((route) => {
|
||
if (item.id === route.startingPointId) {
|
||
route.startPointX = x
|
||
route.startPointY = y
|
||
route.beginHigh = Number(item.locationDeepPx)
|
||
route.beginWidth = Number(item.locationWidePx)
|
||
route.actualStartPointX = actualPoint.actualLocationX
|
||
route.actualStartPointY = actualPoint.actualLocationY
|
||
}
|
||
if (item.id === route.endPointId) {
|
||
route.endPointX = x
|
||
route.endPointY = y
|
||
route.endHigh = Number(item.locationDeepPx)
|
||
route.endWidth = Number(item.locationWidePx)
|
||
route.actualEndPointX = actualPoint.actualLocationX
|
||
route.actualEndPointY = actualPoint.actualLocationY
|
||
}
|
||
})
|
||
addEditHistory()
|
||
}
|
||
// 旋转
|
||
const rotateEnd = (angle, item, index) => {
|
||
state.allMapPointInfo[index].angle = angle
|
||
addEditHistory()
|
||
}
|
||
|
||
//节点选中
|
||
const editNodePropertiesRef = ref()
|
||
const activatedHandle = (item, index) => {
|
||
if (toolbarSwitchType.value === 'editRoute') return
|
||
|
||
state.currentItemIndex = index
|
||
//让路线不选中
|
||
state.selectedCurve = ''
|
||
if (state.mapRouteList.length > 0 && state.currentDragTarget.index !== null) {
|
||
state.mapRouteList[state.currentDragTarget.index].isSelected = false
|
||
state.currentDragTarget = { index: null, type: null }
|
||
}
|
||
//显示出可走路线
|
||
state.isCanTakeRoutes = state.mapRouteList.forEach((route) => {
|
||
if (
|
||
route.startingPointId === item.id ||
|
||
(route.direction === 2 && route.endPointId === item.id)
|
||
) {
|
||
route.isSelected = true
|
||
} else {
|
||
route.isSelected = false
|
||
}
|
||
})
|
||
//节点编辑
|
||
if (toolbarSwitchType.value === 'editNode' && item.type !== 7) {
|
||
let list = state.allMapPointInfo.filter((item) => item.type === 3)
|
||
removeEventListener() //移除监听
|
||
editNodePropertiesRef.value.open(JSON.parse(JSON.stringify(item)), list)
|
||
}
|
||
}
|
||
//非选中
|
||
const deactivatedHandle = () => {}
|
||
|
||
//添加历史记录
|
||
const addEditHistory = () => {
|
||
//要判断是不是在最新的记录上修改 如果不是 要把currentIndex之后的记录都删掉 保持当前修改是最新的
|
||
if (state.currentIndex < state.allHistoryList.length - 1) {
|
||
state.allHistoryList.splice(state.currentIndex + 1)
|
||
}
|
||
state.allHistoryList.push({
|
||
allMapPointInfo: JSON.parse(JSON.stringify(state.allMapPointInfo)),
|
||
mapRouteList: JSON.parse(JSON.stringify(state.mapRouteList))
|
||
})
|
||
state.currentIndex = state.allHistoryList.length - 1
|
||
interfaceRefreshed.value = true
|
||
}
|
||
|
||
//上一步
|
||
const backPreviousStep = () => {
|
||
if (state.currentIndex > 0) {
|
||
state.currentIndex--
|
||
state.allMapPointInfo.splice(
|
||
0,
|
||
state.allMapPointInfo.length,
|
||
...JSON.parse(JSON.stringify(state.allHistoryList[state.currentIndex]?.allMapPointInfo))
|
||
)
|
||
state.mapRouteList.splice(
|
||
0,
|
||
state.mapRouteList.length,
|
||
...JSON.parse(JSON.stringify(state.allHistoryList[state.currentIndex]?.mapRouteList))
|
||
)
|
||
} else {
|
||
message.warning('没了老铁')
|
||
}
|
||
}
|
||
//下一步
|
||
const backNextStep = () => {
|
||
if (state.currentIndex < state.allHistoryList.length - 1) {
|
||
state.currentIndex++
|
||
state.allMapPointInfo.splice(
|
||
0,
|
||
state.allMapPointInfo.length,
|
||
...JSON.parse(JSON.stringify(state.allHistoryList[state.currentIndex]?.allMapPointInfo))
|
||
)
|
||
state.mapRouteList.splice(
|
||
0,
|
||
state.mapRouteList.length,
|
||
...JSON.parse(JSON.stringify(state.allHistoryList[state.currentIndex]?.mapRouteList))
|
||
)
|
||
} else {
|
||
message.warning('没了老铁')
|
||
}
|
||
}
|
||
|
||
//地图点击
|
||
const mapClick = (e) => {
|
||
const x = disposeEventPoints(e).x
|
||
const y = disposeEventPoints(e).y
|
||
const actualLocationX = disposeEventPoints(e).actualLocationX
|
||
const actualLocationY = disposeEventPoints(e).actualLocationY
|
||
|
||
state.actualLocation.x = actualLocationX
|
||
state.actualLocation.y = actualLocationY
|
||
|
||
//新增节点
|
||
if (toolbarSwitchType.value === 'drawNodes') {
|
||
state.allMapPointInfo.push({
|
||
positionMapId: imgBgObj.positionMapId, //地图的id
|
||
layerSelectionShow: true,
|
||
locationX: x,
|
||
locationY: y,
|
||
actualLocationX: actualLocationX,
|
||
actualLocationY: actualLocationY,
|
||
locationDeep: 40,
|
||
locationWide: 40,
|
||
locationDeepPx: 8,
|
||
locationWidePx: 8,
|
||
angle: 0,
|
||
draggable: true,
|
||
resizable: true,
|
||
rotatable: false,
|
||
lockAspectRatio: false, //横纵比
|
||
mapImageUrl: '',
|
||
locationYaw: 0, //弧度
|
||
type: 1, //默认类型1 路径节点
|
||
dataList: [], //存库位的
|
||
dataObj: {} //存 设备点 停车点 文字
|
||
})
|
||
addEditHistory()
|
||
}
|
||
//文字输入
|
||
if (toolbarSwitchType.value === 'text') {
|
||
state.showInputBox = true
|
||
state.inputBoxStyle.locationX = x
|
||
state.inputBoxStyle.locationY = y
|
||
state.inputBoxStyle.actualLocationX = actualLocationX
|
||
state.inputBoxStyle.actualLocationY = actualLocationY
|
||
|
||
// 聚焦输入框
|
||
setTimeout(() => {
|
||
inputBoxRef.value.focus()
|
||
}, 0)
|
||
}
|
||
//测距
|
||
if (toolbarSwitchType.value === 'ranging') {
|
||
measureDistancesClick(e)
|
||
}
|
||
}
|
||
//输入文字样式改变
|
||
const textFormSuccess = (form) => {
|
||
state.inputBoxStyle.fontSize = `${form.fontSize}`
|
||
state.inputBoxStyle.fontFamily = `${form.fontFamily}`
|
||
state.inputBoxStyle.fontColor = `${form.fontColor}`
|
||
}
|
||
//输入结束
|
||
const handleInputEnd = () => {
|
||
if (state.inputBoxValue) {
|
||
state.showInputBox = false
|
||
state.allMapPointInfo.push({
|
||
type: 7, //类型 7文字
|
||
positionMapId: imgBgObj.positionMapId, //地图的id
|
||
locationX: state.inputBoxStyle.locationX, //x
|
||
locationY: state.inputBoxStyle.locationY, //y
|
||
actualLocationX: state.inputBoxStyle.actualLocationX,
|
||
actualLocationY: state.inputBoxStyle.actualLocationY,
|
||
locationDeep: null, //h
|
||
locationWide: null, //w
|
||
angle: 0, //旋转角度
|
||
draggable: true, //是否可以拖动
|
||
resizable: false, //是否可以调整大小
|
||
rotatable: true, //是否可以旋转
|
||
lockAspectRatio: true, //是否锁定比例
|
||
mapImageUrl: '', //图片
|
||
text: state.inputBoxValue, //文字内容
|
||
fontColor: state.inputBoxStyle.fontColor, //文字颜色
|
||
fontFamily: state.inputBoxStyle.fontFamily, //字体类型
|
||
fontSize: state.inputBoxStyle.fontSize,
|
||
dataObj: {}, //存 设备点 停车点 文字
|
||
layerSelectionShow: true,
|
||
locationDeep: 20,
|
||
locationWide: 20,
|
||
locationDeepPx: 4,
|
||
locationWidePx: 4
|
||
})
|
||
addEditHistory()
|
||
state.inputBoxValue = ''
|
||
}
|
||
}
|
||
//编辑节点成功
|
||
const submitNodeSuccess = (form) => {
|
||
form.locationDeepPx = Number(form.locationDeep) / 100 / imgBgObj.resolution
|
||
form.locationWidePx = Number(form.locationWide) / 100 / imgBgObj.resolution
|
||
state.allMapPointInfo[state.currentItemIndex] = form
|
||
|
||
//节点位置改变 通知路径里面的节点也改变
|
||
state.mapRouteList.forEach((item) => {
|
||
if (form.id === item.startingPointId) {
|
||
item.startPointX = form.locationX
|
||
item.startPointY = form.locationY
|
||
item.beginHigh = Number(form.locationDeepPx)
|
||
item.beginWidth = Number(form.locationWidePx)
|
||
item.actualStartPointX = disposeEventPoint(form.locationX, form.locationY).actualLocationX
|
||
item.actualStartPointY = disposeEventPoint(form.locationX, form.locationY).actualLocationY
|
||
}
|
||
if (form.id === item.endPointId) {
|
||
item.endPointX = form.locationX
|
||
item.endPointY = form.locationY
|
||
item.endHigh = Number(form.locationDeepPx)
|
||
item.endWidth = Number(form.locationWidePx)
|
||
item.actualEndPointX = disposeEventPoint(form.locationX, form.locationY).actualLocationX
|
||
item.actualEndPointY = disposeEventPoint(form.locationX, form.locationY).actualLocationY
|
||
}
|
||
})
|
||
addEditHistory()
|
||
|
||
//存在库位未填写排序号的问题
|
||
state.noLocationNumberList = state.allMapPointInfo.reduce((invalidIndexes, item, index) => {
|
||
if (item.type === 2 && !item.locationNumber) {
|
||
invalidIndexes.push(index) // 如果不满足条件,记录索引
|
||
}
|
||
return invalidIndexes
|
||
}, [])
|
||
}
|
||
|
||
//工具栏点击
|
||
const toolbarSwitchType = ref('')
|
||
const state = reactive({
|
||
topToolList: [
|
||
// {
|
||
// switchType: 'open',
|
||
// name: '打开',
|
||
// icon: 'ep:folder-add',
|
||
// isActive: false
|
||
// },
|
||
{
|
||
switchType: 'save',
|
||
name: '保存',
|
||
icon: 'ep:folder-checked',
|
||
isActive: false
|
||
},
|
||
// {
|
||
// switchType: 'saveAs',
|
||
// name: '另存为',
|
||
// icon: 'ep:folder-opened',
|
||
// isActive: false
|
||
// },
|
||
{
|
||
switchType: 'choose',
|
||
name: '选择',
|
||
icon: 'ep:position',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'move',
|
||
name: '移动',
|
||
icon: 'ep:rank',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'revolve',
|
||
name: '旋转',
|
||
icon: 'ep:refresh-right',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'copy',
|
||
name: '复制',
|
||
icon: 'ep:document',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'paste',
|
||
name: '粘贴',
|
||
icon: 'ep:copy-document',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'delete',
|
||
name: '删除',
|
||
icon: 'ep:delete',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'tools',
|
||
name: '工具',
|
||
icon: 'ep:briefcase',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'lineLibrary',
|
||
name: '线库',
|
||
icon: 'ep:message-box',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'region',
|
||
name: '区域',
|
||
icon: 'ep:full-screen',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'text',
|
||
name: '文字',
|
||
icon: 'ep:edit-pen',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'equipment',
|
||
name: '设备',
|
||
icon: 'ep:video-camera-filled',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'ranging',
|
||
name: '测距',
|
||
icon: 'ep:folder-add',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'layer',
|
||
name: '图层',
|
||
icon: 'ep:copy-document',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'marker',
|
||
name: '标记',
|
||
icon: 'ep:map-location',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'grid',
|
||
name: '网格',
|
||
icon: 'ep:grid',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'larger',
|
||
name: '放大',
|
||
icon: 'ep:zoom-in',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'smaller',
|
||
name: '缩小',
|
||
icon: 'ep:zoom-out',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'withdraw',
|
||
name: '撤回',
|
||
icon: 'ep:top-left',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'next',
|
||
name: '重做',
|
||
icon: 'ep:top-right',
|
||
isActive: false
|
||
}
|
||
],
|
||
rightToolList: [
|
||
{
|
||
switchType: 'drawNodes',
|
||
name: '新增节点',
|
||
icon: 'ep:circle-plus-filled',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'editNode',
|
||
name: '编辑节点',
|
||
icon: 'ep:edit',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'clickDrawRoute',
|
||
name: '新增路线',
|
||
icon: 'ep:semi-select',
|
||
isActive: false
|
||
},
|
||
// {
|
||
// switchType: 'drawRoute',
|
||
// name: '框选绘制',
|
||
// icon: 'ep:semi-select',
|
||
// isActive: false
|
||
// },
|
||
{
|
||
switchType: 'editRoute',
|
||
name: '编辑路线',
|
||
icon: 'ep:edit-pen',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'generateLine',
|
||
name: '生成直线',
|
||
icon: 'ep:finished',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'bulkDelete',
|
||
name: '批量删除',
|
||
icon: 'ep:delete',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'showSortNum',
|
||
name: '节点序号',
|
||
icon: 'ep:chat-dot-square',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'routeWidth',
|
||
name: '路线宽度',
|
||
icon: 'ep:semi-select',
|
||
isActive: false
|
||
},
|
||
{
|
||
switchType: 'addPointOnline',
|
||
name: '线上加点',
|
||
icon: 'ep:add-location',
|
||
isActive: false
|
||
}
|
||
],
|
||
isShowToolbar: false, //工具栏展示隐藏
|
||
isShowGrid: false, //网格展示隐藏
|
||
moveForm: {
|
||
locationX: 0,
|
||
locationY: 0
|
||
}, //移动的表单
|
||
rotationForm: {
|
||
angle: 0
|
||
}, //旋转的表单
|
||
copyMapItem: '', //复制的值
|
||
cursorStyle: 'auto',
|
||
drawSelectionAreaShow: false, //绘制框选区域
|
||
allDrawSelectionAreaBox: [], // 所有框选区域
|
||
drawSelectionAreaBox: { x: 0, y: 0, width: 0, height: 0 }, //绘制区域的位置,长宽
|
||
drawSelectionStartPoint: { x: 0, y: 0 }, //开始绘制的点位
|
||
drawSelectionPointList: [], //绘制选中的list
|
||
textFormToolShow: false, //文字表单显示隐藏
|
||
showInputBox: false, //输入框显示隐藏
|
||
inputBoxStyle: {
|
||
fontFamily: 'SimSun',
|
||
fontSize: '14',
|
||
fontColor: '#000000',
|
||
locationX: 0,
|
||
locationY: 0,
|
||
actualLocationX: 0,
|
||
actualLocationY: 0
|
||
}, //输入框的样式
|
||
inputBoxValue: '', //输入的值
|
||
isShowLayer: false, //图层显示
|
||
measureDistancesPoints: [], // 存储点击的点位
|
||
measureDistancesNum: 0, // 存储两点之间的距离
|
||
imageChangeMultiple: 1, //图片放大缩小的倍数
|
||
prohibitedOperation: false, //禁止操作 在框选测距等操作时,禁止所有拖动等操作
|
||
currentDragTarget: {
|
||
index: null,
|
||
type: null
|
||
}, //当前拖拽的目标(起点、终点、控制点)
|
||
selectedCurve: '', // 当前选中的曲线
|
||
startDrawPointIndex: -1, // 起始点的索引
|
||
startDrawPoint: null, //新增路线开始的点
|
||
isDrawing: false, //正在绘制
|
||
currentDrawX: 0, //正在绘制的x轴坐标
|
||
currentDrawY: 0, //正在绘制的y轴坐标
|
||
allHistoryList: [], //所有的历史记录 [{allMapPointInfo:[],mapRouteList:[]}]
|
||
allMapPointInfo: [], //当前页面上的所有节点的列表
|
||
mapRouteList: [], //当前页面上所有路线的列表
|
||
currentIndex: 0, //当前处在哪条历史记录
|
||
currentItemIndex: -1, //当前处在哪个节点上
|
||
markForm: {
|
||
macAddress: '', // mac地址
|
||
markProperty: '', //标记属性
|
||
originalNode: '', //原节点
|
||
robotNo: '' //车辆编号
|
||
}, //标记的表单
|
||
mapMarkCarList: [], //标记的车辆列表
|
||
popoverVisible: false, //标记弹窗
|
||
noLocationNumberList: [], //没有排序的库位index
|
||
isShowSortNum: false, //是否显示序号
|
||
actualLocation: {
|
||
x: '',
|
||
y: ''
|
||
},
|
||
gridForm: {
|
||
gridNum: 20 + 'px'
|
||
},
|
||
routeWidthForm: {
|
||
routeWidth: 3
|
||
},
|
||
isSearchSelectVisible: false, //是否显示选择框
|
||
searchSelectedOption: '',
|
||
haveSortNumMapPointInfo: []
|
||
})
|
||
//网格的样式
|
||
const gradientBackground = computed(() => {
|
||
if (!state.isShowGrid)
|
||
return {
|
||
width: `${imgBgObj.width}px`,
|
||
height: `${imgBgObj.height}px`
|
||
}
|
||
|
||
return {
|
||
backgroundImage: `
|
||
linear-gradient(to right, rgba(0, 0, 0, 0.1) 1px, transparent 1px),
|
||
linear-gradient(to bottom, rgba(0, 0, 0, 0.1) 1px, transparent 1px)
|
||
`,
|
||
backgroundSize: `
|
||
${state.gridForm.gridNum} ${state.gridForm.gridNum},
|
||
${state.gridForm.gridNum} ${state.gridForm.gridNum}
|
||
`,
|
||
width: `${imgBgObj.width}px`,
|
||
height: `${imgBgObj.height}px`
|
||
}
|
||
})
|
||
|
||
const toolbarClick = async (item) => {
|
||
let type = item.switchType
|
||
if (state.currentItemIndex === -1 && (type === 'move' || type === 'revolve' || type === 'copy')) {
|
||
message.warning('请先选择要操作的对象')
|
||
return
|
||
}
|
||
|
||
if (
|
||
type === 'delete' &&
|
||
state.currentDragTarget.index === null &&
|
||
state.currentItemIndex === -1
|
||
) {
|
||
message.warning('请先选择要操作的对象')
|
||
return
|
||
}
|
||
|
||
//粘贴
|
||
if (type === 'paste' && !state.copyMapItem) {
|
||
message.warning('请先复制对象')
|
||
return
|
||
}
|
||
|
||
if (
|
||
(type === 'tools' ||
|
||
type === 'text' ||
|
||
type === 'layer' ||
|
||
type === 'grid' ||
|
||
type === 'showSortNum') &&
|
||
toolbarSwitchType.value === type
|
||
) {
|
||
toolbarSwitchType.value = ''
|
||
} else {
|
||
toolbarSwitchType.value = type
|
||
}
|
||
|
||
//鼠标样式的问题
|
||
if (toolbarSwitchType.value !== 'text') {
|
||
state.cursorStyle = `auto`
|
||
state.textFormToolShow = false
|
||
state.showInputBox = false
|
||
state.inputBoxValue = ''
|
||
}
|
||
|
||
if (
|
||
toolbarSwitchType.value === 'generateLine' ||
|
||
toolbarSwitchType.value === 'createLineLibrary' ||
|
||
toolbarSwitchType.value === 'createRegion' ||
|
||
toolbarSwitchType.value === 'bulkDelete'
|
||
) {
|
||
state.cursorStyle = 'crosshair'
|
||
} else if (
|
||
toolbarSwitchType.value === 'addPointOnline' ||
|
||
toolbarSwitchType.value === 'editRoute' ||
|
||
toolbarSwitchType.value === 'drawNodes' ||
|
||
toolbarSwitchType.value === 'editNode' ||
|
||
toolbarSwitchType.value === 'clickDrawRoute'
|
||
) {
|
||
state.cursorStyle = 'pointer'
|
||
} else {
|
||
state.cursorStyle = `auto`
|
||
//工具切换 不适用框选的 要把框选的信息都删掉
|
||
state.drawSelectionAreaShow = false
|
||
state.allDrawSelectionAreaBox = []
|
||
state.drawSelectionPointList = []
|
||
}
|
||
|
||
//非测距
|
||
if (toolbarSwitchType.value !== 'ranging') {
|
||
state.measureDistancesPoints = [] // 清空存储点击的点位
|
||
state.measureDistancesNum = 0 // 清空存储两点之间的距离
|
||
}
|
||
|
||
//禁止操作 在框选测距等操作时,禁止所有拖动等操作
|
||
if (
|
||
toolbarSwitchType.value === 'drawNodes' ||
|
||
toolbarSwitchType.value === 'editNode' ||
|
||
toolbarSwitchType.value === 'clickDrawRoute' ||
|
||
toolbarSwitchType.value === 'drawRoute' ||
|
||
toolbarSwitchType.value === 'createLineLibrary' ||
|
||
toolbarSwitchType.value === 'createRegion' ||
|
||
toolbarSwitchType.value === 'bulkDelete' ||
|
||
toolbarSwitchType.value === 'editRoute' ||
|
||
toolbarSwitchType.value === 'generateLine'
|
||
) {
|
||
state.prohibitedOperation = true
|
||
} else {
|
||
state.prohibitedOperation = false
|
||
}
|
||
|
||
switch (type) {
|
||
case 'open':
|
||
// 打开
|
||
break
|
||
case 'save':
|
||
//保存
|
||
await message.confirm('请确认是否保存?')
|
||
saveMap()
|
||
break
|
||
case 'saveAs':
|
||
//另存为 存为json文件 无法直接访问用户的文件系统 不能选择存在那个文件夹里面
|
||
const jsonString = JSON.stringify(state.allHistoryList[state.currentIndex], null, 2)
|
||
const blob = new Blob([jsonString], { type: 'application/json' })
|
||
const url = URL.createObjectURL(blob)
|
||
|
||
const a = document.createElement('a')
|
||
a.href = url
|
||
a.download = 'mapJson.json'
|
||
document.body.appendChild(a)
|
||
a.click()
|
||
document.body.removeChild(a)
|
||
URL.revokeObjectURL(url)
|
||
|
||
message.success('下载成功')
|
||
break
|
||
case 'choose':
|
||
//选择
|
||
break
|
||
case 'move':
|
||
//移动
|
||
console.log(state.allMapPointInfo[state.currentItemIndex])
|
||
state.moveForm.locationX = Number(state.allMapPointInfo[state.currentItemIndex].locationX)
|
||
state.moveForm.locationY = Number(state.allMapPointInfo[state.currentItemIndex].locationY)
|
||
break
|
||
case 'revolve':
|
||
state.rotationForm.angle = Number(state.allMapPointInfo[state.currentItemIndex]?.angle) || 0
|
||
break
|
||
case 'copy':
|
||
//复制
|
||
replicationNode()
|
||
break
|
||
case 'paste':
|
||
//粘贴
|
||
pasteNode()
|
||
break
|
||
case 'delete':
|
||
//删除
|
||
if (state.currentItemIndex !== -1) {
|
||
let deleteId = state.allMapPointInfo[state.currentItemIndex].id
|
||
// 点删除之后 跟点相关的路线都要删除
|
||
state.mapRouteList = state.mapRouteList.filter(
|
||
(item) => item.startingPointId !== deleteId && item.endPointId !== deleteId
|
||
)
|
||
state.allMapPointInfo.splice(state.currentItemIndex, 1)
|
||
//恢复index
|
||
state.currentItemIndex = -1
|
||
}
|
||
if (state.currentDragTarget.index !== null) {
|
||
state.mapRouteList.splice(state.currentDragTarget.index, 1)
|
||
state.currentDragTarget.index = null
|
||
}
|
||
addEditHistory()
|
||
setTimeout(() => {
|
||
toolbarSwitchType.value = ''
|
||
}, 200)
|
||
break
|
||
case 'tools':
|
||
//工具
|
||
if (toolbarSwitchType.value === 'tools') {
|
||
state.isShowToolbar = true
|
||
item.isActive = true
|
||
} else {
|
||
state.isShowToolbar = false
|
||
item.isActive = false
|
||
}
|
||
break
|
||
case 'lineLibrary':
|
||
// 线库
|
||
break
|
||
case 'region':
|
||
// 区域
|
||
break
|
||
case 'createLineLibrary':
|
||
// 生成线库
|
||
state.isShowSortNum = false
|
||
state.rightToolList.forEach((item) => {
|
||
if (item.switchType === 'showSortNum') {
|
||
item.isActive = false
|
||
}
|
||
})
|
||
break
|
||
case 'createRegion':
|
||
// 生成区域
|
||
break
|
||
case 'lineLibraryManage':
|
||
// 线库管理
|
||
removeEventListener() //移除监听
|
||
lineLibraryManagementDialogRef.value.open()
|
||
break
|
||
case 'regionManage':
|
||
removeEventListener() //移除监听
|
||
itemAreaManagementDialogRef.value.open()
|
||
// 区域管理
|
||
break
|
||
case 'text':
|
||
//文字
|
||
if (toolbarSwitchType.value === 'text') {
|
||
state.textFormToolShow = true
|
||
state.cursorStyle = `url('${cursorCollection.input}'),auto`
|
||
} else {
|
||
state.cursorStyle = `auto`
|
||
state.textFormToolShow = false
|
||
state.showInputBox = false
|
||
state.inputBoxValue = ''
|
||
}
|
||
break
|
||
case 'ranging':
|
||
// 测距
|
||
break
|
||
case 'layer':
|
||
//图层
|
||
if (toolbarSwitchType.value === 'layer') {
|
||
state.isShowLayer = true
|
||
item.isActive = true
|
||
} else {
|
||
state.isShowLayer = false
|
||
item.isActive = false
|
||
}
|
||
break
|
||
case 'marker':
|
||
// 标记
|
||
mapMark()
|
||
break
|
||
case 'grid':
|
||
//网格
|
||
if (toolbarSwitchType.value === 'grid') {
|
||
state.isShowGrid = true
|
||
item.isActive = true
|
||
} else {
|
||
state.isShowGrid = false
|
||
item.isActive = false
|
||
}
|
||
break
|
||
case 'larger':
|
||
//放大
|
||
if (state.imageChangeMultiple < 4) {
|
||
state.imageChangeMultiple += 0.2
|
||
} else {
|
||
message.warning('不能在放大了')
|
||
}
|
||
break
|
||
case 'smaller':
|
||
//缩小
|
||
if (state.imageChangeMultiple > 0.2) {
|
||
state.imageChangeMultiple -= 0.2
|
||
} else {
|
||
message.warning('不能在缩小了')
|
||
}
|
||
break
|
||
case 'withdraw':
|
||
//上一步
|
||
backPreviousStep()
|
||
break
|
||
case 'next':
|
||
//下一步
|
||
backNextStep()
|
||
break
|
||
case 'drawNodes':
|
||
//新增节点
|
||
break
|
||
case 'editNode':
|
||
// 编辑节点
|
||
break
|
||
case 'drawRoute':
|
||
//新增路线
|
||
break
|
||
case 'editRoute':
|
||
// 编辑路线
|
||
|
||
break
|
||
case 'equipment':
|
||
// 设备
|
||
let equipmentList = state.allMapPointInfo.filter((item) => {
|
||
return item.type === 3
|
||
})
|
||
removeEventListener() //移除监听
|
||
equipmentToolDialogRef.value.open(equipmentList)
|
||
break
|
||
case 'generateLine':
|
||
// 生成直线
|
||
break
|
||
case 'bulkDelete':
|
||
// 批量删除
|
||
break
|
||
case 'showSortNum':
|
||
// 显示节点序号
|
||
//网格
|
||
if (toolbarSwitchType.value === 'showSortNum') {
|
||
state.isShowSortNum = true
|
||
item.isActive = true
|
||
} else {
|
||
state.isShowSortNum = false
|
||
item.isActive = false
|
||
}
|
||
break
|
||
}
|
||
}
|
||
|
||
//复制
|
||
const replicationNode = () => {
|
||
let copyMapItem = state.allMapPointInfo[state.currentItemIndex]
|
||
|
||
if (copyMapItem.type === 3) {
|
||
message.warning('设备不能复制!')
|
||
return
|
||
}
|
||
|
||
let locationX = Number(copyMapItem.locationX) + 20
|
||
let locationY = Number(copyMapItem.locationY) + 20
|
||
let actualPoint = disposeEventPoint(locationX, locationY)
|
||
if (copyMapItem.dataList.length > 0) {
|
||
copyMapItem.dataList.forEach((item) => {
|
||
delete item.id
|
||
delete item.locationNo
|
||
delete item.mapItemId
|
||
})
|
||
copyMapItem.dataJson = JSON.stringify(copyMapItem.dataList)
|
||
}
|
||
|
||
if (copyMapItem.dataObj && JSON.stringify(copyMapItem.dataObj) !== '{}') {
|
||
delete copyMapItem.dataObj.id
|
||
delete copyMapItem.dataObj.positionMapItemId
|
||
copyMapItem.dataJson = JSON.stringify(copyMapItem.dataObj)
|
||
}
|
||
state.copyMapItem = {
|
||
positionMapId: copyMapItem.positionMapId,
|
||
locationX: locationX,
|
||
locationY: locationY,
|
||
actualLocationX: actualPoint.actualLocationX,
|
||
actualLocationY: actualPoint.actualLocationY,
|
||
type: copyMapItem.type,
|
||
dataJson: copyMapItem.dataJson || '',
|
||
dataObj: copyMapItem.dataObj || {},
|
||
dataList: copyMapItem.dataList || [],
|
||
locationDeep: copyMapItem.locationDeep,
|
||
locationWide: copyMapItem.locationWide,
|
||
locationDeepPx: copyMapItem.locationDeepPx,
|
||
locationWidePx: copyMapItem.locationWidePx,
|
||
draggable: copyMapItem.draggable,
|
||
resizable: copyMapItem.resizable,
|
||
rotatable: copyMapItem.rotatable,
|
||
lockAspectRatio: copyMapItem.lockAspectRatio,
|
||
layerSelectionShow: copyMapItem.layerSelectionShow,
|
||
mapImageUrl: copyMapItem.mapImageUrl,
|
||
locationYaw: copyMapItem.locationYaw,
|
||
areaId: null,
|
||
locationNumber: null
|
||
}
|
||
message.success('复制成功')
|
||
}
|
||
//粘贴
|
||
const pasteNode = () => {
|
||
let copyObj = JSON.parse(JSON.stringify(state.copyMapItem))
|
||
state.allMapPointInfo.push(copyObj)
|
||
message.success('粘贴成功')
|
||
addEditHistory()
|
||
}
|
||
//移动工具表单提交
|
||
const moveFormSubmit = async () => {
|
||
state.allMapPointInfo[state.currentItemIndex].locationX = Number(state.moveForm.locationX)
|
||
state.allMapPointInfo[state.currentItemIndex].locationY = Number(state.moveForm.locationY)
|
||
addEditHistory()
|
||
}
|
||
//旋转工具表单提交
|
||
const rotationFormSubmit = () => {
|
||
state.allMapPointInfo[state.currentItemIndex].angle = state.rotationForm.angle
|
||
addEditHistory()
|
||
}
|
||
|
||
//标记
|
||
const mapMark = async () => {
|
||
state.mapMarkCarList = await MapApi.getListByMapId(imgBgObj.positionMapId)
|
||
|
||
if (state.currentItemIndex != -1) {
|
||
let item = state.allMapPointInfo[state.currentItemIndex]
|
||
state.markForm.originalNode = `[${item.locationX},${item.locationY}]`
|
||
if (item.type == 1) {
|
||
state.markForm.markProperty = '路径点'
|
||
} else if (item.type == 2) {
|
||
state.markForm.markProperty = '库位点'
|
||
} else if (item.type == 3) {
|
||
state.markForm.markProperty = '设备点'
|
||
} else if (item.type == 4) {
|
||
state.markForm.markProperty = '停车点'
|
||
} else if (item.type == 5) {
|
||
state.markForm.markProperty = '区域变更点'
|
||
} else if (item.type == 6) {
|
||
state.markForm.markProperty = '等待点'
|
||
}
|
||
} else {
|
||
state.markForm.markProperty = ''
|
||
state.markForm.originalNode = ''
|
||
}
|
||
state.popoverVisible = true
|
||
}
|
||
//标记提交
|
||
const macAddressChange = (e) => {
|
||
const targetItem = state.mapMarkCarList.find((item) => item.macAddress === e)
|
||
if (targetItem) {
|
||
state.markForm.robotNo = targetItem.robotNo
|
||
}
|
||
}
|
||
//取消标记
|
||
const markFormCancel = () => {
|
||
state.popoverVisible = false
|
||
state.markForm.markProperty = ''
|
||
state.markForm.originalNode = ''
|
||
state.markForm.macAddress = ''
|
||
state.markForm.robotNo = ''
|
||
}
|
||
//确认标记
|
||
const markFormSubmit = async () => {
|
||
if (!state.markForm.macAddress) {
|
||
message.warning('请选择车辆')
|
||
return
|
||
}
|
||
|
||
// 格式是 '{"area":"E区","batSoc":"80","floor":"3","robotNo":"100","x":"-1.720683217048645","y":"-14.308184623718262","yaw":"-3.0042707920074463"}'
|
||
let res = await MapApi.getAGVPointInformation(state.markForm.macAddress)
|
||
if (res) {
|
||
//点
|
||
let point = JSON.parse(res)
|
||
let pointPx = convertActualToBrowser(point.x, point.y)
|
||
|
||
if (state.currentItemIndex !== -1) {
|
||
state.allMapPointInfo[state.currentItemIndex].locationYaw = point.yaw
|
||
state.allMapPointInfo[state.currentItemIndex].locationX = pointPx.x
|
||
state.allMapPointInfo[state.currentItemIndex].locationY = pointPx.y
|
||
state.allMapPointInfo[state.currentItemIndex].actualLocationX = point.x
|
||
state.allMapPointInfo[state.currentItemIndex].actualLocationY = point.y
|
||
|
||
//更改路线里的
|
||
let item = state.allMapPointInfo[state.currentItemIndex]
|
||
state.mapRouteList.forEach((route) => {
|
||
if (item.id === route.startingPointId) {
|
||
route.startPointX = pointPx.x
|
||
route.startPointY = pointPx.y
|
||
route.beginHigh = Number(item.locationDeepPx)
|
||
route.beginWidth = Number(item.locationWidePx)
|
||
route.actualStartPointX = point.x
|
||
route.actualStartPointY = point.y
|
||
}
|
||
if (item.id === route.endPointId) {
|
||
route.endPointX = pointPx.x
|
||
route.endPointY = pointPx.y
|
||
route.endHigh = Number(item.locationDeepPx)
|
||
route.endWidth = Number(item.locationWidePx)
|
||
route.actualEndPointX = point.x
|
||
route.actualEndPointY = point.y
|
||
}
|
||
})
|
||
|
||
addEditHistory()
|
||
} else {
|
||
//新增一个节点
|
||
state.allMapPointInfo.push({
|
||
positionMapId: imgBgObj.positionMapId, //地图的id
|
||
layerSelectionShow: true,
|
||
locationX: pointPx.x,
|
||
locationY: pointPx.y,
|
||
actualLocationX: point.x,
|
||
actualLocationY: point.y,
|
||
locationDeep: 40,
|
||
locationWide: 40,
|
||
locationDeepPx: 8,
|
||
locationWidePx: 8,
|
||
angle: 0,
|
||
draggable: true,
|
||
resizable: true,
|
||
rotatable: false,
|
||
lockAspectRatio: false, //横纵比
|
||
mapImageUrl: '',
|
||
type: 1, //默认类型1 路径节点
|
||
dataList: [], //存库位的
|
||
dataObj: {}, //存 设备点 停车点 文字
|
||
locationYaw: point.yaw //弧度
|
||
})
|
||
message.success('标记成功')
|
||
addEditHistory()
|
||
}
|
||
} else {
|
||
message.warning('未采集到该AGV点位信息')
|
||
}
|
||
state.popoverVisible = false
|
||
}
|
||
|
||
//鼠标拖动绘制节点
|
||
// 从点开始绘制
|
||
const startFromPoint = (index, event) => {
|
||
if (toolbarSwitchType.value === 'clickDrawRoute') {
|
||
let list = state.allMapPointInfo
|
||
const point = list[index]
|
||
if (point.id) {
|
||
state.startDrawPoint = point //开始点
|
||
state.startDrawPointIndex = index
|
||
state.isDrawing = true
|
||
event.preventDefault() // 防止默认行为
|
||
} else {
|
||
message.warning('选择的节点未保存')
|
||
// 重置状态
|
||
state.startDrawPointIndex = -1 // 起始点的索引
|
||
state.startDrawPoint = null
|
||
state.isDrawing = false
|
||
state.currentDrawX = 0
|
||
state.currentDrawY = 0
|
||
}
|
||
}
|
||
}
|
||
//开始框选绘制
|
||
const startDrawSelection = (event) => {
|
||
if (
|
||
toolbarSwitchType.value === 'createLineLibrary' ||
|
||
toolbarSwitchType.value === 'createRegion' ||
|
||
toolbarSwitchType.value === 'drawRoute' ||
|
||
toolbarSwitchType.value == 'generateLine' ||
|
||
toolbarSwitchType.value === 'bulkDelete'
|
||
) {
|
||
const backgroundRect = mapBackgroundRef.value.getBoundingClientRect()
|
||
|
||
const x = disposeEventPoints(event).x
|
||
const y = disposeEventPoints(event).y
|
||
|
||
// 确保点击在背景区域内
|
||
if (x >= 0 && x <= backgroundRect.width && y >= 0 && y <= backgroundRect.height) {
|
||
state.drawSelectionAreaShow = true
|
||
state.drawSelectionStartPoint = { x: x, y: y }
|
||
state.drawSelectionAreaBox = { x: x, y: y, width: 0, height: 0 }
|
||
}
|
||
event.preventDefault() // 阻止默认行为(避免选中图片或文本)
|
||
}
|
||
}
|
||
// 更新框选区域
|
||
const updateDrawSelection = (event) => {
|
||
if (
|
||
toolbarSwitchType.value === 'createLineLibrary' ||
|
||
toolbarSwitchType.value === 'createRegion' ||
|
||
toolbarSwitchType.value === 'drawRoute' ||
|
||
toolbarSwitchType.value === 'generateLine' ||
|
||
toolbarSwitchType.value === 'bulkDelete'
|
||
) {
|
||
if (state.drawSelectionAreaShow) {
|
||
const x = disposeEventPoints(event).x
|
||
const y = disposeEventPoints(event).y
|
||
|
||
state.drawSelectionAreaBox = {
|
||
x: Math.min(state.drawSelectionStartPoint.x, x),
|
||
y: Math.min(state.drawSelectionStartPoint.y, y),
|
||
width: Math.abs(Number(x) - Number(state.drawSelectionStartPoint.x)),
|
||
height: Math.abs(Number(y) - Number(state.drawSelectionStartPoint.y))
|
||
}
|
||
}
|
||
event.preventDefault() // 阻止默认行为(避免选中图片或文本)
|
||
}
|
||
if (toolbarSwitchType.value === 'clickDrawRoute') {
|
||
// 实时绘制
|
||
if (state.isDrawing) {
|
||
const x = disposeEventPoints(event).x
|
||
const y = disposeEventPoints(event).y
|
||
|
||
state.currentDrawX = x
|
||
state.currentDrawY = y
|
||
}
|
||
event.preventDefault() // 阻止默认行为(避免选中图片或文本)
|
||
}
|
||
}
|
||
//结束框选绘制
|
||
const endDrawSelection = (event) => {
|
||
if (
|
||
toolbarSwitchType.value === 'createLineLibrary' ||
|
||
toolbarSwitchType.value === 'createRegion' ||
|
||
toolbarSwitchType.value === 'drawRoute' ||
|
||
toolbarSwitchType.value === 'generateLine' ||
|
||
toolbarSwitchType.value === 'bulkDelete'
|
||
) {
|
||
state.drawSelectionAreaShow = false
|
||
state.allDrawSelectionAreaBox.push({ ...state.drawSelectionAreaBox })
|
||
state.drawSelectionAreaBox = { x: 0, y: 0, width: 0, height: 0 }
|
||
event.preventDefault() // 阻止默认行为(避免选中图片或文本)
|
||
return
|
||
}
|
||
if (toolbarSwitchType.value === 'clickDrawRoute') {
|
||
if (state.isDrawing) {
|
||
// 找到最近的终点
|
||
const endPointIndex = findClosestPoint(state.currentDrawX, state.currentDrawY)
|
||
|
||
if (endPointIndex !== null && endPointIndex !== state.startDrawPointIndex) {
|
||
let list = state.allMapPointInfo
|
||
const endPoint = list[endPointIndex]
|
||
|
||
//先判断节点有没有保存
|
||
if (!endPoint.id) {
|
||
message.warning('选择的节点未保存')
|
||
// 重置状态
|
||
state.startDrawPointIndex = -1 // 起始点的索引
|
||
state.startDrawPoint = null
|
||
state.isDrawing = false
|
||
state.currentDrawX = 0
|
||
state.currentDrawY = 0
|
||
return
|
||
}
|
||
|
||
const newLine = {
|
||
startPointX: state.startDrawPoint.locationX,
|
||
startPointY: state.startDrawPoint.locationY,
|
||
endPointX: endPoint.locationX,
|
||
endPointY: endPoint.locationY
|
||
}
|
||
// 检查是否已存在相同的直线
|
||
const isDuplicate = state.mapRouteList.some(
|
||
(line) =>
|
||
(line.startPointX === newLine.startPointX &&
|
||
line.startPointY === newLine.startPointY &&
|
||
line.endPointX === newLine.endPointX &&
|
||
line.endPointY === newLine.endPointY) ||
|
||
(line.startPointX === newLine.endPointX &&
|
||
line.startPointY === newLine.endPointY &&
|
||
line.endPointX === newLine.startPointX &&
|
||
line.endPointY === newLine.startPointY)
|
||
)
|
||
if (isDuplicate) {
|
||
message.warning('此路线已存在')
|
||
} else {
|
||
// 保存当前直线
|
||
state.mapRouteList.push({
|
||
isSelected: false, //是否选中
|
||
startingPointId: state.startDrawPoint.id,
|
||
endPointId: endPoint.id,
|
||
startPointX: state.startDrawPoint.locationX, //开始点
|
||
startPointY: state.startDrawPoint.locationY, //开始点
|
||
endPointX: endPoint.locationX, //结束点
|
||
endPointY: endPoint.locationY, //结束点
|
||
actualStartPointX: state.startDrawPoint.actualLocationX, //实际开始点位x轴
|
||
actualStartPointY: state.startDrawPoint.actualLocationY, //实际开始点位y轴
|
||
actualEndPointX: endPoint.actualLocationX, //实际结束点位x轴
|
||
actualEndPointY: endPoint.actualLocationY, //实际结束点位y轴
|
||
actualBeginControlX: '', //实际开始控制点x轴
|
||
actualBeginControlY: '', //实际开始控制点y轴
|
||
actualEndControlX: '', //实际结束控制点x轴
|
||
actualEndControlY: '', //实际结束控制点y轴
|
||
beginControlX: 0, //开始控制点x轴
|
||
beginControlY: 0, //开始控制点y轴
|
||
endControlX: 0, //结束控制点x轴
|
||
endControlY: 0, //结束控制点y轴
|
||
expansionZoneFront: 0, //膨胀区域前
|
||
expansionZoneAfter: 0, //膨胀区域后
|
||
expansionZoneLeft: 0, // 膨胀区域左
|
||
expansionZoneRight: 0, //膨胀区域右
|
||
method: 0, //行走方法 0.直线 1.上左曲线2.上右曲线3.下左曲线 4.下右曲线
|
||
direction: 2, //方向 1.单向 2.双向,
|
||
forwardSpeedLimit: 1.5, //正向限速
|
||
reverseSpeedLimit: 0.4, // 反向限速
|
||
toward: 0, // 车头朝向( 0:正正 1:正反 2:反正 3:反反)
|
||
beginWidth: state.startDrawPoint.locationWidePx, //起点宽
|
||
beginHigh: state.startDrawPoint.locationDeepPx, // 起点高
|
||
endWidth: endPoint.locationWidePx, // 终点宽
|
||
endHigh: endPoint.locationDeepPx, // 终点高
|
||
startingSortNum: state.startDrawPoint.sortNum,
|
||
endPointSortNum: endPoint.sortNum
|
||
})
|
||
addEditHistory()
|
||
}
|
||
}
|
||
// 重置状态
|
||
state.startDrawPointIndex = -1 // 起始点的索引
|
||
state.startDrawPoint = null
|
||
state.isDrawing = false
|
||
state.currentDrawX = 0
|
||
state.currentDrawY = 0
|
||
}
|
||
event.preventDefault() // 阻止默认行为(避免选中图片或文本)
|
||
return
|
||
}
|
||
}
|
||
// 找到最近的点
|
||
const findClosestPoint = (x, y) => {
|
||
let minDistance = Infinity
|
||
let closestIndex = null
|
||
let list = state.allMapPointInfo
|
||
list.forEach((point, index) => {
|
||
const distance = Math.sqrt((point.locationX - x) ** 2 + (point.locationY - y) ** 2)
|
||
if (distance < minDistance && distance < point.locationWide) {
|
||
// 10 是点的捕捉范围
|
||
minDistance = distance
|
||
closestIndex = index
|
||
}
|
||
})
|
||
return closestIndex
|
||
}
|
||
//点击区域
|
||
const clickDrawSelectionArea = () => {
|
||
let points = state.allMapPointInfo
|
||
|
||
state.drawSelectionPointList = []
|
||
state.allDrawSelectionAreaBox.forEach((box) => {
|
||
points.forEach((point) => {
|
||
if (
|
||
point.locationX >= box.x &&
|
||
point.locationX <= box.x + box.width &&
|
||
point.locationY >= box.y &&
|
||
point.locationY <= box.y + box.height
|
||
) {
|
||
state.drawSelectionPointList.push(point)
|
||
}
|
||
})
|
||
})
|
||
|
||
// 清空框选区域
|
||
state.allDrawSelectionAreaBox = []
|
||
//去重
|
||
state.drawSelectionPointList = deduplicateArrayById(state.drawSelectionPointList)
|
||
//只要库位的
|
||
let binLocation = state.drawSelectionPointList.filter((item) => item.type === 2)
|
||
//所以类型的
|
||
let routeList = state.drawSelectionPointList
|
||
|
||
let isHaveId = binLocation.every((item) => {
|
||
item.id
|
||
})
|
||
|
||
//线库
|
||
if (toolbarSwitchType.value === 'createLineLibrary') {
|
||
//线库
|
||
if (binLocation.length < 2) {
|
||
message.warning('至少选择两个库位')
|
||
return
|
||
}
|
||
|
||
// if (!isStraightLine(binLocation)) {
|
||
// message.warning('您选择的库位不在一条直线上')
|
||
// return
|
||
// }
|
||
|
||
let isHaveId = binLocation.every((item) => {
|
||
return item.id
|
||
})
|
||
if (!isHaveId) {
|
||
message.warning('您选择的库位存在未保存的')
|
||
return
|
||
}
|
||
|
||
//判断线库的排序是不是一样
|
||
const locationNumbers = findDuplicateLocationIds(binLocation)
|
||
if (locationNumbers.length > 0) {
|
||
message.warning('您选择的库位的序号不能重复')
|
||
return
|
||
}
|
||
|
||
removeEventListener() //移除监听
|
||
lineLibrarySettingDialogRef.value.open(binLocation)
|
||
}
|
||
//区域
|
||
if (toolbarSwitchType.value === 'createRegion') {
|
||
if (binLocation.length < 1) {
|
||
message.warning('至少选择两个库位')
|
||
return
|
||
}
|
||
let isHaveId = binLocation.every((item) => {
|
||
return item.id
|
||
})
|
||
if (!isHaveId) {
|
||
message.warning('您选择的库位存在未保存的')
|
||
return
|
||
}
|
||
removeEventListener() //移除监听
|
||
itemAreaSettingDialogRef.value.open(binLocation)
|
||
}
|
||
//绘制直线
|
||
if (toolbarSwitchType.value === 'drawRoute') {
|
||
if (routeList.length !== 2) {
|
||
message.warning('只能选择两个路径点')
|
||
return
|
||
}
|
||
let isHaveId = routeList.every((item) => {
|
||
return item.id
|
||
})
|
||
if (!isHaveId) {
|
||
message.warning('您选择的路径点存在未保存的')
|
||
return
|
||
}
|
||
let curve = {
|
||
isSelected: false, //是否选中
|
||
startingPointId: routeList[0].id,
|
||
endPointId: routeList[1].id,
|
||
startPointX: routeList[0].locationX, //开始点
|
||
startPointY: routeList[0].locationY, //开始点
|
||
endPointX: routeList[1].locationX, //结束点
|
||
endPointY: routeList[1].locationY, //结束点
|
||
actualStartPointX: routeList[0].actualLocationX, //实际开始点位x轴
|
||
actualStartPointY: routeList[0].actualLocationY, //实际开始点位y轴
|
||
actualEndPointX: routeList[1].actualLocationX, //实际结束点位x轴
|
||
actualEndPointY: routeList[1].actualLocationY, //实际结束点位y轴
|
||
actualBeginControlX: '', //实际开始控制点x轴
|
||
actualBeginControlY: '', //实际开始控制点y轴
|
||
actualEndControlX: '', //实际结束控制点x轴
|
||
actualEndControlY: '', //实际结束控制点y轴
|
||
beginControlX: 0, //开始控制点x轴
|
||
beginControlY: 0, //开始控制点y轴
|
||
endControlX: 0, //结束控制点x轴
|
||
endControlY: 0, //结束控制点y轴
|
||
expansionZoneFront: 0, //膨胀区域前
|
||
expansionZoneAfter: 0, //膨胀区域后
|
||
expansionZoneLeft: 0, // 膨胀区域左
|
||
expansionZoneRight: 0, //膨胀区域右
|
||
method: 0, //行走方法 0.直线 1.上左曲线2.上右曲线3.下左曲线 4.下右曲线
|
||
direction: 2, //方向 1.单向 2.双向,
|
||
forwardSpeedLimit: 1.5, //正向限速
|
||
reverseSpeedLimit: 0.4, // 反向限速
|
||
toward: 0, // 车头朝向( 0:正正 1:正反 2:反正 3:反反)
|
||
beginWidth: routeList[0].locationWidePx, //起点宽
|
||
beginHigh: routeList[0].locationDeepPx, // 起点高
|
||
endWidth: routeList[1].locationWidePx, // 终点宽
|
||
endHigh: routeList[1].locationDeepPx // 终点高
|
||
}
|
||
state.selectedCurve = curve
|
||
state.mapRouteList.push(curve)
|
||
addEditHistory()
|
||
}
|
||
//生成直线
|
||
if (toolbarSwitchType.value === 'generateLine') {
|
||
if (routeList.length < 3) {
|
||
message.warning('至少框选三个点')
|
||
return
|
||
}
|
||
let isHaveId = routeList.every((item) => {
|
||
return item.id
|
||
})
|
||
if (!isHaveId) {
|
||
message.warning('您选择的路径点存在未保存的')
|
||
return
|
||
}
|
||
GenerateStraightLinesDialogRef.value.open(routeList)
|
||
}
|
||
//批量删除
|
||
if (toolbarSwitchType.value === 'bulkDelete') {
|
||
if (routeList.length < 1) {
|
||
message.warning('至少选择一个点')
|
||
return
|
||
}
|
||
//删除的节点id合集
|
||
const routeIds = routeList.map((route) => route.id)
|
||
//在数组中去除这些节点
|
||
state.allMapPointInfo = state.allMapPointInfo.filter((item) => {
|
||
return !routeIds.includes(item.id)
|
||
})
|
||
//将路线中的删除
|
||
state.mapRouteList = state.mapRouteList.filter((item) => {
|
||
return !routeIds.includes(item.startingPointId) && !routeIds.includes(item.endPointId)
|
||
})
|
||
state.currentItemIndex = -1
|
||
addEditHistory()
|
||
}
|
||
}
|
||
//生成直线 选择完成开始点和结束点
|
||
const GenerateStraightLinesSubmit = (pointList, form) => {
|
||
const list = mapPointsToLine(pointList, form.startPointId, form.endPointId)
|
||
const idNameMap = {}
|
||
list.forEach((item) => {
|
||
idNameMap[item.id] = item
|
||
})
|
||
// 遍历第二个数组,更新 name
|
||
state.allMapPointInfo.forEach((item) => {
|
||
if (idNameMap[item.id]) {
|
||
let actualPoint = disposeEventPoint(
|
||
idNameMap[item.id].locationX,
|
||
idNameMap[item.id].locationY
|
||
)
|
||
item.locationX = idNameMap[item.id].locationX
|
||
item.locationY = idNameMap[item.id].locationY
|
||
item.actualLocationX = actualPoint.actualLocationX
|
||
item.actualLocationY = actualPoint.actualLocationY
|
||
}
|
||
})
|
||
state.mapRouteList.forEach((item) => {
|
||
if (idNameMap[item.startingPointId]) {
|
||
let actualPoint = disposeEventPoint(
|
||
idNameMap[item.startingPointId].locationX,
|
||
idNameMap[item.startingPointId].locationY
|
||
)
|
||
item.startPointX = idNameMap[item.startingPointId].locationX
|
||
item.startPointY = idNameMap[item.startingPointId].locationY
|
||
item.actualStartPointX = actualPoint.actualLocationX
|
||
item.actualStartPointY = actualPoint.actualLocationY
|
||
}
|
||
if (idNameMap[item.endPointId]) {
|
||
let actualPoint = disposeEventPoint(
|
||
idNameMap[item.endPointId].locationX,
|
||
idNameMap[item.endPointId].locationY
|
||
)
|
||
item.endPointX = idNameMap[item.endPointId].locationX
|
||
item.endPointY = idNameMap[item.endPointId].locationY
|
||
item.actualEndPointX = actualPoint.actualLocationX
|
||
item.actualEndPointY = actualPoint.actualLocationY
|
||
}
|
||
})
|
||
addEditHistory()
|
||
}
|
||
//去重
|
||
const deduplicateArrayById = (arr) => {
|
||
const idSet = new Set()
|
||
return arr.filter((item) => {
|
||
if (idSet.has(item.id)) {
|
||
return false
|
||
}
|
||
idSet.add(item.id)
|
||
return true
|
||
})
|
||
}
|
||
//返回生成线库时 排序相同项的id
|
||
const findDuplicateLocationIds = (data) => {
|
||
const locationMap = {}
|
||
|
||
data.forEach((item) => {
|
||
if (!locationMap[item.locationNumber]) {
|
||
locationMap[item.locationNumber] = []
|
||
}
|
||
locationMap[item.locationNumber].push(item.id)
|
||
})
|
||
|
||
return Object.values(locationMap).filter((ids) => ids.length > 1)
|
||
}
|
||
|
||
//将一个数组中的点位 按照第一个和最后一个排成一条直线
|
||
const mapPointsToLine = (points, startPointId, endPointId) => {
|
||
const startPoint = points.find((point) => point.id === startPointId)
|
||
const endPoint = points.find((point) => point.id === endPointId)
|
||
|
||
if (!startPoint || !endPoint) {
|
||
message.warning('选择的点位有误')
|
||
return
|
||
}
|
||
|
||
const dx = endPoint.locationX - startPoint.locationX
|
||
const dy = endPoint.locationY - startPoint.locationY
|
||
const length = Math.sqrt(dx * dx + dy * dy)
|
||
|
||
return points.map((point) => {
|
||
if (point.id === startPointId || point.id === endPointId) {
|
||
return point
|
||
}
|
||
|
||
const vx = point.locationX - startPoint.locationX
|
||
const vy = point.locationY - startPoint.locationY
|
||
const dotProduct = vx * dx + vy * dy
|
||
const projectionLength = dotProduct / length
|
||
|
||
const t = Math.max(0, Math.min(1, projectionLength / length))
|
||
|
||
const newX = startPoint.locationX + t * dx
|
||
const newY = startPoint.locationY + t * dy
|
||
|
||
return {
|
||
...point,
|
||
locationX: newX,
|
||
locationY: newY
|
||
}
|
||
})
|
||
}
|
||
|
||
//计算是不是在同一条直线的
|
||
const isStraightLine = (binLocation) => {
|
||
if (binLocation.length <= 2) {
|
||
return true // 两个点一定在一条直线上
|
||
}
|
||
|
||
const firstPoint = binLocation[0]
|
||
const secondPoint = binLocation[1]
|
||
|
||
// 检查是否垂直直线(所有 x 相同)
|
||
const isVertical = binLocation.every(
|
||
(point) => Number(point.locationX) === Number(firstPoint.locationX)
|
||
)
|
||
if (isVertical) {
|
||
return true
|
||
}
|
||
|
||
// 检查是否水平直线(所有 y 相同)
|
||
const isHorizontal = binLocation.every(
|
||
(point) => Number(point.locationY) === Number(firstPoint.locationY)
|
||
)
|
||
if (isHorizontal) {
|
||
return true
|
||
}
|
||
|
||
// 计算斜率
|
||
const slope =
|
||
Number(secondPoint.locationY) -
|
||
Number(firstPoint.locationY) / (Number(secondPoint.locationX) - Number(firstPoint.locationX))
|
||
|
||
// 检查所有点是否在同一条斜线上
|
||
return binLocation.every((point) => {
|
||
const currentSlope =
|
||
(Number(point.locationY) - Number(firstPoint.locationY)) /
|
||
(Number(point.locationX) - Number(firstPoint.locationX))
|
||
return Math.abs(currentSlope - slope) < Number.EPSILON // 处理浮点数精度问题
|
||
})
|
||
}
|
||
//三阶贝塞尔曲线
|
||
// 开始拖拽
|
||
const startDrag = (item, index, type) => {
|
||
state.currentDragTarget = { index, type }
|
||
window.addEventListener('mousemove', handleDrag)
|
||
window.addEventListener('mouseup', endDrag)
|
||
}
|
||
// 处理拖动事件
|
||
const handleDrag = (event) => {
|
||
let x = disposeEventPoints(event).x
|
||
let y = disposeEventPoints(event).y
|
||
|
||
if (state.currentDragTarget.index !== null) {
|
||
const curve = state.mapRouteList[state.currentDragTarget.index]
|
||
|
||
// 确保控制点不超出盒子范围
|
||
x = Math.max(0, Math.min(x, imgBgObj.width))
|
||
y = Math.max(0, Math.min(y, imgBgObj.height))
|
||
|
||
if (state.currentDragTarget.type === 'start') {
|
||
curve.beginControlX = x
|
||
curve.beginControlY = y
|
||
} else {
|
||
curve.endControlX = x
|
||
curve.endControlY = y
|
||
}
|
||
}
|
||
}
|
||
// 结束拖动
|
||
const endDrag = (event) => {
|
||
const curve = state.mapRouteList[state.currentDragTarget.index]
|
||
let actualBeginControl = disposeEventPoint(curve.beginControlX, curve.beginControlY)
|
||
let actualEndControl = disposeEventPoint(curve.endControlX, curve.endControlY)
|
||
|
||
curve.actualBeginControlX = actualBeginControl.actualLocationX
|
||
curve.actualBeginControlY = actualBeginControl.actualLocationY
|
||
|
||
curve.actualEndControlX = actualEndControl.actualLocationX
|
||
curve.actualEndControlY = actualEndControl.actualLocationY
|
||
|
||
addEditHistory()
|
||
|
||
state.currentDragTarget.type = null
|
||
window.removeEventListener('mousemove', handleDrag)
|
||
window.removeEventListener('mouseup', endDrag)
|
||
}
|
||
// 获取曲线的路径
|
||
const getCurvePath = (curve) => {
|
||
let startPointX = Number(curve.startPointX)
|
||
let startPointY = Number(curve.startPointY)
|
||
let endPointX = Number(curve.endPointX)
|
||
let endPointY = Number(curve.endPointY)
|
||
let path = `M ${startPointX} ${startPointY} C ${curve.beginControlX} ${curve.beginControlY}, ${curve.endControlX} ${curve.endControlY}, ${endPointX} ${endPointY}`
|
||
|
||
curve.curvePath = path
|
||
return path
|
||
}
|
||
// 编辑路线
|
||
const handleChooseRoute = async (item, index, type, e) => {
|
||
if (toolbarSwitchType.value === 'addPointOnline') {
|
||
if (!item.id) {
|
||
message.warning('该路线未保存')
|
||
return
|
||
}
|
||
//直线情况下 线上加点
|
||
if (type === 'line') {
|
||
const id = await MapApi.getNodeId()
|
||
const pageX = disposeEventPoints(e).x
|
||
const pageY = disposeEventPoints(e).y
|
||
const x = mapPointToLineSegment(item, pageX, pageY).newX
|
||
const y = mapPointToLineSegment(item, pageX, pageY).newY
|
||
const actualLocationX = disposeEventPoint(x, y).actualLocationX
|
||
const actualLocationY = disposeEventPoint(x, y).actualLocationY
|
||
|
||
let newPoint = {
|
||
id: id,
|
||
positionMapId: imgBgObj.positionMapId, //地图的id
|
||
layerSelectionShow: true,
|
||
locationX: x,
|
||
locationY: y,
|
||
actualLocationX: actualLocationX,
|
||
actualLocationY: actualLocationY,
|
||
locationDeep: 40,
|
||
locationWide: 40,
|
||
locationDeepPx: 8,
|
||
locationWidePx: 8,
|
||
angle: 0,
|
||
draggable: true,
|
||
resizable: true,
|
||
rotatable: false,
|
||
lockAspectRatio: false, //横纵比
|
||
mapImageUrl: '',
|
||
locationYaw: 0, //弧度
|
||
type: 1, //默认类型1 路径节点
|
||
dataList: [], //存库位的
|
||
dataObj: {} //存 设备点 停车点 文字
|
||
}
|
||
let positionMapLineOne = {
|
||
isSelected: false, //是否选中
|
||
startingPointId: item.startingPointId,
|
||
endPointId: id,
|
||
startPointX: item.startPointX, //开始点
|
||
startPointY: item.startPointY, //开始点
|
||
endPointX: x, //结束点
|
||
endPointY: y, //结束点
|
||
actualStartPointX: item.actualStartPointX, //实际开始点位x轴
|
||
actualStartPointY: item.actualStartPointY, //实际开始点位y轴
|
||
actualEndPointX: actualLocationX, //实际结束点位x轴
|
||
actualEndPointY: actualLocationY, //实际结束点位y轴
|
||
actualBeginControlX: '', //实际开始控制点x轴
|
||
actualBeginControlY: '', //实际开始控制点y轴
|
||
actualEndControlX: '', //实际结束控制点x轴
|
||
actualEndControlY: '', //实际结束控制点y轴
|
||
beginControlX: 0, //开始控制点x轴
|
||
beginControlY: 0, //开始控制点y轴
|
||
endControlX: 0, //结束控制点x轴
|
||
endControlY: 0, //结束控制点y轴
|
||
expansionZoneFront: 0, //膨胀区域前
|
||
expansionZoneAfter: 0, //膨胀区域后
|
||
expansionZoneLeft: 0, // 膨胀区域左
|
||
expansionZoneRight: 0, //膨胀区域右
|
||
method: 0, //行走方法 0.直线 1.上左曲线2.上右曲线3.下左曲线 4.下右曲线
|
||
direction: 2, //方向 1.单向 2.双向,
|
||
forwardSpeedLimit: 1.5, //正向限速
|
||
reverseSpeedLimit: 0.4, // 反向限速
|
||
toward: 0, // 车头朝向( 0:正正 1:正反 2:反正 3:反反)
|
||
beginWidth: 8, //起点宽
|
||
beginHigh: 8, // 起点高
|
||
endWidth: 8, // 终点宽
|
||
endHigh: 8 // 终点高
|
||
}
|
||
let positionMapLineTwo = {
|
||
isSelected: false, //是否选中
|
||
startingPointId: id,
|
||
endPointId: item.endPointId,
|
||
startPointX: x, //开始点
|
||
startPointY: y, //开始点
|
||
endPointX: item.endPointX, //结束点
|
||
endPointY: item.endPointY, //结束点
|
||
actualStartPointX: actualLocationX, //实际开始点位x轴
|
||
actualStartPointY: actualLocationY, //实际开始点位y轴
|
||
actualEndPointX: item.actualEndPointX, //实际结束点位x轴
|
||
actualEndPointY: item.actualEndPointY, //实际结束点位y轴
|
||
actualBeginControlX: '', //实际开始控制点x轴
|
||
actualBeginControlY: '', //实际开始控制点y轴
|
||
actualEndControlX: '', //实际结束控制点x轴
|
||
actualEndControlY: '', //实际结束控制点y轴
|
||
beginControlX: 0, //开始控制点x轴
|
||
beginControlY: 0, //开始控制点y轴
|
||
endControlX: 0, //结束控制点x轴
|
||
endControlY: 0, //结束控制点y轴
|
||
expansionZoneFront: 0, //膨胀区域前
|
||
expansionZoneAfter: 0, //膨胀区域后
|
||
expansionZoneLeft: 0, // 膨胀区域左
|
||
expansionZoneRight: 0, //膨胀区域右
|
||
method: 0, //行走方法 0.直线 1.上左曲线2.上右曲线3.下左曲线 4.下右曲线
|
||
direction: 2, //方向 1.单向 2.双向,
|
||
forwardSpeedLimit: 1.5, //正向限速
|
||
reverseSpeedLimit: 0.4, // 反向限速
|
||
toward: 0, // 车头朝向( 0:正正 1:正反 2:反正 3:反反)
|
||
beginWidth: 8, //起点宽
|
||
beginHigh: 8, // 起点高
|
||
endWidth: 8, // 终点宽
|
||
endHigh: 8 // 终点高
|
||
}
|
||
|
||
try {
|
||
let res = await MapApi.lineAddItem([
|
||
{
|
||
...newPoint,
|
||
oldLineId: item.id,
|
||
positionMapLines: [positionMapLineOne, positionMapLineTwo]
|
||
}
|
||
])
|
||
newPoint.sortNum = res?.ITEM?.sortNum || ''
|
||
res?.LINE.forEach((lineItem) => {
|
||
if (
|
||
lineItem.startingPointId === positionMapLineOne.startingPointId &&
|
||
lineItem.endPointId === positionMapLineOne.endPointId
|
||
) {
|
||
positionMapLineOne.startingSortNum = lineItem.startingSortNum
|
||
positionMapLineOne.endPointSortNum = lineItem.endPointSortNum
|
||
}
|
||
if (
|
||
lineItem.startingPointId === positionMapLineTwo.startingPointId &&
|
||
lineItem.endPointId === positionMapLineTwo.endPointId
|
||
) {
|
||
positionMapLineTwo.startingSortNum = lineItem.startingSortNum
|
||
positionMapLineTwo.endPointSortNum = lineItem.endPointSortNum
|
||
}
|
||
})
|
||
|
||
state.allMapPointInfo.push(newPoint)
|
||
state.mapRouteList.push(positionMapLineOne)
|
||
state.mapRouteList.push(positionMapLineTwo)
|
||
state.mapRouteList.splice(index, 1)
|
||
state.allHistoryList[0] = {
|
||
allMapPointInfo: JSON.parse(JSON.stringify(state.allMapPointInfo)),
|
||
mapRouteList: JSON.parse(JSON.stringify(state.mapRouteList))
|
||
}
|
||
state.currentIndex = 0
|
||
message.success('添加成功')
|
||
} catch (e) {
|
||
message.error(e)
|
||
}
|
||
}
|
||
} else {
|
||
state.mapRouteList.forEach((curve, i) => {
|
||
curve.isSelected = i === index
|
||
})
|
||
state.selectedCurve = item
|
||
state.currentDragTarget.index = index
|
||
//让节点不选中
|
||
state.currentItemIndex = -1
|
||
|
||
if (toolbarSwitchType.value === 'editRoute') {
|
||
removeEventListener() //移除监听
|
||
editMapRouteDialogRef.value.open(JSON.parse(JSON.stringify(item)))
|
||
}
|
||
}
|
||
}
|
||
|
||
const mapPointToLineSegment = (linePoint, x, y) => {
|
||
const dx = Number(linePoint.endPointX) - Number(linePoint.startPointX)
|
||
const dy = Number(linePoint.endPointY) - Number(linePoint.startPointY)
|
||
const length = Math.sqrt(dx * dx + dy * dy)
|
||
|
||
const vx = x - Number(linePoint.startPointX)
|
||
const vy = y - Number(linePoint.startPointY)
|
||
const dotProduct = vx * dx + vy * dy
|
||
const projectionLength = dotProduct / length
|
||
|
||
const t = Math.max(0, Math.min(1, projectionLength / length))
|
||
|
||
const newX = Number(linePoint.startPointX) + t * dx
|
||
const newY = Number(linePoint.startPointY) + t * dy
|
||
|
||
return { newX, newY }
|
||
}
|
||
|
||
//编辑路线成功
|
||
const editMapRouteDialogSubmit = (form) => {
|
||
state.mapRouteList[state.currentDragTarget.index] = form
|
||
|
||
//路线位置改变 通知路径里面的节点也改变
|
||
state.mapRouteList.forEach((item) => {
|
||
if (form.startingPointId === item.startingPointId) {
|
||
item.startPointX = form.startPointX
|
||
item.startPointY = form.startPointY
|
||
item.beginHigh = Number(form.beginHigh)
|
||
item.beginWidth = Number(form.beginWidth)
|
||
item.actualStartPointX = disposeEventPoint(form.startPointX, form.startPointY).actualLocationX
|
||
item.actualStartPointY = disposeEventPoint(form.startPointX, form.startPointY).actualLocationY
|
||
}
|
||
if (form.endPointId === item.endPointId) {
|
||
item.endPointX = form.endPointX
|
||
item.endPointY = form.endPointY
|
||
item.endHigh = Number(form.endHigh)
|
||
item.endWidth = Number(form.endWidth)
|
||
item.actualEndPointX = disposeEventPoint(form.endPointX, form.endPointY).actualLocationX
|
||
item.actualEndPointY = disposeEventPoint(form.endPointX, form.endPointY).actualLocationY
|
||
}
|
||
})
|
||
state.allMapPointInfo.forEach((item) => {
|
||
if (item.id === form.startingPointId) {
|
||
item.locationX = form.startPointX
|
||
item.locationY = form.startPointY
|
||
item.actualLocationX = disposeEventPoint(form.startPointX, form.startPointY).actualLocationX
|
||
item.actualLocationY = disposeEventPoint(form.startPointX, form.startPointY).actualLocationY
|
||
}
|
||
if (item.id === form.endPointId) {
|
||
item.locationX = form.endPointX
|
||
item.locationY = form.endPointY
|
||
item.actualLocationX = disposeEventPoint(form.endPointX, form.endPointY).actualLocationX
|
||
item.actualLocationY = disposeEventPoint(form.endPointX, form.endPointY).actualLocationY
|
||
}
|
||
})
|
||
//增加一条历史记录
|
||
addEditHistory() //重新监听键盘事件
|
||
}
|
||
//测距相关
|
||
// 计算连线的样式
|
||
const rangingLineStyle = computed(() => {
|
||
if (state.measureDistancesPoints.length === 2) {
|
||
const [point1, point2] = state.measureDistancesPoints
|
||
const length = Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2))
|
||
const angle = (Math.atan2(point2.y - point1.y, point2.x - point1.x) * 180) / Math.PI
|
||
return {
|
||
position: 'absolute',
|
||
left: `${point1.x}px`,
|
||
top: `${point1.y}px`,
|
||
width: `${length}px`,
|
||
height: '.125rem',
|
||
backgroundColor: 'red',
|
||
transform: `rotate(${angle}deg)`,
|
||
transformOrigin: '0 0'
|
||
}
|
||
}
|
||
return {}
|
||
})
|
||
|
||
// 计算距离信息的样式(显示在连线中间,并根据角度调整文字方向)
|
||
const rangingTextStyle = computed(() => {
|
||
if (state.measureDistancesPoints.length === 2) {
|
||
const [point1, point2] = state.measureDistancesPoints
|
||
const midX = (point1.x + point2.x) / 2
|
||
const midY = (point1.y + point2.y) / 2
|
||
const angle = (Math.atan2(point2.y - point1.y, point2.x - point1.x) * 180) / Math.PI
|
||
|
||
// 调整文字方向,使其始终易于阅读
|
||
let textRotation = 0
|
||
if (angle > 90 || angle < -90) {
|
||
textRotation = angle + 180 // 翻转文字方向
|
||
} else {
|
||
textRotation = angle
|
||
}
|
||
|
||
return {
|
||
position: 'absolute',
|
||
left: `${midX}px`,
|
||
top: `${midY}px`,
|
||
transform: `translate(-50%, -50%) rotate(${textRotation}deg)`,
|
||
backgroundColor: 'rgba(255, 255, 255, 0.8)',
|
||
padding: '.125rem',
|
||
borderRadius: '.25rem',
|
||
fontSize: '.75rem',
|
||
color: 'black',
|
||
whiteSpace: 'nowrap', // 防止文字换行
|
||
pointerEvents: 'none' // 防止文字遮挡点击事件
|
||
}
|
||
}
|
||
return {}
|
||
})
|
||
|
||
// 计算点的样式
|
||
const getRangingPointStyle = (point) => ({
|
||
position: 'absolute',
|
||
left: `${point.x - 4}px`,
|
||
top: `${point.y - 4}px`,
|
||
width: '.5rem',
|
||
height: '.5rem',
|
||
backgroundColor: 'blue',
|
||
borderRadius: '50%'
|
||
})
|
||
|
||
//计算路线的距离
|
||
const calculateRouteLength = (item, type) => {
|
||
const {
|
||
startPointX,
|
||
startPointY,
|
||
endPointX,
|
||
endPointY,
|
||
beginControlX,
|
||
beginControlY,
|
||
endControlX,
|
||
endControlY
|
||
} = item
|
||
|
||
if (type == 'line') {
|
||
const dx = startPointX - endPointX
|
||
const dy = startPointY - endPointY
|
||
const length = Math.sqrt(dx * dx + dy * dy) * Number(imgBgObj.resolution)
|
||
return length.toFixed(2)
|
||
} else {
|
||
const steps = 100 // 离散化的步数,步数越多越精确
|
||
let length = 0
|
||
let prevPoint = null
|
||
|
||
for (let i = 0; i <= steps; i++) {
|
||
const t = i / steps
|
||
// 三阶贝塞尔曲线公式
|
||
const x =
|
||
(1 - t) ** 3 * startPointX +
|
||
3 * t * (1 - t) ** 2 * beginControlX +
|
||
3 * t ** 2 * (1 - t) * endControlX +
|
||
t ** 3 * endPointX
|
||
const y =
|
||
(1 - t) ** 3 * startPointY +
|
||
3 * t * (1 - t) ** 2 * beginControlY +
|
||
3 * t ** 2 * (1 - t) * endControlY +
|
||
t ** 3 * endPointY
|
||
const currentPoint = [x, y]
|
||
|
||
if (prevPoint) {
|
||
const dx = currentPoint[0] - prevPoint[0]
|
||
const dy = currentPoint[1] - prevPoint[1]
|
||
length += Math.sqrt(dx * dx + dy * dy)
|
||
}
|
||
prevPoint = currentPoint
|
||
}
|
||
length = (length * Number(imgBgObj.resolution)).toFixed(2)
|
||
return length
|
||
}
|
||
}
|
||
|
||
// 处理点击事件
|
||
const measureDistancesClick = (event) => {
|
||
// 获取点击点相对于整个页面的坐标
|
||
|
||
const x = disposeEventPoints(event).x
|
||
const y = disposeEventPoints(event).y
|
||
|
||
// 检查点击是否发生在背景区域内
|
||
if (state.measureDistancesPoints.length === 2) {
|
||
// 如果已经有两个点,清空信息
|
||
state.measureDistancesPoints = []
|
||
state.measureDistancesNum = null
|
||
} else {
|
||
// 记录点击的点位(相对于背景区域的坐标)
|
||
const offsetX = x
|
||
const offsetY = y
|
||
state.measureDistancesPoints.push({ x: offsetX, y: offsetY })
|
||
if (state.measureDistancesPoints.length === 2) {
|
||
// 计算两点之间的距离
|
||
const [point1, point2] = state.measureDistancesPoints
|
||
let distancesNum = Math.sqrt(
|
||
Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2)
|
||
)
|
||
state.measureDistancesNum = distancesNum * Number(imgBgObj.resolution)
|
||
}
|
||
}
|
||
}
|
||
//获取扫描图 地图背景相关的信息
|
||
const imgBgObj = reactive({
|
||
imgUrl: '',
|
||
positionMapId: '',
|
||
width: '',
|
||
height: '',
|
||
floor: '',
|
||
area: '',
|
||
resolution: 0,
|
||
origin: null
|
||
})
|
||
//接收参数
|
||
const { query } = useRoute() // 查询参数
|
||
//获取地图点位
|
||
const getMapList = async () => {
|
||
if (!query.mapId) return
|
||
let res = await MapApi.getPositionMap(query.mapId)
|
||
let yamlJson = JSON.parse(res.yamlJson)
|
||
imgBgObj.positionMapId = res.id
|
||
imgBgObj.floor = res.floor
|
||
imgBgObj.area = res.area
|
||
imgBgObj.width = yamlJson.width
|
||
imgBgObj.height = yamlJson.height
|
||
imgBgObj.origin = yamlJson.origin
|
||
imgBgObj.resolution = yamlJson.resolution
|
||
// 调转换成png的接口
|
||
getMapData(imgBgObj)
|
||
}
|
||
//调转换成png的接口
|
||
const getMapData = async (mapInfo) => {
|
||
let data = await MapApi.getPositionMapdDwnloadPngBase64({
|
||
floor: mapInfo.floor,
|
||
area: mapInfo.area
|
||
})
|
||
imgBgObj.imgUrl = data
|
||
|
||
//获取节点 路径等信息
|
||
await getAllNodeList()
|
||
await getAllMapRoute()
|
||
}
|
||
//获取所有的点位 处理七种类型的
|
||
const getAllNodeList = async () => {
|
||
let list = await MapApi.getPositionMapItemList({
|
||
positionMapId: imgBgObj.positionMapId
|
||
})
|
||
state.allMapPointInfo = []
|
||
state.currentIndex = 0
|
||
list.forEach((item) => {
|
||
item.layerSelectionShow = true //用于图层
|
||
item.locationX = Number(item.locationX)
|
||
item.locationY = Number(item.locationY)
|
||
|
||
if (item.type === 1) {
|
||
item.dataObj = {}
|
||
item.dataList = []
|
||
item.locationDeep = 40
|
||
item.locationWide = 40
|
||
item.draggable = true
|
||
item.resizable = false
|
||
item.rotatable = false
|
||
item.lockAspectRatio = true
|
||
} else if (item.type === 6) {
|
||
item.dataObj = {}
|
||
item.dataList = []
|
||
item.locationDeep = 150
|
||
item.locationWide = 150
|
||
item.draggable = true
|
||
item.resizable = false
|
||
item.rotatable = false
|
||
item.lockAspectRatio = true
|
||
} 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
|
||
item.areaName = item.dataList[0].areaName || undefined
|
||
item.laneName = item.dataList[0].laneName || undefined
|
||
item.skuInfo = item.dataList[0].skuInfo || undefined
|
||
item.draggable = true
|
||
item.resizable = true
|
||
item.rotatable = false
|
||
item.lockAspectRatio = true
|
||
} else if (item.type === 3) {
|
||
item.dataObj = JSONBigInt({ storeAsString: true }).parse(item.dataJson)
|
||
item.dataList = []
|
||
item.locationDeep = item.dataObj.locationDeep
|
||
item.locationWide = item.dataObj.locationWide
|
||
item.deviceId = item.dataObj.id
|
||
item.deviceNo = item.dataObj.deviceNo
|
||
item.deviceType = item.dataObj.deviceType
|
||
item.mapImageUrl = item.dataObj.mapImageUrl
|
||
item.draggable = true
|
||
item.resizable = true
|
||
item.rotatable = false
|
||
item.lockAspectRatio = true
|
||
} else if (item.type === 4) {
|
||
item.dataObj = JSONBigInt({ storeAsString: true }).parse(item.dataJson)
|
||
item.dataList = []
|
||
item.locationDeep = item.dataObj.locationDeep
|
||
item.locationWide = item.dataObj.locationWide
|
||
item.draggable = true
|
||
item.resizable = true
|
||
item.rotatable = false
|
||
item.lockAspectRatio = true
|
||
} else if (item.type === 5) {
|
||
item.dataObj = item.dataJson ? JSONBigInt({ storeAsString: true }).parse(item.dataJson) : {}
|
||
item.dataList = []
|
||
item.locationDeep = 150
|
||
item.locationWide = 150
|
||
item.draggable = true
|
||
item.resizable = false
|
||
item.rotatable = false
|
||
item.lockAspectRatio = true
|
||
} else if (item.type === 7) {
|
||
item.dataObj = JSONBigInt({ storeAsString: true }).parse(item.dataJson)
|
||
item.text = item.dataObj.text
|
||
item.fontColor = item.dataObj.fontColor
|
||
item.fontFamily = item.dataObj.fontFamily
|
||
item.fontSize = item.dataObj.fontSize
|
||
item.angle = item.dataObj.angle
|
||
item.draggable = true
|
||
item.resizable = false
|
||
item.rotatable = true
|
||
item.lockAspectRatio = true
|
||
item.locationDeep = 20
|
||
item.locationWide = 20
|
||
}
|
||
|
||
//要将实际的cm改成px
|
||
if (item.locationWide && item.locationDeep) {
|
||
let pxObj = cmConversionPx(item.locationWide, item.locationDeep)
|
||
item.locationWidePx = pxObj.pWidth
|
||
item.locationDeepPx = pxObj.pHeight
|
||
}
|
||
|
||
state.allMapPointInfo.push(item)
|
||
})
|
||
|
||
//包含sortNumber的
|
||
state.haveSortNumMapPointInfo = state.allMapPointInfo.filter((item) => {
|
||
return item.sortNum
|
||
})
|
||
}
|
||
//获取所有的路线
|
||
const getAllMapRoute = async () => {
|
||
state.mapRouteList = await MapApi.getPositionMapLineByPositionMapId(imgBgObj.positionMapId)
|
||
state.allHistoryList = []
|
||
state.allHistoryList[0] = {
|
||
allMapPointInfo: JSON.parse(JSON.stringify(state.allMapPointInfo)),
|
||
mapRouteList: JSON.parse(JSON.stringify(state.mapRouteList))
|
||
}
|
||
}
|
||
//获取设备类型
|
||
const getDeviceTypeName = (deviceType) => {
|
||
let list = getIntDictOptions(DICT_TYPE.DEVICE_TYPE)
|
||
let deviceItem = list.find((item) => {
|
||
return item.value == deviceType
|
||
})
|
||
return deviceItem.label
|
||
}
|
||
//保存地图按钮
|
||
const saveMap = async () => {
|
||
//判断是否存在库位未填写排序号
|
||
state.noLocationNumberList = state.allMapPointInfo.reduce((invalidIndexes, item, index) => {
|
||
if (item.type === 2 && !item.locationNumber) {
|
||
invalidIndexes.push(index) // 如果不满足条件,记录索引
|
||
}
|
||
return invalidIndexes
|
||
}, [])
|
||
|
||
if (state.noLocationNumberList.length !== 0) {
|
||
state.currentItemIndex = -1
|
||
message.error('存在库位未填写排序号')
|
||
return
|
||
}
|
||
|
||
const loading = ElLoading.service({
|
||
lock: true,
|
||
text: '保存中',
|
||
background: 'rgba(255, 255, 255, 0.7)'
|
||
})
|
||
try {
|
||
//节点的保存
|
||
await saveNodeList()
|
||
//路线的保存
|
||
await saveMapRoute()
|
||
//获取新的数据
|
||
await getAllNodeList()
|
||
await getAllMapRoute()
|
||
//关闭loading
|
||
loading.close()
|
||
message.success('保存成功')
|
||
} catch (error) {
|
||
loading.close()
|
||
}
|
||
}
|
||
//节点的保存
|
||
const saveNodeList = async () => {
|
||
let list = state.allMapPointInfo
|
||
|
||
list.forEach((item) => {
|
||
if (item.type === 2) {
|
||
// 库位点 类型为数组
|
||
item.dataList.forEach((node) => {
|
||
node.locationDeep = item.locationDeep
|
||
node.locationWide = item.locationWide
|
||
})
|
||
item.dataJson = JSON.stringify(item.dataList)
|
||
} else if (item.type === 3 || item.type === 4) {
|
||
//设备类型
|
||
item.dataObj.locationWide = item.locationWide
|
||
item.dataObj.locationDeep = item.locationDeep
|
||
item.dataJson = JSON.stringify(item.dataObj)
|
||
} else if (item.type === 7) {
|
||
//文字类型
|
||
item.dataObj.positionMapId = imgBgObj.positionMapId
|
||
item.dataObj.text = item.text
|
||
item.dataObj.fontColor = item.fontColor
|
||
item.dataObj.fontType = item.fontType
|
||
item.dataObj.fontFamily = item.fontFamily
|
||
item.dataObj.fontSize = item.fontSize
|
||
item.dataObj.rotatable = item.rotatable
|
||
item.dataObj.angle = item.angle
|
||
//dataJson数据
|
||
item.dataJson = JSON.stringify(item.dataObj)
|
||
}
|
||
})
|
||
await MapApi.batchSaveOrEditOrDelMapItem(imgBgObj.positionMapId, list)
|
||
}
|
||
//路线的保存
|
||
const saveMapRoute = async () => {
|
||
await MapApi.createOrEditOrDelPositionMapLine(imgBgObj.positionMapId, state.mapRouteList)
|
||
}
|
||
//线库新增 要在库位新增线库信息
|
||
const submitLineLibraryFormSuccess = (obj) => {
|
||
state.allMapPointInfo.forEach((item) => {
|
||
if (obj.mapItemIds.includes(item.id)) {
|
||
item.laneName = obj.laneName
|
||
item.laneId = obj.id
|
||
item.dataList.forEach((node) => {
|
||
node.laneName = obj.laneName
|
||
node.laneId = obj.id
|
||
})
|
||
}
|
||
})
|
||
}
|
||
//区域新增 要在库位新增区域信息
|
||
const itemAreaSettingSubmitSuccess = (obj) => {
|
||
state.allMapPointInfo.forEach((item) => {
|
||
if (obj.mapItemIds.includes(item.id)) {
|
||
item.areaName = obj.areaName
|
||
item.skuInfo = obj.skuInfo
|
||
item.areaId = obj.id
|
||
item.dataList.forEach((node) => {
|
||
node.areaName = obj.areaName
|
||
node.skuInfo = obj.skuInfo
|
||
node.areaId = obj.id
|
||
})
|
||
}
|
||
})
|
||
}
|
||
//线库删除 要在库位中删除线库信息
|
||
const lineLibraryManagementDelete = (mapItemIds) => {
|
||
state.allMapPointInfo.forEach((item) => {
|
||
if (mapItemIds.includes(item.id)) {
|
||
item.laneId = undefined
|
||
item.laneName = undefined
|
||
item.dataList.forEach((node) => {
|
||
node.laneName = undefined
|
||
node.laneId = undefined
|
||
})
|
||
}
|
||
})
|
||
}
|
||
//区域删除 要在库位删除区域信息
|
||
const itemAreaManagementDelete = (mapItemIds) => {
|
||
state.allMapPointInfo.forEach((item) => {
|
||
if (mapItemIds.includes(item.id)) {
|
||
item.areaName = undefined
|
||
item.skuInfo = undefined
|
||
item.areaId = undefined
|
||
item.dataList.forEach((node) => {
|
||
node.areaName = undefined
|
||
node.skuInfo = undefined
|
||
node.areaId = undefined
|
||
})
|
||
}
|
||
})
|
||
}
|
||
//线库编辑 要在库位中编辑线库信息
|
||
const lineLibraryManagementEdit = (obj) => {
|
||
console.log(obj)
|
||
state.allMapPointInfo.forEach((item) => {
|
||
if (obj.mapItemIds.includes(item.id)) {
|
||
item.laneName = obj.laneName
|
||
item.laneId = obj.id
|
||
item.dataList.forEach((node) => {
|
||
node.laneName = obj.laneName
|
||
node.laneId = obj.id
|
||
})
|
||
}
|
||
})
|
||
}
|
||
//区域编辑 要在库位编辑区域信息
|
||
const itemAreaManagementEdit = (obj) => {
|
||
console.log(obj)
|
||
state.allMapPointInfo.forEach((item) => {
|
||
if (obj.mapItemIds.includes(item.id)) {
|
||
item.areaName = obj.areaName
|
||
item.skuInfo = obj.skuInfo
|
||
item.areaId = obj.id
|
||
item.dataList.forEach((node) => {
|
||
node.areaName = obj.areaName
|
||
node.skuInfo = obj.skuInfo
|
||
node.areaId = obj.id
|
||
})
|
||
}
|
||
})
|
||
}
|
||
|
||
//筛选图层
|
||
const layerSelectionSuccess = (typeList) => {
|
||
state.allMapPointInfo.forEach((item) => {
|
||
// 如果 type 存在于第一个数组中,则将 layerSelectionShow 设置为 false
|
||
if (typeList.includes(item.type)) {
|
||
item.layerSelectionShow = false
|
||
} else {
|
||
item.layerSelectionShow = true
|
||
}
|
||
})
|
||
}
|
||
//处理数据 考虑滚动条,缩放等各种情况的的坐标数据
|
||
const disposeEventPoints = (event) => {
|
||
const rect = mapBackgroundRef.value.getBoundingClientRect()
|
||
const scrollLeft = mapBackgroundRef.value.scrollLeft // 水平滚动条偏移量
|
||
const scrollTop = mapBackgroundRef.value.scrollTop // 垂直滚动条偏移量
|
||
// const devicePixelRatio = window.devicePixelRatio || 1 //浏览器缩放
|
||
const devicePixelRatio = 1
|
||
|
||
// 计算页面坐标(考虑设备像素比和滚动条偏移量)
|
||
const x = (event.clientX - rect.left + scrollLeft) / state.imageChangeMultiple / devicePixelRatio
|
||
const y = (event.clientY - rect.top + scrollTop) / state.imageChangeMultiple / devicePixelRatio
|
||
|
||
// 转换为实际坐标
|
||
const actualLocationX = imgBgObj.origin[0] + x * imgBgObj.resolution
|
||
const actualLocationY = imgBgObj.origin[1] + (imgBgObj.height - y) * imgBgObj.resolution
|
||
|
||
return {
|
||
x,
|
||
y,
|
||
actualLocationX,
|
||
actualLocationY
|
||
}
|
||
}
|
||
//实际坐标 传入x y轴坐标
|
||
const disposeEventPoint = (x, y) => {
|
||
const actualLocationX = Number(imgBgObj.origin[0]) + Number(x) * Number(imgBgObj.resolution)
|
||
const actualLocationY =
|
||
Number(imgBgObj.origin[1]) + (Number(imgBgObj.height) - Number(y)) * Number(imgBgObj.resolution)
|
||
return {
|
||
actualLocationX,
|
||
actualLocationY
|
||
}
|
||
}
|
||
// 传入实际现场的数据,转换成浏览器坐标的数据
|
||
const convertActualToBrowser = (pointX, pointY) => {
|
||
const y1 = Number(imgBgObj.origin[1]) + Number(imgBgObj.height) * Number(imgBgObj.resolution)
|
||
let x = Math.max(Number(pointX) - Number(imgBgObj.origin[0]), 0) / Number(imgBgObj.resolution)
|
||
let y = Math.max(y1 - Number(pointY), 0) / Number(imgBgObj.resolution)
|
||
|
||
return {
|
||
x,
|
||
y
|
||
}
|
||
}
|
||
//将节点实际宽高cm转换成px
|
||
const cmConversionPx = (cWidth, cHeight) => {
|
||
let pWidth = Number(cWidth) / imgBgObj.resolution / 100
|
||
let pHeight = Number(cHeight) / imgBgObj.resolution / 100
|
||
|
||
return {
|
||
pWidth,
|
||
pHeight
|
||
}
|
||
}
|
||
// 计算直线中间箭头的路径
|
||
const getLineMidArrowPath = (item) => {
|
||
const midX = (Number(item.startPointX) + Number(item.endPointX)) / 2
|
||
const midY = (Number(item.startPointY) + Number(item.endPointY)) / 2
|
||
|
||
let dx = item.endPointX - item.startPointX
|
||
let dy = item.endPointY - item.startPointY
|
||
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 getBezierMidArrowPath = (item) => {
|
||
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')
|
||
path.setAttribute('d', item.curvePath)
|
||
const length = path.getTotalLength()
|
||
const midPoint = path.getPointAtLength(length / 2)
|
||
const prevPoint = path.getPointAtLength(length / 2 - 1)
|
||
return `M ${prevPoint.x} ${prevPoint.y} L ${midPoint.x} ${midPoint.y}`
|
||
}
|
||
// 计算贝塞尔曲线中间文字的 x 坐标
|
||
const computedCurveTextX = (item) => {
|
||
return (
|
||
(Number(item.startPointX) +
|
||
Number(item.beginControlX) +
|
||
Number(item.endControlX) +
|
||
Number(item.endPointX)) /
|
||
4
|
||
)
|
||
}
|
||
// 计算贝塞尔曲线中间文字的 y 坐标
|
||
const computedCurveTextY = (item) => {
|
||
return (
|
||
(Number(item.startPointY) +
|
||
Number(item.beginControlY) +
|
||
Number(item.endControlY) +
|
||
Number(item.endPointY)) /
|
||
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) {
|
||
//右击
|
||
if (e.button == 2) {
|
||
state.selectedCurve = ''
|
||
//清除路线的选中
|
||
state.mapRouteList.forEach((item) => {
|
||
item.isSelected = false
|
||
})
|
||
state.currentDragTarget = { index: null, type: null }
|
||
state.currentItemIndex = -1
|
||
}
|
||
}
|
||
// 阻止默认菜单弹出
|
||
window.document.oncontextmenu = function () {
|
||
return false
|
||
}
|
||
//监听键盘事件
|
||
const handleKeyDown = (event) => {
|
||
if (event.ctrlKey) {
|
||
if (event.key === 'c') {
|
||
//复制
|
||
if (state.currentItemIndex === -1) {
|
||
message.warning('请先选择要操作的对象')
|
||
return
|
||
}
|
||
replicationNode()
|
||
} else if (event.key === 'v') {
|
||
//粘贴
|
||
if (!state.copyMapItem) {
|
||
message.warning('请先复制对象')
|
||
return
|
||
}
|
||
pasteNode()
|
||
} else if (event.key === 'z') {
|
||
//撤回
|
||
backPreviousStep()
|
||
} else if (event.key === 'y') {
|
||
//重写
|
||
backNextStep()
|
||
}
|
||
}
|
||
}
|
||
|
||
//鼠标滚轮
|
||
const handleWheel = (event) => {
|
||
// 判断 Ctrl 键是否被按下
|
||
if (event.ctrlKey) {
|
||
// 阻止默认的滚动行为
|
||
event.preventDefault()
|
||
|
||
// 根据滚轮滚动方向调整缩放比例
|
||
if (event.deltaY < 0) {
|
||
// 向上滚动,放大
|
||
//放大
|
||
if (state.imageChangeMultiple < 4) {
|
||
state.imageChangeMultiple += 0.2
|
||
} else {
|
||
state.imageChangeMultiple = 3.8
|
||
message.warning('不能在放大了')
|
||
}
|
||
} else {
|
||
//缩小
|
||
if (state.imageChangeMultiple > 0.2) {
|
||
state.imageChangeMultiple -= 0.1
|
||
} else {
|
||
state.imageChangeMultiple = 0.1
|
||
message.warning('不能在缩小了')
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
const addEventListener = () => {
|
||
window.addEventListener('keydown', handleKeyDown)
|
||
}
|
||
const removeEventListener = () => {
|
||
window.removeEventListener('keydown', handleKeyDown)
|
||
}
|
||
|
||
const router = useRouter()
|
||
onBeforeRouteLeave((to, from) => {
|
||
if (to.path == '/mapPage/realTimeMap' && to.query.mapId != imgBgObj.positionMapId) {
|
||
router.replace({
|
||
name: 'MapPageRealTimeMap',
|
||
query: {
|
||
mapId: imgBgObj.positionMapId
|
||
}
|
||
})
|
||
}
|
||
})
|
||
|
||
onMounted(() => {
|
||
getMapList()
|
||
window.addEventListener('keydown', handleKeyDown)
|
||
})
|
||
|
||
onUnmounted(() => {
|
||
window.removeEventListener('keydown', handleKeyDown)
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.item-tooltip-name {
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
max-width: 12.5rem;
|
||
}
|
||
|
||
.edit-map-page {
|
||
-webkit-user-select: none;
|
||
-moz-user-select: none;
|
||
-ms-user-select: none;
|
||
user-select: none;
|
||
|
||
.map-container {
|
||
position: relative;
|
||
width: 100%;
|
||
overflow: auto;
|
||
height: calc(100vh - 120px);
|
||
|
||
.map-bg {
|
||
background-size: contain;
|
||
background-repeat: no-repeat;
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
|
||
.map-box-inner {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
.top-tool {
|
||
margin-bottom: 2px;
|
||
|
||
.top-tool-list-container {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
background-color: #fff;
|
||
box-shadow: rgba(0, 0, 0, 0.06) 0rem 0.125rem 0.1875rem;
|
||
height: 60px;
|
||
padding: 0 0.75rem;
|
||
|
||
.top-tool-list {
|
||
display: flex;
|
||
align-items: center;
|
||
text-align: center;
|
||
position: relative;
|
||
|
||
.top-tool-item {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.tool-item {
|
||
cursor: pointer;
|
||
width: 44px;
|
||
height: 60px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.name {
|
||
cursor: pointer;
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.right-tool-list {
|
||
position: absolute;
|
||
right: 0;
|
||
top: 80px;
|
||
background-color: #fff;
|
||
z-index: 999;
|
||
text-align: center;
|
||
box-shadow: rgba(0, 0, 0, 0.05) 0rem 0rem 0rem 0.0625rem;
|
||
|
||
.tool-item {
|
||
cursor: pointer;
|
||
padding: 8px;
|
||
.name {
|
||
font-family:
|
||
PingFangSC,
|
||
PingFang SC;
|
||
font-weight: 400;
|
||
font-size: 12px;
|
||
color: #0d162a;
|
||
text-align: center;
|
||
font-style: normal;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.input-box-class {
|
||
position: absolute;
|
||
border: 0.0625rem solid #00329f;
|
||
padding: 0.25rem;
|
||
outline: none;
|
||
}
|
||
|
||
.selection-area-btn {
|
||
width: 5rem;
|
||
margin-left: 0.25rem;
|
||
}
|
||
|
||
.svg-div {
|
||
z-index: 99999;
|
||
|
||
svg {
|
||
cursor: crosshair;
|
||
|
||
path.selected {
|
||
stroke: blue;
|
||
}
|
||
}
|
||
}
|
||
|
||
.node-text {
|
||
white-space: nowrap; /* 防止文字自动换行 */
|
||
}
|
||
}
|
||
|
||
.tool-active {
|
||
background: #ebf1ff;
|
||
border-bottom: 0.125rem solid #1677ff;
|
||
}
|
||
|
||
.right-tool-active {
|
||
background: #ebf1ff !important;
|
||
}
|
||
|
||
.drop-down-menu {
|
||
.drop-down-menu-item {
|
||
cursor: pointer;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
width: 100%;
|
||
padding: 0.6875rem 0;
|
||
font-family:
|
||
PingFangSC,
|
||
PingFang SC;
|
||
font-weight: 400;
|
||
font-size: 0.875rem;
|
||
color: #0d162a;
|
||
line-height: 1.25rem;
|
||
text-align: left;
|
||
font-style: normal;
|
||
border-bottom: 0.0625rem solid #e9e9e9;
|
||
}
|
||
}
|
||
|
||
.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;
|
||
}
|
||
.actual-location {
|
||
position: fixed;
|
||
bottom: 0.625rem;
|
||
right: 1rem;
|
||
background-color: #f48924;
|
||
color: #fff;
|
||
padding: 0.375rem;
|
||
border-radius: 0.125rem;
|
||
font-size: 0.875rem;
|
||
user-select: none;
|
||
}
|
||
|
||
.search-select {
|
||
.search-icon {
|
||
widows: 20px;
|
||
height: 20px;
|
||
}
|
||
}
|
||
</style>
|