Merge pull request #2488 from dataease/pr@dev@perf_visual_select

perf: 优化下拉框过滤组件数据量大卡顿 使用虚拟dom
This commit is contained in:
fit2cloud-chenyw 2022-06-23 16:56:48 +08:00 committed by GitHub
commit 5b8df7176b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 199 additions and 9 deletions

View File

@ -0,0 +1,166 @@
<template>
<el-select
ref="visualSelect"
v-model="selectValue"
:class="classId"
popper-class="VisualSelects"
v-bind="$attrs"
v-on="$listeners"
@visible-change="popChange"
>
<el-option v-for="item in options" :key="item.id" :label="item.text" :value="item.id" />
</el-select>
</template>
<script>
import { uuid } from 'vue-uuid'
export default {
name: 'ElVisualSelect',
model: {
prop: 'value', //
event: 'update' //
},
props: {
classId: {
type: String,
require: true,
default: uuid.v1()
},
list: {
type: Array,
default: () => {
return []
}
},
value: {
type: [String, Number, Array],
default: ''
}
},
data() {
return {
newList: [],
selectValue: this.value,
options: [],
domList: null,
slectBoxDom: null,
scrollbar: null,
startIndex: 0,
endIndex: 0,
maxLength: 9, // 9
itemHeight: 34, // select
maxHeightDom: null,
defaultFirst: false
}
},
watch: {
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.init()
}
},
mounted() {
this.resetList()
this.init()
},
methods: {
addScrollDiv(selectDom) {
this.maxHeightDom = document.createElement('div')
this.maxHeightDom.className = 'el-select-height'
selectDom.insertBefore(this.maxHeightDom, this.domList)
},
reCacularHeight() {
this.maxHeightDom.style.height = this.newList.length * this.itemHeight + 'px'
},
resetList(arrys) {
if (Array.isArray(arrys)) {
this.newList = arrys.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)
},
init() {
if (this.defaultFirst && this.list.length > 0) {
this.selectValue = this.list[0].value
}
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.slectBoxDom = document.querySelector(`.${this.classId} .el-select-dropdown__wrap`)
this.slectBoxDom.style.display = 'flex'
this.slectBoxDom.style.flexDirection = 'row'
this.domList = selectDom.querySelector(
`.${this.classId} .el-select-dropdown__wrap .el-select-dropdown__list`
)
this.addScrollDiv(this.slectBoxDom)
this.scrollFn()
},
scrollFn() {
this.scrollbar.addEventListener('scroll', this.callback, false)
},
callback() {
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.domList.style.paddingTop = 0 + 'px'
this.resetList()
this.reCacularHeight()
}
}
}
</script>
<style lang="scss">
.VisualSelects {
.el-scrollbar {
position: relative;
height: 251px;
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;
}
}
</style>

View File

@ -1,9 +1,10 @@
<template>
<el-select
v-if="element.options!== null && element.options.attrs!==null && show"
<component
:is="mode"
v-if="element.options!== null && element.options.attrs!==null && show "
ref="deSelect"
v-model="value"
:class-id="'visual-' + element.id + '-' + inDraw + '-' + inScreen"
:collapse-tags="showNumber"
:clearable="!element.options.attrs.multiple"
:multiple="element.options.attrs.multiple"
@ -12,12 +13,13 @@
:size="size"
:filterable="true"
popper-class="coustom-de-select"
:list="datas"
@change="changeValue"
@focus="setOptionWidth"
@blur="onBlur"
>
<el-option
v-for="item in datas"
v-for="item in templateDatas || datas"
:key="item[element.options.attrs.key]"
:style="{width:selectOptionWidth}"
:label="item[element.options.attrs.label]"
@ -25,11 +27,12 @@
>
<span :title="item[element.options.attrs.label]" style="display:inline-block;width:100%;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;">{{ item[element.options.attrs.label] }}</span>
</el-option>
</el-select>
</component>
</template>
<script>
import ElVisualSelect from '@/components/ElVisualSelect'
import { multFieldValues, linkMultFieldValues } from '@/api/dataset/dataset'
import bus from '@/utils/bus'
import { getLinkToken, getToken } from '@/utils/auth'
@ -37,6 +40,7 @@ import customInput from '@/components/widget/DeWidget/customInput'
import { textSelectWidget } from '@/components/widget/DeWidget/serviceNameFn.js'
export default {
components: { ElVisualSelect },
mixins: [customInput],
props: {
element: {
@ -65,6 +69,16 @@ export default {
}
},
computed: {
mode() {
let result = 'el-select'
if (this.element.options && this.element.options.attrs && this.element.options.attrs.visual) {
result = 'el-visual-select'
}
return result
},
templateDatas() {
return this.mode === 'el-visual-select' ? [] : null
},
operator() {
return this.element.options.attrs.multiple ? 'in' : 'eq'
},
@ -308,4 +322,4 @@ export default {
background-color: rgb(245, 247, 250, .5) !important;
}
}
</style>
</style>

View File

@ -19,7 +19,8 @@ const dialogPanel = {
value: 'id',
fieldId: '',
dragItems: [],
sort: {}
sort: {},
visual: false
},
value: '',
manualModify: false
@ -52,6 +53,7 @@ class TextSelectServiceImpl extends WidgetService {
super(options)
this.filterDialog = true
this.showSwitch = true
this.showVisual = true
}
initLeftPanel() {

View File

@ -9,8 +9,13 @@
:inactive-text="$t('panel.single_choice')"
@change="multipleChange"
/>
<span v-if="widget.showVisual" style="padding-left: 20px;">
<el-checkbox v-model="attrs.visual" @change="showVisualChange">虚拟化</el-checkbox>
</span>
</div>
</el-col>
<el-col :span="16">
<div class="filter-options-right">
<span style="padding-right: 10px;">
@ -64,7 +69,7 @@
<el-checkbox v-model="attrs.enableParameters" @change="enableParametersChange"><span>
{{ $t('panel.binding_parameters') }} </span> </el-checkbox>
<el-popover placement="bottom-end" :disabled="!attrs.enableParameters" width="200">
<el-popover placement="bottom-end" :disabled="!attrs.enableParameters" width="200">
<div class="view-container-class">
<el-checkbox-group v-model="attrs.parameters">
<el-checkbox
@ -99,7 +104,7 @@
</template>
<script>
import {mapState} from "vuex";
import { mapState } from 'vuex'
export default {
name: 'FilterControl',
@ -163,6 +168,9 @@ export default {
}
this.fillAttrs2Filter()
},
showVisualChange(value) {
this.fillAttrs2Filter()
},
fillAttrs2Filter() {}
}