前言 ?
在公司经常接触到后台管理系统的开发,基本上 90% 以上都是 table 页面,业务逻辑也基本上一样。刚开始也想的是找一个基于 element 二次封装的 el-table,在搜寻过程中也接触了很多优秀的项目,其中包括:(vxe-table、avue……)但是这些项目终归没有自己开发灵活,所有就诞生了我的 Pro-Table 组件 ???
? 请注意:以下内容只代表我个人封装思想,如果你觉得还不错,请帮我点个小小的 star,如果你有更好的想法,请在评论区留言,蟹蟹 ??
一、在线预览 ?
Link:http://admin.spicyboy.cn ✨
二、Git 仓库地址 (欢迎 Star⭐⭐⭐)
Gitee:https://gitee.com/laramie/Geeker-Admin ✨
GitHub:https://github.com/HalseySpicy/Geeker-Admin ✨
三、Pro-Table 功能 ???
表格内容自适应屏幕宽高,溢出内容表格内部滚动。
表格数据操作 Hooks (单条数据删除、批量删除、重置密码、状态切换……)
表格数据多选 Hooks (支持现跨页勾选数据)
表格序号累加排序
表格列排序、单元格内容格式化(根据字典自动格式化)
树形表格展示(后期会增加懒加载)
表格数据导入组件、导出钩子函数
表格查询(可携带初始参数)、重置功能的封装
表格分页模块封装(Pagination)
表格数据刷新、列显隐、搜索显隐设置
四、需求分析 ?
首先我们来看效果图(总共可以分为五个区域):
1、表格搜索区域
2、表格数据操作按钮区域
3、表格功能按钮区域
4、表格主体内容展示区域
5、表格分页区域
1、搜索区域分析:
可以看到搜索区域的字段都是存在于表格当中的,并且每个页面的搜索、重置方法都是一样的逻辑,只是不同的查询参数而已。我们完全可以在传表格配置项 columns 时,直接指定某个字段的 search:true 就能把该项变为搜索项,然后使用 SearchType 字段可以指定搜索框的类型,最后把表格的搜索方法都封装成 Hooks 钩子函数。页面上完全就不会存在搜索逻辑了。
2、表格数据操作按钮区域分析:
表格数据操作按钮基本上每个页面都会不一样,所以我们直接使用 作用域插槽 来完成每个页面的数据操作按钮区域,作用域插槽 可以将表格多选数据信息从 Pro-Table 的 Hooks 多选钩子函数中传到页面上使用。
3、表格功能按钮区域分析:
这块区域没什么特殊功能,只有三个按钮,其功能分别为:表格数据刷新(一直会携带当前查询和分页条件)、表格列显隐设置、表格搜索区域显隐(方便展示更多的数据信息)。 可通过 toolButton 属性控制这块区域的显隐。
4、表格主体内容展示区域分析:
这块区域主要就是数据展示,配置 columns 项传到 Pro-Table 组件中就行了。使用作用域插槽可以自定义每一列的显示自己需要的内容,还支持表格数据多选(内部已封装了多选 Hooks 钩子函数)。
5、表格分页区域分析:
分页也没有什么特殊的功能,该支持的都支持了。 ??
五、Pro-Table 文档
1、Pro-Table 属性配置:
2、ColumnProps 属性配置(都是可选参数):
六、代码实现?(详情去项目里查看,这里只贴了一部分代码)
1、Pro-table 组件:
Template:
<template><div class="table-box"><!-- 查询表单 --><SearchForm:search="search":reset="reset":searchParam="searchParam":columns="searchColumns"v-show="isShowSearch"</SearchForm><!-- 表格头部 操作按钮 --><div class="table-header"><div class="header-button-lf"><slot name="tableHeader" :ids="selectedListIds" :isSelected="isSelected"></slot></div><div class="header-button-ri" v-if="toolButton"><el-button :icon="Refresh" circle @click="getTableList"> </el-button><el-button :icon="Operation" circle @click="openColSetting"> </el-button><el-button :icon="Search" circle v-if="searchColumns.length" @click="isShowSearch = !isShowSearch"> </el-button></div></div><!-- 表格主体 --><el-tableheight="575"ref="tableRef":data="tableData":border="border"@selection-change="selectionChange":row-key="getRowKeys":stripe="stripe":tree-props="{ children: childrenName }"<template v-for="item in tableColumns" :key="item"><!-- selection || index --><el-table-columnv-if="item.type == 'selection' || item.type == 'index'":type="item.type":reserve-selection="item.type == 'selection'":label="item.label":width="item.width":fixed="item.fixed"</el-table-column><!-- expand(展开查看详情,请使用作用域插槽) --><el-table-columnv-if="item.type == 'expand'":type="item.type":label="item.label":width="item.width":fixed="item.fixed"v-slot="scope"<slot :name="item.type" :row="scope.row"></slot></el-table-column><!-- other --><el-table-columnv-if="item.prop && !item.type && item.isShow":prop="item.prop":label="item.label":width="item.width":sortable="item.sortable":show-overflow-tooltip="true":resizable="true":fixed="item.fixed"v-slot="scope"<!-- 自定义配置每一列 slot(作用域插槽) --><slot :name="item.prop" :row="scope.row"><!-- 图片(自带预览) --><el-imagev-if="item.image":src="scope.row[item.prop!]":preview-src-list="[scope.row[item.prop!]]"fit="cover"class="table-image"preview-teleported/><!-- tag 标签(自带格式化) --><el-tag v-else-if="item.tag" :type="filterEnum(scope.row[item.prop!],item.enum,'tag')">{{ item.enum?.length ? filterEnum(scope.row[item.prop!],item.enum): defaultFormat(0,0,scope.row[item.prop!]) }}</el-tag><!-- 文字(自带格式化) --><span v-else>{{ item.enum?.length ? filterEnum(scope.row[item.prop!],item.enum): defaultFormat(0,0,scope.row[item.prop!]) }}</span></slot></el-table-column></template><template #empty><div class="table-empty"><img src="@/assets/images/notData.png" alt="notData" /><div>暂无数据</div></div></template></el-table><!-- 分页 --><Paginationv-if="pagination":pageable="pageable":handleSizeChange="handleSizeChange":handleCurrentChange="handleCurrentChange"</Pagination><!-- 列设置 --><ColSetting v-if="toolButton" ref="colRef" :tableRef="tableRef" :colSetting="colSetting"></ColSetting></div></template>
Script:
<script setup lang="ts" name="proTable">import { ref, onMounted } from "vue";import { useTable } from "@/hooks/useTable";import { useSelection } from "@/hooks/useSelection";import { Refresh, Operation, Search } from "@element-plus/icons-vue";import { ColumnProps } from "@/components/ProTable/interface";import { filterEnum, defaultFormat } from "@/utils/util";import SearchForm from "@/components/SearchForm/index.vue";import Pagination from "@/components/Pagination/index.vue";import ColSetting from "./components/ColSetting.vue";
const tableRef = ref();
// 是否显示搜索模块 const isShowSearch = ref(true);
interface ProTableProps { columns: Partial[]; // 列配置项 requestApi: (params: any) => Promise; // 请求表格数据的api ==> 必传 pagination?: boolean; // 是否需要分页组件 ==> 非必传(默认为true) initParam?: any; // 初始化请求参数 ==> 非必传(默认为{}) border?: boolean; // 表格是否显示边框 ==> 非必传(默认为true) stripe?: boolean; // 是否带斑马纹表格 ==> 非必传(默认为false) toolButton?: boolean; // 是否显示表格功能按钮 ==> 非必传(默认为true) childrenName?: string; // 当数据存在 children 时,指定 children key 名字 ==> 非必传(默认为"children") }
// 接受父组件参数,配置默认值 const props = withDefaults(defineProps(), { columns: () => [], pagination: true, initParam: {}, border: true, stripe: false, toolButton: true, childrenName: "children" });
// 表格多选 Hooks const { selectionChange, getRowKeys, selectedListIds, isSelected } = useSelection();
// 表格操作 Hooks const { tableData, pageable, searchParam, initSearchParam, getTableList, search, reset, handleSizeChange, handleCurrentChange } = useTable(props.requestApi, props.initParam, props.pagination);
// 表格列配置项处理(添加 isShow 属性,控制显示/隐藏) const tableColumns = ref<Partial[]>(); tableColumns.value = props.columns.map(item => { return { ...item, isShow: true }; });
// 过滤需要搜索的配置项 const searchColumns = props.columns.filter(item => item.search);
// 设置搜索表单的默认值 searchColumns.forEach(column => { if (column.initSearchParam !== undefined && column.initSearchParam !== null) { initSearchParam.value[column.prop!] = column.initSearchParam; searchParam.value[column.prop!] = column.initSearchParam; } });
// 列设置 const colRef = ref(); // 过滤掉不需要设置显隐的列 const colSetting = tableColumns.value.filter((item: Partial) => { return item.type !== "selection" && item.type !== "index" && item.type !== "expand"; }); const openColSetting = () => { colRef.value.openColSetting(); };
// 获取表格数据 onMounted(() => { getTableList(); });
// 暴露给父组件的参数和方法 defineExpose({ searchParam, refresh: getTableList });
````.
原文:https://juejin.cn/post/7094890833064755208