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>