基于iview的自定义下拉树组件,超详细讲解

最近由于项目的需求表单里需要用到下拉树选项,但iview里面并没有这个组件,所以我基于iview的select组件和tree组件进行了二次封装,实现了这个组件。这里面也借鉴了这位朋友的思路https://www.jianshu.com/p/0ce52f43864e不过感觉他有很多坑都没有仔细说明,所以下面我将的比较详细

先说下我所实现的功能:
1.点击选择并显示到select


展开下拉框.jpg

选择完选项.jpg

2.修改回填时根据回填的id展示对应的name,并在树中选中所回填的子节点且展开这个子节点所有的父节点


修改回填时的.jpg

打开选项框展示所选择的节点并展开路径.jpg

话不多说,上代码分析。


<FormItem label="业务组">

          <selectTree

            :data="formItem.groupId"

            v-if="panel.panelVisible"

            @changeModel="backfill"

            :width="'240px'"

            :tree="businessGroupTree"

          ></selectTree>

    </FormItem>

上面代码的data属性就是表单中需要双向绑定的的值,width是调整宽度,而tree属性则是需要传入的线性数组,注意不是树形结构的数组,在这个组件中会将传入的线性数组自动转换成树形结构。
这里注意表单一般要默认清空值,所以我用了v-if来清空上一次的选择的数据,changeModel是子组件返回的函数。
下面是组件的html

<template>
  <div>
    <Select size="small" v-model="bindData" :style="{width:realWidth}">
      <Option
        v-show="false"
        v-for="(item,index) in optionList"
        :key="index"
        :value="item.value"
      >{{item.label}}</Option>
      <Tree :data="suitableTreeData" @on-select-change="handleSelectChange"></Tree>
    </Select>
    <Button ref="optionClick" v-show="false"></Button>
  </div>
</template>
<script>

上面的代码中,我用v-show将option隐藏起来,在后面加上iview的 tree组件,用的是最基础的版本,如果想要扩展可以再自己添加方法二次封装。
下面是js代码

  props: {
    data: {
      type: String
    },
    tree: {
      type: Array,
      default: () => []
    },
    width: {
      type: String | Number
    }
  },
  
  data() {
    return {
      bindData: this.data,//父组件传入的需要双向绑定的值
      optionList: [],//Select组件的option需要使用v-for遍历的数组
      parentArr: []//这个数组是保存修改时回填所需要展开的所有父节点id
    };
  },

接下来是所需要用的方法
首先需要将线性数组转化成树结构数组

setTreeData(source) {//将后台数据转化成treeData
      let cloneData = cloneDeep(source); // 对源数据深度克隆
      return cloneData.filter(father => {
        // 循环所有项,并添加children属性
        let branchArr = cloneData.filter(child => father.id == child.parentId); // 返回每一项的子级数组
        branchArr.length > 0 ? (father.children = branchArr) : ""; //给父级添加一个children属性,并赋值
        return father.parentId == ""; //返回第一层
      });
    },

然后将树数组再转话成iview所需要的格式

getTree(tree = [], selectId, parentIdArr) {//将转化的treeData转变成iview格式的TreeData
    //tree是需要传入的树数组,selectId和parentIdArr是修改回填时所需要用的参数,分别表示回填时所填入的id和这个id所有的父级节点的id
      const _this = this;
      let arr = [];
      // let pArr=parentIdArr
      if (!!tree && tree.length !== 0) {
        tree.forEach(item => {
          let obj = {};
          obj.title = item.name;
          obj.id = item.id;
          obj.expand = false;
          if (selectId == obj.id) {
            obj.selected = true;
          }
          if (!!parentIdArr && parentIdArr.length !== 0) {
            for(let k of parentIdArr){
              if (obj.id == k) {//这里表示如果此节点的id如果与parentIdArr里面的id相等就展开此节点
                obj.expand = true;
              }
            }
          }
          obj.children = _this.getTree(item.children, selectId, parentIdArr); // 递归调用
          arr.push(obj);
        });
      }
      return arr;
    },

用计算属性将转换后的tree数据传给Tree组件

computed: {
    suitableTreeData() {
      const _this = this;
      let parentarr = _this.findSelectNode(_this.setTreeData(_this.tree), _this.bindData);
      return _this.getTree(_this.setTreeData(_this.tree),_this.bindData,parentarr);
    },
    realWidth() {
      let w = this.width;
      if (!w) {
        w = "250px";
      }
      return w;
    }
  }

接下来是Tree组件的事件函数

handleSelectChange(data) {//选择树节点后返回的数据
      let value = null; //新建select数组的值
      let label = null; //新建select数组的名字
      data.forEach(item => {
        value = item.id;
        label = item.title;
        this.bindData = value;
        this.optionList = [];
        this.optionList.push({//将选择节点的数据push到数组中
          value,
          label
        });
      });
      this.$refs.optionClick.$el.click();//这行代码是解决选择了树组件选项不能收起下拉框的问题
      
      this.emitFn();//解决选择了节点后给父组件不能实时接收所发射的数据得问题
    },

发射给父组件的函数和Select组件所需要的数据

emitFn() {//还需要再mounted里面调用一次此函数
      this.optionList = this.findOption(this.bindData, this.tree);
      this.$emit("changeModel", this.bindData);
    },
findOption(id, tree) {//回填修改时Select所需要的option数据
      let findOptionList = tree
        .filter(item => {
          if (item.id === id) {
            return item;
          }
        })
        .map(item => {
          return {
            value: item.id,
            label: item.name
          };
        });
      return findOptionList;
    },

然后是回填时,找到需要展开的所有父节点的数据

findSelectNode(tree, id) {//根据子节点找到所有父节点
      const _this = this;
      if (!tree) {
        return;
      }
      tree.forEach(item => {
        if (item.id == id) {
          if (item.parentId) {
            _this.parentArr.push(item.parentId);//如果找到子节点push父节点的parentId
            _this.findSelectNode(_this.tree, item.parentId);//继续向上递归找出所有父节点
          }
        } else {
          if (item.children) {//如果当前层级找不到,存在子节点继续递归
            _this.findSelectNode(item.children, id);
          }
        }
      });
      return _this.parentArr;//找到第一层返回所有父节点
    },

以上就是我所有的代码和思路,封装这个组件里面用的递归函数比较多,还是花了不少时间,所以也想分享出来让大家少踩些坑,所以写的比较详细,篇幅比较长。这是我第一次在简书上分享,如果对大家有所帮助还希望能点个赞,哈哈

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容