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

news2024/9/26 5:17:39

前言

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


效果图


代码实现

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

HTML

<!-- 
 * @description: 通用树形弹窗选择框
 * @fileName: treeDialogSelect/index.vue 
 * @author: tan 
 * @date: 2024-09-10 16:31:49
 * @Attributes: data	展示数据	array
                value 展示在输入框的值
                props	配置选项,具体配置可以参照element ui库中el-tree的配置	object
                expand-first-level 当根节点只有一个是,是否展开,默认为是
                search-highlight 是否开启搜索高亮,默认开启,仅在slot-tree不传入时生效
                onFilter 自定义过滤规则,参数为:
                    value 搜索框的值
                    data 原始数据
                    callback 回调函数 接受一个参数类型boolean  为是否展示该节点 
                !!!!!必传!!!!!! props-value	每个树节点用来作为唯一标识的属性,整棵树应该是唯一的 !!!!!!
!-->
<template>
    <div class="treeDialogSelectCom">
        <slot :value="value" v-if="$slots.default"></slot>
        <el-input
            v-else
            v-bind="$attrs"
            :value="valueFilter(value)"
            suffix-icon="el-icon-arrow-down"
            :placeholder="$attrs['placeholder'] || '请选择'"
            :clearable="$attrs['clearable'] || true"
            class="treeDialogSelectInput"
            @focus="open"
            @clear="selectClear()"
        ></el-input>
        <el-dialog
            :visible.sync="visible"
            :width="$attrs['width'] || '50vw'"
            :append-to-body="true"
            :close-on-click-modal="false"
            :close-on-press-escape="false"
            :title="$attrs['dialog-title'] || '请选择'"
        >
            <div class="treeDialogBody">
                <el-input placeholder="输入关键字进行过滤" v-model="filterText" clearable>
                    <slot slot="prepend" name="prepend"></slot>
                </el-input>
                <div class="treeDialogBodyTree">
                    <el-tree
                        v-bind="$attrs"
                        :data="data"
                        :props="props"
                        @node-click="handleNodeClick"
                        :check-strictly="$attrs['check-strictly']"
                        :icon-class="$attrs['icon-class']"
                        :lazy="$attrs['lazy']"
                        :load="$attrs['load']"
                        :node-key="props.value"
                        :filter-node-method="filterNode"
                        :default-expanded-keys="defaultExpandedKeys"
                        ref="myTree"
                    >
                        <template slot-scope="{ node, data }">
                            <slot :node="node" :data="data" name="tree">
                                <span class="slotSpan">
                                    <i v-show="!node.disabled && filterText" class="el-icon-warning"></i>
                                    <span>
                                        <span v-html="searchHighlightFilter(node, data)"></span>
                                        <b v-if="$attrs['show-count'] != undefined && data[props.children]">({{ data[props.children].length }})</b>
                                    </span>
                                </span>
                            </slot>
                        </template>
                    </el-tree>
                </div>
            </div>
            <div class="footer">
                <el-button type="primary" plain @click="selectClear()">清 空</el-button>
            </div>
        </el-dialog>
    </div>
</template>

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 `<strong class="highlight">${match}</strong>`;
            });
            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;
}

使用案例

<template>
    <treeDialogSelect
        v-model="treeDialogSelectVal"
        :data="treeDialogSelectData"
        show-count
    ></treeDialogSelect>
</template>

<script>
import treeDialogSelect from '@/components/treeDialogSelect';
export default {
    components: { treeDialogSelect },
    data() {
        return {
            treeDialogSelectValue: '',
            treeDialogSelectData: [
                {
                    id: 1,
                    label: '一级 1',
                    children: [
                        {
                            id: 4,
                            label: '二级 1-1',
                            children: [
                                {
                                    id: 9,
                                    label: '三级 1-1-1',
                                },
                                {
                                    id: 10,
                                    label: '三级 1-1-2',
                                },
                            ],
                        },
                    ],
                },
                {
                    id: 2,
                    label: '一级 2',
                    children: [
                        {
                            id: 5,
                            label: '二级 2-1',
                        },
                        {
                            id: 6,
                            label: '二级 2-2',
                        },
                    ],
                },
                {
                    id: 3,
                    label: '一级 3',
                    children: [
                        {
                            id: 7,
                            label: '二级 3-1',
                        },
                        {
                            id: 8,
                            label: '二级 3-2',
                        },
                    ],
                },
            ],
        };
    },
};
</script>


