vue3封装table组件 element Plus

本文详细介绍了Vue.js组件`scTable`的代码结构和功能实现,包括数据加载、分页、排序、过滤等特性。组件支持远程数据源,可以动态调整高度,并提供了刷新、分页改变等操作。此外,还展示了如何处理空数据及自定义列模板。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

<template>
  <div class="scTable" ref="scTableMain" :style="{ height: theight }" v-loading="loading">
    <div :class="!hideDo ? 'scTable-table' : 'scTable-table-noPag'">
      <el-table
        v-bind="$attrs"
        :data="tableData"
        :row-key="rowKey"
        :key="toggleIndex"
        ref="scTable"
        height="100%"
        @sort-change="sortChange"
        @filter-change="filterChange"
      >
        <slot></slot>
        <template v-for="(item, index) in userColumn" :key="index">
          <el-table-column
            v-if="!item.hide"
            :column-key="item.prop"
            :label="item.label"
            :prop="item.prop"
            :width="item.width"
            :sortable="item.sortable"
            :fixed="item.fixed"
            :filters="item.filters"
            :filter-method="remoteFilter || !item.filters ? null : filterHandler"
          >
            <template #default="scope">
              <slot :name="item.prop" v-bind="scope">
                {{ scope.row[item.prop] }}
              </slot>
            </template>
          </el-table-column>
        </template>
        <el-table-column min-width="1"></el-table-column>
        <template #empty>
          <el-empty :description="emptyText" :image-size="80"></el-empty>
        </template>
      </el-table>
    </div>
    <div class="scTable-page" v-if="!hidePagination && !hideDo">
      <div class="scTable-pagination">
        <el-pagination
          v-if="!hidePagination"
          background
          :small="true"
          :layout="paginationLayout"
          :total="total"
          :page-size="pageSize"
          :pager-count="pagerCount"
          v-model:currentPage="currentPage"
          @current-change="paginationChange"
        ></el-pagination>
      </div>
      <div class="scTable-do" v-if="!hiderefresh">
        <el-button
          @click="refresh"
          icon="el-icon-refresh"
          circle
          style="margin-left: 15px"
        ></el-button>
      </div>
    </div>
  </div>
</template>

<script>
import { reactive, watch, onMounted, ref, toRefs, getCurrentInstance, computed } from 'vue'
import config from '@/config/table'

