el-select 自定义下拉框内容,包含 input 框过滤搜索 + el-tree 树形选择,还有下拉框最大高度的改变方法,组件,且支持v-model双向绑定值(vue2,vue3双版本)

需求背景

需要再下拉框中加上搜索框和树形选择,看下图就很明显了。

解决方法就是在 el-select 中加上 el-input 和 el-tree,我这个版本是组件化的,支持 v-model 双向绑定值,可以帮助快速开发并在不同页面中使用,同时也方便优化维护,如有问题私信我,有帮助点点赞就可以啦。

代码如下:

vue2代码

<template>
  <div class="about">
    <el-select
      v-model="selectVal"
      placeholder="请选择"
      size="mini"
      clearable
      ref="selectRef"
      class="select"
      collapse-tags
      multiple
      @clear="clear"
      @remove-tag="removeTag"
      value-key="code"
      popper-class="eloption"
      :popper-append-to-body="true"
    >
      <!-- 防止无数据时显示无数据 -->
      <el-option
        key="id"
        value="1"
        label="1"
        style="position: fixed; top: -100%; z-index: -11; opacity: 0"
      >
      </el-option>

      <el-input
        class="input"
        placeholder="请输入搜索内容"
        prefix-icon="el-icon-search"
        v-model="treeFilter"
        size="mini"
        clearable
      ></el-input>
      <div style="margin-top: 50px">
        <el-tree
          :data="treeData"
          :props="defaultProps"
          @node-click="handleNodeClick"
          ref="tree"
          node-key="code"
          :filter-node-method="filterNode"
          show-checkbox
          @change="change"
        >
        </el-tree>
      </div>
    </el-select>
  </div>
</template>

<script>
/**
 *  搜索组件 (自定义下拉框内容)
 *  引入:import selectCustom from '@/components/select-custom/index.vue';
 *  使用:<selectCustom v-model="value"></selectCustom>
 *  v-model 绑定所需要的值
 */

export default {
  name: "selectCustom",
  props: {
    modelValue: {
      type: String,
      default: "",
    },
  },
  data() {
    return {
      selectVal: "", // select框的绑定值
      selectName: "", // select框显示的name
      treeFilter: "", // 搜索框绑定值,用作过滤
      // 展示的 porps
      defaultProps: {
        label: "label",
        children: "children",
      },
      // 树形控件数据
      treeData: [
        {
          code: "1",
          label: "根节点1",
          children: [
            {
              code: "1-1",
              label: "子节点1",
              children: [
                { code: "1-1", label: "孙子节点1" },
                { code: "1-2", label: "孙子节点2" },
              ],
            },
            {
              code: "1-2",
              label: "子节点2",
              children: [
                { code: "1-2-1", label: "孙子节点4" },
                { code: "1-2-2", label: "孙子节点3" },
              ],
            },
          ],
        },
        {
          code: "2",
          label: "根节点2",
          children: [
            {
              code: "2-1",
              label: "子节点2-1",
              children: [
                { code: "2-1-1", label: "孙子节点2-1-1" },
                { code: "2-1-2", label: "孙子节点2-1-2" },
              ],
            },
            {
              code: "2-2",
              label: "子节点2",
              children: [
                { code: "2-2-1", label: "孙子节点2-2-1" },
                { code: "2-2-2", label: "孙子节点2-2-2" },
              ],
            },
          ],
        },
      ],
    };
  },
  watch: {
    // 搜索过滤,监听input搜索框绑定的treeFilter
    treeFilter(val) {
      this.$refs.tree.filter(val);
      // 当搜索框键入值改变时,将该值作为入参执行树形控件的过滤事件filterNode
    },
    modelValue(newV, oldV) {
      if (!newV) {
        this.selectVal = [];
        this.selectName = "";
        this.$refs.tree.setCheckedKeys([]);
      }
    },
  },
  methods: {
    /** 移除标签 */
    removeTag(val) {
      // 设置删除的值选中为false
      this.$refs.tree.setChecked(val.code, false);
      // 过滤空数据
      const list = this.$refs.tree.getCheckedKeys().filter((item) => {
        return item;
      });
      this.$emit("update:modelValue", list);
    },
    /** 获取数结构 */
    getTreeData() {
      /* 获取数据 */
      // treeData.value = [];在此处获取所需要的数据
    },
    // 模糊查询(搜索过滤),实质为筛选出树形控件中符合输入条件的选项,过滤掉其他选项
    filterNode(value, data) {
      if (!value) return true;
      const filterRes = data.label.indexOf(value) !== -1;
      return filterRes;
    },
    // 改变选中
    change() {
      const arr = []; // 选中的列表数据
      const list = []; // 选中的code列表
      this.$refs.tree.getCheckedNodes().forEach((item) => {
        // 只获取第三级的数据
        if (!item.children) {
          arr.push(item);
          list.push(item.code);
        }
      });
      this.selectVal = arr;
      this.$emit("update:modelValue", list);
    },
    // 清空选中
    clear() {
      this.$refs.tree.setCheckedKeys([]);
      this.$emit("update:modelValue", []);
    },
  },

  mounted() {
    this.getTreeData(); // 获取职位类型数据并渲染到树形控件中
  },
};
</script>

<style scoped lang="scss">
.input {
  width: 100%;
  position: absolute;
  top: 0px;
  z-index: 1;
  padding: 10px 4% 10px 4%;
  background: #fff;
}

/** 防止滑动时复选框可以看见 */
:deep(.el-tree-node__content) {
  .el-checkbox {
    z-index: 0;
  }
}

.select {
  width: 350px;
}
</style>

<style>
.eloption .el-select-dropdown__wrap {
  // max-height: 350px !important; // 最大高度限制
}
</style>

