edgarhrh
5 months ago
5 changed files with 3427 additions and 6479 deletions
@ -0,0 +1,462 @@ |
|||||
|
<template> |
||||
|
<div class="sys-role-container"> |
||||
|
<el-card shadow="hover" :body-style="{ paddingBottom: '0' }"> |
||||
|
<el-form :model="state.queryParams" ref="queryForm" :inline="true"> |
||||
|
<el-form-item> |
||||
|
<el-button-group> |
||||
|
<el-button icon="ele-Refresh" @click="resetMap"> 重置地图 </el-button> |
||||
|
</el-button-group> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
</el-card> |
||||
|
|
||||
|
<el-card class="full-table" shadow="hover" style="margin-top: 5px" v-loading="state.loading" element-loading-text="Loading..."> |
||||
|
<div id="container" class="container" /> |
||||
|
</el-card> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts" setup name="sysRole"> |
||||
|
import { onMounted, reactive, ref } from 'vue'; |
||||
|
import Konva from 'konva'; |
||||
|
import { ElMessageBox, ElMessage } from 'element-plus'; |
||||
|
import mapData from './map/lab0522xiezuo(1).json'; |
||||
|
const state = reactive({ |
||||
|
loading: false, |
||||
|
queryParams: { |
||||
|
name: undefined, |
||||
|
code: undefined, |
||||
|
}, |
||||
|
}); |
||||
|
const mapCanvas = reactive<Record<string, any>>({ |
||||
|
// 地图JSON |
||||
|
mapData: {}, |
||||
|
// agvImg: require('@/assets/agv.png'), |
||||
|
//每种图形的实例 |
||||
|
circle: null, |
||||
|
allGroup: [], |
||||
|
rect: null, |
||||
|
arrow: null, |
||||
|
text: null, |
||||
|
// 页面canvas舞台 |
||||
|
stage: null, |
||||
|
//根据页面宽高以及实际地图计算出来的缩放比例 |
||||
|
widthScale: 0, |
||||
|
heightScale: 0, |
||||
|
// canvas图层 |
||||
|
layer: null, |
||||
|
//记录贝塞尔曲线的实例,也就是路径 |
||||
|
bezier: [], |
||||
|
//贝塞尔曲线辅助线,默认不显示,当拖拽曲线路径才显示 |
||||
|
dottedLinesArr: [], |
||||
|
//贝塞尔曲线可拖动的控制点列表 |
||||
|
anchorArr: [], |
||||
|
//贝塞尔曲线长度 |
||||
|
bezierLength: 0, |
||||
|
//贝塞尔曲线所有点的集合 |
||||
|
anchorPoints: [], |
||||
|
//地图可编辑模式 |
||||
|
showEditMap: false, |
||||
|
//当前选中的站点信息 |
||||
|
advancedPoint: {}, |
||||
|
//当前选中的路径信息 |
||||
|
advancedCurve: {}, |
||||
|
}); |
||||
|
|
||||
|
// 开始绘制 |
||||
|
const draw = () => { |
||||
|
const { normalPosList, advancedPointList, advancedCurveList } = mapCanvas.mapData; |
||||
|
|
||||
|
// 绘制普通点 |
||||
|
if (normalPosList?.length) { |
||||
|
for (let i = 0; i < normalPosList.length; i += 3) { |
||||
|
const x = normalPosList[i].x * mapCanvas.widthScale; |
||||
|
const y = normalPosList[i].y * mapCanvas.heightScale; |
||||
|
const circle = drawCircle(x, y, 2, '#c7d4ca'); |
||||
|
mapCanvas.layer.add(circle); |
||||
|
} |
||||
|
} |
||||
|
// 绘制矩形 |
||||
|
for (let i = 0; i < advancedPointList.length; i++) { |
||||
|
const list = advancedPointList[i]; |
||||
|
// mapCanvas.tableData[0].Interference.push({ name: list.instanceName, type: 'allGroup', i }); |
||||
|
const x = list.pos.x * mapCanvas.widthScale; |
||||
|
const y = list.pos.y * mapCanvas.heightScale; |
||||
|
const number = list.instanceName.substring(2); |
||||
|
const name = list.instanceName.substring(0, 2); |
||||
|
if (list.dir) list.dir = parseFloat(list.dir).toFixed(3); |
||||
|
let rotation = (list.dir / Math.PI) * 180; |
||||
|
rotation = isNaN(rotation) ? 0 : rotation; |
||||
|
const color = list?.enable ? '#c5b8af' : '#1ABC9C'; |
||||
|
const width = 50; |
||||
|
const height = 37; |
||||
|
mapCanvas.rect = new Konva.Rect({ |
||||
|
opacity: 0.5, |
||||
|
x, |
||||
|
y, |
||||
|
width, |
||||
|
height, |
||||
|
color, |
||||
|
// perfectDrawEnabled: false, |
||||
|
offset: { x: width / 2, y: height / 2 }, |
||||
|
rotation, |
||||
|
draggable: false, |
||||
|
// listening: false, |
||||
|
}); |
||||
|
mapCanvas.rect.cache(); |
||||
|
const group = new Konva.Group({ name: list.instanceName }); |
||||
|
const rect = drawRect(x, y, rotation, color); |
||||
|
const circle = drawCircle(x, y, 3, 'red'); |
||||
|
const arrow = drawArrow(x, y, width, rotation); |
||||
|
const text1 = drawText(x, y, width, height, name, 'top', rotation); |
||||
|
const text2 = drawText(x, y, width, height, number, 'bottom', rotation); |
||||
|
// group.on('click', (evt) => { |
||||
|
// evt.cancelBubble = true; //阻止事件向上冒泡 才能拿到group 对象 |
||||
|
// setCurrentPoint(i); |
||||
|
// if (!mapCanvas.showInterference) setCurrentTarget(evt.currentTarget.attrs.name, 'fill', evt.currentTarget); |
||||
|
// else setInterference(mapCanvas.advancedPoint); //如果在查看冲突状态需要设置冲突颜色 |
||||
|
// // if (mapCanvas.drawer && mapCanvas.drawerTabsName === 'second') showInterferenceForm(mapCanvas.advancedPoint); //显示冲突的表单 |
||||
|
// }); |
||||
|
group.on('mouseover', () => (document.body.style.cursor = 'pointer')); |
||||
|
group.on('mouseout', () => (document.body.style.cursor = 'default')); |
||||
|
group.add(rect, circle, arrow, text1, text2); |
||||
|
mapCanvas.allGroup.push(group); |
||||
|
mapCanvas.layer.add(group); |
||||
|
} |
||||
|
// 绘制贝塞尔曲线 或 直线 |
||||
|
for (let i = 0; i < advancedCurveList.length; i += 2) { |
||||
|
const list = advancedCurveList[i]; |
||||
|
|
||||
|
const color = list?.enable ? '#c5b8af' : '#2C3E50'; |
||||
|
// mapCanvas.tableData[1].Interference.push({ name: list.instanceName, type: 'bezier', i: i / 2 }); |
||||
|
let arr = []; |
||||
|
arr[i] = {}; |
||||
|
const startPos = { x: list.startPos.pos.x * mapCanvas.widthScale, y: list.startPos.pos.y * mapCanvas.heightScale }; |
||||
|
const endPos = { x: list.endPos.pos.x * mapCanvas.widthScale, y: list.endPos.pos.y * mapCanvas.heightScale }; |
||||
|
|
||||
|
switch (list.className) { |
||||
|
case 'BezierPath': |
||||
|
let control1 = { x: list.controlPos1?.x * mapCanvas.widthScale, y: list.controlPos1.y * mapCanvas.heightScale }; |
||||
|
let control2 = { x: list.controlPos2?.x * mapCanvas.widthScale, y: list.controlPos2.y * mapCanvas.heightScale }; |
||||
|
arr[i].control1 = buildAnchor(control1.x, control1.y, 1, i); |
||||
|
arr[i].control2 = buildAnchor(control2.x, control2.y, 2, i); |
||||
|
mapCanvas.anchorPoints.push({ startPos, control1, control2, endPos, instanceName: list.instanceName }); |
||||
|
/* 绘制贝塞尔控制点 */ |
||||
|
const bezierLine = new Konva.Shape({ |
||||
|
stroke: color, |
||||
|
strokeWidth: 2, |
||||
|
perfectDrawEnabled: false, //启用或禁用完美绘制,以提高性能 |
||||
|
name: list.instanceName, |
||||
|
draggable: false, |
||||
|
sceneFunc: (ctx, shape) => { |
||||
|
ctx.globalCompositeOperation = 'destination-over'; //设置或返回如何将一个新的图像绘制到目标或已有的图像上,解决冲突的问题 |
||||
|
ctx.beginPath(); |
||||
|
ctx.moveTo(startPos.x, startPos.y); |
||||
|
ctx.bezierCurveTo(arr[i].control1.x(), arr[i].control1.y(), arr[i].control2.x(), arr[i].control2.y(), endPos.x, endPos.y); |
||||
|
ctx.fillStrokeShape(shape); |
||||
|
}, |
||||
|
}) as any; |
||||
|
bezierLine.startPos = startPos; |
||||
|
bezierLine.endPos = endPos; |
||||
|
/* 绘制贝塞尔辅助线 */ |
||||
|
// const updateDottedLines = new Konva.Shape({ |
||||
|
// perfectDrawEnabled: false, |
||||
|
// visible: false, |
||||
|
// // stroke: 'lightgray', |
||||
|
// listening: false, |
||||
|
// draggable: false, |
||||
|
// // dash: [10, 10], |
||||
|
// sceneFunc: (ctx, shape) => { |
||||
|
// ctx.beginPath(); |
||||
|
// ctx.moveTo(startPos.x, startPos.y); |
||||
|
// ctx.lineTo(arr[i].control1.x(), arr[i].control1.y()); |
||||
|
// ctx.moveTo(endPos.x, endPos.y); |
||||
|
// ctx.lineTo(arr[i].control2.x(), arr[i].control2.y()); |
||||
|
// ctx.moveTo(arr[i].control1.x(), arr[i].control1.y()); |
||||
|
// ctx.lineTo(arr[i].control2.x(), arr[i].control2.y()); |
||||
|
// ctx.restore(); |
||||
|
// ctx.fillStrokeShape(shape); |
||||
|
// }, |
||||
|
// }); |
||||
|
// mapCanvas.dottedLinesArr.push(updateDottedLines); |
||||
|
// mapCanvas.bezier.push(bezierLine); |
||||
|
// bezierLine.on('mouseover', () => { |
||||
|
// document.body.style.cursor = 'pointer'; |
||||
|
// bezierLine.strokeWidth(3); |
||||
|
// }); |
||||
|
// bezierLine.on('mouseout', () => { |
||||
|
// document.body.style.cursor = 'default'; |
||||
|
// bezierLine.strokeWidth(2); |
||||
|
// }); |
||||
|
// 贝塞尔曲线的点击事件 |
||||
|
// bezierLine.on('click', (evt) => { |
||||
|
// setCurrentCurve(i); |
||||
|
// if (!mapCanvas.showInterference) { |
||||
|
// setCurrentTarget(list.instanceName, 'stroke', evt.currentTarget); |
||||
|
// } else { |
||||
|
// setInterference(mapCanvas.advancedCurve); //显示冲突模式下 |
||||
|
// } |
||||
|
// //计算贝塞尔曲线长度 |
||||
|
// // 获取控制点(因为这个控制点可能被拖动过,因此需要这样动态获取当前的控制点) |
||||
|
// control1 = { x: arr[i].control1.x(), y: arr[i].control1.y() }; |
||||
|
// control2 = { x: arr[i].control2.x(), y: arr[i].control2.y() }; |
||||
|
// // 对显示的advancedCurve进行控制点的换算 |
||||
|
// mapCanvas.advancedCurve.controlPos1 = unitFormat(control1); |
||||
|
// mapCanvas.advancedCurve.controlPos2 = unitFormat(control2); |
||||
|
// getBezierLength(startPos, control1, control2, endPos); |
||||
|
// //if (mapCanvas.drawer && mapCanvas.drawerTabsName === 'second') showInterferenceForm(mapCanvas.advancedCurve); |
||||
|
// }); |
||||
|
mapCanvas.layer.add(bezierLine); |
||||
|
//mapCanvas.layer.add(bezierLine, updateDottedLines);//暂时不用绘制贝塞尔曲线可拖动,所以辅助线不需要绘制 |
||||
|
break; |
||||
|
case 'StraightPath': |
||||
|
// 绘制直线 |
||||
|
const line = new Konva.Line({ |
||||
|
points: [startPos.x, startPos.y, endPos.x, endPos.y], |
||||
|
stroke: color, |
||||
|
strokeWidth: 2, |
||||
|
perfectDrawEnabled: false, |
||||
|
name: list.instanceName, |
||||
|
draggable: false, |
||||
|
listening: false, |
||||
|
}); |
||||
|
mapCanvas.layer.add(line); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const drawCircle = (x, y, radius, fill) => { |
||||
|
if (!mapCanvas.circle) { |
||||
|
mapCanvas.circle = new Konva.Circle({ |
||||
|
x, |
||||
|
y, |
||||
|
radius, |
||||
|
// visible: true, |
||||
|
fill, |
||||
|
draggable: false, |
||||
|
listening: false, |
||||
|
}); |
||||
|
mapCanvas.circle.cache(); |
||||
|
} else mapCanvas.circle = mapCanvas.circle.clone({ x, y, fill }); |
||||
|
// 鼠标经过控制点 放大或缩小 |
||||
|
// mapCanvas.circle.on('mouseover', (evt) => { |
||||
|
// evt.currentTarget.destroy(); |
||||
|
// // mapCanvas.circle.radius(4); |
||||
|
// }); |
||||
|
return mapCanvas.circle; |
||||
|
}; |
||||
|
const drawRect = (x, y, rotation, fill) => { |
||||
|
mapCanvas.rect = mapCanvas.rect.clone({ x, y, rotation, fill, opacity: 0.5 }); |
||||
|
return mapCanvas.rect; |
||||
|
}; |
||||
|
const drawArrow = (x, y, width, rotation) => { |
||||
|
if (!mapCanvas.arrow) { |
||||
|
mapCanvas.arrow = new Konva.Arrow({ |
||||
|
x, |
||||
|
y, |
||||
|
points: [0, 0, width, 0], |
||||
|
stroke: '#FFFFF0', |
||||
|
strokeWidth: 2, |
||||
|
perfectDrawEnabled: false, |
||||
|
pointerLength: 10, |
||||
|
pointerWidth: 15, |
||||
|
offset: { |
||||
|
x: width / 2, |
||||
|
y: 0, |
||||
|
}, |
||||
|
rotation, |
||||
|
listening: false, |
||||
|
draggable: false, |
||||
|
}); |
||||
|
mapCanvas.arrow.cache(); |
||||
|
} else mapCanvas.arrow = mapCanvas.arrow.clone({ x, y, rotation }); |
||||
|
return mapCanvas.arrow; |
||||
|
}; |
||||
|
const drawText = (x, y, width, height, text, verticalAlign, rotation) => { |
||||
|
const offset = { x: width / 2, y: verticalAlign === 'bottom' ? height / 2 : height / 2 }; |
||||
|
if (!mapCanvas.text) { |
||||
|
mapCanvas.text = new Konva.Text({ |
||||
|
x, |
||||
|
y, |
||||
|
text, |
||||
|
fontSize: 12, |
||||
|
fill: '#fff', |
||||
|
align: 'center', |
||||
|
verticalAlign, |
||||
|
width, |
||||
|
height, |
||||
|
offset, |
||||
|
scaleY: -1, |
||||
|
rotation, |
||||
|
perfectDrawEnabled: false, |
||||
|
listening: false, |
||||
|
draggable: false, |
||||
|
}); |
||||
|
mapCanvas.text.cache(); |
||||
|
} else mapCanvas.text = mapCanvas.text.clone({ x, y, text, verticalAlign, rotation, offset }); |
||||
|
return mapCanvas.text; |
||||
|
}; |
||||
|
// 绘制可拖动贝塞尔曲线的点 |
||||
|
const buildAnchor = (x, y, type, index) => { |
||||
|
let anchor = null as any; |
||||
|
|
||||
|
anchor = new Konva.Circle({ |
||||
|
x, |
||||
|
y, |
||||
|
radius: 2.5, |
||||
|
fill: 'red', |
||||
|
draggable: true, |
||||
|
visible: false, |
||||
|
perfectDrawEnabled: false, |
||||
|
listening: false, |
||||
|
}); |
||||
|
anchor.transformsEnabled('position'); //仅启用位置转换,以提高绘制性能 |
||||
|
mapCanvas.layer.add(anchor); |
||||
|
// anchor.cache(); |
||||
|
|
||||
|
// anchor = mapCanvas.anchorArr[0].clone({ x, y }); |
||||
|
// mapCanvas.layer.add(anchor); |
||||
|
|
||||
|
// 鼠标经过控制点 放大或缩小 |
||||
|
anchor.on('mouseover', () => { |
||||
|
document.body.style.cursor = 'pointer'; |
||||
|
anchor.radius(4); |
||||
|
}); |
||||
|
// 鼠标离开控制点 放大或缩小 |
||||
|
anchor.on('mouseout', () => { |
||||
|
document.body.style.cursor = 'default'; |
||||
|
anchor.radius(2.5); |
||||
|
}); |
||||
|
// anchor.on('dragmove', (e) => { |
||||
|
// if (!mapCanvas.drawer) return; |
||||
|
// const anchorPoint = mapCanvas.anchorPoints[index / 2]; |
||||
|
// mapCanvas.mapType = 1; |
||||
|
// // 如果当前拖动的不是当前贝塞尔曲线就重新赋值对象 |
||||
|
// if (anchorPoint.instanceName !== mapCanvas.advancedCurve.instanceName) { |
||||
|
// mapCanvas.advancedCurve = advancedCurveList[index]; |
||||
|
// } |
||||
|
// if (mapCanvas.drawerTabsName === 'one') { |
||||
|
// const position = anchor.position(); |
||||
|
// anchorPoint['control' + type] = position; |
||||
|
// getBezierLength(anchorPoint.startPos, anchorPoint.control1, anchorPoint.control2, anchorPoint.endPos); |
||||
|
|
||||
|
// //赋值控制点 |
||||
|
// mapCanvas.advancedCurve['controlPos' + type] = unitFormat(position); |
||||
|
// } |
||||
|
// }); |
||||
|
mapCanvas.anchorArr.push(anchor); //把控制点搜集起来,用作显示隐藏 |
||||
|
return anchor; |
||||
|
}; |
||||
|
|
||||
|
const initMap = () => { |
||||
|
state.loading = true; |
||||
|
// this.mapData = await this.getMapInfos(); |
||||
|
mapCanvas.mapData = mapData; |
||||
|
const { header } = mapCanvas.mapData; |
||||
|
// 获取文档显示区的宽高 |
||||
|
const map = document.querySelector('#container') as any; |
||||
|
|
||||
|
let xMin = parseFloat(Math.abs(header.minPos.x).toFixed(3)); |
||||
|
let yMin = parseFloat(Math.abs(header.minPos.y).toFixed(3)); |
||||
|
let xMax = parseFloat(Math.abs(header.maxPos.x).toFixed(3)); |
||||
|
let yMax = parseFloat(Math.abs(header.maxPos.y).toFixed(3)); |
||||
|
const Xlength = xMin + xMax; |
||||
|
const Ylength = yMin + yMax; |
||||
|
mapCanvas.widthScale = Number(map.offsetWidth / Xlength); |
||||
|
mapCanvas.heightScale = Number(map.offsetHeight / Ylength); |
||||
|
|
||||
|
mapCanvas.stage = new Konva.Stage({ |
||||
|
container: '#container', |
||||
|
width: map.offsetWidth, |
||||
|
height: map.offsetHeight - 70, |
||||
|
x: xMin * mapCanvas.widthScale, |
||||
|
y: yMax * mapCanvas.heightScale, |
||||
|
scaleY: -1, |
||||
|
draggable: true, |
||||
|
}); |
||||
|
mapCanvas.layer = new Konva.Layer(); |
||||
|
mapCanvas.stage.add(mapCanvas.layer); |
||||
|
|
||||
|
const max = 4; // 放大最大的比例 |
||||
|
const min = 0.1; // 缩小最小的比例 |
||||
|
const step = 0.02; // 每次缩放的比例 |
||||
|
// 监听鼠标滚动放大缩小 |
||||
|
mapCanvas.stage.on('wheel', (e) => { |
||||
|
const x = e.evt.offsetX; |
||||
|
const y = e.evt.offsetY; |
||||
|
const offsetX = ((x - mapCanvas.layer.offsetX()) * mapCanvas.layer.scaleX()) / (mapCanvas.layer.scaleX() - step) - (x - mapCanvas.layer.offsetX()); |
||||
|
const offsetY = ((y - mapCanvas.layer.offsetY()) * mapCanvas.layer.scaleY()) / (mapCanvas.layer.scaleY() - step) - (y - mapCanvas.layer.offsetY()); |
||||
|
if (e.evt.wheelDelta > 0) { |
||||
|
// 放大 |
||||
|
if (mapCanvas.layer.scaleX() < max && mapCanvas.layer.scaleY() < max) { |
||||
|
mapCanvas.layer.scaleX(mapCanvas.layer.scaleX() + step); |
||||
|
mapCanvas.layer.scaleY(mapCanvas.layer.scaleY() + step); |
||||
|
mapCanvas.layer.move({ x: -offsetX, y: -offsetY }); // 跟随鼠标偏移位置 |
||||
|
} |
||||
|
} else { |
||||
|
// 缩小 |
||||
|
if (mapCanvas.layer.scaleX() > min && mapCanvas.layer.scaleY() > min) { |
||||
|
mapCanvas.layer.scaleX(mapCanvas.layer.scaleX() - step); |
||||
|
mapCanvas.layer.scaleY(mapCanvas.layer.scaleY() - step); |
||||
|
mapCanvas.layer.move({ x: offsetX, y: offsetY }); // 跟随鼠标偏移位置 |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// mapCanvas.stage.on('click', (evt) => { |
||||
|
// // 点击stage外层 还原 |
||||
|
// if (!evt.target.parent) { |
||||
|
// if (mapCanvas.currentTarget) { |
||||
|
// mapCanvas.currentTarget[mapCanvas.currentTarget.type](mapCanvas.currentTarget.prevfill); |
||||
|
// mapCanvas.currentTarget = null; |
||||
|
// mapCanvas.advancedPoint = { pos: { x: 0, y: 0 } }; |
||||
|
// mapCanvas.advancedCurve = {}; |
||||
|
// } |
||||
|
// } |
||||
|
// }); |
||||
|
|
||||
|
setTimeout(() => { |
||||
|
draw(); |
||||
|
state.loading = false; |
||||
|
}, 1200); |
||||
|
}; |
||||
|
|
||||
|
onMounted(() => initMap()); |
||||
|
|
||||
|
const resetMap = () => { |
||||
|
mapCanvas.advancedCurve = {}; |
||||
|
mapCanvas.advancedPoint = {}; |
||||
|
mapCanvas.bezierLength = 0; |
||||
|
mapCanvas.allGroup = []; |
||||
|
mapCanvas.dottedLinesArr = []; |
||||
|
mapCanvas.anchorPoints = []; |
||||
|
mapCanvas.anchorArr = []; |
||||
|
mapCanvas.bezier = []; |
||||
|
mapCanvas.layer.clear(); |
||||
|
mapCanvas.layer.destroy(); |
||||
|
state.loading = true; |
||||
|
mapCanvas.layer = new Konva.Layer(); |
||||
|
mapCanvas.stage.add(mapCanvas.layer); |
||||
|
|
||||
|
setTimeout(() => { |
||||
|
draw(); |
||||
|
state.loading = false; |
||||
|
}, 1200); |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style scoped lang="scss"> |
||||
|
.full-table { |
||||
|
.container { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
|
||||
|
background-color: #faf9f6; |
||||
|
overflow: hidden; |
||||
|
z-index: 99; |
||||
|
border: 2px solid #ded9e1; |
||||
|
border-radius: 15px; |
||||
|
} |
||||
|
} |
||||
|
</style> |
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Loading…
Reference in new issue