这里要实现一个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
"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里面监听得到一下
class="list-mask" v-if="isselectaddress" :proviceList="proviceList" :cityListMap="cityListMap" :disListMap="disListMap" @haveselectedaddress="selectedaddress">
selectedaddress 方法实现
selectedaddress(evtValue){
this.isselectaddress = false
if(evtValue === ''){
return
}
this.date = evtValue
}
好了,到这里就已经完成这个简单组件了
资源下载地址