vue下拉选择组件
可以实现多选,联想搜索(仅单选支持)
功能展示
可联想搜索的单选

多选

使用介绍
支持从父组件传的参数有
'redBorder','width','height','placeholder','optionList','selectOption','multiple','color','conflictList','disabled','isSearch'——按照自己需要传入,不需要的可不传
redBorder: 边框是否变红(自己项目中做必填校验时需要,可忽略不传)——可传值 true/false
width: 文本框的长度(默认208px)——传入值示例 width="100px"
height: 文本框的高度(默认28px)——传入值示例 height="30px"
placeholder:文本框提示语(默认请选择)
optionList:下拉选择列表——传入值示例([{name: '下拉选项1',code: 1}])
selectOption: 默认选中项 ——传入值示例(单选 直接传选中项code :如1,多选则需要传选中的数组[{name: '下拉选项1',code: 1}])
multiple:是否多选——多选传true,单选传false或不传
color:文本框中文字颜色—— 传定义了颜色的类名
disabled:是否禁用——true/false
conflictList: 在项目中用来检查冲突时使用可忽略不传
isSearch: 是否支持联想搜索——true/false
触发到父组件可接收的方法
@input:联想搜索时input框输入时触发,可用来重新请求下拉列表
@change: 选中项发生改变时触发,可用来给选中项赋值
@visibleChange: 下拉列表收起/展开时触发 参数为true时表示展开,false表示收起(可实现点击下拉框实时获取下拉列表数据)
调用组件
// 引入组件 import selectOption from "../../../basic/selectOption"; components: { selectOption }, // 使用 <selectOption :selectOption="role.code" :optionList="roleOption" placeholder="请选择角色" @change="selectRole" ></selectOption> // data中的变量 role:{name: '',code: ''} optionList: [{name: '角色1',code: '1'}] // methods中的方法 // 点击选择某个角色时触发 selectRole(val) { this.$set(this.role, "name", val.name); this.$set(this.role, "code", val.code); },
组件源码如下
注:由于字体颜色、字体大小、背景颜色、边框颜色、常用flex布局是直接写的公共样式库中的类名,故需样式需自己调整
<template>
<div>
<el-popover
placement="bottom-start"
:visible-arrow="false"
:popper-class="$store.state.model == 1 ? 'popperTop0' : 'popperTop0 simpleStyle'"
v-model="optionShow"
:disabled="disabled"
trigger="click">
<div class="selectBox rowJbAc" slot="reference" :class="[optionShow ? 'borderMcA1' : 'borderScA9',redBorder? 'redBorder': '']" :style="{width: selectWidth,minHeight: selectHeight}">
<!-- 复选且不为空时展示 -->
<div class=" multipleTextBox fontScA2 fontSizeA flexRow" v-if="multiple && selectedOptions.length!=0" :class="[disabled ? 'notAllow' : 'pointer']">
<!-- <div v-for="(item,index) in selectedOptions" :class="[isConflict(2,item.code) ? 'fontScC2' : 'fontScA2']" :key="index">{{item.name}}</div> -->
<div v-for="(item,index) in selectedOptions" :key="index" class="rowAc multipleTextItem borderScN4 borderAll">
<div class="fontSizeA multipleText" :class="[isConflict(2,item.code) ? 'fontScC2' : 'fontScA2']">{{item.name}}</div>
<div class="icon-close-circle fontSizeA fontScN6 pointer" @click.stop="clearItem(index)"></div>
</div>
<!-- <input type="text" class="selectSearchInput fontSizeB fontScN8" v-model="selectText" @input="inputEvent"> -->
</div>
<!-- 复选展示 -->
<input type="text" @focus="inputFocus" ref="input" v-if="multiple &&selectedOptions.length==0" v-model="selectText" readonly class="input fontSizeA" :class="{'fontScA1': !color || color=='fontScA2' ,'fontScC2': color == 'fontScC2','opacity':multiple&&selectedOptions.length!=0,'notAllow':disabled }" :placeholder="selectPlaceholder">
<!-- 单选展示 -->
<input type="text" ref="input" v-if="!multiple" @focus="inputFocus" @blur="inputBlur" v-model="selectText" :readonly="!isSearch" class="input fontSizeA" :class="[color? color: 'fontScA2',disabled ? 'notAllow' : 'pointer']" :placeholder="selectPlaceholder" @input="inputEvent">
<div class="fontSizeB fontScN5 arrowIcon rowAc" :class="[optionShow ? 'icon-arrow-up' : 'icon-arrow-down',disabled ? 'notAllow' : 'pointer']"></div>
</div>
<div class="optionWrap fontSizeA bgcScB1" :style="{minWidth: selectWidth}">
<div class="optionItem hoverBgcScA10 rowJbAc textOver" v-for="(item,index) in optionList" :key="index"
:class="[checkSelected(item) ? 'fontMcA1' : 'fontScA2']"
@click.stop="clickOption(item)" ref="selectInput">
<!-- <input type="text" class="selectInputOpacity"> -->
{{item.name}}
</div>
<div v-if="optionList.length == 0" class="fontScA6 fontSizeA empty" >暂无数据</div>
</div>
</el-popover>
</div>
</template>
<script>
export default {
data() {
return {
selectWidth: '208px', // 选择框宽度
selectHeight: '28px', // 选择框高度
selectPlaceholder: '请选择', // 提示消息
selectText: '',
selectedOption: {name: '',code: ''},// 记录当前选中的项————单选
selectedOptions: [],// 记录当前选中的项————多选
optionShow: false,// 选择框是否展示
optionClick: false,// 是否点击选项
isGetDefault: false, // 是否赋值默认数据
oldSelectText: '', // 记录旧的输入值
}
},
watch: {
optionList(newValue) {
// console.log('下拉列表:',newValue)
this.setOption('listChange')
},
selectOption() {
this.setOption('')
},
optionShow() {
this.$emit('visibleChange',this.optionShow)
if(this.optionShow) { // 滚动到对应的位置
// this.$refs.input.focus()
if(!this.multiple && this.selectedOption.name) {
setTimeout(()=>{
var idx = this.optionList.findIndex(item =>{return item.code == this.selectedOption.code})
if(idx != -1 && this.$refs.selectInput && this.$refs.selectInput[idx]) {
// this.$refs.selectInput[idx].focus()
this.$refs.selectInput[idx].scrollIntoView()
}
},20)
}
}else {
if(this.isSearch && this.$refs.input) {
this.$refs.input.blur()
}
}
},
},
created() {
this.$bus.$on('changeSelect')
},
// width ————宽度
// placeholder———— 提示
// optionList————选项列表{name: '',code: ''}
// selectOption————选中项
props: ['redBorder','width','height','placeholder','optionList','selectOption','multiple','color','conflictList','disabled','isSearch'],
mounted() {
this.setOption('')
},
methods: {
inputFocus() {
},
// input框失焦
inputBlur() {
this.selectText = this.selectedOption.name
this.optionShow = false
},
// 点击选项选中
clickOption(item) {
// console.log('clickOption')
this.optionClick = true
if(this.multiple) { // 多选
var idx = this.selectedOptions.findIndex(option => {return option.code == item.code})
if(idx == -1) {
this.selectedOptions.push(item)
}else {
this.selectedOptions.splice(idx,1)
}
// this.selectedOptions.forEach((option,index) => {
// if(index == 0) {
// this.selectText = option.name
// }else {
// this.selectText += `,${option.name}`
// }
// })
this.$emit('change',this.selectedOptions)
}else { // 单选
this.optionShow = false
this.selectText = item.name
this.$set(this.selectedOption,'name',item.name)
this.$set(this.selectedOption,'code',item.code)
this.$emit('change',this.selectedOption)
}
},
checkSelected(item) {
if(this.multiple) { // 多选
return this.selectedOptions.some((option) => {return item.code == option.code})
}else { // 单选
return item.code === this.selectedOption.code
}
},
// 检查是否冲突
isConflict(type,id) {
if(!this.conflictList) {
return false
}
return this.conflictList.some( item => { return item.type == type && item.id ==id })
},
// 赋值
setOption(type) {
// 判断父页面有无传对应的值
if(this.width) {
this.selectWidth = this.width
}
if(this.height) {
this.selectHeight = this.height
}
if(this.placeholder) {
this.selectPlaceholder = this.placeholder
}
// 有选中项
if(type != 'listChange' || !this.isGetDefault) {
if(this.selectOption && this.selectOption != '') {
if(this.multiple) { // 多选
this.selectedOptions = []
this.selectOption.forEach((item,index) => {
// if(index == 0) {
// this.selectText = item.name
// }else {
// this.selectText += `,${item.name}`
// }
var idx = this.optionList.findIndex(option => {return option.code == item.code})
if(idx != -1) {
this.selectedOptions.push(this.optionList[idx])
}
})
}else { // 单选
var idx = this.optionList.findIndex(item => {return item.code == this.selectOption })
if(idx != -1) {
this.selectText = this.optionList[idx].name
this.$set(this.selectedOption,'name',this.optionList[idx].name)
this.$set(this.selectedOption,'code',this.optionList[idx].code)
}
}
}else if(this.selectOption === 0) {
var idx = this.optionList.findIndex(item => {return item.code == this.selectOption })
if(idx != -1) {
this.selectText = this.optionList[idx].name
this.$set(this.selectedOption,'name',this.optionList[idx].name)
this.$set(this.selectedOption,'code',this.optionList[idx].code)
}
}else {
if(!this.multiple) {
this.selectText = ''
this.selectedOption = {name: '',code: ''}
}else {
this.selectedOptions = []
}
}
this.isGetDefault = true
}
},
inputEvent() {
if(this.oldSelectText != this.selectText) {
this.oldSelectText = this.selectText
this.$emit('input',this.selectText)
}
},
// 删除某个多选项
clearItem(index) {
console.log('sleectOption:',index,this.selectOption)
this.selectedOptions.splice(index,1)
this.$emit('change',this.selectedOptions)
}
}
}
</script>
<style lang="scss" scoped>
.selectSearchInput {
flex: 1;
min-width: 80px;
border: none;
outline: none;
}
.opacity {
opacity: 0;
position: absolute;
top: 0;
left: 0;
}
.multipleTextBox {
flex-wrap: wrap;
flex: 1;
padding: 0 4px 0 8px;
min-height: 26px;
}
.multipleTextItem {
min-height: 20px;
line-height: 20px;
padding: 0 4px;
margin-top: 2px;
margin-bottom: 2px;
box-sizing: border-box;
border-radius: 2px;
margin-right: 4px;
}
.multipleText {
margin-right: 2px;
}
.optionWrap::-webkit-scrollbar{
width:4px;
height:4px;
}
.selectBox {
position: relative;
border-width: 1px;
border-style: solid;
border-radius: 4px;
box-sizing: border-box;
outline: none;
.input {
min-height: 26px;
border-radius: 4px;
line-height: 26px;
text-indent: 8px;
border: none;
flex: 1;
outline: none;
}
.arrowIcon {
min-height: 26px;
height: 100%;
padding: 0 10px;
}
}
.empty {
height: 28px;
line-height: 28px;
text-align: center;
}
.optionWrap {
// position: absolute;
max-height: 300px;
overflow: auto;
// top: 28px;
// left: 0px;
// z-index: 4;
// width: 100%;
// padding: 2px 12px 12px;
box-sizing: border-box;
// border-style: solid;
// border-width: 1px;
border-radius: 4px;
}
.optionItem {
position: relative;
padding: 0 8px;
box-sizing: border-box;
// margin-top: 10px;
height: 28px;
border-radius: 5px;
flex: 1;
cursor: pointer;
}
// .selectInputOpacity {
// width: 0px;
// height: 0px;
// opacity: 0;
// position: absolute;
// top: 0;
// left: 0;
// border: none;
// outline: none;
// }
</style>

浙公网安备 33010602011771号