6089 lines
No EOL
169 KiB
JavaScript
6089 lines
No EOL
169 KiB
JavaScript
// 地图页面 - 高德地图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'
|
||
});
|
||
}
|
||
}); |