起因
微信小程序雖然已經(jīng)有現(xiàn)成的封裝好的省市區(qū)選擇器給開發(fā)者使用摊溶,然鵝不幸的是,微信地址庫(kù)的數(shù)據(jù)和公司用的地址庫(kù)數(shù)據(jù)很難一一對(duì)上玖雁,那就只能擼起袖子自己寫個(gè)組件了更扁。
最終效果
最終效果
思維導(dǎo)圖
思維導(dǎo)圖
主要代碼
組件 region-picker.js
/* region-picker.js */
import area from '本地 json 數(shù)據(jù)';
Component({
properties: {
showRegion: {
type: Boolean,
observer: function(newVal, oldVal) {
this.setData({
dialog: newVal,
});
},
},
regionValue: {
type: Array,
value: [],
observer: function(newVal, oldVal) {
if (newVal.length > 0) {
let select = -1;
for (let i = newVal.length - 1; i >= 0; i--) {
if (newVal[i].id !== '') {
select = i;
break;
}
}
// 除最低級(jí)別區(qū)(select = 2)以外,需要獲取當(dāng)前級(jí)別下一級(jí)的數(shù)據(jù)
this.setData({
['region.tabs']: newVal,
['region.select']: select < 2 ? select+1 : select,
}, () => {
this.setData({
area: this.getChildArea(select < 2 ? select+1 : select),
});
});
}
},
},
},
data: {
dialog: false,
area: area,
region: {
tabs: [
{
name: '請(qǐng)選擇',
id: '',
},
{
name: '請(qǐng)選擇',
id: '',
},
{
name: '請(qǐng)選擇',
id: '',
},
],
select: 0,
},
},
methods: {
// 關(guān)閉 picker 觸發(fā)的方法
emitHideRegion: function() {
if (this.data.region.tabs[2].id === '') {
wx.showToast({
title: '請(qǐng)選擇所在地',
icon: 'none',
duration: 2000,
});
return false;
}
let myEventDetail = {}; // detail對(duì)象赫冬,提供給事件監(jiān)聽函數(shù)
let myEventOption = {}; // 觸發(fā)事件的選項(xiàng)
this.setData({
dialog: !this.data.dialog,
});
myEventDetail = {
showRegion: this.data.dialog,
regionValue: this.data.region.tabs,
};
this.triggerEvent('myevent', myEventDetail, myEventOption);
},
bindRegionChange: function(e) {
// 獲取當(dāng)前選中項(xiàng)的name和id并賦值給data中的數(shù)據(jù)
let id ='region.tabs[' + this.data.region.select + '].id';
let name ='region.tabs[' + this.data.region.select + '].name';
this.setData({
[id]: e.target.dataset.id,
[name]: e.target.dataset.name,
});
// 除了三級(jí)以外的需要獲取對(duì)應(yīng)子選項(xiàng)
if (this.data.region.select < 2) {
this.setData({
['region.select']: ++this.data.region.select,
}, () => {
// 獲取子選項(xiàng)
this.setData({
area: this.getChildArea(this.data.region.select),
});
});
} else {
// 三級(jí)選項(xiàng)選擇完畢關(guān)閉省市區(qū)選擇器
this.emitHideRegion();
}
},
getChildArea: function(level) {
let _id = '';
// 默認(rèn)取完整的數(shù)據(jù)
let _area = area;
// 根據(jù)層級(jí)取當(dāng)前層級(jí)下的數(shù)據(jù)
for (let i = 0; i < level; i++) {
_id = this.data.region.tabs[i].id;
for (let j = 0; j < _area.length; j++) {
if (_area[j].id === _id) {
_area = _area[j]._child;
break;
}
}
}
return _area;
},
// 省市區(qū)tab切換
changeRegionLevel: function(e) {
let level = e.target.dataset.level;
// 三級(jí)選項(xiàng)的tab點(diǎn)擊無效果
if (level === 2) return false;
// 當(dāng)前選中tab和級(jí)別小于當(dāng)前選中tab的狀態(tài)都置為初始化狀態(tài)
for (let i = level; i < 3; i++) {
let string = 'region.tabs['+ i +']';
this.setData({
[string]: {
name: '請(qǐng)選擇',
id: '',
},
});
}
this.setData({
['region.select']: level,
});
this.setData({
area: this.getChildArea(level),
});
},
},
});
組件 region-picker.wxml
/* region-picker.wxml */
<view class="free-dialog {{dialog ? 'free-dialog--show' : ''}}">
<view class="free-dialog__mask" bindtap="emitHideRegion"></view>
<view class="free-dialog__container">
<view class="free-dialog__container__header">
<view>選擇所在地區(qū)</view>
<image
src="自行替換36rpx*36rpx的x圖標(biāo)"
class="close"
bindtap="emitHideRegion">
</image>
</view>
<view class="free-dialog__container__content">
<view class="free-content {{isIphoneX ? 'ipx' : ''}}">
<view class="free-content__tabs">
<view
class="free-content__tabs__tab {{region.select === index ? 'select' : ''}}"
wx:for="{{region.tabs}}"
wx:key="{{index}}"
wx:if="{{index <= region.select}}"
data-level="{{index}}"
bindtap="changeRegionLevel">
{{item.name}}
</view>
</view>
<scroll-view scroll-y class="free-content__scroll">
<view
class="free-content__scroll__item"
wx:for="{{area}}"
wx:key="id"
data-id="{{item.id}}"
data-name="{{item.name}}"
bindtap="bindRegionChange">
{{item.name}}
</view>
</scroll-view>
</view>
</view>
</view>
</view>
組件 region-picker.wxss
/* region-picker.wxss */
.free-dialog__mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 10;
background: rgba(0, 0, 0, 0.7);
display: none;
}
.free-dialog__container {
position: fixed;
left: 0;
bottom: 0;
width: 100%;
background: #F1F1F1;
transform: translateY(150%);
transition: all 0.4s ease;
z-index: 11;
}
.free-dialog--show .free-dialog__container {
transform: translateY(0);
}
.free-dialog--show .free-dialog__mask {
display: block;
}
.free-dialog__container__header {
padding: 24rpx 30rpx;
text-align: center;
background: white;
}
.free-dialog__container__header .close {
position:absolute;
right:30rpx;
top:31rpx;
width:36rpx;
height:36rpx;
}
.free-content {
background: white;
border-bottom: 40rpx solid white;
}
.free-content.ipx {
border-bottom: 72rpx solid white;
}
.free-content__tabs__tab {
display: inline-block;
padding: 12rpx 46rpx;
font-size: 32rpx;
color: #333;
border-bottom: 4rpx solid white;
}
.free-content__tabs__tab.select {
border-color: #FA263C;
}
.free-content__scroll {
padding: 0 40rpx;
height: 480rpx;
box-sizing: border-box;
}
.free-content__scroll__item {
margin-top: 40rpx;
height: 40rpx;
line-height: 40rpx;
font-size: 28rpx;
color: #333;
}
頁(yè)面的 WXML
/* 頁(yè)面的 WXML */
<view bindtap="chooseRegion">請(qǐng)選擇</view>
<view>
<text wx:if="{{regionValue[0].id}}">{{regionValue[0].name}}</text>
<text wx:if="{{regionValue[1].id}}">{{regionValue[1].name}}</text>
<text wx:if="{{regionValue[2].id}}">{{regionValue[2].name}}</text>
</view>
...
<region-picker
region-value="{{regionValue}}"
show-region="{{showRegion}}"
bind:myevent="emitHideRegion">
</region-picker>
頁(yè)面的 js
/* 頁(yè)面的 js */
Page({
data: {
regionValue: [],
showRegion: false,
},
chooseRegion: function() {
this.setData({
showRegion: true,
});
},
emitHideRegion: function(e) {
this.setData({
showRegion: e.detail.showRegion,
regionValue: e.detail.regionValue,
});
},
});
總結(jié)
需要注意下的是浓镜,最低級(jí)別區(qū)級(jí)別是個(gè)特殊的臨界點(diǎn),因?yàn)閰^(qū)后面沒有更低級(jí)別劲厌,所以不需要獲取下一級(jí)別的數(shù)據(jù)膛薛,也不能觸發(fā) tab 事件。
然后父組件傳遞子自組件的值补鼻,如果后期父組件變更了這個(gè)值哄啄,子組件可以在響應(yīng)函數(shù) observer 里監(jiān)聽到值的變化。
我本次使用的本地省市區(qū) JSON 數(shù)據(jù)格式為:
/* area.js */
module.exports = [{
id: '...',
name: '...',
_child: [{
id: '...',
name: '...',
_child: [{
id: '...',
name: '...'
}, ...]
}, ...]
}, ...]
寫的不是特別好风范,也希望能幫助到有需要的人吧咨跌,有疑問戳微信小程序官方文檔,沒有什么比官方文檔更靠譜的了硼婿!