|
|
@@ -60,7 +60,6 @@ export default {
|
|
|
|
|
|
|
|
|
routeGroups: {},
|
|
|
- polylines: [],
|
|
|
privateStyle: {
|
|
|
legend: {}
|
|
|
},
|
|
|
@@ -349,6 +348,7 @@ export default {
|
|
|
// 2. 处理路线类(干线/特勤)
|
|
|
this.routeGroups[config.name] = [];
|
|
|
const lines = realRouteConfigs[config.name] || [];
|
|
|
+ const trunkSegments = [];
|
|
|
|
|
|
for (let lineIdx = 0; lineIdx < lines.length; lineIdx += 1) {
|
|
|
if (this.isComponentDestroyed || drawSeq !== this.drawSeq) return;
|
|
|
@@ -369,6 +369,22 @@ export default {
|
|
|
path
|
|
|
});
|
|
|
|
|
|
+ // 统计干线协调实际生成的线段数
|
|
|
+ if (config.name === '干线协调' && overlays.length > 0) {
|
|
|
+ const polylineCount = overlays.filter(o => o instanceof this.AMap.Polyline).length;
|
|
|
+ for (let s = 0; s < polylineCount; s++) {
|
|
|
+ const idx = trunkSegments.length + 1;
|
|
|
+ trunkSegments.push({
|
|
|
+ id: 'trunk_' + idx,
|
|
|
+ label: '干线' + idx,
|
|
|
+ intersections: Array.from({ length: 6 }, (_, k) => '干线' + idx + '_路口' + (k + 1)),
|
|
|
+ distances: Array.from({ length: 6 }, (_, k) => k * 1000),
|
|
|
+ _lineIdx: lineIdx,
|
|
|
+ _segmentIdx: s
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
if (this.isComponentDestroyed || drawSeq !== this.drawSeq) return;
|
|
|
|
|
|
if (overlays.length > 0) {
|
|
|
@@ -378,6 +394,11 @@ export default {
|
|
|
|
|
|
await this.sleep(80);
|
|
|
}
|
|
|
+
|
|
|
+ // 干线协调绘制完成后,通知父组件用于菜单渲染
|
|
|
+ if (config.name === '干线协调' && trunkSegments.length > 0) {
|
|
|
+ this.$emit('bindTrunkMenuTree', trunkSegments);
|
|
|
+ }
|
|
|
}
|
|
|
},
|
|
|
|
|
|
@@ -463,11 +484,24 @@ export default {
|
|
|
basePath = this.buildFallbackLinePath(line.start, line.end, 30);
|
|
|
}
|
|
|
|
|
|
+ // --- 核心优化:物理分散偏移 (Dispersal Offset) ---
|
|
|
+ // 根据 lineIdx 为每条路径应用微小偏移,避免多条线路完全重合
|
|
|
+ // 0.00015 度约等于 15 米,(lineIdx - 3) 将多条线在中心点两侧排开
|
|
|
+ const offsetVal = (Number(lineIdx) - 3) * 0.00015;
|
|
|
+ const applyOffset = (p) => {
|
|
|
+ const lng = p.lng || (p.getLng ? p.getLng() : (Array.isArray(p) ? Number(p[0]) : 0));
|
|
|
+ const lat = p.lat || (p.getLat ? p.getLat() : (Array.isArray(p) ? Number(p[1]) : 0));
|
|
|
+ return [lng + offsetVal, lat + offsetVal];
|
|
|
+ };
|
|
|
+
|
|
|
const segments = this.extractMainStraightSegments(basePath);
|
|
|
const overlays = [];
|
|
|
|
|
|
- segments.forEach((segmentPath, segmentIdx) => {
|
|
|
- if (!Array.isArray(segmentPath) || segmentPath.length < 2) return;
|
|
|
+ segments.forEach((rawSegmentPath, segmentIdx) => {
|
|
|
+ if (!Array.isArray(rawSegmentPath) || rawSegmentPath.length < 2) return;
|
|
|
+
|
|
|
+ // 应用偏移到当前段的所有点
|
|
|
+ const segmentPath = rawSegmentPath.map(p => applyOffset(p));
|
|
|
|
|
|
const polyline = new this.AMap.Polyline({
|
|
|
path: segmentPath,
|
|
|
@@ -479,14 +513,72 @@ export default {
|
|
|
overlays.push(polyline);
|
|
|
|
|
|
const totalPoints = segmentPath.length;
|
|
|
- const indices = this.pickEvenlySpacedIndices(totalPoints, 8);
|
|
|
+
|
|
|
+ // --- 核心优化:全路段物理距离均匀放置方向箭头 ---
|
|
|
+ // 1. 计算整条 segmentPath 的总物理距离及每个点的累计距离
|
|
|
+ const pathDistances = [0];
|
|
|
+ let totalPathDist = 0;
|
|
|
+ for (let j = 0; j < totalPoints - 1; j++) {
|
|
|
+ const d = this.calcApproxDistance(segmentPath[j], segmentPath[j+1]);
|
|
|
+ totalPathDist += d;
|
|
|
+ pathDistances.push(totalPathDist);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 设定标准间距:约每 0.0018 度 (约 200 米) 放置一个箭头
|
|
|
+ const targetSpacing = 0.0018;
|
|
|
+ let currentTargetDist = targetSpacing / 2; // 第一个箭头放在 1/2 间距处,让分布更美观
|
|
|
+
|
|
|
+ while (currentTargetDist < totalPathDist) {
|
|
|
+ // 3. 寻找对应 targetDist 的路径位置 (线性插值)
|
|
|
+ let foundIdx = 0;
|
|
|
+ for (let j = 0; j < pathDistances.length - 1; j++) {
|
|
|
+ if (currentTargetDist >= pathDistances[j] && currentTargetDist <= pathDistances[j + 1]) {
|
|
|
+ foundIdx = j;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const p1 = segmentPath[foundIdx];
|
|
|
+ const p2 = segmentPath[foundIdx + 1];
|
|
|
+ if (p1 && p2) {
|
|
|
+ // 在 p1 和 p2 之间线性插值
|
|
|
+ const ratio = (currentTargetDist - pathDistances[foundIdx]) / (pathDistances[foundIdx + 1] - pathDistances[foundIdx]);
|
|
|
+ const lng1 = Number(p1[0]);
|
|
|
+ const lat1 = Number(p1[1]);
|
|
|
+ const lng2 = Number(p2[0]);
|
|
|
+ const lat2 = Number(p2[1]);
|
|
|
+
|
|
|
+ const midLng = lng1 + (lng2 - lng1) * ratio;
|
|
|
+ const midLat = lat1 + (lat2 - lat1) * ratio;
|
|
|
+
|
|
|
+ const bearing = this.calcBearingDeg(p1, p2);
|
|
|
+ const rotation = bearing - 90;
|
|
|
+
|
|
|
+ const directionMarker = new this.AMap.Marker({
|
|
|
+ position: [midLng, midLat],
|
|
|
+ content: `
|
|
|
+ <div style="transform: rotate(${rotation}deg); width: 20px; height: 10px; display: flex; align-items: center; pointer-events: none; opacity: 0.85;">
|
|
|
+ <img src="${require('@/assets/map/direction.png')}" style="width: 100%; height: auto;" />
|
|
|
+ </div>
|
|
|
+ `,
|
|
|
+ offset: new this.AMap.Pixel(-10, -5),
|
|
|
+ zIndex: 20,
|
|
|
+ bubble: true
|
|
|
+ });
|
|
|
+ overlays.push(directionMarker);
|
|
|
+ }
|
|
|
+ currentTargetDist += targetSpacing;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 为圆点保留原来的分布 logic (不受箭头影响)
|
|
|
+ const indices = this.pickEvenlySpacedIndices(totalPoints, 6);
|
|
|
|
|
|
// 为第一个和最后一个圆点设置特殊类型
|
|
|
for (let i = 0; i < indices.length; i++) {
|
|
|
const idx = indices[i];
|
|
|
const p = segmentPath[idx];
|
|
|
- const lng = p && typeof p.lng === 'number' ? p.lng : (Array.isArray(p) ? Number(p[0]) : NaN);
|
|
|
- const lat = p && typeof p.lat === 'number' ? p.lat : (Array.isArray(p) ? Number(p[1]) : NaN);
|
|
|
+ const lng = Number(p[0]);
|
|
|
+ const lat = Number(p[1]);
|
|
|
if (Number.isNaN(lng) || Number.isNaN(lat)) continue;
|
|
|
|
|
|
// 确定圆点类型:第一个为start,最后一个为end,其余为normal
|
|
|
@@ -512,18 +604,11 @@ export default {
|
|
|
pickEvenlySpacedIndices(totalPoints, count) {
|
|
|
const total = Math.max(Number(totalPoints) || 0, 0);
|
|
|
const target = Math.max(Number(count) || 0, 0);
|
|
|
- if (total <= 0) return [];
|
|
|
- if (target <= 0) return [];
|
|
|
+ if (total <= 0 || target <= 0) return [];
|
|
|
if (target >= total) return Array.from({ length: total }, (_, i) => i);
|
|
|
if (target === 1) return [0];
|
|
|
|
|
|
- const set = new Set();
|
|
|
- const last = total - 1;
|
|
|
- for (let k = 0; k < target; k += 1) {
|
|
|
- const idx = Math.round((k * last) / (target - 1));
|
|
|
- set.add(idx);
|
|
|
- }
|
|
|
- return Array.from(set).sort((a, b) => a - b);
|
|
|
+ return Array.from({ length: target }, (_, k) => Math.round((k * (total - 1)) / (target - 1)));
|
|
|
},
|
|
|
|
|
|
buildFallbackLinePath(start, end, pointCount) {
|
|
|
@@ -546,7 +631,14 @@ export default {
|
|
|
},
|
|
|
|
|
|
extractMainStraightSegments(path) {
|
|
|
- const points = (path || []).filter(p => p && typeof p.lng === 'number' && typeof p.lat === 'number');
|
|
|
+ const getCoord = (p) => {
|
|
|
+ if (!p) return { lng: NaN, lat: NaN };
|
|
|
+ if (Array.isArray(p)) return { lng: Number(p[0]), lat: Number(p[1]) };
|
|
|
+ if (p.getLng) return { lng: p.getLng(), lat: p.getLat() };
|
|
|
+ return { lng: Number(p.lng), lat: Number(p.lat) };
|
|
|
+ };
|
|
|
+
|
|
|
+ const points = (path || []).map(p => getCoord(p)).filter(p => !isNaN(p.lng) && !isNaN(p.lat));
|
|
|
if (points.length < 2) return [];
|
|
|
|
|
|
const thresholdDeg = 18;
|
|
|
@@ -600,13 +692,24 @@ export default {
|
|
|
return finalSegments.length > 0 ? finalSegments : [points];
|
|
|
},
|
|
|
|
|
|
+ // 内部通用工具:从不同格式的点中提取经纬度数组 [lng, lat]
|
|
|
+ _getCoords(p) {
|
|
|
+ if (!p) return [0, 0];
|
|
|
+ const lng = p.lng || (p.getLng ? p.getLng() : (Array.isArray(p) ? Number(p[0]) : 0));
|
|
|
+ const lat = p.lat || (p.getLat ? p.getLat() : (Array.isArray(p) ? Number(p[1]) : 0));
|
|
|
+ return [lng, lat];
|
|
|
+ },
|
|
|
+
|
|
|
calcBearingDeg(a, b) {
|
|
|
- const latRad = ((a.lat + b.lat) / 2) * Math.PI / 180;
|
|
|
- const dx = (b.lng - a.lng) * Math.cos(latRad);
|
|
|
- const dy = (b.lat - a.lat);
|
|
|
- let deg = Math.atan2(dy, dx) * 180 / Math.PI;
|
|
|
- if (deg < 0) deg += 360;
|
|
|
- return deg;
|
|
|
+ const [alng, alat] = this._getCoords(a);
|
|
|
+ const [blng, blat] = this._getCoords(b);
|
|
|
+
|
|
|
+ const latRad = ((alat + blat) / 2) * Math.PI / 180;
|
|
|
+ const dx = (blng - alng) * Math.cos(latRad);
|
|
|
+ const dy = (blat - alat);
|
|
|
+
|
|
|
+ const mathAngle = Math.atan2(dy, dx) * 180 / Math.PI;
|
|
|
+ return (90 - mathAngle + 360) % 360;
|
|
|
},
|
|
|
|
|
|
calcAngleDiffDeg(a, b) {
|
|
|
@@ -616,9 +719,12 @@ export default {
|
|
|
},
|
|
|
|
|
|
calcApproxDistance(a, b) {
|
|
|
- const latRad = ((a.lat + b.lat) / 2) * Math.PI / 180;
|
|
|
- const dx = (b.lng - a.lng) * Math.cos(latRad);
|
|
|
- const dy = (b.lat - a.lat);
|
|
|
+ const [alng, alat] = this._getCoords(a);
|
|
|
+ const [blng, blat] = this._getCoords(b);
|
|
|
+
|
|
|
+ const latRad = ((alat + blat) / 2) * Math.PI / 180;
|
|
|
+ const dx = (blng - alng) * Math.cos(latRad);
|
|
|
+ const dy = (blat - alat);
|
|
|
return Math.sqrt(dx * dx + dy * dy);
|
|
|
},
|
|
|
|
|
|
@@ -626,8 +732,8 @@ export default {
|
|
|
if (!position || !config) return null;
|
|
|
|
|
|
try {
|
|
|
- const lng = Number(position[0] || position.lng);
|
|
|
- const lat = Number(position[1] || position.lat);
|
|
|
+ const lng = position.getLng ? position.getLng() : Number(position[0] !== undefined ? position[0] : position.lng);
|
|
|
+ const lat = position.getLat ? position.getLat() : Number(position[1] !== undefined ? position[1] : position.lat);
|
|
|
if (isNaN(lng) || isNaN(lat)) return null;
|
|
|
|
|
|
// 状态文字:起、终、或者状态配置的首字母
|
|
|
@@ -637,99 +743,58 @@ export default {
|
|
|
|
|
|
const isAbnormal = ["离线", "降级", "故障"].includes(config.name);
|
|
|
const isRoute = ["干线协调", "勤务路线"].includes(config.name);
|
|
|
+ const isStartEnd = type === 'start' || type === 'end';
|
|
|
+
|
|
|
+ // 核心配置映射:减少嵌套逻辑
|
|
|
+ const markerStyle = isStartEnd ? {
|
|
|
+ size: '24px',
|
|
|
+ height: '30px',
|
|
|
+ offset: [-12, -30],
|
|
|
+ zIndex: 120,
|
|
|
+ border: '2px solid #fff'
|
|
|
+ } : (isAbnormal ? {
|
|
|
+ size: '30px',
|
|
|
+ height: '30px',
|
|
|
+ offset: [-15, -15],
|
|
|
+ zIndex: 110,
|
|
|
+ border: 'none'
|
|
|
+ } : {
|
|
|
+ size: '14px',
|
|
|
+ height: '14px',
|
|
|
+ offset: [-9, -9], // 14px + 2px padding * 2 = 18px total
|
|
|
+ zIndex: 100,
|
|
|
+ border: isRoute ? 'none' : '1.5px solid rgba(255,255,255,0.7)'
|
|
|
+ });
|
|
|
|
|
|
- // 动态计算尺寸:起终点最大,异常点次之,普通点最小
|
|
|
- const isStartEnd = type === 'start' || type === 'end';
|
|
|
- const size = isStartEnd ? '24px' : (isAbnormal ? '30px' : '14px');
|
|
|
- const sizeNumber = parseInt(size, 10);
|
|
|
- const paddingNumber = (isAbnormal || isStartEnd) ? 0 : 2;
|
|
|
- const outerSizeNumber = sizeNumber + paddingNumber * 2;
|
|
|
-
|
|
|
- // 边框样式:起终点用实白边框
|
|
|
- const borderStyle = isStartEnd
|
|
|
- ? '2px solid #fff'
|
|
|
- : '1.5px solid rgba(255,255,255,0.7)';
|
|
|
-
|
|
|
- // 生成标记内容
|
|
|
- let markerContent = '';
|
|
|
- if (isStartEnd) {
|
|
|
- const iconPath = type === 'start'
|
|
|
- ? require('@/assets/map/start.png')
|
|
|
- : require('@/assets/map/end.png');
|
|
|
- markerContent = `
|
|
|
- <div class="pure-light-node start-end-node"
|
|
|
- style="
|
|
|
- width: ${size};
|
|
|
- height: 30px;
|
|
|
- background: transparent;
|
|
|
- border: none;
|
|
|
- display: flex;
|
|
|
- justify-content: center;
|
|
|
- align-items: flex-end;
|
|
|
- cursor: pointer;
|
|
|
- transform-origin: bottom center;
|
|
|
- ">
|
|
|
- <img src="${iconPath}" style="width: 100%; height: auto; object-fit: contain; pointer-events: none;" />
|
|
|
- </div>
|
|
|
- `;
|
|
|
- } else if (isAbnormal) {
|
|
|
- const iconName = config.name === '离线' ? 'lixian' : config.name === '降级' ? 'jiangji' : 'guzhang';
|
|
|
- markerContent = `
|
|
|
- <div class="pure-light-node ${isAbnormal ? 'breathe' : ''}"
|
|
|
- style="
|
|
|
- width: ${size};
|
|
|
- height: ${size};
|
|
|
- background: transparent;
|
|
|
- box-shadow: none;
|
|
|
- border: none;
|
|
|
- box-sizing: content-box;
|
|
|
- display: flex;
|
|
|
- justify-content: center;
|
|
|
- align-items: center;
|
|
|
- cursor: pointer;
|
|
|
- padding: ${paddingNumber}px;
|
|
|
- ">
|
|
|
- <img src="${require(`@/assets/images/icon_${iconName}.png`)}" style="width: 100%; height: 100%; object-fit: contain;" />
|
|
|
- </div>
|
|
|
- `;
|
|
|
- } else {
|
|
|
- markerContent = `
|
|
|
- <div class="pure-light-node ${isAbnormal ? 'breathe' : ''} ${isRoute ? 'route-node' : ''}"
|
|
|
- style="
|
|
|
- width: ${size};
|
|
|
- height: ${size};
|
|
|
- background: ${config.color || '#999'};
|
|
|
- box-shadow: ${isRoute ? 'none' : `0 0 8px ${config.color}`};
|
|
|
- border: ${isRoute ? 'none' : borderStyle};
|
|
|
- box-sizing: content-box;
|
|
|
- display: flex;
|
|
|
- justify-content: center;
|
|
|
- align-items: center;
|
|
|
- color: #fff;
|
|
|
- border-radius: 50%;
|
|
|
- cursor: pointer;
|
|
|
- padding: ${paddingNumber}px;
|
|
|
- ">
|
|
|
- <span style="transform: scale(0.8); font-weight: bold; font-size: 12px;">${displayText}</span>
|
|
|
- </div>
|
|
|
- `;
|
|
|
- }
|
|
|
-
|
|
|
- // 计算偏移量:起点终点底部对齐,其他中心对齐
|
|
|
- let markerOffset;
|
|
|
- if (isStartEnd) {
|
|
|
- // 设置 24px 宽 30px 高,偏移量设置为底部中心对齐
|
|
|
- markerOffset = new this.AMap.Pixel(-12, -30);
|
|
|
- } else {
|
|
|
- markerOffset = new this.AMap.Pixel(-Math.round(outerSizeNumber / 2), -Math.round(outerSizeNumber / 2));
|
|
|
- }
|
|
|
+ // 生成标记内容
|
|
|
+ let markerContent = '';
|
|
|
+ if (isStartEnd) {
|
|
|
+ markerContent = `
|
|
|
+ <div class="pure-light-node start-end-node" style="width: ${markerStyle.size}; height: ${markerStyle.height}; background: transparent; border: none; display: flex; justify-content: center; align-items: flex-end; cursor: pointer; transform-origin: bottom center;">
|
|
|
+ <img src="${require(`@/assets/map/${type}.png`)}" style="width: 100%; height: auto; object-fit: contain; pointer-events: none;" />
|
|
|
+ </div>
|
|
|
+ `;
|
|
|
+ } else if (isAbnormal) {
|
|
|
+ const iconName = config.name === '离线' ? 'lixian' : config.name === '降级' ? 'jiangji' : 'guzhang';
|
|
|
+ markerContent = `
|
|
|
+ <div class="pure-light-node breathe" style="width: ${markerStyle.size}; height: ${markerStyle.height}; background: transparent; border: none; box-sizing: content-box; display: flex; justify-content: center; align-items: center; cursor: pointer; padding: 0;">
|
|
|
+ <img src="${require(`@/assets/images/icon_${iconName}.png`)}" style="width: 100%; height: 100%; object-fit: contain;" />
|
|
|
+ </div>
|
|
|
+ `;
|
|
|
+ } else {
|
|
|
+ markerContent = `
|
|
|
+ <div class="pure-light-node ${isRoute ? 'route-node' : ''}" style="width: ${markerStyle.size}; height: ${markerStyle.height}; background: ${config.color || '#999'}; box-shadow: ${isRoute ? 'none' : `0 0 8px ${config.color}`}; border: ${markerStyle.border}; box-sizing: content-box; display: flex; justify-content: center; align-items: center; color: #fff; border-radius: 50%; cursor: pointer; padding: 2px;">
|
|
|
+ <span style="transform: scale(0.8); font-weight: bold; font-size: 12px;">${displayText}</span>
|
|
|
+ </div>
|
|
|
+ `;
|
|
|
+ }
|
|
|
|
|
|
- const marker = new this.AMap.Marker({
|
|
|
- position: [lng, lat],
|
|
|
- zIndex: isStartEnd ? 120 : (isAbnormal ? 110 : 100),
|
|
|
- content: markerContent,
|
|
|
- offset: markerOffset,
|
|
|
- extData: {
|
|
|
+ const marker = new this.AMap.Marker({
|
|
|
+ position: [lng, lat],
|
|
|
+ zIndex: markerStyle.zIndex,
|
|
|
+ content: markerContent,
|
|
|
+ offset: new this.AMap.Pixel(...markerStyle.offset),
|
|
|
+ extData: {
|
|
|
...config,
|
|
|
position: [lng, lat],
|
|
|
statusColor: config.color || '#999',
|
|
|
@@ -827,7 +892,8 @@ export default {
|
|
|
if (!this.infoWindow) {
|
|
|
this.infoWindow = new this.AMap.InfoWindow({
|
|
|
isCustom: true,
|
|
|
- offset: new this.AMap.Pixel(0, -20)
|
|
|
+ offset: new this.AMap.Pixel(0, -20),
|
|
|
+ autoMove: false // 防止弹窗自动平移导致的中心点偏移
|
|
|
});
|
|
|
}
|
|
|
|
|
|
@@ -934,27 +1000,55 @@ export default {
|
|
|
},
|
|
|
|
|
|
focusByLocation(targetPos) {
|
|
|
- if (!this.isMapReady() || !targetPos || targetPos.length !== 2) return;
|
|
|
+ if (!this.isMapReady() || !targetPos) return;
|
|
|
|
|
|
- let foundMarker = null;
|
|
|
+ // 如果是字符串坐标 "lng,lat",则解析
|
|
|
+ let pos = targetPos;
|
|
|
+ if (typeof targetPos === 'string') {
|
|
|
+ pos = targetPos.split(',').map(Number);
|
|
|
+ }
|
|
|
+ if (!Array.isArray(pos) || pos.length < 2) return;
|
|
|
+
|
|
|
+ const [targetLng, targetLat] = pos;
|
|
|
+ let bestMarker = null;
|
|
|
+ let minDistanceSq = Infinity;
|
|
|
+
|
|
|
+ // 遍历所有路由组,寻找离坐标点最近的标记
|
|
|
Object.values(this.routeGroups).forEach(group => {
|
|
|
- const marker = group.find(item => {
|
|
|
- if (!(item instanceof this.AMap.Marker)) return false;
|
|
|
- const pos = item.getExtData().position;
|
|
|
- return Math.abs(pos[0] - targetPos[0]) < 0.0001 && Math.abs(pos[1] - targetPos[1]) < 0.0001;
|
|
|
+ if (!Array.isArray(group)) return;
|
|
|
+ group.forEach(item => {
|
|
|
+ if (!(item instanceof this.AMap.Marker)) return;
|
|
|
+ const markerExt = item.getExtData();
|
|
|
+ const markerPos = markerExt.position;
|
|
|
+ if (!markerPos) return;
|
|
|
+
|
|
|
+ const dx = markerPos[0] - targetLng;
|
|
|
+ const dy = markerPos[1] - targetLat;
|
|
|
+ const distSq = dx * dx + dy * dy;
|
|
|
+
|
|
|
+ // 容差范围内(约 20 米),寻找最接近的点
|
|
|
+ if (distSq < 0.000001) {
|
|
|
+ // 优先规则:如果距离相同或非常接近,优先选择异常状态点(离线/降级/故障)
|
|
|
+ const isAbnormal = ["离线", "降级", "故障"].includes(markerExt.name);
|
|
|
+ const currentIsAbnormal = bestMarker ? ["离线", "降级", "故障"].includes(bestMarker.getExtData().name) : false;
|
|
|
+
|
|
|
+ if (distSq < minDistanceSq || (isAbnormal && !currentIsAbnormal)) {
|
|
|
+ minDistanceSq = distSq;
|
|
|
+ bestMarker = item;
|
|
|
+ }
|
|
|
+ }
|
|
|
});
|
|
|
- if (marker) foundMarker = marker;
|
|
|
});
|
|
|
|
|
|
- if (foundMarker) {
|
|
|
- const finalPos = foundMarker.getPosition();
|
|
|
+ if (bestMarker) {
|
|
|
+ const finalPos = bestMarker.getPosition();
|
|
|
this.map.setZoomAndCenter(17, finalPos, false, 500);
|
|
|
setTimeout(() => {
|
|
|
- if (!this.isComponentDestroyed) this.openLightInfo(foundMarker.getExtData(), finalPos);
|
|
|
+ if (!this.isComponentDestroyed) this.openLightInfo(bestMarker.getExtData(), finalPos);
|
|
|
}, 600);
|
|
|
} else {
|
|
|
// 如果找不到对应的标记,直接使用传入的坐标设置地图中心
|
|
|
- this.map.setZoomAndCenter(17, targetPos, false, 500);
|
|
|
+ this.map.setZoomAndCenter(17, [targetLng, targetLat], false, 500);
|
|
|
}
|
|
|
},
|
|
|
|