许多商家在搭建小程序时,会遇到一个核心痛点:用户打开小程序,却找不到门店在哪,或者无法根据距离远近筛选最近的门店。这直接导致线下流量无法有效转化。本教程将手把手教你从零开始,在小程序中实现门店导航定位功能,并设置距离筛选,让用户能一键导航到最近的门店。
---
### 前言介绍
对于拥有多家线下门店的商家而言,小程序中的“门店导航”与“距离筛选”是提升用户体验和到店率的关键功能。用户只需打开小程序,系统便能自动获取其位置,并按照距离远近展示门店列表。点击任意门店,即可调用手机自带地图(如微信内置地图、高德、百度)进行导航。本教程将基于主流的小程序开发框架(以微信小程序为例,其他平台逻辑类似),详细讲解如何通过高德地图API实现这一整套流程。你将学会如何申请API密钥、如何在小程序中获取用户位置、如何计算门店距离以及如何实现排序筛选。
---
### 前置准备
在开始编写代码之前,你需要准备好以下三样东西:
1. **注册并登录高德开放平台**:访问高德开放平台官网(lbs.amap.com),完成开发者注册。这是获取地图服务能力的关键。
2. **创建应用并获取Key**:登录后,在“应用管理”中创建一个新应用。在应用详情页,点击“添加Key”,平台选择“Web服务”,服务类型选择“地理编码/逆地理编码”和“周边搜索”。创建成功后,你会得到一个字符串格式的Key,例如 `a7b9c5e8f1234567890abcdef1234567`。请妥善保管这个Key。
3. **准备门店数据**:你需要一份包含所有门店信息的列表。至少需要包含:门店名称、详细地址、经度(lng)、纬度(lat)。例如:
- 门店A:北京市朝阳区xxx路1号,经度:116.397428,纬度:39.90923
- 门店B:上海市浦东新区yyy路200号,经度:121.5447,纬度:31.2243
如果暂时没有经纬度,可以使用高德的地理编码API,通过地址反查获取。
---
### 分步操作步骤
#### 步骤1:配置小程序后台权限与高德SDK
这一步是基础,确保小程序有权限获取用户位置,并能调用高德服务。
1. **开通位置接口权限**:登录微信公众平台(mp.weixin.qq.com),进入你的小程序后台。在左侧菜单栏找到“开发” -> “开发管理” -> “接口设置”。找到“获取位置”和“wx.getLocation”接口,确保其状态为“已开通”。
2. **引入高德地图JS API**:在小程序项目的根目录下,找到 `app.json` 文件。在 `pages` 数组之后,添加一个 `permission` 字段,用于向用户解释获取位置的目的。同时,在需要使用地图的页面的 `index.wxml` 中,通过 `
```
#### 步骤2:获取用户当前地理位置
用户打开小程序时,我们需要请求其授权并获取经纬度。
1. **编写获取位置函数**:在 `pages/index/index.js` 文件中,编写一个 `getUserLocation` 方法。
```javascript
// 获取用户当前位置
getUserLocation() {
const that = this;
wx.getLocation({
type: 'wgs84', // 返回 gps 坐标
success(res) {
const latitude = res.latitude;
const longitude = res.longitude;
console.log('用户位置获取成功:', latitude, longitude);
// 将获取到的经纬度存储到页面数据中
that.setData({
userLatitude: latitude,
userLongitude: longitude
});
// 获取到用户位置后,立即调用计算距离和排序的函数
that.calculateAndSortStores();
},
fail(err) {
console.log('获取位置失败:', err);
wx.showToast({
title: '位置获取失败,请检查定位权限',
icon: 'none'
});
}
});
}
```
2. **在页面加载时调用**:在页面的 `onLoad` 或 `onShow` 生命周期函数中调用 `getUserLocation` 方法。
```javascript
onLoad() {
this.getUserLocation();
}
```
#### 步骤3:计算门店距离并进行排序
拿到用户位置后,我们需要遍历门店列表,计算每个门店与用户的直线距离,并按距离升序排列。
1. **定义门店数据**:在 `pages/index/index.js` 的 `data` 对象中,定义门店列表。这里假设你已经有了门店的经纬度。
```javascript
data: {
storeList: [
{ id: 1, name: '门店A', address: '北京市朝阳区xxx路1号', lat: 39.90923, lng: 116.397428 },
{ id: 2, name: '门店B', address: '上海市浦东新区yyy路200号', lat: 31.2243, lng: 121.5447 },
{ id: 3, name: '门店C', address: '广州市天河区zzz路88号', lat: 23.1291, lng: 113.2644 }
],
sortedStoreList: [], // 用于存放排序后的门店列表
userLatitude: null,
userLongitude: null
}
```
2. **编写距离计算函数**:使用Haversine公式计算两点间的直线距离(单位:公里)。
```javascript
// 计算两点之间的距离(Haversine公式)
calculateDistance(lat1, lng1, lat2, lng2) {
const radLat1 = lat1 * Math.PI / 180.0;
const radLat2 = lat2 * Math.PI / 180.0;
const a = radLat1 - radLat2;
const b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0;
const s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a/2),2) + Math.cos(radLat1)*Math.cos(radLat2)*Math.pow(Math.sin(b/2),2)));
const distance = s * 6378.137; // 地球半径,单位公里
return Math.round(distance * 100) / 100; // 保留两位小数
}
```
3. **编写排序函数**:遍历门店列表,计算距离并排序。
```javascript
// 计算并排序门店
calculateAndSortStores() {
const userLat = this.data.userLatitude;
const userLng = this.data.userLongitude;
if (!userLat || !userLng) {
console.log('用户位置未获取,无法排序');
return;
}
let storeList = this.data.storeList;
// 为每个门店添加距离字段
storeList.forEach(item => {
item.distance = this.calculateDistance(userLat, userLng, item.lat, item.lng);
});
// 按距离升序排序
storeList.sort((a, b) => {
return a.distance - b.distance;
});
// 更新页面数据
this.setData({
sortedStoreList: storeList
});
console.log('门店排序完成:', storeList);
}
```
#### 步骤4:实现门店列表展示与导航跳转
将排序后的门店列表渲染到页面上,并为每个门店添加“去这里”按钮,实现一键导航。
1. **编写WXML模板**:在 `pages/index/index.wxml` 中,使用 `wx:for` 循环渲染门店列表。
```html
```
2. **编写导航函数**:在 `pages/index/index.js` 中,实现 `navigateToStore` 方法。该方法会调用微信小程序的 `openLocation` 和 `chooseLocation` 接口,或者直接使用 `wx.openLocation` 打开地图并规划路线。
```javascript
// 导航到门店
navigateToStore(e) {
const lat = parseFloat(e.currentTarget.dataset.lat);
const lng = parseFloat(e.currentTarget.dataset.lng);
const name = e.currentTarget.dataset.name;
// 使用微信内置地图打开位置
wx.openLocation({
latitude: lat,
longitude: lng,
name: name,
scale: 18 // 缩放级别
});
}
```
#### 步骤5:添加距离筛选功能(按范围筛选)
除了排序,用户可能只想看“3公里以内”或“5公里以内”的门店。我们需要添加一个筛选器。
1. **添加筛选控件**:在WXML列表上方添加一个选择器。
```html
距离筛选:{{filterRange[filterIndex]}}
```
2. **初始化筛选数据**:在 `data` 中定义筛选范围和当前选中索引。
```javascript
data: {
// ... 其他数据
filterRange: ['全部', '1公里内', '3公里内', '5公里内', '10公里内'],
filterIndex: 0
}
```
3. **实现筛选逻辑**:当用户选择筛选条件时,重新过滤并渲染门店列表。
```javascript
// 筛选条件变更
onFilterChange(e) {
const index = e.detail.value;
this.setData({
filterIndex: index
});
this.filterStores();
},
// 根据筛选条件过滤门店
filterStores() {
const allStores = this.data.storeList; // 原始完整列表
const userLat = this.data.userLatitude;
const userLng = this.data.userLongitude;
const filterIndex = this.data.filterIndex;
let filteredList = [];
// 先计算所有门店距离
allStores.forEach(item => {
item.distance = this.calculateDistance(userLat, userLng, item.lat, item.lng);
});
// 根据筛选条件过滤
switch (filterIndex) {
case 0: // 全部
filteredList = allStores;
break;
case 1: // 1公里内
filteredList = allStores.filter(item => item.distance <= 1);
break;
case 2: // 3公里内
filteredList = allStores.filter(item => item.distance <= 3);
break;
case 3: // 5公里内
filteredList = allStores.filter(item => item.distance <= 5);
break;
case 4: // 10公里内
filteredList = allStores.filter(item => item.distance <= 10);
break;
default:
filteredList = allStores;
}
// 按距离排序
filteredList.sort((a, b) => a.distance - b.distance);
this.setData({
sortedStoreList: filteredList
});
}
```
**注意**:在 `onLoad` 中获取用户位置后,需要调用 `filterStores` 而不是 `calculateAndSortStores`,因为 `filterStores` 内部已经包含了计算和排序逻辑。你可以将 `getUserLocation` 中成功回调里的 `that.calculateAndSortStores()` 替换为 `that.filterStores()`。
---
### 常见问题
**问题1:为什么获取用户位置时一直弹窗授权,但点击允许后仍然失败?**
**解答**:这通常是因为小程序后台没有开通位置接口权限,或者手机系统设置了禁止该应用获取位置。请先检查步骤1中的微信公众平台接口设置,确保“获取位置”接口已开通。其次,检查手机设置 -> 隐私 -> 定位服务,确保微信的定位权限是开启状态。
**问题2:计算出的门店距离与高德地图、百度地图显示的不一致?**
**解答**:本教程使用的是Haversine公式计算球面两点间的直线距离,而地图APP导航显示的是实际道路距离(通常更长)。这是正常的。如果你需要更精确的道路距离,可以调用高德地图的“驾车路径规划”API,但这会消耗更多配额。对于门店列表展示,直线距离已经足够满足“距离筛选”的需求。
**问题3:点击“去这里”按钮后,没有弹出地图导航界面?**
**解答**:请检查 `navigateToStore` 函数中 `e.currentTarget.dataset.lat` 和 `e.currentTarget.dataset.lng` 是否能正确获取到数据。在WXML中,`data-lat` 和 `data-lng` 的值必须是数字类型。如果从后台获取的数据是字符串,需要用 `parseFloat` 进行转换。另外,确保 `wx.openLocation` 的 `latitude` 和 `longitude` 参数是有效的数字。
**问题4:门店列表没有按距离排序,或者筛选后列表为空?**
**解答**:首先,在 `onLoad` 中调用 `filterStores` 前,确保 `getUserLocation` 已经成功获取到 `userLatitude` 和 `userLongitude`。可以在 `filterStores` 函数开头打印这两个值进行调试。其次,检查 `storeList` 数据中的 `lat` 和 `lng` 字段名是否与代码中的一致。如果筛选后列表为空,说明所有门店都超出了你选择的筛选范围,可以尝试选择“全部”查看。
---
### 收尾总结
通过以上五个步骤,你已经成功为小程序添加了门店导航定位与距离筛选功能。用户现在可以直观地看到自己与各门店的距离,并一键导航前往。这个功能不仅能显著提升用户体验,还能有效提高线下门店的到店率。在实际运营中,你还可以进一步优化,比如在门店卡片上显示“营业时间”、“联系电话”等信息,或者结合地图组件在地图上直接标注所有门店位置,让交互更加丰富。记住,核心逻辑就是“获取位置 -> 计算距离 -> 排序筛选 -> 导航跳转”,掌握了这个流程,你就能灵活应对各种线下门店展示需求。