地图编辑

This commit is contained in:
yyy 2025-02-10 18:07:22 +08:00
parent ce81a142a7
commit 2f8d31b63c
15 changed files with 589 additions and 193 deletions

View File

@ -18,7 +18,7 @@
</el-select> </el-select>
</ElDialog> </ElDialog>
<div v-else class="custom-hover" @click.stop="showTopSearch = !showTopSearch"> <div v-else class="custom-hover" @click.stop="showTopSearch = !showTopSearch">
<Icon icon="ep:search" /> <Icon icon="ep:search" color="var(--top-header-text-color)" />
<el-select <el-select
@click.stop @click.stop
filterable filterable

View File

@ -59,7 +59,7 @@ export default defineComponent({
<Backtop></Backtop> <Backtop></Backtop>
{<Setting></Setting>} {/* <Setting></Setting> */}
</section> </section>
) )
} }

View File

@ -54,7 +54,13 @@ onMounted(() => {
<ElPopover :width="400" placement="bottom" trigger="click"> <ElPopover :width="400" placement="bottom" trigger="click">
<template #reference> <template #reference>
<ElBadge :is-dot="unreadCount > 0" class="item"> <ElBadge :is-dot="unreadCount > 0" class="item">
<Icon :size="18" class="cursor-pointer" icon="ep:bell" @click="getList" /> <Icon
:size="18"
class="cursor-pointer"
icon="ep:bell"
@click="getList"
color="var(--top-header-text-color)"
/>
</ElBadge> </ElBadge>
</template> </template>
<ElTabs v-model="activeName"> <ElTabs v-model="activeName">

View File

@ -297,6 +297,6 @@ $prefix-cls: #{$namespace}-setting;
.#{$prefix-cls} { .#{$prefix-cls} {
border-radius: 6px 0 0 6px; border-radius: 6px 0 0 6px;
z-index: 1200;/*修正没有z-index会被表格层覆盖,值不要超过4000*/ z-index: 1200; /*修正没有z-index会被表格层覆盖,值不要超过4000*/
} }
</style> </style>

View File

@ -1,6 +1,6 @@
<script lang="tsx"> <script lang="tsx">
import { defineComponent, computed } from 'vue' import { defineComponent, computed } from 'vue'
import { Message } from '@/layout/components//Message' import { Message } from '@/layout/components/Message'
import { Collapse } from '@/layout/components/Collapse' import { Collapse } from '@/layout/components/Collapse'
import { UserInfo } from '@/layout/components/UserInfo' import { UserInfo } from '@/layout/components/UserInfo'
import { Screenfull } from '@/layout/components/Screenfull' import { Screenfull } from '@/layout/components/Screenfull'
@ -52,7 +52,7 @@ export default defineComponent({
'h-[var(--top-tool-height)] relative px-[var(--top-tool-p-x)] flex items-center justify-between', 'h-[var(--top-tool-height)] relative px-[var(--top-tool-p-x)] flex items-center justify-between',
'dark:bg-[var(--el-bg-color)]' 'dark:bg-[var(--el-bg-color)]'
]} ]}
style="height:60px" style="height:61px"
> >
{layout.value !== 'top' ? ( {layout.value !== 'top' ? (
<div class="h-full flex items-center"> <div class="h-full flex items-center">

View File

@ -55,7 +55,7 @@ export const useRenderLayout = () => {
'w-[var(--left-menu-max-width)]': !appStore.getCollapse 'w-[var(--left-menu-max-width)]': !appStore.getCollapse
} }
]} ]}
style="transition: all var(--transition-time-02);height:59px;display: flex; style="transition: all var(--transition-time-02);height:60px;display: flex;
align-items: center;" align-items: center;"
></Logo> ></Logo>
) : undefined} ) : undefined}

View File

