playground效果图

这里要实现一个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    
      }

好了,到这里就已经完成这个简单组件了

资源下载地址

Logo

前往低代码交流专区

更多推荐