项目开发中需要用到一个具有滚动加载并出现加载动画的表格。
所以结合了element-plus中的表格组件进行了二次封装。
技术栈为 vue3 + ts + element-plus
思路
- 加载数据:对表格中的滚动条事件进行监听,到达条件则触发数据请求。
- 加载动画实现:使用克隆方法,触发数据请求的时候将克隆节点 append 到表格底部,请求结束的时候移除节点
效果图
代码实现
ScrollTable.vue 滚动表格组件
<template>
<div class="scroll-table" ref="scrollTableRef">
<el-table
:data="tableData"
style="width: 100%"
:max-height="400"
class="scroll_table"
@selection-change="handleSelectionChange"
>
<el-table-column
v-if="showSelection"
type="selection"
align="center"
width="60"
></el-table-column>
<el-table-column
v-if="showIndex"
type="index"
label="序号"
align="center"
width="80"
></el-table-column>
<el-table-column
v-for="(item) in tableConfigs"
:prop="item.prop"
:label="item.name"
:width="item.width"
:key="item.prop">
<template #default="scope">
<slot :name="item.prop" :row="scope.row">{{scope.row[item.prop]}}</slot>
</template>
</el-table-column>
</el-table>
<!-- 加载动画节点 -->
<div class="loading-row" id="loading" v-show="false">
<el-icon class="is-loading">
<loading />
</el-icon>
加载中...
</div>
</div>
</template>
<script lang="ts" setup>
import {nextTick, onMounted, ref, watch} from 'vue'
const props = defineProps({
showIndex: {
type: Boolean,
default: false
},
showSelection: {
type: Boolean,
default: false
},
tableConfigs: {
type: Array,
default: () => []
},
tableData: {
type: Array,
default: () => []
},
isLoading: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['selectionChange', 'load']);
function handleSelectionChange(event) {
emit("selectionChange", event);
}
// 防抖开关
let toggle = ref(true)
function setLoading() {
// 防抖
if (toggle.value) {
appendLoading()
// 触发请求事件
emit('load')
}
}
// 关闭loading动画
watch(() => props.isLoading, val => {
if (!val) {
removeLoading()
}
})
// loading动画节点缓存
let loadingTemp :any = null
// table dom element
const scrollTableRef = ref()
// 添加加载图标
function appendLoading() {
toggle.value = false
console.log('append')
// 使用加载缓存,不需要每次加载都重新克隆一次
if (!loadingTemp) {
// 克隆动画节点
let loading :any = document.querySelector('#loading')
loadingTemp = loading.cloneNode(true)
}
nextTick(() => {
loadingTemp.style.display = 'flex'
// 加载动画的节点宽度设置为表格宽度相等
loadingTemp.style.width = scrollTableRef.value.clientWidth + 'px'
table.appendChild(loadingTemp)
// 出现加载动画的时候将滚动条滚动到最底部,完整呈现加载动画
dom.scrollTop = dom.scrollHeight
})
}
// 移除加载图标
function removeLoading() {
toggle.value = true
table.removeChild(loadingTemp)
}
// 表格中需要滚动的节点缓存
let dom :any = null
// 需要添加动画节点的dom元素缓存
let table :any = null
onMounted(() => {
// 需要给改table加一个标识类,不然会选择到同一页面中的其他表格 class="scroll_table"
dom = document.querySelector(".scroll_table .el-table__body-wrapper")
table = document.querySelector('.scroll_table .el-table__body tbody')
// 添加监听事件
dom.addEventListener("scroll", (v) => {
const scrollDistance = dom.scrollHeight - dom.scrollTop - dom.clientHeight
if (scrollDistance <= 1) {
console.log('end...')
// 添加动画
setLoading()
}
})
})
</script>
<style scoped lang="scss">
.scroll-table {
.loading-row {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
line-height: 45px;
color: rgba(102, 109, 128, 1);
.is-loading {
margin-right: 2px;
}
}
::-webkit-scrollbar {
width: 6px;
height: 100%;
background-color: #FFF;
}
::-webkit-scrollbar-thumb {
width: 6px;
background-color: rgba(153, 153, 153, 1);
border-radius: 3px;
}
}
</style>
父组件中使用:
template:
<scroll-table
:table-configs="configs"
:table-data="tableData"
:is-loading="isLoading"
@load="getTableData"
></scroll-table>
scipt:
import ScrollTable from '../../components/ScrollTable.vue'
//表格配置
const configs = [
{
name: 'ID',
prop: 'id'
},
{
name: '名称',
prop: 'name'
},
{
name: '描述',
prop: 'event'
},
{
name: '成员',
prop: 'user'
},
{
name: '状态',
prop: 'status'
}
]
// 表格初始数据
const tableData = ref([
{
id: '1',
name: '22',
event: 'xx',
user: 'cet',
status: '启动'
},
{
id: '2',
name: '322',
event: '2xx',
user: 'cet343',
status: '关闭'
},
{
id: '2',
name: '322',
event: '2xx',
user: 'cet343',
status: '关闭'
},
{
id: '2',
name: '322',
event: '2xx',
user: 'cet343',
status: '关闭'
},
{
id: '2',
name: '322',
event: '2xx',
user: 'cet343',
status: '关闭'
},
{
id: '2',
name: '322',
event: '2xx',
user: 'cet343',
status: '关闭'
},
{
id: '2',
name: '322',
event: '2xx',
user: 'cet343',
status: '关闭'
},
{
id: '2',
name: '322',
event: '2xx',
user: 'cet343',
status: '关闭'
},
{
id: '2',
name: '322',
event: '2xx',
user: 'cet343',
status: '关闭'
},
{
id: '2',
name: '322',
event: '2xx',
user: 'cet343',
status: '关闭'
},
])
// 加载动画展示开关
const isLoading = ref(true)
// 接口请求
function getTableData() {
// 开启加载动画
isLoading.value = true
// 模拟接口请求
setTimeout(() => {
tableData.value.push( {
id: '3',
name: '322',
event: '2xx',
user: 'cet343',
status: '关闭'
})
// 关闭加载动画
isLoading.value = false
}, 2000)
}
样式以及加载动画可以自行修改