vue3 + element-plus + hooks实现完整的带分页的表格组件

前言

很早之前写了一篇《vue + element-ui二次封装Table组件,实现表格内容的完全自定义》的文章,这篇文章仅仅阐述了vue2基于element-ui如何实现一个通用化的表格,而没有写如何更优美的去获取表格数据以及分页。
由于项目一直使用react,最近才有项目使用到vue3,那借着这个机会,给大家分享一下实现一个完整的带分页的表格的二次封装。

用到的第三方库

由于我在react的项目中习惯于用ahooks的第三方库,所以vue3项目中也找到一个实现方式及用法类似的库v-e-hooks-plus,在实现useTable hook的方法里会用到其提供的useRequest(http请求的方法)。
vue-hooks-plus: https://inhiblab-core.gitee.io/docs/hooks/en/useRequest/plugins/fetchsing/index.html

表格组件封装

<script setup>
const props = defineProps({
    columns: Array,
    tableData: Array,
    pagination: {
        type: Boolean,
        default: true,
    }, // 是否需要分页
    showCheckBox: Boolean, // 是否展示多选框
    loading: Boolean,
    paginationData: {
        type: Object,
        default: () => {
            return {
                current: 1,
                total: 0
            }
        }
    }
});
const emits = defineEmits(["handleCurrentChange", "selectRows"]);
// 改变页数
const handleCurrentChange = (value) => {
    emits('handleCurrentChange', value)
};
// 多选
const handleSelectionChange = (value) => {
    emits("selectRows", value);
};
</script>
<template>
    <div class="table-container">
        <el-table :data="tableData" v-loading="loading" scrollbar-always-on flexible element-loading-text="加载中..." size="large" style="width: 100%"
            @selection-change="handleSelectionChange">
            <el-table-column type="selection" width="55" v-if="showCheckBox" />
            <template v-for="column in columns" :key="column.prop">
                <el-table-column :prop="column.prop" :label="column.title" :width="column.width" min-width="160" :fixed="column.fixed">
                    <template #default="scope">
                        <slot v-if="column.slot" :name="column.prop" :row="scope.row" />
                        <span v-else>{{ scope.row[column.prop] ?? "-" }}</span>
                    </template>
                </el-table-column>
            </template>
        </el-table>
        <div class="pagination-container" v-if="pagination && tableData?.length > 0">
            <el-pagination background layout="total, prev, pager, next" :page-sizes="[10, 20, 30, 40]"
                :current-page="paginationData.current" :total="paginationData.total" @current-change="handleCurrentChange" />
        </div>
    </div>
</template>

表格组件的使用

<script setup>
import * as api from "@/services/index";
import Search from "./components/search.vue";
import { useTable } from '@/hooks/useTable'
const columns = [
    { prop: "action", title: "操作", width: 120, slot: true },
    { prop: "column1", title: "列名1" },
    { prop: "column2", title: "列名2" },
    { prop: "column3", title: "列名3" },
    { prop: "column4", title: "列名4" },
    { prop: "column5", title: "列名5"},
];
const { searchParam, handleCurrentChange, tableData, pagination, search, reset, loading } = useTable(api.getApnList)
// 搜索
const handleSearch = (params) => {
    searchParam.value = params;
    search()
};
</script>
<template>
    <div>
        <Search @handleSearch="handleSearch" :reset="reset" />
        <div class="searchTable-wrap">
            <c-table :columns="columns" :tableData="tableData" :loading="loading" :paginationData="pagination"
                @handleCurrentChange="(value) => handleCurrentChange(value)">
                <template #action="record">
                        <a>删除</a>
                </template>
            </c-table>
        </div>
    </div>
</template>

useTable hook方法实现

