2022-01-29 業(yè)務(wù)需求封裝一個(gè)antd-vue的下拉輸入框

我們先來(lái)看效果

點(diǎn)擊獲取焦點(diǎn)時(shí)


image.png

選中項(xiàng)目后


image.png

鼠標(biāo)懸浮時(shí)
image.png

獲取焦點(diǎn) 輸入內(nèi)容后 支持查詢功能,并提示已選功能


image.png

點(diǎn)擊清空按鈕后回到最開(kāi)始的獲取焦點(diǎn)
image.png

自動(dòng)支持全選 和反選全選
image.png

原生的ANTD的 兼容原本的單選功能,
image.png

調(diào)用方式 因?yàn)槲疫@里是循環(huán)輸出,所有你只需要對(duì)應(yīng)自己的變量即可

<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>

以下是設(shè)計(jì)邏輯, 大量應(yīng)用ES6 語(yǔ)法 。

我的寫組件的思路 盡量只關(guān)心輸入輸出的數(shù)據(jù),內(nèi)部數(shù)據(jù)預(yù)定義,并且保持多種狀態(tài)??梢詳U(kuò)展,插槽等,盡量讓開(kāi)發(fā)組員只做配屬數(shù)據(jù) 不做邏輯本功能的邏輯處理。

<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 
     * 多功能組件優(yōu)化選項(xiàng)框, 輸入輸入多選和單選的作用
     */
    name: 'Ytselect',
    model:{
        prop: 'value',
        event: 'outData'
    },
    slots:{
        innerOption: 'option 內(nèi)容體顯示插槽'
    },
    props: {
        value: String | Number | Array,
        keyName: String,        // 當(dāng)前組件的唯一標(biāo)識(shí)符
        width: String,
        mode: String | Boolean,           // 選擇模式同antd
        selectAllText: {       // 全選文字設(shè)置
            type: String,
            default: '全部'
        },
        options:{           // 下拉選項(xiàng)組
            type: Array,
            default: Array, // [{label, value, disabled, hide : false} ]
        },
        tipSize:{          // 提示文字超過(guò)長(zhǎng)度 則提示
            type: Number | String,
            default: 0
        },
        placeholder: {    
            type: String, default: '請(qǐng)選擇內(nèi)容'
        },
        LabelsSplit: {       // input的展示文字的分隔符
             type: String, default: '、'
        },
        config: Object,  // 其他配置屬性
        events: Object,  // 其他配置函數(shù),
    },
    data(){
        return {
            selectOptions:[],      // 本地選擇下拉數(shù)組
            myValue: undefined,   // 被選中后的數(shù)據(jù)
            open: false,         // 開(kāi)啟下拉
            lastSelect: false,  // 上一次選擇內(nèi)容對(duì)比值
            selected:'',    // 選擇框的內(nèi)容個(gè)數(shù)
            inputValue:'',  // 輸入框的內(nèi)容
            isSearch: false, // 搜索狀態(tài),關(guān)閉ALL選項(xiàng)
        }
    },
    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時(shí)刻監(jiān)聽(tīng)了值變化
            if(status){
                 this.inputValue = ''
                 this.selectOptions.forEach(item => item.hide = false)
            } else {
                this.computedInputText()
            }  
            this.open = status
        },
        // 監(jiān)聽(tīng)顯示被選擇個(gè)數(shù)的內(nèi)容
        computedInputText(){
            const length = (this.myValue ?? []).filter( value => value !== 'all').length
            this.inputValue = this.selected = length ? `已經(jīng)選中${length}項(xiàng)目` : ''
        },
        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 // 取值之后長(zhǎng)度
                const length = options.length
                if(  !lastSelect && ( valueSize === length || hasAll)) {
                    // 累計(jì)全選
                    this.myValue =[ 'all', ...allSpan ]
                } else if( hasAll && valueSize === length ){
                    // 累計(jì)不全選
                    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 {
                // 開(kāi)啟搜索模式
                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>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容