export default {
  name: 'scTable',
  props: {
    tableName: { type: String, default: '' },
    apiObj: { type: Object, default: () => {} },
    params: { type: Object, default: () => ({}) },
    data: { type: Object, default: () => {} },
    height: { type: [String, Number], default: '100%' },
    rowKey: { type: String, default: '' },
    column: { type: Object, default: () => {} },
    hideDo: { type: Boolean, default: false }, // 是否显示分页
    pagerCount: { type: Number, default: 7 }, // 是否显示分页
    hiderefresh: { type: Boolean, default: false }, // 是否显示刷新
    remoteSort: { type: Boolean, default: false },
    remoteFilter: { type: Boolean, default: false },
    hidePagination: { type: Boolean, default: false },
    paginationLayout: { type: String, default: 'total, prev, pager, next, jumper' }
  },

  setup(props) {
    const { proxy } = getCurrentInstance()
    const scTable = ref(null)
    const scTableMain = ref(null)
    const state = reactive({
      emptyText: '暂无数据',
      toggleIndex: 0,
      tableData: [],
      pageSize: config.pageSize,
      total: 0,
      currentPage: 1,
      prop: null,
      order: null,
      loading: false,
      tableHeight: '100%',
      tableParams: props.params,
      userColumn: []
    })
    // 计算高度
    const theight = computed(() => {
      return Number(props.height) ? `${Number(props.height)}px` : props.height
    })
    // 监听响应式对象
    watch(
      () => [props.data, props.apiObj],
      ([newdataValue, newapiObjValue], [olddataValue, oldapiObjValue]) => {
        // 判断是不是newdataValue发生的变化
        if (olddataValue !== newdataValue) {
          if (props.data != undefined) {
            state.tableData = props.data
            state.total = state.tableData.length
          }
        }
        // 判断是不是oldapiObjValue发生的变化
        if (oldapiObjValue !== newapiObjValue) {
          state.tableParams = props.params
          refresh()
        }
      }
    )
    onMounted(() => {
      state.userColumn = props.column

      // 判断是否静态数据
      if (props.apiObj) {
        getData()
      } else if (props.data) {
        state.tableData = props.data
        state.total = state.tableData.length
      }
    })

    // 获取数据
    const getData = async () => {
      state.loading = true

      const reqData = {
        [config.request.page]: state.currentPage,
        [config.request.pageSize]: state.pageSize
      }
      if (props.hidePagination) {
        delete reqData[config.request.page]
        delete reqData[config.request.pageSize]
      }
      Object.assign(reqData, state.tableParams)
      try {
        var res = await props.apiObj.get(reqData)
      } catch (error) {
        console.log(error)
        state.loading = false
        state.emptyText = error.statusText
        return false
      }
      if (res.code != 200) {
        state.loading = false
        state.emptyText = res.msg
      } else {
        state.emptyText = '暂无数据'
        if (res.data) {
          state.tableData = res.data.list
          state.total = res.data.total
        } else {
          state.tableData = []
          state.total = 0
        }
        state.loading = false
      }
      scTable.value.$el.querySelector('.el-table__body-wrapper').scrollTop = 0
    }
    // 分页点击
    const paginationChange = () => {
      getData()
    }
    // 刷新数据
    const refresh = () => {
      scTable.value.clearSelection()
      getData()
    }
    // 更新数据 合并上一次params
    const upData = (params, page = 1) => {
      state.currentPage = page
      scTable.value.clearSelection()
      Object.assign(state.tableParams, params || {})
      getData()
    }
    // 清空数据data
    const clearData = () => {
      state.tableData = []
    }
    // 重载数据 替换params
    const reload = (params, page = 1) => {
      state.currentPage = page
      state.tableParams = params || {}
      scTable.value.clearSelection()
      scTable.value.clearSort()
      scTable.value.clearFilter()
      getData()
    }
    // 排序事件
    const sortChange = (obj) => {
      if (!props.remoteSort) {
        return false
      }
      if (obj.column && obj.prop) {
        state.prop = obj.prop
        state.order = obj.order
      } else {
        state.prop = null
        state.order = null
      }
      getData()
    }
    // 本地过滤
    const filterHandler = (value, row, column) => {
      const property = column.property
      return row[property] === value
    }
    // 过滤事件
    const filterChange = (filters) => {
      if (!props.remoteFilter) {
        return false
      }
      Object.keys(filters).forEach((key) => {
        filters[key] = filters[key].join(',')
      })
      upData(filters)
    }
    // 原生方法转发
    const clearSelection = () => {
      scTable.value.clearSelection()
    }
    const toggleRowSelection = (row, selected) => {
      scTable.value.toggleRowSelection(row, selected)
    }
    const toggleAllSelection = () => {
      scTable.value.toggleAllSelection()
    }
    const toggleRowExpansion = (row, expanded) => {
      scTable.value.toggleRowExpansion(row, expanded)
    }
    const setCurrentRow = (row) => {
      scTable.value.setCurrentRow(row)
    }
    const clearSort = () => {
      scTable.value.clearSort()
    }
    const clearFilter = (columnKey) => {
      scTable.value.clearFilter(columnKey)
    }
    const doLayout = () => {
      scTable.value.doLayout()
    }
    const sort = (prop, order) => {
      scTable.value.sort(prop, order)
    }
    return {
      ...toRefs(state),
      scTable,
      scTableMain,
      getData,
      paginationChange,
      refresh,
      upData,
      reload,
      sortChange,
      filterHandler,
      filterChange,
      clearSelection,
      toggleRowSelection,
      toggleAllSelection,
      toggleRowExpansion,
      setCurrentRow,
      clearSort,
      clearFilter,
      doLayout,
      sort,
      clearData,
      theight
    }
  }
}
</script>

