Vue 封装下拉框


下拉框组件,可输入筛选数据。

template层

<template>
    <div class="select-options" v-click-outside="hideList">
        <input class="input-screen" v-if="searchable" type="text" ref="currentInput" :placeholder="placeholder" v-model="currentVal" :style="inputStyle" @focus="inputFocus" @input="changeOptionDatas">
        <div class="input-screen div-screen" :class="{'placeholder-color': (placeholder && !currentVal)}" v-else :style="inputStyle" ref="currentInput" @click="inputFocus">
            {{ (placeholder && !currentVal) ? placeholder : currentVal }}
        </div>
        <i class="icon-caret-down"></i>
        <ul class="options-wrap" :class="{show: showOptions}" ref="currentOptions">
            <template v-if="originalOptions && originalOptions.length">
                <li class="options-list" v-for="(item, i) in originalOptions" :class="{active: itemKey ? item[itemKey] == currentVal : item == currentVal}" :key="i" @click="selectOptions(item)">
                    {{ itemKey ? item[itemKey] : item }}
                </li>
            </template>
            <li class="options-list" v-else>
                暂无数据
            </li>
        </ul>
    </div>
</template>


script层

这里 props 有几个参数需要注意:
1、value:直接在组件身上v-model可实现双向绑定
2、placeholder:提示词
3、options:数据
4、itemKey:是否可取数据的某个字段
5、inputStyle:input输入框的样式
6、searchable:可输入筛选属性
注意:popup 需要去找之前到文章,名字为 显示弹出框工具函数
<script>
import { popup } from "@/utils/index"
export default {
    data() {
        return {
            currentVal: '',
            showOptions: false,
            originalOptions: [],
            antiShakeTimer: null, // 防抖延迟计时器
        }
    },
    props: {
        value: {
            type: String,
            default: ''
        },
        placeholder: {
            type: String,
            default: ''
        },
        options: {
            type: Array,
            default: _ => []
        },
        itemKey: {
            type: String,
            default: ''
        },
        inputStyle: {
            type: Object,
            default: _ => { }
        },
        // 可搜索
        searchable: {
            type: Boolean,
            default: false
        }
    },
    created() {
        this.currentVal = this.value;
        this.originalOptions = this.options;
    },
    methods: {
        selectOptions(item) {
            let text = (this.itemKey ? item[this.itemKey] : item);
            this.$emit('input', text);
            this.$emit('on-change', text);
            this.showOptions = false;
        },
        inputFocus() {
            if (!this.showOptions) {
                let input_dom = this.$refs.currentInput;
                let options_dom = this.$refs.currentOptions;
                popup(this, input_dom, options_dom, '', true);
                this.showOptions = true;
                this.$emit('on-focus', '');
            } else {
                this.showOptions = false;
            }
        },
        hideList() {
            this.showOptions = false;
        },
        changeOptionDatas() {
            this.antiShakeTimer && clearTimeout(this.antiShakeTimer);
            this.antiShakeTimer = setTimeout(_ => {
                this.originalOptions = this.options.filter(v => {
                    console.log(v[this.itemKey].indexOf(this.currentVal))
                    return (this.itemKey ? v[this.itemKey].indexOf(this.currentVal) != -1 : v.indexOf(this.currentVal) != -1)
                })
                this.$emit('input', this.currentVal);
            }, 300)
        }
    },
    watch: {
        value(newVal, oldVal) {
            this.currentVal = newVal
        },
        options(newVal, oldVal) {
            this.originalOptions = newVal
        },
    }
}
</script>



样式层

我这里使用的是less
<style lang="less" scoped>
.select-options {
    width: 100%;
    height: 100%;
    position: relative;
    text-align: center;


    .icon-caret-down {
        color: #747474;
        position: absolute;
        right: 0;
        top: 50%;
        transform: translateY(-50%);
    }


    .input-screen {
        border: 0;
        width: 100%;
        padding-right: 14px;


        &.div-screen {
            cursor: pointer;
        }


        &.placeholder-color {
            color: rgb(197, 200, 206);
        }
    }


    .options-wrap {
        z-index: 99;
        position: absolute;
        width: 100%;
        max-height: 30vh;
        overflow: auto;
        box-shadow: 0 1px 6px rgba(0, 0, 0, 0.2);
        border-radius: 4px;
        visibility: hidden;
        opacity: 0;
        transform-origin: 0% 0%;
        transform: scaleY(.8);
        transition: opacity .3s, transform .3s, visibility .3s;


        &.show {
            visibility: visible;
            opacity: 1;
            transform-origin: 0% 0%;
            transform: scaleY(1);
        }


        .options-list {
            color: #333;
            border-bottom: 1px solid #E3E3E3;
            background: #fff;
            padding: 7px 16px;
            cursor: pointer;


            &:hover {
                background: #f3f3f3;
            }


            &.active {
                background: #337DFF;
                color: #fff;
            }


        }
    }
}</style>

143

声明:Web前端小站 - 前端博客 - 王搏的个人博客|版权所有,违者必究|如未注明,均为原创

转载:转载请注明原文链接 - Vue 封装下拉框

评论
孙瑞杰生日