Element UI框架中巧用树选择器

7,398 阅读2分钟

先看效果图

树选择器

基本功能

在Element UI框架中有选择器和树形控件,但是没有树形选择器,也就是图上的这种方式的选择器,所以只能自定义选择器的slot。这里介绍的是多选情况,如果是单选则去掉复选框,修改一部分的处理即可。

html部分的代码:

<el-select
    v-model="dataArr"
    :multiple="multiple"
    filterable
    :placeholder="placeholder"
    :disabled="disabled"
    :collapse-tags="multiple"
    @remove-tag="handleTagChange"
    @visible-change="handleOptionHidden"
    class="hi-input">
    <el-option value="0"
        class="hidden">
    </el-option>
    <!--el-tree绑定的数组中children里的key值不能是0-->
    <el-tree
        ref="tree"
        :data="options"
        node-key="key"
        show-checkbox
        :default-checked-keys="selectedData"
        @check="handleCheckChange"
        :props="defaultProps">
    </el-tree>
</el-select>

树形控件到选择器的绑定

在el-tree中绑定的值是已选择的key值组成的数组,check绑定的事件函数是为了:

  • 得到现在树选择器上选中的值
  • 过滤undefined、null的值(是为了容错处理) 具体代码如下:
handleCheckChange: function() {
    this.selectedData = this.$refs.tree.getCheckedKeys().filter(_ => _);
}

因为选择器是有label值和key值区分的,所以,每当在el-tree中选中值key值变化时,选择器上绑定的值label值也应该随之变化,所以在watch中监听key值,目的是在el-tree绑定的data中找到当前key值对应的label值 具体代码如下:

watch: {
    selectedData: function(newValue) {
        this.$nextTick(() => { this.dataArr = this.handleDataTransform(newValue, 'key', 'label'); });
    },
},
methods: {
    getNameById(array, value, id, name, multi) {
        let arr = array || [];
        let flag;
        let result = arr.filter(item => {
            return item[id] + '' === value + '';
        });
        if (multi) {
            flag = result.map(item => {
                return item[name];
            });
        } else {
            let obj = result[0];
            flag = name ? obj && obj[name] : obj;
        }
        return flag;
    },
    handleDataTransform: function(source, key, value) {
        return this.options.map(_ => {
            let arr = source.map(item => this.getNameById(
                _.children,
                item,
                key,
                value
            )).filter(item => item);
            return arr;
        }).reduce((acc, cur) => {
            return acc.concat(cur);
        }, []);
    }
}

到这里为止,已经完成了树形控件到选择器的单向绑定

选择器到树形控件的绑定

remove-tag事件

现在处理选择器的值发生改变时,树形控件也变化。因为此时是多选,所以要在remove-tag事件中处理,具体代码如下:

handleTagChange: function() {
    //  handleDataTransform已经在之前定义过
    this.selectedData = this.handleDataTransform(this.dataArr, 'label', 'key');
    this.$refs.tree.setCheckedKeys(this.selectedData);
},

change事件

通过测试发现,当在键盘上点击delete时,也会删除选择器内选中的元素,我尝试过绑定@keyup.delete事件,但是绑定不成功,如果使用了.native修饰符可以监听到事件了,但是阻止了原生删除事件,所以也不可取。经过测试发现绑定change事件就可以处理键盘的delete事件,绑定的函数和handleTagChange一样。

优化

此时,这个树形选择器已经完成了~👏👏,但是,我们还可以进一步优化,比如

选择项不变,不重复发请求

如果选择后的内容与选择前的内容一样,不再发生请求的处理。 在选择器中绑定的visible-change事件可以处理,思想是:

  1. item值为true,即展开下拉框时,把此时的值存储下来,注意⚠️:这时候存储下来的值必须放在一个全局变量中,函数内的变量会在每次进入这个函数时初始化,所以在下拉框收起再进来这个函数时,之前存储的值已经没有了。
  2. item值为false,即收起下拉框时,判断之前存储下的变量值和当前变量值是否相等,如果不相等才触发数据的更新。 具体代码如下:
handleOptionHidden: function(item) {
    // 处理选中内容没变的情况
    if (item) {
        this.selectedItem = [...this.selectedData];
    } else {
        //  this.$util.isEqual()是判断两个数组是否相等函数,网上很多,请自行谷歌
        if (!this.$util.isEqual(this.selectedItem, this.selectedData)) {
            this.handleUpdate(this.selectedData);
        }
    }
}

选择器的搜索功能

当数据量大的时候,选择器一般都会有搜索的需求,在选择器上封装过的搜索功能无法满足需求,因为树形控件本身有搜索的函数,所以在选择器上自定义filter-mothod事件,调用树形控件的搜索事件即可。

handleSelectFilter: function(val) {
    this.$refs.tree.filter(val);
},

在树形控件上绑定filter-node-method自定义函数,支持忽略大小写搜索

handleTreeFilter: function(value, data) {
    if (!value) return true;
    return data.label.toUpperCase().indexOf(value.toUpperCase()) !== -1;
},

以上就是全部内容了,如果哪里写的差点意思,请告诉我哈~记得点赞👍,thanks~