import {
    reactive,
    onMounted,
    computed,
    toRefs
} from 'vue'
import {
    useRequest
} from 'vue-hooks-plus'
/**
 * @description table 页面表格操作方法封装
 * @param {Function} api 获取表格数据 api 方法(必传)
 * @param {Object} initParam 获取数据初始化参数(非必传,默认为{})
 * @param {Boolean} isPageable 是否有分页(非必传,默认为true)
 * @param {Boolean} manual 是否手动触发(非必传,默认为false)
 * @param {Function} dataCallBack 对后台返回的数据进行处理的方法(非必传)
 * */
export const useTable = (api, initParam = {}, isPageable = true, manual = false, dataCallBack) => {

    /**
     * 1.获取表格数据的方法getTableData
     * 2.查询条件变量searchParam
     * 3.分页查询条件变量pagination
     * 4.分页pageSize变更的方法handleSizeChange()
     * 5.分页currentPage变更的方法handleCurrentChange()
     * 6.查询方法search()
     * 7.初始化参数的变量searchInitParam
     */
    const state = reactive({
        // 表格数据
        tableData: [],
        // 分页数据
        pagination: {
            // 当前页数
            current: 1,
            // 每页显示条数
            pageSize: 10,
            // 总条数
            total: 0
        },
        // 查询参数(只包括查询)
        searchParam: {},
        // 初始化默认的查询参数
        searchInitParam: {},
        // 总参数(包含分页和查询参数)
        totalParam: {},
        // 是否加载中
        loading: false,
        // 接口返回的所有内容
        responseData: null
    });
    const pageParam = computed({
        get: () => {
            return {
                current: state.pagination.current,
                pageSize: state.pagination.pageSize
            };
        }
    });
    onMounted(() => {
        !manual && reset();
    });
    // 调用接口获取表格数据
    const getData = useRequest(api, {
        manual: true
    })
    const getTableData = async() => {
        try {
            //合并查询参数
            Object.assign(state.totalParam, isPageable ? pageParam.value : {}, initParam)
            state.loading = true
            let data = await getData.runAsync(state.totalParam);
            state.responseData = data;
            const {
                current,
                pageSize,
                total,
                list
            } = data; // 接口返回内容的数据格式
            //回调处理,此处可以满足对特殊数据的处理
            dataCallBack && (data = dataCallBack(data));
            //根据是否分页,进行赋值
            state.tableData = isPageable ? list : data;
            //对分页参数进行更新
            isPageable && updatePagination({
                current,
                pageSize,
                total
            });
        } catch (err) {

        } finally {
            state.loading = false
        }
    }
    const updatePagination = (resPageable) => {
        Object.assign(state.pagination, resPageable);
    };
    const updatedTotalParam = () => {
        state.totalParam = {};
        // 处理查询参数,可以给查询参数加自定义前缀操作
        let nowSearchParam = {};
        // 防止手动清空输入框携带参数(这里可以自定义查询参数前缀)
        for (let key in state.searchParam) {
            // * 某些情况下参数为 false/0 也应该携带参数
            if (state.searchParam[key] || state.searchParam[key] === false || state.searchParam[key] === 0) {
                nowSearchParam[key] = state.searchParam[key];
            }
        }
        Object.assign(state.totalParam, nowSearchParam, isPageable ? pageParam.value : {});
    };

    const search = () => {
        state.pagination.current = 1;
        updatedTotalParam();
        getTableData();
    };

    const reset = () => {
        state.searchParam = {};
        // 重置搜索表单的时,如果有默认搜索参数,则重置默认的搜索参数
        Object.keys(state.searchInitParam).forEach(key => {
            state.searchParam[key] = state.searchInitParam[key];
        });
        search()
    };

    const handleSizeChange = (val) => {
        state.pagination.current = 1;
        state.pagination.pageSize = val;
        getTableData();
    };

    const handleCurrentChange = (val) => {
        state.pagination.current = val;
        getTableData();
    };

    return {
        ...toRefs(state),
        getTableData,
        handleCurrentChange,
        handleSizeChange,
        search,
        reset
    }


}

题外话

本人空闲时间做了一个计算房贷的微信小程序,有需求,有兴趣的可以扫码看看

在这里插入图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值