項目中經常遇到要選擇城市。用到三級聯動的方式 微信小程式的 組件 是三級聯動的,但是無法自定義,這讓我們心痛不已,值得我們欣慰的 picker view 組件是可以自定義添加多個選項,但還是無法聯動。既然這樣那就自己寫一個聯動。 做到如下圖所示: 分為動態獲取地址 引用靜態文件獲取地址 <! mor ...
項目中經常遇到要選擇城市。用到三級聯動的方式
- 微信小程式的
picker
組件mode=date
是三級聯動的,但是無法自定義,這讓我們心痛不已,值得我們欣慰的 picker-view 組件是可以自定義添加多個選項,但還是無法聯動。既然這樣那就自己寫一個聯動。 - 做到如下圖所示:
- 分為動態獲取地址
- 引用靜態文件獲取地址
- addressAdd.wxml
<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>
- addressAdd.wxss
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
};
- 使用靜態獲取的時候。js如下
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() {
// 頁面關閉
}
});
- 靜態獲取三級聯動 的話則需要文件area.js點擊下載