树形弹窗选择框/vue2/Element/弹框选择

前言

此类选择器根据vue+elementUI实现,使用vue3的可以根据此案例稍作改动即可实现,主要功能有弹出选择、搜索过滤、搜索结果高亮等,此选择器只支持单选,如需多选可在此基础进行改造。


效果图

树形弹窗选择框/vue2/Element/弹框选择_第1张图片


代码实现

使用时,props-value必须要传,并且要保证其唯一性!!!

HTML



JS

export default {
    props: {
        value: {
            type: undefined,
            default: null,
        },
        data: {
            type: Array,
            default: () => new Array(),
        },
        props: {
            type: Object,
            default: () => {
                return {
                    label: 'label',
                    value: 'value',
                    children: 'children',
                };
            },
        },
    },
    data() {
        return {
            defaultExpandedKeys: [],
            visible: false,
            filterText: '',
        };
    },
    created() {
        this.propsInit();
    },
    mounted() {
        setTimeout(this.initData, 10);
    },
    beforeUpdate() {
        this.propsInit();
        this.initData();
    },

    methods: {
        open() {
            this.visible = true;
        },
        initData() {
            let newItem = this.recurrenceQuery(this.data, this.props.value, this.value);
            if (newItem?.length) {
                if (this.props.value && newItem[0][this.props.value]) {
                    this.defaultExpandedKeys = [newItem[0][this.props.value]];
                }
                this.$nextTick(() => {
                    if (this.$refs.myTree?.setCurrentNode) this.$refs.myTree.setCurrentNode(newItem[0]);
                });
            } else {
                if (this.data.length == 1 && this.$attrs['expand-first-level'] !== false) {
                    this.defaultExpandedKeys = [this.data[0][this.props.value]];
                }
            }

            this.$forceUpdate();
        },

        // 单选事件
        handleNodeClick(data, e) {
            if (this.props.disabled && e.disabled) {
                return false;
            } else {
                if (data[this.props.children] && data[this.props.children]?.length) {
                    return false;
                }
            }

            this.$emit('input', data[this.props.value]);
            this.visible = false;
            this.$emit('change', data, e);
        },
        //   递归查找通用方法
        recurrenceQuery(list, key, value) {
            if (!list || !key || !value) return [];
            let queryData = [];
            list.map(item => {
                if (item[this.props.children] && item[this.props.children].length) {
                    queryData.push(...this.recurrenceQuery(item[this.props.children], key, value));
                }
                if (item[key] == value) {
                    queryData.push(item);
                }
                return item;
            });
            return queryData;
        },

        selectClear(flag) {
            if (!flag) {
                this.$emit('input', '');
                this.$emit('change', null, null);
            }
            this.$refs.myTree.setCurrentKey(null);
            this.remoteMethod('');
        },
        getCheckedNodes() {
            if (this.value !== null && this.value !== undefined && this.value !== '') {
                return this.$refs.myTree.getCheckedNodes();
            }
            return [];
        },
        getCurrentNode() {
            if (this.value !== null && this.value !== undefined && this.value !== '') {
                return this.$refs.myTree.getCurrentNode();
            }
            return null;
        },
        valueFilter(val) {
            let res = '';
            [res] = this.recurrenceQuery(this.data, this.props.value, val);
            return res?.[this.props.label] || '';
        },
        propsInit() {
            this.props.label = this.props.label || 'label';
            this.props.value = this.props.value || 'value';
            this.props.children = this.props.children || 'children';
        },

        remoteMethod(query) {
            this.$refs.myTree.filter(query);
        },
        filterNode(value, data) {
            if (!value) return true;
            let result = true;

            if (this.$listeners.onFilter) {
                this.$emit('onFilter', value, data, res => {
                    result = res;
                });
            } else {
                result = data[this.props.label].indexOf(value) !== -1;
            }
            return result;
        },
        searchHighlightFilter(node, data) {
            let { label } = this.props;
            if (this.$attrs['search-highlight'] === false) return data[label];
            if (!this.filterText) return data[label];
            const regex = new RegExp(this.filterText, 'gi');
            let text = data[label].replace(regex, match => {
                return `${match}`;
            });
            return text;
        },
    },

    watch: {
        value: {
            deep: true,
            handler(val) {
                if (!val) {
                    this.selectClear(true);
                }
            },
        },
        filterText(val) {
            this.$refs.myTree.filter(val);
        },
    },
};

CSS

.selecTree {
    max-height: 50vh;
    overflow: auto;
    padding: 5px;
    ::v-deep .el-tree-node__content {
        font-size: 14px;
    }
}
::v-deep.slotSpan {
    font-size: 14px;
    > i {
        color: #67c23a;
        margin-right: 5px;
    }
    b {
        font-weight: normal;
        font-size: 12px;
        color: #999;
    }
    .highlight {
        color: #67c23a;
    }
}

.treeDialogBody {
    max-height: 60vh;
    display: flex;
    flex-direction: column;
    ::v-deep .el-input__validateIcon {
        display: none;
    }
    ::v-deep .treeDialogBodyTree {
        flex: 1;
        overflow: auto;
        padding: 12px 8px;
        margin: 12px 0;
        background-color: #f5f7fa;
        border-radius: 5px;
        .el-tree {
            background: transparent;
            .el-tree-node__content:hover {
                background-color: #eaeef4;
            }
        }
    }
}
.footer {
    text-align: right;
}

使用案例






使用文档 

以下只列举主要属性与方法,更多具体的属性配置请移步element官网进行查看。

属性

属性名 类型 默认值 是否必传 说明
value / v-model string / number - 绑定值

props

object

与element保持一致

配置选项,具体配置可以参照element ui库中el-tree的配置

expand-first-level

boolean true

当根节点只有一个时,是否展开

search-highlight

boolean true

是否开启搜索高亮,仅在slot-tree未传入时生效

show-count boolean false 若节点中存在children,则在父节点展示所属children的数量,注意但设置插槽时 show-count将失效

事件

事件名称 说明 回调参数

change

当选择项发生改变时触发 共两个参数,依次为:当前节点的数据,当前节点的 Node 对象

onFilter

当过滤框输入的值改变时触发

共三个参数,依次为:搜索框的值,当前节点的数据,回调函数callback, 接受一个参数类型boolean,表示是否展示该节点

插槽

name 说明

-

页面展示的输入框slot,如果传入默认插槽,则会不显示默认el-input,参数为 { value }

prepend

弹窗中过滤文本框的顶部插槽

tree

自定义树节点的内容,参数为 { node, data }

你可能感兴趣的:(vue.js,前端,javascript,elementui)