微信小程序实现省市区三级地址选择
国际惯例先上效果图:
省市区三级联动,选择省自动刷新市,选择市自动刷新区,点击取消自动返回上一级重新选择,点击确定,保存地址。
数据库
这份数据库是某天在网上逛到的,当时未记录出处,直接贴出给读者使用,实在不妥,此处仅贴出表结构,方便大家交流学习。如有读者了解此份数据出处,烦请留言,谢谢!
数据表结构如下:
部分使用到的字段信息:
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 }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: .NET 6新增的20个API介绍