el-tree父子联动

121 阅读2分钟
//标签使用,封装为组件,改为全局使用,直接调用,
// 部门父子联动下拉树
main.jsimport VTreeSelect from "@/components/VTreeSelect";
Vue.component('VTreeSelect', VTreeSelect)


<VTreeSelect
   :disable-branch-nodes="false"
   :show-count="true"
   v-model="queryParams.like_org_id"
   :options="deptOptions"
   placeholder="请选择部门"
   clearable
   :multiple="true"
/>
//组件内容
<template>
   <div>
      <el-popover placement="bottom-start" width="300" trigger="click" @show="popoverOpen = true" @hide="popoverOpen = false">
         <div class="v_dept_tree_select_con">
            <div class="v_dept_tree_select_con_t">
               <el-checkbox v-model="isCheckStrictly">父子节点联动</el-checkbox>
            </div>

            <div class="v_dept_tree_select_con_c">
               <el-tree
                  ref="tree"
                  :data="treeOptions"
                  :show-checkbox="multiple"
                  :node-key="defaultOptions.id"
                  :check-strictly="!isCheckStrictly"
                  :props="defaultOptions"
                  @check="treeCheck"
               ></el-tree>
            </div>
         </div>

         <div slot="reference" class="v_dept_tree_select">
            <div class="v_dept_tree_select_v">
               <span v-if="checkedArr.length < 1" class="v_dept_tree_select_v_placeholder">{{ placeholder }}</span>
               <span v-else>{{ checkedArr.map(item => item[defaultOptions.label]).join() }}</span>
            </div>

            <div class="v_dept_tree_select_v_icon" @mouseenter="isClearIcon = true" @mouseleave="isClearIcon = false">
               <i v-if="!isClearIcon && clearable" class="el-icon-arrow-down v_dept_tree_select_v_arrow" :class="{ v_dept_tree_select_v_arrow_show: popoverOpen }"></i>
               <i v-else class="el-icon-circle-close" @click.stop="handleClear"></i>
            </div>
         </div>
      </el-popover>
   </div>
</template>

<script>
export default {
   model: {
      prop: "value",
      event: "change",
   },
   props: {
      value: {
         type: [String, Number, Array],
         default: ""
      },

      options: {
         type: Array,
         default: []
      },

      placeholder: {
         type: String,
         default: ""
      },

      // 父子节点是否联动
      showCheckStrictly: {
         type: Boolean,
         default: true
      },

      // 是否显示清空按钮
      clearable: {
         type: Boolean,
         default: false
      },

      // 是否多选
      multiple: {
         type: Boolean,
         default: false,
      },

      // 默认数据参数选项
      defaultOptions: {
         type: Object,
         default: () => {
            return {
               children: "children",
               label: "label",
               id: "id"
            }
         }
      }
   },
   data() {
      return {
         isCheckStrictly: true, // 父子节点是否联动
         popoverOpen: false, // 显示状态
         isClearIcon: false, // 清空按钮显示状态

         treeOptions: [], // 部门树列表
         treeArr: [], // 部门列表
      }
   },
   computed: {
      // 当前选中的数组
      checkedArr() {
         if( this.multiple ) {
            let arr = [];
            this.value && this.value.forEach(id => {
               let cItem = this.treeArr.filter(tItem => tItem[this.defaultOptions.id] === id)[0];
               if(cItem) arr.push(cItem);
            })
            return arr
         }else {
            let cItem = this.treeArr.filter(tItem => tItem[this.defaultOptions.id] === this.value)[0];
            return cItem ? [cItem] : []
         }
      }
   },
   watch: {
      showCheckStrictly: {
         immediate: true,
         handler() {
            this.isCheckStrictly = this.showCheckStrictly;
         }
      },

      options: {
         deep: true,
         immediate: true,
         handler() {
            this.treeOptions = this.options;
            this.treeArr = this.turnFormatTreeData([], this.treeOptions, 0);
         }
      }
   },
   created() {
      this.init();
   },
   methods: {
      init() {
         // 初始化是根据value,设置已选中项
         let arr = [];
         if( this.multiple ) {
            arr = this.value ? this.value : [];
         }else {
            arr = this.value ? [this.value] : []
         }
         this.$nextTick(() => {
            this.$refs.tree.setCheckedKeys(arr);
         })
      },

      /**
       * 树节点点击
       */
      treeCheck(value, {checkedKeys, checkedNodes}) {
         let arr = [];
         if(this.isCheckStrictly) {
            arr = this.turnFormatTreeData([], checkedNodes, value[this.defaultOptions.id]);
         }else {
            arr = checkedNodes;
         }
         let idArr = arr.map(item => item[this.defaultOptions.id]);
         this.$emit("change", this.multiple ? idArr : idArr[0]);
      },

      /**
       * 清空
       */
      handleClear() {
         this.$emit("change", this.multiple ? [] : "");
         this.$refs.tree.setCheckedKeys([]);
      },

      /**
       * 部门树 格式化为 单例数组
       */
      turnFormatTreeData(arr, data, pId) {
         data.forEach(item => {
            let cItem = JSON.parse( JSON.stringify(item) );
            delete cItem[this.defaultOptions.children];
            cItem.pId = pId;
            arr.push(cItem);

            if(item[this.defaultOptions.children] && item[this.defaultOptions.children].length > 0) {
               this.turnFormatTreeData(arr, item[this.defaultOptions.children], item[this.defaultOptions.id]);
            }
         })
         return arr
      },
   }
}
</script>

<style scoped lang="scss">
.v_dept_tree_select {
   width: 217px;
   height: 36px;
   border: 1px solid #DCDFE6;
   border-radius: 4px;
   padding: 0 29px 0 15px;
   cursor: pointer;
   overflow: hidden;
   position: relative;
   transition: all .3s;

   .v_dept_tree_select_v {
      width: 100%;
      color: #606266;
      font-size: 14px;
      white-space: nowrap;
      overflow: auto;
   }

   .v_dept_tree_select_v_placeholder {
      color: #c0c4cc;
   }

   .v_dept_tree_select_v_icon {
      color: #C0C4CC;
      position: absolute;
      top: 0;
      right: 9px;

      .v_dept_tree_select_v_arrow {
         transform: rotateZ(0deg);
         transition: all .3s;
      }

      .v_dept_tree_select_v_arrow_show {
         transform: rotateZ(-180deg);
      }
   }
}

.v_dept_tree_select:hover {
   border-color: #C0C4CC;
}

.v_dept_tree_select_con_t {
   padding-bottom: 5px;
   margin-bottom: 5px;
   border-bottom: 1px solid #DCDFE6;
}

.v_dept_tree_select_con_c {
   max-height: 300px;
   overflow: auto;
}
</style>