|
|
@@ -1,6 +1,7 @@
|
|
|
<template>
|
|
|
<div class="mapdata">
|
|
|
<div id="container" ref="container" style="width:300px"></div>
|
|
|
+
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
@@ -88,15 +89,7 @@ export default {
|
|
|
})
|
|
|
},
|
|
|
|
|
|
- // 校验字符串是否为有效的 JSON
|
|
|
- isJson(str) {
|
|
|
- try {
|
|
|
- JSON.parse(str)
|
|
|
- return true
|
|
|
- } catch (e) {
|
|
|
- return false
|
|
|
- }
|
|
|
- },
|
|
|
+
|
|
|
initKonva() {
|
|
|
// 创建舞台
|
|
|
this.stage = new Konva.Stage({
|
|
|
@@ -107,8 +100,7 @@ export default {
|
|
|
|
|
|
// 创建底图图层
|
|
|
this.bgLayer = new Konva.Layer();
|
|
|
-// // 绘制无限网格
|
|
|
-// this.drawGrid(20, 20, '#e0e0e0'); // 每个单元格50x50,浅灰色网格
|
|
|
+
|
|
|
// 创建底图
|
|
|
const bgImage = new Image();
|
|
|
bgImage.src = require('@/assets/images/table.png');
|
|
|
@@ -144,251 +136,142 @@ export default {
|
|
|
this.renderGrid(imageSrc, 6, 3, 450, 100, 120, 100, 50, 50, 60, 25);
|
|
|
},
|
|
|
|
|
|
- // 绘制无限网格
|
|
|
- // drawGrid(cellWidth, cellHeight, gridColor) {
|
|
|
- // const width = 1200
|
|
|
- // const height = 860
|
|
|
- //
|
|
|
- // // 绘制竖线
|
|
|
- // for (let i = 0; i <= width; i += cellWidth) {
|
|
|
- // const verticalLine = new Konva.Line({
|
|
|
- // points: [i, 0, i, height],
|
|
|
- // stroke: gridColor,
|
|
|
- // strokeWidth: 1
|
|
|
- // })
|
|
|
- // this.layer.add(verticalLine)
|
|
|
- // }
|
|
|
- //
|
|
|
- // // 绘制横线
|
|
|
- // for (let j = 0; j <= height; j += cellHeight) {
|
|
|
- // const horizontalLine = new Konva.Line({
|
|
|
- // points: [0, j, width, j],
|
|
|
- // stroke: gridColor,
|
|
|
- // strokeWidth: 1
|
|
|
- // })
|
|
|
- // this.layer.add(horizontalLine)
|
|
|
- // }
|
|
|
- //
|
|
|
- // this.layer.draw()
|
|
|
- // },
|
|
|
-
|
|
|
renderGrid(imageSrc) {
|
|
|
- this.selectedStates = [] // 用数组来存储选中状态
|
|
|
- this.rects = {}
|
|
|
- this.texts = {}
|
|
|
- this.bgrects = {}
|
|
|
- this.redrects = {}
|
|
|
- this.redtexts = {}
|
|
|
- this.selectedText = []
|
|
|
- this.pointIdList = [] // 初始化选中的点ID列表
|
|
|
-
|
|
|
- const positions = JSON.parse(this.value)
|
|
|
-
|
|
|
- positions.forEach((pos, index) => {
|
|
|
-
|
|
|
- let colOffset = 0;
|
|
|
-
|
|
|
- // 计算当前元素在组中的位置
|
|
|
- const groupSize = 3;
|
|
|
- const groupIndex = Math.floor(index / groupSize);
|
|
|
- const positionInGroup = index % groupSize;
|
|
|
- // 每一行第二个和第三个增加col
|
|
|
- if (positionInGroup === 1) {
|
|
|
- colOffset = 2;
|
|
|
- } else if (positionInGroup === 2) {
|
|
|
- colOffset = 4;
|
|
|
- }
|
|
|
- // 根据索引调整行偏移量
|
|
|
- if (groupIndex == 0) {
|
|
|
- // 第一组(索引0,1,2)
|
|
|
- pos.row -= 1;
|
|
|
- } else if (groupIndex == 1) {
|
|
|
- // 第二组(索引3,4,5)
|
|
|
- pos.row -= 0.5;
|
|
|
- } else if (groupIndex == 2) {
|
|
|
- // 第三组及之后(索引6,7,8...)
|
|
|
- pos.row += 0.1;
|
|
|
- }else if (groupIndex == 3) {
|
|
|
- // 第四组及之后(索引9,10,11...)
|
|
|
- pos.row += 0.6;
|
|
|
- }else{
|
|
|
- pos.row += 0.5;
|
|
|
- }
|
|
|
-
|
|
|
- // console.log(pos.col,pos.row,'横纵坐标')
|
|
|
- const x = (pos.col + colOffset) * 11; // 每个单元格宽度为50
|
|
|
- const y = pos.row * 25; // 每个单元格高度为50 // 每个单元格高度为50
|
|
|
- const labelText = pos.pointName // 对应的文字
|
|
|
-
|
|
|
- const point = new Image()
|
|
|
- point.src = pos.pointIcon
|
|
|
+ this.selectedStates = []; // 用数组存储选中状态
|
|
|
+ this.rects = {};
|
|
|
+ this.texts = {};
|
|
|
+ this.bgrects = {};
|
|
|
+ this.redrects = {};
|
|
|
+ this.redtexts = {};
|
|
|
+ this.selectedText = [];
|
|
|
+ this.pointIdList = []; // 初始化选中的点ID列表
|
|
|
+
|
|
|
+ const positions = JSON.parse(this.value); // 获取点位数据
|
|
|
+
|
|
|
+ // **计算柜子的布局范围**
|
|
|
+ const cabinetWidth = this.stage.width(); // 柜子的宽度
|
|
|
+ const cabinetHeight = this.stage.height(); // 柜子的高度
|
|
|
+ const cols = Math.max(...positions.map(p => p.col)) + 1; // 根据数据动态计算总列数
|
|
|
+ const rows = Math.max(...positions.map(p => p.row)) + 1; // 根据数据动态计算总行数
|
|
|
+ // 调整横向和纵向间距
|
|
|
+ const horizontalSpacingFactor = 1; // 横向间距放大倍数
|
|
|
+ const verticalSpacingFactor = 0.7; // 纵向间距缩小倍数
|
|
|
+
|
|
|
+ const cellWidth = (cabinetWidth / cols) * horizontalSpacingFactor; // 调整横向间距
|
|
|
+ const cellHeight = (cabinetHeight / rows) * verticalSpacingFactor; // 调整纵向间距
|
|
|
+
|
|
|
+ // 遍历点位,按行列布局计算位置
|
|
|
+ positions.forEach(pos => {
|
|
|
+ const col = pos.col; // 当前点位的列
|
|
|
+ const row = pos.row; // 当前点位的行
|
|
|
+
|
|
|
+ // 计算点位的实际位置,确保它们位于柜子显示区域内
|
|
|
+ const x = col * cellWidth-100;
|
|
|
+ const y = row * cellHeight-30;
|
|
|
+
|
|
|
+ const labelText = pos.pointName; // 对应的文字
|
|
|
+
|
|
|
+ const point = new Image();
|
|
|
+ point.src = pos.pointIcon;
|
|
|
point.onload = () => {
|
|
|
const knovaImage = new Konva.Image({
|
|
|
- x: x-2,
|
|
|
- y: y+3,
|
|
|
+ x: x +4, // 适当的偏移确保点位不重叠
|
|
|
+ y: y +4, // 适当的偏移确保点位不重叠
|
|
|
image: point,
|
|
|
- width: 30,
|
|
|
- height: 30,
|
|
|
- draggable: false
|
|
|
- })
|
|
|
-
|
|
|
- // 添加点击事件监听器
|
|
|
- // knovaImage.on('click', () => {
|
|
|
- // const isCurrentlySelected = this.redrects[labelText].visible()
|
|
|
- //
|
|
|
- // if (isCurrentlySelected) {
|
|
|
- // // 如果当前已选中,则取消选中
|
|
|
- // this.redrects[labelText].visible(false)
|
|
|
- // this.redtexts[labelText].visible(false)
|
|
|
- // this.pointIdList = this.pointIdList.filter(id => id !== pos.pointId) // 移除ID
|
|
|
- // } else {
|
|
|
- // // 如果当前未选中,则选中
|
|
|
- // this.redrects[labelText].visible(true)
|
|
|
- // this.redtexts[labelText].visible(true)
|
|
|
- // this.pointIdList.push(pos.pointId) // 添加ID
|
|
|
- // }
|
|
|
- //
|
|
|
- // this.layer.batchDraw() // 更新图层显示
|
|
|
- // })
|
|
|
-
|
|
|
- // 底部白色背景
|
|
|
+ width: 34,
|
|
|
+ height: 34,
|
|
|
+ draggable: false,
|
|
|
+ });
|
|
|
+
|
|
|
+ // **绘制背景矩形**(以单元格大小为基础)
|
|
|
const bgrect = new Konva.Rect({
|
|
|
- x: x - 6,
|
|
|
- y: y - 5,
|
|
|
- width: 36,
|
|
|
- height: 58,
|
|
|
+ x: x,
|
|
|
+ y: y,
|
|
|
+ width: 40,
|
|
|
+ height: 55,
|
|
|
cornerRadius: 5,
|
|
|
stroke: 'white',
|
|
|
strokeWidth: 2,
|
|
|
- fill: 'white'
|
|
|
- })
|
|
|
- this.layer.add(bgrect)
|
|
|
- this.bgrects[labelText] = bgrect // 用文字作为键存储
|
|
|
+ fill: 'white',
|
|
|
+ });
|
|
|
+ this.layer.add(bgrect);
|
|
|
+ this.bgrects[labelText] = bgrect;
|
|
|
|
|
|
- // 普通矩形
|
|
|
+ // **普通矩形边框**(同样按照单元格尺寸)
|
|
|
const rect = new Konva.Rect({
|
|
|
- x: x-4,
|
|
|
- y: y-2,
|
|
|
- width: 32,
|
|
|
- height: 54,
|
|
|
+ x: x + 4,
|
|
|
+ y: y + 4,
|
|
|
+ width: 40 - 8,
|
|
|
+ height: 55 - 8,
|
|
|
cornerRadius: 5,
|
|
|
stroke: 'red',
|
|
|
strokeWidth: 2,
|
|
|
- fill: 'white'
|
|
|
- })
|
|
|
- this.layer.add(rect)
|
|
|
- this.rects[labelText] = rect // 用文字作为键存储
|
|
|
- // 先加底部白色 再添加图片
|
|
|
- this.layer.add(knovaImage)
|
|
|
+ fill: 'white',
|
|
|
+ });
|
|
|
+ this.layer.add(rect);
|
|
|
+ this.rects[labelText] = rect;
|
|
|
|
|
|
+ // 添加图片
|
|
|
+ this.layer.add(knovaImage);
|
|
|
|
|
|
- // 普通文字
|
|
|
+ // **绘制点位的文字**(确保与背景矩形不重叠)
|
|
|
const text = new Konva.Text({
|
|
|
- x: x + 2,
|
|
|
- y: y + 34,
|
|
|
+ x: x + 7, // 适当的偏移,确保文字不与背景矩形重叠
|
|
|
+ y: y + 55 - 20, // 文字放在底部
|
|
|
fontSize: 13,
|
|
|
text: labelText,
|
|
|
fontFamily: 'Calibri',
|
|
|
- fill: 'red'
|
|
|
- })
|
|
|
- this.layer.add(text)
|
|
|
- this.texts[labelText] = text // 用文字作为键存储
|
|
|
+ fill: 'red',
|
|
|
+ });
|
|
|
+ this.layer.add(text);
|
|
|
+ this.texts[labelText] = text;
|
|
|
|
|
|
- // 覆盖层(表示选中状态)
|
|
|
+ // **选中覆盖矩形**(保持与背景矩形相同的尺寸)
|
|
|
const redrect = new Konva.Rect({
|
|
|
- x: x - 6,
|
|
|
- y: y - 4,
|
|
|
- width: 36,
|
|
|
- height: 58,
|
|
|
+ x: x,
|
|
|
+ y: y,
|
|
|
+ width: 50,
|
|
|
+ height: 55,
|
|
|
cornerRadius: 5,
|
|
|
fill: 'rgba(97, 97, 97, 0.5)', // 半透明灰色
|
|
|
visible: false, // 初始状态隐藏
|
|
|
- listening: false
|
|
|
- })
|
|
|
- this.layer.add(redrect)
|
|
|
- this.redrects[labelText] = redrect // 用文字作为键存储
|
|
|
+ listening: false,
|
|
|
+ });
|
|
|
+ this.layer.add(redrect);
|
|
|
+ this.redrects[labelText] = redrect;
|
|
|
|
|
|
- // 创建对号文本
|
|
|
+ // **对号文字(表示选中)**
|
|
|
const redtext = new Konva.Text({
|
|
|
- x: x - 5 + 26 / 2, // 水平居中
|
|
|
- y: y + 38 / 2, // 垂直居中
|
|
|
- fontSize: 13, // 根据需要调整字体大小
|
|
|
+ x: x + 40 / 2 - 5, // 水平居中
|
|
|
+ y: y + 55 / 2 - 5, // 垂直居中
|
|
|
+ fontSize: 13,
|
|
|
text: '✔',
|
|
|
fontFamily: 'Arial',
|
|
|
fill: 'white',
|
|
|
align: 'center',
|
|
|
- verticalAlign: 'middle',
|
|
|
visible: false, // 初始隐藏状态
|
|
|
- listening: false
|
|
|
- })
|
|
|
- this.layer.add(redtext)
|
|
|
- this.redtexts[labelText] = redtext // 用文字作为键存储
|
|
|
+ listening: false,
|
|
|
+ });
|
|
|
+ this.layer.add(redtext);
|
|
|
+ this.redtexts[labelText] = redtext;
|
|
|
|
|
|
- // 检查 selectPoints 是否存在并且不为空
|
|
|
+ // 如果初始化时有选中的点位
|
|
|
if (Array.isArray(this.selectPoints) && this.selectPoints.length > 0) {
|
|
|
if (this.selectPoints.includes(pos.pointId)) {
|
|
|
// 设置选中状态
|
|
|
- this.redrects[labelText].visible(true)
|
|
|
- this.redtexts[labelText].visible(true)
|
|
|
- this.pointIdList.push(pos.pointId) // 添加ID
|
|
|
+ this.redrects[labelText].visible(true);
|
|
|
+ this.redtexts[labelText].visible(true);
|
|
|
+ this.pointIdList.push(pos.pointId); // 添加ID
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- this.layer.draw()
|
|
|
- // const positions = [{
|
|
|
- // row: 2,
|
|
|
- // col: 9,
|
|
|
- // pointId: '11',
|
|
|
- // pointName: 'E-11',
|
|
|
- // state: false,
|
|
|
- // pointIcon: 'http://36.133.174.236:9091/prod-api/profile/upload/2024/11/27/point_20241127152959A071.png',
|
|
|
- // pointPicture: 'http://36.133.174.236:9091/prod-api/profile/upload/2024/10/31/AB54CB1D-40BC-409d-BFFD-F55664CF3F01_20241031171608A034.png',
|
|
|
- //
|
|
|
- // }, {
|
|
|
- // row: 2,
|
|
|
- // col: 11,
|
|
|
- // pointId: '12',
|
|
|
- // pointName: 'E-12',
|
|
|
- // state: false,
|
|
|
- // pointIcon: 'http://36.133.174.236:9091/prod-api/profile/upload/2024/11/27/point_20241127152952A070.png',
|
|
|
- // pointPicture: 'http://36.133.174.236:9091/prod-api/profile/upload/2024/10/31/AB54CB1D-40BC-409d-BFFD-F55664CF3F01_20241031171627A036.png',
|
|
|
- // }, {
|
|
|
- // row: 2,
|
|
|
- // col: 13,
|
|
|
- // pointId: '13',
|
|
|
- // pointName: 'E-13',
|
|
|
- // state: false,
|
|
|
- // pointIcon: 'http://36.133.174.236:9091/prod-api/profile/upload/2024/11/27/point_20241127152946A069.png',
|
|
|
- // pointPicture: 'http://36.133.174.236:9091/prod-api/profile/upload/2024/10/31/AB54CB1D-40BC-409d-BFFD-F55664CF3F01_20241031171651A038.png',
|
|
|
- // },{
|
|
|
- // row: 4,
|
|
|
- // col: 9,
|
|
|
- // pointId: '7',
|
|
|
- // pointName: 'E-7',
|
|
|
- // state: true,
|
|
|
- // pointIcon: 'http://36.133.174.236:9091/prod-api/profile/upload/2024/11/27/point_20241127152959A071.png',
|
|
|
- // pointPicture: 'http://36.133.174.236:9091/prod-api/profile/upload/2024/10/31/AB54CB1D-40BC-409d-BFFD-F55664CF3F01_20241031171608A034.png',
|
|
|
- //
|
|
|
- // }, {
|
|
|
- // row: 4,
|
|
|
- // col: 11,
|
|
|
- // pointId: '8',
|
|
|
- // pointName: 'E-8',
|
|
|
- // state: true,
|
|
|
- // pointIcon: 'http://36.133.174.236:9091/prod-api/profile/upload/2024/11/27/point_20241127152952A070.png',
|
|
|
- // pointPicture: 'http://36.133.174.236:9091/prod-api/profile/upload/2024/10/31/AB54CB1D-40BC-409d-BFFD-F55664CF3F01_20241031171627A036.png',
|
|
|
- // }, {
|
|
|
- // row: 4,
|
|
|
- // col: 13,
|
|
|
- // pointId: '9',
|
|
|
- // pointName: 'E-9',
|
|
|
- // state: true,
|
|
|
- // pointIcon: 'http://36.133.174.236:9091/prod-api/profile/upload/2024/11/27/point_20241127152946A069.png',
|
|
|
- // pointPicture: 'http://36.133.174.236:9091/prod-api/profile/upload/2024/10/31/AB54CB1D-40BC-409d-BFFD-F55664CF3F01_20241031171651A038.png',
|
|
|
- // }]
|
|
|
- }
|
|
|
- })
|
|
|
+ this.layer.draw(); // 刷新画布
|
|
|
+ };
|
|
|
+ });
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
}
|
|
|
}
|
|
|
</script>
|