/** * 地图找店页面 */ import { View, Text, Map, ScrollView, Image, Input } from '@tarojs/components' import Taro, { useLoad } from '@tarojs/taro' import { observer } from 'mobx-react' import { useState, useRef, useEffect } from 'react' import { Search, Arrow, Location } from '@taroify/icons' import './index.scss' // 模拟门店数据(带经纬度) const mockStoreData = [ { id: '1', name: '北京黑八大师联盟球厅店', address: '北京市XXX区XXX街道XXX路19号606', latitude: 39.910823, longitude: 116.400470, price: 16.00, billiardCount: 6, billiardEmpty: 6, chessCount: 2, chessEmpty: 2, type: '直营店', phone: '010-12345678', distance: 0, distanceText: '0.05km' }, { id: '2', name: '北京晶彩桌球世界店', address: '北京市XXX区XXX街道XXX路XXXXXX大厦101', latitude: 39.905823, longitude: 116.395470, price: 24.00, billiardCount: 8, billiardEmpty: 8, chessCount: 1, chessEmpty: 1, type: '加盟店', phone: '010-87654321', distance: 0, distanceText: '0.1km' }, { id: '3', name: '北京飓风台球店', address: '北京市XXX区XXX街道106号A01', latitude: 39.915823, longitude: 116.405470, price: 106.00, billiardCount: 10, billiardEmpty: 10, chessCount: 0, chessEmpty: 0, type: '加盟店', phone: '010-11223344', distance: 0, distanceText: '1.3km' }, { id: '4', name: '北京星耀台球俱乐部', address: '北京市XXX区XXX街道XXX路99号', latitude: 39.900823, longitude: 116.390470, price: 30.00, billiardCount: 12, billiardEmpty: 10, chessCount: 3, chessEmpty: 2, type: '直营店', phone: '010-99887766', distance: 0, distanceText: '2.3km' } ] interface Store { id: string name: string address: string latitude: number longitude: number price: number billiardCount: number billiardEmpty: number chessCount: number chessEmpty: number type: string phone: string distance: number distanceText: string } interface Marker { id: number latitude: number longitude: number iconPath: string width: number height: number callout?: any label?: any } const MapStorePage = observer(() => { // 用户位置(默认北京天安门) const [userLocation, setUserLocation] = useState({ latitude: 39.908823, longitude: 116.397470 }) // 地图中心点(默认跟随用户位置) const [mapCenter, setMapCenter] = useState({ latitude: 39.908823, longitude: 116.397470 }) const [storeList, setStoreList] = useState([]) const [markers, setMarkers] = useState([]) const [activeStoreId, setActiveStoreId] = useState(null) const [searchValue, setSearchValue] = useState('') const [selectedCity, setSelectedCity] = useState('选城市') const mapContext = useRef(null) useLoad(() => { // 页面加载时获取用户位置 requestLocationPermission() }) useEffect(() => { // 初始化地图上下文 mapContext.current = Taro.createMapContext('storeMap') }, []) // 请求位置权限并获取位置 const requestLocationPermission = async () => { try { // 检查权限 const setting = await Taro.getSetting() if (!setting.authSetting['scope.userLocation']) { // 请求授权 await Taro.authorize({ scope: 'scope.userLocation' }) } // 获取位置 Taro.showLoading({ title: '定位中...' }) const location = await Taro.getLocation({ type: 'gcj02', // 返回国测局坐标,适用于微信小程序地图 isHighAccuracy: true, altitude: false }) Taro.hideLoading() const userPos = { latitude: location.latitude, longitude: location.longitude } setUserLocation(userPos) setMapCenter(userPos) // 获取附近门店(使用模拟数据) fetchNearbyStores(userPos.latitude, userPos.longitude) } catch (error) { Taro.hideLoading() console.error('获取位置失败:', error) // 使用默认位置并加载门店 fetchNearbyStores(userLocation.latitude, userLocation.longitude) Taro.showModal({ title: '定位提示', content: '未能获取您的位置,将使用默认位置展示门店', showCancel: false }) } } // 获取附近门店(模拟接口) const fetchNearbyStores = async (lat: number, lng: number) => { try { Taro.showLoading({ title: '加载中...' }) // TODO: 替换为真实接口 // const res = await Taro.request({ // url: 'https://your-api.com/api/stores/nearby', // method: 'GET', // data: { // latitude: lat, // longitude: lng, // radius: 10000, // limit: 50 // } // }) // 模拟网络延迟 await new Promise(resolve => setTimeout(resolve, 500)) // 使用模拟数据并计算距离 const storesWithDistance = mockStoreData.map(store => ({ ...store, distance: calculateDistance(lat, lng, store.latitude, store.longitude), distanceText: formatDistance( calculateDistance(lat, lng, store.latitude, store.longitude) ) })) // 按距离排序 storesWithDistance.sort((a, b) => a.distance - b.distance) setStoreList(storesWithDistance) // 生成地图标记 generateMarkers(storesWithDistance) Taro.hideLoading() } catch (error) { Taro.hideLoading() console.error('获取门店失败:', error) Taro.showToast({ title: '获取门店失败', icon: 'none' }) } } // 计算两点之间的距离(米)- Haversine 公式 const calculateDistance = ( lat1: number, lng1: number, lat2: number, lng2: number ): number => { const R = 6371e3 // 地球半径(米) const φ1 = (lat1 * Math.PI) / 180 const φ2 = (lat2 * Math.PI) / 180 const Δφ = ((lat2 - lat1) * Math.PI) / 180 const Δλ = ((lng2 - lng1) * Math.PI) / 180 const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) + Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) * Math.sin(Δλ / 2) const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)) return R * c // 返回米 } // 格式化距离显示 const formatDistance = (distance: number): string => { if (distance < 1000) { return `${distance.toFixed(0)}m` } else { return `${(distance / 1000).toFixed(2)}km` } } // 生成地图标记 const generateMarkers = (stores: Store[]) => { // 使用默认标记图标(微信小程序会提供默认图标) // 如果需要自定义图标,准备好图片后替换 iconPath const newMarkers: Marker[] = stores.map((store, index) => ({ id: index, latitude: store.latitude, longitude: store.longitude, iconPath: '', // 空字符串使用默认图标,或指定自定义图标路径 width: 30, height: 30, callout: { content: store.name, color: '#333333', fontSize: 12, borderRadius: 8, bgColor: '#FFFFFF', padding: 8, display: 'BYCLICK' // 点击标记时显示 } })) setMarkers(newMarkers) } // 点击地图标记 const handleMarkerTap = (e: any) => { const markerIndex = e.detail.markerId const store = storeList[markerIndex] if (store) { setActiveStoreId(store.id) setMapCenter({ latitude: store.latitude, longitude: store.longitude }) } } // 点击列表项 const handleStoreItemClick = (store: Store) => { setActiveStoreId(store.id) // 移动地图到对应位置 setMapCenter({ latitude: store.latitude, longitude: store.longitude }) // 使用 moveToLocation 移动地图(需要 mapContext) if (mapContext.current) { mapContext.current.moveToLocation({ latitude: store.latitude, longitude: store.longitude }) } } // 重新定位 const handleRelocate = () => { requestLocationPermission() } // 导航到门店 const handleNavigate = (store: Store, e: any) => { e.stopPropagation() Taro.openLocation({ latitude: store.latitude, longitude: store.longitude, name: store.name, address: store.address, scale: 18 }) } // 搜索门店 const handleSearch = () => { if (!searchValue.trim()) { Taro.showToast({ title: '请输入门店名称或地址', icon: 'none' }) return } // TODO: 调用搜索接口 const filtered = mockStoreData.filter( store => store.name.includes(searchValue) || store.address.includes(searchValue) ) if (filtered.length > 0) { setStoreList(filtered) generateMarkers(filtered) // 移动到第一个搜索结果 setMapCenter({ latitude: filtered[0].latitude, longitude: filtered[0].longitude }) } else { Taro.showToast({ title: '未找到相关门店', icon: 'none' }) } } return ( {/* 地图区域 */} {}} /> {/* 重新定位按钮 */} {/* 底部内容区域 */} {/* 筛选区域 */} {/* 城市选择 */} {selectedCity} {/* 搜索输入框 */} setSearchValue(e.detail.value)} /> {/* 门店列表 */} 附近门店 共 {storeList.length} 家 {storeList.map((store) => ( handleStoreItemClick(store)} > {/* 头部:名称和距离 */} {store.name} 距离{store.distanceText} {/* 地址 */} {store.address} {/* 价格信息 */} 台球 ¥ {store.price.toFixed(2)} 棋牌 ¥ {(store.price + 4).toFixed(2)} ))} {storeList.length === 0 && ( 暂无门店数据 )} ) }) export default MapStorePage