weex 實現仿ios 三級聯動地址選擇器
這裡要實現一個weex 的 一個自定義的三級聯動元件,這裡囉嗦一句為什麼使用 vue 去自定義,一般使用weex的情況下,native也是支援原生擴充套件的,而且相對 android 和ios 各種第三方的元件選擇很多不少還很成熟,為什麼不直接使用呢。
這裡我使用weex的原則是能夠使用vue解決的問題一定不拋給native,原因如下,第一,使用vue 寫的ui能夠更好的保證android ios 的介面的統一性。第二,維護修改只需要維護一個地方,成本相對低很多,我們可能會遇到過這樣的情況,今天說好了我們是顏色是xxx等等,還沒有到下午,產品就要求換成另一個顏色,或者大小,如果是兩份 native的,就需要修改兩個地方了,相對就會麻煩很多,尤其是遇到更大的變動的時候,所以原則上,不到萬不得已,不適用native 的自定義元件
好了,下面說一下這個元件的實現,基於很多前端入手或者剛剛入門的同學基礎比較薄弱,所以會從最基礎的一步一步說明
第一: 資料,資料本來呢一般都是從伺服器獲取的省市區資訊(為什麼從伺服器去拿,因為這個地址一般是作為收貨地址等等的,伺服器需要做一些繫結等等的操作),這邊我們主要寫的是元件,所以資料就使用本地資料了
稍微解釋一下資料 ,省份列表就是一個 Array,裡面放了所有省份資訊
城市資料則是一個map,每一個省會對應n個市 通過省份的 recordid 去獲得的 value value則是一個市的 Array,區也是一樣,是一個map,拿市的id可以獲取到市下面的所有的區資訊
第二:UI
控制元件的ui,當顯示這個控制元件的時候有一層遮罩,遮罩上層顯示有三個並排的list 對應省市區的list,選擇完成之後點選確定,獲取到選擇的結果
<template>
<div class="stories-view" append="tree" :style="{height:`${totalheight}px`}">
<div class="list-mask" :style="{height:`${totalheight-80}px`}" @click="unselectedaddress"></div>
<text class="addbutton basebutton" @click="selectedaddress" >確定</text>
<div class="select-item" >
<list class="listitem">
<cell v-for="(item, index) in proviceList" append="tree" @click="selectprovince(index)">
<text class="cityitem" :style="{color:(index === selectindex)?'#00BBE4':'gray'}"> {{item.name}} </text>
</cell>
</list>
<list class="listitem">
<cell v-for="(item ,index) in cityList" append="tree" @click="selectcity(index)">
<text class="cityitem" :style="{color:(index === selectcityindex)?'#00BBE4':'gray'}"> {{item.name}} </text>
</cell>
</list>
<list class="listitem">
<cell v-for="(item, index) in disList" append="tree" @click="selectdist(index)">
<text class="cityitem" :style="{color:(index === selectdisindex)?'#00BBE4':'gray'}"> {{item.name}} </text>
</cell>
</list>
</div>
</div>
</template>
<style>
.stories-view {
min-height:250px;
overflow-y:auto;
}
.list-mask{
position: absolute;
top: 0;
left: 0;
width: 750px;
z-index: 10;
background-color: black;
opacity: 0.65;
}
.select-item{
flex-direction: row;
flex-wrap: nowrap;
position: absolute;
background-color: white;
align-items: center;
justify-content: center center;
bottom: 80px;
height: 600px;
width: 750px;
z-index:101;
opacity: 1;
}
.listitem{
max-height: 500px;
margin-top: 20px;
margin-bottom: 20px;
width: 250px;
max-height: 500px;
flex-grow:1;
}
.cityitem{
color: gray;
text-align: center;
padding-top: 10px;
padding-bottom: 10px;
font-size: 32px;
}
.addbutton{
bottom: 0px;
width:750px;
padding-top: 18px;
text-align: center;
}
.basebutton{
color:white;
background-color: #00BBE4;
position: absolute;
font-size:32px;
height:80px;
}
</style>
<script>
export default {
props: {
proviceList: {
type: Array,
required: true
},
cityListMap: {
type: Object,
required: true
},
disListMap: {
type: Object,
required: true
}
},
data() {
return{
selectindex:0,
selectcityindex:0,
selectdisindex:0,
cityList:[],//當前市列表
disList:[], // 當前區列表
selectedprovince:{},
selectedcity:{},
selecteddist:{},
}
},
methods: {
selectedaddress(){
// this.isselectaddress = false //關閉選擇框
this.selectplace = proviceList[this.selectindex].name+' '+ this.cityList[this.selectcityindex].name +' ' + this.disList[this.selectdisindex].name
this.$emit('haveselectedaddress',this.selectplace);
},
unselectedaddress(){
this.$emit('haveselectedaddress','');
},
selectprovince(index){
this.selectedprovince = this.proviceList[index]
//顯示 市和區
this.cityList = this.cityListMap[this.proviceList[index].recordId]
this.disList = this.disListMap[this.cityList[0].recordId]
this.selectindex = index;
},
selectcity(index){
this.selectedcity = this.cityList[index]
//顯示區
this.disList = this.disListMap[this.cityList[index].recordId]
this.selectcityindex = index
},
selectdist(index){
this.selecteddist = this.disList[index]
this.selectdisindex = index
}
},
computed: {
totalheight(){
const height = 750/weex.config.env.deviceWidth*weex.config.env.deviceHeight
console.error('height:'+height)
return height
}
},
created(){
this.cityList = cityListMap[proviceList[this.selectindex].recordId]
this.disList = disListMap[this.cityList[this.selectcityindex].recordId]
}
}
</script>
上面是這個 控制元件的程式碼了,vue寫的少,格式不好莫要賤笑
第三:使用
<template>
<div >
<text class="title" @click="update" >{{date}}</text>
<selectvue class="list-mask" v-if="isselectaddress" :proviceList="proviceList" :cityListMap="cityListMap" :disListMap="disListMap" @haveselectedaddress="selectedaddress"></selectvue>
</div>
</template>
<style>
.title { font-size: 48px; }
.list-mask{
position: absolute;
top: 0;
left: 0;
width: 750px;
z-index: 10;
background-color: black;
opacity: 0.65;
}
</style>
<script>
import selectvue from './compent/select_address.vue'
export default {
components:{selectvue},
methods: {
update(e) {
this.isselectaddress = true
},
selectedaddress(evtValue){
this.isselectaddress = false
if(evtValue === ''){
return
}
this.date = evtValue
}
},
data(){
return{
proviceList:[],
cityListMap:{},
disListMap:{},
isselectaddress: false,
date:'點選選擇地址'
}
},
mounted(){
this.proviceList = global.proviceList
this.cityListMap = global.cityListMap
this.disListMap = global.disListMap
}
}
</script>
這裡是使用這個元件,有一點很重要,元件的位置 position: absolute;
一定要是absolute
剩下的就是 v-if 控制一下顯示了
tips
為了在playground 上面顯示,元件的高度需要減掉 180
<div class="stories-view" append="tree" :style="{height:`${totalheight-180}px`}">
因為高度是有一個標題欄是原生的,需要把裝置的高度減去原生這部分的高度,否則顯示會不全,放到專案裡面使用就沒有問題了
元件的幾個小知識點
一個是傳值,通過 props
props: {
proviceList: {
type: Array,
required: true
},
cityListMap: {
type: Object,
required: true
},
disListMap: {
type: Object,
required: true
}
},
當然這個可以用一個object,自己去優化啦
傳值的時候參考引用的介面
然後就是元件給頁面返回值,點選確定之後,需要把獲取到的省市區資訊返回給頁面來顯示,這裡有一個 $emit 提交,然後在父vue裡面監聽得到一下
<selectvue class="list-mask" v-if="isselectaddress" :proviceList="proviceList" :cityListMap="cityListMap" :disListMap="disListMap" @haveselectedaddress="selectedaddress"></selectvue>
selectedaddress 方法實現
selectedaddress(evtValue){
this.isselectaddress = false
if(evtValue === ''){
return
}
this.date = evtValue
}
好了,到這裡就已經完成這個簡單元件了