// 地图页面 - 高德地图SDK集成版本 const app = getApp(); const apiClient = require('../../utils/api-client.js'); const { MapUtils, MAP_CONFIG } = require('../../utils/map-config.js'); const { systemInfoHelper } = require('../../utils/system-info-helper.js'); const config = require('../../config/config.js'); const authManager = require('../../utils/auth.js'); // 使用完整版本的高德地图SDK,包含逆地理编码功能 const { AMapWX } = require('../../libs/amap-wx.js'); const { MAP_FEED_MARKER_STYLE, MAP_FEED_DEBOUNCE, MAP_FEED_BUBBLE_LIMIT, getFeedScalePreset, buildBoundsPayload, resolveFeedThumbnail, buildCacheKey, isCacheFresh } = require('../../utils/map-feed-helpers.js'); // 导入合作商家数据模块 const { getAllMerchants, convertMerchantsToMarkers } = require('../../utils/merchant-data.js'); const wsManager = require('../../utils/websocket-manager-v2.js'); const marker_blank = '/images/map/marker_blank.png'; const baseAmapUrl = config.api.baseAmapUrl; const shouldUseMockData = !!config?.mockData; const MAP_FEED_SINGLE_BASE_ID = 20000; const MAP_FEED_CLUSTER_BASE_ID = 30000; const MAP_FEED_ADMIN_BASE_ID = 40000; const MAP_FEED_CLUSTER_STRANGER_ID = 50000; const MAP_FEED_MARKER_STRANGER_ID = 70000; const MAP_FEED_CLUSTER_FRIEND_ID = 60000; const MAP_FEED_MARKER_FRIEND_ID = 80000; function formatFeedCountLabel(count) { const safeCount = Number(count) || 0; if (safeCount <= 0) { return '暂无内容'; } if (safeCount >= 1000) { return `${(safeCount / 1000).toFixed(1)}K 条内容`; } return `${safeCount} 条内容`; } function formatHeatLabel(score) { if (score === undefined || score === null) { return '热度更新中'; } const value = Number(score); if (Number.isNaN(value)) { return '热度更新中'; } const rounded = Math.round(value); if (rounded >= 95) { return `热度爆棚 · ${rounded}`; } if (rounded >= 80) { return `热度很高 · ${rounded}`; } if (rounded >= 60) { return `热度上升 · ${rounded}`; } return '新鲜出炉'; } function formatRadiusLabel(radius) { const safeRadius = Math.max(20, Math.round(Number(radius) || 0)); if (safeRadius >= 1000) { return `范围约 ${(safeRadius / 1000).toFixed(1)} 公里`; } return `范围约 ${safeRadius} 米`; } function buildBubbleData(payload = {}) { const radiusValue = payload.radius || 200; const radiusLabel = payload.radiusLabel || formatRadiusLabel(radiusValue); return { title: payload.title || '附近热点', subtitle: payload.subtitle || '', chip: payload.chip || '', heat: payload.heat || '', radiusLabel, createdAt: payload.createdAt, nickname: payload.nickname, content: payload.content, avatar: payload.avatar, thumbnail: payload.thumbnail || '', feeds: payload.feeds || [], location: payload.location || null, radius: radiusValue, type: payload.type || 'map-feed-cluster', tone: payload.tone || 'sunset' }; } // 格式化后的模拟响应数据 const mockResponseStrangerClusters = { "code": 200, "message": "success", "data": { "displayMode": "friends", "totalUsers": 25, "displayedUsers": 20, "onlineUsers": 15, "userPoints": [{ "customId": "81149107", "nickname": "用户7142", "avatar": "https://oss.faxianwo.me/findme/images/731497293417c888e34942c14dd6e6ce.jpg", "gender": 1, "age": 25, "latitude": 38.9042, "longitude": 113.4074, "address": "北京市朝阳区某某街道", "district": "朝阳区", "isOnline": true, "lastSeen": 1706518800, "stayTime": 3600, "isMember": true, "isFriend": true, "relation": "friend", "remark": "老同学", "privacyLevel": 0, "showPreciseLocation": true, "distance": 150.5, "distanceText": "150m", "isCluster": false, "clusterId": "" }], "clusters": [{ "clusterId": "cluster_001", "latitude": 39.9042, "longitude": 116.4074, "userCount": 5, "onlineCount": 3, "avatarPreview": [ "https://example.com/avatar1.jpg", "https://example.com/avatar2.jpg", "https://example.com/avatar3.jpg" ], "isFriendCluster": true, "canExpand": true, "radius": 100.0 }], "bounds": { "northEast": { "latitude": 39.9142, "longitude": 116.4174 }, "southWest": { "latitude": 39.8942, "longitude": 116.3974 } }, "zoomLevel": 15, "cacheHit": false, "requestId": "req_1706518800123_4567", "friendStats": { "totalFriends": 25, "onlineFriends": 15, "offlineFriends": 10, "nearbyFriends": 8, "relationStats": { "friend": 20, "colleague": 3, "family": 2 } } } } const mockResponseStranger = { "code": 200, "message": "success", "data": { "displayMode": "friends", "totalUsers": 25, "displayedUsers": 20, "onlineUsers": 15, "userPoints": [{ "customId": "81149107", "nickname": "用户7142", "avatar": "https://oss.faxianwo.me/findme/images/731497293417c888e34942c14dd6e6ce.jpg", "gender": 1, "age": 25, "latitude": 39.9042, "longitude": 116.4074, "address": "北京市朝阳区某某街道", "district": "朝阳区", "isOnline": true, "lastSeen": 1706518800, "stayTime": 3600, "isMember": true, "isFriend": true, "relation": "friend", "remark": "老同学", "privacyLevel": 0, "showPreciseLocation": true, "distance": 150.5, "distanceText": "150m", "isCluster": false, "clusterId": "" }], "clusters": null, "bounds": { "northEast": { "latitude": 39.9142, "longitude": 116.4174 }, "southWest": { "latitude": 39.8942, "longitude": 116.3974 } }, "zoomLevel": 15, "cacheHit": false, "requestId": "req_1706518800123_4567", "friendStats": { "totalFriends": 25, "onlineFriends": 15, "offlineFriends": 10, "nearbyFriends": 8, "relationStats": { "friend": 20, "colleague": 3, "family": 2 } } } } const mockResponseDynamic = { "code": 200, "message": "success", "data": { "displayMode": "friends", "totalUsers": 25, "displayedUsers": 20, "onlineUsers": 15, "points": [{ "feedUuid": "81149107", "nickname": "用户7142", "thumbnail": "https://oss.faxianwo.me/findme/images/731497293417c888e34942c14dd6e6ce.jpg", "gender": 1, "age": 25, "latitude": 39.9042, "longitude": 116.4074, "address": "北京市朝阳区某某街道", "district": "朝阳区", "isOnline": true, "lastSeen": 1706518800, "stayTime": 3600, "isMember": true, "isFriend": true, "relation": "friend", "remark": "老同学", "privacyLevel": 0, "showPreciseLocation": true, "distance": 150.5, "distanceText": "150m", "isCluster": false, "clusterId": "" }], "clusters": null, "bounds": { "northEast": { "latitude": 39.9142, "longitude": 116.4174 }, "southWest": { "latitude": 39.8942, "longitude": 116.3974 } }, "zoomLevel": 15, "cacheHit": false, "requestId": "req_1706518800123_4567", "friendStats": { "totalFriends": 25, "onlineFriends": 15, "offlineFriends": 10, "nearbyFriends": 8, "relationStats": { "friend": 20, "colleague": 3, "family": 2 } } } } const mockResponseFriendMarkerCluster = { "code": 200, "message": "success", "data": { "displayMode": "friends", "totalUsers": 25, "displayedUsers": 20, "onlineUsers": 15, "userPoints": [{ "customId": "39361203", "nickname": "用户4023", "avatar": "", "gender": 1, "age": 25, "latitude": 39.9742, "longitude": 116.9074, "address": "北京市朝阳区某某街道", "district": "朝阳区", "isOnline": true, "lastSeen": 1706518800, "stayTime": 3600, "isMember": true, "isFriend": true, "relation": "friend", "remark": "老同学", "privacyLevel": 0, "showPreciseLocation": true, "distance": 150.5, "distanceText": "150m", "isCluster": false, "clusterId": "" }], "bounds": { "northEast": { "latitude": 39.9142, "longitude": 116.4174 }, "southWest": { "latitude": 39.8942, "longitude": 116.3974 } }, "clusters": [{ "clusterId": "cluster_001", "latitude": 40.9042, "longitude": 116.4074, "userCount": 5, "onlineCount": 3, "avatarPreview": [ "https://example.com/avatar1.jpg", "https://example.com/avatar2.jpg", "https://example.com/avatar3.jpg" ], "isFriendCluster": true, "canExpand": true, "radius": 100.0 }], "zoomLevel": 15, "cacheHit": false, "requestId": "req_1706518800123_4567", "friendStats": { "totalFriends": 25, "onlineFriends": 15, "offlineFriends": 10, "nearbyFriends": 8, "relationStats": { "friend": 20, "colleague": 3, "family": 2 } } } } const mockResponseFriend = { "code": 200, "message": "success", "data": { "displayMode": "friends", "totalUsers": 25, "displayedUsers": 20, "onlineUsers": 15, "userPoints": [{ "customId": "81149107", "nickname": "用户7142", "avatar": "", "gender": 1, "age": 25, "latitude": 40.9042, "longitude": 116.4074, "address": "北京市朝阳区某某街道", "district": "朝阳区", "isOnline": true, "lastSeen": 1706518800, "stayTime": 3600, "isMember": true, "isFriend": true, "relation": "friend", "remark": "老同学", "privacyLevel": 0, "showPreciseLocation": true, "distance": 150.5, "distanceText": "150m", "isCluster": false, "clusterId": "" }], "clusters": null, "bounds": { "northEast": { "latitude": 39.9142, "longitude": 116.4174 }, "southWest": { "latitude": 39.8942, "longitude": 116.3974 } }, "zoomLevel": 15, "cacheHit": false, "requestId": "req_1706518800123_4567", "friendStats": { "totalFriends": 25, "onlineFriends": 15, "offlineFriends": 10, "nearbyFriends": 8, "relationStats": { "friend": 20, "colleague": 3, "family": 2 } } } } Page({ data: { showCameraAction: false, // 拍照弹窗 isLoggedIn: app.globalData.isLoggedIn, // 登录状态 isDarkMode:false, amapFun: null, poiAroundList: [], showPoiAroundModal: false, // 地图配置 longitude: 0, latitude: 0, currentMapScale: 12, // 初始缩放级别,低于自动切换阈值 //商家信息 merchantMarkers: [], merchantList: [], // 商家列表数据 merchantInfoModalVisible: false, selectedMerchant: null, activeMerchantId: null, // 当前激活的商户ID // 静态数据 staticDataMarkers: [], // 存储静态数据标记 // 位置信息 currentAddress: '获取位置中...', addressDetail: null, accuracy: 0, currentDistrict: '新市区', // 设置默认地点名称 currentCity: '乌鲁木齐', // 设置默认城市名称 weatherInfo: null, // SDK状态 amapReady: false, amapError: null, // 好友数据 friendsData: [], // 用户信息 userInfo: null, userAvatar: '', avatar: '', avatarUrl: '', showUserLocationIndicator: false, showMyAvatarMarker: true, myLocation: null, // 保存自己的位置信息 // 地图样式控制 enableSatellite: false, currentMapType: 'standard', // standard, satellite, traffic, transit shouldFollowLocation: true, // 控制是否跟随用户位置移动 autoSatelliteZoom: 18, // 超过此缩放级别自动切换卫星图 maxScale: 22, // 地图最大缩放级别,设置为22以提高卫星图清晰度 mapTheme: 'light', // 地图主题:light 或 dark // 系统适配 statusBarHeight: 44, menuButtonHeight: 32, navBarHeight: 88, safeAreaBottom: 0, // UI状态 selectedUser: null, currentFilter: 'friends', friendsCount: 0, strangersCount: 0, showFilterModal: false, // 🔥 浮动UI状态 isOnline: true, friendRequestCount: 0, unreadMessageCount: 0, nearbyCount: 0, // 地址信息 weather: null, // 默认位置(如果定位失败) defaultLocation: { latitude: 39.908692, longitude: 116.397979, address: '北京市' }, // 地图动态相关 locationDynamics: [], // 存储地图上的所有动态 showDynamicInputModal: false, // 显示动态输入弹窗 currentDynamicLocation: null, // 当前要发布动态的位置 dynamicContent: '', // 动态内容 selectedDynamic: null, // 当前选中的动态 showDynamicDetailModal: false, // 显示动态详情弹窗 mapFeedMode: 'nearby', mapFeedLevel: 'street', mapFeedLoading: false, mapFeedError: '', mapFeedMarkers: [], mapFeedPreviewList: [], activeFeedBubble: null, activeFeedBubbleLoading: false, hasUserInteractedWithMap: false, poiDetail: { address: '', // 预先定义 address 属性 name: '' }, poiDetailShow: false, showPoiRemindModal: false, poiRemindRadius: 300, // 默认300米 poiRemindCircle: null, poiRemindMode: ['arrive'], // 多选,初始为到达 showFriendSelectModal: false, lastPoiForRemind: null, allFriendsList: [], // 新增:用于好友选择弹窗的模拟数据 // 新增 friendSearchText: '', filteredFriendsList: [], showLocationMarkModal: false, locationMarkSearchText: '', locationMarkList: [], filteredLocationMarkList: [], // 搜索相关状态 showSearchBox: false, searchKeyword: '', searchResults: [], // 存储搜索结果 searchResultInfoModalVisible: false, placeMarkId: '', // 更多菜单相关状态 showMoreMenu: false, // 地图设置相关状态 showMapSettings: false, isClosingMapSettings: false, mapStyle:null, // POI提醒相关状态 poiRemindSelectedFriends: [], // 存储已选择的好友信息 // 地点收藏栏相关状态 showLocationFavoriteBar: false, // 控制收藏栏显示 locationFavoriteTab: 'favorite', // 当前选中的标签:nearby或favorite locationFavorites: [], // 所有收藏地点 filteredLocationFavorites: [], // 过滤后的收藏地点 locationFavoriteSearch: '', // 搜索关键词 showTraffic: false, // 默认不显示交通拥堵 merchantName: "", merchant: "", merchantAddress: "", shareModalVisible: true, shareShareVisible: false, searchResultNavigationVisible: false, selectResult: null, addressIsFavorited: false, longitude: 116.404, // 初始经度(示例:北京) latitude: 39.915, // 初始纬度 metric: "0米", // 米/千米 imperial: "0英尺", // 英尺 allMarkers: [], userMarker: null, renderKey: null, poiDetailAddress: '详细地址', busStops: [], // 公交站点标记 busLines: [], // 公交线路 isBusLayerVisible: false, // 公交图层显示状态 busDataLoading: false, // 公交数据加载状态 lastBusRegion: null, // 上一次加载公交数据的区域 busDataExpireTime: 300000, // 公交数据缓存过期时间(5分钟) busDataCache: new Map(), mapClickCollect: '收藏', // 数据存储 busLines: [], subwayLines: [], markers: [], polylines: [], // 存储当前设备方向角度(0-360°) // 标记是否支持设备方向 isSupport: false, // 标记是否已获取权限 hasPermission: false, mapClickCollect: '收藏', isMapClick: false, markers: [], activeFeedBubbleSingle: null, clusterMarkers: [], // 新增:存储聚合点 marker polygons: [], // 存储多边形覆盖物 clusterThreshold: 1000, //陌生人和好友聚合的临界值单位米 activeFeedSingleVisible: false, cacheEntryString: '', directonAngle: -1, mapFeedVisible: false, startY: 0, modalTranslateY: 0, isMoving: false, shareOptions: [{ type: "wechat", icon: "/images/map/Wechat_logo.png", name: "微信" }, { type: "douyin", icon: "/images/map/Tiktok_logo.png", name: "抖音" }, { type: "weibo", icon: "/images/map/Microblog_logo.png", name: "微博" }, { type: "email", icon: "/images/map/Email_logo.png", name: "邮箱" }, { type: "qq", icon: "/images/map/QQ_logo.png", name: "QQ" }, { type: "copy", icon: "/images/map/Copy_logo.png", name: "复制" }, ], }, // 显示地点收藏栏 onOpenLocationFavoriteBar() { this.initLocationFavorites(); this.setData({ showLocationFavoriteBar: true, locationFavoriteTab: 'favorite', locationFavoriteSearch: '' }); }, // 关闭地点收藏栏 onCloseLocationFavoriteBar() { this.setData({ showLocationFavoriteBar: false }); }, // 初始化收藏地点数据 initLocationFavorites() { // 创建一些模拟数据 if (!shouldUseMockData) { this.setData({ locationFavorites: [], filteredLocationFavorites: [] }); return; } }, // 切换标签 onChangeLocationFavoriteTab(e) { const tab = e.currentTarget.dataset.tab; this.setData({ locationFavoriteTab: tab, locationFavoriteSearch: '' }); // 根据标签切换显示不同的内容 if (tab === 'nearby') { // 这里可以添加获取附近地点的逻辑 // 暂时使用收藏地点数据 this.setData({ filteredLocationFavorites: this.data.locationFavorites }); } else if (tab === 'favorite') { this.setData({ filteredLocationFavorites: this.data.locationFavorites.filter(item => item.isFavorited) }); } }, previewImageAvatar(e) { try { // 获取当前点击的图片索引和动态索引 const avatar = e.currentTarget.dataset.avatar; // 🔥 设置标志,防止预览关闭后触发页面刷新 this._skipNextOnShowReload = true; const imageUrls=[avatar]; // 调用微信小程序的图片预览API wx.previewImage({ current: avatar, urls: imageUrls, success: () => { // 预览打开成功,标志已设置,关闭时会触发 onShow }, fail: (err) => { console.error('图片预览失败:', err); // 预览失败,清除标志 this._skipNextOnShowReload = false; wx.showToast({ title: '预览图片失败', icon: 'none' }); } }); } catch (error) { console.error('图片预览过程中出错:', error); // 出错时清除标志 this._skipNextOnShowReload = false; wx.showToast({ title: '预览图片失败', icon: 'none' }); } }, // 地点收藏搜索 onLocationFavoriteSearch(e) { const searchText = e.detail.value; this.setData({ locationFavoriteSearch: searchText }); // 根据搜索关键词筛选地点 let filtered = []; if (this.data.locationFavoriteTab === 'nearby') { filtered = this.data.locationFavorites.filter(item => item.name.includes(searchText) || item.address.includes(searchText) ); } else { filtered = this.data.locationFavorites.filter(item => item.isFavorited && (item.name.includes(searchText) || item.address.includes(searchText)) ); } this.setData({ filteredLocationFavorites: filtered }); }, // 清除地点收藏搜索 onClearLocationFavoriteSearch() { this.setData({ locationFavoriteSearch: '', filteredLocationFavorites: this.data.locationFavoriteTab === 'nearby' ? this.data.locationFavorites : this.data.locationFavorites.filter(item => item.isFavorited) }); }, // 切换地点收藏状态 onToggleLocationFavorite(e) { const id = e.currentTarget.dataset.id; const locationFavorites = this.data.locationFavorites.map(item => { if (item.id === id) { item.isFavorited = !item.isFavorited; } return item; }); // 根据当前标签和搜索词更新过滤后的列表 let filtered = []; if (this.data.locationFavoriteTab === 'nearby') { filtered = locationFavorites.filter(item => item.name.includes(this.data.locationFavoriteSearch) || item.address.includes(this.data.locationFavoriteSearch) ); } else { filtered = locationFavorites.filter(item => item.isFavorited && (item.name.includes(this.data.locationFavoriteSearch) || item.address.includes(this.data.locationFavoriteSearch)) ); } this.setData({ locationFavorites: locationFavorites, filteredLocationFavorites: filtered }); }, // 显示筛选选项弹窗 showFilterOptions() { this.setData({ showFilterModal: true }); }, // 隐藏筛选选项弹窗 hideFilterOptions() { this.setData({ showFilterModal: false }); }, initMapFeedState() { if (this.mapFeedDebounceTimer) { clearTimeout(this.mapFeedDebounceTimer); } this.mapFeedDebounceTimer = null; this.mapFeedCache = new Map(); this.mapFeedMarkerLookup = new Map(); this.mapStrangerMarkerLookup = new Map(); this.setData({ mapFeedMarkers: [], mapFeedPreviewList: [], activeFeedBubble: null, mapFeedMode: 'nearby', mapFeedLevel: 'street', mapFeedError: '', mapFeedLoading: false }); }, // 显示拍照功能弹窗 showCameraActionSheet() { this.setData({ showCameraAction: true }); }, // 隐藏拍照功能弹窗 hideCameraActionSheet() { this.setData({ showCameraAction: false }); }, // 拍摄照片 takePhoto() { this.hideCameraActionSheet(); // 检查登录状态 // 未登录进入登录页面 if (!authManager.isLoggedIn()) { wx.navigateTo({ url: '/pages/login/login' }); } else { wx.navigateTo({ url: '/subpackages/media/camera/camera' // 已登录用户跳转到拍摄页面 }); } }, // 从相册选择 chooseImage() { this.hideCameraActionSheet(); // 检查登录状态 // 未登录进入登录页面 if (!authManager.isLoggedIn()) { wx.navigateTo({ url: '/pages/login/login' }); } else { wx.chooseImage({ count: 1, sizeType: ['original', 'compressed'], sourceType: ['album'], success: (res) => { const tempFilePath = res.tempFilePaths[0]; // 显示加载提示 wx.showLoading({ title: '上传中...', mask: true }); // 处理选择的图片 this.uploadImage(tempFilePath) .then(imageUrl => { wx.hideLoading(); wx.navigateTo({ url: `/pages/edits/edits?imagePath=${encodeURIComponent(imageUrl)}`, success: () => { wx.showToast({ title: '上传成功', icon: 'success' }); }, fail: (err) => { console.error('跳转发布页失败:', err); wx.showToast({ title: '跳转失败,请重试', icon: 'none' }); } }); }) .catch(error => { wx.hideLoading(); console.error('上传过程出错:', error); wx.showToast({ title: error.message || '上传失败,请重试', icon: 'none' }); }); }, fail: (error) => { console.error('选择图片失败:', error); wx.showToast({ title: '选择图片失败', icon: 'none' }); } }); } }, // 上传图片 uploadImage(tempFilePath) { return new Promise((resolve, reject) => { try { wx.uploadFile({ url: `${config.api.baseUrl}/api/v1/file/upload`, filePath: tempFilePath, name: 'file', formData: { file_type: 'image', usage_type: 'feed' }, header: { 'Authorization': `Bearer ${apiClient.getToken()}` }, success: (uploadRes) => { // 🔥 统一处理401 - 使用apiClient的统一处理 if (apiClient.is401Error(uploadRes)) { const app = getApp(); const isLoggedIn = app?.globalData?.isLoggedIn || false; apiClient.handle401Error(isLoggedIn); // 对已登录用户抛出错误 if (isLoggedIn) { reject(new Error('登录已过期,请重新登录')); } else { // 对未登录用户返回空数据 resolve(null); } return; } // 解析响应数据 let result; try { result = JSON.parse(uploadRes.data); } catch (e) { console.error('解析响应JSON失败,响应数据:', uploadRes.data); reject(new Error('解析服务器响应失败')); return; } // 上传成功 if (uploadRes.statusCode === 200 && result.code === 0) { if (result.data && result.data.file_url) { resolve(result.data.file_url); } else { reject(new Error('服务器未返回图片URL')); } } else { reject(new Error(result.msg || '上传失败')); } }, fail: (error) => { console.error('上传请求失败:', error); let errorMsg = '网络请求失败,请检查网络'; if (error.errMsg) { errorMsg = error.errMsg; } reject(new Error(errorMsg)); } }); } catch (error) { console.error('上传图片到服务器失败:', error); reject(error); } }); }, // 显示筛选选项弹窗 showFilterOptions() { this.setData({ showFilterModal: true }); }, // 隐藏筛选选项弹窗 hideFilterOptions() { this.setData({ showFilterModal: false }); }, onLoad() { console.log("on-->" + "onLoad"); // 初始化模拟天气数据,确保天气信息始终显示 if (shouldUseMockData) { const mockWeatherInfo = { temperature: 25, weather: '晴', icon: '☀️', isCloudIcon: false, humidity: 45, windSpeed: 3.2, windDir: '东南风', aqi: 85, city: this.data.currentCity || '乌鲁木齐', updateTime: new Date().toLocaleTimeString('zh-CN') }; this.setData({ weatherInfo: mockWeatherInfo }); console.debug('✅ 已设置模拟天气数据:', mockWeatherInfo); } else { this.setData({ weatherInfo: null }); } this.amapFun = new AMapWX({ key: config.amapKey }); // 使用已经创建的系统信息辅助实例 this.systemInfoHelper = systemInfoHelper; // 获取系统信息和胶囊按钮位置 const systemInfo = this.systemInfoHelper.getSystemInfoSync(); const menuButtonInfo = this.systemInfoHelper.getMenuButtonBoundingClientRect(); // 计算导航栏高度和胶囊位置信息 const navBarHeight = this.systemInfoHelper.getNavBarHeight(systemInfo); this.setData({ statusBarHeight: systemInfo.statusBarHeight || 44, navBarHeight: navBarHeight, windowHeight: systemInfo.windowHeight || 667, safeAreaBottom: systemInfo.safeArea?.bottom || 0, menuButtonTop: menuButtonInfo.top || 48, menuButtonRight: menuButtonInfo.right || 365, menuButtonWidth: menuButtonInfo.width || 87, menuButtonHeight: menuButtonInfo.height || 32 }); // 初始化API客户端 this.apiClient = apiClient; // 初始化地图动态状态 this.initMapFeedState(); // 初始化静态数据 this.initStaticData(); // 初始化合作商家数据 this.initMerchantMarkers(); // 初始化地图上下文 this.mapCtx = wx.createMapContext('main-map', this); // this.mapCtx.initMarkerCluster({ // enableDefaultStyle: false, // 使用默认聚合样式 // zoomOnClick: true, // 点击聚合点自动放大 // gridSize: 60, // 聚合计算网格大小,单位px // complete(res) {} // }); this.initSystemInfo(); this.initAMapSDK(); console.log("onload----->" + Date.now()); this.requestLocationPermission(); this.lastFriendsFetch = 0; this.lastNearbyFetch = 0; this.lastWeatherFetch = 0; this.lastLocationSnapshot = null; // 🔥 加载浮动UI相关数据 this.loadFloatingUIData(); // 注入模拟好友数据 if (shouldUseMockData) { const mockFriends = [{ userId: 'u1', nickname: '小明', avatarUrl: '' }, { userId: 'u2', nickname: '小红', avatarUrl: '' }, { userId: 'u3', nickname: '小刚', avatarUrl: '' }, { userId: 'u4', nickname: '小美', avatarUrl: '' }, { userId: 'u5', nickname: '小强', avatarUrl: '' } ]; this.setData({ allFriendsList: mockFriends, filteredFriendsList: mockFriends }); const mockLocationMarkList = [{ id: 'loc1', lat: 44.897383746906186, lng: 82.07800276534772, name: '博尔塔拉长城医院', remindFriends: [{ userId: 'u1', avatarUrl: '' }, { userId: 'u2', avatarUrl: '' } ], remindType: 'arrive' }, { id: 'loc2', lat: 44.90334720165053, lng: 82.06464725905994, name: '博乐市第八中学', remindFriends: [{ userId: 'u3', avatarUrl: '' }], remindType: 'leave' } ]; this.setData({ locationMarkList: mockLocationMarkList, filteredLocationMarkList: mockLocationMarkList }); } else { this.setData({ allFriendsList: [], filteredFriendsList: [], locationMarkList: [], filteredLocationMarkList: [] }); this.setData({ isBusLayerVisible: wx.getStorageSync('busLayerState') || false }); } wx.showShareMenu({ withShareTicket: true, // 可选,是否带上 shareTicket menus: ['shareAppMessage', 'shareTimeline'] // 指定分享菜单,'shareAppMessage' 用于分享到聊天 }) // 初始化计算 this.updateScale(); this.startListening(); // this.getCityBoundary("大理"); this.loadUserInfo(); // this.getInitialOrientation(); this.checkSystemTheme(); }, // 核心:判断系统主题 + 手机系统类型(安卓/苹果) checkSystemTheme() { try { // 同步获取系统信息(含主题、系统类型等) const systemInfo = wx.getSystemInfoSync(); // 1. 判断手机系统类型(安卓/苹果) const isIOS = /ios/i.test(systemInfo.system); // 匹配 iOS 系统(忽略大小写) const isAndroid = /android/i.test(systemInfo.system); // 匹配 Android 系统(忽略大小写) // 2. 判断系统主题(深色/浅色) const isDark = systemInfo.theme === 'dark'; // 3. 更新数据(同时保存系统类型和主题状态) if(isAndroid){ this.setData({ isDarkMode: false, isIOS: isIOS, isAndroid: isAndroid }); }else{ this.setData({ isDarkMode: isDark, isIOS: isIOS, isAndroid: isAndroid }); } // 打印日志(清晰展示系统类型+主题) console.log( '当前设备:', isIOS ? '苹果手机(iOS)' : isAndroid ? '安卓手机(Android)' : '其他设备', ' | 系统主题:', isDark ? '深色模式' : '浅色模式' ); } catch (err) { // 异常处理(低版本微信/获取系统信息失败) console.error('获取系统信息(主题+设备类型)失败:', err); this.setData({ isDarkMode: false, // 异常时默认浅色模式 isIOS: false, isAndroid: false }); } }, async loadUserInfo() { try { // 先从全局数据获取 let userInfo = getApp().globalData.userInfo; // 如果全局没有完整信息,从API获取 // const response = await this.apiClient.getUserInfo(); // console.log("info--->2"+response); // if (!userInfo) { const response = await apiClient.getUserInfo(); if (response && response.code === 0) { userInfo = { ...userInfo, user: response.data }; // 更新全局数据 getApp().globalData.userInfo = userInfo; console.log("info--->1" + response); } // } this.setData({ userInfo: userInfo }); this.initWebSocketFriendFeatures(); } catch (error) { console.error('❌ 获取用户信息失败:', error); // 不影响主要功能,继续加载好友列表 } }, getInitialOrientation() { let that = this; return new Promise((resolve, reject) => { // 先检查设备是否支持罗盘(更准确的方向传感器) wx.getSystemInfo({ success: (sysInfo) => { // 扩展支持检测:同时检查罗盘和设备运动传感器 const supportCompass = typeof wx.onCompassChange === 'function'; const supportMotion = typeof wx.onDeviceMotionChange === 'function'; if (!supportCompass && !supportMotion) { console.warn('设备不支持方向感应,使用默认方向'); resolve(0); // 返回默认角度(正北) return; } // 优先使用罗盘(方向更稳定) if (supportCompass) { const compassListener = (res) => { // 罗盘返回的是与正北的夹角(0-360°) const angle = Math.round(res.direction); if (typeof angle === 'number' && !isNaN(angle)) { this.setData({ directonAngle: angle }); this.updateArrowDirection(angle); wx.offCompassChange(compassListener); // 移除监听 resolve(angle); } }; wx.onCompassChange(compassListener); // 启动罗盘(部分设备需要主动启动) wx.startCompass({ success: () => {}, fail: () => { wx.offCompassChange(compassListener); fallbackToMotionSensor(resolve); // 启动失败时降级 } }); } else { // 无罗盘时使用设备运动传感器 fallbackToMotionSensor(resolve); } // 超时处理 setTimeout(() => { if (supportCompass) wx.offCompassChange(); if (supportMotion) wx.offDeviceMotionChange(); console.warn('获取方向超时,使用默认值'); resolve(0); }, 3000); }, fail: (err) => { console.error('获取系统信息失败', err); resolve(0); // 系统信息获取失败时仍返回默认值 } }); // 设备运动传感器降级处理 function fallbackToMotionSensor(resolve) { const motionListener = (deviceData) => { // alpha值:绕z轴旋转角度(0-360°) if (typeof deviceData.alpha === 'number' && !isNaN(deviceData.alpha)) { const angle = Math.round(deviceData.alpha); console.log("angle--->" + angle); that.setData({ directonAngle: angle }); that.updateArrowDirection(angle); // wx.offDeviceMotionChange(motionListener); resolve(angle); } }; wx.onDeviceMotionChange(motionListener); // 启动设备运动监听(部分设备需要) // wx.startDeviceMotionListening({ // interval: 'normal', // success: () => {}, // fail: (err) => { // wx.offDeviceMotionChange(motionListener); // console.warn('设备运动监听启动失败', err); // resolve(0); // } // }); } }); }, initWebSocketFriendFeatures() { try { console.log('🔌 初始化WebSocket好友事件监听器'); // 绑定处理函数到 this,方便后续移除 this.handleNotificationMessage = this.handleNotificationMessage.bind(this); wsManager.on('user_offline', this.handleNotificationMessage); } catch (error) { console.error('初始化WebSocket功能失败:', error); } }, // 处理通知消息 handleNotificationMessage(msg) { try { console.log('🔔 收到心跳---->:', msg); } catch (error) { console.error('处理通知消息失败:', error); } }, // 调用高德接口获取城市边界 getCityBoundary(cityName) { let that = this; wx.request({ url: baseAmapUrl + '/config/district', data: { keywords: cityName, subdistrict: 0, // 不返回子区域 extensions: 'all', // 必须为all才能获取polyline key: config.amapKey // 替换为你的Key }, success: (res) => { if (res.data.status === '1' && res.data.districts.length > 0) { const district = res.data.districts[0]; const polyline = district.polyline; // 边界坐标串 // 解析为小程序polygons格式 const polygons = that.parsePolylineToPolygons(polyline, district.adcode); // 更新地图数据 this.setData({ polygons: polygons }); } }, fail: (err) => { console.error('获取边界失败:', err); } }); }, // 解析高德接口返回的polyline为小程序polygons格式 parsePolylineToPolygons(polyline, cityId) { const polygons = []; // 按 | 分割多个环(每个环是一个独立多边形) const rings = polyline.split('|'); rings.forEach((ring, index) => { // 按 ; 分割单个环的坐标点 const pointStrs = ring.split(';'); // 转换为 { longitude, latitude } 格式 const points = pointStrs.map(pointStr => { const [lng, lat] = pointStr.split(',').map(Number); return { longitude: lng, latitude: lat }; }); // 添加到polygons数组(可自定义样式) polygons.push({ id: `${cityId}_${index}`, // 唯一标识(主区域+子区域区分) points, fillColor: '#1098B0', strokeColor: '#0DD2FC', strokeWidth: 2, fillOpacity: 0.6, strokeDasharray: [5, 5] }); }); return polygons; }, /** * 生成区域唯一标识 * @param {Object} region - 地图区域 * @returns {String} 区域标识 */ getRegionKey(region) { // 四舍五入到小数点后4位,减少精度导致的重复 return [ Math.round(region.southwest.latitude * 10000) / 10000, Math.round(region.southwest.longitude * 10000) / 10000, Math.round(region.northeast.latitude * 10000) / 10000, Math.round(region.northeast.longitude * 10000) / 10000 ].join(','); }, /** * 检查是否同一区域 * @param {String} regionKey - 区域标识 * @returns {Boolean} 是否同一区域 */ isSameRegion(regionKey) { return this.data.lastBusRegion === regionKey; }, /** * 检查缓存是否过期 * @param {String} regionKey - 区域标识 * @returns {Boolean} 是否过期 */ isCacheExpired(regionKey) { const cache = this.busDataCache.get(regionKey); if (!cache) return true; return Date.now() - cache.timestamp > this.data.busDataExpireTime; }, // 4. 更新箭头方向(根据实际业务场景修改) 可旋转但是会重复多个 updateArrowDirection(angle) { const targetIndex = this.data.markers.findIndex(marker => marker.id === 1); if (targetIndex !== -1) { this.setData({ directonAngle: angle, [`markers[${targetIndex}].directonAngle`]: angle, [`markers[${targetIndex}].directionPath`]: '/images/map/direction_arrow.png' }); } }, // 页面隐藏/卸载时停止监听(必须做,否则会耗电) onHide() { this.stopListening(); }, onUnload() { this.stopListening(); // 移除 WebSocket 通知监听器 try { if (this.handleNotificationMessage) { wsManager.off('user_offline', this.handleNotificationMessage); console.log('✅ 已移除 WebSocket 通知监听器'); } } catch (error) { console.error('移除 WebSocket 通知监听器失败:', error); } }, // 停止监听的工具方法 startListening() { // 停止可能存在的旧监听(避免重复监听) wx.stopDeviceMotionListening({}); // 新增:记录上一次的角度值,用于判断角度变化是否超过阈值 let lastAngle = null; // 记录上次更新时间 let lastUpdateTime = 0; // 角度变化阈值(度),超过此值才更新 const ANGLE_THRESHOLD = 5; // 最小更新间隔(毫秒),避免过于频繁更新 const MIN_UPDATE_INTERVAL = 1000; // 启动新监听 wx.startDeviceMotionListening({ interval: 'game', // 高频监听(适合实时方向更新) success: () => { console.log('方向监听启动成功'); // 监听方向变化事件 this.motionListener = wx.onDeviceMotionChange((res) => { // res.alpha:设备绕垂直轴旋转角度(0-360°,0为正北) if (typeof res.alpha === 'number' && !isNaN(res.alpha)) { // 取整数角度,减少抖动 const currentAngle = Math.round(res.alpha); const now = Date.now(); // 满足以下条件才更新: // 1. 距离上次更新超过最小间隔时间 // 2. 角度变化超过阈值(首次记录时直接更新) const isAngleChangedEnough = lastAngle === null || Math.abs(currentAngle - lastAngle) >= ANGLE_THRESHOLD; const isTimeEnough = now - lastUpdateTime >= MIN_UPDATE_INTERVAL; if (isAngleChangedEnough && isTimeEnough) { console.log(`方向变化超过${ANGLE_THRESHOLD}度,更新角度: ${currentAngle}`); this.updateArrowDirection(currentAngle); // 更新记录值 lastAngle = currentAngle; lastUpdateTime = now; } if (this.data.directonAngle == -1) { this.setData({ directonAngle: currentAngle }); this.updateArrowDirection(currentAngle); } } }); }, fail: (err) => { console.error('监听启动失败:', err); } }); }, // 页面隐藏/卸载时停止监听(必须做,否则会耗电) onHide() { this.stopListening(); }, onUnload() { this.stopListening(); }, // 停止监听的工具方法 stopListening() { if (this.motionListener) { this.motionListener(); // 移除监听事件 this.motionListener = null; } wx.stopDeviceMotionListening({}); // 停止监听服务 }, updateScale() { const that = this // 直接获取当前缩放级别(同步操作,无延迟) this.mapCtx.getScale({ success(res) { const currentScale = res.scale // 计算比例尺数值 that.calcScale(currentScale) }, // 极端情况:如果获取失败,100ms后重试(确保最终能更新) fail() { setTimeout(() => { that.updateScale() }, 100) } }) }, calcScale(scale) { // 计算100px线段对应的实际距离(米) const metersPerPx = 156543.03392 * Math.cos(39.915 * Math.PI / 180) / Math.pow(2, scale) const totalMeters = metersPerPx * 100 // 100px线段的实际距离 // ---------------------- // 公制单位:米 → 千米(自动转换+取整) // ---------------------- let metricText; if (totalMeters >= 1000) { // ≥1000米时转千米,四舍五入取整数 const km = Math.round(totalMeters / 1000) metricText = `${km}千米` } else { // <1000米时保留米,四舍五入取整数 metricText = `${Math.round(totalMeters)}米` } metricText = metricText // ---------------------- // 英制单位:英尺 → 英里(自动转换+取整) // 1英里 = 5280英尺(转换临界点) // ---------------------- const totalFeet = totalMeters * 3.28084 // 先转成英尺 let imperialText; if (totalFeet >= 5280) { // ≥5280英尺时转英里,四舍五入取整数 const miles = Math.round(totalFeet / 5280) imperialText = `${miles}英里` } else { // <5280英尺时保留英尺,四舍五入取整数 imperialText = `${Math.round(totalFeet)}英尺` } this.setData({ metric: metricText, imperial: imperialText }) }, onShow() { console.log("on-->" + "onShow"); // 同步全局登录状态 this.setData({ isLoggedIn: app.globalData.isLoggedIn, userInfo: app.globalData.userInfo }); if (this.data.amapReady) { this.startLocationService(); } // 设置tabBar选中状态为"发现"(索引0) if (typeof this.getTabBar === 'function' && this.getTabBar()) { this.getTabBar().setData({ selected: 0 }); } if (app.globalData && app.globalData.needOpenLocationFavoriteBar) { setTimeout(() => { this.onOpenLocationFavoriteBar(); }, 300); app.globalData.needOpenLocationFavoriteBar = false; } }, onHide() { this.stopLocationService(); wx.hideLoading(); }, onUnload() { this.stopLocationService(); }, // 初始化高德地图SDK initAMapSDK() { try { // 使用完整版本的高德地图SDK,包含逆地理编码功能 this.amapFun = new AMapWX({ key: config.amapKey || '' // 需要有效的API Key才能使用全部功能 }); this.setData({ amapReady: true, amapError: null }); console.debug('✅ 高德地图SDK初始化成功'); } catch (error) { console.error('❌ 高德地图SDK初始化失败:', error); this.setData({ amapReady: false, amapError: error.message || '高德地图SDK初始化失败' }); wx.showToast({ title: '定位服务异常', icon: 'none' }); } }, // 初始化系统信息 initSystemInfo() { try { const systemInfo = this.systemInfoHelper.getSystemInfoSync(); const menuButtonInfo = wx.getMenuButtonBoundingClientRect(); const statusBarHeight = systemInfo.statusBarHeight || 44; const menuButtonHeight = menuButtonInfo.height || 32; const menuButtonTop = menuButtonInfo.top || statusBarHeight + 6; const navBarHeight = statusBarHeight + menuButtonHeight + 10; this.setData({ statusBarHeight: statusBarHeight, menuButtonHeight: menuButtonHeight, navBarHeight: navBarHeight, windowHeight: systemInfo.windowHeight || 667, safeAreaBottom: systemInfo.safeArea ? systemInfo.windowHeight - systemInfo.safeArea.bottom : 0 }); } catch (error) { console.error('获取系统信息失败:', error); // 设置默认值 this.setData({ statusBarHeight: 44, menuButtonHeight: 32, menuButtonTop: 50, navBarHeight: 88, windowHeight: 667, safeAreaBottom: 0 }); } }, // 请求位置权限 requestLocationPermission() { // 直接尝试获取位置,如果失败会自动触发权限请求 this.getCurrentLocation(); }, // 获取当前位置 - 双保险定位策略 getCurrentLocation() { if (!this.data.amapReady) { this.getWxLocation(); // 降级方案 return; } this.amapFun.getWxLocation((location, error) => { if (error) { console.error('高德SDK获取位置失败:', error); this.getWxLocation(); // 降级方案 return; } this.processLocationResult(location); }, { type: 'gcj02', isHighAccuracy: true, highAccuracyExpireTime: 4000 }); }, // 微信原生定位降级方案 getWxLocation() { wx.getLocation({ type: 'gcj02', isHighAccuracy: true, highAccuracyExpireTime: 4000, success: (location) => { // 模拟高德地图数据格式,但不直接显示坐标 const enhancedLocation = { ...location, address: '定位中...', // 使用友好的占位符 formattedAddress: '', province: '', city: '', district: '', street: '' }; this.processLocationResult(enhancedLocation); }, fail: (error) => { console.error('微信原生定位也失败:', error); this.handleLocationError(error); } }); }, // 🔥 新增:基于坐标的简化位置推断 - 修复版 getLocationByCoordinates(latitude, longitude) { const lat = parseFloat(latitude); const lng = parseFloat(longitude); let province = ''; let city = ''; let district = ''; // 基于经纬度范围的简化位置推断 // 实际项目中可能需要调用专业的地理编码API或后端服务 // 北京地区 if (lat >= 39.4 && lat <= 41.6 && lng >= 115.4 && lng <= 117.5) { province = '北京'; city = '北京市'; district = '朝阳区'; } // 上海地区 else if (lat >= 30.6 && lat <= 31.8 && lng >= 121.2 && lng <= 122.1) { province = '上海'; city = '上海市'; district = '浦东新区'; } // 广州地区 else if (lat >= 22.5 && lat <= 23.5 && lng >= 112.9 && lng <= 114.3) { province = '广东'; city = '广州市'; district = '天河区'; } // 深圳地区 else if (lat >= 22.4 && lat <= 22.8 && lng >= 113.7 && lng <= 114.6) { province = '广东'; city = '深圳市'; district = '南山区'; } // 更多城市的判断(可以根据实际需求添加更多城市) // 杭州地区 else if (lat >= 29.0 && lat <= 30.5 && lng >= 118.0 && lng <= 120.0) { province = '浙江'; city = '杭州市'; district = '西湖区'; } // 南京地区 else if (lat >= 31.0 && lat <= 33.0 && lng >= 117.0 && lng <= 119.0) { province = '江苏'; city = '南京市'; district = '玄武区'; } // 成都地区 else if (lat >= 30.0 && lat <= 31.5 && lng >= 103.5 && lng <= 104.5) { province = '四川'; city = '成都市'; district = '锦江区'; } // 其他地区 - 使用通用位置信息 else { // 改进:为了更好的用户体验,不再使用'地图'作为默认区域 // 而是根据经纬度范围提供一个更有意义的默认位置 const latInt = Math.round(lat); const lngInt = Math.round(lng); // 使用经纬度整数部分作为位置标识,让用户知道这是一个估算的位置 district = `${latInt}°${lngInt}°附近`; city = '中国'; province = '位置'; } const address = province && city ? `${province}${city}${district}` : `${district}`; return { province, city, district, address }; }, // 处理位置结果 processLocationResult(location) { console.log('✅ 获取到经纬度: lat: ' + location.latitude + " long: " + location.longitude); // 保存自己的位置信息 const myLocation = { latitude: location.latitude, longitude: location.longitude, accuracy: location.accuracy || 0 }; this.setData({ myLocation: myLocation }) const previousLocation = this.lastLocationSnapshot; this.lastLocationSnapshot = myLocation; // 保存位置精度;仅在需要跟随或尚未初始化时更新地图中心 const locationUpdate = { accuracy: location.accuracy || 0, myLocation: myLocation }; const hasInitialCenter = typeof this.data.latitude === 'number' && typeof this.data.longitude === 'number' && !Number.isNaN(this.data.latitude) && !Number.isNaN(this.data.longitude) && this.data.latitude !== 0 && this.data.longitude !== 0; if (this.data.shouldFollowLocation || !hasInitialCenter) { locationUpdate.latitude = location.latitude; locationUpdate.longitude = location.longitude; } this.setData(locationUpdate); // 创建用户位置标记(带头像和电量信息) this.createUserMarkers(myLocation); // 只有在shouldFollowLocation为true时才移动地图 if (this.mapCtx && this.data.shouldFollowLocation) { try { this.setData({ shouldFollowLocation: false }); this.mapCtx.moveToLocation({ latitude: location.latitude, longitude: location.longitude, success: () => { console.debug('✅ 地图移动到当前位置成功'); }, fail: (error) => { console.error('❌ 地图移动失败:', error); } }); } catch (error) { console.error('❌ 移动地图到当前位置时出现异常:', error); } } // 解析地址 - 使用Promise处理异步结果 this.resolveAddress(location).then(() => { // 地址解析完成后继续执行其他操作 console.debug('✅ 地址解析完成,继续执行其他操作'); }).catch((error) => { console.error('⚠️ 地址解析过程中出现错误,但继续执行其他操作:', error); }); // 加载好友位置 const now = Date.now(); if (!this.lastFriendsFetch || (now - this.lastFriendsFetch) > 45 * 1000) { this.lastFriendsFetch = now; if (!this.data.mapFeedVisible && !this.data.isOnline) { this.loadFriendsLocation(); } } // 获取附近用户 - 已启用 if (!this.lastNearbyFetch || (now - this.lastNearbyFetch) > 45 * 1000) { if (this.data.isOnline && !this.data.mapFeedVisible) { this.lastNearbyFetch = now; this.loadNearbyUsers(); } } this.scheduleMapFeedRefresh('location'); }, // 创建用户位置标记(带头像和电量信息) createUserMarkers(myLocation) { // 安全地获取头像URL,处理 userInfo 可能为 null 的情况 let avatarUrl = '/images/default-stranger.png'; // 默认头像 if (app.globalData.userInfo && app.globalData.userInfo.user && app.globalData.userInfo.user.avatar) { avatarUrl = app.globalData.userInfo.user.avatar; } if (avatarUrl.includes('myqcloud')) { avatarUrl = avatarUrl + '?imageMogr2/auto-orient/thumbnail/100x100/quality/85'; } console.log("userInfoUrl--->" + avatarUrl); let userMarkers = []; const userMarker = { id: 1, latitude: myLocation.latitude, longitude: myLocation.longitude, iconPath: '/images/map/marker_blank.png', avatarUrl: avatarUrl ? avatarUrl : '/images/default-stranger.png', directionPath: '/images/map/direction_arrow.png', type: 'mine', directonAngle: this.data.directonAngle, joinCluster: false, title: "我", height: 45, width: 45, customCallout: { anchorX: 0, // 水平锚点设为 0.5(图标宽度的正中间) anchorY: 70, // 垂直锚点设为 1(图标底部,紧贴地图坐标点) display: 'ALWAYS' }, zIndex: 1 } // this.data.allMarkers.push(userMarker); const existingIndex = this.data.allMarkers.findIndex(marker => marker.id === 1); if (existingIndex == -1) { this.data.allMarkers.push(userMarker); } userMarkers.push(userMarker); this.updateMarkersByMarkersType(userMarkers); }, // 解析地址 - 多级解析策略 async resolveAddress(location) { console.log('🏠 处理地址信息,location数据:', location); let address = '未知位置'; let addressDetail = {}; let currentDistrict = ''; let currentCity = ''; let realLocationInfo = {}; // 优先尝试获取真实位置信息(无论登录状态) try { realLocationInfo = await this.tryGetRealLocationInfo(location.latitude, location.longitude); } catch (error) { console.error('⚠️ 获取真实位置信息失败:', error); } if (realLocationInfo) { address = realLocationInfo.address || address; currentDistrict = realLocationInfo.district || currentDistrict; // 不设置currentCity,只使用区级信息 console.log('✅ 使用真实位置信息:', { address, currentDistrict }); } else { // 从高德地图定位结果中获取地址信息 if (location.address && location.address.trim()) { address = location.address; console.debug('✅ 使用高德地图返回的地址:', address); } else if (location.formattedAddress && location.formattedAddress.trim()) { address = location.formattedAddress; console.debug('✅ 使用格式化地址:', address); } else { // 降级处理:使用坐标作为地址 address = `位置: ${location.latitude.toFixed(6)}, ${location.longitude.toFixed(6)}`; console.debug('⚠️ 降级使用坐标作为地址:', address); } } // 优先使用SDK返回的结构化地址 if (location.province || location.city || location.district) { addressDetail = { province: location.province || '', city: location.city || '', district: location.district || '', street: location.street || '', streetNumber: location.streetNumber || '', description: location.description || address }; currentDistrict = location.district || ''; // 不设置currentCity,只使用区级信息 } // 如果SDK没有返回结构化信息,从完整地址字符串中智能解析 if (!currentDistrict && !currentCity && address) { // 智能解析中国地址格式的正则表达式 const addressPattern = /(.*?)省?(.*?)市?(.*?)区?(.*?)县?(.*?)镇?(.*?)乡?(.*)/; const match = address.match(addressPattern); if (match) { const [, province, city, district] = match; // 不设置currentCity,只使用区级信息 currentDistrict = district; console.debug('📍 正则解析结果:', { province, district: currentDistrict }); } } // 降级处理:如果以上方法都失败,提取地址前几个字符作为区域显示 if (!currentDistrict && !currentCity && address) { // 最后的降级方案:使用地址的前几个字符 currentDistrict = address.substring(0, 4) || '定位中...'; console.debug('📍 降级使用地址前缀:', currentDistrict); } console.debug('📍 最终解析的区域信息:', { address, currentDistrict, currentCity, locationData: location }); this.setData({ currentAddress: address, addressDetail: addressDetail, currentDistrict: currentDistrict, currentCity: '' // 不显示城市信息,只显示区级信息 }); if (!this.lastWeatherFetch || (Date.now() - this.lastWeatherFetch) > 30 * 60 * 1000) { this.lastWeatherFetch = Date.now(); this.loadWeatherInfo(location.latitude, location.longitude); } // 更新位置信息到服务器 this.updateLocationToServer({ ...location, address: address, addressDetail: addressDetail }); }, // 更新位置到服务器 - 尝试从响应中获取地址信息 async updateLocationToServer(locationData) { try { // 🔥 添加登录状态检查 const app = getApp(); const isLoggedIn = app?.globalData?.isLoggedIn || false; if (!isLoggedIn) { // 未登录状态下,使用设备直接返回的地址信息(如果有) if (locationData.address) { this.setData({ currentDistrict: locationData.address, locationLoading: false }); } return { success: false, error: '用户未登录', skipAuth: true }; } const systemInfo = this.systemInfoHelper.getSystemInfoSync(); const updateData = { latitude: parseFloat(locationData.latitude), longitude: parseFloat(locationData.longitude), altitude: parseFloat(locationData.altitude) || 0, accuracy: parseFloat(locationData.accuracy) || 0, speed: parseFloat(locationData.speed) || 0, direction: parseFloat(locationData.direction) || 0, deviceType: 'miniprogram', deviceModel: systemInfo.model || 'Unknown', battery: systemInfo.battery || 100 }; console.log('✅ 上传的经纬度:', ' latitude ' + locationData.latitude + ' longitude ' + locationData.longitude); const response = await this.apiClient.updateLocation(updateData); console.log('✅ 位置更新成功,服务器响应:', response); // 🔥 优化:从服务器响应中获取准确的地址信息 if (response && response.data) { const serverData = response.data; // 检查服务器是否返回了地址信息 if (serverData.address || serverData.district || serverData.city || serverData.province) { // 服务器位置信息是基于坐标查询的,比本地解析更准确 // 对于登录用户,始终优先使用服务器返回的地址信息 if (isLoggedIn) { const newDistrict = serverData.district || serverData.city || this.data.currentDistrict; const newAddress = serverData.address || this.data.currentAddress; this.setData({ // currentDistrict: newDistrict, currentCity: '', // 不显示城市信息,只使用区级信息 currentAddress: newAddress }); console.debug('✅ 已使用位置更新接口的地址信息更新左上角显示:', { district: newDistrict, address: newAddress }); } // 对于未登录用户,如果当前区域信息不准确或缺失,优先使用服务器信息 else if (!this.data.currentDistrict || this.data.currentDistrict === '定位中...' || this.data.currentDistrict === '位置信息' || this.data.currentDistrict.includes('位置 ')) { const newDistrict = serverData.district || serverData.city || this.data.currentDistrict; const newAddress = serverData.address || this.data.currentAddress; this.setData({ // currentDistrict: newDistrict, currentCity: '', // 不显示城市信息,只使用区级信息 currentAddress: newAddress }); } } } } catch (error) { console.error('❌ 位置更新失败:', error); // 不阻断用户体验,仅记录错误 } }, // 处理位置错误 handleLocationError(error) { console.error('位置服务错误:', error); let errorMessage = '获取位置失败'; if (error.errMsg) { if (error.errMsg.includes('auth deny')) { errorMessage = '位置权限被拒绝,请在设置中开启位置权限'; } else if (error.errMsg.includes('timeout')) { errorMessage = '定位超时,请检查网络连接'; } else if (error.errMsg.includes('locate fail')) { errorMessage = '定位失败,请稍后重试'; } } wx.showToast({ title: errorMessage, icon: 'none', duration: 3000 }); // 设置默认位置(如果有的话) if (this.data.defaultLocation) { this.setData({ longitude: this.data.defaultLocation.longitude, latitude: this.data.defaultLocation.latitude, currentAddress: this.data.defaultLocation.address || '位置获取失败' }); } }, // 启动位置更新服务 - 增强版:实时位置更新 startLocationService() { // 清除旧的定时器 if (this.locationTimer) { clearInterval(this.locationTimer); } // if (!this.data.hasUserInteractedWithMap) { // this.setData({ // shouldFollowLocation: false // }); // } // 立即更新一次位置 if (this.data.amapReady) { this.getCurrentLocation(); } const refreshInterval = config?.locationUpdateInterval || 30000; this.locationTimer = setInterval(() => { if (this.data.amapReady) { this.getCurrentLocation(); } }, refreshInterval); }, // 停止位置更新服务 stopLocationService() { if (this.locationTimer) { clearInterval(this.locationTimer); this.locationTimer = null; } }, // 计算两点之间的距离(米) calculateDistance(lat1, lng1, lat2, lng2) { const R = 6371000; // 地球半径(米) const dLat = (lat2 - lat1) * Math.PI / 180; const dLng = (lng2 - lng1) * Math.PI / 180; const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * Math.sin(dLng / 2) * Math.sin(dLng / 2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return R * c; }, // 格式化距离显示 formatDistance(distance) { if (distance < 1000) { return Math.round(distance) + 'm'; } else if (distance < 10000) { return (distance / 1000).toFixed(1) + 'km'; } else { return Math.round(distance / 1000) + 'km'; } }, // 加载好友位置 async loadFriendsLocation() { try { console.debug('👥 开始加载好友位置'); // const response = await this.apiClient.getFriendsLocation(); const [region, scaleResult] = await Promise.all([ this.getMapRegion(), this.getMapScale() ]); let scale = Math.round(scaleResult?.scale || this.data.scale || 13); const payload = buildBoundsPayload(region); if (scale > 20) { scale = 20; } const friendsAndStrangerSprams = { "bounds": payload.bounds, "zoomLevel": scale, "displayMode": "friends", // "maxPoints": 200, // "clusterThreshold": 100, // "onlineMode": "all", "showOffline": true, "clusterThreshold": this.data.clusterThreshold, // "genderFilter": 0, // "ageRangeMin": 18, // "ageRangeMax": 35, // "memberOnly": false, // "enableRealTimeUpdate": false, // "currentLat": this.data.myLocation.latitude, // "currentLng": this.data.myLocation.longitude } let response = await this.apiClient.getFriendsAndStranger(friendsAndStrangerSprams); // 处理API响应数据结构 if (response && response.code === 0 && response.data) { if (!this.data.mapFeedVisible && !this.data.isOnline) { // if (scale > 8) { // response = mockResponseFriend // } else { // response = mockResponseFriendMarkerCluster; // } this.addFriendsMarkersByResult(response.data); this.setData({ friendsData: response.data.userPoints }); } // 只保存数据,不直接操作markers数组 // this.setData({ // friendsData: friendsLocation, // friendsCount: friendsLocation.length // }); // 调用统一的标记管理函数来更新地图标记 } else { console.debug('📍 暂无好友位置信息'); this.setData({ friendsCount: 0, friendsData: [] }); // 调用统一的标记管理函数来更新地图标记 } } catch (error) { console.error('❌ 加载好友位置失败:', error); // this.setData({ // friendsCount: 0, // friendsData: [] // }); // 调用统一的标记管理函数来更新地图标记 // 只对已登录用户显示友好的错误提示 const app = getApp(); const isLoggedIn = app?.globalData?.isLoggedIn || false; // if (isLoggedIn && error.message && !error.message.includes('401')) { // wx.showToast({ // title: '好友位置加载失败', // icon: 'none', // duration: 2000 // }); // } } }, // 回到自己位置 - 定位按钮功能 centerToUserLocation() { if (!this.data.myLocation) { this.getCurrentLocation(); return; } // 设置为跟随用户位置,并重置交互标记 this.setData({ shouldFollowLocation: false, hasUserInteractedWithMap: false }); // 移动地图到自己的位置 this.mapCtx.moveToLocation({ latitude: this.data.myLocation.latitude, longitude: this.data.myLocation.longitude }); // 重置缩放级别到合适的级别 this.setData({ latitude: this.data.myLocation.latitude, longitude: this.data.myLocation.longitude, currentMapScale: 16 }); // 获取地图上下文(关键:确保能拿到实时缩放级别) this.mapCtx = wx.createMapContext('main-map') // 初始化计算 let that = this; setTimeout(() => { that.updateScale() }, 1000) }, //收藏地址 async onPoiFavoriteCommon(address, latitude, longitude, name) { wx.request({ url: `${config.api.baseUrl}/api/v1/location/place-mark`, method: 'POST', data: JSON.stringify({ "name": name, "category": "home", // "home" | "work" | "favorite" | "other" "icon": "home_icon", "latitude": latitude, "longitude": longitude, "address": address, "description": "温馨的家", "isPrivate": true, "reminder": false }), header: { 'Authorization': `Bearer ${apiClient.getToken()}`, 'Content-Type': 'application/json', }, success: (res) => { if (res.data.code === 0) { this.setData({ placeMarkId: res.data.data.placeMarkId, mapClickCollect: '取消收藏' }); wx.showToast({ title: '已收藏', icon: 'success' }); } else { wx.showToast({ title: '收藏失败', icon: 'success' }); } }, fail: () => { wx.showToast({ title: '收藏失败,请重试', icon: 'none' }); } }); }, //取消收藏 async onPoiCancelCommon(placeMarkId) { wx.request({ url: `${config.api.baseUrl}/api/v1/location/place-mark/${placeMarkId}`, method: 'DELETE', header: { 'Authorization': `Bearer ${apiClient.getToken()}`, 'Content-Type': 'application/json', }, success: (res) => { if (res.data.code === 0) { this.setData({ placeMarkId: "", mapClickCollect: "收藏" }); wx.showToast({ title: '取消收藏成功', icon: 'success' }); } else { wx.showToast({ title: '取消收藏失败', icon: 'success' }); } }, fail: () => { wx.showToast({ title: '取消收藏失败,请重试', icon: 'none' }); } }); }, //收藏地址 async onPoiFavorite(e) { const address = e.currentTarget.dataset.poidetail.address; const latitude = e.currentTarget.dataset.poidetail.latitude; const longitude = e.currentTarget.dataset.poidetail.longitude; const name = e.currentTarget.dataset.poidetail.name; if (this.data.placeMarkId == '') { //空 说明还没有收藏地址 this.onPoiFavoriteCommon(address, latitude, longitude, name); } else { //说明刚才收藏了 再点击就是取消 this.onPoiCancelCommon(this.data.placeMarkId); } }, // 地图区域变化完成事件 onMapRegionChange(e) { // 无论缩放开始/进行中/结束,都强制更新(确保不遗漏) console.log("地图移动---->" + e.detail); // this.addDynamicMarkersByResult(mockResponseDynamic.data); const type = e.causedBy; if (type === 'scale') { //放大缩小 this.updateScale() } const detail = e.detail || {}; const changeType = e.type || 'unknown'; // if (detail.causedBy !== 'update') { // } // 当地图被手动拖动时,停止跟随用户位置 if (changeType === 'end' && (detail.causedBy === 'drag' || detail.causedBy === 'scale')) { this.setData({ shouldFollowLocation: false, hasUserInteractedWithMap: true }); } // 只在拖拽结束时处理,避免频繁触发 if (changeType === 'end' && detail.scale) { setTimeout(() => { const currentScale = Math.round(detail.scale); // this.setData({ // currentMapScale: currentScale // }); if (this.mapCtx && this.data.myLocation) { try { // 更新用户标记的位置 // this.data.userMarker.latitude = this.data.myLocation.latitude; // this.data.userMarker.longitude = this.data.myLocation.longitude; if (this.data.isOnline && !this.data.mapFeedVisible) { this.loadNearbyUsers(); } if (!this.data.mapFeedVisible && !this.data.isOnline) { this.loadFriendsLocation(); } if (this.data.mapFeedVisible) { this.scheduleMapFeedRefresh('location'); } } catch (error) { console.error('⚠️ 地图区域变化后更新标记失败:', error); } } }, 300); const isUserGesture = detail.causedBy === 'drag' || detail.causedBy === 'scale'; if (isUserGesture) { // this.scheduleMapFeedRefresh('region-change'); } } }, // 地图点击事件 - 显示点击位置的POI资料卡 onMapTap(e) { // 点击地图时关闭用户详情卡片 this.closeUserCard(); this.hideFeedBubble(); // 获取点击位置的经纬度 if (e && e.detail && e.detail.latitude && e.detail.longitude) { // 相机移动到点击位置 if (this.data.shouldFollowLocation) { this.mapCtx && this.mapCtx.moveToLocation({ latitude: e.detail.latitude, longitude: e.detail.longitude }); } } }, // 地图长按事件 onMapLongTap(e) { // 只有在动态模式下才显示发布动态选项 if (this.data.isOnline) { // 保存当前长按位置 const location = { latitude: e.detail.latitude, longitude: e.detail.longitude }; this.setData({ currentDynamicLocation: location, showDynamicInputModal: true, dynamicContent: '' }); } // else { // wx.showToast({ // title: e && e.detail && e.detail.latitude && e.detail.longitude ? // `长按:${e.detail.latitude.toFixed(6)},${e.detail.longitude.toFixed(6)}` : // '长按地图操作', // icon: 'none' // }); // } }, // 关闭动态输入弹窗 closeDynamicInputModal() { this.setData({ showDynamicInputModal: false, dynamicContent: '' }); }, // 输入动态内容 onDynamicContentInput(e) { this.setData({ dynamicContent: e.detail.value }); }, // 发布动态 publishDynamic() { const { dynamicContent, currentDynamicLocation } = this.data; if (!dynamicContent.trim()) { wx.showToast({ title: '动态内容不能为空', icon: 'none' }); return; } if (!currentDynamicLocation) { wx.showToast({ title: '位置信息无效', icon: 'none' }); return; } // 创建新动态 const newDynamic = { id: Date.now(), // 使用时间戳作为唯一ID content: dynamicContent.trim(), latitude: currentDynamicLocation.latitude, longitude: currentDynamicLocation.longitude, creator: this.data.userInfo?.nickname || '匿名用户', avatar: this.data.userInfo?.avatarUrl || '', createTime: new Date().toISOString(), likes: 0, comments: [] }; // 添加到动态列表 const updatedDynamics = [...this.data.locationDynamics, newDynamic]; this.setData({ locationDynamics: updatedDynamics, showDynamicInputModal: false, dynamicContent: '' }); wx.showToast({ title: '动态发布成功', icon: 'success' }); }, // 点击动态标记 onDynamicMarkerTap(e) { const markerId = e.detail.markerId; // 查找对应的动态 const dynamic = this.data.locationDynamics.find(d => d.id === markerId); if (dynamic) { this.setData({ selectedDynamic: dynamic, showDynamicDetailModal: true }); } }, // 关闭动态详情弹窗 closeDynamicDetailModal() { this.setData({ showDynamicDetailModal: false, selectedDynamic: null }); }, onMarkerTap(e) { const markerId = e.detail.markerId || e.markerId; if (markerId === 1) { // 点击的是用户自己的位置 return; } const markers = this.data.markers || []; const clickedMarker = markers.find(marker => String(marker.id) === String(markerId)); const markerType = clickedMarker?.type || ''; // if (markerType === 'map-feed-cluster') { // this.setData({ // currentMapScale:18 // }) // this.toLocationByScale(18, clickedMarker.latitude, clickedMarker.longitude); // return; // } if (markerType === 'stranger-cluster' || markerType === 'friend-cluster') { this.setData({ currentMapScale: 18 }) this.toLocationByScale(18, clickedMarker.latitude, clickedMarker.longitude); return; } if (markerType.startsWith('map-feed')) { this.handleMapFeedMarkerTap(clickedMarker); return; } if (markerType === 'staticData') { this.hideFeedBubble(); wx.showModal({ title: clickedMarker?.title || '位置提示', content: clickedMarker?.description || '暂无更多信息', showCancel: false, confirmText: '我知道了' }); return; } if (markerType === 'merchant') { this.hideFeedBubble(); const merchantId = clickedMarker?.merchantId || (markerId >= 10000 ? markerId - 10000 : null); const merchant = this.data.merchantList?.find(m => m.id === merchantId); if (merchant) { this.setData({ selectedMerchant: merchant, merchantInfoModalVisible: true, activeMerchantId: merchantId }); // 更新markers中的active状态 this.updateMerchantActiveState(merchantId); } return; } if (markerType === 'dynamic') { this.hideFeedBubble(); const dynamic = this.data.locationDynamics.find(d => d.id === markerId); if (dynamic) { this.setData({ selectedDynamic: dynamic, showDynamicDetailModal: true }); } return; } if (markerType == 'friend-single') { // 计算距离 const myLocation = this.data.myLocation; const distance = myLocation ? this.calculateDistance( myLocation.latitude, myLocation.longitude, clickedMarker.latitude, clickedMarker.longitude ) : 0; this.setData({ selectedUser: { ...clickedMarker, distance: distance > 0 ? this.formatDistance(distance) : '', userId: clickedMarker.customId || clickedMarker.userId, nickname: clickedMarker.nickname, avatarUrl: clickedMarker.avatarUrl, originAvatarUrl:clickedMarker.originAvatarUrl, isFriend: true } }); this.hideFeedBubble(); return; } if (markerType == 'stranger-single') { if (clickedMarker) { // 计算距离 const myLocation = this.data.myLocation; const distance = myLocation ? this.calculateDistance( myLocation.latitude, myLocation.longitude, clickedMarker.latitude, clickedMarker.longitude ) : 0; this.setData({ selectedUser: { ...clickedMarker, distance: distance > 0 ? this.formatDistance(distance) : '', userId: clickedMarker.customId || clickedMarker.userId, avatarUrl: clickedMarker.avatarUrl, nickname: clickedMarker.nickname, originAvatarUrl:clickedMarker.originAvatarUrl, isFriend: false } }); this.hideFeedBubble(); return; } } }, toggleLike(){ wx.showToast({ title: '点赞成功', icon: 'none' }); }, // 地图POI点击事件 onPoiTap(e) { console.log('📍 POI点击事件:', e.detail); let that = this; const poi = { name: e.detail.name, address: '', latitude: e.detail.latitude, longitude: e.detail.longitude }; that.setData({ poiDetail: poi, renderKey: Date.now() }) this.tryGetRealLocationInfo(e.detail.latitude, e.detail.longitude).then((realLocationInfo) => { if (realLocationInfo) { // 先重置数据,再重新赋值 this.setData({ poiDetailAddress: realLocationInfo.address, // 先清空 poiDetailShow: true, renderKey: Date.now() }); } }); that.mapCtx && that.mapCtx.moveToLocation({ latitude: poi.latitude, longitude: poi.longitude, success: () => { // 地图移动完成后再设置数据,确保视图同步 that.setData({ latitude: poi.latitude, longitude: poi.longitude, currentMapScale: 17 }); } }); // 获取地图上下文(关键:确保能拿到实时缩放级别) that.mapCtx = wx.createMapContext('main-map') setTimeout(() => { that.updateScale() }, 1000) that.setData({ poiDetailShow: true }); }, toLocationByScale(scale, lat, lon) { let that = this; this.mapCtx && this.mapCtx.moveToLocation({ latitude: lat, longitude: lon, success: () => { // 地图移动完成后再设置数据,确保视图同步 that.setData({ latitude: lat, longitude: lon }); if (scale != -1) { setTimeout(() => { that.updateScale() }, 1000) } } }); }, // 关闭POI详情卡片 closePoiCard() { this.setData({ poiDetail: null, poiDetailShow: false, placeMarkId: '', mapClickCollect: '收藏' }); }, // POI操作:导航 onPoiNavigate() { const poi = this.data.poiDetail; if (!poi) { wx.showToast({ title: '无POI信息', icon: 'none' }); return; } wx.openLocation({ latitude: poi.latitude, longitude: poi.longitude, name: poi.name || '目标位置', address: poi.address || '', scale: 16 }); }, // POI操作:提醒 onPoiRemind() { // 关闭POI弹窗,打开提醒弹窗,初始化范围、好友、模式 const poi = this.data.poiDetail; this.setData({ showPoiRemindModal: true, poiRemindRadius: 300, poiRemindSelectedFriends: [], poiRemindMode: ['arrive'], // 多选 showFriendSelectModal: false, lastPoiForRemind: poi }); this.updatePoiRemindCircle(300); if (poi) { this.fitMapToCircle(poi.latitude, poi.longitude, 300); } }, // 到达/离开提醒多选切换 onPoiRemindModeChange(e) { let values = e.detail.value; if (!Array.isArray(values)) { values = values ? [values] : []; } // 至少保留一个选项,若全取消则默认到达 if (values.length === 0) { values = ['arrive']; } this.setData({ poiRemindMode: values }); }, // 切换好友选中状态 onToggleFriendSelect(e) { const userId = e.currentTarget.dataset.userid; // 选满且未选中的不能再点 if (this.data.poiRemindSelectedFriends.length >= 3 && !this.isFriendSelected(userId)) return; let selected = this.data.poiRemindSelectedFriends.slice(); const idx = selected.findIndex(f => f.userId === userId); if (idx > -1) { selected.splice(idx, 1); } else { if (selected.length >= 3) { wx.showToast({ title: '最多选择3位好友', icon: 'none' }); return; } const friend = this.data.allFriendsList.find(f => f.userId === userId); if (friend) selected.push(friend); } // 更新filteredFriendsList的selected字段 const selectedIds = selected.map(f => f.userId); const filteredFriendsList = this.data.filteredFriendsList.map(f => ({ ...f, selected: selectedIds.includes(f.userId) })); this.setData({ poiRemindSelectedFriends: selected, filteredFriendsList }); }, // 判断某个好友是否已被选中 isFriendSelected(userId) { return this.data.poiRemindSelectedFriends.some(f => f.userId === userId); }, // 完成好友选择,关闭弹窗 onFriendSelectDone() { this.setData({ showFriendSelectModal: false }); }, // 关闭弹窗 onCloseFriendSelect() { this.setData({ showFriendSelectModal: false }); }, // 移除已选好友 onRemoveSelectedFriend(e) { const userId = e.currentTarget.dataset.userid; let selected = this.data.poiRemindSelectedFriends.slice(); const idx = selected.findIndex(f => f.userId === userId); if (idx > -1) { selected.splice(idx, 1); // 更新filteredFriendsList的selected字段 const selectedIds = selected.map(f => f.userId); const filteredFriendsList = this.data.filteredFriendsList.map(f => ({ ...f, selected: selectedIds.includes(f.userId) })); this.setData({ poiRemindSelectedFriends: selected, filteredFriendsList }); } }, // 关闭提醒弹窗 closePoiRemindModal() { this.setData({ showPoiRemindModal: false }); this.updatePoiRemindCircle(0); // 隐藏圆圈 }, // 拖动滑动条时更新范围和圆圈 onPoiRemindRadiusChange(e) { const radius = Number(e.detail.value); this.setData({ poiRemindRadius: radius }); this.updatePoiRemindCircle(radius); // 地图相机包含提醒范围(自适应padding) const poi = this.data.lastPoiForRemind; if (poi) { this.fitMapToCircle(poi.latitude, poi.longitude, radius); } }, // 更新地图上的圆圈覆盖物 updatePoiRemindCircle(radius) { const poi = this.data.poiDetail || this.data.lastPoiForRemind; if (!poi || !radius) { this.setData({ circles: [] }); return; } this.setData({ circles: [{ latitude: poi.latitude, longitude: poi.longitude, color: '#667eea88', fillColor: '#667eea22', radius: radius, strokeWidth: 2 }], lastPoiForRemind: poi }); }, // 刷新数据 onRefresh() { wx.showToast({ title: '刷新中...', icon: 'loading', duration: 1000 }); // 重新获取位置和好友数据 this.getCurrentLocation(); // 如果已有位置信息,直接刷新其他数据 if (this.data.myLocation) { this.loadWeatherInfo(this.data.myLocation.latitude, this.data.myLocation.longitude); } }, // 关闭用户详情卡片 closeUserCard() { this.setData({ selectedUser: null }); }, // 开始聊天 startChat(e) { const userId = e.currentTarget.dataset.userid; const niackName = e.currentTarget.dataset.nickname; // 获取当前用户信息 const userInfo = this.data.userInfo; if (!userInfo) { wx.showToast({ title: '用户信息错误', icon: 'none' }); return; } // 获取当前用户ID const currentUserId = userInfo.user?.customId || userInfo.customId || ''; if (!currentUserId) { wx.showToast({ title: '用户ID错误', icon: 'none' }); return; } // 构建会话ID(单聊格式) // 🔥 修复:不传递conversationId,让聊天页面从API获取正确的会话ID // 跳转到聊天页面,使用正确的路径和参数格式 wx.navigateTo({ url: `/pages/message/chat/chat?targetId=${userId}&name=${encodeURIComponent(niackName)}&chatType=0&sendMessage=${this.data.merchant}` }); }, // 添加好友 addFriend(e) { const userId = e.currentTarget.dataset.userid; // 实现添加好友逻辑 wx.showModal({ title: '添加好友', content: '确定要添加这位用户为好友吗?', success: (res) => { if (res.confirm) { // 调用添加好友API this.performAddFriend(userId); } } }); }, // 执行添加好友 async performAddFriend(userId) { try { const response = await this.apiClient.addFriend(userId, '通过地图添加'); if (response.code === 0) { wx.showToast({ title: '好友请求已发送', icon: 'success' }); this.closeUserCard(); } else { throw new Error(response.message || '添加好友失败'); } } catch (error) { console.error('添加好友失败:', error); wx.showToast({ title: error.message || '添加好友失败', icon: 'none' }); } }, // 查看用户资料 viewProfile(e) { const userId = e.currentTarget.dataset.userid; // 跳转到用户资料页面 wx.navigateTo({ url: `/pages/user-profile/user-profile?userId=${userId}` }); }, // 获取天气信息 async loadWeatherInfo(latitude, longitude) { try { // 🔥 优化:优先使用第三方API获取天气信息,不依赖登录状态 console.debug('🌤️ 开始获取天气信息'); // 1. 首先尝试通过腾讯地图API直接获取真实天气信息 let weatherData = await this.tryGetRealWeatherInfo(latitude, longitude); // 2. 如果第三方API获取失败,再尝试调用服务器天气API if (!weatherData) { const response = await this.apiClient.getWeatherInfo(latitude, longitude); if (response && response.code === 0 && response.data) { weatherData = response.data; } else { } } if (weatherData) { // 转换天气图标 const weatherIcon = this.getWeatherIcon(weatherData.weather, weatherData.icon); // 判断是否为云彩图标 const isCloudIcon = this.isCloudWeatherIcon(weatherData.weather, weatherData.icon, weatherIcon); const weatherInfo = { temperature: Math.round(weatherData.temperature), weather: weatherData.weather, icon: weatherIcon, isCloudIcon: isCloudIcon, humidity: weatherData.humidity, windSpeed: weatherData.windSpeed, windDir: weatherData.windDir, aqi: weatherData.aqi, city: '', // 不显示城市信息,只使用区级信息 updateTime: weatherData.updateTime }; // 🔥 重要修复:使用天气接口返回的准确位置信息更新左上角显示 if (weatherData.city) { // 天气接口返回的城市信息是最准确的(基于坐标查询的) // 优先使用天气接口的位置信息更新左上角显示 let newDistrict = this.data.currentDistrict; // 如果当前区域信息不准确或缺失,使用天气接口的城市信息 if (!this.data.currentDistrict || this.data.currentDistrict === '定位中...' || this.data.currentDistrict === '位置信息' || this.data.currentDistrict.includes('位置 ')) { newDistrict = weatherData.city; } // 更新区域显示信息 this.setData({ currentCity: '', // 不显示城市信息,只使用区级信息 // currentDistrict: newDistrict, weatherInfo: weatherInfo }); } else { // 没有城市信息时,只更新天气信息 this.setData({ weatherInfo: weatherInfo }); } console.debug('✅ 天气信息获取成功:', weatherInfo); } else { // 提供默认天气信息 await this.provideDefaultWeatherInfo(latitude, longitude); } } catch (error) { console.error('❌ 获取天气信息失败:', error); // 🔥 新增:提供默认天气信息,确保用户始终能看到天气数据 await this.provideDefaultWeatherInfo(latitude, longitude); } }, // 提供默认天气信息 async provideDefaultWeatherInfo(latitude, longitude) { // 尝试获取真实天气信息 let defaultWeatherInfo = null; try { // 尝试使用腾讯地图API的天气服务获取真实天气数据 const weatherResponse = await new Promise((resolve, reject) => { wx.request({ url: 'https://apis.map.qq.com/ws/weather/v1/', data: { location: `${latitude},${longitude}`, key: 'OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77' }, success: resolve, fail: reject }); }); // 处理天气API响应 if (weatherResponse && weatherResponse.data && weatherResponse.data.status === 0 && weatherResponse.data.result) { const weatherData = weatherResponse.data.result; const currentWeather = weatherData.weather[0]; // 构建真实天气信息 defaultWeatherInfo = { temperature: parseInt(currentWeather.temp, 10), weather: currentWeather.weather, icon: this.getWeatherIcon(currentWeather.weather, ''), isCloudIcon: this.isCloudWeatherIcon(currentWeather.weather, '', ''), humidity: currentWeather.humidity, windSpeed: currentWeather.wind.degree, windDir: currentWeather.wind.direction, aqi: weatherData.aqi, // 空气质量指数 city: '', // 不显示城市信息,只使用区级信息 updateTime: new Date().toLocaleTimeString() }; console.debug('✅ 成功获取真实天气信息:', defaultWeatherInfo); } else { console.warn('⚠️ 腾讯地图天气API返回数据异常'); } } catch (error) { console.error('❌ 获取真实天气信息失败:', error); } // 如果无法获取真实天气信息,才使用随机数据作为备选 if (!defaultWeatherInfo) { const randomTemp = Math.floor(Math.random() * 20) + 15; // 15-35度 const randomHumidity = Math.floor(Math.random() * 40) + 40; // 40-80% const randomWindSpeed = Math.floor(Math.random() * 10) + 1; // 1-11级 const weatherConditions = ['晴', '多云', '阴', '小雨', '中雨', '大雨']; const randomWeather = weatherConditions[Math.floor(Math.random() * weatherConditions.length)]; // 获取天气图标 const weatherIcon = this.getWeatherIcon(randomWeather, ''); const isCloudIcon = this.isCloudWeatherIcon(randomWeather, '', weatherIcon); // 构建默认天气信息 defaultWeatherInfo = { temperature: randomTemp, weather: randomWeather, icon: weatherIcon, isCloudIcon: isCloudIcon, humidity: randomHumidity, windSpeed: randomWindSpeed, windDir: '东北风', aqi: 60, city: '', // 不显示城市信息,只使用区级信息 updateTime: new Date().toLocaleTimeString() }; } // 确保区域信息不为空 let districtToShow = this.data.currentDistrict; // 先设置一个默认的占位文本,避免显示坐标 if (!districtToShow || districtToShow === '定位中...' || districtToShow.includes('位置 ') || districtToShow === '地图' || districtToShow === '默认') { // 立即设置一个友好的占位符,避免显示坐标 this.setData({ currentDistrict: '定位中...' }); } // 尝试通过API获取真实的地址信息,无论是否登录 this.tryGetRealLocationInfo(latitude, longitude).then((realLocationInfo) => { if (realLocationInfo && realLocationInfo.city) { console.debug('✅ 成功获取真实地址信息:', realLocationInfo); districtToShow = realLocationInfo.district || realLocationInfo.city; } else { // 如果区域信息仍为空,使用坐标推断的位置 if (!districtToShow || districtToShow === '定位中...' || districtToShow.includes('位置 ') || districtToShow === '地图' || districtToShow === '默认') { // 或者尝试其他方式获取位置信息 districtToShow = '北京市'; } } // 先设置天气信息 this.setData({ weatherInfo: defaultWeatherInfo }); // 对于登录用户,保留从服务器获取的真实地址信息 // 直接从全局获取登录状态,避免页面数据同步不及时的问题 const app = getApp(); const isLoggedIn = app?.globalData?.isLoggedIn || false; if (isLoggedIn && this.data.currentDistrict && this.data.currentDistrict !== '定位中...' && !this.data.currentDistrict.includes('位置 ') && this.data.currentDistrict !== '地图' && this.data.currentDistrict !== '默认') { console.debug('✅ 登录用户,保留服务器返回的真实地址信息:', this.data.currentDistrict); return; } // 更新数据 this.setData({ currentCity: '' // 不显示城市信息,只显示区级信息 // currentDistrict: districtToShow }); console.debug('✅ 默认天气信息已提供:', defaultWeatherInfo); }).catch((error) => { console.error('❌ 获取真实地址信息失败:', error); // 如果区域信息仍为空,使用坐标推断的位置 if (!districtToShow || districtToShow === '定位中...' || districtToShow.includes('位置 ') || districtToShow === '地图' || districtToShow === '默认') { // 使用更具体的默认位置 districtToShow = '北京市'; // 不设置城市信息,只使用区级信息 defaultWeatherInfo.city = ''; } // 先设置天气信息 this.setData({ weatherInfo: defaultWeatherInfo }); // 对于登录用户,保留从服务器获取的真实地址信息 // 直接从全局获取登录状态,避免页面数据同步不及时的问题 const app = getApp(); const isLoggedIn = app?.globalData?.isLoggedIn || false; if (isLoggedIn && this.data.currentDistrict && this.data.currentDistrict !== '定位中...' && !this.data.currentDistrict.includes('位置 ') && this.data.currentDistrict !== '地图' && this.data.currentDistrict !== '默认') { console.debug('✅ 登录用户,保留服务器返回的真实地址信息:', this.data.currentDistrict); return; } // 更新数据 this.setData({ currentCity: '', // 不显示城市信息,只使用区级信息 // currentDistrict: districtToShow }); }); }, async tryGetRealWeatherInfo(latitude, longitude) { return null; }, // 尝试获取真实的位置信息 tryGetRealLocationInfo(latitude, longitude) { return new Promise((resolve, reject) => { const key = config.amapKey; // 注意:需单独申请Web服务Key(与小程序Key不同) wx.request({ url: baseAmapUrl + `/geocode/regeo`, data: { location: `${longitude},${latitude}`, key: key, radius: 1000 }, success: (res) => { if (res.data.status === '1') { const addressComponent = res.data.regeocode.addressComponent; const locationInfo = { province: addressComponent.province || '', city: addressComponent.city || '', district: addressComponent.district || '', address: res.data.regeocode.formatted_address || '' }; resolve(locationInfo); } else { reject(new Error('解析失败:' + res.data.info)); } }, fail: reject }); }); }, // 根据天气状况获取对应的表情符号 getWeatherIcon(weather, iconCode) { // 天气图标映射 const weatherIcons = { '晴': '☀️', '多云': '⛅', '阴': '☁️', '小雨': '🌦️', '中雨': '🌧️', '大雨': '⛈️', '雷雨': '⛈️', '雪': '❄️', '小雪': '🌨️', '大雪': '❄️', '雾': '🌫️', '霾': '😷', '沙尘': '🌪️' }; // 根据iconCode映射 const iconMapping = { 'clear-day': '☀️', 'clear-night': '🌙', 'partly-cloudy-day': '⛅', 'partly-cloudy-night': '☁️', 'cloudy': '☁️', 'rain': '🌧️', 'sleet': '🌨️', 'snow': '❄️', 'wind': '💨', 'fog': '🌫️' }; let finalIcon = '🌤️'; // 优先使用iconCode映射,其次使用天气描述映射 if (iconCode && iconMapping[iconCode]) { finalIcon = iconMapping[iconCode]; } else if (weather && weatherIcons[weather]) { finalIcon = weatherIcons[weather]; } return finalIcon; }, // 判断是否为云彩相关图标(需要变成黑色) isCloudWeatherIcon(weather, iconCode, icon) { // 云彩相关的天气状况 const cloudWeatherTypes = ['多云', '阴', '雾', '霾']; // 云彩相关的iconCode const cloudIconCodes = ['partly-cloudy-day', 'partly-cloudy-night', 'cloudy', 'fog']; // 云彩相关的emoji const cloudEmojis = ['⛅', '☁️', '🌫️']; // 检查各种条件 if (weather && cloudWeatherTypes.includes(weather)) { return true; } if (iconCode && cloudIconCodes.includes(iconCode)) { return true; } if (icon && cloudEmojis.includes(icon)) { return true; } return false; }, // 获取附近陌生人用户(新增功能) async loadNearbyUsers() { try { const myLocation = this.data.myLocation; if (!myLocation) { return; } console.debug('👥 开始获取附近用户'); const [region, scaleResult] = await Promise.all([ this.getMapRegion(), this.getMapScale() ]); let scale = Math.round(scaleResult?.scale || this.data.scale || 13); const payload = buildBoundsPayload(region); if (scale > 20) { scale = 20; } const friendsAndStrangerSprams = { "bounds": payload.bounds, "onlineMode": "all", "zoomLevel": scale, "displayMode": "strangers", // "maxPoints": 200, "clusterThreshold": this.data.clusterThreshold, // "onlineMode": "all", // "showOffline": true, // "genderFilter": 0, // "ageRangeMin": 18, // "ageRangeMax": 35, // "memberOnly": false, // "enableRealTimeUpdate": false, "currentLat": this.data.myLocation.latitude, "currentLng": this.data.myLocation.longitude } let response = await this.apiClient.getFriendsAndStranger(friendsAndStrangerSprams); if (response && response.code === 0 && response.data) { if (this.data.isOnline && !this.data.mapFeedVisible) { this.addStrangerMarkersByResult(response.data); } } else { console.debug('📍 暂无附近用户'); this.nearbyUsersData = []; this.setData({ strangersCount: 0 }); if (this.data.isOnline) { // this.addStrangerMarkersData(); } } } catch (error) { console.error('❌ 获取附近用户失败:', error); this.nearbyUsersData = []; this.setData({ strangersCount: 0 }); if (this.data.isOnline) { // this.addStrangerMarkersData(); } } }, //添加陌生人数据 addStrangerMarkersByResult(responseStranger) { const userPoints = responseStranger.userPoints || []; const clusters = responseStranger.clusters || []; this.clearMarkersByType("stranger-cluster"); this.clearMarkersByType("stranger-single"); const strangerMarkers = userPoints.map((stranger, index) => { // 为每个标记生成唯一id和随机经纬度偏移 console.log("responseStranger--->single" + "lat--->" + stranger.latitude + "long--->" + stranger.longitude); let avatarUrl = '/images/default-stranger.png'; if (stranger.avatar) { if (stranger.avatar.includes('myqcloud')) { avatarUrl = stranger.avatar + '?imageMogr2/auto-orient/thumbnail/100x100/quality/85'; } else { avatarUrl = stranger.avatar } } const marker = { id: MAP_FEED_MARKER_STRANGER_ID + index, // 基于基础坐标添加随机偏移(确保经纬度不同) latitude: stranger.latitude, longitude: stranger.longitude, iconPath: marker_blank, type: 'stranger-single', customId: stranger.customId, clusterColor: '#FF0000', avatarUrl: avatarUrl, originAvatarUrl: stranger.avatar, merchantType: '', isOnline: stranger.isOnline, nickname: stranger.nickname, // 昵称加索引区分 battery: 100, width: 45, height: 45, radius: 380, joinCluster: false, title: stranger.nickname, customCallout: { anchorY: 50, anchorX: 0, display: 'ALWAYS' }, }; return marker; }); this.updateMarkersByMarkersType(strangerMarkers); const strangerClusterMarkers = clusters.map((stranger, index) => { console.log("responseStranger--->cluster" + "lat--->" + stranger.latitude + "long--->" + stranger.longitude); const marker = { // id: stranger.customId, id: MAP_FEED_CLUSTER_STRANGER_ID + index, latitude: stranger.latitude, longitude: stranger.longitude, iconPath: marker_blank, feedCount: stranger.onlineCount, customId: stranger.clusterId, type: 'stranger-cluster', badgeColor: MAP_FEED_MARKER_STYLE.stranger.background, badgeTextColor: MAP_FEED_MARKER_STYLE.stranger.color, battery: 100, customCallout: { anchorY: 50, anchorX: 0, display: 'ALWAYS' }, radius: 380, width: 45, height: 45, }; return marker; }); this.updateMarkersByMarkersType(strangerClusterMarkers); }, //添加好友数据 addFriendsMarkersByResult(responseStranger) { const userPoints = responseStranger.userPoints || []; const clusters = responseStranger.clusters || []; // 生成100个经纬度不同的标记 this.clearMarkersByType("friend-single"); this.clearMarkersByType("friend-cluster"); const friendMarkers = userPoints.map((stranger, index) => { // 为每个标记生成唯一id和随机经纬度偏移 let avatarUrl = '/images/default-stranger.png'; if (stranger.avatar) { if (stranger.avatar.includes('myqcloud')) { avatarUrl = stranger.avatar + '?imageMogr2/auto-orient/thumbnail/100x100/quality/85'; } else { avatarUrl = stranger.avatar } } const marker = { // id: stranger.customId, id: MAP_FEED_MARKER_FRIEND_ID + index, // 基于基础坐标添加随机偏移(确保经纬度不同) latitude: stranger.latitude, longitude: stranger.longitude, iconPath: marker_blank, type: 'friend-single', originAvatarUrl:stranger.avatar, customId: stranger.customId, clusterColor: '#FF0000', avatarUrl: avatarUrl, merchantType: '', isOnline: stranger.isOnline, nickname: stranger.nickname, // 昵称加索引区分 battery: 100, width: 45, height: 45, joinCluster: false, title: stranger.nickname, radius: 380, customCallout: { anchorY: 50, anchorX: 0, display: 'ALWAYS' }, }; return marker; }); this.updateMarkersByMarkersType(friendMarkers); const friendClusterMarkers = clusters.map((stranger, index) => { // 为每个标记生成唯一id和随机经纬度偏移 const marker = { // id: stranger.customId, id: MAP_FEED_CLUSTER_FRIEND_ID + index, latitude: stranger.latitude, longitude: stranger.longitude, iconPath: marker_blank, feedCount: stranger.onlineCount, customId: stranger.clusterId, type: 'friend-cluster', badgeColor: MAP_FEED_MARKER_STYLE.stranger.background, badgeTextColor: MAP_FEED_MARKER_STYLE.stranger.color, battery: 100, customCallout: { anchorY: 50, anchorX: 0, display: 'ALWAYS' }, radius: 380, width: 45, height: 45, }; return marker; }); this.updateMarkersByMarkersType(friendClusterMarkers); }, // 生成0.001到0.01之间的随机数(复用之前的方法) getRandomNumber() { const min = 0.001; const max = 0.01; return min + Math.random() * (max - min); }, // 生成随机偏移量(可正负,扩大差异范围) getRandomOffset() { const min = -1.0; // 负偏移,扩大范围 const max = 1.0; return min + Math.random() * (max - min); }, updateMarkersByMarkersType(toBeUpdateMarkers = []) { if (toBeUpdateMarkers.length == 0) { return; } // 验证标记点数据 toBeUpdateMarkers.forEach((newMarker, index) => { // 确保每个标记点都有必要的属性 if (!newMarker.width || !newMarker.height) { console.warn(`标记点 ${newMarker.id} 缺少尺寸属性,使用默认值`); newMarker.width = newMarker.width || 45; newMarker.height = newMarker.height || 45; } if (!newMarker.id) { console.error(`第 ${index} 个标记点缺少 id 属性`); newMarker.id = Date.now() + index; // 生成临时 ID } }); // console.log('进入updateMarkers方法,参数:', JSON.stringify(toBeUpdateMarkers)); // console.log('allMarkers:', JSON.stringify(this.data.allMarkers)); let newMarkers = []; newMarkers = this.data.allMarkers; toBeUpdateMarkers.forEach(newMarker => { // 查找是否已有相同id的marker const existingIndex = this.data.allMarkers.findIndex(marker => marker.id === newMarker.id); if (existingIndex > -1) { // 存在:替换该位置的marker(更新所有属性) // this.data.allMarkers[existingIndex] = newMarker; newMarkers[existingIndex] = newMarker } else { // 不存在:添加到数组 // this.data.allMarkers.push(newMarker); newMarkers.push(newMarker); } }); console.log('最终显示的markers:', JSON.stringify(newMarkers)); this.setData({ allMarkers: newMarkers }); this.setData({ markers: [] }, () => { this.setData({ markers: newMarkers }); }); }, // 生成0.001到0.01之间的随机数(复用之前的方法) getRandomNumber() { const min = 0.001; const max = 0.01; return min + Math.random() * (max - min); }, // 生成随机偏移量(可正负,扩大差异范围) getRandomOffset() { const min = -1.0; // 负偏移,扩大范围 const max = 1.0; return min + Math.random() * (max - min); }, // 偶遇模式开关 onDynamicToggle(e) { if (this.data.mapFeedVisible) { wx.showToast({ title: '请先关闭动态', icon: 'none', duration: 1200 }); return; } this.setData({ isOnline: !this.data.isOnline }); if (this.data.isOnline) { this.clearMarkersByType("friend-single"); this.clearMarkersByType("friend-cluster"); this.loadNearbyUsers(); this.setData({ markers: this.data.allMarkers }); } else { this.clearStranger(); this.loadFriendsLocation() } }, //清除陌生人数据 clearStranger() { //关闭偶遇时 删除陌生人数据 this.data.allMarkers = this.data.allMarkers.filter(item => { // 返回 true 表示保留该元素,false 表示删除 return item.type !== "stranger-single"; // 这里替换为你的判断条件 }); this.data.allMarkers = this.data.allMarkers.filter(item => { // 返回 true 表示保留该元素,false 表示删除 return item.type !== "stranger-cluster"; // 这里替换为你的判断条件 }); this.mapStrangerMarkerLookup.clear(); this.setData({ markers: this.data.allMarkers }); }, clearMarkersByType(type) { //关闭偶遇时 删除陌生人数据 this.data.allMarkers = this.data.allMarkers.filter(item => { // 返回 true 表示保留该元素,false 表示删除 return item.type !== type; // 这里替换为你的判断条件 }); // this.setData({ // markers: this.data.allMarkers // }); }, //清除好友数据 clearFriend() { //关闭偶遇时 删除陌生人数据 this.data.allMarkers = this.data.allMarkers.filter(item => { // 返回 true 表示保留该元素,false 表示删除 return item.type !== "friend-single"; // 这里替换为你的判断条件 }); this.data.allMarkers = this.data.allMarkers.filter(item => { // 返回 true 表示保留该元素,false 表示删除 return item.type !== "friend-cluster"; // 这里替换为你的判断条件 }); this.mapStrangerMarkerLookup.clear(); this.setData({ markers: this.data.allMarkers }); }, // 初始化静态数据 initStaticData() { // 模拟静态数据,实际项目中可能从API获取 const staticData = [{ id: 'static1', title: '热门景点', description: '游客推荐', latitude: 44.897383746906186 + 0.005, longitude: 82.07800276534772 + 0.005, imageUrl: '/images/map/marker_lan.png' }, { id: 'static2', title: '美食推荐', description: '当地特色', latitude: 44.897383746906186 - 0.005, longitude: 82.07800276534772 + 0.005, imageUrl: '/images/map/marker_hong.png' }, { id: 'static3', title: '购物中心', description: '购物天堂', latitude: 44.897383746906186 + 0.005, longitude: 82.07800276534772 - 0.005, imageUrl: '/images/map/marker_yule.png' } ]; this.setData({ staticDataMarkers: staticData }); }, // 初始化合作商家标记 initMerchantMarkers() { try { // 获取所有商家数据 const merchants = getAllMerchants(); // 将商家数据转换为marker格式 const merchantMarkers = convertMerchantsToMarkers(merchants); // 保存商家列表到data中(用于点击事件查找) this.setData({ merchantList: merchants }); // 将商家markers添加到地图 if (merchantMarkers && merchantMarkers.length > 0) { this.updateMarkersByMarkersType(merchantMarkers); console.log('✅ 已加载', merchantMarkers.length, '个合作商家标记'); } } catch (error) { console.error('❌ 初始化商家标记失败:', error); } }, scheduleMapFeedRefresh(trigger = 'auto') { if (!this.mapCtx) { return; } if (!this.data.mapFeedVisible) { if (this.mapFeedDebounceTimer) { clearTimeout(this.mapFeedDebounceTimer); this.mapFeedDebounceTimer = null; } return; } if (this.mapFeedDebounceTimer) { clearTimeout(this.mapFeedDebounceTimer); } this.mapFeedDebounceTimer = setTimeout(() => { this.refreshMapFeeds(trigger); }, MAP_FEED_DEBOUNCE); }, async refreshMapFeeds(trigger = 'auto') { if (!this.mapCtx || !this.apiClient) { return; } if (!this.data.mapFeedVisible) { return; } if (!this.mapFeedCache) { this.mapFeedCache = new Map(); } try { const [region, scaleResult] = await Promise.all([ this.getMapRegion(), this.getMapScale() ]); const scale = scaleResult?.scale || this.data.scale || 13; const preset = getFeedScalePreset(scale); const payload = buildBoundsPayload(region); if (!payload) { console.warn('⚠️ 地图区域数据无效,无法请求地图动态'); return; } console.log("preset---->" + preset.mode); console.log("scale---->" + scale); if (preset.mode === 'nearby') { payload.maxResults = 50; } else { payload.level = preset.level; payload.maxResults = 80; } const cacheKey = buildCacheKey(preset, payload); const cachedEntry = this.mapFeedCache.get(cacheKey); this.setData({ mapFeedLoading: true, mapFeedError: '' }); let response; if (preset.mode === 'nearby') { response = await this.apiClient.getMapFeedsNearby(payload); } else { response = await this.apiClient.getMapFeedsAdministrative(payload); } if (!response || response.code !== 200 || !response.data) { throw new Error(response?.message || '地图动态加载失败'); } const isDataSame = JSON.stringify(this.data.cacheEntryString) === JSON.stringify(response.data); this.setData({ cacheEntryString: response.data }); this.mapFeedCache.set(cacheKey, { data: response.data, preset, timestamp: Date.now() }); if (isDataSame) { return; } if (preset.mode === 'nearby') { this.addDynamicMarkersByResult(response.data); } else { this.addDynamicClusterByResult(response.data); } } catch (error) { console.error('❌ 地图动态刷新失败:', error); const errMsg = error?.errMsg || error?.message || ''; const isTransient = errMsg.includes('getRegion') || errMsg.includes('getScale'); if (isTransient && this.data.mapFeedVisible) { setTimeout(() => { this.scheduleMapFeedRefresh('retry'); }, 600); } this.setData({ mapFeedLoading: false, mapFeedError: isTransient ? '' : (errMsg || '地图动态加载失败') }); } }, cropImageToCenter(imagePath) { return new Promise((resolve, reject) => { const TARGET_CROP_W = 87; const TARGET_CROP_H = 77; const RADIUS = 10; const SAFE_RADIUS = Math.min(RADIUS, TARGET_CROP_W / 2, TARGET_CROP_H / 2); if (typeof imagePath !== 'string' || !imagePath.trim()) { return reject(new Error('图片路径不能为空')); } wx.getImageInfo({ src: imagePath, success: (imgInfo) => { const { width: ORI_W, height: ORI_H, path: ORI_LOCAL_PATH } = imgInfo; // 计算等比缩放比例 const targetRatio = TARGET_CROP_W / TARGET_CROP_H; const imageRatio = ORI_W / ORI_H; let scaledW, scaledH; if (imageRatio > targetRatio) { scaledH = TARGET_CROP_H; scaledW = (ORI_W * TARGET_CROP_H) / ORI_H; } else { scaledW = TARGET_CROP_W; scaledH = (ORI_H * TARGET_CROP_W) / ORI_W; } // 计算裁剪中心点 const centerX = (scaledW - TARGET_CROP_W) / 2; const centerY = (scaledH - TARGET_CROP_H) / 2; const cropX = Math.max(0, centerX); const cropY = Math.max(0, centerY); const query = wx.createSelectorQuery(); query.select('#cropCanvas') .fields({ node: true, size: true }) .exec((res) => { if (!res[0] || !res[0].node) { return reject(new Error('未找到Canvas画布')); } const canvas = res[0].node; const ctx = canvas.getContext('2d'); const dpr = wx.getSystemInfoSync().pixelRatio; // 正确设置Canvas尺寸 canvas.width = TARGET_CROP_W * dpr; canvas.height = TARGET_CROP_H * dpr; ctx.scale(dpr, dpr); ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; const image = canvas.createImage(); image.src = ORI_LOCAL_PATH; image.onload = () => { try { ctx.clearRect(0, 0, TARGET_CROP_W, TARGET_CROP_H); // 创建圆角裁剪路径 ctx.beginPath(); this.createRoundedPath(ctx, 0, 0, TARGET_CROP_W, TARGET_CROP_H, SAFE_RADIUS); ctx.clip(); // 绘制图片 ctx.drawImage(image, -cropX, -cropY, scaledW, scaledH); // 导出图片 wx.canvasToTempFilePath({ canvas: canvas, destWidth: TARGET_CROP_W * dpr, destHeight: TARGET_CROP_H * dpr, fileType: 'png', quality: 1, success: (res) => resolve(res.tempFilePath), fail: (err) => reject(new Error(`导出失败:${err.errMsg}`)) }); } catch (e) { reject(new Error(`绘制失败:${e.message}`)); } }; image.onerror = () => reject(new Error('图片加载失败')); }); }, fail: (err) => reject(new Error(`获取图片信息失败:${err.errMsg}`)) }); }); }, // 圆角路径创建函数 createRoundedPath(ctx, x, y, width, height, radius) { ctx.beginPath(); ctx.moveTo(x + radius, y); ctx.arcTo(x + width, y, x + width, y + height, radius); ctx.arcTo(x + width, y + height, x, y + height, radius); ctx.arcTo(x, y + height, x, y, radius); ctx.arcTo(x, y, x + width, y, radius); ctx.closePath(); }, /** * 小程序版 - 动态添加Marker(保留你的原始逻辑,仅适配裁剪方法) */ async addDynamicMarkersByResult(responseStranger) { console.log("responseStranger----->" + JSON.stringify(responseStranger)); const userPoints = responseStranger.points || []; const friendMarkers = userPoints.map(async (stranger, index) => { // 注意async let thumbnail = "/images/findme-logo.png"; let avatarUrl = ''; try { if (stranger.thumbnail && stranger.thumbnail.includes('myqcloud')) { // 清理腾讯云原有缩放参数(保留你的原始逻辑) const cleanImageUrl = stranger.thumbnail.replace("thumbnail=300x300", ""); // 调用小程序版裁剪方法 thumbnail = await this.cropImageToCenter(stranger.thumbnail); } else { avatarUrl = stranger.thumbnail; // 保留你的原始逻辑 } if (avatarUrl) { thumbnail = marker_blank; // 保留你的原始逻辑(需确保marker_blank已定义) } } catch (error) { // 裁剪失败兜底(保留你的原始逻辑) console.error(`第${index}个marker裁剪失败:`, error); thumbnail = "/images/findme-logo.png"; } console.log("thumbnail-->" + thumbnail); // 保留你原始的marker结构,仅调整宽高为86/77 const marker = { id: Number(MAP_FEED_SINGLE_BASE_ID + index), // 需确保MAP_FEED_SINGLE_BASE_ID已定义 latitude: stranger.location.latitude, longitude: stranger.location.longitude, iconPath: thumbnail ? thumbnail : "/images/findme-logo.png", type: 'map-feed-single', feedUuid: stranger.feedUuid, likeCount: stranger.likeCount, commentCount: stranger.commentCount, clusterColor: '#FF0000', avatarUrl: avatarUrl, originalUrl: stranger.thumbnail ? stranger.thumbnail : "/images/findme-logo.png", merchantType: '', zIndex: 10, battery: 100, width: 87, // 裁剪宽度 height: 77, // 裁剪高度 imageLoaded: false, radius: 380, joinCluster: false, customCallout: { anchorY: 86, anchorX: 0, display: 'ALWAYS' }, }; return marker; }); // 等待所有marker异步处理完成 const resolvedMarkers = await Promise.all(friendMarkers); // 保留你原始的清空/更新逻辑 this.clearMarkersByType("map-feed-single"); this.clearMarkersByType("map-feed-cluster"); this.updateMarkersByMarkersType(resolvedMarkers); }, //添加区城市省级国家级动态数据 addDynamicClusterByResult(responseStranger) { const userPoints = responseStranger.userPoints || []; const clusters = responseStranger.clusters || []; const friendMarkers = userPoints.map((stranger, index) => { // 为每个标记生成唯一id和随机经纬度偏移 const marker = { id: Number(MAP_FEED_SINGLE_BASE_ID + index), // 基于基础坐标添加随机偏移(确保经纬度不同) latitude: stranger.latitude, longitude: stranger.longitude, iconPath: marker_blank, type: 'map-feed-single', customId: stranger.customId, clusterColor: '#FF0000', avatarUrl: stranger.thumbnail ? stranger.thumbnail : '/images/findme-logo.png', merchantType: '', isOnline: stranger.isOnline, nickname: stranger.nickname, // 昵称加索引区分 battery: 100, width: 45, height: 45, radius: 380, joinCluster: false, title: stranger.nickname, customCallout: { anchorY: 50, anchorX: 0, display: 'ALWAYS' }, }; return marker; }); this.updateMarkersByMarkersType(friendMarkers); const friendClusterMarkers = clusters.filter(stranger => stranger.count !== 1) // 过滤掉 count 为 1 的数据 // const friendClusterMarkers = clusters // 过滤掉 count 为 1 的数据 .map((stranger, index) => { const marker = { // id: stranger.customId, id: Number(MAP_FEED_CLUSTER_BASE_ID + index), latitude: stranger.location.latitude, longitude: stranger.location.longitude, iconPath: marker_blank, feedCount: stranger.count, customId: stranger.id, type: 'map-feed-cluster', badgeColor: MAP_FEED_MARKER_STYLE.cluster.background, badgeTextColor: MAP_FEED_MARKER_STYLE.cluster.color, battery: 100, customCallout: { anchorY: 50, anchorX: 0, display: 'ALWAYS' }, radius: 380, width: 45, height: 45, }; return marker; }); // if (friendClusterMarkers.length > 0) { this.clearMarkersByType("map-feed-single"); this.clearMarkersByType("map-feed-cluster"); // } this.updateMarkersByMarkersType(friendClusterMarkers); }, // 图片预览 previewImage(e) { try { // 获取当前点击的图片索引和动态索引 const originalurl = e.currentTarget.dataset.originalurl; // 优先使用原始图片URL或高质量图片,确保预览完整清晰的图片 const imageUrls = [originalurl]; // 🔥 设置标志,防止预览关闭后触发页面刷新 this._skipNextOnShowReload = true; console.log("originalurl--->"+imageUrls); // 调用微信小程序的图片预览API wx.previewImage({ current: imageUrls[0], // 当前显示图片的URL urls: imageUrls, // 需要预览的图片URL列表 success: () => { // 预览打开成功,标志已设置,关闭时会触发 onShow }, fail: (err) => { console.error('图片预览失败:', err); // 预览失败,清除标志 this._skipNextOnShowReload = false; wx.showToast({ title: '预览图片失败', icon: 'none' }); } }); } catch (error) { console.error('图片预览过程中出错:', error); // 出错时清除标志 this._skipNextOnShowReload = false; wx.showToast({ title: '预览图片失败', icon: 'none' }); } }, // 触摸开始 onTouchStart(e) { this.setData({ startY: e.touches[0].clientY, isMoving: true }); }, // 触摸移动 onTouchMove(e) { if (!this.data.isMoving) return; const moveY = e.touches[0].clientY; const diff = moveY - this.data.startY; // 只允许向下滑动 if (diff > 0) { this.setData({ modalTranslateY: diff }); } }, // 触摸结束 onTouchEnd() { // 滑动距离超过50px则关闭弹窗 if (this.data.modalTranslateY > 50) { if(this.data.searchResultNavigationVisible==true){ this.handleHideBottmNavigation(); this.setData({ modalTranslateY: 0, isMoving: false }); return; } if(this.data.shareShareVisible==true){ this.handleHideShareBottm(); this.setData({ modalTranslateY: 0, isMoving: false }); return; } if(this.data.searchResultInfoModalVisible==true){ this.handleHideBottm(); } } else { // 否则复位 this.setData({ modalTranslateY: 0, isMoving: false }); } }, //开启动态 enableMapFeedView() { this.closeUserCard(); this.hideFeedBubble(); this.setData({ mapFeedVisible: true, mapFeedError: '' }, () => { // 更新地图动态数据标记 this.scheduleMapFeedRefresh('manual'); }); wx.showToast({ title: '已开启附近动态', icon: 'none', duration: 1200 }); }, disableMapFeedView(showToast = true) { if (this.mapFeedDebounceTimer) { clearTimeout(this.mapFeedDebounceTimer); this.mapFeedDebounceTimer = null; } this.hideFeedBubble(); if (this.mapFeedMarkerLookup) { this.mapFeedMarkerLookup.clear(); } this.setData({ mapFeedVisible: false, mapFeedMarkers: [], mapFeedLoading: false, mapFeedError: '' }, () => { this.data.allMarkers = this.data.allMarkers.filter(item => { // 返回 true 表示保留该元素,false 表示删除 return item.type !== "map-feed-single"; // 这里替换为你的判断条件 }); this.data.allMarkers = this.data.allMarkers.filter(item => { // 返回 true 表示保留该元素,false 表示删除 return item.type !== "map-feed-cluster"; // 这里替换为你的判断条件 }); this.setData({ markers: this.data.allMarkers }); }); if (showToast) { wx.showToast({ title: '已关闭附近动态', icon: 'none', duration: 1200 }); } }, //关闭或开启动态 toggleMapFeedView() { if (this.data.mapFeedVisible) { this.disableMapFeedView(true); if (this.data.isOnline) { this.loadNearbyUsers(); } if (!this.data.isOnline) { this.loadFriendsLocation(); } return; } if (!this.ensureLoggedInForAction()) { return; } this.clearStranger(); this.clearFriend(); this.enableMapFeedView(); }, showFeedBubble(bubble) { this.setData({ activeFeedBubble: bubble, activeFeedBubbleLoading: false }); }, hideFeedBubble() { this.setData({ activeFeedBubble: null, activeFeedBubbleSingle: null, activeFeedBubbleLoading: false, activeFeedSingleVisible: false }); }, async handleMapFeedMarkerTap(markerMeta) { if (!markerMeta) { return; } // const markerMeta = this.mapFeedMarkerLookup?.get(marker.id); // if (!markerMeta) { // wx.showToast({ // title: '内容已更新,请重试', // icon: 'none' // }); // return; // } if (markerMeta.type === 'map-feed-single') { let response = await this.apiClient.getFeedsDetail(markerMeta.feedUuid); let nickName = ''; let content = ''; let avatar = ''; if (response.data) { nickName = response.data.user.nickname; content = response.data.content; avatar = response.data.user.avatar; let formatted_time = this.formatCommentTime(response.data.createdAt); const baseBubble = buildBubbleData({ type: markerMeta.type, title: markerMeta.payload?.name || '附近热力区', nickname: nickName, avatar: avatar, content: content, createdAt: formatted_time, thumbnail: markerMeta.originalUrl, location: markerMeta.location, tone: 'sunset' }); this.setData({ activeFeedBubbleSingle: baseBubble, activeFeedSingleVisible: true }); } return; } const radius = markerMeta.location?.radius || 320; const countLabel = formatFeedCountLabel(markerMeta.payload?.count || 0); const baseBubble = buildBubbleData({ type: markerMeta.type, title: markerMeta.payload?.name || '附近热力区', // subtitle: markerMeta.payload?.count ? `含 ${countLabel}` : formatRadiusLabel(radius), chip: countLabel, // heat: formatHeatLabel(markerMeta.payload?.hotScore), // thumbnail: resolveFeedThumbnail(markerMeta.payload), feeds: markerMeta.feedCount, location: markerMeta.location, thumbnail: markerMeta.avatarUrl, // radiusLabel: formatRadiusLabel(radius), tone: 'sunset' }); this.setData({ activeFeedBubble: baseBubble, activeFeedBubbleLoading: false }); // this.loadClusterPreview(markerMeta); }, // 格式化评论时间 formatCommentTime(timeStr) { if (!timeStr) return ''; const commentTime = new Date(timeStr); if (isNaN(commentTime.getTime())) return ''; const now = new Date(); const diffMinutes = Math.floor((now - commentTime) / (1000 * 60)); // 5分钟内:显示"刚刚" if (diffMinutes < 5) return '刚刚'; // 格式化日期时间 const year = commentTime.getFullYear(); const month = commentTime.getMonth() + 1; // 不加前导0,直接显示月份 const day = commentTime.getDate(); const hour = String(commentTime.getHours()).padStart(2, '0'); const minute = String(commentTime.getMinutes()).padStart(2, '0'); // 当前年份:显示不带年份的日期和时间,例如:8月24日 17:11 // 非当前年份:显示带年份的日期和时间,例如:2024年 8月24日 17:11 const currentYear = now.getFullYear(); return year === currentYear ? `${month}月${day}日 ${hour}:${minute}` : `${year}年 ${month}月${day}日 ${hour}:${minute}`; }, async loadClusterPreview(markerMeta) { if (!markerMeta || !markerMeta.payload) { return; } try { this.setData({ activeFeedBubbleLoading: true }); const response = await this.apiClient.getMapClusterFeeds({ location: { latitude: Number(markerMeta.payload?.location?.latitude), longitude: Number(markerMeta.payload?.location?.longitude) }, radius: 380, limit: MAP_FEED_BUBBLE_LIMIT }); if (!response || response.code !== 200) { throw new Error(response?.message || '加载聚合动态失败'); } const feeds = (response.data?.feeds || []).map((feed) => { const likeCount = feed.interactions?.likeCount || 0; const commentCount = feed.interactions?.commentCount || 0; let description = '刚刚更新'; if (likeCount > 0 || commentCount > 0) { description = `👍 ${likeCount} · 💬 ${commentCount}`; } return { uuid: feed.uuid, title: feed.location?.name || feed.user?.nickname || '附近动态', description, thumbnail: resolveFeedThumbnail({ thumbnail: feed.media?.[0]?.thumbnailUrl || feed.media?.[0]?.url, contentType: feed.type }), hotScore: feed.interactions?.likeCount || 0 }; }); const existingBubble = this.data.activeFeedBubble || {}; const radius = existingBubble.radius || 320; const clusterCount = markerMeta.payload?.count || feeds.length; const countLabel = formatFeedCountLabel(clusterCount); const bubble = buildBubbleData({ type: markerMeta.type, title: markerMeta.payload?.name || existingBubble.title || '附近热力区', subtitle: markerMeta.payload?.count ? `含 ${countLabel}` : (existingBubble.subtitle || formatRadiusLabel(radius)), chip: countLabel, heat: formatHeatLabel(markerMeta.payload?.hotScore), thumbnail: markerMeta.payload?.thumbnail || feeds[0]?.thumbnail || existingBubble.thumbnail, feeds, location: markerMeta.location, radius, radiusLabel: existingBubble.radiusLabel || formatRadiusLabel(radius), tone: existingBubble.tone || 'sunset' }); this.showFeedBubble(bubble); } catch (error) { console.error('加载聚合预览失败:', error); this.setData({ activeFeedBubbleLoading: false }); wx.showToast({ title: error.message || '加载失败', icon: 'none' }); } }, onFeedBubbleMoreTap(e) { if (!this.ensureLoggedInForAction()) { return; } const { lat, lng, radius } = e.currentTarget.dataset || {}; const bubble = this.data.activeFeedBubble || {}; const latitude = Number(lat || bubble.location?.latitude || this.data.latitude); const longitude = Number(lng || bubble.location?.longitude || this.data.longitude); const radiusValue = Number(radius || bubble.radius || 200); if (Number.isNaN(latitude) || Number.isNaN(longitude)) { wx.showToast({ title: '位置信息不可用', icon: 'none' }); return; } this.hideFeedBubble(); const redirectToken = Date.now(); const appInstance = getApp(); if (appInstance && appInstance.globalData) { appInstance.globalData.mapFeedRedirect = { latitude, longitude, radius: Math.max(50, Math.round(radiusValue)), feedUuid: bubble?.feeds?.[0]?.uuid || '', mode: 'map', token: redirectToken }; } wx.switchTab({ url: '/pages/circle/circle' }); }, onFeedBubbleItemTap(e) { if (!this.ensureLoggedInForAction()) { return; } const { uuid, lat, lng, radius } = e.currentTarget.dataset || {}; const bubble = this.data.activeFeedBubble || {}; const latitude = Number(lat || bubble.location?.latitude || this.data.latitude); const longitude = Number(lng || bubble.location?.longitude || this.data.longitude); const radiusValue = Number(radius || bubble.radius || 200); if (Number.isNaN(latitude) || Number.isNaN(longitude)) { wx.showToast({ title: '位置信息不可用', icon: 'none' }); return; } this.hideFeedBubble(); const redirectToken = Date.now(); const appInstance = getApp(); if (appInstance && appInstance.globalData) { appInstance.globalData.mapFeedRedirect = { latitude, longitude, radius: Math.max(50, Math.round(radiusValue)), feedUuid: uuid || '', mode: 'map', token: redirectToken }; } wx.switchTab({ url: '/pages/circle/circle' }); }, getMapRegion() { return new Promise((resolve, reject) => { if (!this.mapCtx) { reject(new Error('地图上下文未初始化')); return; } this.mapCtx.getRegion({ success: resolve, fail: reject }); }); }, getMapScale() { return new Promise((resolve, reject) => { if (!this.mapCtx) { reject(new Error('地图上下文未初始化')); return; } this.mapCtx.getScale({ success: resolve, fail: reject }); }); }, // 加载浮动UI相关数据 async loadFloatingUIData() { try { // 并行加载各种统计数据 await Promise.all([ this.loadUnreadMessageCount(), this.loadFriendRequestCount(), this.loadNearbyCount() ]); } catch (error) { console.error('加载浮动UI数据失败:', error); } }, // 加载未读消息数 async loadUnreadMessageCount() { try { // 这里应该调用消息API获取未读数 // const response = await chatAPI.getTotalUnreadCount(); // this.updateUnreadMessageCount(response.data?.count || 0); // 临时模拟数据 this.updateUnreadMessageCount(0); } catch (error) { console.error('加载未读消息数失败:', error); this.updateUnreadMessageCount(0); } }, // 加载好友请求数 async loadFriendRequestCount() { try { // 这里应该调用好友API获取请求数 // const response = await friendAPI.getFriendRequestCount(); // this.updateFriendRequestCount(response.data?.count || 0); // 临时模拟数据 this.updateFriendRequestCount(0); } catch (error) { console.error('加载好友请求数失败:', error); this.updateFriendRequestCount(0); } }, // 加载附近的人数量 async loadNearbyCount() { try { this.updateNearbyCount(this.data.strangersCount); } catch (error) { console.error('加载附近的人数量失败:', error); this.updateNearbyCount(0); } }, // 🔥 ===== 浮动UI事件处理 ===== // 跳转到个人主页 openProfile() { wx.navigateTo({ url: '/subpackages/profile/profile/profile' }); }, // 打开好友页面 openFriends() { wx.navigateTo({ url: '/pages/social/friends/friends' }); }, // 打开消息页面 openMessages() { wx.navigateTo({ url: '/pages/message/message' }); }, // 打开WebSocket测试页面(开发调试用) openWebSocketTest() { wx.navigateTo({ url: '/subpackages/dev-tools/websocket-test/websocket-test' }); }, // ===== 搜索功能相关方法 ===== // 显示搜索框 showSearchBox() { this.setData({ showSearchBox: true, searchKeyword: '' }); }, // 隐藏搜索框 hideSearchBox() { this.setData({ showSearchBox: false, searchKeyword: '' }); }, // 搜索输入处理 onSearchInput(e) { const keyword = e.detail.value; this.setData({ searchKeyword: keyword }); // 如果输入内容不为空,开始搜索 if (keyword.trim()) { // 模拟搜索延迟 setTimeout(() => { this.searchPlaces(keyword); }, 300); } else { // 清空搜索结果 this.setData({ searchResults: [] }); } }, // 搜索确认处理 onSearchConfirm() { const keyword = this.data.searchKeyword.trim(); if (keyword) { // 如果已有搜索结果,直接定位第一个结果 if (this.data.searchResults && this.data.searchResults.length > 0) { this.onSearchResultTap({ currentTarget: { dataset: { item: this.data.searchResults[0] } } }); } else { // 否则执行搜索 this.searchPlaces(keyword); wx.showToast({ title: `搜索: ${keyword}`, icon: 'none' }); } } }, // 搜索地点 searchPlaces(keyword) { // 显示加载提示 wx.showLoading({ title: '搜索中...', mask: true }); try { // 高德地图 const params = { key: config.amapKey, keywords: keyword, city: this.data.currentCity || '全国', citylimit: false, location: this.data.myLocation ? `${this.data.myLocation.longitude},${this.data.myLocation.latitude}` : '' }; // 调用高德地图API wx.request({ url: baseAmapUrl + '/assistant/inputtips', data: params, method: 'GET', header: { 'content-type': 'application/json' }, success: (res) => { wx.hideLoading(); if (res.statusCode === 200 && res.data && res.data.status === '1' && res.data.tips) { // 转换格式 const searchResults = res.data.tips.map((tip, index) => { // 从location中提取经纬度 let latitude = 0; let longitude = 0; if (tip.location && typeof tip.location === 'string' && tip.location.split(',').length === 2) { const [lng, lat] = tip.location.split(','); longitude = parseFloat(lng); latitude = parseFloat(lat); } // 根据类型设置不同的图标 let icon = '📍'; if (tip.name.includes('酒店')) icon = '🏨'; else if (tip.name.includes('餐厅')) icon = '🍽️'; else if (tip.name.includes('公园')) icon = '🌳'; else if (tip.name.includes('地铁')) icon = '🚇'; else if (tip.name.includes('购物中心') || tip.name.includes('广场')) icon = '🏬'; return { id: `search_${index}`, name: tip.name, address: tip.address || tip.district || '地址不详', latitude: latitude, longitude: longitude, district: tip.district, icon: icon }; }); this.setData({ searchResults: searchResults }); } else { console.error('❌ 搜索失败,返回数据异常:', res.data); wx.showToast({ title: '未找到相关地点', icon: 'none' }); this.setData({ searchResults: [] }); } }, fail: (error) => { wx.hideLoading(); console.error('❌ 搜索请求失败:', error); wx.showToast({ title: '搜索失败,请检查网络', icon: 'none' }); this.setData({ searchResults: [] }); } }); } catch (error) { wx.hideLoading(); console.error('❌ 搜索过程发生异常:', error); wx.showToast({ title: '搜索过程发生异常', icon: 'none' }); } }, // 点击搜索结果 onSearchResultTap(e) { const item = e.currentTarget.dataset.item; this.setData({ selectResult: item, selectedMerchant: item }); // 隐藏搜索框和搜索结果 this.setData({ showSearchBox: false, searchResults: [] }); // 在地图上添加标记 const marker = { id: new Date().getTime(), latitude: item.latitude, longitude: item.longitude, width: 40, height: 40, type: 'search', // 标记类型:搜索结果 iconPath: '/images/map/marker-select-position.png', callout: { content: item.name, display: 'BYCLICK' } }; this.mapCtx.moveToLocation({ latitude: item.latitude, longitude: item.longitude }); this.setData({ latitude: item.latitude, longitude: item.longitude, currentMapScale: 17 }); let that = this; setTimeout(() => { that.updateScale() }, 1000) // 更新标记数组 console.log("markers--->1-->" + this.data.markers.length) const filteredMarkers = this.data.allMarkers.filter(marker => marker.type !== 'search'); this.setData({ markers: filteredMarkers, allMarkers: filteredMarkers }); this.data.allMarkers.push(marker); this.setData({ markers: this.data.allMarkers, merchantName: item.name, merchant: item.district + item.address, }); console.log("markers--->2-->" + this.data.markers.length) this.setData({ searchResultInfoModalVisible: true, modalTranslateY:0, shouldFollowLocation: false }) // 显示提示信息 wx.showToast({ title: "定位成功", icon: 'success' }); }, handleHideBottm() { const filteredMarkers = this.data.allMarkers.filter(marker => marker.type !== 'search'); this.setData({ markers: filteredMarkers, allMarkers: filteredMarkers }); this.setData({ placeMarkId: '', mapClickCollect: '收藏', searchResultInfoModalVisible: false, shouldFollowLocation: false }) }, handleHideBottmNavigation() { this.setData({ searchResultNavigationVisible: false }) }, onSharechant() { this.setData({ shareShareVisible: true, }) }, onNavigation() { let distance = this.data.myLocation ? this.calculateDistance( this.data.myLocation.latitude, this.data.myLocation.longitude, this.data.selectResult.latitude, this.data.selectResult.longitude ) : 0; distance = this.formatDistance(distance); this.setData({ searchResultNavigationVisible: true, distance: distance }) }, onNavigationTap(e) { const type = e.currentTarget.dataset.type; if (type == "gaode") { this.guideToDouyin("请手动打开高德地图粘贴"); } else if (type == "baidu") { this.guideToDouyin("请手动打开百度地图粘贴"); } else if (type == "tengxun") { this.guideToDouyin("请手动打开腾讯地图粘贴"); } else { this.guideToDouyin("地址已复制"); } }, handleShare(e) { const type = e.currentTarget.dataset.type; console.log("share--->" + type); if (type == "douyin") { this.guideToDouyin("请手动打开抖音粘贴"); } else if (type == "weibo") { this.guideToDouyin("请手动打开微博粘贴"); } else if (type == "email") { this.guideToDouyin("请手动打开邮箱粘贴"); } else if (type == "qq") { this.guideToDouyin("请手动打开QQ粘贴"); } else if (type == "copy") { this.guideToDouyin("地址已复制"); } }, // 引导用户手动打开抖音 guideToDouyin(title) { // 先复制文字或保存图片(参考之前的代码) this.copyShareText(); // 假设已实现复制文字的方法 // 提示用户手动打开抖音 wx.showModal({ title: title, content: this.data.merchant, showCancel: false, confirmText: '确定' }); }, // 复制要分享的文字 copyShareText() { const text = this.data.merchant; wx.setClipboardData({ data: text, success: () => {} }); }, // 配置分享到聊天的内容(必填) onShareAppMessage() { return { title: "Find Me", // 分享的文字内容 path: '/custom-tab-bar/index/index', // 点击分享卡片跳转的页面 imageUrl: "/images/findme-logo.png" // 分享时的图片(可选,增强展示效果) }; }, // 在线图加载成功:标记为已加载 handleAvatarLoaded(e) { const { index } = e.currentTarget.dataset; // 获取当前 Marker 索引 this.setData({ [`markers[${index}].imageLoaded`]: true // 关键:给对应 Marker 添加加载完成状态 }); }, // 在线图加载失败(404/网络错误):保持默认图显示(避免在线图一直透明) handleAvatarError(e) { const { index } = e.currentTarget.dataset; // 可选:如果想加载失败也显示默认图,这里不用改(在线图保持 opacity:0) // 若想重试,可添加重试逻辑: // const markers = this.data.markers; // markers[index].avatarUrl = `${markers[index].avatarUrl}?t=${Date.now()}`; // 加时间戳避免缓存 // this.setData({ markers }); }, // 检查好友关系 async checkFriendRelation(e) { let customId = e.currentTarget.dataset.userid; try { const friendAPI = require('../../utils/friend-api.js'); // 显示加载提示 wx.showLoading({ title: '加载中...', mask: true }); // 获取好友详情 const friendDetailResponse = await friendAPI.getFriendDetail(customId); wx.hideLoading(); if (friendDetailResponse && friendDetailResponse.code === 0 && friendDetailResponse.data) { // 成功获取好友详情,说明是好友关系 this.navigateToFriendProfile(customId); return; } // 跳转到陌生人主页 this.navigateToStrangerProfile(customId); } catch (error) { wx.hideLoading(); console.error('检查好友关系失败:', error); // 跳转到陌生人主页 this.navigateToStrangerProfile(customId); } }, // 跳转到好友主页 navigateToFriendProfile(customId) { if (!customId) { console.error('customId 为空,无法跳转'); wx.showToast({ title: '用户ID无效', icon: 'none' }); return; } const targetUrl = `/subpackages/social/friend-detail/friend-detail?customId=${customId}`; console.log('准备跳转到好友页面:', targetUrl); wx.navigateTo({ url: targetUrl, success: () => { console.log('✅ 成功跳转到好友页面'); }, fail: (err) => { console.error('❌ navigateTo 跳转好友页面失败:', err); // 如果 navigateTo 失败(可能是页面栈已满),尝试使用 redirectTo wx.redirectTo({ url: targetUrl, success: () => { console.log('✅ redirectTo 成功跳转到好友页面'); }, fail: (err2) => { console.error('❌ redirectTo 也失败:', err2); wx.showToast({ title: '跳转失败,请重试', icon: 'none', duration: 2000 }); } }); } }); }, // 跳转到陌生人主页 navigateToStrangerProfile(customId) { if (!customId) { console.error('customId 为空,无法跳转'); wx.showToast({ title: '用户ID无效', icon: 'none' }); return; } const targetUrl = `/subpackages/social/user-preview/user-preview?customId=${customId}`; console.log('准备跳转到陌生人页面:', targetUrl); wx.navigateTo({ url: targetUrl, success: () => { console.log('✅ 成功跳转到陌生人页面'); }, fail: (err) => { console.error('❌ navigateTo 跳转陌生人页面失败:', err); // 如果 navigateTo 失败(可能是页面栈已满),尝试使用 redirectTo wx.redirectTo({ url: targetUrl, success: () => { console.log('✅ redirectTo 成功跳转到陌生人页面'); }, fail: (err2) => { console.error('❌ redirectTo 也失败:', err2); wx.showToast({ title: '跳转失败,请重试', icon: 'none', duration: 2000 }); } }); } }); }, handleHideShareBottm() { this.setData({ shareShareVisible: false }) }, // ===== 更多菜单相关方法 ===== // 切换更多菜单显示状态 toggleMoreMenu() { this.setData({ showMoreMenu: !this.data.showMoreMenu, showMapSettings: false // 确保地图设置不显示 }); }, // 隐藏更多菜单 hideMoreMenu() { this.setData({ showMoreMenu: false }); }, // 处理添加好友 handleAddFriend() { this.hideMoreMenu(); wx.navigateTo({ url: '/subpackages/social/search/search' }); }, // 处理发起群聊 handleStartGroupChat() { this.hideMoreMenu(); wx.navigateTo({ url: '/subpackages/group/create-group/create-group' }); }, // 处理显示二维码 handleShowQRCode() { this.hideMoreMenu(); wx.navigateTo({ url: '/subpackages/qr/qr-code/qr-code' }); }, // 处理扫一扫 handleScanQRCode() { this.hideMoreMenu(); wx.scanCode({ onlyFromCamera: false, success: (res) => { const scanResult = res.result || ''; console.log('扫码结果:', scanResult); // 处理 FindMe 用户二维码格式: FINDME:{customId} if (scanResult && scanResult.startsWith('FINDME:')) { const customId = (scanResult.split(':')[1] || '').trim(); if (!customId) { wx.showToast({ title: '无效的用户二维码', icon: 'none' }); return; } // 检查是否是自己 const currentUser = app.globalData.userInfo?.user; if (currentUser && currentUser.customId === customId) { wx.showToast({ title: '这是你自己的二维码', icon: 'none' }); return; } // 检查好友关系并跳转 this.handleScanResult(customId); } else { wx.showToast({ title: '无效的二维码,这不是FindMe的用户二维码', icon: 'none' }); } }, fail: (err) => { // 用户取消不提示错误 if (err.errMsg && !err.errMsg.includes('cancel')) { console.error('扫一扫失败:', err); wx.showToast({ title: '扫码失败,请重试', icon: 'none' }); } } }); }, // 处理扫码结果(检查好友关系并跳转) async handleScanResult(customId) { if (!customId) { wx.showToast({ title: '用户ID无效', icon: 'none' }); return; } try { const friendAPI = require('../../utils/friend-api.js'); // 显示加载提示 wx.showLoading({ title: '加载中...', mask: true }); // 获取好友详情 const friendDetailResponse = await friendAPI.getFriendDetail(customId); wx.hideLoading(); // 判断是否是好友:code === 0 且有 data 数据 const isFriend = friendDetailResponse?.code === 0 && friendDetailResponse?.data; console.log('扫码好友关系判断结果:', { customId, isFriend, responseCode: friendDetailResponse?.code, hasData: !!friendDetailResponse?.data }); // 根据好友关系跳转到对应页面 if (isFriend) { this.navigateToFriendProfile(customId); } else { this.navigateToStrangerProfile(customId); } } catch (error) { wx.hideLoading(); console.error('检查好友关系失败:', error); // 如果接口调用失败,默认当作陌生人处理 console.log('接口调用失败,当作陌生人处理'); this.navigateToStrangerProfile(customId); } }, // ===== 地图设置相关方法 ===== // 打开地图设置 openMapSettings() { this.setData({ showMapSettings: true, showMoreMenu: false // 确保更多菜单不显示 }); }, // 关闭地图设置 closeMapSettings() { // 先添加滑出动画类 this.setData({ isClosingMapSettings: true }); // 等待动画完成后再隐藏弹窗 setTimeout(() => { this.setData({ showMapSettings: false, isClosingMapSettings: false }); }, 300); // 与CSS动画时间一致 }, // 设置地图图层 setMapLayer(e) { const layer = e.currentTarget.dataset.layer; this.setData({ showTraffic: false, currentMapType: layer, enableSatellite: layer === 'satellite' }); wx.showToast({ title: this.getLayerTitle(layer), icon: 'none' }); }, // 获取图层标题 getLayerTitle(layer) { const titles = { 'standard': '标准地图', 'satellite': '卫星地图', 'jiache': '驾车模式', 'gonggong': '公共交通模式' }; return titles[layer] || '地图模式'; }, // 打开好友选择弹窗 onOpenFriendSelectModal() { // 同步selected字段 const selectedIds = this.data.poiRemindSelectedFriends.map(f => f.userId); const filteredFriendsList = this.data.filteredFriendsList.map(f => ({ ...f, selected: selectedIds.includes(f.userId) })); this.setData({ showFriendSelectModal: true, filteredFriendsList }); }, // 搜索输入 onFriendSearchInput(e) { const text = e.detail.value.trim(); const list = this.data.allFriendsList.filter(f => !text || (f.nickname && f.nickname.indexOf(text) !== -1)); // 同步selected字段 const selectedIds = this.data.poiRemindSelectedFriends.map(f => f.userId); const filteredFriendsList = list.map(f => ({ ...f, selected: selectedIds.includes(f.userId) })); this.setData({ friendSearchText: text, filteredFriendsList }); }, // 显示附近的人 showNearbyPeople() { // 切换到显示附近的人 this.setData({ currentFilter: 'strangers' }); // 刷新附近用户数据 this.loadNearbyUsers(); // 延迟显示toast,等待数据加载完成 // setTimeout(() => { // wx.showToast({ // title: `发现${this.data.strangersCount}个附近的人`, // icon: 'none', // duration: 2000 // }); // }, 500); }, // 显示好友位置 showFriendsLocation() { // 切换到显示好友 this.setData({ currentFilter: 'friends' }); wx.showToast({ title: `显示${this.data.friendsCount}个好友位置`, icon: 'none', duration: 2000 }); }, // 显示全部位置(好友和陌生人) showAllLocations() { // 切换到显示全部 this.setData({ currentFilter: 'all' }); // 刷新附近用户数据 this.loadNearbyUsers(); // 延迟显示toast,等待数据加载完成 setTimeout(() => { wx.showToast({ title: `显示${this.data.friendsCount}个好友和${this.data.strangersCount}个附近的人`, icon: 'none', duration: 2000 }); }, 500); }, // 🔥 ===== 数据更新方法 ===== // 更新未读消息数 updateUnreadMessageCount(count) { this.setData({ unreadMessageCount: count || 0 }); }, // 更新好友请求数 updateFriendRequestCount(count) { this.setData({ friendRequestCount: count || 0 }); }, // 更新附近的人数量 updateNearbyCount(count) { this.setData({ nearbyCount: count || 0 }); }, // 更新在线状态 updateOnlineStatus(isOnline) { this.setData({ isOnline: isOnline !== false }); }, // 让地图相机完整显示以(lat, lng)为圆心,radius为半径的圆 fitMapToCircle(lat, lng, radius) { // 计算圆的四个边界点(上、下、左、右) // 1度纬度约等于111km,1度经度约等于111km*cos(纬度) const latDelta = radius / 111000; const lngDelta = radius / (111000 * Math.cos(lat * Math.PI / 180)); const points = [{ latitude: lat + latDelta, longitude: lng }, // 上 { latitude: lat - latDelta, longitude: lng }, // 下 { latitude: lat, longitude: lng + lngDelta }, // 右 { latitude: lat, longitude: lng - lngDelta }, // 左 { latitude: lat, longitude: lng } // 圆心 ]; // padding自适应,半径越大padding越小,最小40最大100 let padding = Math.max(40, 120 - Math.round(radius / 10)); this.mapCtx && this.mapCtx.includePoints && this.mapCtx.includePoints({ points, padding: [padding, padding, padding, padding] }); }, // 保存POI提醒设置 onSavePoiRemind() { wx.showToast({ title: '提醒设置已保存', icon: 'success' }); }, // 打开地点标记弹窗 onOpenLocationMarkModal() { this.setData({ showLocationMarkModal: true }); }, // 关闭地点标记弹窗 onCloseLocationMarkModal() { this.setData({ showLocationMarkModal: false }); }, // 筛选输入 onLocationMarkSearchInput(e) { const text = e.detail.value.trim(); const list = this.data.locationMarkList.filter(item => item.name.includes(text)); this.setData({ locationMarkSearchText: text, filteredLocationMarkList: list }); }, // 删除按钮(静态演示) onDeleteLocationMark(e) { wx.showToast({ title: '删除演示', icon: 'none' }); }, // 修改按钮(静态演示) onEditLocationMarkName(e) { wx.showToast({ title: '修改名称演示', icon: 'none' }); }, // 设置提醒好友(静态演示) onSetRemindFriends(e) { wx.showToast({ title: '设置好友演示', icon: 'none' }); }, // 切换提醒类型(静态演示) onToggleRemindType(e) { wx.showToast({ title: '切换类型演示', icon: 'none' }); }, // POI提醒名称输入 onPoiRemindNameInput(e) { const name = e.detail.value; this.setData({ 'lastPoiForRemind.name': name }); }, // 地点标记列表项点击事件 onLocationMarkItemTap(e) { const item = e.currentTarget.dataset.item; this.setData({ showLocationMarkModal: false }); const poi = { detail: { name: item.name, address: item.address || '', latitude: item.lat, longitude: item.lng } }; this.onPoiTap(poi); }, // 添加地点标记 onAddLocationPlace() { // 纯定位版SDK不支持getPoiAround功能 wx.showModal({ title: '提示', content: '该功能暂不可用', showCancel: false }); }, onPoiAroundItemTap(e) { const item = e.currentTarget.dataset.item; this.setData({ showPoiAroundModal: false }); const poi = { detail: { name: item.name, address: item.address || '', latitude: item.latitude, longitude: item.longitude } }; this.onPoiTap(poi); }, onClosePoiAroundModal() { this.setData({ showPoiAroundModal: false }); }, closeMerchantInfo() { this.setData({ merchantInfoModalVisible: false, activeMerchantId: null }); // 清除所有商户的active状态 this.updateMerchantActiveState(null); }, // 更新商户active状态 updateMerchantActiveState(activeId) { const markers = this.data.markers || []; const updatedMarkers = markers.map(marker => { if (marker.type === 'merchant') { const markerBaseId = 10000; const merchantId = marker.id - markerBaseId; return { ...marker, isActive: merchantId === activeId }; } return marker; }); this.setData({ markers: updatedMarkers }); }, preventClose(e) { // 阻止事件冒泡,防止点击卡片内容时关闭弹窗 // 注意:在微信小程序中,使用catchtap已自动阻止事件冒泡,不需要手动调用stopPropagation // 此方法保留为空函数即可 }, // 点击"去"按钮,提示截图享优惠 onDishGoClick(e) { console.log('点击了去按钮'); wx.showToast({ title: '截图享优惠', icon: 'none', duration: 2000 }); // 阻止事件冒泡,防止关闭弹窗 if (e && e.stopPropagation) { e.stopPropagation(); } }, onFavoriteMerchant() { const merchant = this.data.selectedMerchant; merchant.isFavorited = !merchant.isFavorited; this.setData({ selectedMerchant: merchant, merchantList: this.data.merchantList.map(m => m.id === merchant.id ? merchant : m) }); // 可加toast提示 }, onNavigateMerchant() { const merchant = this.data.selectedMerchant; wx.openLocation({ latitude: merchant.latitude, longitude: merchant.longitude, name: merchant.name, address: merchant.address, scale: 16 }); }, //地点搜索结果的弹窗里的收藏 onAddressCollect() { this.setData({ addressIsFavorited: !this.data.addressIsFavorited }) const merchant = this.data.selectedMerchant; const address = merchant.address; const latitude = merchant.latitude; const longitude = merchant.longitude; const name = merchant.name; if (this.data.placeMarkId == '') { //空 说明还没有收藏地址 this.onPoiFavoriteCommon(address, latitude, longitude, name); } else { //说明刚才收藏了 再点击就是取消 this.onPoiCancelCommon(this.data.placeMarkId); } }, // 显示相机操作弹窗 showCameraActionSheet() { this.setData({ showCameraAction: true }); }, // 隐藏相机操作弹窗 hideCameraActionSheet() { this.setData({ showCameraAction: false }); }, ensureLoggedInForAction() { if (this.data.isLoggedIn) { return true; } wx.showToast({ title: '请先登录', icon: 'none' }); this.navigateToLogin(); return false; }, // 导航到登录页面 navigateToLogin() { wx.navigateTo({ url: '/pages/login/login' }); } });