likes
comments
collection
share

微信小程序添加地址的三种实现方式 —— 省市区联动选择器、地图选点、智能识别地址

作者站长头像
站长
· 阅读数 21

添加地址实现方式一般有三种:

  1. 最传统的,省市区联动选择,地址详细手动编辑
  2. 地图选点,通过使用第三方地图位置的SDK来实现,例如:百度、高德、腾讯等等。
  3. 智能识别,复制一串文字、然后识别出联系人、手机号、地址等等信息,例如:寄快递的 app

下面会把三种方式都说一下。demo 可以到这里拉取 ——「微信通用模板」

项目技术相关:

组件库:vant-weapp

小程序插件SDK:腾讯城市选点插件、腾讯位置服务小程序SDK

第三方插件:smartParsePro 智能识别地址

准备工作

注册腾讯位置服务账号、配置信息、下载SDK

开始之前,我们需要先注册一个腾讯位置服务的账号,个人账号日免费额度每日1w,并发额度 5(秒/次),企业账号(企业认证),免费额度为 50w~300w,并发额度 200~1000(秒/次)。一般都够用了。具体怎么注册就不赘述了,主要说一下怎么创建应用 —— 腾讯位置服务官网

控制台 --> 我的应用 --> 创建应用 -->填写名称以及分类

微信小程序添加地址的三种实现方式  ——  省市区联动选择器、地图选点、智能识别地址 添加key -->勾选 WebServiceAPI -->勾选微信小程序 -->填写你的小程序 APPID --> 复制Key(这个很重要小程序要用)

微信小程序添加地址的三种实现方式  ——  省市区联动选择器、地图选点、智能识别地址

创建完应用,接下来就是使用了。我们先下载 SDK —— 下载地址,把 qqmap-wx-jssdk.min.js(压缩版)放进项目里,这里放在utils目录下。

详细参考:微信小程序JavaScript SDK 开发文档

为了方便理解和使用,三种方法的会分开实现

第一种 —— 省市区联动选择,地址详细手动编辑

微信小程序添加地址的三种实现方式  ——  省市区联动选择器、地图选点、智能识别地址

这种方法重点主要在省市区联动器上,vant-weapp 提供了 van-area组件,还提供了一份省市区的数据源,数据源需要先安装依赖:npm i @vant/area-data,目前貌似已经停止维护,而且为本地的需要手动更新,不推荐使用。

我们通过地图服务的 SDK 提供的 getCityList接口获取数据源,通过SDK接口获取的数据和van-area 提供的数据有差异,所以我们需要加工一下。

首先基于 vant 提供的 van-areavan-popup组件封装一个省市区联动选择组件,再准备一个存放表单的页面

area-select 「省市区联动选择组件」

// area-select/index.wxml
<van-popup show="{{ isShowAreaSelect }}" position="bottom" custom-style="height: 50%" bind:close="closeAreaSelect">
  <van-area area-list="{{ areaList }}" :columns-num="3" bind:cancel="closeAreaSelect" bind:confirm="confirmArea" />
