地图编辑

This commit is contained in:
yyy 2025-02-13 14:31:00 +08:00
parent 3680fcabb0
commit 2bbd29674e
7 changed files with 514 additions and 233 deletions

View File

@ -25,7 +25,12 @@
required required
v-if="form.type === 2" 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"
@change="layersNumberChange"
/>
</el-form-item> </el-form-item>
<el-form-item <el-form-item
label="编号" label="编号"
@ -77,6 +82,7 @@
<span class="ml-2">cm</span> <span class="ml-2">cm</span>
</div> </div>
</el-form-item> </el-form-item>
<div v-if="form.type === 2 || form.type === 4">
<el-form-item label="库位方向" prop="direction"> <el-form-item label="库位方向" prop="direction">
<el-select <el-select
v-model="form.direction" v-model="form.direction"
@ -104,6 +110,7 @@
</el-form-item> </el-form-item>
</div> </div>
</div> </div>
</div>
</el-form> </el-form>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
@ -164,20 +171,17 @@ const submit = async (formEl) => {
form.value.dataJson = '' form.value.dataJson = ''
} else if (form.value.type === 2) { } else if (form.value.type === 2) {
// //
let list = []
for (let index = 0; index < form.value.layersNumber; index++) { for (let index = 0; index < form.value.layersNumber; index++) {
if ( if (
form.value.dataList.length > 0 && form.value.dataList.length > 0 &&
form.value.dataList[index] && form.value.dataList[index] &&
form.value.dataList[index].laneId form.value.dataList[index].id
) { ) {
form.value.dataList[index].locationWide = form.value.locationWide list.push(form.value.dataList[index])
form.value.dataList[index].locationDeep = form.value.locationDeep
form.value.dataList[index].direction = form.value.direction //
form.value.dataList[index].inDirection = form.value.inDirection //
form.value.dataList[index].outDirection = form.value.outDirection //
form.value.dataList[index].locationStorey = index + 1 //
} else { } else {
form.value.dataList.push({ list.push({
positionMapId: props.positionMapId, positionMapId: props.positionMapId,
locationWide: form.value.locationWide || undefined, locationWide: form.value.locationWide || undefined,
locationDeep: form.value.locationDeep || undefined, locationDeep: form.value.locationDeep || undefined,
@ -188,8 +192,18 @@ const submit = async (formEl) => {
}) })
} }
} }
form.value.dataList = list
//dataJson //dataJson
form.value.dataJson = JSON.stringify(form.value.dataList) form.value.dataJson = JSON.stringify(form.value.dataList)
} else if (form.value.type === 3) {
//
form.value.dataObj.positionMapId = props.positionMapId
form.value.dataObj.locationWide = form.value.locationWide
form.value.dataObj.locationDeep = form.value.locationDeep
form.value.dataObj.direction = form.value.direction
form.value.dataObj.inDirection = form.value.inDirection
form.value.dataObj.outDirection = form.value.outDirection
form.value.dataObj.deviceType = deviceInfo.value.deviceType //
} else { } else {
// //
form.value.dataObj.positionMapId = props.positionMapId form.value.dataObj.positionMapId = props.positionMapId
@ -206,9 +220,13 @@ const submit = async (formEl) => {
}) })
} }
//
const layersNumberChange = (e) => {}
const open = (item) => { const open = (item) => {
console.log(item)
form.value = item form.value = item
form.value.layersNumber = item.layersNumber || 1 //1 form.value.layersNumber = item.dataList.length || ''
form.value.positionMapId = props.positionMapId form.value.positionMapId = props.positionMapId
dialogFormVisible.value = true dialogFormVisible.value = true
} }
@ -219,6 +237,12 @@ const typeChange = () => {
form.value.layersNumber = undefined form.value.layersNumber = undefined
form.value.dataJson = '' form.value.dataJson = ''
} }
if (form.value.type !== 2 && form.value.type !== 4) {
form.value.direction = undefined
form.value.inDirection = undefined
form.value.outDirection = undefined
}
} }
// //
const directionChange = (e) => { const directionChange = (e) => {

View File

@ -18,23 +18,26 @@
<script setup> <script setup>
import { reactive, ref } from 'vue' import { reactive, ref } from 'vue'
const emit = defineEmits(['layerSelectionSuccess'])
const list = ref([ const list = ref([
{ {
labelType: 'locationPoint', type: 2,
name: '库位点', name: '库位点',
isShow: true isShow: true
}, },
{ {
labelType: 'wayPoint', type: 5,
name: '路径点', name: '路径点',
isShow: true isShow: true
}, },
{ {
labelType: 'devicePoint', type: 3,
name: '设备点', name: '设备点',
isShow: true isShow: true
} }
]) ])
const isUnfold = ref(true) const isUnfold = ref(true)
const changeUnfold = () => { const changeUnfold = () => {
@ -42,6 +45,8 @@ const changeUnfold = () => {
} }
const changeSelectList = (item) => { const changeSelectList = (item) => {
item.isShow = !item.isShow item.isShow = !item.isShow
const types = list.value.filter((item) => !item.isShow).map((item) => item.type)
emit('layerSelectionSuccess', types)
} }
const open = (item) => {} const open = (item) => {}

View File

@ -130,7 +130,7 @@
> >
<VueDragResizeRotate <VueDragResizeRotate
v-for="(item, index) in allHistoryList[currentIndex]" v-for="(item, index) in allHistoryList[currentIndex]"
:key="index" :key="item.locationX"
:parent="true" :parent="true"
:x="item.locationX" :x="item.locationX"
:y="item.locationY" :y="item.locationY"
@ -150,7 +150,7 @@
> >
<!-- 1 路径点 --> <!-- 1 路径点 -->
<div <div
v-if="item.type === 1" v-if="item.type === 1 && item.layerSelectionShow"
:style="{ :style="{
width: item.locationWide + 'px', width: item.locationWide + 'px',
height: item.locationDeep + 'px', height: item.locationDeep + 'px',
@ -162,7 +162,7 @@
</div> </div>
<!-- 2 库位点 --> <!-- 2 库位点 -->
<img <img
v-if="item.type === 2" v-if="item.type === 2 && item.layerSelectionShow"
src="https://api.znkjfw.com/admin-api/infra/file/4/get/库位库存_png_179_1739326653035.png" src="https://api.znkjfw.com/admin-api/infra/file/4/get/库位库存_png_179_1739326653035.png"
alt="" alt=""
:style="{ :style="{
@ -173,7 +173,7 @@
/> />
<!-- 3 设备点 --> <!-- 3 设备点 -->
<img <img
v-if="item.type === 3" v-if="item.type === 3 && item.layerSelectionShow"
src="https://api.znkjfw.com/admin-api/infra/file/4/get/设备点_png_179_1739327151877.png" src="https://api.znkjfw.com/admin-api/infra/file/4/get/设备点_png_179_1739327151877.png"
alt="" alt=""
:style="{ :style="{
@ -184,7 +184,7 @@
/> />
<!-- 4 停车点 --> <!-- 4 停车点 -->
<img <img
v-if="item.type === 4" v-if="item.type === 4 && item.layerSelectionShow"
src="https://api.znkjfw.com/admin-api/infra/file/4/get/停车场-01_png_179_1739326933020.png" src="https://api.znkjfw.com/admin-api/infra/file/4/get/停车场-01_png_179_1739326933020.png"
alt="" alt=""
:style="{ :style="{
@ -195,7 +195,7 @@
/> />
<!-- 5 区域变更点 --> <!-- 5 区域变更点 -->
<img <img
v-if="item.type === 5" v-if="item.type === 5 && item.layerSelectionShow"
src="https://api.znkjfw.com/admin-api/infra/file/4/get/区域_png_179_1739327151876.png" src="https://api.znkjfw.com/admin-api/infra/file/4/get/区域_png_179_1739327151876.png"
alt="" alt=""
:style="{ :style="{
@ -206,7 +206,7 @@
/> />
<!-- 6 等待点 --> <!-- 6 等待点 -->
<img <img
v-if="item.type === 6" v-if="item.type === 6 && item.layerSelectionShow"
src="https://api.znkjfw.com/admin-api/infra/file/4/get/等待点_png_179_1739326991439.png" src="https://api.znkjfw.com/admin-api/infra/file/4/get/等待点_png_179_1739326991439.png"
alt="" alt=""
:style="{ :style="{
@ -216,7 +216,7 @@
}" }"
/> />
<div <div
v-if="item.type === 7" v-if="item.type === 7 && item.layerSelectionShow"
:style="{ :style="{
color: item.fontColor, color: item.fontColor,
fontSize: item.fontSize + 'px', fontSize: item.fontSize + 'px',
@ -232,17 +232,18 @@
<!-- 绘制框选区域 --> <!-- 绘制框选区域 -->
<div <div
v-if="state.drawSelectionArea" v-if="state.drawSelectionAreaSelectedPoints"
:style="{ :style="{
position: 'absolute', position: 'absolute',
left: `${state.drawSelectionAreaBox.x}px`, left: `${state.drawSelectionAreaBox.x}px`,
top: `${state.drawSelectionAreaBox.y}px`, top: `${state.drawSelectionAreaBox.y}px`,
width: `${state.drawSelectionAreaBox.width}px`, width: `${state.drawSelectionAreaBox.width}px`,
height: `${state.drawSelectionAreaBox.height}px`, height: `${state.drawSelectionAreaBox.height}px`,
border: '1px solid blue', border: '2px dashed #007bff',
backgroundColor: 'rgba(0, 0, 255, 0.1)', backgroundColor: 'rgba(0, 123, 255, 0.1)',
zIndex: 99999 zIndex: 99999
}" }"
@click="clickDrawSelectionArea"
></div> ></div>
<!-- 文字输入区域 --> <!-- 文字输入区域 -->
<input <input
@ -276,7 +277,11 @@
@textFormSuccess="textFormSuccess" @textFormSuccess="textFormSuccess"
/> />
<!-- 图层选择 --> <!-- 图层选择 -->
<layerSelectionToolDialog v-if="state.isShowLayer" ref="layerSelectionToolDialogRef" /> <layerSelectionToolDialog
v-if="state.isShowLayer"
ref="layerSelectionToolDialogRef"
@layerSelectionSuccess="layerSelectionSuccess"
/>
<!-- 设备弹窗选择 --> <!-- 设备弹窗选择 -->
<equipmentToolDialog ref="equipmentToolDialogRef" :positionMapId="imgBgObj.positionMapId" /> <equipmentToolDialog ref="equipmentToolDialogRef" :positionMapId="imgBgObj.positionMapId" />
</template> </template>
@ -391,6 +396,7 @@ const mapClick = (e) => {
// //
allMapPointInfo.value.push({ allMapPointInfo.value.push({
positionMapId: imgBgObj.positionMapId, //id positionMapId: imgBgObj.positionMapId, //id
layerSelectionShow: true,
locationX: e.offsetX, locationX: e.offsetX,
locationY: e.offsetY, locationY: e.offsetY,
locationDeep: 16, locationDeep: 16,
@ -449,7 +455,8 @@ const handleInputEnd = () => {
fontColor: state.inputBoxStyle.fontColor, // fontColor: state.inputBoxStyle.fontColor, //
fontFamily: state.inputBoxStyle.fontFamily, // fontFamily: state.inputBoxStyle.fontFamily, //
fontSize: state.inputBoxStyle.fontSize, fontSize: state.inputBoxStyle.fontSize,
dataObj: {} // dataObj: {}, //
layerSelectionShow: true
}) })
addEditHistory() addEditHistory()
state.inputBoxValue = '' state.inputBoxValue = ''
@ -523,7 +530,7 @@ const state = reactive({
switchType: 'tools', switchType: 'tools',
name: '工具', name: '工具',
icon: 'ep:briefcase', icon: 'ep:briefcase',
isActive: false isActive: true
}, },
{ {
switchType: 'lineLibrary', switchType: 'lineLibrary',
@ -571,7 +578,7 @@ const state = reactive({
switchType: 'grid', switchType: 'grid',
name: '网格', name: '网格',
icon: 'ep:grid', icon: 'ep:grid',
isActive: false isActive: true
}, },
{ {
switchType: 'larger', switchType: 'larger',
@ -624,8 +631,8 @@ const state = reactive({
isActive: false isActive: false
} }
], ],
isShowToolbar: false, // isShowToolbar: true, //
isShowGrid: false, // isShowGrid: true, //
moveForm: { moveForm: {
locationX: '', locationX: '',
locationY: '' locationY: ''
@ -635,7 +642,7 @@ const state = reactive({
}, // }, //
copyMapItem: '', // copyMapItem: '', //
cursorStyle: 'auto', cursorStyle: 'auto',
drawSelectionArea: false, // drawSelectionAreaShow: false, //
drawSelectionAreaBox: { x: 0, y: 0, width: 0, height: 0 }, // drawSelectionAreaBox: { x: 0, y: 0, width: 0, height: 0 }, //
drawSelectionAreaStartPos: { x: 0, y: 0 }, // drawSelectionAreaStartPos: { x: 0, y: 0 }, //
drawSelectionAreaSelectedPoints: [], //list drawSelectionAreaSelectedPoints: [], //list
@ -654,7 +661,7 @@ const state = reactive({
const toolbarClick = (item) => { const toolbarClick = (item) => {
let type = item.switchType let type = item.switchType
if ( if (
currentItemIndex.value && currentItemIndex.value === -1 &&
(type === 'move' || type === 'revolve' || type === 'copy' || type === 'delete') (type === 'move' || type === 'revolve' || type === 'copy' || type === 'delete')
) { ) {
message.warning('请先选择要操作的对象') message.warning('请先选择要操作的对象')
@ -716,13 +723,29 @@ const toolbarClick = (item) => {
break break
case 'copy': case 'copy':
// //
state.copyMapItem = allHistoryList.value[currentIndex.value][currentItemIndex.value] let copyMapItem = allHistoryList.value[currentIndex.value][currentItemIndex.value]
state.copyMapItem = {
positionMapId: copyMapItem.positionMapId,
locationX: copyMapItem.locationX,
locationY: copyMapItem.locationY,
type: copyMapItem.type,
dataJson: copyMapItem.dataJson || '',
dataObj: copyMapItem.dataObj || {},
dataList: copyMapItem.dataList || [],
locationDeep: copyMapItem.locationDeep,
locationWide: copyMapItem.locationWide,
draggable: copyMapItem.draggable,
resizable: copyMapItem.resizable,
rotatable: copyMapItem.rotatable,
lockAspectRatio: copyMapItem.lockAspectRatio
}
message.success('复制成功')
break break
case 'paste': case 'paste':
// //
let copyObj = JSON.parse(JSON.stringify(state.copyMapItem)) let copyObj = JSON.parse(JSON.stringify(state.copyMapItem))
copyObj.locationX = copyObj.locationX + 50 copyObj.locationX = Number(copyObj.locationX) + 50
copyObj.locationY = copyObj.locationY + 50 copyObj.locationY = Number(copyObj.locationY) + 50
allMapPointInfo.value.push(copyObj) allMapPointInfo.value.push(copyObj)
addEditHistory() addEditHistory()
break break
@ -845,14 +868,14 @@ const rotationFormSubmit = () => {
// //
const startDrawSelection = (event) => { const startDrawSelection = (event) => {
if (toolbarSwitchType.value !== 'lineLibrary' && toolbarSwitchType.value !== 'region') return if (toolbarSwitchType.value !== 'lineLibrary' && toolbarSwitchType.value !== 'region') return
state.drawSelectionArea = true state.drawSelectionAreaShow = true
state.drawSelectionAreaStartPos = { x: event.offsetX, y: event.offsetY } state.drawSelectionAreaStartPos = { x: event.offsetX, y: event.offsetY }
state.drawSelectionAreaBox = { x: event.offsetX, y: event.offsetY, width: 0, height: 0 } state.drawSelectionAreaBox = { x: event.offsetX, y: event.offsetY, width: 0, height: 0 }
} }
const updateDrawSelection = (event) => { const updateDrawSelection = (event) => {
if (toolbarSwitchType.value !== 'lineLibrary' && toolbarSwitchType.value !== 'region') return if (toolbarSwitchType.value !== 'lineLibrary' && toolbarSwitchType.value !== 'region') return
if (state.drawSelectionArea) { if (state.drawSelectionAreaShow) {
state.drawSelectionAreaBox.width = event.offsetX - state.drawSelectionAreaStartPos.x state.drawSelectionAreaBox.width = event.offsetX - state.drawSelectionAreaStartPos.x
state.drawSelectionAreaBox.height = event.offsetY - state.drawSelectionAreaStartPos.y state.drawSelectionAreaBox.height = event.offsetY - state.drawSelectionAreaStartPos.y
} }
@ -860,8 +883,8 @@ const updateDrawSelection = (event) => {
// //
const endDrawSelection = () => { const endDrawSelection = () => {
if (toolbarSwitchType.value !== 'lineLibrary' && toolbarSwitchType.value !== 'region') return if (toolbarSwitchType.value !== 'lineLibrary' && toolbarSwitchType.value !== 'region') return
state.drawSelectionAreaShow = false
let points = allHistoryList.value[currentIndex.value] let points = allHistoryList.value[currentIndex.value]
state.drawSelectionArea = false
state.drawSelectionAreaSelectedPoints = points.filter((point) => { state.drawSelectionAreaSelectedPoints = points.filter((point) => {
return ( return (
point.locationX >= point.locationX >=
@ -886,7 +909,17 @@ const endDrawSelection = () => {
) )
) )
}) })
console.log(state.drawSelectionAreaSelectedPoints, '选中的') }
//
const clickDrawSelectionArea = () => {
if (toolbarSwitchType.value === 'lineLibrary') {
//线
}
if (toolbarSwitchType.value === 'region') {
//
}
// console.log(state.drawSelectionAreaSelectedPoints, '')
} }
// //
@ -938,33 +971,37 @@ const getAllNodeList = async () => {
positionMapId: imgBgObj.positionMapId positionMapId: imgBgObj.positionMapId
}) })
list.forEach((item) => { list.forEach((item) => {
item.layerSelectionShow = true //
item.locationX = Number(item.locationX)
item.locationY = Number(item.locationY)
if (item.type === 1 || item.type === 5 || item.type === 6) { if (item.type === 1 || item.type === 5 || item.type === 6) {
item.dataObj = {} item.dataObj = {}
item.dataList = [] item.dataList = []
item.locationDeep = 16 item.locationDeep = 16
item.locationWide = 16 item.locationWide = 16
item.draggable = true item.draggable = false
item.resizable = false item.resizable = false
item.rotatable = false item.rotatable = false
item.lockAspectRatio = false item.lockAspectRatio = true
} else if (item.type === 2) { } else if (item.type === 2) {
// //
item.dataList = JSON.parse(item.dataJson) item.dataList = JSON.parse(item.dataJson)
item.locationDeep = item.dataList[0].locationDeep item.locationDeep = item.dataList[0].locationDeep
item.locationWide = item.dataList[0].locationWide item.locationWide = item.dataList[0].locationWide
item.draggable = true item.draggable = false
item.resizable = false item.resizable = false
item.rotatable = false item.rotatable = false
item.lockAspectRatio = false item.lockAspectRatio = true
} else if (item.type === 3 || item.type === 4) { } else if (item.type === 3 || item.type === 4) {
item.dataObj = JSON.parse(item.dataJson) item.dataObj = JSON.parse(item.dataJson)
item.dataList = [] item.dataList = []
item.locationDeep = item.dataObj.locationDeep item.locationDeep = item.dataObj.locationDeep
item.locationWide = item.dataObj.locationWide item.locationWide = item.dataObj.locationWide
item.draggable = true item.draggable = false
item.resizable = false item.resizable = false
item.rotatable = false item.rotatable = false
item.lockAspectRatio = false item.lockAspectRatio = true
} else if (item.type === 7) { } else if (item.type === 7) {
item.dataObj = JSON.parse(item.dataJson) item.dataObj = JSON.parse(item.dataJson)
item.text = item.dataObj.text item.text = item.dataObj.text
@ -974,8 +1011,8 @@ const getAllNodeList = async () => {
item.angle = item.dataObj.angle item.angle = item.dataObj.angle
item.draggable = true item.draggable = true
item.resizable = false item.resizable = false
item.rotatable = false item.rotatable = true
item.lockAspectRatio = false item.lockAspectRatio = true
} }
allMapPointInfo.value.push(item) allMapPointInfo.value.push(item)
}) })
@ -1015,6 +1052,18 @@ const saveNodeList = async () => {
} }
} }
//
const layerSelectionSuccess = (typeList) => {
allHistoryList.value[currentIndex.value].forEach((item) => {
// type layerSelectionShow false
if (typeList.includes(item.type)) {
item.layerSelectionShow = false
} else {
item.layerSelectionShow = true
}
})
}
onMounted(() => { onMounted(() => {
getMapList() getMapList()
}) })

View File

@ -1,180 +1,132 @@
<template> <template>
<div> <div
<!-- SVG 画布 --> @mousedown="startSelection"
<svg @mousemove="updateSelection"
ref="svg" @mouseup="endSelection"
width="500" style="position: relative; height: 100vh; border: 1px solid #ccc"
height="300"
@mousedown="handleMouseDown"
@mousemove="handleMouseMove"
@mouseup="handleMouseUp"
> >
<!-- 绘制所有曲线 --> <!-- 框选区域 -->
<path <div
v-for="(curve, index) in curves" v-if="isSelecting || selectionBox.width > 0"
:key="index" :style="{
:d="getCurvePath(curve)" position: 'absolute',
stroke="#000" left: `${selectionBox.x}px`,
fill="none" top: `${selectionBox.y}px`,
stroke-width="2" width: `${selectionBox.width}px`,
:class="{ selected: selectedCurve === curve }" height: `${selectionBox.height}px`,
@click="svgClick(curve)" border: '2px dashed #007bff',
/> backgroundColor: 'rgba(0, 123, 255, 0.1)'
<!-- 绘制控制点和连线 --> }"
<line ></div>
v-if="selectedCurve"
:x1="selectedCurve.start.x"
:y1="selectedCurve.start.y"
:x2="selectedCurve.control1.x"
:y2="selectedCurve.control1.y"
stroke="gray"
stroke-dasharray="2"
/>
<line
v-if="selectedCurve"
:x1="selectedCurve.end.x"
:y1="selectedCurve.end.y"
:x2="selectedCurve.control2.x"
:y2="selectedCurve.control2.y"
stroke="gray"
stroke-dasharray="2"
/>
<!-- 绘制起点终点和控制点 -->
<circle
v-for="(curve, index) in curves"
:key="'start' + index"
:cx="curve.start.x"
:cy="curve.start.y"
r="5"
fill="red"
@mousedown="startDrag($event, curve, 'start')"
/>
<circle
v-for="(curve, index) in curves"
:key="'end' + index"
:cx="curve.end.x"
:cy="curve.end.y"
r="5"
fill="red"
@mousedown="startDrag($event, curve, 'end')"
/>
<circle
v-if="selectedCurve"
:cx="selectedCurve.control1.x"
:cy="selectedCurve.control1.y"
r="5"
fill="green"
@mousedown="startDrag($event, selectedCurve, 'control1')"
/>
<circle
v-if="selectedCurve"
:cx="selectedCurve.control2.x"
:cy="selectedCurve.control2.y"
r="5"
fill="green"
@mousedown="startDrag($event, selectedCurve, 'control2')"
/>
</svg>
<!-- 显示控制点坐标 --> <!-- 坐标点 -->
<div class="points" v-if="selectedCurve"> <div
<p>起点: ({{ selectedCurve.start.x }}, {{ selectedCurve.start.y }})</p> v-for="(point, index) in points"
<p>控制点 1: ({{ selectedCurve.control1.x }}, {{ selectedCurve.control1.y }})</p> :key="index"
<p>控制点 2: ({{ selectedCurve.control2.x }}, {{ selectedCurve.control2.y }})</p> :style="{
<p>终点: ({{ selectedCurve.end.x }}, {{ selectedCurve.end.y }})</p> position: 'absolute',
</div> left: `${point.x}px`,
top: `${point.y}px`,
width: '10px',
height: '10px',
backgroundColor: selectedPoints.includes(point) ? 'red' : 'blue',
borderRadius: '50%'
}"
></div>
</div> </div>
</template> </template>
<script setup> <script>
import { ref } from 'vue' import { ref } from 'vue'
const svg = ref(null) // SVG
const curves = ref([]) // 线
const selectedCurve = ref(null) // 线
const isDragging = ref(false) //
const dragTarget = ref(null) //
// export default {
const getMousePos = (event) => { setup() {
const rect = svg.value.getBoundingClientRect() //
const points = ref([
{ x: 300, y: 400 },
{ x: 400, y: 400 },
{ x: 200, y: 360 },
{ x: 570, y: 260 },
{ x: 600, y: 477 }
])
//
const selectionBox = ref({
x: 0,
y: 0,
width: 0,
height: 0
})
//
const isSelecting = ref(false)
//
const selectedPoints = ref([])
//
const startPos = ref({ x: 0, y: 0 })
//
const startSelection = (event) => {
isSelecting.value = true
startPos.value = { x: event.offsetX, y: event.offsetY }
selectionBox.value = {
x: event.offsetX,
y: event.offsetY,
width: 0,
height: 0
}
}
//
const updateSelection = (event) => {
if (!isSelecting.value) return
const offsetX = event.offsetX
const offsetY = event.offsetY
selectionBox.value = {
x: Math.min(startPos.value.x, offsetX),
y: Math.min(startPos.value.y, offsetY),
width: Math.abs(offsetX - startPos.value.x),
height: Math.abs(offsetY - startPos.value.y)
}
}
//
const endSelection = () => {
isSelecting.value = false
checkSelectedPoints()
}
//
const checkSelectedPoints = () => {
selectedPoints.value = points.value.filter((point) => {
return (
point.x >= selectionBox.value.x &&
point.x <= selectionBox.value.x + selectionBox.value.width &&
point.y >= selectionBox.value.y &&
point.y <= selectionBox.value.y + selectionBox.value.height
)
})
console.log('选中的点:', selectedPoints.value)
}
return { return {
x: event.clientX - rect.left, points,
y: event.clientY - rect.top selectionBox,
isSelecting,
selectedPoints,
startSelection,
updateSelection,
endSelection
} }
}
//
const startDrag = (event, curve, target) => {
event.preventDefault()
isDragging.value = true
selectedCurve.value = curve
dragTarget.value = target
}
//
const handleMouseDown = (event) => {
const mousePos = getMousePos(event)
// 线
if (!isDragging.value && curves.value.length === 0) {
const newCurve = {
start: mousePos,
end: mousePos,
control1: { x: mousePos.x + 50, y: mousePos.y - 50 },
control2: { x: mousePos.x + 100, y: mousePos.y - 50 }
} }
curves.value.push(newCurve)
selectedCurve.value = newCurve
dragTarget.value = 'end'
isDragging.value = true
}
}
//
const handleMouseMove = (event) => {
if (!isDragging.value || !selectedCurve.value) return
const mousePos = getMousePos(event)
//
if (dragTarget.value === 'start') {
selectedCurve.value.start = mousePos
} else if (dragTarget.value === 'end') {
selectedCurve.value.end = mousePos
} else if (dragTarget.value === 'control1') {
selectedCurve.value.control1 = mousePos
} else if (dragTarget.value === 'control2') {
selectedCurve.value.control2 = mousePos
}
}
//
const handleMouseUp = () => {
isDragging.value = false
dragTarget.value = null
}
// 线
const getCurvePath = (curve) => {
return `M ${curve.start.x} ${curve.start.y} C ${curve.control1.x} ${curve.control1.y}, ${curve.control2.x} ${curve.control2.y}, ${curve.end.x} ${curve.end.y}`
}
const svgClick = (item) => {
console.log(item)
} }
</script> </script>
<style scoped> <style scoped>
svg { /* 可选:添加一些样式 */
border: 1px solid #000;
cursor: crosshair;
}
path.selected {
stroke: blue;
}
.points {
margin-top: 10px;
font-size: 14px;
}
</style> </style>

View File

@ -18,3 +18,74 @@
fontFamily: '' //字体类型 fontFamily: '' //字体类型
} }
``` ```
```
1.普通节点 dataJson 不传数据
2.库位点 dataJson 数据格式:
[
{
"id": 1881176241622024200, //库位id - 编辑的时候会有需要传过来 新增的时候没有则不传
"laneId": 1881177420829642800, //线库id - 编辑的时候有就传过来 新增的时候没有就不传
"laneName": "测试33", //线库名称 - 编辑的时候有就传过来 新增的时候没有就不传
"locationX": "2", //库位坐标x轴 - 编辑的时候有就传过来 新增的时候不用传
"locationY": "7", //库位坐标y轴 - 编辑的时候有就传过来 新增的时候不用传
"locationWide": 10, //宽度 - 编辑的时候有就传过来 新增的时候需要传
"locationDeep": 10, //高度 - 编辑的时候有就传过来 新增的时候需要传
"direction": 1, //库位方向1单向、2双向、3三向、4四向 编辑的时候有就传过来 新增的时候需要传
"inDirection": 1, //进入方向0尾入、1头入 编辑的时候有就传过来 新增的时候需要传
"outDirection": 1, //离开方向0尾出、1头出 编辑的时候有就传过来 新增的时候需要传
"locationStorey": 3, //层数 编辑的时候有就传过来 新增的时候需要传
"mapId": 1, //地图id 编辑的时候有就传过来 新增的时候不需要传
"mapItemId": 1881176241622024200 //地图子表id 编辑的时候有就传过来 新增的时候不需要传
},
{
"id": 1881176241622024200,
"laneId": 1881177420829642800,
"laneName": "测试33",
"locationX": "2",
"locationY": "7",
"locationWide": 10,
"locationDeep": 10,
"direction": 1,
"inDirection": 1,
"outDirection": 1,
"locationStorey": 3,
"mapId": 1,
"mapItemId": 1881176241622024200
}
]
3.设备点 dataJson 数据格式:
{
"id": 1881176241622024200, //设备id - 新增编辑都需要传 - 根据选择的设备获取到
"locationX": "2", //库位坐标x轴 - 编辑的时候有就传过来 新增的时候不用传
"locationY": "7", //库位坐标y轴 - 编辑的时候有就传过来 新增的时候不用传
"locationWide": 10, //宽度 - 编辑的时候有就传过来 新增的时候需要传
"locationDeep": 10, //高度 - 编辑的时候有就传过来 新增的时候需要传
"mapId": 1, //地图id 编辑的时候有就传过来 新增的时候不需要传
"mapItemId": 1881176241622024200 //地图子表id 编辑的时候有就传过来 新增的时候不需要传
}
4.停车点 dataJson 数据格式:
{
"id": 1881176241622024200, //停车点id - 编辑的时候会有需要传过来 新增的时候没有则不传
"locationX": "2", //库位坐标x轴 - 编辑的时候有就传过来 新增的时候不用传
"locationY": "7", //库位坐标y轴 - 编辑的时候有就传过来 新增的时候不用传
"locationWide": 10, //宽度 - 编辑的时候有就传过来 新增的时候需要传
"locationDeep": 10, //高度 - 编辑的时候有就传过来 新增的时候需要传
"direction": 1, //库位方向1单向、2双向、3三向、4四向 编辑的时候有就传过来 新增的时候需要传
"inDirection": 1, //进入方向0尾入、1头入 编辑的时候有就传过来 新增的时候需要传
"outDirection": 1, //离开方向0尾出、1头出 编辑的时候有就传过来 新增的时候需要传
"mapId": 1, //地图id 编辑的时候有就传过来 新增的时候不需要传
"mapItemId": 1881176241622024200 //地图子表id 编辑的时候有就传过来 新增的时候不需要传
},
5.路径点 dataJson 不传数据
6.等待点 dataJson 不传数据
```

View File

@ -0,0 +1,180 @@
<template>
<div>
<!-- SVG 画布 -->
<svg
ref="svg"
width="500"
height="300"
@mousedown="handleMouseDown"
@mousemove="handleMouseMove"
@mouseup="handleMouseUp"
>
<!-- 绘制所有曲线 -->
<path
v-for="(curve, index) in curves"
:key="index"
:d="getCurvePath(curve)"
stroke="#000"
fill="none"
stroke-width="2"
:class="{ selected: selectedCurve === curve }"
@click="svgClick(curve)"
/>
<!-- 绘制控制点和连线 -->
<line
v-if="selectedCurve"
:x1="selectedCurve.start.x"
:y1="selectedCurve.start.y"
:x2="selectedCurve.control1.x"
:y2="selectedCurve.control1.y"
stroke="gray"
stroke-dasharray="2"
/>
<line
v-if="selectedCurve"
:x1="selectedCurve.end.x"
:y1="selectedCurve.end.y"
:x2="selectedCurve.control2.x"
:y2="selectedCurve.control2.y"
stroke="gray"
stroke-dasharray="2"
/>
<!-- 绘制起点终点和控制点 -->
<circle
v-for="(curve, index) in curves"
:key="'start' + index"
:cx="curve.start.x"
:cy="curve.start.y"
r="5"
fill="red"
@mousedown="startDrag($event, curve, 'start')"
/>
<circle
v-for="(curve, index) in curves"
:key="'end' + index"
:cx="curve.end.x"
:cy="curve.end.y"
r="5"
fill="red"
@mousedown="startDrag($event, curve, 'end')"
/>
<circle
v-if="selectedCurve"
:cx="selectedCurve.control1.x"
:cy="selectedCurve.control1.y"
r="5"
fill="green"
@mousedown="startDrag($event, selectedCurve, 'control1')"
/>
<circle
v-if="selectedCurve"
:cx="selectedCurve.control2.x"
:cy="selectedCurve.control2.y"
r="5"
fill="green"
@mousedown="startDrag($event, selectedCurve, 'control2')"
/>
</svg>
<!-- 显示控制点坐标 -->
<div class="points" v-if="selectedCurve">
<p>起点: ({{ selectedCurve.start.x }}, {{ selectedCurve.start.y }})</p>
<p>控制点 1: ({{ selectedCurve.control1.x }}, {{ selectedCurve.control1.y }})</p>
<p>控制点 2: ({{ selectedCurve.control2.x }}, {{ selectedCurve.control2.y }})</p>
<p>终点: ({{ selectedCurve.end.x }}, {{ selectedCurve.end.y }})</p>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const svg = ref(null) // SVG
const curves = ref([]) // 线
const selectedCurve = ref(null) // 线
const isDragging = ref(false) //
const dragTarget = ref(null) //
//
const getMousePos = (event) => {
const rect = svg.value.getBoundingClientRect()
return {
x: event.clientX - rect.left,
y: event.clientY - rect.top
}
}
//
const startDrag = (event, curve, target) => {
event.preventDefault()
isDragging.value = true
selectedCurve.value = curve
dragTarget.value = target
}
//
const handleMouseDown = (event) => {
const mousePos = getMousePos(event)
// 线
if (!isDragging.value && curves.value.length === 0) {
const newCurve = {
start: mousePos,
end: mousePos,
control1: { x: mousePos.x + 50, y: mousePos.y - 50 },
control2: { x: mousePos.x + 100, y: mousePos.y - 50 }
}
curves.value.push(newCurve)
selectedCurve.value = newCurve
dragTarget.value = 'end'
isDragging.value = true
}
}
//
const handleMouseMove = (event) => {
if (!isDragging.value || !selectedCurve.value) return
const mousePos = getMousePos(event)
//
if (dragTarget.value === 'start') {
selectedCurve.value.start = mousePos
} else if (dragTarget.value === 'end') {
selectedCurve.value.end = mousePos
} else if (dragTarget.value === 'control1') {
selectedCurve.value.control1 = mousePos
} else if (dragTarget.value === 'control2') {
selectedCurve.value.control2 = mousePos
}
}
//
const handleMouseUp = () => {
isDragging.value = false
dragTarget.value = null
}
// 线
const getCurvePath = (curve) => {
return `M ${curve.start.x} ${curve.start.y} C ${curve.control1.x} ${curve.control1.y}, ${curve.control2.x} ${curve.control2.y}, ${curve.end.x} ${curve.end.y}`
}
const svgClick = (item) => {
console.log(item)
}
</script>
<style scoped>
svg {
border: 1px solid #000;
cursor: crosshair;
}
path.selected {
stroke: blue;
}
.points {
margin-top: 10px;
font-size: 14px;
}
</style>