findme-miniprogram-frontend/pages/map/map.js

6089 lines
169 KiB
JavaScript
Raw Permalink Normal View History

2025-12-27 17:16:03 +08:00
// 地图页面 - 高德地图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度纬度约等于111km1度经度约等于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'
});
}
});