一、組件樣式
二、依賴
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; } } }