使用文档 

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

属性

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

props

object

与element保持一致

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

expand-first-level

booleantrue

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

search-highlight

booleantrue

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

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

事件

事件名称说明回调参数

change

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

onFilter

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

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

插槽

name说明

-

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

prepend

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

tree

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2125907.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

文件备份的方法有哪些?应该怎么弄?分享六个高效便捷的电脑文件备份方法

我们的电脑中存储了大量重要文件&#xff0c;如照片、文档、视频以及重要资料等。 然而&#xff0c;面对突如其来的硬件故障、病毒攻击或人为误操作&#xff0c;这些数据仿佛都悬于一线。 虽然说可以选择的恢复方法有很多&#xff0c;但是没有任何一个方法可以做到100%的&…

【Mysql】记录sql在执行过程中很慢

在改之前的测试脚本,之前写了一些sql,这次准备顺手把sql也调整一下,因为虽然sql能用&#xff0c;但是写的十分拉跨 附sql 分析过程: 1. 初步推测慢主要是因为left join了好几张表。 然后我去查了也跟开发求证过sql在执行过程中是"先联表&#xff0c;再执行查询条件"…

SCADA|KingSCADA运行报错:加载网络服务失败

哈喽,你好啊,我是雷工! 今天要运行KingSCADA程序时报错:加载 网络服务失败 ,导致无法运行。 解决问题思路及过程记录如下: 01 问题描述 如下图所示,报加载 网络服务失败 ; 02 处理过程 软件运行信息窗口中会有详细的运行信息,可以先查看软件的信息窗口。 也可以在日…

bluez SPP使用

然后退出bluetoothctl&#xff0c;执行rfcomm listen /dev/rfcomm0 1&#xff0c;如果已被使用&#xff0c;则执行rfcomm release 0(执行该命令会有数据回显&#xff0c;既手机收到开发板发来的相同数据&#xff0c;若要关闭回显需要加上参数-r&#xff0c;开启raw mode) 手机上…

项目中常用注解整理

Mybatis/MybatisPlus TableName //用来将指定的数据库表和 JavaBean 进行映射TableId(type IdType.AUTO) //标记为数据库中的主键TableField(existfalse) //标记为数据库中不存在的字段&#xff0c;防止报错Paramparam标签提供了对某个函数的参数的各项说明&#xff0c;包括参…

CTF 竞赛密码学方向学习路径规划

目录 计算机科学基础计算机科学概念的引入、兴趣的引导开发环境的配置与常用工具的安装Watt Toolkit&#xff08;Steam&#xff09;、机场代理Scoop&#xff08;Windows 用户可选&#xff09;常用 Python 库SageMathLinux小工具 yafuOpenSSL Markdown编程基础Python其他编程语言…

@老板:图纸防泄密方法有哪些?图纸防泄密高招分享

图纸&#xff0c;就像是企业的秘密武器设计图纸&#xff0c;里面藏着产品的精髓和创新的火花。一旦这些图纸落入竞争对手之手&#xff0c;就像是自家的独门秘籍被外人偷学&#xff0c;后果不堪设想。因此&#xff0c;保护图纸不泄密&#xff0c;是每个企业都必须认真对待的大事…

【QT】常用类

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;折纸花满衣 &#x1f3e0;个人专栏&#xff1a;QT 目录 &#x1f449;&#x1f3fb;QMediaPlayer&#x1f449;&#x1f3fb;QMediaPlaylistsetPlaybackMode &#x1f449;&#x1f3fb;QDir&#x1f449;…

stm32驱动HX711称重传感器 c++代码分享

一、HX711模块介绍 HX711模块是一种专门用于称重传感器的放大器模块。它的主要功能是将测得的微小电压信号放大到可以被微控制器读取的范围。 HX711模块通常配合称重传感器一起使用&#xff0c;例如压力传感器、负载细胞等。它采用24位的模数转换器&#xff08;ADC&#xff09…

交叉熵函数与kl散度的区别

公式上的区别 手动计算的方式展示如何实现这两个损失函数 交叉熵损失函数 import torch import torch.nn.functional as F# 模型的输出 logits 和真实标签 target logits torch.tensor([[2.0, 0.5, 0.1], [0.3, 2.5, 0.8]], requires_gradTrue) target torch.tensor([0, 1])…

