el-select 下拉树封装

el-select 下拉树封装

安装教程

npm i zwd-vue2-el-plus

使用说明
import Vue from "vue"; 
import zwdElPlus from "zwd-vue2-el-plus";
Vue.use(zwdElPlus);

组件说明

ZwdTreeSelect

使用方式和 element-plus的el-tree-select 一样。

API
Attributes

组件是el-treeel-select的结合体,原始属性未被更改。请跳转查看原组件的相应文档。

属性方法事件插槽
treetreetreetree
selectselectselectselect
<template>
  <div id="app">
    <div>
      <ZwdTreeSelect
        check-on-click-node
        collapse-tags
        show-checkbox
        multiple
        clearable
        highlight-current
        v-model="value"
        :data="data"
        node-key="value"
      ></ZwdTreeSelect>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      value: [44],
      data: [
        {
          label: "212",
          value: 11,
          children: [
            { label: "234", value: 33 },
            { label: "444", value: 44 },
          ],
        },
        { label: "333", value: 22 },
      ],
    };
  },
};
</script>

组件使用参考element-plus

https://element-plus.org/zh-CN/component/tree-select.html

源码

<!--
 * @Author: zhaowendi
 * @Date: 2023-10-07 16:14:27
 * @LastEditTime: 2023-10-20 15:42:39
 * @Description: 
-->
<template>
  <el-select
    ref="select"
    v-bind="selectProps"
    v-on="$listeners"
    :filter-method="selectFilterMethod"
  >
    <template #prefix>
      <slot name="prefix"></slot>
    </template>
    <template #empty>
      <slot name="empty"></slot>
    </template>
    <el-option
      v-for="item in treeFlatData"
      :key="item[propsMap.value]"
      :label="item[propsMap.label]"
      :value="item[propsMap.value]"
      hidden
    >
    </el-option>
    <el-tree
      ref="tree"
      v-bind="treeProps"
      @check="check"
      @node-click="nodeClick"
      :filter-node-method="treeFilterNodeMethod"
      v-on="$listeners"
    >
      <template #default="slotProps">
        <slot v-bind="slotProps"></slot>
      </template>
    </el-tree>
  </el-select>
</template>

<script>
import { Select, Tree } from "element-ui";
import { useSelect } from "./select.js";
import { useTree } from "./tree.js";

import { getForestFlatData } from "zwd-tree-utils";
import { isEqual } from "lodash";
import { isValidArray, toValidArray, isValidValue, treeFind } from "./utils";

export default {
  name: "ZwdTreeSelect",
  componentName: "ZwdTreeSelect",
  // disable `ElSelect` inherit current attrs
  inheritAttrs: false,
  props: {
    ...Select.props,
    ...Tree.props,
  },
  data() {
    return {};
  },
  watch: {
    value: {
      immediate: true,
      deep: true,
      handler() {
        if (this.showCheckbox) {
          this.$nextTick(() => {
            let tree = this.$refs.tree;
            if (
              tree &&
              !isEqual(tree.getCheckedKeys(), toValidArray(this.value))
            ) {
              tree.setCheckedKeys(toValidArray(this.value));
            }
          });
        }
      },
    },
  },
  computed: {
    treeFlatData() {
      return getForestFlatData(JSON.parse(JSON.stringify(this.data)));
    },
    selectProps() {
      return useSelect(this.$props);
    },
    treeProps() {
      return useTree(this.$props);
    },
    propsMap() {
      return {
        value: this.$props.nodeKey || this.$props.valueKey || "value",
        label: "label",
        children: "children",
        disabled: "disabled",
        isLeaf: "isLeaf",
        ...this.$props.props,
      };
    },
  },
  methods: {
    selectFilterMethod(keyword) {
      if (this.filterMethod) this.filterMethod(keyword);

      this.$nextTick(() => {
        let tree = this.$refs.tree;
        // let tree node expand only, same with tree filter
        tree && tree.filter(keyword);
      });
    },
    treeFilterNodeMethod(value, data, node) {
      if (this.filterNodeMethod)
        return this.filterNodeMethod(value, data, node);
      if (!value) return true;
      return this.getNodeValByProp("label", data).includes(value);
    },

    //无复选框点击 通过handleOptionSelect设置select选中值
    nodeClick(data, node, e) {
      // `onCheck` is trigger when `checkOnClickNode`
      if (this.showCheckbox && this.checkOnClickNode) return;

      // now `checkOnClickNode` is false, only no checkbox and `checkStrictly` or `isLeaf`
      let select = this.$refs.select;

      if (!this.showCheckbox && (this.checkStrictly || node.isLeaf)) {
        if (!this.getNodeValByProp("disabled", data)) {
          const option = select.getOption(this.getNodeValByProp("value", data));
          select.handleOptionSelect(option, true);
        }
      } else if (this.expandOnClickNode) {
        e.handleExpandIconClick();
      }
    },

    //当复选框被点击的时候触发
    check(data, params) {
      // ignore when no checkbox, like only `checkOnClickNode` is true
      if (!this.showCheckbox) return;
      const dataValue = this.getNodeValByProp("value", data);

      let tree = this.$refs.tree;

      const uncachedCheckedKeys = params.checkedKeys;
      const cachedKeys = this.multiple
        ? this.value.filter(
            (item) => !tree.getNode(item) && !uncachedCheckedKeys.includes(item)
          )
        : [];
      const checkedKeys = uncachedCheckedKeys.concat(cachedKeys);

      if (this.checkStrictly) {
        this.$emit(
          "input",
          this.multiple
            ? checkedKeys
            : checkedKeys.includes(dataValue)
            ? dataValue
            : undefined
        );
      }
      // only can select leaf node
      else {
        if (this.multiple) {
          this.$emit("input", tree.getCheckedKeys(true));
        } else {
          // select first leaf node when check parent
          const firstLeaf = treeFind(
            [data],
            (data) =>
              !isValidArray(this.getNodeValByProp("children", data)) &&
              !this.getNodeValByProp("disabled", data),
            (data) => this.getNodeValByProp("children", data)
          );
          const firstLeafKey = firstLeaf
            ? this.getNodeValByProp("value", firstLeaf)
            : undefined;

          // unselect when any child checked
          const hasCheckedChild =
            isValidValue(this.value) &&
            !!treeFind(
              [data],
              (data) => this.getNodeValByProp("value", data) === this.value,
              (data) => this.getNodeValByProp("children", data)
            );
          this.$emit(
            "input",
            firstLeafKey === this.value || hasCheckedChild
              ? undefined
              : firstLeafKey
          );
        }
      }

      this.$nextTick(() => {
        const checkedKeys = toValidArray(this.value);
        tree.setCheckedKeys(checkedKeys);
        // console.log(this.$attrs);
        // this.$attrs.onCheck(data, {
        //   checkedKeys: tree.getCheckedKeys(),
        //   checkedNodes: tree.getCheckedNodes(),
        //   halfCheckedKeys: tree.getHalfCheckedKeys(),
        //   halfCheckedNodes: tree.getHalfCheckedNodes(),
        // });
      });
    },

    getNodeValByProp(prop, data) {
      const propVal = this.propsMap[prop];
      let tree = this.$refs.tree;
      if (typeof propVal == "function") {
        return propVal(
          data,
          tree.getNode(this.getNodeValByProp("value", data))
        );
      } else {
        return data[propVal];
      }
    },
  },
};
</script>

<style lang="scss" scoped></style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值