Initial commit
This commit is contained in:
227
miniprogram/pages/nearby/nearby.ts
Normal file
227
miniprogram/pages/nearby/nearby.ts
Normal file
@@ -0,0 +1,227 @@
|
||||
import { NearbyUser } 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<{ userInfo: any; currentLocation: any }>()
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 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()
|
||||
this.setData({ statusBarHeight: info.statusBarHeight })
|
||||
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() {},
|
||||
})
|
||||
Reference in New Issue
Block a user