</van-popup>
// area-select/index.js
import { MAP_KEY } from "../../config.js" //腾讯位置服务的 Key
var QQMapWX = require('../../utils/qqmap-wx-jssdk.min'); // 引入 SDK 文件
var qqmapsdk; // SDK实例对象
Component({
  properties: {},
  data: {
    isShowAreaSelect: false,
    areaList: {
      // 变量名称是 van-area 规定写死的,不能换!不能换!不能换!
      province_list: {}, //省
      city_list: {},  //市
      county_list: {} //区
    },
  },
  lifetimes: {
    attached() {
      //创建SDK实例
      qqmapsdk = new QQMapWX({
        key: MAP_KEY
      });
      //调用 getCityList 
      qqmapsdk.getCityList({
        success: (res) => { //成功后的回调
          console.log('省份数据:', res.result[0]); //打印省份数据
          console.log('城市数据:', res.result[1]); //打印城市数据
          console.log('区县数据:', res.result[2]); //打印区县数据
          this.setData({
            "areaList.province_list": this.ArrayToObject(res.result[0]),
            "areaList.city_list": this.ArrayToObject(res.result[1]),
            "areaList.county_list": this.ArrayToObject(res.result[2]),
          })
        },
        fail: function (error) {
          console.error(error);
        },
        complete: function (res) {
          console.log(res);
        }
      });
    }
  },
  
  methods: {
    //确认选择
    confirmArea: function (e) {
      const values = e.detail.values
      //直辖市,需要处理数据,保持省市一致,例如,省:北京市;市:北京市;区:朝阳区
      if (values.some(x => !Boolean(x)))[values[1], values[2]] = [values[0], values[1]];	1
      const arr = (values.map(x => x.name))
      //调用父组件的传递方法,并传递选择结果。
      this.triggerEvent("confirm", arr)
      this.setData({
        isShowAreaSelect: false,
      })
    },

    //关闭省市区选择组件
    closeAreaSelect: function () {
      this.setData({
        isShowAreaSelect: false
      })
    },
    
    //打开省市区选择组件
    showAreaSelect: function () {
      this.setData({
        isShowAreaSelect: true
      })
    },

   // 格式化省市区数据
    ArrayToObject(arr) {
      const obj = {}
      for (let i = 0; i < arr.length; i++) {
        obj[arr[i].id] = arr[i].fullname
      }
      return obj
    }
  }
})

  // area-select/index.json(注释不要复制)
  "usingComponents": {
    "van-area": "@vant/weapp/area/index",
    "van-popup": "@vant/weapp/popup/index"
  }

如果需要获取省市区的经纬度,可以在confirmArea 里调用 SDK 提供的 geocoder 方法来获取,比较很简单,这种方式最省事,就不赘述了。

address-detail 地址添加页面

这里只显示关键代码,也就是省市区选择的部分,至于手机号码、联系人等,请自行拉取项目

  <!-- address-detail/index.wxml -->
  <van-field value="{{ formData.province + formData.city + formData.county}}" title-width="4.5em" label="所在地区"
    type="textarea" autosize clearable readonly placeholder="省 / 市 / 区 (只读)" border="{{ false }}" columns-num="{{3}}"
    bindtap="showAreaSelect" />
  //address-detail/index.js
  
  //打开省市区选择器
  showAreaSelect: function () {
    this.selectComponent("#area-select").showAreaSelect();
  },
  
  //确认选择结果
  confirmArea: function ({
    detail
  }) {
    this.setData({
      "formData.province": detail[0],
      "formData.city": detail[1],
      "formData.county": detail[2],
    })
  },

第二种 地图选点

微信小程序添加地址的三种实现方式  ——  省市区联动选择器、地图选点、智能识别地址

地图选点也有两种方案:

  1. 直接使用腾讯提供的地图选点插件,方便快捷。但是腾讯地图选点插件有一个缺点,就是不能选择某个城市来进行搜索,默认优先搜索当前城市的地点,如果没有,再自动匹配其他城市的地点。要是觉得这小缺点能接受,推荐使用这个方案。省事。具体实现参考:「开发文档」
  2. 通过 SDK 提供的接口来自己实现,也是本文要介绍的实现方式。 「SDK 接口 + 腾讯地图城市选点插件」,相对于第一种方式,多了个城市选择的功能,这种方式主要的优势是定制化;第一种直接使用选点插件,完全不存在可以插手的可能性。

添加插件

app.json 中添加下面的代码,添加完保存,微信开发者工具会提醒你添加插件,如果没有提醒,需要到小程序的后台添加,「设置」-->「第三方设置」-->「添加插件」-->搜索「腾讯位置服务城市选择器」添加即可。

 "plugins": {
    "citySelector": {
      "version": "1.0.0",
      "provider": "wx63ffb7b7894e99ae"
    }
  },
  "permission": {
    "scope.userLocation": {
      "desc": "你的位置信息将用于小程序定位"
    }
  },

地图选点页面