树形结构数据可以在 getTreeData 方法中自己获取数据,并对数据进行相应的转换。

Vue3代码

<template>
  <div class="about">
    <el-select
      v-model="selectVal"
      placeholder="请选择"
      size="mini"
      clearable
      ref="selectRef"
      collapse-tags
      multiple
      @clear="clear"
      @remove-tag="removeTag"
      value-key="code"
      popper-class="eloption"
      :popper-append-to-body="true"
      style="width: 100%"
    >
      <!-- 防止无数据时显示无数据 -->
      <el-option
        key="id"
        value="1"
        label="1"
        style="position: fixed; top: -100%; z-index: -11; opacity: 0"
      >
      </el-option>

      <el-input
        class="input"
        placeholder="请输入搜索内容"
        prefix-icon="el-icon-search"
        v-model="treeFilter"
        size="mini"
        clearable
      ></el-input>
      <div style="margin-top: 50px">
        <el-tree
          :data="treeData"
          :props="defaultProps"
          @node-click="handleNodeClick"
          ref="treeRef"
          node-key="code"
          :filter-node-method="filterNode"
          show-checkbox
          @change="change"
        >
        </el-tree>
      </div>
    </el-select>
  </div>
</template>

<script setup>
/**
 *  搜索组件 (自定义下拉框内容)
 *  引入:import selectCustom from '@/components/select-custom/index.vue';
 *  使用:<selectCustom v-model="value"></selectCustom>
 *  v-model 绑定所需要的值
 */
import { ref, watch, onMounted } from "vue";

const props = defineProps({
  modelValue: {
    type: String,
    default: "",
  },
});

const selectVal = ref(""); // select框的绑定值
const selectName = ref(""); // select框显示的name
const treeFilter = ref(""); // 搜索框绑定值,用作过滤
// 展示的 porps
const defaultProps = ref({
  label: "label",
  children: "children",
});

const treeRef = ref(); // el-tree

const emit = defineEmits(["update:modelValue"]);

// 树形控件数据
const treeData = ref([
  {
    code: "1",
    label: "根节点1",
    children: [
      {
        code: "1-1",
        label: "子节点1",
        children: [
          { code: "1-1", label: "孙子节点1" },
          { code: "1-2", label: "孙子节点2" },
        ],
      },
      {
        code: "1-2",
        label: "子节点2",
        children: [
          { code: "1-2-1", label: "孙子节点4" },
          { code: "1-2-2", label: "孙子节点3" },
        ],
      },
    ],
  },
  {
    code: "2",
    label: "根节点2",
    children: [
      {
        code: "2-1",
        label: "子节点2-1",
        children: [
          { code: "2-1-1", label: "孙子节点2-1-1" },
          { code: "2-1-2", label: "孙子节点2-1-2" },
        ],
      },
      {
        code: "2-2",
        label: "子节点2",
        children: [
          { code: "2-2-1", label: "孙子节点2-2-1" },
          { code: "2-2-2", label: "孙子节点2-2-2" },
        ],
      },
    ],
  },
]);

watch(treeFilter, (val) => {
  treeRef.value.filter(val);
  // 当搜索框键入值改变时,将该值作为入参执行树形控件的过滤事件filterNode
});

watch(props.modelValue, (newV, oldV) => {
  if (!newV) {
    selectVal.value = [];
    selectName.value = "";
    treeRef.value.setCheckedKeys([]);
  }
});

/** 移除标签 */
const removeTag = (val) => {
  // 设置删除的值选中为false
  treeRef.value.setChecked(val.code, false);
  // 过滤空数据
  const list = treeRef.value.getCheckedKeys().filter((item) => {
    return item;
  });
  emit("update:modelValue", list);
};

/** 获取数结构 */
const getTreeData = () => {
  /* 获取数据 */
  // treeData.value = [];在此处获取所需要的数据
};

// 模糊查询(搜索过滤),实质为筛选出树形控件中符合输入条件的选项,过滤掉其他选项
const filterNode = (value, data) => {
  if (!value) return true;
  const filterRes = data.label.indexOf(value) !== -1;
  return filterRes;
};

// 改变选中
const change = () => {
  const arr = []; // 选中的列表数据
  const list = []; // 选中的code列表
  treeRef.value.getCheckedNodes().forEach((item) => {
    // 只获取第三级的数据
    if (!item.children) {
      arr.push(item);
      list.push(item.code);
    }
  });
  selectVal.value = arr;
  emit("update:modelValue", list);
};
// 清空选中
const clear = () => {
  treeRef.value.setCheckedKeys([]);
  emit("update:modelValue", []);
};

onMounted(() => {
  getTreeData(); // 获取职位类型数据并渲染到树形控件中
});
</script>

<style scoped lang="scss">
.input {
  width: 100%;
  position: absolute;
  top: 0px;
  z-index: 1;
  padding: 10px 4% 10px 4%;
  background: #fff;
}

/** 防止滑动时复选框可以看见 */
:deep(.el-tree-node__content) {
  .el-checkbox {
    z-index: 0;
  }
}
</style>

<style>
.eloption .el-select-dropdown__wrap {
  max-height: 350px !important;
}
</style>

树形结构数据可以在 getTreeData 方法中自己获取数据,并对数据进行相应的转换。

解决最大高度限制

如果下拉框内容较多,显示区域较小,就无法一次性看到更多内容,如下图

解决方案:

在 el-select 中添加自定义样式,在 css 中改变样式

 <el-select
      ***
      popper-class="eloption"
      :popper-append-to-body="true"
    >
</el-select>
<style>
.eloption .el-select-dropdown__wrap {
  max-height: 350px !important;
}
</style>

修改后

在上面代码css中就有这条注释代码,可根据需要自己调

感谢观看,有问题可以联系我。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值