仿el-select组件
时间: 2023-07-13 16:30:47 浏览: 177
好的,下面是一个仿 Element UI 中 el-select 组件的实现方案。
首先,我们可以创建一个 Select 组件,包含以下几个部分:
1. 输入框:用于显示当前选中的选项或输入搜索关键词。
2. 下拉菜单:用于展示所有可选项。
3. 多选/单选按钮:用于切换多选和单选模式。
4. 清除按钮:用于清除已选中的选项。
下面是 Select 组件的基本代码:
```html
<template>
<div class="select" :class="{ 'is-multiple': isMultiple, 'is-focus': isFocus }">
<div class="input-container" @click="toggleMenu">
<div class="input" :class="{ 'is-placeholder': !selectedOptions.length }">
<span class="selected-label" v-if="selectedOptions.length">
<span v-for="(option, index) in selectedOptions" :key="index" class="el-tag el-tag--info">{{ option.label }}</span>
</span>
<input type="text" class="input-field" v-model="searchKeyword" :placeholder="placeholder" :readonly="!filterable" @focus="isFocus = true" @blur="isFocus = false">
</div>
<i class="el-select__caret el-input__icon el-icon-arrow-up" :class="{ 'el-icon-arrow-down': !isMenuOpen }"></i>
</div>
<transition name="el-zoom-in-top">
<ul class="options" v-show="isMenuOpen">
<li v-for="(option, index) in filteredOptions" :key="index" @click="selectOption(option)">
<span class="option-label">{{ option.label }}</span>
<i class="el-icon-check" v-if="isSelected(option)"></i>
</li>
<li v-if="!filteredOptions.length" class="no-data">{{ noDataText }}</li>
</ul>
</transition>
<div class="el-select__tags" v-if="isMultiple">
<span v-for="(option, index) in selectedOptions" :key="index" class="el-tag el-tag--info">
{{ option.label }}
<i class="el-tag__close el-icon-close" @click.stop="removeOption(option)"></i>
</span>
</div>
<div class="el-select__actions" v-if="isMultiple && allowClear">
<span class="el-select__clear" @click="clearSelection">
<i class="el-select__clear-icon el-icon-circle-close"></i>
</span>
</div>
</div>
</template>
<script>
export default {
name: 'Select',
props: {
options: {
type: Array,
required: true
},
value: {
type: [Object, Array],
default: null
},
placeholder: {
type: String,
default: '请选择'
},
multiple: {
type: Boolean,
default: false
},
filterable: {
type: Boolean,
default: false
},
allowClear: {
type: Boolean,
default: false
},
noDataText: {
type: String,
default: '无匹配数据'
}
},
data () {
return {
selectedOptions: [],
searchKeyword: '',
isMenuOpen: false,
isFocus: false
}
},
computed: {
isMultiple () {
return this.multiple || Array.isArray(this.value)
},
filteredOptions () {
return this.options.filter(option => option.label.includes(this.searchKeyword))
},
selectedValues () {
return this.selectedOptions.map(option => option.value)
}
},
watch: {
value (newValue) {
if (!this.isMultiple) {
const selectedOption = this.options.find(option => option.value === newValue)
this.selectedOptions = selectedOption ? [selectedOption] : []
} else {
this.selectedOptions = this.options.filter(option => newValue.indexOf(option.value) !== -1)
}
},
selectedOptions (newSelectedOptions) {
if (!this.isMultiple) {
const newValue = newSelectedOptions.length ? newSelectedOptions[0].value : null
this.$emit('input', newValue)
this.$emit('change', newValue)
} else {
const newValue = newSelectedOptions.map(option => option.value)
this.$emit('input', newValue)
this.$emit('change', newValue)
}
}
},
methods: {
toggleMenu () {
if (!this.disabled) {
this.isMenuOpen = !this.isMenuOpen
}
},
selectOption (option) {
if (!this.isMultiple) {
this.selectedOptions = [option]
this.isMenuOpen = false
} else {
const index = this.selectedOptions.indexOf(option)
if (index !== -1) {
this.selectedOptions.splice(index, 1)
} else {
this.selectedOptions.push(option)
}
}
},
removeOption (option) {
const index = this.selectedOptions.indexOf(option)
if (index !== -1) {
this.selectedOptions.splice(index, 1)
}
},
clearSelection () {
this.selectedOptions = []
},
isSelected (option) {
return this.selectedValues.indexOf(option.value) !== -1
}
}
}
</script>
<style scoped>
.select {
position: relative;
display: inline-block;
width: 200px;
font-size: 14px;
color: #606266;
background-color: #fff;
border: 1px solid #dcdfe6;
border-radius: 4px;
cursor: pointer;
}
.select.is-multiple .input {
display: flex;
flex-wrap: wrap;
align-items: center;
padding: 0 10px;
}
.select.is-multiple .el-select__tags {
display: flex;
flex-wrap: wrap;
padding: 5px 0;
}
.select.is-multiple .el-tag {
margin-right: 10px;
margin-bottom: 5px;
font-size: 12px;
line-height: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.select.is-multiple .el-tag__close {
margin-left: 5px;
cursor: pointer;
}
.select.is-multiple .el-select__actions {
display: flex;
align-items: center;
height: 34px;
padding-right: 10px;
}
.select.is-multiple .el-select__clear {
margin-right: 5px;
font-size: 12px;
color: #c0c4cc;
cursor: pointer;
}
.select.is-multiple .el-select__clear:hover {
color: #909399;
}
.select.is-focus {
border-color: #409EFF;
box-shadow: 0 2px 6px rgba(0,0,0,.2);
}
.select.is-focus .el-select__caret {
color: #409EFF;
}
.select .input-container {
display: flex;
align-items: center;
height: 34px;
padding: 0 10px;
}
.select .input {
display: flex;
align-items: center;
height: 100%;
flex: 1;
overflow: hidden;
}
.select .input.is-placeholder .input-field {
color: #c0c4cc;
}
.select .input .selected-label {
display: flex;
flex-wrap: wrap;
align-items: center;
margin-right: 5px;
}
.select .input .selected-label .el-tag {
margin-right: 5px;
margin-bottom: 5px;
font-size: 12px;
line-height: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.select .input .input-field {
flex: 1;
border: none;
outline: none;
margin-right: 5px;
font-size: 14px;
line-height: 1;
background-color: transparent;
}
.select .el-select__caret {
color: #dcdfe6;
font-size: 12px;
transition: all .3s;
}
.select .el-select__caret.el-icon-arrow-up {
transform: rotate(-180deg);
}
.select .options {
position: absolute;
top: 100%;
left: 0;
z-index: 99;
width: 100%;
max-height: 250px;
margin: 0;
padding: 6px 0;
background-color: #fff;
border: 1px solid #dcdfe6;
border-top: none;
border-radius: 0 0 4px 4px;
box-shadow: 0 2px 6px rgba(0,0,0,.2);
overflow-y: auto;
}
.select .options::-webkit-scrollbar {
width: 6px;
}
.select .options::-webkit-scrollbar-thumb {
background-color: rgba(0,0,0,.2);
border-radius: 3px;
}
.select .options li {
padding: 6px 12px;
font-size: 14px;
color: #606266;
white-space: nowrap;
cursor: pointer;
}
.select .options li:hover {
background-color: #f5f5f5;
}
.select .options li .option-label {
display: inline-block;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}
.select .options li .el-icon-check {
margin-left: 5px;
color: #409EFF;
}
.select .options li .el-icon-check.disabled {
color: #c0c4cc;
}
.select .options li.no-data {
text-align: center;
color: #c0c4cc;
}
</style>
```
上面的代码中,props 中的 options 数组包含了所有可选项,每个选项由 label 和 value 两个属性组成。搜索关键词通过 v-model 绑定到 input 输入框中,并且通过 @focus 和 @blur 事件实现了输入框的聚焦和失焦效果。下拉菜单的显示和隐藏通过 v-show 和 isMenuOpen 控制。选中的选项通过 selectedOptions 数组保存,selectOption 方法用于切换选项的选中状态。
如果您需要支持多选功能,可以将 multiple 属性设置为 true。此时,Select 组件会显示一个 el-select__tags 区域,用于展示已选中的选项。同时,也会显示一个 el-select__actions 区域,用于实现清除已选中的选项功能。如果您需要支持清除已选中的选项功能,可以将 allowClear 属性设置为 true。
希望这个仿 Element UI 中 el-select 组件的实现方案可以帮助到您。
阅读全文