<style scoped>
.scTable {
}
.scTable-table {
  height: calc(100% - 50px);
}
.scTable-table-noPag {
  height: 100%;
}
.scTable-page {
  height: 50px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 15px;
}
.scTable-do {
  white-space: nowrap;
}
</style>

### 实现 Vue3Element Plus 的前端分页 在 Vue3 中使用 Element Plus 的 `el-table` 组件实现前端分页,可以通过组合 `el-pagination` 来完成。以下是具体的实现方式: #### 数据准备 为了展示表格数据并支持分页操作,需要定义以下变量: - **tableData**: 存储完整的表格数据。 - **currentPage**: 当前页面索引,默认为第一页。 - **pageSize**: 每页显示的数据条数。 通过计算属性来筛选当前页码对应的数据集[^1]。 ```javascript <script setup> import { ref, computed } from &#39;vue&#39;; const tableData = ref([ { id: 1, name: &#39;张三&#39;, age: 20 }, { id: 2, name: &#39;李四&#39;, age: 25 }, { id: 3, name: &#39;王五&#39;, age: 30 }, { id: 4, name: &#39;赵六&#39;, age: 35 } ]); const currentPage = ref(1); const pageSize = ref(2); // 计算当前页的数据 const paginatedTableData = computed(() => { const start = (currentPage.value - 1) * pageSize.value; const end = start + pageSize.value; return tableData.value.slice(start, end); }); </script> ``` #### 页面布局 利用 `el-table` 显示数据,并结合 `el-pagination` 控制分页逻辑。 ```html <template> <div> <!-- 表格 --> <el-table :data="paginatedTableData"> <el-table-column prop="id" label="ID"></el-table-column> <el-table-column prop="name" label="姓名"></el-table-column> <el-table-column prop="age" label="年龄"></el-table-column> </el-table> <!-- 分页 --> <el-pagination layout="prev, pager, next" :total="tableData.length" :page-size="pageSize" v-model:current-page="currentPage" ></el-pagination> </div> </template> ``` 上述代码实现了基础的前端分页功能。当用户切换到不同的页码时,会自动更新表格中的数据显示范围。 --- ### 跨页勾选的功能扩展 如果希望实现跨页保存已勾选的状态,则需引入额外的状态管理机制。可以创建一个对象用于存储每一条记录的勾选状态。 ```javascript <script setup> import { reactive, toRefs } from &#39;vue&#39;; const state = reactive({ selectedRowsMap: new Map(), // 存储被勾选的行 ID }); function handleSelectionChange(selection, row) { if (selection.includes(row)) { state.selectedRowsMap.set(row.id, true); // 添加至 map } else { state.selectedRowsMap.delete(row.id); // 移除自 map } } defineExpose({ ...toRefs(state), handleSelectionChange }); </script> ``` 随后修改模板部分以绑定事件监听器和初始选择状态: ```html <el-table :data="paginatedTableData" @selection-change="(val, row) => handleSelectionChange(val, row)" > <el-table-column type="selection" width="55"> <template #default="{ row }"> <el-checkbox v-if="state.selectedRowsMap.has(row.id)" // 如果已被标记则预设为选中 checked /> </template> </el-table-column> ... </el-table> ``` 此设计允许即使跳转不同页面也能保留之前的选择结果。 --- ### 封装成可重用组件 对于更复杂的场景,建议将以上逻辑封装为独立组件以便于维护与复用[^2]。例如提供统一接口接受外部传入参数如总数据源、字段映射关系等。 ```javascript export default defineComponent({ props: [&#39;tableData&#39;], emits: [], data() { return {}; }, methods: {}, }); ``` 最终形成高度定制化的解决方案满足业务需求[^3]。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值