级联选择组件(一)

1,812 阅读1分钟

组件思想及功能

大致 UI 参考Ant Design,

主要交互逻辑

将要实现的使用方法

<cascader dataSource="data" onChange="onChange">
  <button>toggle</button>  <!-- 可选插槽触发器 --> 
</cascader>

确定输入数据格式

const source = [{
        name: '春天',
        children: [
          {name: '花开'},
          {name: '温暖'},
          {name: '光芒万丈'}
        ]
      },{
        name: '夏天',
        children: [
          {name: '炎热'},
          {name: '凉快'},
        ]
      }]

思考渲染数据方式

如何渲染数据的关键问题所在就是不知道数据到底与几层,就无法通过对每层进行 v-for 渲染。

递归的方式渲染

问题又出现了,如何在一个组件里使用自己呢?

可以这样,创建cascaderItem.vue 组件:

const cascaderItem = {
  name: 'cascaderItem',
  components: {
    cascaderItem: cascaderItem
  },
  props: {
    sourceItem: {
      type: Object
    }
  }
}
export default cascaderItem

就可在 template 里使用

<div>
    {{sourceItem.name}}
    <cascader-item
      v-for="item in sourceItem.children"
      v-if="sourceItem.children"
      :source-item="item"
    ></cascader-item>
  </div>

其实可以就像普通组件一样写,vue 支持在使用标签时如果和 name 相同,就默认是使用自己

export default {
 name: 'cascaderItem',
 props: {
   sourceItem: {
     type: Object
   }
 }
}

至此,就可在 cascader.vue 引入 item 组件并使用来渲染数据,先是粗糙的把数据都显示出来:

渲染弹出内容

cascaderItem.vue

<div class="cascaderItem">
    <div class="left">
      <div class="label" v-for="item in items" @click="leftSelected = item">
        {{item.name}}
      </div>
    </div>
    <div class="right" v-if="rightItems">
      <cascader-item :items="rightItems" />
    </div>
  </div>

只分左右两个容器,递归的渲染

export default {
  name: 'cascaderItem',
  props: {
    items: {
      type: Array
    }
  },
  data() {
    return {
      leftSelected: null
    }
  },
  computed: {
    rightItems() {
      if(this.leftSelected && this.leftSelected.children) {
        return this.leftSelected.children
      } else {
        return null
      }
    }
  }
}

cascader.vue 里的 popover 部分传入数据即可

<template>
  <div class="cascader">
    <div class="trigger" @click="popoverVisible=!popoverVisible"></div>
    <div class="popover" v-if="popoverVisible">
      <cascader-item :items="source" />
    </div>
  </div>
</template>

初次渲染弹出层即完成,