element-ui 2.0 下拉树组件简单记录

element-ui 2.0 下拉树组件简单记录

开发过程中有时需要使用下拉树的功能,element-ui 2.0 没有该组件。所有根据el-select 和el-tree组件基础上做一个简单的下拉树。

大致格式如下


<template>
  <div class="select_tree_container">
    <el-select
      ref="selectRef"
      v-bind="$attrs"
      :value="modelValue"
      :multiple="multiple"
      :popper-class="'select_tree_container_popper_class' + ' ' + popperClass"
      @remove-tag="onRemoveTag"
      @clear="clearHandle">
      <el-option v-for="(item, i) in selectOptions" :key="i" hidden :value="item.id" :label="item.label"> </el-option>
      <el-tree
        :class="elTreeClass"
        ref="selectTreeRef"
        :data="data"
        :node-key="nodeKey"
        v-bind="$attrs"
        :show-checkbox="multiple"
        @node-click="handleNodeClick"
        @check-change="onCheckChange"
        @check="onCheck">
      </el-tree>
    </el-select>
  </div>
</template>

如上,使用el-option撑开弹窗高度,el-option的所有项为树的所有勾选结果,v-bind="$attrs"属性透传接收原有的el-select,el-tree的属性。

完整组件


<template>
  <div class="select_tree_container">
    <el-select
      ref="selectRef"
      v-bind="$attrs"
      :value="modelValue"
      :multiple="multiple"
      :popper-class="'select_tree_container_popper_class' + ' ' + popperClass"
      @remove-tag="onRemoveTag"
      @clear="clearHandle">
      <el-option v-for="(item, i) in selectOptions" :key="i" hidden :value="item.id" :label="item.label"> </el-option>
      <el-tree
        :class="elTreeClass"
        ref="selectTreeRef"
        :data="data"
        :node-key="nodeKey"
        v-bind="$attrs"
        :show-checkbox="multiple"
        @node-click="handleNodeClick"
        @check-change="onCheckChange"
        @check="onCheck">
      </el-tree>
    </el-select>
  </div>
</template>

<script>
import { defineComponent, onMounted, getCurrentInstance, ref, watch, nextTick } from '@vue/composition-api';

// 该 defaultOption 是为了将下拉框的高度撑起来
const defaultOption = { id: '——', label: '请选择' };

export default defineComponent({
  model: {
    prop: 'modelValue',
    event: 'update:modelValue',
  },
  props: {
    modelValue: {
      type: String | Number | Array,
    },
    // tree里面的类型的值对应的属性
    nodeKey: {
      type: String,
      default: 'id',
    },
    multiple: {
      type: Boolean,
      default: false,
    },
    popperClass: {
      type: String,
      default: '',
    },
    data: {
      type: Array,
      default: () => {
        return [];
      },
    },
    elTreeClass: {
      type: String,
      default: '',
    },
    /**
     * 是否点击父节点也选中
     */
    isSelParent: {
      type: Boolean,
      default: true,
    },
  },
  setup(props, { emit }) {
    const insp = getCurrentInstance()?.proxy;
    const selectOptions = ref([defaultOption]);
    watch(
      () => props.modelValue,
      newVal => {
        resetOptionAndTreeData(newVal);
      }
    );
    watch(
      () => props.data,
      async newData => {
        await nextTick();
        // 需要关闭下拉框,不然会导致el-select input中的文字回显失败
        insp?.$refs?.selectRef?.blur();
        resetOptionAndTreeData(props.modelValue);
      }
    );

    // 根据key值回显option数据和tree数据
    const resetOptionAndTreeData = async key => {
      await nextTick();
      resetModelOptions(key);
      setSelectOption(key);
    };

    const resetModelOptions = key => {
      if (props.multiple) {
        insp?.$refs?.selectTreeRef?.setCheckedKeys(key);
      } else {
        insp?.$refs?.selectTreeRef?.setCurrentKey(key);
      }
    };

    // 设置当前 select 的option;
    const setSelectOption = key => {
      if (props.multiple) {
        let nodes = insp?.$refs?.selectTreeRef?.getCheckedNodes();
        if (nodes) {
          selectOptions.value = [defaultOption].concat(
            nodes.map(node => ({ id: node[props.nodeKey], label: node[insp?.$attrs?.props?.label ?? 'label'] }))
          );
          // 此处可以渲染的有父节点值,需同步下子节点
          if (nodes.length && key.length && key.length !== nodes.length) {
            let newModel = insp?.$refs?.selectTreeRef?.getCheckedKeys();
            emit('update:modelValue', newModel);
          }
        }
      } else {
        let node = insp?.$refs?.selectTreeRef?.getNode(key)?.data;
        if (node) {
          selectOptions.value = [defaultOption].concat({ id: node[props.nodeKey], label: node[insp?.$attrs?.props?.label ?? 'label'] });
        }
      }
    };

    // // 切换选项
    const handleNodeClick = (node, nodeObj) => {
      if (props.multiple) {
      } else {
        if (props.isSelParent) {
          emit('update:modelValue', node[props.nodeKey]);
          insp?.$refs?.selectRef?.blur();
        } else {
          // 需要判断当前点击的是否是子节点
          if (!nodeObj.childNodes || (Array.isArray(nodeObj.childNodes) && nodeObj.childNodes.length === 0)) {
            emit('update:modelValue', node[props.nodeKey]);
            insp?.$refs?.selectRef?.blur();
          } else {
            // 此处是重置高亮,因为点击父节点会将高亮状态移动到父节点
            let key = props.modelValue || null;
            insp?.$refs?.selectTreeRef?.setCurrentKey(key);
          }
        }
      }
    };
    // // 清除选中
    const clearHandle = () => {
      if (props.multiple) {
        emit('update:modelValue', []);
        insp?.$refs?.selectTreeRef?.setCheckedKeys([]);
      } else {
        emit('update:modelValue', '');
        insp?.$refs?.selectTreeRef?.setCurrentKey(null);
      }
    };

    const onCheckChange = (val, val2, val3) => {};

    const onCheck = (node, data) => {
      emit('update:modelValue', data.checkedKeys);
    };

    const onRemoveTag = val => {
      insp?.$refs?.selectTreeRef?.setChecked(val, false, true);
      let newModel = insp?.$refs?.selectTreeRef?.getCheckedKeys();
      emit('update:modelValue', newModel);
    };

    onMounted(() => {
      resetOptionAndTreeData(props.modelValue);
    });
    return {
      selectOptions,
      handleNodeClick,
      clearHandle,
      onCheckChange,
      onCheck,
      onRemoveTag,
    };
  },
});
</script>

<style scoped lang="less">
.select_tree_container {
}
</style>

<style lang="less">
.select_tree_container_popper_class {
  min-height: 200px;
  .el-scrollbar {
    // height: 100%;
  }
}
</style>


使用组件


import SelectTree from './SelectTree.vue';

 <SelectTree
      v-model="testModel"
      :data="treeList"
      collapse-tags
      highlight-current
      node-key="testId"
      :props="propsData"
      :clearable="true"
      popper-class="test_class"></SelectTree>

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值