视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
微信小程序实现省市区三级地址选择
2020-11-27 22:03:21 责编:小采
文档

国际惯例先上效果图:

省市区三级联动,选择省自动刷新市,选择市自动刷新区,点击取消自动返回上一级重新选择,点击确定,保存地址。

数据库

这份数据库是某天在网上逛到的,当时未记录出处,直接贴出给读者使用,实在不妥,此处仅贴出表结构,方便大家交流学习。如有读者了解此份数据出处,烦请留言,谢谢!

数据表结构如下:

部分使用到的字段信息:

id:唯一标识每一个数据

name:地区名

parent_id:上级地区的id,若parent_id = 0 ,表示无上级信息,当前即为最高行政区。

extra:主要标识少数民族自治州或者自治县的信息,如:巴音郭楞 蒙古 自治州,此处存储 蒙古 

例:

suffix:行政级别 市 省 县 区等

部分地区数据信息如下:

后台

后台仅需提供一个接口,根据parent_id,查询地区信息

此处使用的后台是SSM框架,贴出主要接口、sql

1.与小程序交互接口

@RequestMapping(value = "/getArea", method = RequestMethod.POST)
 private @ResponseBody
 List<District> getArea(HttpServletRequest request) {
 int parentId = Integer.parseInt(request.getParameter("parentId"));
 logger.info("getArea");
 List<District> list = new ArrayList<District>();
 try {
 list = districtService.getAreas(parentId);
 } catch (Exception e) {
 
 }
 return list;
 }

2.查询sql

 <select id="getAreas" resultType="District">
 <!-- 具体的sql -->
 SELECT
 id,concat(name,extra,suffix) as name,parent_id as parentId
 FROM
 district
 WHERE
 parent_id = #{parentId}
 </select>

前端

先贴出css:

.hotCity {
 padding-right: 50rpx;
 margin: auto;
}
 
.weui-grid {
 padding: 10rpx 0;
 width: 160rpx;
 box-sizing: border-box;
 border: 1rpx solid #ececec;
 border-radius: 8rpx;
 background-color: white;
 margin: 8rpx 0;
}
 
.weui-grids {
 display: flex;
 flex-direction: row;
 justify-content: space-between;
}
 
.weui-grid__label {
 display: block;
 text-align: center;
 color: #333;
 font-size: 30rpx;
 white-space: nowrap;
 text-overflow: ellipsis;
 overflow: hidden;
}
.county {
 display: flex;
 flex-wrap: wrap;
 margin-top: 30px;
 margin-left: 15px;
}
/** 头部css **/
.headTitle{
 display: flex;
}
 
.headButton{
 background: #f68135;
 border-radius: 25rpx;
 border: 1px solid #f68135;
 color: #fff;
 height: 80rpx;
 line-height: 80rpx;
 margin: 0 auto;
 width: 150rpx;
 font-size: 45rpx;
 text-align: center;
 padding:0px;
 vertical-align:middle ;
}

html

html仅由两部分组成:

头部:确定、取消按钮,显示当前选择地址信息

         确定取消主要绑定了两个方法:submitChoose 及 cancleChoose 两个方法,点击不同按钮,执行不同js方法。

         显示当前地址信息:finalCity,只要在js中使用setData设置该值,该值就会动态改变。

city:显示当前可选的地区

        使用block组件,对json数组areaList进行循环显示,同样,使用setData设置该值,该值就会动态改变,达到省市区联动选择的效果。每一个小地区控件,有bindArea方法,并且在用户选择该地区,执行bindArea方法时,使用data-数据名的方法,向后台传递用户选择数据。

<view class="headTitle">
<button class="headButton" bindtap="cancleChoose">取消</button>
<view>{{finalCity == "" ? "请选择地址" : finalCity}}</view>
<button class="headButton" bindtap="submitChoose">确定</button>
</view>
<view class="county">
 <block class="hotCity" wx:for-items="{{areaList}}" wx:key="id">
 <view class="weui-grid" style="margin-right: 16rpx;" data-parentId="{{item.parentId}}" data-id="{{item.id}}" data-city="{{item.name}}" bindtap="bindArea">
 <view class="weui-grid__label">{{item.name}}</view>
 </view>
 </block>
</view>

js:

// pages/chooseCity/chooseCity.js
//获取应用实例
const model = require('../cityChoose/cityChoose.js')
const config = require('../../utils/config.js')
const util = require('../../utils/util.js')
const app = getApp();
//记录省市区
var nav = 0;
var chooseCity = new Array(3);
//记录每一次的parentId
var finalParentId = new Array(3);
var flag = 0;
Page({
 
 /**
 * 页面的初始数据
 */
 data: {
 finalCity:"",
 },
 
 /**
 * 生命周期函数--监听页面加载
 */
 onLoad: function(options) {
 //parentId = 0 取所有省份数据
 var that = this;
 that.getData(0);
 chooseCity = new Array("","","");
 finalParentId = new Array(0,0,0);
 nav = 0;
 },
 submitChoose:function(e){
 if(flag != 1){
 util.showLog("请选择完整地址")
 return;
 }else{
 var address_components = { "province": "", "city": "", "district": ""};
 address_components["province"] = chooseCity[0];
 address_components["city"] = chooseCity[1];
 address_components["district"] = chooseCity[2];
 console.log(address_components);
 app.globalData.address_components = address_components;
 wx.navigateBack();
 }
 },
 cancleChoose:function(e){
 console.log(finalParentId);
 var that = this;
 if(nav == 0){
 wx.navigateBack();
 } else {
 nav = nav - 1;
 chooseCity[nav] = "";
 console.log(chooseCity);
 that.setData({
 finalCity: chooseCity[0] + chooseCity[1] + chooseCity[2]
 })
 that.getData(finalParentId[nav]);
 }
 },
 bindArea: function(e) {
 if(flag == 0){
 console.log(e);
 var that = this;
 var parentId = e.currentTarget.dataset.id;
 var city = e.currentTarget.dataset.city;
 that.getData(parentId);
 chooseCity[nav] = city;
 finalParentId[nav] = e.currentTarget.dataset.parentid;
 nav++;
 console.log(chooseCity)
 that.setData({
 finalCity:chooseCity[0]+chooseCity[1]+chooseCity[2]
 })
 }
 },
 getData(parentId) {
 var that = this;
 var url = config.getArea + "?parentId=" + parentId;
 wx.request({
 url: url,
 success: (res) => {
 console.log("地区数据请求成功");
 console.log(res)
 if (res.data.length != 0) {
 flag = 0;
 //设置数据到全局变量
 that.setData({
 areaList: res.data,
 });
 }else{
 //防止用户再次点击;
 flag = 1;
 }
 },
 method: "POST",
 header: {
 "content-type": "application/x-www-form-urlencoded;charset=utf-8",
 },
 fail: (res) => {
 console.log("地区数据请求失败");
 }
 })
 },
})

js解析

全局变量作用:

      //记录用户已选择层次

      var nav = 0;

      //记录省市区三级数据

       var chooseCity = new Array(3);

      //记录每一次的parentId,主要记录用户选择路径,取消时根据用户路径显示上一级数据

     var finalParentId = new Array(3);

    //记录是否已经到最底层,再无数据可以选择

    var flag = 0;

执行过程:

    进入页面执行onLoad生命周期函数,在onLoad中调用getData初始化数据,及默认显示行政级别为省的数据,即请求parent_id为0的数据

  getData: 

getData(parentId) {
 var that = this;
//请求的url,由后台决定,此处填入你的请求url即可
 var url = config.getArea + "?parentId=" + parentId;
 wx.request({
 url: url,
 success: (res) => {
 console.log("地区数据请求成功");
 console.log(res)
 if (res.data.length != 0) {
 flag = 0;
 //设置数据到全局变量
 that.setData({
 areaList: res.data,
 });
 }else{
 //已到最后一层数据
 flag = 1;
 }
 },
 method: "POST",
 header: {
 "content-type": "application/x-www-form-urlencoded;charset=utf-8",
 },
 fail: (res) => {
 console.log("地区数据请求失败");
 }
 })
 },

点击地区数据执行bindArea

