0


下拉框弹窗/基于element ui/自定义selectdialog

1.前言

    小谭最近遇到一个需求:因为下拉选项过多,用el-tree对于逐级寻找很不方便,于是小谭就自己手写了个下拉框弹窗,废话不多说,上效果图:

页面展示: 基本上和普通下拉框一样

点击下拉框弹出的弹窗: 弹窗内最多可以实现三级下拉如果想实现更多,可以自己根据源码更改哦

2.代码实现:

HTML:

<!--
 * @description: 通用下拉弹窗
 * @fileName: SelectDialog.vue
 * @author:xiaotan
 * @date: 2023-04-06 10:42:58
 * @Attributes: props:{
        value: 对应label对应值 默认值value,
        label: '展示的文字 默认值label,
        children: 默认值 children,
        parentId:关联的父级id字段 默认值parentId,若要实现回显则必传!!!
     };
     content: 双向绑定的值,
     data:展示的数据
     disabled:是否禁用
     selectLastNode:是否只能选择最后一级
     showSearch: 是否展示搜索栏 默认不展示

     @change 选中数据触发的事件 第一个参数为选中的节点数据 第二个参数为选中的节点数据链
     @searchData 搜索逻辑 参数为搜索的关键字

     slot
     search 自定义搜索内容
     first 自定义一级内容 使用方法:  <template #first="{data}"></template> data为当前节点数据
     secend 自定义一二级内容
     three 自定义三级内容
!-->
<template>
   <div>
      <div class="mySelectInput">
         <el-select
            ref="category"
            value
            :placeholder="showContent?'':'请选择'"
            clearable
            style="width: 100%;"
            :disabled="disabled"
            @focus="$refs.category.blur()"
            @click.native="handleDialog"
         />
         <div class="mySelectInputText" v-if="content">{{ showContent}}</div>
      </div>

      <el-dialog
         class="category-container"
         :visible.sync="visible"
         width="80vw"
         :append-to-body="true"
         :before-close="handleClose"
         :close-on-click-modal="false"
         :close-on-press-escape="false"
      >
         <div v-loading="loading">
            <div slot="title">请选择</div>
            <el-row type="flex" class="search" v-if="showSearch">
               <slot name="search" v-if="$scopedSlots.search"></slot>
               <template v-else>
                  <el-input v-model="searchVal" placeholder="请输入"></el-input>
                  <el-button type="primary" @click="handleSearch" style="margin-right: 10px;">搜索</el-button>
                  <el-button type="primary" plain @click="handleClear">重置</el-button>
               </template>
            </el-row>
            <div class="main">
               <div v-if="!categoryList[0]">
                  <div>
                     <el-empty description="暂无数据"></el-empty>
                  </div>
               </div>

               <div>
                  <div class="list" v-for="(item, index) in categoryList" :key="item.id + index">
                     <p
                        class="list-title"
                        @click="handleChange(item)"
                        :class="item[props.value]|classFilter"
                     >
                        <slot name="first" :data="item" v-if="$scopedSlots.first"></slot>
                        <template v-else>{{ item[props.label||'label'] }}</template>
                     </p>
                     <div class="content">
                        <el-dropdown
                           v-for="(i, index) in item[props.children ||'children']"
                           :class="i[props.value]|classFilter"
                           :key="i.id + index"
                        >
                           <span class="down" @click="handleChange(item, i)">
                              <slot name="secend" :data="item" v-if="$scopedSlots.secend"></slot>
                              <template v-else>
                                 {{ i[props.label||'label'] }}
                                 <i
                                    v-if=" i[props.children ||'children']"
                                    class="el-icon-arrow-down"
                                 ></i>
                              </template>
                           </span>
                           <el-dropdown-menu slot="dropdown" v-if=" i[props.children ||'children']">
                              <el-dropdown-item
                                 v-for="(j, index) in i[props.children ||'children']"
                                 :key="j.id + index"
                                 @click.native="handleChange(item, i, j)"
                                 :class="j[props.value]|classFilter"
                              >
                                 <slot name="three" :data="item" v-if="$scopedSlots.three"></slot>
                                 <template v-else>{{ j[props.label||'label'] }}</template>
                              </el-dropdown-item>
                           </el-dropdown-menu>
                        </el-dropdown>
                     </div>
                  </div>
               </div>
            </div>
         </div>
      </el-dialog>
   </div>
</template>

JS:

