返回

Element-UI封装实现TreeSelect 支持下拉框中输入值检索

前端

前言

前些天实现的一个树形的下拉组件,可最近又让我支持下拉检索功能,查了一圈没有合适的,只好今天自己写一下,凑合能用吧,支持检索功能了最起码,可有些参数还是要配置的,使用起来比之前那一篇麻烦了一丢丢。

实现

我们下载依赖包,再新建文件夹新建Element-UI的component:

# 安装依赖
pnpm i element-ui -S

# 新建Element-UI文件夹
pnpm i element-ui -S

# 再新建一个component文件夹
pnpm i element-ui -S

新建好文件夹后,我们再进入Element-UI的component文件夹:

# 进入Element-UI component
pnpm i element-ui -S

我一个一个创建吧:

  1. treeSelect组件先创建一个TreeSelect:
import { defineAsyncComponent } from 'element-ui/src/utils/async-component';

// 异步加载TreeSelect
const AsyncTreeSelect = defineAsyncComponent(
    () => import('@/components/treeSelect/index.vue')
);
export default AsyncTreeSelect;

我们新建treeSelect文件夹:

# 在Element-UI component新建treeSelect文件夹
pnpm i element-ui -S

treeSelect里新建TreeSelect:

import Tree from "vue-tree";
import TreeUi from "vue-tree/src/index.js";

// 引入的样式
import './Tree.less';

export default {
  name: 'AsyncTreeSelect',
  components: {
    TreeUi,
  },
  props: {
    treeData: {
      type: Object,
      default: () => {
        return {};
      }
    },
    defaultVal: {
      type: Array,
      default: []
    },
    hideIfSingle: {
      type: Boolean,
      default: false
    },
    onlyParent: {
      type: Boolean,
      default: false
    }
  },
  methods: {
    // 选择
    handleSelect({ target: { value } }, node) {
      if (this.onlyParent) {
        value = node.dataId;
      }
      this.propSetInput(value);
      if (value) {
        this.$emit('input', value);
        this.$emit('node', node);
      }
    },
    propSetInput(value) {
      if (Array.isArray(this.propValue)) {
        let array = [];
        if (value !== null && value !== undefined) {
          array = array.concat(this.propValue).concat(value);
        }
        this.syncInput(array);
      } else if (value !== null && value !== undefined) {
        this.syncInput(value);
      } else {
        this.syncInput(null);
      }
    },
    // 同步input
    syncInput(value) {
      let changed = false;
      const inputValue = value;
      if (Array.isArray(inputValue)) {
        changed = this.propValue !== inputValue;
      } else {
        changed = inputValue !== this.propValue;
      }
      if (!changed) return;

      this.$emit('update:input', value);
    },

    // 单选触发
    defaultActiveChange({ startValue }) {
      if (this.onlyParent) {
        this.propSetInput(startValue);
      } else {
        this.propSetInput(startValue);
      }
      this.$emit('update:defaultActive', startValue);
    },
    // 根据value设置选中值
    handleVal(val) {
      setTimeout(() {
        if (this.onlyParent) {
          this.$emit('input', val);
          this.$emit('node', null);
        } else {
          this.$emit('input', val);
          this.$emit('node', null);
        }
      }, 100);
    }
  },
  render(h) {
    let value = this.propValue;
    value =
      typeof value === 'object' ? value.map(v => String(v)) : String(value);

    return (
      <TreeUi
        className={this._treeClass}
        defaultActive={value}
        showCheckbox={this.showCheckbox}
        selectedVals={value}
        search={this.showSearch}
        custom={this.slotRender}
        on-defaultActiveChange={this.defaultActiveChange}
        on-toggle={this.handleSelect}
        on-search={this.handleSearch}
      />
    );
  }
};

再写样式:

// Element-UI component/treeSelect/Tree.less
// 样式
.tree-class {
  padding: 12px 16px;
}
.tree-class-sub {
  padding: 12px 16px 0 32px;
  background-color: #f3f3f3;
}
.tree-class-btn {
  width: 130px;
}

再写TreeSelect.vue:

<template>
  <AsyncTreeSelect />
</template>

<script>
import { defineAsyncComponent } from 'element-ui/src/utils/async-component';
import TreeSelect from './treeSelect';

// 异步加载TreeSelect
const AsyncTreeSelect = defineAsyncComponent(
    () => import('@/components/treeSelect/index.vue')
);
export default AsyncTreeSelect;
</script>

<style lang="less">
  @import '../TreeSelect.less';
</style>
  1. 模拟的数据
{
  id: '1',
  name: '我的菜单',
  children: [
    {
      id: '11',
      name: '子菜单 1'
    },
    {
      id: '12',
      name: '子菜单 2'
    },
    {
      id: '13',
      name: '子菜单 3'
    }
  ]
}
  1. 普通的Select组件
import { defineAsyncComponent } from 'element-ui/src/utils/async-component';

// 异步加载TreeSelect
const AsyncTreeSelect = defineAsyncComponent(
    () => import('@/components/treeSelect/index.vue')
);
export default AsyncTreeSelect;
  1. 测试的页面
<template>
  <div class="tree-class">
    <TreeSelect
      treeData={data}
      defaultVal={[31, 32]}
      onlyParent
    />
  </div>

  <div class="tree-class">
    <TreeSelect
      treeData={data}
      defaultVal={[31, 32]}
      onlyParent
      hideIfSingle
    />
  </div>
</template>

<script>
import { defineAsyncComponent } from 'element-ui/src/utils/async-component';

import data from './data';
// 异步加载TreeSelect
const AsyncTreeSelect = defineAsyncComponent(
    () => import('@/components/treeSelect/index.vue')
);
export default {
  components: {
    TreeSelect: AsyncTreeSelect
  },
  data: data
};
</script>

<style lang="less">
  @import '../TreeSelect.less';
</style>

结语

这个Element-UI的treeSelect 下拉检索组件我们自己做的还是蛮复杂的,要比之前的组件要复杂很多,你可以自己创建你的,根据你自己的情况设置,各有所得。

演示

你可以看这个地址,已经出结果了,就是这个我们自己做好的Element-UI treeSelect 下拉检索组件:

https://github.com/caobeibei/treeSelect

希望你能和我提一些意见和新的发现。