236 lines
6.4 KiB
TypeScript
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(139, 124, 246, 0.3)',
|
|
fillColor: 'rgba(139, 124, 246, 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: '#8b7cf6',
|
|
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: '#1b1b20',
|
|
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() {},
|
|
})
|