Files
PetCommunity/miniprogram/pages/nearby/nearby.ts
chenwu 3d21a9d297 fix: 修复评论/资料/导航胶囊/宠物档案/定位等多处问题,补全相关云函数
- 新增 addComment/getComments/getFollowList/followUser/getUser/updateProfile/updatePet/deletePet 云函数并完成部署
- 统一通过 app.globalData.navBarInfo 避让系统胶囊按钮(编辑资料/编辑宠物等页面保存按钮被遮挡问题)
- 编辑资料页所在城市改为省份 picker 选择
- 修复"我的"页动态为空(getUserPosts 应传 openid 而非数据库 _id)
- 重构编辑宠物页:品种改用 picker 滚轮(替代有数量上限的 actionSheet),标签改用 selectedTagSet 映射 + 自定义标签输入(5字以内、仅中英文/数字、最多8个)
- 接通宠物保存/删除真实云函数调用并同步本地缓存
- 移除 feed 页对不存在的 getStories 云函数调用,修复模拟器卡死
- 移除 post 页对不存在的 wx.reverseGeocoder API 的调用
- 统一扩展 AppGlobalData 类型并修正所有 getApp 调用的泛型,解决 TS 报错
2026-06-07 15:59:21 +08:00

236 lines
6.4 KiB
TypeScript