坑点一:map 地图组件,在开发者工具中无法正常显示,需点击预览,手机上查看 坑点二:小程序 2.17 版本库之后,wx.getLocation 有调用频率限制,用wx.onLocationChange 代替 ,「频率限制说明」 坑点三: 用户手动关闭的授权,wx.authorize` 无法调出授权窗口

<!-- map/index.wxml -->
<view class="container">
  <!-- 顶部导航栏 -->
  <navbar navConfig="{{navConfig}}" />

  <!-- 搜索组件 -->
  <van-search value="{{ addressInput }}" shape="round" placeholder="请输入搜索关键词" use-left-icon-slot
    bind:change="nearby_search">
    <view class="cityName ellipsis_1" slot="left-icon" bindtap="toSelectCity">{{cityName}}</view>
  </van-search>

  <!--地图容器-->
  <map id="myMap" style="width:100%;height:450rpx;" longitude="{{longitude}}" latitude="{{latitude}}" scale="15"
    bindregionchange="mapChange">
    <cover-view class="map-prompt">您可拖动地图, 标记准确位置</cover-view>
    <cover-image class="current-site-icon" src="../../images/marker.png"></cover-image>
    <cover-view class="reload" bindtap="reload">
      <cover-view class="center1">
        <cover-view class="center2"></cover-view>
      </cover-view>
    </cover-view>
  </map>

  <!-- 地点列表 -->
  <scroll-view class="scroll" bindscrolltolower="loadLocation" scroll-y lower-threshold="100">
    <view class="near-item" wx:for="{{nearList}}" wx:key="index" id="{{index}}" data-name="{{item.title}}"
      bindtap="chooseCenter">
      <van-icon name="location-o" custom-class="current-site" color="{{activeColor}}" wx:if="{{index == selectedId}}" />
      <van-icon name="location-o" custom-class="current-site" wx:else />
      <view class="item-main ellipsis_1">
        <view class="title {{ index == selectedId?'activeTitle':'' }}">{{item.title}}</view>
        <view class="address {{ index == selectedId?'activeAddress':'' }}">{{item.addr}}</view>
      </view>
    </view>
  </scroll-view>
</view>
 //map/index.js
 import {
    MAP_KEY
  } from "../../config"
  const cityS	elector = requirePlugin('citySelector');
  import QQMapWX from "../../utils/qqmap-wx-jssdk.min.js"
  let qqmapsdk;
  Page({
    data: {
      navConfig: {
        title: "地图选点",
        isLeftArrow: true
      },
      cityName: "佛山市",
      latitude: '',
      longitude: '',
      centerData: {},
      nearList: [],
      selectedId: 0,
      defaultKeyword: '房产小区', 
      keyword: '',
      pageIndex: 1,
      pageSize: 20,
      isDone: false,
      MAP_KEY,
    },

    onLoad: async function (options) {
     //获取地图 map 实例
      this.mapCtx = wx.createMapContext('myMap')
      // 实例化API核心类
      qqmapsdk = new QQMapWX({
        key: MAP_KEY
      });
      //判断是否授权定位
      let scopeRes = await wx.getSetting()
      if (!scopeRes.authSetting['scope.userLocation']) {
        try {
          // 没有授权的时候弹出授权窗口
          await wx.authorize({
            scope: 'scope.userLocation',
          })
        } catch (error) {
          // 用户手动关闭的授权,无法自动调起授权窗口,我们只能提示引导用户授权
          wx.showModal({
            showCancel: false,
            title: '位置授权',
            content: '该功能,需要进行「位置授权」才能使用。可点击「右上角」-->「设置」-->「位置消息」-->「仅在使用小程序使用」',
          })
          return
        }
      }
      wx.showLoading({
        title: '加载中'
      });

      //判断表单是否传了经纬度,如果有则使用表单的,用于在地图里显示刚才选择的位置
      const longitudeLatitude = options ? options.longitudeLatitude : ""
      if (longitudeLatitude) {
        const longLatArr = longitudeLatitude.split(',')
        this.initLocation(longLatArr[1], longLatArr[0])
      } else {
        //微信作死!新版本 wx.getLocation 存在调用频率限制, 使用 onLocationChange 来代替
        wx.startLocationUpdate({
          success: (res) => {
            wx.onLocationChange((location) => {
              if (location) {
                wx.stopLocationUpdate()
                const {
                  latitude,
                  longitude
                } = this.data
                if (latitude && longitude) return
                this.initLocation.call(this, location.latitude, location.longitude)
              }
            })
          },
        })
      }
    },

    onShow: function () {
      //城市选择插件,获取城市信息
      const selectedCity = citySelector.getCity();
      if (selectedCity) {
        const {
          fullname,
          location
        } = selectedCity
        this.setData({
          longitude: location.longitude,
          latitude: location.latitude,
          cityName: fullname
        })
        this.data.pageIndex = 1
        this.data.isDone = false
        this.nearby_search()
      }

    },
    onUnload() {
      // 页面卸载时清空插件数据,防止再次进入页面,getCity返回的是上次的结果
      citySelector.clearCity();
    },

    toSelectCity() {
      const key = MAP_KEY; // 使用在腾讯位置服务申请的key
      const referer = '微信模板'; // 调用插件的app的名称
      wx.navigateTo({
        url: `plugin://citySelector/index?key=${key}&referer=${referer}`,
      })
    },

    //监听拖动地图,拖动结束根据中心点更新页面
    mapChange: function (e) {
      if (e.type == 'end' && (e.causedBy == 'scale' || e.causedBy == 'drag')) {
        this.mapCtx.getCenterLocation({
          success: (res) => {
            this.setData({
              nearList: [],
              latitude: res.latitude,
              longitude: res.longitude,
            })
            this.data.pageIndex = 1
            this.data.isDone = false
            this.nearby_search.call(this);
          }
        })
      }

    },

    //重新定位
    reload: function () {
      this.setData({
        nearList: []
      })
      this.onLoad();
    },
	
    chooseCenter: function (e) {
      let id = e.currentTarget.id;
      for (let i = 0; i < this.data.nearList.length; i++) {
        if (i == id) {
          this.setData({
            selectedId: id,
            centerData: this.data.nearList[i],
            latitude: this.data.nearList[i].latitude,
            longitude: this.data.nearList[i].longitude,
          });
          this.selectedOk()
          return;
        }
      }
    },
      
	//搜索附近地点
    nearby_search: function (e) {
      if (e) this.data.keyword = e.detail
      wx.hideLoading();
      wx.showLoading({
        title: '加载中'
      });
      qqmapsdk.search({
        keyword: this.data.keyword,
        location: this.data.latitude + ',' + this.data.longitude,
        page_size: this.data.pageSize,
        page_index: this.data.pageIndex,
        success: (res) => {
          wx.hideLoading();
          var sug = [];
          for (let i = 0; i < res.data.length; i++) {
            sug.push({
              title: res.data[i].title,
              id: res.data[i].id,
              addr: res.data[i].address,
              province: res.data[i].ad_info.province,
              city: res.data[i].ad_info.city,
              district: res.data[i].ad_info.district,
              latitude: res.data[i].location.lat,
              longitude: res.data[i].location.lng
            });
          }
          let pageIndex = this.data.pageIndex + 1
          if (sug.length < this.data.pageSize) {
            this.data.isDone = true
            pageIndex = this.data.pageIndex
          };
          this.setData({
            selectedId: 0,
            centerData: sug[0],
            nearList: this.data.nearList.concat(sug),
            pageIndex: pageIndex
          })
        },
        fail: function (res) {
          wx.hideLoading();
        },
        complete: function (res) {
          wx.hideLoading();
        }
      });
    },
      
    //确认选择地址
    selectedOk: function () {
      let pages = getCurrentPages(); 
      //获取上一个页面的实例
      let prevPage = pages[pages.length - 2];
      const {
        title,
        city,
        district,
        province,
        latitude,
        longitude
      } = this.data.centerData
      prevPage.setData({
        "formData.county": district,
        "formData.province": province,
        "formData.city": city,
        "formData.addressDetail": title,
        "formData.longitudeLatitude": longitude + ',' + latitude,
      })
      wx.navigateBack({
        delta: 1
      })
    },

    //初始化
    initLocation: function (latitude, longitude) {
      qqmapsdk.reverseGeocoder({
        location: {
          latitude: latitude,
          longitude: longitude
        },
        get_poi: 1,
        success: (res) => {
          this.setData({
            latitude: latitude,
            longitude: longitude,
            keyword: this.data.defaultKeyword,
            cityName: res.result.address_component.city
          })
          // 调用接口
          this.nearby_search.call(this);
        },
      });
    },
    //滚动加载
    loadLocation() {
      if (!this.data.isDone) this.nearby_search();
    }
  })