测试开发基础——软件测试中的bug

二、软件测试中的Bug 1. 软件测试的生命周期 软件测试贯穿于软件的整个生命周期 需求分析 测试计划 测试设计与开发 测试执行 测试评估 上线 运行维护 用户角度&#xff1a;软件需求是否合理 技术角度&#xff1a;技术上是否可行&#xff0c;是否还有优化空间 测试角度…

某郊到家:互联网时代下的按摩服务革新

在快速发展的时代背景下&#xff0c;一群具有前瞻性的企业家在2018年勇敢地进军了按摩服务行业&#xff0c;引领了一场对传统模式的革新。他们不仅在竞争激烈的市场中站稳脚跟&#xff0c;还成功地在不断变化的市场环境中确立了自己的位置。 创新的商业模式和持续的努力&#x…

【2024】前端学习笔记2-有序列表-无序列表-描述列表

学习笔记 有序列表:ol基本使用:嵌套使用扩展:使用CSS改变序号类型无序列表:ul基本使用扩展:使用CSS改变符号类型扩展:使用CSS定制列表样式描述列表:dl基本使用扩展:使用CSS定制类型格式总结有序列表:ol 有序列表由<ol>标签包裹一组<li>(列表项)标签组成…

区块链之变:揭秘Web3对互联网的全面改变

随着技术的进步&#xff0c;区块链 逐渐从一个相对小众的概念演变为重塑互联网结构的核心力量。特别是 Web3 的兴起&#xff0c;标志着互联网进入了一个新的发展阶段。这一变革不仅仅是技术的升级&#xff0c;更是对互联网功能、数据控制和用户体验的全面重新定义。本文将详细探…

数学建模笔记—— 回归分析

数学建模笔记—— 回归分析 回归分析1. 回归分析的一般步骤2. 一元线性回归分析2.1 具体过程2.1.1 确定回归方程中的解释变量和被解释变量2.1.2 确定回归模型和建立回归方程2.1.3 利用回归直线进行估计和预测2.1.4 对回归方程进行各种检验(补充)1. 回归直线的拟合优度2. 显著性…

哪款提醒软件能清晰展示每日工作任务?

在快节奏的工作环境中&#xff0c;每天的工作任务堆积如山&#xff0c;如何有效地整理和清晰查看这些任务&#xff0c;成为了提高工作效率的关键。一款优秀的提醒软件能够帮助我们将任务条理化&#xff0c;确保每一项工作都能按时完成。 敬业签就是这样一款能够清晰展示每日工…

VR 尺寸美学主观评价-解决方案-现场体验研讨会报名

棣拓科技VR创新解决方案助力尺寸美学所见即所得! 诚邀各位行业专家莅临指导交流 请扫描海报二维码踊跃报名&#xff0c;谢谢 中国上海 2024.10.25 亮点介绍 1、通过精湛渲染技术&#xff0c;最真实展现设计效果&#xff0c;并通过VR设备一比一比例进行展现。 2、设置相关设…

suid提权的环境搭建+反弹shell

SUID&#xff08;Set User ID&#xff09;是一种特殊的文件权限设置&#xff0c;它允许文件在执行时具有文件所有者的权限。当具有SUID权限的文件被执行时&#xff0c;执行该文件的用户会暂时获得文件所有者的权限。这种权限通常用于需要高权限操作的程序&#xff0c;如‌passw…

建筑用能该如何统一管理?水电气集抄太麻烦?!看看这个吧!建筑能耗分析管理系统 您的运维“好帮手”

安科瑞刘鸿鹏 随着工业化和信息化进程的加速&#xff0c;企业对能源管理的需求愈加迫切。安科瑞电气股份有限公司推出的Acrel-5000能耗管理系统运用物联网技术&#xff0c;实时采集电表、水表、燃气表等能源计量仪表的数据&#xff0c;并结合大数据技术进行处理和存储。该平台旨…

uniapp(H5)设置反向代理,设置成功后页面报错

设置反向代理后&#xff0c;页面报错图&#xff1a; 反向代理代码&#xff1a;devServer下面就是配置对应的代理&#xff0c;一般这样就没问题了 "h5": {"router": {"mode": "hash"},"devServer": {"port": 517…