最近项目中有使用到省市区选择器,因为是二开的。原项目中是直接写在页面中的...emm,我现在将其独立设计成一个组件,并优化了一些动画效果。
<template>
<view class="selector-box" @tap="close" v-show="showPopup">
<view class="main" @tap.stop :class="{ 'show-picker': showPicker, 'close-picker': !showPicker }">
<view class="operator">
<text class="cancel" @tap="close">取消</text>
<text class="confirm" @tap="confirm">确定</text>
</view>
<picker-view indicator-style="height: 50rpx;" :value="geoIndex" @change="changePicker" @tap.stop>
<!--省-->
<picker-view-column>
<view v-for="(item, index) in provArray" :key="index">{{item.areaName}}</view>
</picker-view-column>
<!--地级市-->
<picker-view-column>
<view v-for="(item, index) in cityArray" :key="index">{{item.areaName}}</view>
</picker-view-column>
<!--区县-->
<picker-view-column>
<view v-for="(item, index) in areaArray" :key="index">{{item.areaName}}</view>
</picker-view-column>
</picker-view>
</view>
</view>
</template>
<script>
const http = require('../../utils/http.js');
export default {
data() {
return {
showPopup: false,
showPicker: false,
provArray: [],
cityArray: [],
areaArray: [],
geoIndex: [0, 0, 0]
}
},
created() {
this.loadProvinces();
},
methods: {
open() {
this.showPopup = true;
setTimeout(() => {
this.showPicker = true;
})
},
close() {
this.showPicker = false;
setTimeout(() => {
this.showPopup = false;
}, 300)
},
changePicker(e) {
const [pIdx, cIdx, aIdx] = e.detail.value;
if (pIdx !== this.geoIndex[0]) {
this.loadCities(this.provArray[pIdx].areaId);
this.geoIndex[0] = pIdx;
this.geoIndex[1] = 0;
this.geoIndex[2] = 0;
} else if (cIdx !== this.geoIndex[1]) {
this.loadAreas(this.cityArray[cIdx].areaId);
this.geoIndex[1] = cIdx;
this.geoIndex[2] = 0;
} else if (aIdx !== this.geoIndex[2]) {
this.geoIndex[2] = aIdx;
}
},
confirm() {
const addressInfo = {
province: this.provArray[this.geoIndex[0]].areaName,
provinceId: this.provArray[this.geoIndex[0]].areaId,
city: this.cityArray[this.geoIndex[1]].areaName,
cityId: this.cityArray[this.geoIndex[1]].areaId,
area: this.areaArray[this.geoIndex[2]].areaName,
areaId: this.areaArray[this.geoIndex[2]].areaId,
}
this.$emit('confirm', addressInfo);
},
loadProvinces(provinceId, cityId, areaId) {
this.loadGeoList({ pid: 0 }).then(res => {
this.setData({ provArray: res });
const provId = provinceId === undefined ? res[0].areaId : provinceId;
this.loadCities(provId, cityId, areaId);
})
},
loadCities(provinceId, cityId, areaId) {
this.loadGeoList({ pid: provinceId }).then(res => {
this.setData({ cityArray: res });
const ciId = cityId === undefined ? res[0].areaId : cityId;
this.loadAreas(ciId, areaId);
})
},
loadAreas(cityId, areaId) {
this.loadGeoList({ pid: cityId }).then(res => {
this.setData({ areaArray: res });
})
},
loadGeoList(params) {
return new Promise(resolve => {
http.request({
url: "/p/area/listByPid",
method: "GET",
data: params,
callBack: res => {
resolve(res);
},
errCallBack: err => {
console.log(err);
resolve([]);
}
})
})
}
}
}
</script>
<style>
.selector-box {
position: fixed;
top: 0;
width: 100%;
height: 100vh;
background-color: rgba(0, 0, 0, .3);
}
.main {
width: 100%;
height: 480rpx;
background-color: #fff;
border-top-left-radius: 22rpx;
border-top-right-radius: 22rpx;
position: absolute;
transition: all .3s;
bottom: 0;
}
.close-picker {
bottom: -100%;
}
.show-picker {
bottom: 0;
}
.operator {
width: 100%;
padding: 0 28rpx;
box-sizing: border-box;
position: absolute;
top: 26rpx;
display: flex;
justify-content: space-between;
}
.operator .confirm {
color: #5b91e0;
}
.operator .cancel {
color: #847e7e;
}
picker-view {
width: 100%;
height: 400rpx;
position: absolute;
bottom: 0;
}
picker-view-column {
background-color: white;
width: 100vw;
height: 400rpx;
}
picker-view-column view {
vertical-align: middle;
font-size: 28rpx;
line-height: 28rpx;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
</style>