地图选点使用

「地图选点」使用很简单,只需要跳转到「地图选点页面」就可以了。

  <van-field value="{{ formData.addressDetail }}" title-width="4.5em" label="服务地址" type="textarea" autosize clearable
    placeholder="点击选择服务地址" border="{{ false }}" center readonly right-icon="location-o"
    bind:change="changeAddressDetail" bindtap="getLocation" />
// longitudeLatitude 经纬度字符串,如果不为空默认使用 longitudeLatitude,如果为空就获取用户当前的经纬度
  getLocation() {
    const {
      longitudeLatitude
    } = this.data.formData
    wx.navigateTo({
      url: '../../pages/map/index?longitudeLatitude=' + (longitudeLatitude || ''),
    })
  },

第三种 智能识别地址

微信小程序添加地址的三种实现方式  ——  省市区联动选择器、地图选点、智能识别地址

智能识别地址,听起来很高大上,其实复制一段文本,然后通过与数据源的比对校验来识别出,手机号、联系人、省市区、详细地址等等。主要是这份数据源是否足够完善

智能识别地址也有两种方式

  1. 直接调用第三方提供的智能识别地址 api,没啥好说,百度、腾讯等都有提供服务(有免费额度),给点小钱,效果应该是最好。推荐使用!这种方式基本没有前端什么事,把文本传给后端,后端稍微配置一下,调用 SDK 提供的地址识别 api ,然后返回结果给前端就好。

  2. 前端自己来实现,这种方法准确率应该没有第一种那么高,不过只要不是用户故意捣乱,基本都也能识别出来。这里使用开源库 —— 「smartParsePro」来实现。