let that = null;
export default {
    props: {
        content: {
            type: undefined,
            default: null,
        },
        disabled: {
            type: Boolean,
            default: false,
        },
        data: {
            type: Array,
            default: [],
        },
        props: {
            type: Object,
            default: { value: 'value', label: 'label', children: 'children', parentId: 'parentId' },
        },
    },
    data() {
        return {
            visible: false,
            loading: false,
            showResult: false,
            searchVal: '',
            selectData: null,
            showContent: '',
        };
    },
    computed: {
        categoryList() {
            return this.data;
        },
        selectLastNode() {
            return this.$attrs.selectLastNode == '';
        },
        showSearch() {
            return this.$attrs.showSearch == '';
        },
    },
    created() {
        that = this;
    },
    mounted() {
        let timer = setInterval(() => {
            if (this.data) {
                clearInterval(timer);
                if (this.content) {
                    this.getSelectData();
                    this.$nextTick(() => {
                        this.contentFilter();
                        this.$forceUpdate();
                    });
                }
            }
        }, 500);
    },

    methods: {
        // 打开弹窗
        handleDialog() {
            if (!this.disabled) this.visible = true;
        },
        // 选中内容改变
        handleChange(first, secend, three) {
            let obj = three || secend || first;
            if (this.selectLastNode && obj[this.props.children || 'children'] && obj[this.props.children || 'children']?.length) {
                return false;
            }
            this.selectData = {
                first,
                secend,
                three,
            };
            this.visible = false;
            this.showContent = obj[this.props.label];
            this.$emit('update:content', obj[this.props.value]);
            this.$emit('change', obj, this.selectData);
        },
        // 关闭弹窗
        handleClose() {
            this.visible = false;
        },
        // 默认搜索事件
        handleSearch() {
            this.showResult = true;
            this.$emit('searchData', this.searchVal);
        },
        // 搜索条件重置
        handleClear() {
            this.searchVal = '';
            this.showResult = false;
        },
        // 选项回显过滤
        contentFilter() {
            if (this.selectData) {
                let obj = this.selectData.three || this.selectData.secend || this.selectData.first;
                this.showContent = obj?.[this.props.label] || this.content;
            } else {
                this.showContent = this.content;
            }
        },
        // 获取回显数据
        getSelectData() {
            let [firstData] = this.recurrenceQuery(this.data, 'id', this.content);
            this.selectData = {
                first: '',
                secend: '',
                three: '',
            };

            let flag1 = firstData && firstData[this.props.parentId || 'parentId'] != undefined && firstData[this.props.parentId || 'parentId'] != null;
            if (flag1) {
                let [secendData] = this.recurrenceQuery(this.data, 'id', firstData[this.props.parentId || 'parentId']);
                let flag2 = secendData && secendData[this.props.parentId || 'parentId'] != undefined && secendData[this.props.parentId || 'parentId'] != null;
                if (flag2) {
                    let [threeData] = this.recurrenceQuery(this.data, 'id', secendData[this.props.parentId || 'parentId']);
                    let flag3 = threeData && threeData[this.props.parentId || 'parentId'] != undefined && threeData[this.props.parentId || 'parentId'] != null;
                    if (flag3) {
                        this.selectData.first = threeData;
                        this.selectData.secend = secendData;
                        this.selectData.three = firstData;
                        this.$set(this.selectData, 'first', threeData);
                        this.$set(this.selectData, 'secend', secendData);
                        this.$set(this.selectData, 'three', firstData);
                    } else {
                        this.$set(this.selectData, 'first', secendData);
                        this.$set(this.selectData, 'secend', firstData);
                    }
                } else {
                    this.$set(this.selectData, 'first', secendData);
                    this.$set(this.selectData, 'secend', firstData);
                }
            } else {
                this.$set(this.selectData, 'first', firstData);
            }
            if (this.content && !this.selectData.first) {
                this.getSelectData();
                this.contentFilter();
            }
        },
    },

    filters: {
        classFilter(item) {
            if (that.selectData) {
                for (const key in that.selectData) {
                    if (that.selectData[key]?.[that.props.value] == item) {
                        return 'active';
                    }
                }
            }
            return '';
        },
    },
};

CSS:

.category-container {
    ::v-deep .el-dialog {
        user-select: none;
        background: #ffffff;
        border-radius: 12px;
        margin: 0 !important;
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
        .el-dialog__header {
            padding: 12px 20px 0;
            font-size: 16px;
            font-weight: 500;
            color: #1b62ff;
            line-height: 22px;
        }
        .el-dialog__body {
            padding: 8px 20px;
            .search {
                margin-bottom: 10px;
                .el-input {
                    width: 162px;
                    margin-right: 16px;
                }
            }

            .main {
                height: 650px;
                overflow-y: auto;
                .com {
                    padding: 9px 0;
                    border-bottom: 1px solid #e1e1e5;
                    .title {
                        font-size: 16px;
                        font-weight: 600;
                        color: #3b3b5a;
                        line-height: 22px;
                        margin: 0;
                    }
                }
                .list {
                    display: flex;
                    flex-direction: row;
                    padding: 4px 0;
                    border-bottom: 1px solid #e1e1e5;
                    box-sizing: border-box;
                    align-items: center;
                    .list-title {
                        font-size: 14px;
                        font-weight: 600;
                        color: #3b3b5a;
                        margin: 0;
                        padding: 2px 8px;
                        cursor: pointer;
                        min-width: 72px;
                        text-align-last: justify;
                        border-radius: 4px;
                        text-align: justify;
                        &:hover {
                            background: #e8efff;
                        }
                    }
                    .content {
                        flex: 1;
                        margin: 0 14px;
                        .el-dropdown {
                            line-height: 22px;
                            margin-right: 5px;
                            border-radius: 4px;
                            padding: 0px 8px;
                            border: 1px solid transparent;
                            color: #6e7b9a;
                            margin-bottom: 2px;
                            margin-top: 2px;
                            &:hover {
                                background: #e8efff;
                            }
                            .down {
                                // font-size: 16px;
                                font-size: 12px;
                                line-height: 22px;
                                cursor: pointer;
                            }
                            .el-dropdown-menu__item {
                                font-size: 14px;
                                color: #1b62ff;
                                line-height: 16px;
                            }
                        }
                    }
                }
            }
        }
    }
}
.active {
    border: 1px dashed #aaa !important;
}
.el-dropdown-menu__item.active {
    border: 0px !important;
    background-color: #eee;
    color: #000 !important;
}

.mySelectInput {
    position: relative;
    > .mySelectInputText {
        position: absolute;
        left: 0;
        right: 0;
        bottom: 0;
        top: 0;
        margin: auto;
        line-height: 32px;
        padding-left: 15px;
        padding-right: 32px;
        font-size: 13px;
        pointer-events: none;
    }
}


本文转载自: https://blog.csdn.net/qq_45947497/article/details/130157706
版权归原作者 小谭鸡米花 所有, 如有侵权,请联系我们删除。

“下拉框弹窗/基于element ui/自定义selectdialog”的评论:

还没有评论