diff --git a/src/static/image/reset.png b/src/static/image/reset.png
new file mode 100644
index 0000000..6a73d83
Binary files /dev/null and b/src/static/image/reset.png differ
diff --git a/src/static/map.html b/src/static/map.html
index cfc140f..909744a 100644
--- a/src/static/map.html
+++ b/src/static/map.html
@@ -32,6 +32,8 @@
padding: 0;
overflow: hidden;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
+ /* 确保 body 背景色与地图底色一致,防止加载前的白屏 */
+ background-color: #F5F3F0;
}
#map-container {
@@ -386,6 +388,34 @@
transform: scale(0.95);
}
+ /* 重置地图按钮 */
+ .reset-map-btn {
+ position: fixed;
+ bottom: 70px; /* 定位在定位按钮上方:20px + 32px + 18px间距 */
+ right: 20px;
+ width: 32px;
+ height: 32px;
+ background: rgba(255, 255, 255, 0.95) url('./image/reset.png') center center / 70% no-repeat;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
+ z-index: 998;
+ pointer-events: auto;
+ transition: transform 0.2s, box-shadow 0.2s;
+ }
+
+ .reset-map-btn:hover {
+ transform: scale(1.1);
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);
+ }
+
+ .reset-map-btn:active {
+ transform: scale(0.95);
+ }
+
/* 地图 Label 样式 - 提取至 CSS */
.map-project-label {
color: #002db6 !important;
@@ -466,7 +496,6 @@
-
+
@@ -597,7 +626,7 @@
if (xhr.status >= 200 && xhr.status < 300) {
const res = Utils.safeParseJSON(encodeURIComponent(xhr.responseText));
resolve(res?.code >= 200 && res?.code < 300 ? (res.data || []) : []);
- } else {
+ } else {
resolve([]);
}
}
@@ -629,24 +658,28 @@
watchPositionId: null,
watchOrientationId: null,
watchLocationTimer: null,
- mapLoaded: false
+ mapLoaded: false,
+ // 新增:网络状态处理相关
+ networkDebounceTimer: null,
+ isNetworkRecovering: false
},
locationIconCache: null,
baiduGeolocation: null,
- lastHeading: null, // 记录上一次的方向,用于避免频繁更新图标
- updateMarkerTimer: null, // 防抖定时器
- headingHistory: [], // 方向历史记录,用于平滑滤波(移动平均)
- headingHistorySize: 5, // 历史记录大小(5个样本的移动平均)
- lastOrientationUpdateTime: 0, // 上次方向更新时间,用于节流
- headingOffset: 0, // 方向偏移量,用于校正方向偏差(度),如果需要反转方向可以设置为180
- lastLocationTime: 0, // 上次位置更新时间
- lastLocationPoint: null, // 上次位置点,用于计算移动速度
- isMoving: false, // 是否在移动状态
+ lastHeading: null,
+ updateMarkerTimer: null,
+ headingHistory: [],
+ headingHistorySize: 5,
+ lastOrientationUpdateTime: 0,
+ headingOffset: 0,
+ lastLocationTime: 0,
+ lastLocationPoint: null,
+ isMoving: false,
elements: {
panel: null,
tree: null,
closeBtn: null,
- projectListContainer: null
+ projectListContainer: null,
+ resetBtn: null
},
init() {
@@ -670,6 +703,9 @@
// 处理 URL Action
this.handleUrlActions(params);
+
+ // 初始化网络监听优化
+ this.setupNetworkListeners();
},
cacheElements() {
@@ -681,6 +717,7 @@
this.elements.deselectAllBtn = document.getElementById('deselect-all-btn');
this.elements.arBtn = document.getElementById('ar-btn');
this.elements.locationCenterBtn = document.getElementById('location-center-btn');
+ this.elements.resetBtn = document.getElementById('reset-map-btn');
},
bindEvents() {
@@ -708,6 +745,11 @@
this.elements.locationCenterBtn.addEventListener('click', () => this.centerToLocation());
}
+ // 重置地图按钮
+ if (this.elements.resetBtn) {
+ this.elements.resetBtn.addEventListener('click', () => this.resetMap());
+ }
+
// 页面卸载时清理定位监听
window.addEventListener('beforeunload', () => this.stopLocationTracking());
@@ -729,10 +771,66 @@
window.drawModel = this.drawModel.bind(this);
},
+ /**
+ * 优化后的网络状态监听:防抖处理,避免频繁闪烁
+ */
+ setupNetworkListeners() {
+ window.addEventListener('online', () => {
+ console.log('网络连接已恢复,等待稳定...');
+
+ // 清除之前的定时器,防止在不稳定时频繁触发
+ if (this.state.networkDebounceTimer) {
+ clearTimeout(this.state.networkDebounceTimer);
+ }
+
+ // 设置防抖,等待2秒钟确认网络稳定后再刷新
+ this.state.networkDebounceTimer = setTimeout(() => {
+ if (this.map && !this.state.isNetworkRecovering) {
+ console.log('网络稳定,执行地图刷新');
+ this.state.isNetworkRecovering = true;
+
+ // 记录当前状态
+ const zoom = this.map.getZoom();
+ const center = this.map.getCenter();
+
+ // 触发瓦片重载的轻微变动 (只变动一点点,减少视觉跳跃)
+ // 使用 panBy(0,0) 有时无法触发瓦片重载,setZoom 是最暴力的有效方法
+ // 这里使用极其微小的缩放变化,并快速还原
+ this.map.setZoom(zoom + 0.000001);
+
+ setTimeout(() => {
+ this.map.setZoom(zoom);
+ this.state.isNetworkRecovering = false;
+ }, 200);
+ }
+ }, 2000); // 2秒防抖时间
+ });
+
+ window.addEventListener('offline', () => {
+ console.log('网络已断开');
+ // 如果在等待刷新期间断网,取消刷新
+ if (this.state.networkDebounceTimer) {
+ clearTimeout(this.state.networkDebounceTimer);
+ this.state.networkDebounceTimer = null;
+ }
+ });
+ },
+
+ /**
+ * 重置地图(解决网络差导致的黑屏/闪烁问题)
+ * 强制刷新当前页面
+ */
+ resetMap() {
+ console.log('用户请求重置地图...');
+ // 在 WebGL 地图环境(特别是内嵌 WebView),如果因网络问题导致 Context 丢失或瓦片加载卡死,
+ // 简单的 destroy() 可能不够彻底,直接 location.reload() 是最稳妥的修复方式。
+ window.location.reload();
+ },
+
initMap(projectInfo) {
this.map = new BMapGL.Map('map-container', {
enableHighResolution: true, // 开启高清适配
- backgroundColor: [245, 243, 240, 255] // [R, G, B, A] 显式设置 GL 引擎的清屏颜色
+ backgroundColor: [245, 243, 240, 255] // [R, G, B, A] 显式设置 GL 引擎的清屏颜色,与CSS一致
})
const center = projectInfo[0] ? new BMapGL.Point(projectInfo[0].lng, projectInfo[0].lat) : new BMapGL.Point(116.404, 39.915);
this.map.centerAndZoom(center, 12);
@@ -759,22 +857,15 @@
this.initLocationTracking();
}
});
-
- // 网络状态监听(如果运行在 UniApp 环境中)
- window.addEventListener('online', () => {
- if (App.map) {
- // 网络恢复时,稍微缩放一下地图,触发瓦片重新加载,避免卡死在白屏状态
- const zoom = App.map.getZoom();
- App.map.setZoom(zoom + 0.01);
- setTimeout(() => App.map.setZoom(zoom), 100);
- }
- });
+
+ // 注意:原有的 window.addEventListener('online') 已移至 setupNetworkListeners 统一管理
},
/**
* 初始化定位跟踪
*/
initLocationTracking() {
+ // ... (保持原有代码不变)
// 等待地图加载完成
if (!this.map || !this.state.mapLoaded) {
console.log('等待地图加载完成...');
@@ -793,7 +884,7 @@
// 开始监听设备方向变化(使用设备方向传感器)
this.startWatchingOrientation();
},
-
+
/**
* 创建定位标记图标(使用 real_postion.png)
*/
@@ -1071,239 +1162,6 @@
});
},
- /**
- * 使用uni-app GPS定位(WGS84坐标,最准确)
- */
- startUniAppGPSGeolocation() {
- console.log('使用uni-app GPS定位(WGS84)');
- // 先获取一次当前位置
- uni.getLocation({
- type: 'wgs84', // 使用WGS84坐标系(GPS原始坐标),然后转换为BD09
- altitude: false,
- geocode: false,
- highAccuracyExpireTime: 10000, // 增加等待时间,获取GPS定位
- success: (res) => {
- console.log('uni-app GPS定位成功:', res.longitude, res.latitude, '精度:', res.accuracy);
- // uni-app返回的是WGS84坐标,需要转换为BD09
- this.wgs84ToBd09(res.longitude, res.latitude, (bd09) => {
- if (bd09 && bd09.lng && bd09.lat) {
- this.state.currentLocation = {
- lat: bd09.lat,
- lng: bd09.lng,
- accuracy: res.accuracy || 0
- };
- console.log('GPS坐标转换完成 - BD09:', bd09.lng, bd09.lat);
- this.updateLocationMarker();
- } else {
- console.error('GPS坐标转换失败,使用GCJ02定位');
- this.startUniAppGeolocation();
- }
- });
- },
- fail: (err) => {
- console.error('uni-app GPS定位失败:', err);
- // 降级到GCJ02定位
- this.startUniAppGeolocation();
- }
- });
-
- // 持续监听位置变化(改为每5秒更新一次)
- this.state.watchLocationTimer = setInterval(() => {
- uni.getLocation({
- type: 'wgs84',
- altitude: false,
- geocode: false,
- highAccuracyExpireTime: 10000,
- success: (res) => {
- this.wgs84ToBd09(res.longitude, res.latitude, (bd09) => {
- if (bd09 && bd09.lng && bd09.lat) {
- // 只更新精度更好的位置
- if (!this.state.currentLocation || res.accuracy < this.state.currentLocation.accuracy || res.accuracy < 20) {
- this.state.currentLocation = {
- lat: bd09.lat,
- lng: bd09.lng,
- accuracy: res.accuracy || 0
- };
- this.updateLocationMarker();
- }
- }
- });
- },
- fail: (err) => {
- console.error('uni-app GPS定位更新失败:', err);
- }
- });
- }, 3000); // 每3秒更新一次
- },
-
- /**
- * 使用uni-app定位(GCJ02坐标)
- */
- startUniAppGeolocation() {
- // 先获取一次当前位置
- uni.getLocation({
- type: 'gcj02', // 使用GCJ02坐标系(火星坐标),然后转换为BD09
- altitude: false,
- geocode: false,
- highAccuracyExpireTime: 4000,
- success: (res) => {
- console.log('uni-app定位成功:', res.longitude, res.latitude, '精度:', res.accuracy);
- // uni-app返回的是GCJ02坐标,需要转换为BD09
- this.gcj02ToBd09(res.longitude, res.latitude, (bd09) => {
- if (bd09 && bd09.lng && bd09.lat) {
- this.state.currentLocation = {
- lat: bd09.lat,
- lng: bd09.lng,
- accuracy: res.accuracy || 0
- };
- console.log('坐标转换完成 - BD09:', bd09.lng, bd09.lat);
- this.updateLocationMarker();
- } else {
- console.error('坐标转换失败,使用备用方案');
- // 如果转换失败,尝试使用百度地图定位
- if (typeof BMapGL !== 'undefined' && BMapGL.Geolocation) {
- this.startBaiduGeolocation();
- } else {
- this.startBrowserGeolocation();
- }
- }
- });
- },
- fail: (err) => {
- console.error('uni-app定位失败:', err);
- // 降级到百度地图定位或浏览器定位
- if (typeof BMapGL !== 'undefined' && BMapGL.Geolocation) {
- this.startBaiduGeolocation();
- } else {
- this.startBrowserGeolocation();
- }
- }
- });
-
- // 持续监听位置变化(改为每5秒更新一次)
- this.state.watchLocationTimer = setInterval(() => {
- uni.getLocation({
- type: 'gcj02',
- altitude: false,
- geocode: false,
- highAccuracyExpireTime: 4000,
- success: (res) => {
- this.gcj02ToBd09(res.longitude, res.latitude, (bd09) => {
- if (bd09 && bd09.lng && bd09.lat) {
- // 只更新精度更好的位置
- if (!this.state.currentLocation || res.accuracy < this.state.currentLocation.accuracy || res.accuracy < 50) {
- this.state.currentLocation = {
- lat: bd09.lat,
- lng: bd09.lng,
- accuracy: res.accuracy || 0
- };
- this.updateLocationMarker();
- }
- }
- });
- },
- fail: (err) => {
- console.error('uni-app定位更新失败:', err);
- }
- });
- }, 3000); // 每3秒更新一次位置
- },
-
- /**
- * 使用5+定位
- */
- startPlusGeolocation() {
- const geolocation = plus.geolocation;
- const options = {
- enableHighAccuracy: true,
- timeout: 10000,
- maximumAge: 0
- };
-
- // 获取当前位置
- geolocation.getCurrentPosition(
- (position) => {
- console.log('5+定位成功:', position);
- // 5+返回的是WGS84坐标,需要转换为BD09
- this.wgs84ToBd09(position.coords.longitude, position.coords.latitude, (bd09) => {
- this.state.currentLocation = {
- lat: bd09.lat,
- lng: bd09.lng,
- accuracy: position.coords.accuracy || 0
- };
- this.updateLocationMarker();
- });
- },
- (error) => {
- console.error('5+定位失败:', error);
- this.startBrowserGeolocation();
- },
- options
- );
-
- // 持续监听位置变化
- this.state.watchPositionId = geolocation.watchPosition(
- (position) => {
- this.wgs84ToBd09(position.coords.longitude, position.coords.latitude, (bd09) => {
- this.state.currentLocation = {
- lat: bd09.lat,
- lng: bd09.lng,
- accuracy: position.coords.accuracy || 0
- };
- this.updateLocationMarker();
- });
- },
- (error) => {
- console.error('5+定位监听错误:', error);
- },
- options
- );
- },
-
- /**
- * 使用浏览器定位
- */
- startBrowserGeolocation() {
- if (!navigator.geolocation) {
- console.error('浏览器不支持定位');
- return;
- }
-
- const options = {
- enableHighAccuracy: true, // 高精度定位(使用GPS)
- timeout: 20000, // 增加超时时间到20秒,确保获取高精度定位
- maximumAge: 0 // 不使用缓存,始终获取最新位置
- };
-
- console.log('开始浏览器定位,选项:', options);
-
- // 先获取一次当前位置
- navigator.geolocation.getCurrentPosition(
- (position) => {
- console.log('获取到初始位置:', position.coords);
- this.updateLocation(position);
- },
- (error) => {
- console.error('获取初始位置失败:', error);
- this.handleLocationError(error);
- },
- options
- );
-
- // 持续监听位置变化
- this.state.watchPositionId = navigator.geolocation.watchPosition(
- (position) => {
- console.log('位置更新:', position.coords);
- this.updateLocation(position);
- },
- (error) => {
- console.error('位置监听错误:', error);
- this.handleLocationError(error);
- },
- options
- );
- },
-
/**
* GCJ02坐标转BD09坐标(百度坐标)
*/
@@ -1554,14 +1412,14 @@
updateLocationMarker() {
// 确保地图已加载完成
if (!this.map || !this.state.mapLoaded) {
- return;
- }
-
+ return;
+ }
+
// 如果没有位置信息,静默返回
if (!this.state.currentLocation) {
- return;
- }
-
+ return;
+ }
+
// 如果已经有待处理的更新,取消它,重新调度(确保使用最新数据)
if (this.updateMarkerTimer) {
cancelAnimationFrame(this.updateMarkerTimer);
@@ -1825,7 +1683,7 @@
const targetHistorySize = this.isMoving ? 15 : 7;
if (!this.headingHistorySize) {
this.headingHistorySize = targetHistorySize;
- } else {
+ } else {
// 动态调整历史记录大小
this.headingHistorySize = targetHistorySize;
}
@@ -2138,6 +1996,12 @@
clearInterval(this.state.watchLocationTimer);
this.state.watchLocationTimer = null;
}
+
+ // 清理网络防抖定时器
+ if (this.state.networkDebounceTimer) {
+ clearTimeout(this.state.networkDebounceTimer);
+ this.state.networkDebounceTimer = null;
+ }
// 清理更新标记的定时器
if (this.updateMarkerTimer !== null) {
@@ -2230,7 +2094,7 @@
this.elements.projectListContainer.innerHTML = '
暂无工程数据
';
return;
}
-
+
const fragment = document.createDocumentFragment();
projectInfo.forEach((project, index) => {
const projectId = project.id || `project-${index}`;
@@ -2513,13 +2377,13 @@
},
renderTreeNode(node, container, level) {
- const nodeDiv = document.createElement('div');
- nodeDiv.className = 'tree-node';
+ const nodeDiv = document.createElement('div');
+ nodeDiv.className = 'tree-node';
nodeDiv.style.paddingLeft = (level * 20) + 'px';
-
- const itemDiv = document.createElement('div');
- itemDiv.className = 'tree-node-item';
-
+
+ const itemDiv = document.createElement('div');
+ itemDiv.className = 'tree-node-item';
+
const hasChildren = node.children && node.children.length > 0;
// 展开图标
@@ -2546,21 +2410,21 @@
}
// 复选框
- const checkbox = document.createElement('input');
- checkbox.type = 'checkbox';
- checkbox.className = 'tree-node-checkbox';
+ const checkbox = document.createElement('input');
+ checkbox.type = 'checkbox';
+ checkbox.className = 'tree-node-checkbox';
checkbox.dataset.id = node.id; // 添加data-id用于全选/全不选时更新UI
checkbox.checked = this.state.checkedNodeIds.includes(node.id);
checkbox.addEventListener('change', (e) => this.handleCheck(node, e.target.checked));
itemDiv.appendChild(checkbox);
-
+
// 文本
- const label = document.createElement('span');
- label.className = 'tree-node-label';
+ const label = document.createElement('span');
+ label.className = 'tree-node-label';
label.textContent = node.nodeName || node.name || `Node ${node.id}`;
- itemDiv.appendChild(label);
+ itemDiv.appendChild(label);
- nodeDiv.appendChild(itemDiv);
+ nodeDiv.appendChild(itemDiv);
// 子容器
if (hasChildren) {