下面主要说说第二种

「拉取代码」——>「复制 smartWeChat 到自己的项目」

代码实现:

// app.js
import address_parse from "./utils/smartWeChat/js/address_parse"

App({
  // 识别地址
  smart: function (val) {
    return address_parse.method(val || '')
  },
  
  //手动重新挂载数据
  getAddressData: function () { 
    address_parse.getData()
  },
})
// address-detail_03/index.js
const app = getApp()
//智能识别
insightAddress() {
	// 重新挂载数据,非必须,保险起见
    app.getAddressData()
  	// 解析文本,提取地址信息
    const addressObj = app.smart(this.data.smartAddress)
    let {
        name = "",
        phone = "",
        province = "",
        city = "",
        county = "",
        street = "",
        address = ""
    } = addressObj
    this.setData({
      "formData.contactName": name,
      "formData.contactTel": phone,
      "formData.province": province,
      "formData.city": city,
      "formData.county": county,
      "formData.addressDetail": street + address
    })
  },

这样就算完成了 。

注意点:

  1. 手机预览的时候,需要在小程序配置好请求地址或者开发调试模式,不然无法加载数据源。

  2. 小程序后台没有配置数据源请求地址的时候,开发者工具记得勾选

微信小程序添加地址的三种实现方式  ——  省市区联动选择器、地图选点、智能识别地址

  1. smartWeChat 开源库用的数据源地址,是开发者提供的,正式环境,推荐把数据源下载下来并存在自己的服务器上。开发者把数据源分成了多等份存储起来,这样做应该是为了避免文件过大,以及微信请求限制数据大小为 2M ,下载时候也需要按照多等分切割开,不然你就需要改源码

修改数据源请求地址

//smartWeChat/js/address_parse.js

wx.request({
  //把地址 https://wangzc.wang/addressJson/ 替换成自己的 
  url: "https://wangzc.wang/addressJson/" + i,
  method: "GET",
  success: function (res) {}
})

码字不易,麻烦多多点赞,谢谢。

demo 可以到这里拉取——「微信通用模板」

相关文章:

转载自:https://juejin.cn/post/6979432031961022478
评论
请登录