2022-01-29 业务需求封装一个antd-vue的下拉输

2022-01-29  本文已影响0人  _DevilTimer

我们先来看效果

点击获取焦点时


image.png

选中项目后


image.png
鼠标悬浮时
image.png

获取焦点 输入内容后 支持查询功能,并提示已选功能


image.png
点击清空按钮后回到最开始的获取焦点
image.png
自动支持全选 和反选全选
image.png
原生的ANTD的 兼容原本的单选功能,
image.png

调用方式 因为我这里是循环输出,所有你只需要对应自己的变量即可

<div class="title-right">
      <slot name="right">
             <template v-for="(item, i) in btns">
                  <span :key="i" class="input-select">
                        <Ytselect v-model="form[item.keyName || 'key'+i]" @change="change(item)" :options="item.options" :tipSize="5" class="title-right_select" :mode="item.mode"/>
                  </span>
           </template>
      </slot>
 </div>

以下是设计逻辑, 大量应用ES6 语法 。

我的写组件的思路 尽量只关心输入输出的数据,内部数据预定义,并且保持多种状态。可以扩展,插槽等,尽量让开发组员只做配属数据 不做逻辑本功能的逻辑处理。

<template>
    <span :class="['kyol-Ytselect',keyName]" :style="{width}">
        <a-select :mode="mode" v-model="myValue" :open="!mode ? undefined : open" class="select" v-bind="config" v-on="events" @change="change(myValue)" show-search>
            <a-select-option value="all" v-if="mode || !isSearch">
                {{selectAllText}}
            </a-select-option>
            <template v-for="(item, i) in selectOptions">
                <a-select-option :key="i" :value="item.value" :disabled="item.disabled" v-if="!item.hide"  >
                    <slot name="innerOption" :record="item" :index="i">
                        <a-tooltip placement="top">
                            <template slot="title" v-if=" tipSize && item.label && item.label.length >= tipSize ">
                                <!-- 提示文字 -->
                                <span>{{item.label}}</span>
                            </template>
                            <!-- 正常文字 -->
                            <span>{{item.label}}</span>
                        </a-tooltip>
                    </slot>
                </a-select-option>
            </template>
        </a-select>
       <a-tooltip placement="top" v-if="mode">
            <template slot="title" v-if="showSelectors.length">
                <!-- 提示文字 -->
                <span>{{showSelectors}}</span>
            </template>
            <!-- 正常文字 -->
            <a-input v-model="inputValue" ref="input" @focus="toggle(true)" @blur="toggle(false)" :placeholder="placeholder" class="input"  allowClear @change="inputChange" @click.native="toggle(true)">
            </a-input>
        </a-tooltip>
    </span>
</template>
<script>
export default {
    /**
     * author: luowei 
     * 多功能组件优化选项框, 输入输入多选和单选的作用
     */
    name: 'Ytselect',
    model:{
        prop: 'value',
        event: 'outData'
    },
    slots:{
        innerOption: 'option 内容体显示插槽'
    },
    props: {
        value: String | Number | Array,
        keyName: String,        // 当前组件的唯一标识符
        width: String,
        mode: String | Boolean,           // 选择模式同antd
        selectAllText: {       // 全选文字设置
            type: String,
            default: '全部'
        },
        options:{           // 下拉选项组
            type: Array,
            default: Array, // [{label, value, disabled, hide : false} ]
        },
        tipSize:{          // 提示文字超过长度 则提示
            type: Number | String,
            default: 0
        },
        placeholder: {    
            type: String, default: '请选择内容'
        },
        LabelsSplit: {       // input的展示文字的分隔符
             type: String, default: '、'
        },
        config: Object,  // 其他配置属性
        events: Object,  // 其他配置函数,
    },
    data(){
        return {
            selectOptions:[],      // 本地选择下拉数组
            myValue: undefined,   // 被选中后的数据
            open: false,         // 开启下拉
            lastSelect: false,  // 上一次选择内容对比值
            selected:'',    // 选择框的内容个数
            inputValue:'',  // 输入框的内容
            isSearch: false, // 搜索状态,关闭ALL选项
        }
    },
    computed:{
        showSelectors(){
            return this.options.filter(({value}) => (this.myValue || []).includes(value) ).map( ({label}) => label).join(this.LabelsSplit)
        }
    },
    watch:{
        value:{
            handler(cval){
                this.myValue = cval
            },
            immediate: true
        },
        options:{
            handler(cval){
                this.selectOptions = cval.map(item =>({...item,disabled:false, hide: false}))
                this.mode && this.computedInputText()
            },
            immediate: true
        }
    },
    methods:{
        toggle( status ){
            // 只存在为真,则显示为空,为false时刻监听了值变化
            if(status){
                 this.inputValue = ''
                 this.selectOptions.forEach(item => item.hide = false)
            } else {
                this.computedInputText()
            }  
            this.open = status
        },
        // 监听显示被选择个数的内容
        computedInputText(){
            const length = (this.myValue ?? []).filter( value => value !== 'all').length
            this.inputValue = this.selected = length ? `已经选中${length}项目` : ''
        },
        change(){
            const { myValue,lastSelect, options} = this 
            if(this.mode){
                const allSpan = options.map( item => item.value)
                const hasAll = myValue.includes('all')  // 取值包含 全部
                const valueSize = myValue.length // 取值之后长度
                const length = options.length
                if(  !lastSelect && ( valueSize === length || hasAll)) {
                    // 累计全选
                    this.myValue =[ 'all', ...allSpan ]
                } else if( hasAll && valueSize === length ){
                    // 累计不全选
                    this.myValue = this.myValue.filter(value => value !== 'all')
                }else if(lastSelect && !hasAll){
                    this.myValue = []
                }
                this.lastSelect = this.myValue.includes('all')
                this.computedInputText()
            }
            this.throwData()
        },
        // 输入框变化
        inputChange(){
            if(this.myValue.length && this.inputValue ===''){
                // 清空操作
                this.lastSelect = false
                this.myValue = []
                this.$refs['input'].focus()
            }else {
                // 开启搜索模式
                this.selectOptions.forEach( item => item.hide = !item.label.includes(this.inputValue))
                this.isSearch = true
            }

        },
        throwData(){
            this.$emit('outData', this.myValue)
            this.$emit('change', { keyName: this.keyName, value: this.myValue})
        },
    }
    
}
</script>
<style lang="scss" scoped>
.kyol-Ytselect{
    display: inline-block;
    min-width:150px;
    width:100%;
    position: relative;
    .select{
        width:100%;
        /deep/ .ant-select-selection--multiple{
            height:35px;
            overflow: hidden;
        }
    }
    .input{
        position:absolute;
        left:0;
        right:0;
        top:0;
    }
}

</style>
上一篇下一篇

猜你喜欢

热点阅读