一、组件样式
二、依赖
elmentui的el-popover组件
三、代码
<!-- 带搜索框支持多选的下拉组件 --> <template> <div class="vue-dropdown default-theme" ref="select_box"> <!-- @click="isShow =! isShow" --> <div class="cur-name" v-popover:popoverSelect> <input type="hidden" v-model="value" /> <span class="one-ellipsis" style="width:100%;display:inline-block;">{{ selectValue }}</span> </div> <!-- 下拉弹框 --> <el-popover v-model="isShow" ref="popoverSelect" popper-class="select-popover" placement="bottom" :width="popwidth" trigger="click"> <div class="search-module clearfix" v-show="isNeedSearch"> <input class="search-text" v-model="searchText" /> </div> <ul class="list-module"> <li v-for="(item,index) in itemlist" @click="selectToggle(item)" :class="activeValue===item.name ? 'isactive':''" :key="index" > <span class="list-item-text">{{item.name}}</span> <span class="'el-icon-check'" v-if="multiple&&item.checked"></span> </li> </ul> <div class="tip-nodata" v-show="isNoData">No results matched "{{searchText}}"</div> </el-popover> </div> </template> <script> export default { data() { return { selectValue: "", activeValue: "", // 当前选中项 itemlist: [], // 全部下拉选项 isShow: false, // 下拉框是否显示 isNoData: false, // 是否无数据 searchText: "", // 搜索词 selectData: [], // 选中的数据 popwidth: 200, // 下拉框的宽度 }; }, props: { placeholder: { //input placeholder的默认值 type: String, default: "请选择" }, isNeedSearch: { //是否需要搜索框 type: Boolean, default: false }, nodatatext: { //没有搜索到时的文本提示 type: String, default: "未找到结果" }, value: { // 默认选中的值 type: String }, optionlist: { //选择项数组 type: Array }, multiple: { // 是否可以多选 type: Boolean, default: false }, record: { // 是否记录上一次的选择 type: String, // 记录的识别ID default: '' }, }, watch: { searchText(newVal, oldVal) { // 搜索框实时检索 this.search(newVal); }, optionlist: { // 选项列表变化时 deep: true, handler: function (newVal,oldVal){ this.init() } }, value(newVal, oldVal) { // 外部传入的选中值变化时 console.log(newVal) this.initValue() } }, mounted() { this.init() this.popwidth = this.$refs.select_box.clientWidth }, methods: { // 初始选中值回显 initValue() { let that = this let valueSelected = this.optionlist.filter(option => option.id === that.value) let valueName = '' if(valueSelected.length){ valueName = valueSelected[0].name } this.selectValue = valueName ? valueName : this.placeholder; this.activeValue = this.selectValue; }, // 初始化数据 init() { let that = this this.itemlist = this.optionlist; this.initValue() if(this.record) { // 如果有记录功能 let selected = localStorage.getItem('recordInfo_'+this.record) if(selected){ selected = JSON.parse(selected) let recordList = [] selected.map(item => { let filter = that.optionlist.filter(option => option.id === item.id) if(filter.length){ recordList.push(filter[0]) } }) localStorage.setItem('recordInfo_'+this.record, JSON.stringify(recordList)) if(recordList.length){ this.selectToggle(recordList[0]) } else { this.selectToggle(this.optionlist[0]) } } else { this.selectToggle(this.optionlist[0]) } } //点击组件以外的地方,收起 document.addEventListener( "click", e => { if (!this.$el.contains(e.target)) { this.isShow = false; } }, false ); }, // 点击选中事件 selectToggle(data) { if (this.multiple) { // 如果是多选 data.checked = !data.checked; if (data.checked) { this.selectData.push(data); } else { let index = this.selectData.findIndex(item => item.id === data.id); this.selectData.splice(index, 1); } let selectName = this.selectData.map(item => item.name); if (selectName.length) { this.selectValue = selectName.join(","); } else { this.selectValue = ""; } this.activeValue = this.selectValue; this.setRecordList(this.selectData) this.$emit("item-click", this.selectData); } else { // 只能单选时 this.isShow = false; this.selectValue = data.name; this.activeValue = this.selectValue; this.setRecordList(data) this.$emit("item-click", data); } }, // 设置选中记录 setRecordList(data) { if(this.record) { let recordList = localStorage.getItem('recordInfo_'+this.record) if(recordList) { recordList = JSON.parse(recordList) } else { recordList = [] } let index = recordList.findIndex(item => item.id === data.id) if(index > -1) { recordList.splice(index,1) } recordList.unshift(data) if(recordList.length>3) { recordList = recordList.slice(0, 3) } localStorage.setItem('recordInfo_'+this.record, JSON.stringify(recordList)) } }, // 搜索事件 search(val) { this.itemlist = this.optionlist.filter(item => { return item.name.indexOf(val) != -1; }); if (this.itemlist.length > 0) { this.activeValue = this.itemlist[0].name; this.isNoData = false; } else { this.isNoData = true; } } } }; </script> <style lang="stylus" scoped> .list-and-search { margin-top: 1px; min-width: 100%; z-index: 1000; background: #fff; border: 1px solid #cfcfcf; // border-radius: 4px; // position: absolute; // box-shadow: 5px 5px rgba(102, 102, 102, 0.1); display: none; &.on { display: block; } } .cur-name { height: 100%; line-height: 1.44; position: relative; border: 1px solid #cfcfcf; border-radius: 4px; outline: none; color: #555; cursor: pointer; font-size: 14px; padding: 6px 25px 6px 12px; &::after { display: inline-block; content: ''; width: 8px; height: 8px; margin-left: 2px; border-bottom: 1px solid #999; border-left: 1px solid #999; position: absolute; top: 50%; right: 12px; margin-top: -7px; vertical-align: middle; transform: rotate(-45deg); } } .vue-dropdown.default-theme { width: 100%; height: 32px; display: inline-block; vertical-align: middle; // z-index: 10; cursor: pointer; -webkit-user-select: none; user-select: none; position: relative; /* &:focus{ background-color: #d4d4d4; border-color: #8c8c8c; } */ &._self-show { display: block !important; } input::-webkit-input-placeholder { font-size: 14px; } } .tip-nodata { padding: 3px; background: #f5f5f5; margin: 0 5px; white-space: nowrap; font-size: 14px; color: #333; } </style>
四、elmentui组件样式修改
.el-popper[x-placement^=bottom], .el-popper[x-placement^=right]{ &.select-popover{ // 可搜索可多选下拉组件 margin-top: 2px; background: #fff; box-shadow: none; padding: 0; .popper__arrow{ display: none; } .search-module { position: relative; padding: 4px 8px; .search-text { width: 100%; height: 30px; // text-indent: 10px; padding: 6px 12px; font-size: 14px; border: 1px solid #cfcfcf; border-radius: 4px; box-shadow: none; outline: none; } } input::-webkit-input-placeholder { font-size: 14px; } .list-module { max-height: 200px; overflow-y: auto; li { &._self-hide { display: none; } cursor: pointer; padding: 0 15px; height : 36px; line-height : 36px; color: #555; &:hover{ background: #ddeeff; } &.isactive { background-color: #f5f5f5; } } } } /deep/ .popover-item{ border-bottom: 1px solid #EEEEEE; height:70px; line-height:30px; &:last-child{ border-bottom: none; } } }