element组件 表格滚动加载 + 加载图标

1,424 阅读1分钟

项目开发中需要用到一个具有滚动加载并出现加载动画的表格。

所以结合了element-plus中的表格组件进行了二次封装。

技术栈为 vue3 + ts + element-plus

思路

  1. 加载数据:对表格中的滚动条事件进行监听,到达条件则触发数据请求。
  2. 加载动画实现:使用克隆方法,触发数据请求的时候将克隆节点 append 到表格底部,请求结束的时候移除节点

效果图

image.png

代码实现

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)
}

样式以及加载动画可以自行修改