连锁零售品牌"优果生鲜"最近遇到一个痛点——60%的顾客在微信咨询门店位置,客服每天要重复发送地址200多次。我们决定用小程序地图功能彻底解决这个问题,以下是完整的实现方案。
在开始编码前,需要明确几个关键决策点:
先创建基础项目结构:
code复制/pages/store-map
├── index.js
├── index.json
├── index.wxml
└── index.wxss
/assets
├── markers
│ ├── supermarket.png
│ └── restaurant.png
└── icons
└── location.png
配置app.json添加必要权限:
json复制{
"permission": {
"scope.userLocation": {
"desc": "用于展示最近门店及导航路线"
}
}
}
wxml文件使用微信原生map组件:
html复制<view class="map-container">
<map
id="storeMap"
longitude="{{center.longitude}}"
latitude="{{center.latitude}}"
markers="{{markers}}"
scale="16"
show-location
bindmarkertap="handleMarkerTap"
style="width: 100%; height: 70vh;">
</map>
<view class="location-btn" bindtap="recenterMap">
<image src="/assets/icons/location.png" mode="aspectFit"/>
</view>
</view>
关键CSS处理地图自适应:
css复制.map-container {
position: relative;
height: 70vh;
}
.location-btn {
position: absolute;
right: 20rpx;
bottom: 40rpx;
width: 80rpx;
height: 80rpx;
background: #fff;
border-radius: 50%;
box-shadow: 0 2px 6px rgba(0,0,0,0.16);
}
在JS中初始化地图数据:
javascript复制Page({
data: {
center: { longitude: 116.404, latitude: 39.915 },
markers: [],
currentStore: null
},
onLoad() {
this.loadStoresData()
this.getUserLocation()
},
// 模拟API数据获取
loadStoresData() {
const stores = require('../../assets/data/stores.json')
const markers = stores.map(store => ({
id: store.id,
latitude: store.location.lat,
longitude: store.location.lng,
iconPath: this.getMarkerIcon(store.type),
width: 32,
height: 40,
callout: {
content: store.name,
color: '#333',
fontSize: 14,
borderRadius: 4,
padding: 8,
display: 'ALWAYS'
}
}))
this.setData({ markers })
},
getMarkerIcon(type) {
const icons = {
supermarket: '/assets/markers/supermarket.png',
restaurant: '/assets/markers/restaurant.png'
}
return icons[type] || '/assets/markers/default.png'
}
})
实现分级定位获取方案:
javascript复制// 基础定位(快速响应)
getUserLocation() {
wx.getLocation({
type: 'gcj02',
success: res => {
this.setData({
center: {
longitude: res.longitude,
latitude: res.latitude
}
})
this.calculateDistances()
},
fail: () => this.showLocationGuide()
})
},
// 高精度定位(需用户授权)
requestPreciseLocation() {
wx.authorize({
scope: 'scope.userLocation',
success: () => {
wx.getLocation({
type: 'gcj02',
altitude: true,
success: res => {
// 更新位置并重新计算距离
}
})
}
})
},
实现门店距离筛选逻辑:
javascript复制calculateDistances() {
const { center, markers } = this.data
const storesWithDistance = markers.map(marker => {
const distance = this.getDistance(
center.latitude,
center.longitude,
marker.latitude,
marker.longitude
)
return { ...marker, distance }
})
storesWithDistance.sort((a, b) => a.distance - b.distance)
this.setData({ markers: storesWithDistance })
},
// 哈弗辛公式计算两点间距离
getDistance(lat1, lng1, lat2, lng2) {
const rad = num => num * Math.PI / 180
const R = 6371 // 地球半径(km)
const dLat = rad(lat2 - lat1)
const dLng = rad(lng2 - lng1)
const a =
Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(rad(lat1)) * Math.cos(rad(lat2)) *
Math.sin(dLng/2) * Math.sin(dLng/2)
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
return R * c
}
完善标记点点击事件处理:
javascript复制handleMarkerTap(e) {
const markerId = e.markerId
const store = this.data.markers.find(m => m.id === markerId)
this.setData({ currentStore: store })
wx.createSelectorQuery()
.select('#storeMap')
.context(res => {
res.context.moveToLocation({
longitude: store.longitude,
latitude: store.latitude
})
}).exec()
},
// 地图复位按钮
recenterMap() {
const { longitude, latitude } = this.data.center
wx.createSelectorQuery()
.select('#storeMap')
.context(res => {
res.context.moveToLocation({
longitude,
latitude
})
}).exec()
}
实现分页加载标记点策略:
javascript复制let loading = false
let currentPage = 1
onReachBottom() {
if (!loading) {
this.loadMoreStores()
}
},
loadMoreStores() {
loading = true
wx.showLoading({ title: '加载更多门店...' })
wx.cloud.callFunction({
name: 'getStores',
data: { page: currentPage }
}).then(res => {
const newMarkers = res.result.map(store => ({
/* 转换数据格式 */
}))
this.setData({
markers: [...this.data.markers, ...newMarkers]
})
currentPage++
}).finally(() => {
loading = false
wx.hideLoading()
})
}
添加门店热度可视化层:
javascript复制enableHeatmap() {
wx.createSelectorQuery()
.select('#storeMap')
.context(res => {
res.context.addHeatmap({
data: this.generateHeatmapData(),
radius: 20,
opacity: 0.6
})
}).exec()
},
generateHeatmapData() {
return this.data.markers.map(marker => ({
latitude: marker.latitude,
longitude: marker.longitude,
intensity: Math.random() * 10 // 实际项目用真实访问数据
}))
}
集成腾讯地图路线API:
javascript复制startNavigation(store) {
wx.openLocation({
latitude: store.latitude,
longitude: store.longitude,
name: store.name,
address: store.address,
scale: 18
})
}
在"优果生鲜"项目中,这套方案上线后客服咨询量下降72%,门店到访率提升15%。有个细节值得注意:我们发现在iOS设备上,wx.getLocation的响应速度比Android平均快300ms,这促使我们对Android用户增加了加载动画。