import { NearbyUser, AppGlobalData } from '../../types/index'
import { api } from '../../utils/api'
import { formatDistance, formatLastSeen, getAvatarGradient, getPetEmoji } from '../../utils/format'
import { requireLocation } from '../../utils/auth'
import { NEARBY_RADIUS_KM } from '../../utils/constants'
const DEFAULT_LAT = 31.2304
const DEFAULT_LNG = 121.4737
const app = getApp<{ globalData: AppGlobalData }>()
Page({
data: {
statusBarHeight: 0,
navbarHeight: 44,
navbarPaddingRight: 0,
tabBarHeight: 56,
viewMode: 'map' as 'map' | 'list',
mapCenter: { latitude: DEFAULT_LAT, longitude: DEFAULT_LNG },
mapScale: 15,
markers: [] as any[],
circles: [] as any[],
nearbyList: [] as any[],
onlineCount: 0,
activePet: null as any,
loading: false,
refreshing: false,
hasMore: true,
myLocation: null as any,
},
onLoad() {
const info = wx.getSystemInfoSync()
const { statusBarHeight, navbarHeight, navbarPaddingRight } = app.globalData.navBarInfo
this.setData({
statusBarHeight,
navbarHeight,
navbarPaddingRight,
tabBarHeight: info.windowHeight - (info.safeArea?.bottom ?? info.windowHeight) + 56,
})
this.initLocation()
},
onShow() {
const tabBar = this.getTabBar() as any
tabBar?.setSelected?.(1)
},
async initLocation() {
try {
const loc = await requireLocation()
this.setData({
mapCenter: loc,
myLocation: loc,
circles: [{
latitude: loc.latitude,
longitude: loc.longitude,
radius: NEARBY_RADIUS_KM * 1000,
strokeWidth: 2,
strokeColor: 'rgba(255, 79, 145, 0.3)',
fillColor: 'rgba(255, 79, 145, 0.05)',
}],
})
app.globalData.currentLocation = loc
this.loadNearby(loc.latitude, loc.longitude)
} catch {
this.loadNearby(DEFAULT_LAT, DEFAULT_LNG)
}
},
async loadNearby(lat: number, lng: number) {
this.setData({ loading: true })
try {
const list = await api.getNearbyPets(lat, lng, NEARBY_RADIUS_KM)
const nearbyList = list.map(item => this.enrichItem(item, lat, lng))
const onlineCount = nearbyList.filter(i => i.isOnline).length
this.setData({
nearbyList,
onlineCount,
markers: this.buildMarkers(nearbyList, lat, lng),
})
} catch {
wx.showToast({ title: '加载附近失败', icon: 'none' })
} finally {
this.setData({ loading: false, refreshing: false })
}
},
enrichItem(item: NearbyUser, myLat: number, myLng: number): any {
const pet = item.pet || item.user?.pets?.[0]
return {
userId: item.userId,
petId: pet?._id,
petName: pet?.name || '未知',
breed: pet?.breed || '宠物',
emoji: pet ? getPetEmoji(pet.species, pet.breed) : '🐾',
gradient: getAvatarGradient(item.userId),
distanceText: formatDistance(item.distance),
isOnline: item.isOnline,
lastSeenText: formatLastSeen(item.user?.lastSeen || ''),
locationName: item.user?.location || '',
latitude: item.location?.latitude || myLat,
longitude: item.location?.longitude || myLng,
}
},
buildMarkers(list: any[], myLat: number, myLng: number): any[] {
const markers: any[] = [
{
id: 0,
latitude: myLat,
longitude: myLng,
width: 40,
height: 40,
callout: {
content: '我在这',
color: '#ffffff',
fontSize: 11,
borderRadius: 12,
bgColor: '#ff4f91',
padding: 6,
display: 'ALWAYS',
},
},
]
list.forEach((item, idx) => {
markers.push({
id: idx + 1,
latitude: item.latitude,
longitude: item.longitude,
width: 36,
height: 36,
callout: {
content: item.petName,
color: '#272235',
fontSize: 11,
borderRadius: 12,
bgColor: 'rgba(255,255,255,0.92)',
padding: 6,
display: 'BYCLICK',
},
})
})
return markers
},
onMarkerTap(e: WechatMiniprogram.CustomEvent) {
const id = e.markerId
if (id === 0) return
const item = this.data.nearbyList[id - 1]
if (!item) return
this.setData({ activePet: item })
},
onPopupTap() {
const pet = this.data.activePet
if (!pet) return
wx.navigateTo({ url: `/pages/pet-detail/pet-detail?userId=${pet.userId}` })
},
async onGreeting(e: WechatMiniprogram.CustomEvent) {
const { uid, pid } = e.currentTarget.dataset
if (!uid) return
try {
await api.sendGreeting(uid, pid)
wx.showToast({ title: '打招呼成功!', icon: 'success' })
this.setData({ activePet: null })
} catch {
wx.showToast({ title: '发送失败', icon: 'none' })
}
},
onPreviewTap(e: WechatMiniprogram.CustomEvent) {
const { uid, lat, lng } = e.currentTarget.dataset
const item = this.data.nearbyList.find((i: any) => i.userId === uid)
if (!item) return
this.setData({
mapCenter: { latitude: Number(lat), longitude: Number(lng) },
activePet: item,
})
},
onItemTap(e: WechatMiniprogram.CustomEvent) {
const uid = e.currentTarget.dataset.uid
wx.navigateTo({ url: `/pages/pet-detail/pet-detail?userId=${uid}` })
},
setViewMode(e: WechatMiniprogram.CustomEvent) {
const mode = e.currentTarget.dataset.mode as 'map' | 'list'
this.setData({ viewMode: mode, activePet: null })
},
onLocateMe() {
const loc = this.data.myLocation
if (loc) {
this.setData({ mapCenter: { ...loc } })
} else {
this.initLocation()
}
},
onZoomIn() {
const scale = Math.min(20, this.data.mapScale + 1)
this.setData({ mapScale: scale })
},
onZoomOut() {
const scale = Math.max(10, this.data.mapScale - 1)
this.setData({ mapScale: scale })
},
onRegionChange() {},
onFilter() {
wx.showActionSheet({
itemList: ['500m 内', '1km 内', '3km 内', '5km 内'],
success: res => {
const radii = [0.5, 1, 3, 5]
const r = radii[res.tapIndex]
const loc = this.data.myLocation
if (loc) this.loadNearby(loc.latitude, loc.longitude)
},
})
},
async onRefresh() {
this.setData({ refreshing: true })
const loc = this.data.myLocation
if (loc) await this.loadNearby(loc.latitude, loc.longitude)
else this.setData({ refreshing: false })
},
onLoadMore() {},
})