我們先來(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>