项目中经常遇到要选择城市。用到三级联动的方式
- 微信小程序的
picker
组件 mode=date
是三级联动的,但是无法自定义,这让我们心痛不已,值得我们欣慰的 picker-view 组件是可以自定义添加多个选项,但还是无法联动。既然这样那就自己写一个联动。 - 做到如下图所示:
- 分为动态获取地址
- 引用静态文件获取地址
<view class="add-address">
<view class="add-form">
<view class="form-item">
<input class="input" bindinput="bindinputName" placeholder="姓名" value="{{address.name}}" />
</view>
<view class="form-item">
<input class="input" bindinput="bindinputMobile" value="{{address.mobile}}" placeholder="手机号码" />
</view>
<view class="form-item">
<input class="input" bindinput="bindinputAddress" value="{{address.address}}" placeholder="详细地址" />
</view>
<view class="form-item" bindtap='select'>
<view class="weui-cell__bd">
{{areaInfo}}
</view>
</view>
<view class="form-default">
<text bindtap="bindIsDefault" class="default-input {{address.isDefault == 1 ? 'selected' : ''}}">设为默认地址</text>
</view>
</view>
<view class="btns">
<button class="cannel" bindtap="cancelAddress">取消</button>
<button class="save" bindtap="saveAddress">保存</button>
</view>
</view>
<view class="bg-mask" bindtap="cancelSelectRegion" wx:if="{{openSelectRegion}}"></view>
<view class="picker-view" animation="{{animationAddressMenu}}" style="visibility:{{addressMenuIsShow ? 'visible':'hidden'}}">
<!-- 确认取消按钮 -->
<view class='btn'>
<text catchtap="cityCancel">取消</text>
<text style="float: right" catchtap="citySure">确定</text>
</view>
<!-- 选择地址 -->
<picker-view class='cont' bindchange="cityChange" value="{{value}}" wx:key="">
<!-- 省 -->
<picker-view-column>
<view wx:for="{{provinces}}" class="picker-item" wx:key="{{index}}">{{item.area}}</view>
</picker-view-column>
<!-- 市 -->
<picker-view-column>
<view wx:for="{{citys}}" class="picker-item" wx:key="index">{{item.area}}</view>
</picker-view-column>
<!-- 区 -->
<picker-view-column>
<view wx:for="{{areas}}" class="picker-item" wx:key="index">{{item.area}}</view>
</picker-view-column>
</picker-view>
</view>
page{
height: 100%;
background: #f4f4f4;
}
.add-address .add-form{
background: #fff;
width: 100%;
height: auto;
overflow: hidden;
}
.add-address .form-item{
height: 116rpx;
padding-left: 31.25rpx;
border-bottom: 1px solid #d9d9d9;
display: flex;
align-items: center;
padding-right: 31.25rpx;
}
.add-address .input{
flex: 1;
height: 44rpx;
line-height: 44rpx;
overflow: hidden;
}
.add-address .form-default{
border-bottom: 1px solid #d9d9d9;
height: 96rpx;
background: #fafafa;
padding-top: 28rpx;
font-size: 28rpx;
}
.default-input{
margin: 0 auto;
display: block;
width: 240rpx;
height: 40rpx;
padding-left: 50rpx;
line-height: 40rpx;
background: url(http://yanxuan.nosdn.127.net/hxm/yanxuan-wap/p/20161201/style/img/sprites/checkbox-sed825af9d3-a6b8540d42.png) 1rpx -448rpx no-repeat;
background-size: 38rpx 486rpx;
font-size: 28rpx;
}
.default-input.selected{
background: url(http://yanxuan.nosdn.127.net/hxm/yanxuan-wap/p/20161201/style/img/sprites/checkbox-sed825af9d3-a6b8540d42.png) 0 -192rpx no-repeat;
background-size: 38rpx 486rpx;
}
.add-address .btns{
position: fixed;
bottom: 0;
left: 0;
overflow: hidden;
display: flex;
height: 100rpx;
width: 100%;
}
.add-address .cannel,.add-address .save{
flex: 1;
height: 100rpx;
text-align: center;
line-height: 100rpx;
font-size: 28rpx;
color: #fff;
border:none;
border-radius: 0;
}
.add-address .cannel{
background: #3F3F3F;
}
.add-address .save{
background: #a78845;
}
.region-select{
width: 100%;
height: 600rpx;
background: #fff;
position: fixed;
z-index: 10;
left:0;
bottom: 0;
}
.region-select .hd{
height: 108rpx;
width: 100%;
border-bottom: 1px solid #f4f4f4;
padding: 46rpx 30rpx 0 30rpx;
}
.region-select .region-selected{
float: left;
height: 60rpx;
display: flex;
}
.region-select .region-selected .item{
max-width: 140rpx;
margin-right: 30rpx;
text-align: left;
line-height: 60rpx;
height: 100%;
color: #333;
font-size: 28rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.region-select .region-selected .item.disabled{
color: #999;
}
.region-select .region-selected .item.selected{
color: #a78845;
}
.region-select .done{
float: right;
height: 60rpx;
width: 60rpx;
border: none;
background: #fff;
line-height: 60rpx;
text-align: center;
color: #333;
font-size: 28rpx;
}
.region-select .done.disabled{
color: #999;
}
.region-select .bd{
height: 492rpx;
width: 100%;
padding: 0 30rpx;
}
.region-select .region-list{
height: 492rpx;
}
.region-select .region-list .item{
width: 100%;
height: 104rpx;
line-height: 104rpx;
text-align: left;
color: #333;
font-size: 28rpx;
}
.region-select .region-list .item.selected{
color: #b4282d;
}
.bg-mask{
height: 100%;
width: 100%;
background: rgba(0, 0, 0, 0.4);
position: fixed;
top:0;
left:0;
z-index: 8;
}
.picker-view {
width: 100%;
display: flex;
z-index:12;
background-color: #fff;
/* background: rgba(0, 0, 0, .2); */
flex-direction: column;
justify-content: center;
align-items: center;
position: fixed;
bottom: 0;
left: 0rpx;
height: 40vh;
}
.btn {
width: 100%;
height: 90rpx;
padding: 0 24rpx;
box-sizing: border-box;
line-height: 90rpx;
text-align: center;
display: flex;
background: rgba(255,255,255,.8);
justify-content: space-between;
}
.cont {
width: 100%;
height: 389rpx;
}
.picker-item {
line-height: 70rpx;
margin-left: 5rpx;
margin-right: 5rpx;
text-align: center;
}
.address {
width: 100%;
height: 90rpx;
line-height: 90rpx;
text-align: center;
border-bottom: 1rpx solid #f1f1f1;
}
- addressAdd.js (分两个版本一个是动态获取的
就是选择的时候动态向后台获取内容
下方是动态获取的例子:)
var util = require('../../../utils/util.js');
var api = require('../../../config/api.js');
var app = getApp();
Page({
data: {
addressId: 0,
openSelectRegion: false,
regionType: 1,
selectRegionDone: false,
szxqList: [],
szxq: {
id: "",
name: "请选择小区"
},
szdsList: [],
szds: {
id: "",
name: ""
},
fanghao: "",
animationAddressMenu: {},
addressMenuIsShow: false,
value: [0, 0, 0],
provinces: [],
citys: [],
areas: [],
areaInfo: '',
areaJson: {}
},
bindinputMobile(event) {
let address = this.data.address;
address.mobile = event.detail.value;
this.setData({
address: address
});
},
bindinputName(event) {
let address = this.data.address;
address.name = event.detail.value;
this.setData({
address: address
});
},
bindinputAddress(event) {
let address = this.data.address;
address.address = event.detail.value;
this.setData({
address: address
});
},
bindIsDefault() {
let address = this.data.address;
address.isDefault = !address.isDefault;
this.setData({
address: address
});
},
getAddressDetail() {
let that = this;
// util.request(api.AddressDetail, {
// id: that.data.addressId
// }).then(function(res) {
// if (res.errno === 0) {
// if (res.data) {
// that.setData({
// address: res.data
// });
// }
// }
// });
},
wxChooseAddress() {
let that = this;
let address = this.data.address;
// 用户已经同意小程序使用地址功能
wx.chooseAddress({
success: function(res) {
address.provinceId = 99999;
address.cityId = 88888;
address.areaId = 77777;
address.name = res.userName;
address.mobile = res.telNumber;
address.provinceName = res.provinceName;
address.cityName = res.cityName;
address.areaName = res.countyName;
address.address = res.provinceName + res.cityName + res.countyName + res.detailInfo;
that.setData({
address: address,
});
}
});
},
wxAddress() {
let that = this;
// 可以通过 wx.getSetting 先查询一下用户是否授权了 "scope.address" 这个 scope
wx.getSetting({
success(res) {
if (!res.authSetting['scope.address']) {
wx.authorize({
scope: 'scope.address',
success() {
that.wxChooseAddress();
}
})
} else {
that.wxChooseAddress();
}
}
})
},
onLoad: function(options) {
let that = this;
// 页面初始化 options为页面跳转所带来的参数
console.log(options);
if (options.id && options.id != 0) {
this.setData({
addressId: options.id
});
this.getAddressDetail();
} else {
that.wxAddress();
}
},
onReady: function() {
},
cancelAddress() {
wx.navigateBack();
},
saveAddress() {
console.log(this.data.address);
let address = this.data.address;
if (address.name == '') {
util.showErrorToast('请输入姓名');
return false;
}
if (address.mobile == '') {
util.showErrorToast('请输入手机号码');
return false;
}
if (address.areaId == 0) {
util.showErrorToast('请输入省市区');
return false;
}
if (address.address == '') {
util.showErrorToast('请输入详细地址');
return false;
}
let that = this;
},
onShow: function() {
// 获取所在栋数
var animation = wx.createAnimation({
duration: 500,
timingFunction: 'linear',
})
this.animation = animation
const that = this
// 获取所在地区
console.log()
util.getAreaReq().then(provinces => {
util.getAreaReq(provinces[0].code).then(citys => {
util.getAreaReq(citys[0].code).then(areas => {
that.setData({
provinces: provinces,
citys: citys,
areas: areas,
areaJson: {
provinces: {
id: 40,
name: "广东省"
},
citys: {
id: 4006,
name: "河源市"
},
areas: {
id: 400602,
name: "源城区"
}
}
})
var areas = that.data.areaJson.areas.name == null ? "" : that.data.areaJson.areas.name
var areaInfo = that.data.areaJson.provinces.name + '·' + that.data.areaJson.citys.name + '·' + areas
that.setData({
areaInfo: areaInfo,
})
})
})
})
},
// 点击所在地区弹出选择框
select: function(e) {
// 如果已经显示,不在执行显示动画
if (this.data.addressMenuIsShow) {
return false
} else {
// 执行显示动画
this.startAddressAnimation(true)
}
},
// 处理省市县联动逻辑
cityChange: function(e) {
// console.log(this.data.provinces)
var value = e.detail.value
var provinces = this.data.provinces
var citys = this.data.citys
var areas = this.data.areas
var provinceNum = value[0]
var cityNum = value[1]
var countyNum = value[2]
var that = this;
// console.log(provinces)
// 如果省份选择项和之前不一样,表示滑动了省份,此时市默认是省的第一组数据,
if (this.data.value[0] != provinceNum) {
var id = provinces[provinceNum].id
// console.log(citys[cityNum])
util.getAreaReq(provinces[provinceNum].code).then(citys => {
util.getAreaReq(citys[0].code).then(areas => {
this.setData({
value: [provinceNum, 0, 0],
citys: citys,
areas: areas,
areaJson: {
provinces: {
id: provinces[provinceNum].code,
name: provinces[provinceNum].area
},
citys: {
id: citys[0].code,
name: citys[0].area
},
areas: {
id: areas.length > 0 ? areas[0].code : null,
name: areas.length > 0 ? areas[0].area : null,
}
}
})
})
})
} else if (this.data.value[1] != cityNum) {
// 滑动选择了第二项数据,即市,此时区显示省市对应的第一组数据
var id = citys[cityNum].id
util.getAreaReq(citys[cityNum].code).then(areas => {
this.setData({
value: [provinceNum, cityNum, 0],
areas: areas,
areaJson: {
provinces: {
id: provinces[provinceNum].code,
name: provinces[provinceNum].area
},
citys: {
id: citys[cityNum].code,
name: citys[cityNum].area
},
areas: {
id: areas.length > 0 ? areas[0].code : null,
name: areas.length > 0 ? areas[0].area : null,
}
}
})
})
} else {
// 滑动选择了区
this.setData({
value: [provinceNum, cityNum, countyNum],
areaJson: {
provinces: {
id: provinces[provinceNum].code,
name: provinces[provinceNum].area
},
citys: {
id: citys[cityNum].code,
name: citys[cityNum].area
},
areas: {
id: areas[countyNum].code,
name: areas[countyNum].area
}
}
})
// console.log(that.data.areaJson)
}
},
// 执行动画
startAddressAnimation: function(isShow) {
if (isShow) {
// vh是用来表示尺寸的单位,高度全屏是100vh
this.animation.translateY(0 + 'vh').step()
} else {
this.animation.translateY(40 + 'vh').step()
}
this.setData({
animationAddressMenu: this.animation.export(),
addressMenuIsShow: isShow,
})
},
// 点击地区选择取消按钮
cityCancel: function(e) {
this.startAddressAnimation(false)
},
// 点击地区选择确定按钮
citySure: function(e) {
var that = this
var city = that.data.city
var value = that.data.value
this.startAddressAnimation(false)
// console.log(that.data.areaJson)
var areas = that.data.areaJson.areas.name == null ? "" : that.data.areaJson.areas.name
// 将选择的城市信息显示到输入框
var areaInfo = that.data.areaJson.provinces.name + '·' + that.data.areaJson.citys.name + '·' + areas
that.setData({
areaInfo: areaInfo,
})
},
onHide: function() {
// 页面隐藏
},
onUnload: function() {
// 页面关闭
}
});
- 需要使用外部js(utils) 自己封装的一个工具
var api = require('../config/api.js');
var app = getApp();
var user = require('./user.js');
/**
* 封装微信的的request
*/
function request(url, data = {}, method = "GET") {
return new Promise(function(resolve, reject) {
user.checkLogin().then(res => {
}).catch(() => {
wx.switchTab({
url: '/pages/ucenter/index/index?show=true'
});
});
wx.request({
url: url,
data: data,
method: method,
header: {
'Content-Type': 'application/x-www-form-urlencoded',
'Cookie': "token=" + wx.getStorageSync('token') + ";" + wx.getStorageSync('sessionid'),
'X-Requested-With': "XMLHttpRequest"
},
success: function(res) {
if (res.statusCode == 400) {
user.loginByWeixin().then(res => {
app.globalData.hasLogin = true;
});
wx.redirectTo({
url: '/pages/index/index'
});
wx.showToast({
title: '已经重新登录',
})
}
if (res.header["Set-Cookie"]) {
wx.setStorageSync("sessionid", res.header["Set-Cookie"])
}
if (res.statusCode == 200) {
if (res.data.errno == 501) {
} else {
resolve(res);
}
} else {
reject(res);
}
},
fail: function(err) {
reject(err)
}
})
});
}
function getAreaReq(id) {
const that = this;
return new Promise(function(resolve, reject) {
that.request("****", JSON.stringify({
}), "post").then(response => {
console.log(response.data.rs_data)
resolve(response.data.rs_data);
})
})
}
module.exports = {
request,
getAreaReq
};
var util = require('../../../utils/util.js');
var api = require('../../../config/api.js');
var area = require('../../../config/area.js');
var app = getApp();
Page({
data: {
address: {
id: 0,
provinceId: 0,
cityId: 0,
areaId: 0,
address: '',
name: '',
mobile: '',
isDefault: 0,
provinceName: '',
cityName: '',
areaName: ''
},
addressId: 0,
openSelectRegion: false,
regionType: 1,
selectRegionDone: false,
szxqList: [],
szxq: {
id: "",
name: "请选择小区"
},
szdsList: [],
szds: {
id: "",
name: ""
},
fanghao: "",
animationAddressMenu: {},
addressMenuIsShow: false,
value: [0, 0, 0],
provinces: [],
citys: [],
areas: [],
areaInfo: '',
areaJson: {}
},
bindinputMobile(event) {
let address = this.data.address;
address.mobile = event.detail.value;
this.setData({
address: address
});
},
bindinputName(event) {
let address = this.data.address;
address.name = event.detail.value;
this.setData({
address: address
});
},
bindinputAddress(event) {
let address = this.data.address;
address.address = event.detail.value;
this.setData({
address: address
});
},
bindIsDefault() {
let address = this.data.address;
address.isDefault = !address.isDefault;
this.setData({
address: address
});
},
getAddressDetail() {
let that = this;
util.request(api.AddressDetail, {
id: that.data.addressId
}).then(function(res) {
if (res.errno === 0) {
if (res.data) {
that.setData({
address: res.data
});
}
}
});
},
wxChooseAddress() {
let that = this;
let address = this.data.address;
// 用户已经同意小程序使用地址功能
wx.chooseAddress({
success: function(res) {
address.provinceId = 99999;
address.cityId = 88888;
address.areaId = 77777;
address.name = res.userName;
address.mobile = res.telNumber;
address.provinceName = res.provinceName;
address.cityName = res.cityName;
address.areaName = res.countyName;
address.address = res.provinceName + res.cityName + res.countyName + res.detailInfo;
that.setData({
address: address,
});
}
});
},
wxAddress() {
let that = this;
// 可以通过 wx.getSetting 先查询一下用户是否授权了 "scope.address" 这个 scope
wx.getSetting({
success(res) {
if (!res.authSetting['scope.address']) {
wx.authorize({
scope: 'scope.address',
success() {
that.wxChooseAddress();
}
})
} else {
that.wxChooseAddress();
}
}
})
},
onLoad: function(options) {
let that = this;
// 页面初始化 options为页面跳转所带来的参数
console.log(options);
if (options.id && options.id != 0) {
this.setData({
addressId: options.id
});
this.getAddressDetail();
} else {
that.wxAddress();
}
},
onReady: function() {
},
cancelAddress() {
wx.navigateBack();
},
saveAddress() {
console.log(this.data.address);
let address = this.data.address;
if (address.name == '') {
util.showErrorToast('请输入姓名');
return false;
}
if (address.mobile == '') {
util.showErrorToast('请输入手机号码');
return false;
}
if (address.areaId == 0) {
util.showErrorToast('请输入省市区');
return false;
}
if (address.address == '') {
util.showErrorToast('请输入详细地址');
return false;
}
if (!check.isValidPhone(address.mobile)) {
util.showErrorToast('手机号不正确');
return false;
}
},
onShow: function() {
// 获取所在栋数
var animation = wx.createAnimation({
duration: 500,
timingFunction: 'linear',
})
this.animation = animation
util.request("https://www.xaibox.com/czbb/interface/dataInfo.php", JSON.stringify({
"param_key": {
"info_mode": "getcity_jd"
},
"secret_key": "047709aaa7df22205d818bf4c1707458"
}), "post").then(response => {
console.log(response)
that.setData({
szxqList: response.data.rs_data
})
that.setData({
szxq: response.data.data[0]
})
that.setData({
szds: response.data.data[0]['buildingList']['0']
})
that.setData({
szdsList: response.data.data[0]['buildingList']
})
})
// 获取所在地区
that.setData({
provinces: areajs,
citys: areajs[0].children,
areas: areajs[0].children ? areajs[0].children[0].children : [],
areaJson: {
provinces: {
id: 40,
name: "广东省"
},
citys: {
id: 4006,
name: "河源市"
},
areas: {
id: 400602,
name: "源城区"
}
}
})
var areas = that.data.areaJson.areas.name == null ? "" : that.data.areaJson.areas.name
var areaInfo = that.data.areaJson.provinces.name + '·' + that.data.areaJson.citys.name + '·' + areas
that.setData({
areaInfo: areaInfo,
})
},
// 点击所在地区弹出选择框
select: function(e) {
// 如果已经显示,不在执行显示动画
if (this.data.addressMenuIsShow) {
return false
} else {
// 执行显示动画
this.startAddressAnimation(true)
}
},
// 处理省市县联动逻辑
cityChange: function(e) {
// console.log(this.data.provinces)
var value = e.detail.value
var provinces = this.data.provinces
var citys = this.data.citys
var areas = this.data.areas
var provinceNum = value[0]
var cityNum = value[1]
var countyNum = value[2]
var that = this;
// console.log(provinces)
// 如果省份选择项和之前不一样,表示滑动了省份,此时市默认是省的第一组数据,
if (this.data.value[0] != provinceNum) {
var id = provinces[provinceNum].id
// console.log(citys[cityNum])
this.setData({
value: [provinceNum, 0, 0],
citys: provinces[provinceNum].children,
areas: provinces[provinceNum].children ? provinces[provinceNum].children[0].children : [],
areaJson: {
provinces: {
id: provinces[provinceNum].code,
name: provinces[provinceNum].area
},
citys: {
id: provinces[provinceNum].children[0].code,
name: provinces[provinceNum].children[0].area
},
areas: {
id: citys[cityNum].children.length > 0 ? citys[cityNum].children[0].code : null,
name: citys[cityNum].children.length > 0 ? citys[cityNum].children[0].area : null
}
}
})
} else if (this.data.value[1] != cityNum) {
// 滑动选择了第二项数据,即市,此时区显示省市对应的第一组数据
var id = citys[cityNum].id
this.setData({
value: [provinceNum, cityNum, 0],
areas: citys[cityNum].children,
areaJson: {
provinces: {
id: provinces[provinceNum].code,
name: provinces[provinceNum].area
},
citys: {
id: citys[cityNum].code,
name: citys[cityNum].area
},
areas: {
id: citys[cityNum].children.length > 0 ? citys[cityNum].children[0].code : null,
name: citys[cityNum].children.length > 0 ? citys[cityNum].children[0].area : null
}
}
})
} else {
// 滑动选择了区
this.setData({
value: [provinceNum, cityNum, countyNum],
areaJson: {
provinces: {
id: provinces[provinceNum].code,
name: provinces[provinceNum].area
},
citys: {
id: citys[cityNum].code,
name: citys[cityNum].area
},
areas: {
id: areas[countyNum].code,
name: areas[countyNum].area
}
}
})
// console.log(that.data.areaJson)
}
},
// 执行动画
startAddressAnimation: function(isShow) {
if (isShow) {
// vh是用来表示尺寸的单位,高度全屏是100vh
this.animation.translateY(0 + 'vh').step()
} else {
this.animation.translateY(40 + 'vh').step()
}
this.setData({
animationAddressMenu: this.animation.export(),
addressMenuIsShow: isShow,
})
},
// 点击地区选择取消按钮
cityCancel: function(e) {
this.startAddressAnimation(false)
},
// 点击地区选择确定按钮
citySure: function(e) {
var that = this
var city = that.data.city
var value = that.data.value
this.startAddressAnimation(false)
// console.log(that.data.areaJson)
var areas = that.data.areaJson.areas.name == null ? "" : that.data.areaJson.areas.name
// 将选择的城市信息显示到输入框
var areaInfo = that.data.areaJson.provinces.name + '·' + that.data.areaJson.citys.name + '·' + areas
that.setData({
areaInfo: areaInfo,
})
},
onHide: function() {
// 页面隐藏
},
onUnload: function() {
// 页面关闭
}
});