@ -72,7 +72,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
}, },
//地图 //地图
{ {
path: '/warehouse/map', // 商品中心 path: '/warehouse/map',
component: Layout, component: Layout,
name: 'warehouseMap', name: 'warehouseMap',
meta: { meta: {

View File

@ -94,7 +94,7 @@ export const useAppStore = defineStore('app', {
// 头部字体颜色 // 头部字体颜色
topHeaderTextColor: '#ffffff', topHeaderTextColor: '#ffffff',
// 头部悬停颜色 // 头部悬停颜色
topHeaderHoverColor: '#f6f6f6', topHeaderHoverColor: '#215bd8',
// 头部边框颜色 // 头部边框颜色
topToolBorderColor: '#E2E7F5' topToolBorderColor: '#E2E7F5'
} }

View File

@ -21,7 +21,7 @@
/* left menu end */ /* left menu end */
/* logo start */ /* logo start */
--logo-height: 50px; --logo-height: 40px;
--logo-title-text-color: #fff; --logo-title-text-color: #fff;
/* logo end */ /* logo end */
@ -31,14 +31,14 @@
--top-header-text-color: 'inherit'; --top-header-text-color: 'inherit';
--top-header-hover-color: #f6f6f6; --top-header-hover-color: #215bd8;
--top-tool-height: var(--logo-height); --top-tool-height: var(--logo-height);
--top-tool-p-x: 0; --top-tool-p-x: 0;
/* --tags-view-height: 35px; 开启面包屑时 */ /* --tags-view-height: 35px; 开启面包屑时 */
--tags-view-height: 8px; --tags-view-height: 16px;
/* header start */ /* header start */
/* tab menu start */ /* tab menu start */

View File

@ -21,7 +21,13 @@
<el-form-item label="层数" prop="layersNumber" required v-if="form.type === 2"> <el-form-item label="层数" prop="layersNumber" required v-if="form.type === 2">
<el-input-number v-model="form.layersNumber" :min="1" :max="3" /> <el-input-number v-model="form.layersNumber" :min="1" :max="3" />
</el-form-item> </el-form-item>
<el-form-item label="编号" prop="id" required v-if="form.type === 3"> <el-form-item
label="编号"
prop="id"
required
v-if="form.type === 3"
:rules="{ required: true, message: '请选择设备编号', trigger: 'change' }"
>
<div> <div>
<el-select <el-select
v-model="deviceInfo.deviceType" v-model="deviceInfo.deviceType"
@ -199,11 +205,12 @@ const directionChange = (e) => {
// //
const deviceInfo = ref({ const deviceInfo = ref({
positionMapId: props.positionMapId, positionMapId: '',
deviceType: '' deviceType: ''
}) })
const deviceList = ref([]) const deviceList = ref([])
const getDeviceList = async () => { const getDeviceList = async () => {
deviceInfo.value.positionMapId = props.positionMapId
deviceList.value = await MapApi.getDeviceInformationList(deviceInfo.value) deviceList.value = await MapApi.getDeviceInformationList(deviceInfo.value)
} }
const deviceTypeChange = () => { const deviceTypeChange = () => {

View File

@ -0,0 +1,38 @@
<template>
<Dialog v-model="dialogFormVisible" title="设备" width="600" class="text-form-dialog">
<div> 11 </div>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="formSubmit"> 确认 </el-button>
</div>
</template>
</Dialog>
</template>
<script setup>
import { reactive, ref } from 'vue'
const dialogFormVisible = ref(false)
const open = (item) => {
dialogFormVisible.value = true
}
const formSubmit = () => {}
defineExpose({ open }) // open
</script>
<style lang="scss">
.text-form-dialog {
.el-dialog__header {
border-bottom: none;
}
.el-dialog__footer {
padding-bottom: 30px;
border-top: none !important;
}
}
</style>

View File

@ -0,0 +1,103 @@
<template>
<div class="layer-select">
<div class="top-list" v-if="isUnfold">
<div class="item" v-for="(item, index) in list" :key="index" @click="changeSelectList(item)">
<span class="name">{{ item.name }}</span>
<el-icon v-if="item.isShow" color="#1677FF"><View /></el-icon>
<el-icon v-else color="#444444"><Hide /></el-icon>
</div>
</div>
<div class="top-button" @click="changeUnfold">
<span class="title">图例</span>
<el-icon v-if="isUnfold" color="#98A4BF"><CaretTop /></el-icon>
<el-icon v-else color="#98A4BF"><CaretBottom /></el-icon>
</div>
</div>
</template>
<script setup>
import { reactive, ref } from 'vue'
const list = ref([
{
labelType: 'locationPoint',
name: '库位点',
isShow: true
},
{
labelType: 'wayPoint',
name: '路径点',
isShow: true
},
{
labelType: 'devicePoint',
name: '设备点',
isShow: true
}
])
const isUnfold = ref(true)
const changeUnfold = () => {
isUnfold.value = !isUnfold.value
}
const changeSelectList = (item) => {
item.isShow = !item.isShow
}
const open = (item) => {}
defineExpose({ open }) // open
</script>
<style lang="scss">
.layer-select {
background-color: #fff;
position: fixed;
bottom: 20px;
width: 144px;
cursor: pointer;
.top-list {
.item {
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 22px;
box-sizing: border-box;
.name {
font-family:
PingFangSC,
PingFang SC;
font-weight: 400;
font-size: 14px;
color: rgba(0, 0, 0, 0.88);
line-height: 20px;
text-align: left;
font-style: normal;
}
}
}
.top-button {
display: flex;
align-items: center;
justify-content: center;
padding: 10px 0;
border: 1px solid #eeeeee;
.title {
font-family:
PingFangSC,
PingFang SC;
font-weight: 400;
font-size: 16px;
color: #98a4bf;
line-height: 22px;
text-align: left;
font-style: normal;
}
}
}
</style>

View File

@ -1,8 +1,13 @@
<template> <template>
<Dialog v-model="dialogFormVisible" title="文字设置" width="400" class="text-form-dialog"> <div class="text-form-dialog">
<el-form :model="form"> <el-form :model="form" :inline="true">
<el-form-item label="字体"> <el-form-item label="字体">
<el-select v-model="form.fontType" placeholder="请选择"> <el-select
v-model="form.fontFamily"
placeholder="请选择"
class="!w-100px"
@change="fontStyleChange()"
>
<el-option label="宋体" value="SimSun" /> <el-option label="宋体" value="SimSun" />
<el-option label="黑体" value="SimHei" /> <el-option label="黑体" value="SimHei" />
<el-option label="微软雅黑" value="Microsoft Yahei" /> <el-option label="微软雅黑" value="Microsoft Yahei" />
@ -11,8 +16,10 @@
<el-option label="仿宋" value="FangSong" /> <el-option label="仿宋" value="FangSong" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="字号"> <el-form-item label="字号">
<el-input-number <el-input-number
@change="fontStyleChange()"
v-model="form.fontSize" v-model="form.fontSize"
:step="1" :step="1"
:min="10" :min="10"
@ -21,45 +28,36 @@
/> />
</el-form-item> </el-form-item>
<el-form-item label="颜色"> <el-form-item label="颜色">
<el-color-picker v-model="form.fontColor" /> <el-color-picker v-model="form.fontColor" @change="fontStyleChange()" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> </div>
<div class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="dialogFormVisible = false"> 确认 </el-button>
</div>
</template>
</Dialog>
</template> </template>
<script setup> <script setup>
import { reactive, ref } from 'vue' import { reactive, ref } from 'vue'
const dialogFormVisible = ref(false)
const formLabelWidth = '140px'
const form = reactive({ const form = reactive({
fontType: 'SimSun', fontFamily: 'SimSun',
fontSize: '14', fontSize: '14',
fontColor: '#000000' fontColor: '#000000'
}) })
const open = (item) => { const fontStyleChange = () => {
dialogFormVisible.value = true emit('textFormSuccess', form)
} }
defineExpose({ open }) // open const emit = defineEmits(['textFormSuccess'])
</script> </script>
<style lang="scss"> <style lang="scss">
.text-form-dialog { .text-form-dialog {
.el-dialog__header { background-color: #fff;
border-bottom: none; position: absolute;
} top: 76px;
left: 30%;
.el-dialog__footer { box-shadow: rgba(0, 0, 0, 0.05) 0px 0px 0px 1px;
border-top: none !important; padding: 18px 20px 0 20px;
} border-radius: 6px;
} }
</style> </style>

View File

@ -1,19 +1,19 @@
<template> <template>
<div> <div class="top-tool">
<div class="top-tool-list"> <div class="top-tool-list">
<div v-for="item in state.topToolList" :key="item.id" class="top-tool-item"> <div v-for="item in state.topToolList" :key="item.switchType" class="top-tool-item">
<el-popover <el-popover
placement="bottom" placement="bottom"
:width="80" :width="80"
trigger="click" trigger="click"
v-if="item.id === 5 || item.id === 6" v-if="item.switchType === 'move' || item.switchType === 'revolve'"
:disabled="currentItemIndex === -1" :disabled="!currentItemIndex"
> >
<template #reference> <template #reference>
<div <div
class="tool-item" class="tool-item"
:class=" :class="
toolbarTypeIndex === item.id toolbarSwitchType === item.switchType
? 'tool-active' ? 'tool-active'
: item.isActive : item.isActive
? 'right-tool-active' ? 'right-tool-active'
@ -26,7 +26,7 @@
</div> </div>
</template> </template>
<!-- 位置 --> <!-- 位置 -->
<el-form :model="state.moveForm" v-if="item.id === 5"> <el-form :model="state.moveForm" v-if="item.switchType === 'move'">
<el-form-item label="X"> <el-form-item label="X">
<el-input v-model="state.moveForm.x" placeholder="请输入" /> <el-input v-model="state.moveForm.x" placeholder="请输入" />
</el-form-item> </el-form-item>
@ -38,7 +38,7 @@
</div> </div>
</el-form> </el-form>
<!-- 旋转 --> <!-- 旋转 -->
<el-form :model="state.rotationForm" v-if="item.id === 6"> <el-form :model="state.rotationForm" v-if="item.switchType === 'revolve'">
<el-form-item label="角度"> <el-form-item label="角度">
<el-input v-model="state.rotationForm.angle" placeholder="请输入" /> <el-input v-model="state.rotationForm.angle" placeholder="请输入" />
</el-form-item> </el-form-item>
@ -47,7 +47,7 @@
</div> </div>
</el-form> </el-form>
<!-- 字体 --> <!-- 字体 -->
<el-form :model="state.rotationForm" v-if="item.id === 13"> <el-form :model="state.rotationForm" v-if="item.switchType === 'text'">
<el-form-item label="角度"> <el-form-item label="角度">
<el-input v-model="state.rotationForm.angle" placeholder="请输入" /> <el-input v-model="state.rotationForm.angle" placeholder="请输入" />
</el-form-item> </el-form-item>
@ -61,23 +61,38 @@
v-else v-else
class="tool-item" class="tool-item"
:class=" :class="
toolbarTypeIndex === item.id ? 'tool-active' : item.isActive ? 'right-tool-active' : '' toolbarSwitchType === item.switchType
? 'tool-active'
: item.isActive
? 'right-tool-active'
: ''
" "
@click="toolbarClick(item)" @click="toolbarClick(item)"
> >
<Icon :icon="item.icon" :size="24" /> <Icon :icon="item.icon" :size="24" />
<div class="name"> {{ item.name }} </div> <div class="name"> {{ item.name }} </div>
</div> </div>
<div class="line" v-if="item.id === 3 || item.id === 10 || item.id === 17"></div> <div
class="line"
v-if="
item.switchType === 'saveAs' ||
item.switchType === 'delete' ||
item.switchType === 'ranging'
"
></div>
</div> </div>
</div> </div>
<div class="right-tool-list" v-if="state.isShowToolbar"> <div class="right-tool-list" v-if="state.isShowToolbar">
<div <div
v-for="item in state.rightToolList" v-for="item in state.rightToolList"
:key="item.id" :key="item.switchType"
class="tool-item" class="tool-item"
:class=" :class="
toolbarTypeIndex === item.id ? 'tool-active' : item.isActive ? 'right-tool-active' : '' toolbarSwitchType === item.switchType
? 'tool-active'
: item.isActive
? 'right-tool-active'
: ''
" "
@click="toolbarClick(item)" @click="toolbarClick(item)"
> >
@ -86,14 +101,21 @@
</div> </div>
</div> </div>
</div> </div>
<!-- @mousewheel.prevent="rollImg" -->
<div <div
class="map-box" class="map-box"
ref="imgWrap" ref="imgWrap"
@mousewheel.prevent="rollImg"
style="overflow: hidden" style="overflow: hidden"
:style="{ cursor: state.cursorStyle }" :style="{ cursor: state.cursorStyle }"
> >
<div class="map-box-inner" ref="image" @mousedown.prevent="moveImg"> <div
class="map-box-inner"
ref="image"
@mousedown.stop="startDrawSelection"
@mousemove.stop="updateDrawSelection"
@mouseup.stop="endDrawSelection"
>
<img :src="imgBgObj.imgUrl" class="map-box-img" id="mapBg" /> <img :src="imgBgObj.imgUrl" class="map-box-img" id="mapBg" />
<div <div
class="map-box-inner-dot" class="map-box-inner-dot"
@ -119,7 +141,6 @@
:resizable="item.resizable" :resizable="item.resizable"
:rotatable="item.rotatable" :rotatable="item.rotatable"
:lock-aspect-ratio="item.lockAspectRatio" :lock-aspect-ratio="item.lockAspectRatio"
:handles="['tl', 'tr', 'bl', 'br', 'rot']"
style="border: none" style="border: none"
> >
<div <div
@ -138,10 +159,51 @@
v-if="item.labelType === 'node'" v-if="item.labelType === 'node'"
style="width: 100%; height: 100%; background-color: #000; border-radius: 50%" style="width: 100%; height: 100%; background-color: #000; border-radius: 50%"
></div> ></div>
<div
v-if="item.labelType === 'text'"
:style="{
fontSize: item.fontSize + 'px',
fontFamily: item.fontFamily,
color: item.fontColor
}"
>
{{ item.text }}
</div>
</div> </div>
</VueDragResizeRotate> </VueDragResizeRotate>
<!-- 文档 https://github.com/a7650/vue3-draggable-resizable/blob/main/docs/document_zh.md#resizable --> <!-- 文档 https://github.com/a7650/vue3-draggable-resizable/blob/main/docs/document_zh.md#resizable -->
</div> </div>
<!-- 绘制框选区域 -->
<div
v-if="state.drawSelectionArea"
:style="{
position: 'absolute',
left: `${state.drawSelectionAreaBox.x}px`,
top: `${state.drawSelectionAreaBox.y}px`,
width: `${state.drawSelectionAreaBox.width}px`,
height: `${state.drawSelectionAreaBox.height}px`,
border: '1px solid blue',
backgroundColor: 'rgba(0, 0, 255, 0.1)',
zIndex: 99999
}"
></div>
<!-- 文字输入区域 -->
<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.x + 'px',
top: state.inputBoxStyle.y + 'px'
}"
class="input-box-class"
/>
</div> </div>
</div> </div>
<!-- 节点编辑 --> <!-- 节点编辑 -->
@ -151,19 +213,32 @@
@submitNodeSuccess="submitNodeSuccess" @submitNodeSuccess="submitNodeSuccess"
/> />
<!-- 文字输入弹窗 --> <!-- 文字输入弹窗 -->
<textFormToolDialog ref="textFormToolDialogRef" /> <textFormToolDialog
ref="textFormToolDialogRef"
v-if="state.textFormToolShow"
@textFormSuccess="textFormSuccess"
/>
<!-- 图层选择 -->
<layerSelectionToolDialog v-if="state.isShowLayer" ref="layerSelectionToolDialogRef" />
<!-- 设备弹窗选择 -->
<equipmentToolDialog ref="equipmentToolDialogRef" />
</template> </template>
<script setup> <script setup>
import { ref, defineComponent, reactive, nextTick, onMounted } from 'vue' import { ref, defineComponent, reactive, nextTick, onMounted } from 'vue'
import editNodeProperties from './components-tool/editNodeProperties.vue' import editNodeProperties from './components-tool/editNodeProperties.vue'
import textFormToolDialog from './components-tool/textFormToolDialog.vue' import textFormToolDialog from './components-tool/textFormToolDialog.vue'
import equipmentToolDialog from './components-tool/equipmentToolDialog.vue'
import layerSelectionToolDialog from './components-tool/layerSelectionToolDialog.vue'
import * as MapApi from '@/api/map/map' import * as MapApi from '@/api/map/map'
import cursorCollection from './cursorCollection' import cursorCollection from './cursorCollection'
defineOptions({ name: 'editMapPageRealTimeMap' }) defineOptions({ name: 'editMapPageRealTimeMap' })
const equipmentToolDialogRef = ref() //
const textFormToolDialogRef = ref() // const textFormToolDialogRef = ref() //
const inputBoxRef = ref() //
const message = useMessage() // const message = useMessage() //
const formLoading = ref(false) const formLoading = ref(false)
@ -241,10 +316,10 @@ const editNodePropertiesRef = ref()
const activatedHandle = (item, index) => { const activatedHandle = (item, index) => {
// console.log('', item, index) // console.log('', item, index)
currentItemIndex.value = index currentItemIndex.value = index
if (item.labelType === 'node') {
if (toolbarTypeIndex.value === 23) { //
editNodePropertiesRef.value.open(item) if (toolbarSwitchType.value === 'editNode' && item.labelType === 'node') {
} editNodePropertiesRef.value.open(item)
} }
} }
// //
@ -290,7 +365,7 @@ const backNextStep = () => {
// //
const mapClick = (e) => { const mapClick = (e) => {
if (toolbarTypeIndex.value === 22) { if (toolbarSwitchType.value === 'drawNodes') {
// //
imgList.value.push({ imgList.value.push({
x: e.offsetX, x: e.offsetX,
@ -308,7 +383,59 @@ const mapClick = (e) => {
currentIndex.value++ currentIndex.value++
allHistoryList.value.push(JSON.parse(JSON.stringify(imgList.value))) allHistoryList.value.push(JSON.parse(JSON.stringify(imgList.value)))
} }
//
if (toolbarSwitchType.value === 'text') {
state.showInputBox = true
state.inputBoxStyle = {
fontSize: state.textForm.fontSize,
fontFamily: state.textForm.fontFamily,
fontColor: state.textForm.fontColor,
x: e.offsetX,
y: e.offsetY
}
//
setTimeout(() => {
inputBoxRef.value.focus()
}, 0)
}
} }
//
const textFormSuccess = (form) => {
state.textForm = JSON.parse(JSON.stringify(form))
state.inputBoxStyle = {
fontSize: `${form.fontSize}`,
fontFamily: `${form.fontFamily}`,
fontColor: `${form.fontColor}`
}
}
//
const handleInputEnd = () => {
if (state.inputBoxValue) {
state.showInputBox = false
imgList.value.push({
labelType: 'text', //
mapId: '', //id
x: state.inputBoxStyle.x, //x
y: state.inputBoxStyle.y, //y
h: '', //h
w: '', //w
angle: 0, //
draggable: true, //
resizable: false, //
rotatable: false, //
lockAspectRatio: true, //
img: '', //
text: state.inputBoxValue, //
fontColor: state.inputBoxStyle.fontColor, //
fontFamily: state.inputBoxStyle.fontFamily, //
fontSize: state.inputBoxStyle.fontSize
})
addEditHistory()
state.inputBoxValue = ''
}
}
// //
const saveMap = async () => { const saveMap = async () => {
// //
@ -346,131 +473,137 @@ const submitNodeSuccess = (item) => {
} }
// //
const toolbarTypeIndex = ref(0) const toolbarSwitchType = ref('')
const state = reactive({ const state = reactive({
topToolList: [ topToolList: [
{ {
id: 1, switchType: 'open',
name: '打开', name: '打开',
icon: 'ep:folder-add', icon: 'ep:folder-add',
isActive: false isActive: false
}, },
{ {
id: 2, switchType: 'save',
name: '保存', name: '保存',
icon: 'ep:folder-checked', icon: 'ep:folder-checked',
isActive: false isActive: false
}, },
{ {
id: 3, switchType: 'saveAs',
name: '另存为', name: '另存为',
icon: 'ep:folder-opened', icon: 'ep:folder-opened',
isActive: false isActive: false
}, },
{ {
id: 4, switchType: 'choose',
name: '选择', name: '选择',
icon: 'ep:position', icon: 'ep:position',
isActive: false isActive: false
}, },
{ {
id: 5, switchType: 'move',
name: '移动', name: '移动',
icon: 'ep:rank', icon: 'ep:rank',
isActive: false isActive: false
}, },
{ {
id: 6, switchType: 'revolve',
name: '旋转', name: '旋转',
icon: 'ep:folder-add', icon: 'ep:folder-add',
isActive: false isActive: false
}, },
{ {
id: 7, switchType: 'copy',
name: '复制', name: '复制',
icon: 'ep:document', icon: 'ep:document',
isActive: false isActive: false
}, },
{ {
id: 8, switchType: 'paste',
name: '粘贴', name: '粘贴',
icon: 'ep:copy-document', icon: 'ep:copy-document',
isActive: false isActive: false
}, },
{ {
id: 9, switchType: 'delete',
name: '删除', name: '删除',
icon: 'ep:delete', icon: 'ep:delete',
isActive: false isActive: false
}, },
{ {
id: 10, switchType: 'tools',
name: '工具', name: '工具',
icon: 'ep:briefcase', icon: 'ep:briefcase',
isActive: false isActive: false
}, },
{ {
id: 11, switchType: 'lineLibrary',
name: '线库', name: '线库',
icon: 'ep:folder-add', icon: 'ep:folder-add',
isActive: false isActive: false
}, },
{ {
id: 12, switchType: 'region',
name: '区域', name: '区域',
icon: 'ep:folder-add', icon: 'ep:folder-add',
isActive: false isActive: false
}, },
{ {
id: 13, switchType: 'text',
name: '文字', name: '文字',
icon: 'ep:folder-add', icon: 'ep:folder-add',
isActive: false isActive: false
}, },
{ {
id: 14, switchType: 'equipment',
name: '设备',
icon: 'ep:folder-add',
isActive: false
},
{
switchType: 'ranging',
name: '测距', name: '测距',
icon: 'ep:folder-add', icon: 'ep:folder-add',
isActive: false isActive: false
}, },
{ {
id: 15, switchType: 'layer',
name: '图层', name: '图层',
icon: 'ep:folder-add', icon: 'ep:folder-add',
isActive: false isActive: false
}, },
{ {
id: 16, switchType: 'marker',
name: '标记', name: '标记',
icon: 'ep:folder-add', icon: 'ep:folder-add',
isActive: false isActive: false
}, },
{ {
id: 17, switchType: 'grid',
name: '网格', name: '网格',
icon: 'ep:folder-add', icon: 'ep:folder-add',
isActive: false isActive: false
}, },
{ {
id: 18, switchType: 'larger',
name: '放大', name: '放大',
icon: 'ep:zoom-in', icon: 'ep:zoom-in',
isActive: false isActive: false
}, },
{ {
id: 19, switchType: 'smaller',
name: '缩小', name: '缩小',
icon: 'ep:zoom-out', icon: 'ep:zoom-out',
isActive: false isActive: false
}, },
{ {
id: 20, switchType: 'withdraw',
name: '撤回', name: '撤回',
icon: 'ep:top-left', icon: 'ep:top-left',
isActive: false isActive: false
}, },
{ {
id: 21, switchType: 'next',
name: '重做', name: '重做',
icon: 'ep:top-right', icon: 'ep:top-right',
isActive: false isActive: false
@ -478,25 +611,25 @@ const state = reactive({
], ],
rightToolList: [ rightToolList: [
{ {
id: 22, switchType: 'drawNodes',
name: '绘制节点', name: '绘制节点',
icon: 'ep:circle-plus-filled', icon: 'ep:circle-plus-filled',
isActive: false isActive: false
}, },
{ {
id: 23, switchType: 'editNode',
name: '编辑节点', name: '编辑节点',
icon: 'ep:circle-plus-filled', icon: 'ep:circle-plus-filled',
isActive: false isActive: false
}, },
{ {
id: 24, switchType: 'drawRoute',
name: '绘制路线', name: '绘制路线',
icon: 'ep:semi-select', icon: 'ep:semi-select',
isActive: false isActive: false
}, },
{ {
id: 25, switchType: 'editRoute',
name: '编辑路线', name: '编辑路线',
icon: 'ep:semi-select', icon: 'ep:semi-select',
isActive: false isActive: false
@ -512,32 +645,59 @@ const state = reactive({
angle: '' angle: ''
}, // }, //
copyMapItem: '', // copyMapItem: '', //
cursorStyle: 'auto' cursorStyle: 'auto',
drawSelectionArea: false, //
drawSelectionAreaBox: { x: 0, y: 0, width: 0, height: 0 }, //
drawSelectionAreaStartPos: { x: 0, y: 0 }, //
drawSelectionAreaSelectedPoints: [], //list
textForm: {
fontFamily: 'SimSun',
fontSize: '14',
fontColor: '#000000'
}, //14#000000
textFormToolShow: false, //
showInputBox: false, //
inputBoxStyle: {}, //
inputBoxValue: '', //
isShowLayer: false //
}) })
const toolbarClick = (item) => { const toolbarClick = (item) => {
let type = item.id let type = item.switchType
if (currentItemIndex.value === -1 && (type === 5 || type === 6 || type === 7 || type === 9)) { if (
currentItemIndex.value &&
(type === 'move' || type === 'revolve' || type === 'copy' || type === 'delete')
) {
message.warning('请先选择要操作的对象') message.warning('请先选择要操作的对象')
return return
} }
if (type === 8 && !state.copyMapItem) { //
if (type === 'paste' && !state.copyMapItem) {
message.warning('请先复制对象') message.warning('请先复制对象')
return return
} }
if ((type === 10 || type === 13 || type === 17) && toolbarTypeIndex.value === type) { if (
toolbarTypeIndex.value = 0 (type === 'tools' || type === 'text' || type === 'layer' || type === 'grid') &&
toolbarSwitchType.value === type
) {
toolbarSwitchType.value = ''
} else { } else {
toolbarTypeIndex.value = type toolbarSwitchType.value = type
}
if (toolbarSwitchType.value !== 'text') {
state.cursorStyle = `auto`
} }
switch (type) { switch (type) {
case 1: case 'open':
//
break break
case 2: case 'save':
//
saveMap() saveMap()
break break
case 3: case 'saveAs':
// json 访 // json 访
const jsonString = JSON.stringify(allHistoryList.value[currentIndex.value], null, 2) const jsonString = JSON.stringify(allHistoryList.value[currentIndex.value], null, 2)
const blob = new Blob([jsonString], { type: 'application/json' }) const blob = new Blob([jsonString], { type: 'application/json' })
@ -553,59 +713,79 @@ const toolbarClick = (item) => {
message.success('下载成功') message.success('下载成功')
break break
case 4: case 'choose':
//
break break
case 5: case 'move':
//
break break
case 6: case 'revolve':
break break
case 7: case 'copy':
// //
state.copyMapItem = allHistoryList.value[currentIndex.value][currentItemIndex.value] state.copyMapItem = allHistoryList.value[currentIndex.value][currentItemIndex.value]
break break
case 8: case 'paste':
// //
let item = JSON.parse(JSON.stringify(state.copyMapItem)) let copyObj = JSON.parse(JSON.stringify(state.copyMapItem))
item.x = item.x + 50 copyObj.x = copyObj.x + 50
item.y = item.y + 50 copyObj.y = copyObj.y + 50
imgList.value.push(item) imgList.value.push(copyObj)
addEditHistory() addEditHistory()
break break
case 9: case 'delete':
// //
imgList.value.splice(currentItemIndex.value, 1) imgList.value.splice(currentItemIndex.value, 1)
addEditHistory() addEditHistory()
break break
case 10: case 'tools':
// //
if (toolbarTypeIndex.value === 10) { if (toolbarSwitchType.value === 'tools') {
state.isShowToolbar = true state.isShowToolbar = true
item.isActive = true
} else { } else {
state.isShowToolbar = false state.isShowToolbar = false
item.isActive = false
} }
break break
case 11: case 'lineLibrary':
// 线
break break
case 12: case 'region':
//
break break
case 13: case 'text':
// //
textFormToolDialogRef.value.open() if (toolbarSwitchType.value === 'text') {
// if (toolbarTypeIndex.value === 13) { state.textFormToolShow = true
// state.cursorStyle = `url('${cursorCollection.input}'),auto` state.cursorStyle = `url('${cursorCollection.input}'),auto`
// } else { } else {
// state.cursorStyle = `auto` state.textFormToolShow = false
// } state.cursorStyle = `auto`
state.textFormToolShow = false
state.showInputBox = false
state.inputBoxValue = ''
}
break break
case 14: case 'ranging':
//
break break
case 15: case 'layer':
//
if (toolbarSwitchType.value === 'layer') {
state.isShowLayer = true
item.isActive = true
} else {
state.isShowLayer = false
item.isActive = false
}
break break
case 16: case 'marker':
//
break break
case 17: case 'grid':
// //
if (toolbarTypeIndex.value === 17) { if (toolbarSwitchType.value === 'grid') {
state.isShowGrid = true state.isShowGrid = true
item.isActive = true item.isActive = true
} else { } else {
@ -613,26 +793,35 @@ const toolbarClick = (item) => {
item.isActive = false item.isActive = false
} }
break break
case 18: case 'larger':
//
break break
case 19: case 'smaller':
//
break break
case 20: case 'withdraw':
// //
backPreviousStep() backPreviousStep()
break break
case 21: case 'next':
// //
backNextStep() backNextStep()
break break
case 22: case 'drawNodes':
drawNodes() //
break break
case 23: case 'editNode':
//
break break
case 24: case 'drawRoute':
//线
break break
case 25: case 'editRoute':
// 线
break
case 'equipment':
//
equipmentToolDialogRef.value.open()
break break
} }
} }
@ -647,6 +836,53 @@ const rotationFormSubmit = () => {
allHistoryList.value[currentIndex.value][currentItemIndex.value].angle = state.rotationForm.angle allHistoryList.value[currentIndex.value][currentItemIndex.value].angle = state.rotationForm.angle
} }
//
const startDrawSelection = (event) => {
if (toolbarSwitchType.value !== 'lineLibrary' && toolbarSwitchType.value !== 'region') return
state.drawSelectionArea = true
state.drawSelectionAreaStartPos = { x: event.offsetX, y: event.offsetY }
state.drawSelectionAreaBox = { x: event.offsetX, y: event.offsetY, width: 0, height: 0 }
}
const updateDrawSelection = (event) => {
if (toolbarSwitchType.value !== 'lineLibrary' && toolbarSwitchType.value !== 'region') return
if (state.drawSelectionArea) {
state.drawSelectionAreaBox.width = event.offsetX - state.drawSelectionAreaStartPos.x
state.drawSelectionAreaBox.height = event.offsetY - state.drawSelectionAreaStartPos.y
}
}
//
const endDrawSelection = () => {
if (toolbarSwitchType.value !== 'lineLibrary' && toolbarSwitchType.value !== 'region') return
let points = allHistoryList.value[currentIndex.value]
state.drawSelectionArea = false
state.drawSelectionAreaSelectedPoints = points.filter((point) => {
return (
point.x >=
Math.min(
state.drawSelectionAreaStartPos.x,
state.drawSelectionAreaStartPos.x + state.drawSelectionAreaBox.width
) &&
point.x <=
Math.max(
state.drawSelectionAreaStartPos.x,
state.drawSelectionAreaStartPos.x + state.drawSelectionAreaBox.width
) &&
point.y >=
Math.min(
state.drawSelectionAreaStartPos.y,
state.drawSelectionAreaStartPos.y + state.drawSelectionAreaBox.height
) &&
point.y <=
Math.max(
state.drawSelectionAreaStartPos.y,
state.drawSelectionAreaStartPos.y + state.drawSelectionAreaBox.height
)
)
})
console.log(state.drawSelectionAreaSelectedPoints, '选中的')
}
onMounted(() => { onMounted(() => {
imgList.value = [ imgList.value = [
{ {
@ -718,34 +954,66 @@ onMounted(() => {
box-sizing: border-box; box-sizing: border-box;
} }
.top-tool-list { .top-tool {
display: flex; .top-tool-list {
align-items: center;
text-align: center;
background-color: #fff;
margin-top: -20px;
margin-left: -20px;
margin-right: -20px;
padding: 0 14px;
margin-bottom: 20px;
box-shadow: rgba(0, 0, 0, 0.05) 0px 0px 0px 1px;
height: 70px;
.top-tool-item {
display: flex; display: flex;
align-items: center; align-items: center;
text-align: center;
background-color: #fff;
margin-top: -15px;
margin-left: -20px;
margin-right: -20px;
padding: 0 14px;
margin-bottom: 20px;
box-shadow: rgba(0, 0, 0, 0.05) 0px 0px 0px 1px;
height: 70px;
.top-tool-item {
display: flex;
align-items: center;
.tool-item {
cursor: pointer;
width: 52px;
height: 70px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.name {
font-family:
PingFangSC,
PingFang SC;
font-weight: 400;
font-size: 14px;
color: #0d162a;
line-height: 20px;
text-align: center;
font-style: normal;
}
}
.line {
margin: 0 14px;
width: 1px;
height: 47px;
border: 1px solid #cccccc;
}
}
}
.right-tool-list {
position: absolute;
right: 0;
top: 94px;
background-color: #fff;
z-index: 999;
text-align: center;
box-shadow: rgba(0, 0, 0, 0.05) 0px 0px 0px 1px;
.tool-item { .tool-item {
cursor: pointer; cursor: pointer;
// cursor: url('https://api.znkjfw.com/admin-api/infra/file/4/get/_png_179_1738982726561.png'), padding: 10px;
// auto;
width: 52px;
height: 70px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.name { .name {
font-family: font-family:
PingFangSC, PingFangSC,
@ -758,46 +1026,22 @@ onMounted(() => {
font-style: normal; font-style: normal;
} }
} }
.line { }
margin: 0 14px;
width: 1px; .tool-active {
height: 47px; background: #ebf1ff;
border: 1px solid #cccccc; border-bottom: 2px solid #1677ff;
} }
.right-tool-active {
background: #ebf1ff;
} }
} }
.right-tool-list { .input-box-class {
position: absolute; position: absolute;
right: 0; border: 1px solid #00329f;
top: 94px; padding: 4px;
background-color: #fff; outline: none;
z-index: 999;
text-align: center;
box-shadow: rgba(0, 0, 0, 0.05) 0px 0px 0px 1px;
.tool-item {
cursor: pointer;
padding: 10px;
.name {
font-family:
PingFangSC,
PingFang SC;
font-weight: 400;
font-size: 14px;
color: #0d162a;
line-height: 20px;
text-align: center;
font-style: normal;
}
}
}
.tool-active {
background: #ebf1ff;
border-bottom: 2px solid #1677ff;
}
.right-tool-active {
background: #ebf1ff;
} }
</style> </style>

View File

@ -705,7 +705,7 @@ const formData = ref({
skuInfo: undefined, // skuInfo: undefined, //
skuBatch: undefined, // skuBatch: undefined, //
skuNumber: undefined, // skuNumber: undefined, //
priority: undefined, // priority: 50, //
otherMsg: undefined, // otherMsg: undefined, //
doCycle: 0, //(0:1) doCycle: 0, //(0:1)
doMoveAll: 0, //线/(0:1) doMoveAll: 0, //线/(0:1)
@ -740,7 +740,7 @@ const resetFormData = () => {
skuInfo: undefined, // skuInfo: undefined, //
skuBatch: undefined, // skuBatch: undefined, //
skuNumber: undefined, // skuNumber: undefined, //
priority: undefined, // priority: 50, //
otherMsg: undefined, // otherMsg: undefined, //
doCycle: 0, //(0:1) doCycle: 0, //(0:1)
doMoveAll: 0, //线/(0:1) doMoveAll: 0, //线/(0:1)