<template> <el-select v-if="show" ref="visualSelect" v-model="selectValue" :class="classId" popper-class="VisualSelects coustom-de-select" no-match-text=" " reserve-keyword clearable v-bind="$attrs" v-on="$listeners" @change="visualChange" @visible-change="popChange" > <p v-if="startIndex === 0 && $attrs.multiple" class="select-all" > <el-checkbox v-model="selectAll" v-customStyle="customStyle" :indeterminate="isIndeterminate" @change="selectAllChange" >{{ $t('dataset.check_all') }} </el-checkbox> </p> <el-option v-for="item in options" :key="item.id" :label="item.text" :value="item.id" :class="setSelect(item.id)" > <span :title="item.text">{{ item.text }}</span> </el-option> </el-select> </template> <script> import { handlerInputStyle } from '@/components/widget/deWidget/serviceNameFn.js' import { uuid } from 'vue-uuid' export default { name: 'ElVisualSelect', model: { prop: 'value', // 绑定的值,通过父组件传递 event: 'update' // 自定义名 }, props: { classId: { type: String, require: true, default: uuid.v1() }, customStyle: { type: Object, default: () => { } }, list: { type: Array, default: () => { return [] } }, value: { type: [String, Number, Array], default: '' }, keyWord: { type: String, default: '' } }, data() { return { newList: [], selectValue: this.value, options: [], domList: null, selectBoxDom: null, scrollbar: null, startIndex: 0, endIndex: 0, maxLength: 9, // 弹出框最大支持9个条目 itemHeight: 34, // select组件选项高度 maxHeightDom: null, defaultFirst: false, show: true, selectAll: false } }, computed: { isIndeterminate() { return Array.isArray(this.selectValue) && this.selectValue.length > 0 && this.isAllSelect() > 0 && this.selectValue.length !== this.halfSelect() } }, watch: { value(val) { this.selectValue = val }, selectValue(val) { this.$emit('update', val) if (!val) { this.resetList() this.maxHeightDom.style.height = this.newList.length * 34 + 'px' this.domList.style.paddingTop = 0 + 'px' } }, list() { this.resetList() this.show = false this.$nextTick(() => { this.show = true this.$nextTick(() => { this.init() }) }) }, keyWord(val, old) { if (val === old) return const results = val ? this.list.filter(item => item.text.includes(val)) : null this.resetList(results) this.reCacularHeight() this.$nextTick(() => { this.callback() }) } }, mounted() { this.resetList() this.$nextTick(() => { this.init() }) }, methods: { resetSelectAll() { this.selectAll = false }, setSelect(id) { if (Array.isArray(this.selectValue)) { return this.selectValue.map(ele => ele.id).includes(id) && 'selected' } return this.selectValue === id && 'selected' }, selectAllChange(val) { let vals = val ? [...this.list.map(ele => ele.id)] : [] if (this.keyWord.trim() && val) { vals = this.list.filter(item => item.text.includes(this.keyWord.trim())).map(ele => ele.id) } this.visualChange(vals) this.selectValue = vals this.$emit('change', vals) this.$emit('handleShowNumber') }, addScrollDiv(selectDom) { this.maxHeightDom = document.createElement('div') this.maxHeightDom.className = 'el-select-height' selectDom.insertBefore(this.maxHeightDom, this.domList) }, reCacularHeight() { const h = this.$attrs.multiple ? 16 : 0 this.maxHeightDom.style.height = this.newList.length * this.itemHeight + h + 'px' }, resetList(arrays) { if (Array.isArray(arrays)) { this.newList = arrays.slice() this.domList.style.paddingTop = 0 + 'px' this.scrollbar.scrollTop = 0 this.callback() } else { this.newList = this.list.slice() } this.options = this.newList.slice(0, this.maxLength) }, customInputStyle() { if (!this.$parent.$parent.handlerInputStyle || !this.$refs.visualSelect) return handlerInputStyle(this.$refs.visualSelect.$el.querySelector('.el-input__inner'), this.$parent.element.style) handlerInputStyle(this.$refs.visualSelect.$el.querySelector('.el-select__input'), { wordColor: this.$parent.element.style.wordColor }) }, init() { if (this.defaultFirst && this.list.length > 0) { this.selectValue = this.list[0].value } if (!this.list || !this.list.length) { this.customInputStyle() return } const selectDom = document.querySelector( `.${this.classId} .el-select-dropdown .el-select-dropdown__wrap` ) this.scrollbar = document.querySelector(`.${this.classId} .el-select-dropdown .el-scrollbar`) this.selectBoxDom = document.querySelector(`.${this.classId} .el-select-dropdown__wrap`) this.selectBoxDom.style.display = 'flex' this.selectBoxDom.style.flexDirection = 'row' this.domList = selectDom.querySelector( `.${this.classId} .el-select-dropdown__wrap .el-select-dropdown__list` ) this.addScrollDiv(this.selectBoxDom) this.scrollFn() this.customInputStyle() }, scrollFn() { this.scrollbar.addEventListener('scroll', this.callback, false) }, callback() { if (!this.scrollbar) return const scrollTop = this.scrollbar.scrollTop this.startIndex = parseInt(scrollTop / this.itemHeight) this.endIndex = this.startIndex + this.maxLength this.options = this.newList.slice(this.startIndex, this.endIndex) this.domList.style.paddingTop = scrollTop - (scrollTop % this.itemHeight) + 'px' }, popChange() { this.$emit('resetKeyWords', '') this.domList.style.paddingTop = 0 + 'px' this.startIndex = 0 this.$nextTick(() => { if (this.$attrs.multiple) { this.selectAll = this.selectValue.length === this.list.length } }) this.resetList() this.reCacularHeight() }, isAllSelect() { let vals = this.list.length if (this.keyWord.trim()) { vals = this.list.filter(item => item.text.includes(this.keyWord.trim())).map(ele => ele.id).filter(ele => this.selectValue.includes(ele)).length } return vals }, halfSelect() { let vals = this.list.length if (this.keyWord.trim()) { vals = this.list.filter(item => item.text.includes(this.keyWord.trim())).map(ele => ele.id).length } return vals }, visualChange(val) { if (this.$attrs.multiple) { this.selectAll = val.length === this.halfSelect() } this.$emit('visual-change', val) } } } </script> <style lang="scss"> .VisualSelects { .el-scrollbar { position: relative; height: 245px; overflow: inherit; overflow-x: hidden; content-visibility: auto; } ::-webkit-scrollbar { background: #ffffff !important; } .el-select-height { width: 1px; position: absolute; top: 0; left: 0; } .el-select-dropdown__list { width: 100%; position: absolute; top: 0; left: 0; } .el-select-dropdown__wrap { height: 0; } } .select-all { padding: 10px 20px 0 20px; } </style>