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, '#D5D8DC'); |
|||
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