vue-tree-select树形组件,支持一次加载上万条树据无卡顿
vue
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
项目地址:https://gitcode.com/gh_mirrors/vu/vue
免费下载资源
·
在使用vue中tree和select组件,通常会调用后端接口从而去渲染数据。
若后端返回数据量过于庞大,则会导致渲染缓慢的问题和页面直接卡死的问题。
处理方案
1.先渲染100条数据
2.滚动时前端处理加载分页获取数据并添加
vue-tree树形结构递归调用组件
<template>
<div class="ec-tree">
<div v-for="(item,index) in treeData" :key="index">
<div
class="parent"
@click.stop="itemClick(item)"
:class="{on:item.checked}"
:title="item[attrs.replaceFields.title]">
<!-- <input type="checkbox" @click.stop="onClick(item)" v-model="item.check">-->
<a-icon v-if="item[attrs.replaceFields.children] && item[attrs.replaceFields.children].length" type="caret-right"></a-icon>
<span>{{item[attrs.replaceFields.title]}}</span>
</div>
<div class="children" v-if="item[attrs.replaceFields.children] && item[attrs.replaceFields.children].length && item.expansion">
<ec-tree
:treeData="item[attrs.replaceFields.children]"
:attrs="attrs"
:defaultValue="defaultValue"
@tree-node-click="treeNodeClick"
>
</ec-tree>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'ec-tree',
props: {
treeData: {
type: Array,
default: () => []
},
attrs: {
type: Object,
default: () => {
return {
replaceFields: {
children: 'children',
title: 'title',
value: 'value',
key: 'key'
}
}
}
},
defaultValue: {
type: String,
default: ''
}
},
data () {
return {
scopesDefault: [],
node: [],
check: ''
}
},
methods: {
itemClick (item) {
this.$emit('tree-node-click', item) // 组件回调
this.treeNodeClick(item)
},
treeNodeClick (item) {
let name = this.attrs.replaceFields.title
this.expansion(this.treeData, item)
this.setSelect(this.treeData, item[name])
this.$emit('change', item) // 返回结果
},
/***
* 处理展开,递归处理
* @param row
*/
expansion (array, row) {
let children = this.attrs.replaceFields.children
array.map(item => {
if (row === item) {
this.$set(item, 'expansion', !item.expansion)
}
if (item[children] && item[children].length) {
this.expansion(item[children], row)
}
})
},
/**
* 设置择中数据,递归处理
* @param array 数组
* @param val
*/
setSelect (array, val) {
let children = this.attrs.replaceFields.children
let name = this.attrs.replaceFields.title
array.forEach(item => {
this.$set(item, 'checked', false)
if (item[name] === val) {
this.$set(item, 'checked', true)
}
if (item[children] && item[children].length) {
this.setSelect(item.children, val)
}
})
}
},
created () {
this.setSelect(this.treeData, this.defaultValue)
}
}
</script>
<style lang="scss">
.ec-tree {
position: relative;
width: 100%;
.parent {
width: 100%;
padding: 5px 0;
overflow: hidden;
color: #444;
white-space: nowrap;
text-overflow: ellipsis;
cursor: pointer;
&.on {
color: #3f98e3;
}
}
.children {
width: 100%;
padding: 5px 0 5px 20px;
overflow: hidden;
color: #444;
white-space: nowrap;
text-overflow: ellipsis;
cursor: pointer;
&.on {
color: #3f98e3;
}
}
}
</style>
父组件代码vue-tree-select
<template>
<div>
<div class="edit-tree-select-asyn">
<a-input
@click.stop="onFocus"
@change="onChange"
@blur="onBlur"
@input="onInput"
:placeholder="placeholder"
:value="value"
v-bind="attrs"
>
<a-icon type="down" :class="visible ? 'up':'down'" @click.stop="onDown" slot="suffix" style="color: rgba(0, 0, 0, 0.2); cursor: pointer;"></a-icon>
</a-input>
<div class="tree-body" @scroll="onScroll" v-show="visible">
<ec-tree :treeData="filterList.length ? filterList : treeData" :attrs="attrs" :defaultValue="getValue" @change="getTreeData"></ec-tree>
<a-empty v-if="treeData.length<=0"/>
</div>
</div>
</div>
</template>
<script>
import ecTree from '@/components/ec/ec-tree/ec-tree'
export default {
name: 'edit-tree-select-asyn',
components: {
ecTree
},
props: {
parseDictData: {
type: Array,
default: () => []
},
component: {
type: Object,
default:{}
},
},
data () {
return {
visible: false,
treeData: [],
filterList: [],
currentState: false, // 当前状态
pages: 0,
flag: false,
value:""
}
},
computed: {
attrs () {
const attrs = {
replaceFields: {
children: 'children',
title: this.labelProp,
value: this.valueProp,
key: this.valueProp,
size: 50
},
allowClear: false,
showSearch: false,
inputBoxSetting: true,
...this.component.attrs
}
return attrs
}
},
methods: {
onChange (evnet) {
let value = evnet.target.value
this.filter(this.parseDictData, value)
this.setInputBoxSetting(false) // 输入重置
this.updateMode(value || '')
},
// 更新数据输入框的值
updateMode (value) {
this.value=value
},
/**
* 滑动页面加载数据
* */
onScroll (e) {
if (this.treeData.length >= this.parseDictData.length) { // 加载完成后不在重新处理拉
return false
}
let event = e.target
let scrollHeight = event.scrollHeight - event.clientHeight
if (scrollHeight - event.scrollTop < 100 && !this.flag) {
this.flag = true
this.setData()
this.flag = false
}
},
// 获取结果数据
getTreeData (item) {
let name = this.attrs.replaceFields.title
this.updateMode(item[name])
this.$emit('change', item) // 返回结果
},
onFocus () {
// this.treeData = this.parseDictData || []
this.setData()
this.currentState = true
this.visible = true
},
// 设置数据
setData () {
for (let i = 0; i < this.attrs.replaceFields.size; i++) { // 选加载50条
if (!this.parseDictData[this.pages]) {
break
}
this.treeData.push(this.parseDictData[this.pages])
this.pages++
}
},
// 失去交点
onBlur () {
if (!this.currentState && this.attrs.inputBoxSetting) { // 不选择失去焦点清空内容
this.value=''
}
},
setInputBoxSetting (state) { // 设置不选择时清空内容true\false
if (this.attrs.inputBoxSetting) {
this.currentState = state
}
},
// 点击下面
onDown () {
this.setData()
this.visible = !this.visible
},
/**
* 数据过滤
* @param array //数组
* @param value //输入值
*/
filter (array, value) {
if (value) {
let reg = /^[A-Za-z]+$/
let children = this.attrs.replaceFields.children
let name = this.attrs.replaceFields.title
if (reg.test(value)) { // 拼音
array.forEach(item => {
if (item.spellCode && (item.spellCode.toLowerCase().indexOf(value.toLowerCase()) > -1 || item.spellCode.indexOf(value) > -1)) {
this.filterList.push(item)
}
if (item[children] && item[children].length) {
this.filter(item[children], value)
}
})
} else {
array.forEach(item => {
if (item[name].toLowerCase().indexOf(value.toLowerCase()) > -1 || item[name].indexOf(value) > -1) {
this.filterList.push(item)
}
if (item[children] && item[children].length) {
this.filter(item[children], value)
}
})
}
} else {
this.filterList = []
}
},
// 隐藏内容
hide () {
if (this.visible) {
this.filterList = []
this.visible = false
}
},
// 输入内容
onInput () {
this.currentState = false
}
},
mounted () {
// 监听全局点击事件
document.addEventListener('click', this.hide)
},
beforeDestroy () {
// 在页面注销前,将点击事件给移除
document.removeEventListener('click', this.hide)
}
}
</script>
<style lang="scss">
.edit-tree-select-asyn {
position: relative;
.tree-body {
position: absolute;
top: 35px;
left: 0;
z-index: 999;
box-sizing: border-box;
width: 100%;
min-height: 100px;
max-height: 200px;
padding: 5px;
overflow: auto;
overflow-y: scroll;
background: #fff;
border: 1px solid #eee;
box-shadow: 0 5px 10px #eee;
.parent {
width: 100%;
padding: 5px 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
cursor: pointer;
&.on {
color: #3f98e3;
}
}
.children {
width: 100%;
padding: 5px 0;
padding-left: 20px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
cursor: pointer;
&.on {
color: #3f98e3;
}
}
}
.down {
color: #ff2e31;
transition-duration: 0.3s;
}
.up {
color: #000c17;
transform: rotate(180deg);
transition-duration: 0.3s;
}
}
</style>
调用组vue-tree-select
<vue-tree-select></vue-tree-select>
GitHub 加速计划 / vu / vue
207.52 K
33.66 K
下载
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
最近提交(Master分支:1 个月前 )
73486cb5
* chore: fix link broken
Signed-off-by: snoppy <michaleli@foxmail.com>
* Update packages/template-compiler/README.md [skip ci]
---------
Signed-off-by: snoppy <michaleli@foxmail.com>
Co-authored-by: Eduardo San Martin Morote <posva@users.noreply.github.com> 3 个月前
e428d891
Updated Browser Compatibility reference. The previous currently returns HTTP 404. 3 个月前
更多推荐
已为社区贡献3条内容
所有评论(0)