@fleet-frontend/mower-maps 0.0.9-beta.10 → 0.0.9-beta.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/constants.d.ts +1 -0
- package/dist/config/constants.d.ts.map +1 -1
- package/dist/config/styles.d.ts.map +1 -1
- package/dist/index.esm.js +175 -60
- package/dist/index.js +175 -60
- package/dist/render/BoundaryLabelsManager.d.ts.map +1 -1
- package/dist/render/MowerMapRenderer.d.ts.map +1 -1
- package/dist/render/layers/BoundaryBorderLayer.d.ts +4 -0
- package/dist/render/layers/BoundaryBorderLayer.d.ts.map +1 -1
- package/dist/render/layers/PathLayer.d.ts +10 -2
- package/dist/render/layers/PathLayer.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/config/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,eAAO,MAAM,YAAY,KAAK,CAAC;AAE/B;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;CAUtB,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,iBAAiB
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/config/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,eAAO,MAAM,YAAY,KAAK,CAAC;AAE/B;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;CAUtB,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;;CAOpB,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,aAAa;;;;;;CAMhB,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;CAUf,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;CAWrB,CAAC;AAGX,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEjF,eAAO,MAAM,qBAAqB,sqUAqB3B,CAAC;AAER;;GAEG;AACH,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B;;GAEG;AACH,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC;;GAEG;AACH,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC;;GAEG;AACH,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAC9C;;GAEG;AACH,eAAO,MAAM,uBAAuB,IAAI,CAAC;AACzC;;GAEG;AACH,eAAO,MAAM,kBAAkB,IAAI,CAAC;AACpC;;GAEG;AACH,eAAO,MAAM,qBAAqB,IAAI,CAAC;AACvC;;GAEG;AACH,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAChD;;GAEG;AACH,eAAO,MAAM,oBAAoB,IAAI,CAAC;AACtC;;GAEG;AACH,eAAO,MAAM,UAAU,KAAK,CAAC;AAE7B,eAAO,MAAM,eAAe,4BAA4B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../src/config/styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,
|
|
1
|
+
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../src/config/styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAEpG;;GAEG;AACH,eAAO,MAAM,eAAe,EAMvB,aAAa,CAAC;AAEnB,eAAO,MAAM,sBAAsB,EAK9B,aAAa,CAAC;AAEnB,eAAO,MAAM,eAAe,EAKvB,aAAa,CAAC;AAEnB,eAAO,MAAM,oBAAoB,EAM5B,iBAAiB,CAAC;AAEvB,eAAO,MAAM,aAAa,EAKrB,eAAe,CAAC;AAErB,eAAO,MAAM,gBAAgB;;;;;;;CAO5B,CAAC;AAEF,eAAO,MAAM,cAAc;;;;;;CAM1B,CAAC;AAEF,eAAO,MAAM,cAAc,EAKtB,YAAY,CAAC;AAElB,eAAO,MAAM,cAAc,EAAE,SAS5B,CAAC"}
|
package/dist/index.esm.js
CHANGED
|
@@ -67,7 +67,8 @@ const DEFAULT_LINE_WIDTHS = {
|
|
|
67
67
|
const DEFAULT_OPACITIES = {
|
|
68
68
|
FULL: 1.0,
|
|
69
69
|
HIGH: 0.7,
|
|
70
|
-
MEDIUM: 0.6
|
|
70
|
+
MEDIUM: 0.6,
|
|
71
|
+
DOODLE: 0.8};
|
|
71
72
|
/**
|
|
72
73
|
* 默认半径设置
|
|
73
74
|
*/
|
|
@@ -1044,15 +1045,37 @@ class PathLayer extends BaseLayer {
|
|
|
1044
1045
|
d += ' Z ';
|
|
1045
1046
|
}
|
|
1046
1047
|
});
|
|
1047
|
-
// 3. svgElements
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1048
|
+
// 3. svgElements(解析 SVG 字符串并提取 path 数据)
|
|
1049
|
+
Object.values(svgElements).forEach((svgPath) => {
|
|
1050
|
+
const svgPathString = svgPath?.metadata?.svg;
|
|
1051
|
+
if (svgPathString && typeof svgPathString === 'string' && svgPathString.trim()) {
|
|
1052
|
+
// 处理转义字符
|
|
1053
|
+
const processedSvgString = svgPathString.replace(/\\n/g, '\n').replace(/\\"/g, '"');
|
|
1054
|
+
// 解析 SVG 字符串
|
|
1055
|
+
const parser = new DOMParser();
|
|
1056
|
+
const svgDoc = parser.parseFromString(processedSvgString, 'image/svg+xml');
|
|
1057
|
+
const svgElement = svgDoc.documentElement;
|
|
1058
|
+
if (svgElement.tagName === 'svg') {
|
|
1059
|
+
// 查找 path 元素
|
|
1060
|
+
const pathElement = svgElement.querySelector('path');
|
|
1061
|
+
if (pathElement) {
|
|
1062
|
+
const pathData = pathElement.getAttribute('d');
|
|
1063
|
+
if (pathData) {
|
|
1064
|
+
// 获取 SVG 元素的变换参数
|
|
1065
|
+
const centerCoords = svgPath.coordinates?.[0] || [0, 0];
|
|
1066
|
+
const center = [centerCoords[0], centerCoords[1]];
|
|
1067
|
+
const userScale = svgPath.metadata.scale || 1;
|
|
1068
|
+
const direction = svgPath.metadata?.direction || 0;
|
|
1069
|
+
const originalWidth = parseFloat(svgElement.getAttribute('width') || '76');
|
|
1070
|
+
const originalHeight = parseFloat(svgElement.getAttribute('height') || '68');
|
|
1071
|
+
// 应用变换到路径数据
|
|
1072
|
+
const transformedPathData = this.transformSvgPath(pathData, center, userScale, direction, originalWidth, originalHeight);
|
|
1073
|
+
d += transformedPathData + ' ';
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1053
1076
|
}
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1077
|
+
}
|
|
1078
|
+
});
|
|
1056
1079
|
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
1057
1080
|
path.setAttribute('d', d);
|
|
1058
1081
|
const clipPath = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath');
|
|
@@ -1077,47 +1100,132 @@ class PathLayer extends BaseLayer {
|
|
|
1077
1100
|
// 2. 创建一个组,应用 clipPath
|
|
1078
1101
|
const group = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
1079
1102
|
group.setAttribute('clip-path', `url(#${clipPathId})`);
|
|
1080
|
-
group.setAttribute('opacity', '0.
|
|
1081
|
-
// 3.
|
|
1103
|
+
group.setAttribute('opacity', '0.5'); // 统一透明度,防止叠加脏乱
|
|
1104
|
+
// 3. 优化渲染:按样式分组并合并路径
|
|
1105
|
+
this.renderOptimizedPaths(group);
|
|
1106
|
+
svgGroup.appendChild(group);
|
|
1107
|
+
}
|
|
1108
|
+
/**
|
|
1109
|
+
* 优化渲染:按样式分组并合并路径,减少 DOM 节点数量
|
|
1110
|
+
*/
|
|
1111
|
+
renderOptimizedPaths(group) {
|
|
1112
|
+
// 按样式分组存储路径数据
|
|
1113
|
+
const styleGroups = new Map();
|
|
1114
|
+
// 收集所有路径数据并按样式分组
|
|
1082
1115
|
for (const element of this.elements) {
|
|
1083
|
-
|
|
1116
|
+
// 类型断言:PathLayer 中的 elements 实际上是 PathElements 结构
|
|
1117
|
+
const pathElement = element;
|
|
1118
|
+
const { id, elements } = pathElement;
|
|
1084
1119
|
this.boundaryPaths[id] = [];
|
|
1120
|
+
elements.forEach((pathElement) => {
|
|
1121
|
+
const { coordinates, style } = pathElement;
|
|
1122
|
+
if (coordinates.length < 2)
|
|
1123
|
+
return;
|
|
1124
|
+
// 生成样式键(用于分组)
|
|
1125
|
+
const styleKey = this.generateStyleKey(style);
|
|
1126
|
+
// 构建路径数据
|
|
1127
|
+
let pathData = `M ${coordinates[0][0]} ${coordinates[0][1]}`;
|
|
1128
|
+
for (let i = 1; i < coordinates.length; i++) {
|
|
1129
|
+
pathData += ` L ${coordinates[i][0]} ${coordinates[i][1]}`;
|
|
1130
|
+
}
|
|
1131
|
+
// 按样式分组存储
|
|
1132
|
+
if (!styleGroups.has(styleKey)) {
|
|
1133
|
+
styleGroups.set(styleKey, { pathData: [], elements: [] });
|
|
1134
|
+
}
|
|
1135
|
+
styleGroups.get(styleKey).pathData.push(pathData);
|
|
1136
|
+
styleGroups.get(styleKey).elements.push(pathElement);
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1139
|
+
// 为每种样式创建一个合并的 path 元素
|
|
1140
|
+
styleGroups.forEach((groupData) => {
|
|
1141
|
+
const { pathData, elements } = groupData;
|
|
1142
|
+
if (pathData.length === 0)
|
|
1143
|
+
return;
|
|
1144
|
+
// 使用第一个元素的样式作为该组的样式
|
|
1145
|
+
const firstElement = elements[0];
|
|
1146
|
+
const style = firstElement.style;
|
|
1147
|
+
// 创建合并的 path 元素
|
|
1148
|
+
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
1149
|
+
// 合并所有路径数据
|
|
1150
|
+
const mergedPathData = pathData.join(' ');
|
|
1151
|
+
path.setAttribute('d', mergedPathData);
|
|
1152
|
+
// 设置样式属性
|
|
1153
|
+
path.setAttribute('fill', 'none');
|
|
1154
|
+
path.setAttribute('stroke', style.lineColor || '#000000');
|
|
1155
|
+
path.setAttribute('mix-blend-mode', 'normal');
|
|
1156
|
+
const lineWidth = Math.max(style.lineWidth || 1, 0.5);
|
|
1157
|
+
path.setAttribute('stroke-width', lineWidth.toString());
|
|
1158
|
+
path.setAttribute('stroke-linecap', 'round');
|
|
1159
|
+
path.setAttribute('stroke-linejoin', 'round');
|
|
1160
|
+
path.classList.add('vector-path');
|
|
1161
|
+
// 将合并的 path 添加到组中
|
|
1162
|
+
group.appendChild(path);
|
|
1163
|
+
// 保存引用到 boundaryPaths 中(保持兼容性)
|
|
1085
1164
|
elements.forEach((element) => {
|
|
1086
|
-
|
|
1165
|
+
const { id } = element;
|
|
1166
|
+
if (!this.boundaryPaths[id]) {
|
|
1167
|
+
this.boundaryPaths[id] = [];
|
|
1168
|
+
}
|
|
1169
|
+
this.boundaryPaths[id].push(path);
|
|
1087
1170
|
});
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* 变换 SVG 路径数据
|
|
1175
|
+
*/
|
|
1176
|
+
transformSvgPath(pathData, center, scale, direction, originalWidth, originalHeight) {
|
|
1177
|
+
// 解析路径数据并应用变换
|
|
1178
|
+
const commands = pathData.match(/[MmLlHhVvCcSsQqTtAaZz][^MmLlHhVvCcSsQqTtAaZz]*/g) || [];
|
|
1179
|
+
let transformedCommands = [];
|
|
1180
|
+
for (const command of commands) {
|
|
1181
|
+
const type = command[0];
|
|
1182
|
+
const params = command
|
|
1183
|
+
.slice(1)
|
|
1184
|
+
.trim()
|
|
1185
|
+
.split(/[\s,]+/)
|
|
1186
|
+
.filter(Boolean)
|
|
1187
|
+
.map(Number);
|
|
1188
|
+
if (type === 'Z' || type === 'z') {
|
|
1189
|
+
// 闭合路径,不需要变换
|
|
1190
|
+
transformedCommands.push(command);
|
|
1191
|
+
continue;
|
|
1192
|
+
}
|
|
1193
|
+
// 处理坐标参数
|
|
1194
|
+
let transformedParams = [];
|
|
1195
|
+
for (let i = 0; i < params.length; i += 2) {
|
|
1196
|
+
if (i + 1 < params.length) {
|
|
1197
|
+
let x = params[i];
|
|
1198
|
+
let y = params[i + 1];
|
|
1199
|
+
// 应用变换:先平移到中心,然后缩放、旋转,最后平移到目标位置
|
|
1200
|
+
// 1. 平移到原点(相对于原始尺寸的中心)
|
|
1201
|
+
x -= originalWidth / 2;
|
|
1202
|
+
y -= originalHeight / 2;
|
|
1203
|
+
// 2. 应用缩放
|
|
1204
|
+
x *= scale;
|
|
1205
|
+
y *= scale;
|
|
1206
|
+
// 3. 应用旋转
|
|
1207
|
+
const cos = Math.cos(-direction);
|
|
1208
|
+
const sin = Math.sin(-direction);
|
|
1209
|
+
const newX = x * cos - y * sin;
|
|
1210
|
+
const newY = x * sin + y * cos;
|
|
1211
|
+
// 4. 平移到目标位置
|
|
1212
|
+
x = newX + center[0];
|
|
1213
|
+
y = newY + center[1];
|
|
1214
|
+
transformedParams.push(x, y);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
// 重建命令
|
|
1218
|
+
if (transformedParams.length > 0) {
|
|
1219
|
+
transformedCommands.push(type + transformedParams.join(' '));
|
|
1220
|
+
}
|
|
1088
1221
|
}
|
|
1089
|
-
|
|
1222
|
+
return transformedCommands.join(' ');
|
|
1090
1223
|
}
|
|
1091
1224
|
/**
|
|
1092
|
-
*
|
|
1225
|
+
* 生成样式键,用于路径分组
|
|
1093
1226
|
*/
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
if (coordinates.length < 2)
|
|
1097
|
-
return;
|
|
1098
|
-
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
1099
|
-
// 构建路径数据
|
|
1100
|
-
let pathData = `M ${coordinates[0][0]} ${coordinates[0][1]}`;
|
|
1101
|
-
for (let i = 1; i < coordinates.length; i++) {
|
|
1102
|
-
pathData += ` L ${coordinates[i][0]} ${coordinates[i][1]}`;
|
|
1103
|
-
}
|
|
1104
|
-
path.style.mixBlendMode = 'normal';
|
|
1105
|
-
// 设置路径属性
|
|
1106
|
-
path.setAttribute('d', pathData);
|
|
1107
|
-
// 直接给fill的颜色设置透明度会导致path重叠的部分颜色叠加,所以使用fill填充实色,通过fill-opacity设置透明度
|
|
1108
|
-
path.setAttribute('fill', 'none');
|
|
1109
|
-
// path.setAttribute('fill-opacity', '0.4');
|
|
1110
|
-
path.setAttribute('stroke', style.lineColor || '#000000');
|
|
1111
|
-
path.setAttribute('mix-blend-mode', 'normal');
|
|
1112
|
-
const lineWidth = Math.max(style.lineWidth || 1, 0.5);
|
|
1113
|
-
path.setAttribute('stroke-width', lineWidth.toString());
|
|
1114
|
-
path.setAttribute('stroke-linecap', 'round');
|
|
1115
|
-
path.setAttribute('stroke-linejoin', 'round');
|
|
1116
|
-
// 注意:这里不设置 opacity,因为透明度由父组控制
|
|
1117
|
-
// path.setAttribute('vector-effect', 'non-scaling-stroke');
|
|
1118
|
-
path.classList.add('vector-path');
|
|
1119
|
-
this.boundaryPaths[id].push(path);
|
|
1120
|
-
group.appendChild(path);
|
|
1227
|
+
generateStyleKey(style) {
|
|
1228
|
+
return `${style.lineColor || '#000000'}-${style.lineWidth || 1}-${style.opacity || 1}`;
|
|
1121
1229
|
}
|
|
1122
1230
|
}
|
|
1123
1231
|
|
|
@@ -1541,7 +1649,7 @@ const DOODLE_STYLES = {
|
|
|
1541
1649
|
lineColor: '#ff5722',
|
|
1542
1650
|
fillColor: '#ff9800', // 粉色半透明填充
|
|
1543
1651
|
lineWidth: DEFAULT_LINE_WIDTHS.TIME_LIMIT_OBSTACLE,
|
|
1544
|
-
opacity: DEFAULT_OPACITIES.
|
|
1652
|
+
opacity: DEFAULT_OPACITIES.DOODLE,
|
|
1545
1653
|
};
|
|
1546
1654
|
const PATH_EDGE_STYLES = {
|
|
1547
1655
|
lineWidth: DEFAULT_LINE_WIDTHS.PATH,
|
|
@@ -4975,6 +5083,12 @@ class BoundaryBorderLayer extends BaseLayer {
|
|
|
4975
5083
|
this.mowingBoundarys = mowingBoundarys;
|
|
4976
5084
|
}
|
|
4977
5085
|
}
|
|
5086
|
+
/**
|
|
5087
|
+
* 获取当前割草任务的边界
|
|
5088
|
+
*/
|
|
5089
|
+
getMowingBoundarys() {
|
|
5090
|
+
return this.mowingBoundarys;
|
|
5091
|
+
}
|
|
4978
5092
|
/**
|
|
4979
5093
|
* SVG渲染方法
|
|
4980
5094
|
*/
|
|
@@ -6152,7 +6266,7 @@ class BoundaryLabelsManager {
|
|
|
6152
6266
|
labelDiv.style.zIndex = BoundaryLabelsManager.Z_INDEX.DEFAULT.toString();
|
|
6153
6267
|
// 计算进度
|
|
6154
6268
|
const progress = boundary.finishedArea && boundary.area
|
|
6155
|
-
? `${Math.
|
|
6269
|
+
? `${Math.floor((boundary.finishedArea / boundary.area) * 100)}%`
|
|
6156
6270
|
: '0%';
|
|
6157
6271
|
// 基础内容(始终显示)
|
|
6158
6272
|
const baseContent = document.createElement('div');
|
|
@@ -6169,13 +6283,15 @@ class BoundaryLabelsManager {
|
|
|
6169
6283
|
this.currentExpandedBoundaryId === boundary.id ? 'block' : 'none';
|
|
6170
6284
|
extendedContent.style.borderTop = '1px solid rgba(255,255,255,0.2)';
|
|
6171
6285
|
extendedContent.style.paddingTop = '6px';
|
|
6172
|
-
|
|
6286
|
+
const boundaryLayer = this.svgView.getLayer(LAYER_DEFAULT_TYPE.BOUNDARY_BORDER);
|
|
6287
|
+
const mowingBoundarys = boundaryLayer.getMowingBoundarys();
|
|
6173
6288
|
// 面积信息
|
|
6174
6289
|
const totalArea = convertAreaByUnits(boundary.area || 0, this.unitType);
|
|
6175
6290
|
const finishedArea = convertAreaByUnits(boundary.finishedArea || 0, this.unitType);
|
|
6176
6291
|
const coverageText = `Coverage: ${finishedArea.value}/${totalArea.value}`;
|
|
6292
|
+
const isMowing = mowingBoundarys.includes(boundary.id);
|
|
6177
6293
|
// 日期信息
|
|
6178
|
-
const dateText = formatBoundaryDateText(boundary.endTime || 0);
|
|
6294
|
+
const dateText = formatBoundaryDateText(isMowing ? Date.now() / 1000 : boundary.endTime || 0);
|
|
6179
6295
|
const covertHtml = `<div style="margin-bottom: 3px; font-weight: bold;">${coverageText}</div>`;
|
|
6180
6296
|
const dateHtml = `<div>${dateText}</div>`;
|
|
6181
6297
|
extendedContent.innerHTML = boundary.finishedArea > 0 ? `${covertHtml}${dateHtml}` : covertHtml;
|
|
@@ -6293,7 +6409,6 @@ class BoundaryLabelsManager {
|
|
|
6293
6409
|
// 计算边界中心点的地图坐标
|
|
6294
6410
|
const mapCenter = this.calculatePolygonCentroid(boundary.points);
|
|
6295
6411
|
if (!mapCenter) {
|
|
6296
|
-
console.warn(`BoundaryLabelsManager: 无法计算边界 ${boundary.name} (ID: ${boundary.id}) 的中心点`);
|
|
6297
6412
|
return;
|
|
6298
6413
|
}
|
|
6299
6414
|
// 直接使用预计算的数据进行坐标转换
|
|
@@ -6378,7 +6493,6 @@ class BoundaryLabelsManager {
|
|
|
6378
6493
|
area = area / 2;
|
|
6379
6494
|
// 如果面积为0,回退到简单的平均值计算
|
|
6380
6495
|
if (Math.abs(area) < 1e-10) {
|
|
6381
|
-
console.warn('BoundaryLabelsManager: 多边形面积为0,使用平均值计算重心');
|
|
6382
6496
|
return this.calculateAverageCenter(validPoints);
|
|
6383
6497
|
}
|
|
6384
6498
|
centroidX = centroidX / (6 * area);
|
|
@@ -8902,7 +9016,7 @@ const MowerMapRenderer = forwardRef(({ unitType = UnitsType.Imperial, language =
|
|
|
8902
9016
|
// const mapRef = useMap();
|
|
8903
9017
|
const [isGoogleMapsReady, setIsGoogleMapsReady] = useState(false);
|
|
8904
9018
|
const [hasInitializedBounds, setHasInitializedBounds] = useState(false);
|
|
8905
|
-
const { clearSubBoundaryBorder, clearObstacles } = useSubBoundaryBorderStore();
|
|
9019
|
+
const { clearSubBoundaryBorder, clearObstacles, clearSvgElements } = useSubBoundaryBorderStore();
|
|
8906
9020
|
const currentProcessMowingStatusRef = useRef(false);
|
|
8907
9021
|
const { updateProcessStateIsMowing, processStateIsMowing } = useProcessMowingState();
|
|
8908
9022
|
const [mowPartitionData, setMowPartitionData] = useState(null);
|
|
@@ -9071,6 +9185,7 @@ const MowerMapRenderer = forwardRef(({ unitType = UnitsType.Imperial, language =
|
|
|
9071
9185
|
return () => {
|
|
9072
9186
|
clearSubBoundaryBorder();
|
|
9073
9187
|
clearObstacles();
|
|
9188
|
+
clearSvgElements();
|
|
9074
9189
|
updateProcessStateIsMowing(false);
|
|
9075
9190
|
currentProcessMowingStatusRef.current = false;
|
|
9076
9191
|
if (overlayRef.current) {
|
|
@@ -9261,8 +9376,8 @@ const MowerMapRenderer = forwardRef(({ unitType = UnitsType.Imperial, language =
|
|
|
9261
9376
|
setMowPartitionData(mowingPartition);
|
|
9262
9377
|
curMowPartitionData = mowingPartition;
|
|
9263
9378
|
}
|
|
9264
|
-
const positionData = realTimeData?.find(item => item?.type === RealTimeDataType.LOCATION);
|
|
9265
|
-
const statusData = realTimeData?.find(item => item?.type === RealTimeDataType.STATUS);
|
|
9379
|
+
const positionData = realTimeData?.find((item) => item?.type === RealTimeDataType.LOCATION);
|
|
9380
|
+
const statusData = realTimeData?.find((item) => item?.type === RealTimeDataType.STATUS);
|
|
9266
9381
|
if (statusData || positionData) {
|
|
9267
9382
|
const currentStatus = statusData?.vehicleState || positionData?.vehicleState;
|
|
9268
9383
|
// 车辆回桩不会回传最后的park的位置,所以根据实时数据的状态数据判断车辆回到桩上
|
|
@@ -9272,23 +9387,23 @@ const MowerMapRenderer = forwardRef(({ unitType = UnitsType.Imperial, language =
|
|
|
9272
9387
|
else if (currentStatus === RobotStatus.WORKING) {
|
|
9273
9388
|
// 兜底收不到割草地块的实时数据,使用状态来兜底
|
|
9274
9389
|
overlayRef.current.resetBorderLayerHighlight();
|
|
9275
|
-
setMowPartitionData(
|
|
9276
|
-
curMowPartitionData =
|
|
9390
|
+
setMowPartitionData({});
|
|
9391
|
+
curMowPartitionData = {};
|
|
9277
9392
|
}
|
|
9278
|
-
else if (currentStatus === RobotStatus.MOWING &&
|
|
9393
|
+
else if (currentStatus === RobotStatus.MOWING &&
|
|
9394
|
+
curMowPartitionData &&
|
|
9395
|
+
!curMowPartitionData?.partitionIds) {
|
|
9279
9396
|
// 如果当前是割草状态,但是地块数据初始化过且不存在则认为是全局割草,则把所有地块都高亮
|
|
9280
|
-
const allPartitionIds = mapJson?.sub_maps?.map(item => item?.id);
|
|
9397
|
+
const allPartitionIds = mapJson?.sub_maps?.map((item) => item?.id);
|
|
9281
9398
|
setMowPartitionData({
|
|
9282
|
-
partitionIds: allPartitionIds
|
|
9399
|
+
partitionIds: allPartitionIds,
|
|
9283
9400
|
});
|
|
9284
9401
|
curMowPartitionData = {
|
|
9285
|
-
partitionIds: allPartitionIds
|
|
9402
|
+
partitionIds: allPartitionIds,
|
|
9286
9403
|
};
|
|
9287
9404
|
}
|
|
9288
9405
|
}
|
|
9289
|
-
if (!mapJson ||
|
|
9290
|
-
!pathJson ||
|
|
9291
|
-
!overlayRef.current)
|
|
9406
|
+
if (!mapJson || !pathJson || !overlayRef.current)
|
|
9292
9407
|
return;
|
|
9293
9408
|
// 根据后端推送的实时数据,进行不同处理
|
|
9294
9409
|
if (curMowPartitionData) {
|
package/dist/index.js
CHANGED
|
@@ -69,7 +69,8 @@ const DEFAULT_LINE_WIDTHS = {
|
|
|
69
69
|
const DEFAULT_OPACITIES = {
|
|
70
70
|
FULL: 1.0,
|
|
71
71
|
HIGH: 0.7,
|
|
72
|
-
MEDIUM: 0.6
|
|
72
|
+
MEDIUM: 0.6,
|
|
73
|
+
DOODLE: 0.8};
|
|
73
74
|
/**
|
|
74
75
|
* 默认半径设置
|
|
75
76
|
*/
|
|
@@ -1046,15 +1047,37 @@ class PathLayer extends BaseLayer {
|
|
|
1046
1047
|
d += ' Z ';
|
|
1047
1048
|
}
|
|
1048
1049
|
});
|
|
1049
|
-
// 3. svgElements
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1050
|
+
// 3. svgElements(解析 SVG 字符串并提取 path 数据)
|
|
1051
|
+
Object.values(svgElements).forEach((svgPath) => {
|
|
1052
|
+
const svgPathString = svgPath?.metadata?.svg;
|
|
1053
|
+
if (svgPathString && typeof svgPathString === 'string' && svgPathString.trim()) {
|
|
1054
|
+
// 处理转义字符
|
|
1055
|
+
const processedSvgString = svgPathString.replace(/\\n/g, '\n').replace(/\\"/g, '"');
|
|
1056
|
+
// 解析 SVG 字符串
|
|
1057
|
+
const parser = new DOMParser();
|
|
1058
|
+
const svgDoc = parser.parseFromString(processedSvgString, 'image/svg+xml');
|
|
1059
|
+
const svgElement = svgDoc.documentElement;
|
|
1060
|
+
if (svgElement.tagName === 'svg') {
|
|
1061
|
+
// 查找 path 元素
|
|
1062
|
+
const pathElement = svgElement.querySelector('path');
|
|
1063
|
+
if (pathElement) {
|
|
1064
|
+
const pathData = pathElement.getAttribute('d');
|
|
1065
|
+
if (pathData) {
|
|
1066
|
+
// 获取 SVG 元素的变换参数
|
|
1067
|
+
const centerCoords = svgPath.coordinates?.[0] || [0, 0];
|
|
1068
|
+
const center = [centerCoords[0], centerCoords[1]];
|
|
1069
|
+
const userScale = svgPath.metadata.scale || 1;
|
|
1070
|
+
const direction = svgPath.metadata?.direction || 0;
|
|
1071
|
+
const originalWidth = parseFloat(svgElement.getAttribute('width') || '76');
|
|
1072
|
+
const originalHeight = parseFloat(svgElement.getAttribute('height') || '68');
|
|
1073
|
+
// 应用变换到路径数据
|
|
1074
|
+
const transformedPathData = this.transformSvgPath(pathData, center, userScale, direction, originalWidth, originalHeight);
|
|
1075
|
+
d += transformedPathData + ' ';
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1055
1078
|
}
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1079
|
+
}
|
|
1080
|
+
});
|
|
1058
1081
|
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
1059
1082
|
path.setAttribute('d', d);
|
|
1060
1083
|
const clipPath = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath');
|
|
@@ -1079,47 +1102,132 @@ class PathLayer extends BaseLayer {
|
|
|
1079
1102
|
// 2. 创建一个组,应用 clipPath
|
|
1080
1103
|
const group = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
1081
1104
|
group.setAttribute('clip-path', `url(#${clipPathId})`);
|
|
1082
|
-
group.setAttribute('opacity', '0.
|
|
1083
|
-
// 3.
|
|
1105
|
+
group.setAttribute('opacity', '0.5'); // 统一透明度,防止叠加脏乱
|
|
1106
|
+
// 3. 优化渲染:按样式分组并合并路径
|
|
1107
|
+
this.renderOptimizedPaths(group);
|
|
1108
|
+
svgGroup.appendChild(group);
|
|
1109
|
+
}
|
|
1110
|
+
/**
|
|
1111
|
+
* 优化渲染:按样式分组并合并路径,减少 DOM 节点数量
|
|
1112
|
+
*/
|
|
1113
|
+
renderOptimizedPaths(group) {
|
|
1114
|
+
// 按样式分组存储路径数据
|
|
1115
|
+
const styleGroups = new Map();
|
|
1116
|
+
// 收集所有路径数据并按样式分组
|
|
1084
1117
|
for (const element of this.elements) {
|
|
1085
|
-
|
|
1118
|
+
// 类型断言:PathLayer 中的 elements 实际上是 PathElements 结构
|
|
1119
|
+
const pathElement = element;
|
|
1120
|
+
const { id, elements } = pathElement;
|
|
1086
1121
|
this.boundaryPaths[id] = [];
|
|
1122
|
+
elements.forEach((pathElement) => {
|
|
1123
|
+
const { coordinates, style } = pathElement;
|
|
1124
|
+
if (coordinates.length < 2)
|
|
1125
|
+
return;
|
|
1126
|
+
// 生成样式键(用于分组)
|
|
1127
|
+
const styleKey = this.generateStyleKey(style);
|
|
1128
|
+
// 构建路径数据
|
|
1129
|
+
let pathData = `M ${coordinates[0][0]} ${coordinates[0][1]}`;
|
|
1130
|
+
for (let i = 1; i < coordinates.length; i++) {
|
|
1131
|
+
pathData += ` L ${coordinates[i][0]} ${coordinates[i][1]}`;
|
|
1132
|
+
}
|
|
1133
|
+
// 按样式分组存储
|
|
1134
|
+
if (!styleGroups.has(styleKey)) {
|
|
1135
|
+
styleGroups.set(styleKey, { pathData: [], elements: [] });
|
|
1136
|
+
}
|
|
1137
|
+
styleGroups.get(styleKey).pathData.push(pathData);
|
|
1138
|
+
styleGroups.get(styleKey).elements.push(pathElement);
|
|
1139
|
+
});
|
|
1140
|
+
}
|
|
1141
|
+
// 为每种样式创建一个合并的 path 元素
|
|
1142
|
+
styleGroups.forEach((groupData) => {
|
|
1143
|
+
const { pathData, elements } = groupData;
|
|
1144
|
+
if (pathData.length === 0)
|
|
1145
|
+
return;
|
|
1146
|
+
// 使用第一个元素的样式作为该组的样式
|
|
1147
|
+
const firstElement = elements[0];
|
|
1148
|
+
const style = firstElement.style;
|
|
1149
|
+
// 创建合并的 path 元素
|
|
1150
|
+
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
1151
|
+
// 合并所有路径数据
|
|
1152
|
+
const mergedPathData = pathData.join(' ');
|
|
1153
|
+
path.setAttribute('d', mergedPathData);
|
|
1154
|
+
// 设置样式属性
|
|
1155
|
+
path.setAttribute('fill', 'none');
|
|
1156
|
+
path.setAttribute('stroke', style.lineColor || '#000000');
|
|
1157
|
+
path.setAttribute('mix-blend-mode', 'normal');
|
|
1158
|
+
const lineWidth = Math.max(style.lineWidth || 1, 0.5);
|
|
1159
|
+
path.setAttribute('stroke-width', lineWidth.toString());
|
|
1160
|
+
path.setAttribute('stroke-linecap', 'round');
|
|
1161
|
+
path.setAttribute('stroke-linejoin', 'round');
|
|
1162
|
+
path.classList.add('vector-path');
|
|
1163
|
+
// 将合并的 path 添加到组中
|
|
1164
|
+
group.appendChild(path);
|
|
1165
|
+
// 保存引用到 boundaryPaths 中(保持兼容性)
|
|
1087
1166
|
elements.forEach((element) => {
|
|
1088
|
-
|
|
1167
|
+
const { id } = element;
|
|
1168
|
+
if (!this.boundaryPaths[id]) {
|
|
1169
|
+
this.boundaryPaths[id] = [];
|
|
1170
|
+
}
|
|
1171
|
+
this.boundaryPaths[id].push(path);
|
|
1089
1172
|
});
|
|
1173
|
+
});
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* 变换 SVG 路径数据
|
|
1177
|
+
*/
|
|
1178
|
+
transformSvgPath(pathData, center, scale, direction, originalWidth, originalHeight) {
|
|
1179
|
+
// 解析路径数据并应用变换
|
|
1180
|
+
const commands = pathData.match(/[MmLlHhVvCcSsQqTtAaZz][^MmLlHhVvCcSsQqTtAaZz]*/g) || [];
|
|
1181
|
+
let transformedCommands = [];
|
|
1182
|
+
for (const command of commands) {
|
|
1183
|
+
const type = command[0];
|
|
1184
|
+
const params = command
|
|
1185
|
+
.slice(1)
|
|
1186
|
+
.trim()
|
|
1187
|
+
.split(/[\s,]+/)
|
|
1188
|
+
.filter(Boolean)
|
|
1189
|
+
.map(Number);
|
|
1190
|
+
if (type === 'Z' || type === 'z') {
|
|
1191
|
+
// 闭合路径,不需要变换
|
|
1192
|
+
transformedCommands.push(command);
|
|
1193
|
+
continue;
|
|
1194
|
+
}
|
|
1195
|
+
// 处理坐标参数
|
|
1196
|
+
let transformedParams = [];
|
|
1197
|
+
for (let i = 0; i < params.length; i += 2) {
|
|
1198
|
+
if (i + 1 < params.length) {
|
|
1199
|
+
let x = params[i];
|
|
1200
|
+
let y = params[i + 1];
|
|
1201
|
+
// 应用变换:先平移到中心,然后缩放、旋转,最后平移到目标位置
|
|
1202
|
+
// 1. 平移到原点(相对于原始尺寸的中心)
|
|
1203
|
+
x -= originalWidth / 2;
|
|
1204
|
+
y -= originalHeight / 2;
|
|
1205
|
+
// 2. 应用缩放
|
|
1206
|
+
x *= scale;
|
|
1207
|
+
y *= scale;
|
|
1208
|
+
// 3. 应用旋转
|
|
1209
|
+
const cos = Math.cos(-direction);
|
|
1210
|
+
const sin = Math.sin(-direction);
|
|
1211
|
+
const newX = x * cos - y * sin;
|
|
1212
|
+
const newY = x * sin + y * cos;
|
|
1213
|
+
// 4. 平移到目标位置
|
|
1214
|
+
x = newX + center[0];
|
|
1215
|
+
y = newY + center[1];
|
|
1216
|
+
transformedParams.push(x, y);
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
// 重建命令
|
|
1220
|
+
if (transformedParams.length > 0) {
|
|
1221
|
+
transformedCommands.push(type + transformedParams.join(' '));
|
|
1222
|
+
}
|
|
1090
1223
|
}
|
|
1091
|
-
|
|
1224
|
+
return transformedCommands.join(' ');
|
|
1092
1225
|
}
|
|
1093
1226
|
/**
|
|
1094
|
-
*
|
|
1227
|
+
* 生成样式键,用于路径分组
|
|
1095
1228
|
*/
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
if (coordinates.length < 2)
|
|
1099
|
-
return;
|
|
1100
|
-
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
1101
|
-
// 构建路径数据
|
|
1102
|
-
let pathData = `M ${coordinates[0][0]} ${coordinates[0][1]}`;
|
|
1103
|
-
for (let i = 1; i < coordinates.length; i++) {
|
|
1104
|
-
pathData += ` L ${coordinates[i][0]} ${coordinates[i][1]}`;
|
|
1105
|
-
}
|
|
1106
|
-
path.style.mixBlendMode = 'normal';
|
|
1107
|
-
// 设置路径属性
|
|
1108
|
-
path.setAttribute('d', pathData);
|
|
1109
|
-
// 直接给fill的颜色设置透明度会导致path重叠的部分颜色叠加,所以使用fill填充实色,通过fill-opacity设置透明度
|
|
1110
|
-
path.setAttribute('fill', 'none');
|
|
1111
|
-
// path.setAttribute('fill-opacity', '0.4');
|
|
1112
|
-
path.setAttribute('stroke', style.lineColor || '#000000');
|
|
1113
|
-
path.setAttribute('mix-blend-mode', 'normal');
|
|
1114
|
-
const lineWidth = Math.max(style.lineWidth || 1, 0.5);
|
|
1115
|
-
path.setAttribute('stroke-width', lineWidth.toString());
|
|
1116
|
-
path.setAttribute('stroke-linecap', 'round');
|
|
1117
|
-
path.setAttribute('stroke-linejoin', 'round');
|
|
1118
|
-
// 注意:这里不设置 opacity,因为透明度由父组控制
|
|
1119
|
-
// path.setAttribute('vector-effect', 'non-scaling-stroke');
|
|
1120
|
-
path.classList.add('vector-path');
|
|
1121
|
-
this.boundaryPaths[id].push(path);
|
|
1122
|
-
group.appendChild(path);
|
|
1229
|
+
generateStyleKey(style) {
|
|
1230
|
+
return `${style.lineColor || '#000000'}-${style.lineWidth || 1}-${style.opacity || 1}`;
|
|
1123
1231
|
}
|
|
1124
1232
|
}
|
|
1125
1233
|
|
|
@@ -1543,7 +1651,7 @@ const DOODLE_STYLES = {
|
|
|
1543
1651
|
lineColor: '#ff5722',
|
|
1544
1652
|
fillColor: '#ff9800', // 粉色半透明填充
|
|
1545
1653
|
lineWidth: DEFAULT_LINE_WIDTHS.TIME_LIMIT_OBSTACLE,
|
|
1546
|
-
opacity: DEFAULT_OPACITIES.
|
|
1654
|
+
opacity: DEFAULT_OPACITIES.DOODLE,
|
|
1547
1655
|
};
|
|
1548
1656
|
const PATH_EDGE_STYLES = {
|
|
1549
1657
|
lineWidth: DEFAULT_LINE_WIDTHS.PATH,
|
|
@@ -4977,6 +5085,12 @@ class BoundaryBorderLayer extends BaseLayer {
|
|
|
4977
5085
|
this.mowingBoundarys = mowingBoundarys;
|
|
4978
5086
|
}
|
|
4979
5087
|
}
|
|
5088
|
+
/**
|
|
5089
|
+
* 获取当前割草任务的边界
|
|
5090
|
+
*/
|
|
5091
|
+
getMowingBoundarys() {
|
|
5092
|
+
return this.mowingBoundarys;
|
|
5093
|
+
}
|
|
4980
5094
|
/**
|
|
4981
5095
|
* SVG渲染方法
|
|
4982
5096
|
*/
|
|
@@ -6154,7 +6268,7 @@ class BoundaryLabelsManager {
|
|
|
6154
6268
|
labelDiv.style.zIndex = BoundaryLabelsManager.Z_INDEX.DEFAULT.toString();
|
|
6155
6269
|
// 计算进度
|
|
6156
6270
|
const progress = boundary.finishedArea && boundary.area
|
|
6157
|
-
? `${Math.
|
|
6271
|
+
? `${Math.floor((boundary.finishedArea / boundary.area) * 100)}%`
|
|
6158
6272
|
: '0%';
|
|
6159
6273
|
// 基础内容(始终显示)
|
|
6160
6274
|
const baseContent = document.createElement('div');
|
|
@@ -6171,13 +6285,15 @@ class BoundaryLabelsManager {
|
|
|
6171
6285
|
this.currentExpandedBoundaryId === boundary.id ? 'block' : 'none';
|
|
6172
6286
|
extendedContent.style.borderTop = '1px solid rgba(255,255,255,0.2)';
|
|
6173
6287
|
extendedContent.style.paddingTop = '6px';
|
|
6174
|
-
|
|
6288
|
+
const boundaryLayer = this.svgView.getLayer(LAYER_DEFAULT_TYPE.BOUNDARY_BORDER);
|
|
6289
|
+
const mowingBoundarys = boundaryLayer.getMowingBoundarys();
|
|
6175
6290
|
// 面积信息
|
|
6176
6291
|
const totalArea = convertAreaByUnits(boundary.area || 0, this.unitType);
|
|
6177
6292
|
const finishedArea = convertAreaByUnits(boundary.finishedArea || 0, this.unitType);
|
|
6178
6293
|
const coverageText = `Coverage: ${finishedArea.value}/${totalArea.value}`;
|
|
6294
|
+
const isMowing = mowingBoundarys.includes(boundary.id);
|
|
6179
6295
|
// 日期信息
|
|
6180
|
-
const dateText = formatBoundaryDateText(boundary.endTime || 0);
|
|
6296
|
+
const dateText = formatBoundaryDateText(isMowing ? Date.now() / 1000 : boundary.endTime || 0);
|
|
6181
6297
|
const covertHtml = `<div style="margin-bottom: 3px; font-weight: bold;">${coverageText}</div>`;
|
|
6182
6298
|
const dateHtml = `<div>${dateText}</div>`;
|
|
6183
6299
|
extendedContent.innerHTML = boundary.finishedArea > 0 ? `${covertHtml}${dateHtml}` : covertHtml;
|
|
@@ -6295,7 +6411,6 @@ class BoundaryLabelsManager {
|
|
|
6295
6411
|
// 计算边界中心点的地图坐标
|
|
6296
6412
|
const mapCenter = this.calculatePolygonCentroid(boundary.points);
|
|
6297
6413
|
if (!mapCenter) {
|
|
6298
|
-
console.warn(`BoundaryLabelsManager: 无法计算边界 ${boundary.name} (ID: ${boundary.id}) 的中心点`);
|
|
6299
6414
|
return;
|
|
6300
6415
|
}
|
|
6301
6416
|
// 直接使用预计算的数据进行坐标转换
|
|
@@ -6380,7 +6495,6 @@ class BoundaryLabelsManager {
|
|
|
6380
6495
|
area = area / 2;
|
|
6381
6496
|
// 如果面积为0,回退到简单的平均值计算
|
|
6382
6497
|
if (Math.abs(area) < 1e-10) {
|
|
6383
|
-
console.warn('BoundaryLabelsManager: 多边形面积为0,使用平均值计算重心');
|
|
6384
6498
|
return this.calculateAverageCenter(validPoints);
|
|
6385
6499
|
}
|
|
6386
6500
|
centroidX = centroidX / (6 * area);
|
|
@@ -8904,7 +9018,7 @@ const MowerMapRenderer = React.forwardRef(({ unitType = UnitsType.Imperial, lang
|
|
|
8904
9018
|
// const mapRef = useMap();
|
|
8905
9019
|
const [isGoogleMapsReady, setIsGoogleMapsReady] = React.useState(false);
|
|
8906
9020
|
const [hasInitializedBounds, setHasInitializedBounds] = React.useState(false);
|
|
8907
|
-
const { clearSubBoundaryBorder, clearObstacles } = useSubBoundaryBorderStore();
|
|
9021
|
+
const { clearSubBoundaryBorder, clearObstacles, clearSvgElements } = useSubBoundaryBorderStore();
|
|
8908
9022
|
const currentProcessMowingStatusRef = React.useRef(false);
|
|
8909
9023
|
const { updateProcessStateIsMowing, processStateIsMowing } = useProcessMowingState();
|
|
8910
9024
|
const [mowPartitionData, setMowPartitionData] = React.useState(null);
|
|
@@ -9073,6 +9187,7 @@ const MowerMapRenderer = React.forwardRef(({ unitType = UnitsType.Imperial, lang
|
|
|
9073
9187
|
return () => {
|
|
9074
9188
|
clearSubBoundaryBorder();
|
|
9075
9189
|
clearObstacles();
|
|
9190
|
+
clearSvgElements();
|
|
9076
9191
|
updateProcessStateIsMowing(false);
|
|
9077
9192
|
currentProcessMowingStatusRef.current = false;
|
|
9078
9193
|
if (overlayRef.current) {
|
|
@@ -9263,8 +9378,8 @@ const MowerMapRenderer = React.forwardRef(({ unitType = UnitsType.Imperial, lang
|
|
|
9263
9378
|
setMowPartitionData(mowingPartition);
|
|
9264
9379
|
curMowPartitionData = mowingPartition;
|
|
9265
9380
|
}
|
|
9266
|
-
const positionData = realTimeData?.find(item => item?.type === RealTimeDataType.LOCATION);
|
|
9267
|
-
const statusData = realTimeData?.find(item => item?.type === RealTimeDataType.STATUS);
|
|
9381
|
+
const positionData = realTimeData?.find((item) => item?.type === RealTimeDataType.LOCATION);
|
|
9382
|
+
const statusData = realTimeData?.find((item) => item?.type === RealTimeDataType.STATUS);
|
|
9268
9383
|
if (statusData || positionData) {
|
|
9269
9384
|
const currentStatus = statusData?.vehicleState || positionData?.vehicleState;
|
|
9270
9385
|
// 车辆回桩不会回传最后的park的位置,所以根据实时数据的状态数据判断车辆回到桩上
|
|
@@ -9274,23 +9389,23 @@ const MowerMapRenderer = React.forwardRef(({ unitType = UnitsType.Imperial, lang
|
|
|
9274
9389
|
else if (currentStatus === RobotStatus.WORKING) {
|
|
9275
9390
|
// 兜底收不到割草地块的实时数据,使用状态来兜底
|
|
9276
9391
|
overlayRef.current.resetBorderLayerHighlight();
|
|
9277
|
-
setMowPartitionData(
|
|
9278
|
-
curMowPartitionData =
|
|
9392
|
+
setMowPartitionData({});
|
|
9393
|
+
curMowPartitionData = {};
|
|
9279
9394
|
}
|
|
9280
|
-
else if (currentStatus === RobotStatus.MOWING &&
|
|
9395
|
+
else if (currentStatus === RobotStatus.MOWING &&
|
|
9396
|
+
curMowPartitionData &&
|
|
9397
|
+
!curMowPartitionData?.partitionIds) {
|
|
9281
9398
|
// 如果当前是割草状态,但是地块数据初始化过且不存在则认为是全局割草,则把所有地块都高亮
|
|
9282
|
-
const allPartitionIds = mapJson?.sub_maps?.map(item => item?.id);
|
|
9399
|
+
const allPartitionIds = mapJson?.sub_maps?.map((item) => item?.id);
|
|
9283
9400
|
setMowPartitionData({
|
|
9284
|
-
partitionIds: allPartitionIds
|
|
9401
|
+
partitionIds: allPartitionIds,
|
|
9285
9402
|
});
|
|
9286
9403
|
curMowPartitionData = {
|
|
9287
|
-
partitionIds: allPartitionIds
|
|
9404
|
+
partitionIds: allPartitionIds,
|
|
9288
9405
|
};
|
|
9289
9406
|
}
|
|
9290
9407
|
}
|
|
9291
|
-
if (!mapJson ||
|
|
9292
|
-
!pathJson ||
|
|
9293
|
-
!overlayRef.current)
|
|
9408
|
+
if (!mapJson || !pathJson || !overlayRef.current)
|
|
9294
9409
|
return;
|
|
9295
9410
|
// 根据后端推送的实时数据,进行不同处理
|
|
9296
9411
|
if (curMowPartitionData) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BoundaryLabelsManager.d.ts","sourceRoot":"","sources":["../../src/render/BoundaryLabelsManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"BoundaryLabelsManager.d.ts","sourceRoot":"","sources":["../../src/render/BoundaryLabelsManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAO1C,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAGzD;;;GAGG;AACH,qBAAa,qBAAqB;IAChC,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,YAAY,CAAiB;IACrC,OAAO,CAAC,SAAS,CAA4B;IAC7C,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,kBAAkB,CAAmC;IAC7D,OAAO,CAAC,QAAQ,CAAY;IAC5B,OAAO,CAAC,QAAQ,CAAS;IAGzB,OAAO,CAAC,yBAAyB,CAAuB;IAGxD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAG7B;IAGF,OAAO,CAAC,QAAQ,CAAa;gBAEjB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE;QAAE,QAAQ,EAAE,SAAS,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE;IAQhI;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAe3B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAc/B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAM1B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAMxB;;OAEG;IACH,aAAa,CAAC,UAAU,EAAE,WAAW;IAIrC;;OAEG;IACH,UAAU,CAAC,QAAQ,EAAE,YAAY,GAAG,WAAW,GAAG,IAAI;IAkGtD;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAkB1B;;OAEG;IACH,OAAO,CAAC,WAAW;IAenB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAgB3B;;OAEG;IACH,cAAc;IAOd;;OAEG;IACH,UAAU,IAAI,WAAW,GAAG,IAAI;IAIhC;;;;;OAKG;IACH,kCAAkC,CAChC,QAAQ,CAAC,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;IAsCnE;;OAEG;IACH,OAAO,CAAC,uBAAuB;IA2B/B;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IAUhC;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IA8C9B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAiB9B;;OAEG;IACH,OAAO,CAAC,kCAAkC;IA4B1C;;OAEG;IACH,OAAO,CAAC,8CAA8C;IAkBtD;;OAEG;IACH,kBAAkB,CAAC,YAAY,EAAE,YAAY,EAAE;IAY/C;;OAEG;IACH,OAAO;IAcP;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,OAAO;IAM3B;;OAEG;IACH,OAAO,CAAC,eAAe;IAKvB;;OAEG;IACH,iBAAiB;IAcjB;;OAEG;IACH,cAAc,CAAC,WAAW,EAAE,OAAO;IAyBnC;;;OAGG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM;CAa7B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MowerMapRenderer.d.ts","sourceRoot":"","sources":["../../src/render/MowerMapRenderer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAQN,MAAM,OAAO,CAAC;AA6Cf,OAAO,EAAa,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAO1F,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,MAAM,EAAE,GAAG,CAAC;KACb;CACF;AAgGD,eAAO,MAAM,gBAAgB,
|
|
1
|
+
{"version":3,"file":"MowerMapRenderer.d.ts","sourceRoot":"","sources":["../../src/render/MowerMapRenderer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAQN,MAAM,OAAO,CAAC;AA6Cf,OAAO,EAAa,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAO1F,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,MAAM,EAAE,GAAG,CAAC;KACb;CACF;AAgGD,eAAO,MAAM,gBAAgB,mGA+sB5B,CAAC;AAIF,eAAe,gBAAgB,CAAC;AAChC,YAAY,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BoundaryBorderLayer.d.ts","sourceRoot":"","sources":["../../../src/render/layers/BoundaryBorderLayer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAKxC;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;IAChD,KAAK,EAAE,MAAM,CAAK;IAClB,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC,CAAC;IACrD,eAAe,EAAE,MAAM,EAAE,CAAC;;IAU1B;;OAEG;IACH,kBAAkB,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE;IAS7C;;OAEG;IACI,OAAO,CAAC,QAAQ,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAe1D;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAuB5B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAoCxB;;OAEG;IACH,OAAO,CAAC,8BAA8B;IA4CtC;;OAEG;IACH,OAAO,CAAC,6BAA6B;IAqDrC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IA2C9B,OAAO,CAAC,UAAU;CAGnB"}
|
|
1
|
+
{"version":3,"file":"BoundaryBorderLayer.d.ts","sourceRoot":"","sources":["../../../src/render/layers/BoundaryBorderLayer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAKxC;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;IAChD,KAAK,EAAE,MAAM,CAAK;IAClB,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC,CAAC;IACrD,eAAe,EAAE,MAAM,EAAE,CAAC;;IAU1B;;OAEG;IACH,kBAAkB,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE;IAS7C;;OAEG;IACH,kBAAkB;IAIlB;;OAEG;IACI,OAAO,CAAC,QAAQ,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAe1D;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAuB5B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAoCxB;;OAEG;IACH,OAAO,CAAC,8BAA8B;IA4CtC;;OAEG;IACH,OAAO,CAAC,6BAA6B;IAqDrC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IA2C9B,OAAO,CAAC,UAAU;CAGnB"}
|
|
@@ -18,8 +18,16 @@ export declare class PathLayer extends BaseLayer {
|
|
|
18
18
|
*/
|
|
19
19
|
drawSVG(svgGroup: SVGGElement, scale: number, lineScale: number): void;
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
21
|
+
* 优化渲染:按样式分组并合并路径,减少 DOM 节点数量
|
|
22
22
|
*/
|
|
23
|
-
private
|
|
23
|
+
private renderOptimizedPaths;
|
|
24
|
+
/**
|
|
25
|
+
* 变换 SVG 路径数据
|
|
26
|
+
*/
|
|
27
|
+
private transformSvgPath;
|
|
28
|
+
/**
|
|
29
|
+
* 生成样式键,用于路径分组
|
|
30
|
+
*/
|
|
31
|
+
private generateStyleKey;
|
|
24
32
|
}
|
|
25
33
|
//# sourceMappingURL=PathLayer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PathLayer.d.ts","sourceRoot":"","sources":["../../../src/render/layers/PathLayer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PathLayer.d.ts","sourceRoot":"","sources":["../../../src/render/layers/PathLayer.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC;;;GAGG;AACH,qBAAa,SAAU,SAAQ,SAAS;IACtC,KAAK,EAAE,MAAM,CAAK;IAClB,KAAK,EAAE,MAAM,CAAK;IAClB,SAAS,EAAE,MAAM,CAAK;IACtB,aAAa,EAAE,GAAG,CAAM;;IAOxB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAmG3B;;OAEG;IACI,OAAO,CAAC,QAAQ,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAuB7E;;OAEG;IACH,OAAO,CAAC,oBAAoB;IA2E5B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAkExB;;OAEG;IACH,OAAO,CAAC,gBAAgB;CAGzB"}
|