bindArea: function(e) {
 //如果未到最后一层,即可向下执行 
 if(flag == 0){
 console.log(e);
 var that = this;
 //获取html传参,获取用户点击信息
 var parentId = e.currentTarget.dataset.id;
 var city = e.currentTarget.dataset.city;
 //根据用户点击的数据,传入当前的id作为下一层的parentId,请求下一层数据,
 that.getData(parentId);
 //记录用户选择
 chooseCity[nav] = city;
//用户点击取消,到此层时,需要使用当前的parientid来请求此层应显示的数据
 finalParentId[nav] = e.currentTarget.dataset.parentid;
//记录路径数+1
 nav++;
 console.log(chooseCity)
//更新用户选择地区显示
 that.setData({
 finalCity:chooseCity[0]+chooseCity[1]+chooseCity[2]
 })
 }
 },

点击取消,执行方法cancleChoose

cancleChoose:function(e){
 var that = this;
//已是最后一层,则返回上一页
 if(nav == 0){
 wx.navigateBack();
 } else {
//记录路径数-1
 nav = nav - 1;
//将上次已选择的地区清空
 chooseCity[nav] = "";
 console.log(chooseCity);
//更新选择数据
 that.setData({
 finalCity: chooseCity[0] + chooseCity[1] + chooseCity[2]
 })
//根据finalParent中记录的每一层应请求的数据来更新地区数据
 that.getData(finalParentId[nav]);
 }
 },

点击确定,执行方法submitChoose   

submitChoose:function(e){
//如果未到最后一层,表示地址未选择完,如果不需要选择完整地址,此处去掉即可
 if(flag != 1){
 util.showLog("请选择完整地址")
 return;
 }else{
//存储数据到全局变量中,采用了json的方式存储,可以分别存储省市区数据
 var address_components = { "province": "", "city": "", "district": ""};
 address_components["province"] = chooseCity[0];
 address_components["city"] = chooseCity[1];
 address_components["district"] = chooseCity[2];
 console.log(address_components);
 app.globalData.address_components = address_components;
//返回上一次页面
 wx.navigateBack();
 }
 },

谢谢大家查看,评论里希望贴出cityChoose.js 及 util.js ,在下面贴出来啦,注:util.js里不是所有方法都要用到。

希望能够帮助到大家。

cityChoose

// pages/chooseCity/chooseCity.js
//获取应用实例
const model = require('../cityChoose/cityChoose.js')
const config = require('../../utils/config.js')
const util = require('../../utils/util.js')
const app = getApp();
//记录省市区
var nav = 0;
var chooseCity = new Array(3);
//记录每一次的parentId
var finalParentId = new Array(3);
//记录是否到最后一级
var flag = 0;
Page({
 
 /**
 * 页面的初始数据
 */
 data: {
 finalCity:"",
 },
 
 /**
 * 生命周期函数--监听页面加载
 */
 onLoad: function(options) {
 //parentId = 0 取所有省份数据
 var that = this;
 that.getData(0);
 chooseCity = new Array("","","");
 finalParentId = new Array(0,0,0);
 nav = 0;
 },
 submitChoose:function(e){
 if(flag != 1){
 util.showLog("请选择完整地址")
 return;
 }else{
 var address_components = { "province": "", "city": "", "district": ""};
 address_components["province"] = chooseCity[0];
 address_components["city"] = chooseCity[1];
 address_components["district"] = chooseCity[2];
 console.log(address_components);
 app.globalData.address_components = address_components;
 wx.navigateBack();
 }
 },
 cancleChoose:function(e){
 console.log(finalParentId);
 var that = this;
 if(nav == 0){
 wx.navigateBack();
 } else {
 nav = nav - 1;
 chooseCity[nav] = "";
 console.log(chooseCity);
 that.setData({
 finalCity: chooseCity[0] + chooseCity[1] + chooseCity[2]
 })
 that.getData(finalParentId[nav]);
 }
 },
 bindArea: function(e) {
 if(flag == 0){
 console.log(e);
 var that = this;
 var parentId = e.currentTarget.dataset.id;
 var city = e.currentTarget.dataset.city;
 //刷新出下一级地址前重复点击
 console.log(chooseCity[nav - 1] );
 console.log(city);
 if(chooseCity[nav-1] == city){
 return;
 }
 that.getData(parentId);
 chooseCity[nav] = city;
 finalParentId[nav] = e.currentTarget.dataset.parentid;
 nav++;
 console.log(chooseCity)
 that.setData({
 finalCity:chooseCity[0]+chooseCity[1]+chooseCity[2]
 })
 }
 },
 getData(parentId) {
 var that = this;
 var url = config.getArea + "?parentId=" + parentId;
 wx.request({
 url: url,
 success: (res) => {
 console.log("地区数据请求成功");
 console.log(res)
 if (res.data.length != 0) {
 flag = 0;
 //设置数据到全局变量
 that.setData({
 areaList: res.data,
 });
 }else{
 //防止用户再次点击;
 flag = 1;
 }
 },
 method: "POST",
 header: {
 "content-type": "application/x-www-form-urlencoded;charset=utf-8",
 },
 fail: (res) => {
 console.log("地区数据请求失败");
 }
 })
 },
})

