vue3 element plus 可筛选树选择

1,301 阅读1分钟

最近在学vue,公司项目采用的是webpack+vue3+element plus,之前用的时候element plus还没出treeSelect相关的插件,就自己用select和tree封了个下拉树选择的组件,当然也可以用官方新出的treeSelect。

ps:公司只有我一个小前端,就没采用ts,边学边做喽

最终效果:可以搜索的下拉树选择

截屏2022-06-09 14.06.45.png

element plus版本:v2.1.11

element plus版本和文档好像都在飞快的更新中,不同版本可能会有意想不到的bug哈哈哈哈

<!--下拉树选择-->
<template>
  <el-select
    collapse-tags
    v-model="statusData"
    placeholder="请选择"
    @remove-tag="removeTag"
    class="treeDataWrap"
  >
    <div class="filterWrap">
      <el-input v-model="filterText" placeholder="请检索关键字" />
    </div>
    <el-option
      style="height: auto; padding: 0; margin-right: 10px; margin-top: 50px"
    >
      <el-tree
        width="100%"
        :filter-node-method="filterNode"
        :data="treeData"
        show-checkbox
        node-key="id"  // 根据实际项目指定
        ref="treeRef"
        highlight-current
        :props="defaultProps" // 根据实际项目指定
        @check-change="handleCheckChange"
        @check="onCheck"
      ></el-tree>
    </el-option>
  </el-select>
</template>
<script>
import {
  ref,
  toRefs,
  reactive,
  watch,
  nextTick,
  onMounted,
  computed,
} from "vue";
import { useStore } from "vuex";
import isEmpty from "lodash/isEmpty";
export default {
  name: "treeDataSelect",
  props: {
    modelValue: {
      type: Object,
    },
    // 显示复选框情况下,是否严格遵循父子不互相关联
    checkStrictly: {
      type: Boolean,
      default() {
        return false;
      },
    },
  },
  setup(props, { emit }) {
    const store = useStore();
    const treeRef = ref(null);
    const filterText = ref("");
    const statusData = ref(null);

    const defaultProps = {
      children: "children",
      label: "text",
    };

    const treeData = [
      {
        id: "1",
        text: "标签1",
        children: [
          {
            id: "1-1",
            text: "标签1-1",
          },
          {
            id: "1-2",
            text: "标签1-2",
          },
          {
            id: "1-3",
            text: "标签1-3",
          },
        ],
      },
      {
        id: "2",
        text: "标签2",
        children: [
          {
            id: "2-1",
            text: "标签2-1",
          },
          {
            id: "2-2",
            text: "标签2-2",
          },
        ],
      },
      {
        id: "3-1",
        text: "标签3-1",
      },
    ];

    const treeSelectData = computed(() => store.state.global.treeSelectData);

    watch(filterText, (val) => {
      treeRef.value.filter(val);
    });

    // 下拉选择框是作为子组件嵌入到表单里面的,会有一些和父组件表单的交互,清空、表单值更新等
    // 多选清空
    watch(statusData, (val) => {
      nextTick(() => {
        if (isEmpty(val)) {
          treeRef.value.setCheckedNodes([]);
          emit("update:modelValue", []);
        }
      });
    });

    watch(treeSelectData, (val) => {
      nextTick(() => {
        if (isEmpty(val)) {
          treeRef.value.setCheckedNodes([]);
          emit("update:modelValue", []);
        }
      });
    });

    const filterNode = (value, data) => {
      if (!value) return true;
      return data.text.indexOf(value) !== -1;
    };

    const handleCheckChange = () => {
    // 树选择时的操作,需根据实际业务
      let response = treeRef.value.getCheckedNodes(false, true);
      let labelData = [];
      let selectedIds = [];
      response.forEach((item) => {
        labelData.push(item.text);
        selectedIds.push(item.id);
      });
      emit("update:modelValue", selectedIds);
      store.commit("global/UPDATE_DATA", {
        treeSelectData: selectedIds,
      });
      statusData.value = labelData;
    };

    const removeTag = (val) => {
      console.log(val, "清空");
    };

    const onCheck = (checkedObj, checkedData) => {
      console.log("oncheck", checkedObj, checkedData);
      const { checkedKeys, halfCheckedKeys } = checkedData;
      // 由于实际业务全选和全不选的效果一致,这里增加了是否全选的判断,可以根据实际业务取舍
      if (checkedKeys.length > 0 && halfCheckedKeys.length == 0) {
        // 选中根结点
        console.log("选中根结点");
        store.commit("global/UPDATE_DATA", {
          allSelected: true,
        });
      } else {
        store.commit("global/UPDATE_DATA", {
          allSelected: false,
        });
      }
    };

    return {
      ...toRefs(state),
      handleCheckChange,
      defaultProps,
      data,
      treeRef,
      filterText,
      filterNode,
      removeTag,
      statusData,
      onCheck,
      treeData,
    };
  },
};
</script>
<style lang="scss" scoped>
.filterWrap {
  padding: 0 10px;
  margin-bottom: 10px;
}
.treeDataWrap {
  position: relative;
}
.treeDataWrap :deep(.el-select-tags-wrapper) {
  display: flex;
}
.filterWrap {
  padding-top: 10px;
  padding-bottom: 8px;
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  z-index: 999;
  background: #fff;
  box-sizing: border-box;
}
</style>

表单使用:

  <el-form ref="searchForm" :inline="false" style="width: 100%">
    <el-row>
      <el-col :md="6" :sm="12">
        <el-form-item label="树选择:">
          <TreeDataSelect
            multiple
            :checkStrictly="true"
            v-model="valueData"
            clearable
          ></TreeDataSelect>
        </el-form-item>
      </el-col>
    </el-row>
  </el-form>