util.js

const formatTime = date => {
 const year = date.getFullYear()
 const month = date.getMonth() + 1
 const day = date.getDate()
 const hour = date.getHours()
 const minute = date.getMinutes()
 const second = date.getSeconds()
 
 return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')
}
 
const formatNumber = n => {
 n = n.toString()
 return n[1] ? n : '0' + n
}
 
function showLog(e) {
 wx.showToast({
 title: e,
 icon: "none"
 })
}
 
function trim(str) {
 return str.replace(/(^\s*)|(\s*$)/g, "");
}
 
function showLoading() {
 wx.showLoading({
 title: '加载中',
 mask: true
 })
}
 
// 验证码倒计时
function phone_code(t, second) {
 // t是this,second是重新发送的间隔时间,需要设置按钮可点击
 var s = second;
 // 避免重复点击
 t.setData({
 phone_code_text: s + "s",
 phone_code_class: "",
 phone_code_buff: true
 });
 
 // 倒计时
 var clock = setInterval(function () {
 if (s > 1) {
 t.setData({
 phone_code_text: --s + "s"
 })
 } else {
 clearInterval(clock);
 t.setData({
 phone_code_text: "重新发送",
 phone_code_class: "on",
 phone_code_buff: false
 });
 // 重置数据
 s = second;
 }
 }, 1000)
}
function getNowFormatDate() {
 var date = new Date();
 var seperator1 = "-";
 var year = date.getFullYear();
 var month = date.getMonth() + 1;
 var strDate = date.getDate();
 if (month >= 1 && month <= 9) {
 month = "0" + month;
 }
 if (strDate >= 0 && strDate <= 9) {
 strDate = "0" + strDate;
 }
 var currentdate = year + seperator1 + month + seperator1 + strDate;
 return currentdate;
}
 
function checkAndCall(sourceId,recordType,tele,app,config){
 console.log(app.globalData.haulUserInfo)
 console.log(tele);
 if (app.globalData.haulUserInfo == null) {
 showLog("正在获取用户数据,请稍后。")
 app.Promise.then(function (value) {
 console.log(value);
 if (value) {
 // success
 wx.makePhoneCall({
 phoneNumber: tele,
 success: ph => {
 mycall(config, app,recordType, sourceId, function () {
 //记录联系次数
 })
 }
 })
 } else {
 // failure
 showLog("注册完成即可联系" + "。。即将跳转")
 setTimeout(function () {
 wx.navigateTo({
 url: '../registUser/registUser',
 })
 }, 1000);
 }
 }).catch(function (error) {
 });
 } else {
 // success
 wx.makePhoneCall({
 phoneNumber: tele,
 success: ph => {
 mycall(config,app, recordType, sourceId,function () {
 //记录联系次数
 
 })
 }
 })
 }
}
 
//记录互相联系
function mycall(config,app, recordType, sourceId, callback) {
 console.log(typeof (recordType))
 var that = this;
 wx.request({
 url: config.insertRecord,
 method: "POST",
 data: {
 sourceId: sourceId,
 userId: app.globalData.haulUserInfo.id,
 recordType: recordType
 },
 header: {
 "content-type": "application/x-www-form-urlencoded",
 },
 success: res => {
 if (res.data.success) {
 console.log('联系成功');
 callback();
 } else {
 showLog(res.data.error);
 }
 }
 })
}
 
module.exports = {
 formatNumber: formatNumber,
 formatTime: formatTime,
 phone_code_clock: phone_code,
 showLoading: showLoading,
 showLog: showLog,
 getNowFormatDate: getNowFormatDate,
 trim: trim,
 mycall: mycall,
 checkAndCall: checkAndCall
}

下载本文
显示全文
专题