什么才是完美的表格二次封装elementPlus表格?-从零开始vue3+vite+ts+pinia+router4后台管理(7)
前言
ElementPlus 是一个优秀的组件库,后台管理表格页面多的话大家都想到表格的二次封装,封装的时候大家都想到el-table-column
每一列写成 "JSON
数组" 写法。然后用vue3的tsx 语法、h 函数 Render函数
去写一些自定义的东西,如下面的例子
html
<template>
<my-table :columns="columns" :data="tableData">
<el-table-column
v-for="(item, index) in columns"
:key="index">
</el-table-column>
</my-table>
</template>
<script setup>
const columns = [
{ prop: 'date', label: 'Date', width: '180'},
{ prop: 'name', label: 'Name'},
{width: '140', label: '操作',
render: ({ row, index }) =>
h('div', null, [
h(
ElButton,
{
type: 'primary',
size: 'small',
onClick: () => handleRenderEdit(row, index)
},
{ default: () => '编辑' }
),
h(
ElButton,
{
type: 'danger',
size: 'small',
onClick: () => handleRenderDelete(row, index)
},
{ default: () => '删除' }
)
])
}
]
// ...其他略
</script>
"JSON
数组" 封装的缺点
有上面的代码可以看出 el-table-column虽然少写了,但是json数组的代码并没有少写,上面的自定义列其实在ElementPlus上面直接写的话也就几行代码
html
<el-button size="small" @click="handleRenderEdit(scope.row)">编辑</el-button>
<el-button size="small" type="danger" @click="handleRenderDelete(scope.row)">删除</el-button>
我们尤其是复杂的管理页面 自定义的列也会越来越复杂,后面我们的代码也会越来越难维护,那么应该怎么办呢?
什么才是完美的二次表格封装
我的想法
- 有一个必须的前提:我们在二次封装elementPlus表格组件的时候不能影响原有组件的事件 ,方法 ,属性
- 在同一个项目里,
render
/jsx
/template
/tsx /h函数混用让人感到吃力。它的语法写法是否简单易学大部分就和elementPlus
一模一样 ?- 它是否确确实实减少了你的工作量?
- 它是否扩展性强,易维护?
下面是我封装二次封装elementPlus表格的一些做法,当然他肯定不是完美的二次表格封装,只是自己抛砖引玉的一些想法
1.table封装的目录结构
bash
├── src
│ ├── api
│ │ ├── table/index.ts # 表格列表数据获取接口请求
│ ├── components
│ │ └── table # 表格组件
│ │ └── tableType/index.ts # 表格配置和表格每一行的配置
│ │ └── spursTable/index.vue # 表格二次封装的主要业务代码
│ ├── views # 页面
│ │ ├── table
│ │ └── tableConfig/index.ts # 表格配置json
│ │ │ └── index.vue # 入口
2. 表格配置项
首先对表格进行一些配置 如 是否显示索引列,是否显示全选列, 是否显示分页,是否有子数据,等等 有其他需要的配置都可以自己去扩展添加
components/table/tableType/index.ts
typescript
//表格行el-table-column配置项
export interface ColumnOption {
prop?: string
label: string
minWidth?: string
slotName?: string
align?: string
}
//表格配置项
export interface TableOption {
propList: ColumnOption[]
showIndexColumn?: boolean
showSelectColumn?: boolean
showPagination?: boolean
childrenProps?: object
}
3. 表格配置json
views/tablet/tableConfig/index.ts
这个是表格每一行配置的数据prop,label,等等 无论你怎么样封装表格 这个每一页的数据都是不同的这个是必不可少的 注意的是我们给自定义列配置了一个slotName, 怎么用 继续看下面
typescript
//表格配置json
import {TableOption} from "@/components/table/tableType";
export const contentTableConfig: TableOption = {
// 表格配置
propList: [
{ prop: 'nickName', label: '姓名', minWidth: '100', align: 'left' },
{ prop: 'roleName', label: '权限名称', minWidth: '100', align: 'left' },
{ prop: 'userMoney.balanceMoney', label: '用户余额', minWidth: '100', align: 'left' },//获取表格list下一级的数据userMoney.balanceMoney
{ prop: 'title', label: '介绍', minWidth: '100', align: 'left' },
{ prop: 'phone', label: '联系方式', minWidth: '100', align: 'left' },
{ prop: 'address', label: '地址', minWidth: '100', align: 'left' },
{ prop: 'createTime', label: '日期', minWidth: '100', align: 'left' },
{
prop: 'state',
label: '状态',
minWidth: '100',
slotName: 'state',
align: 'left'
},
{
label: '操作',
minWidth: '120',
slotName: 'handler',
align: 'left'
}
],
// 表格具有序号列
showIndexColumn: true,
// 表格具有可选列
showSelectColumn: true,
//是否显示分页
showPagination:true
}
4.使用表格配置json-tableConfig
import {contentTableConfig} from './tableConfig/index'// 引入配置
然后通过v-bind="contentTableConfig"
去绑定,不是特自定义的不用管, 有自定义列的通过slotName去在template写插槽比如操作列<template #handler="scope">
,就和elementPlus一样
html
<template>
<div class="app-container">
<div class="filter-container">
<el-input class="w-100" v-model="queryForm.keyword" placeholder="关键字搜索" />
<el-button type="primary" :icon="Search" @click="handleSearch">搜索</el-button>
<el-button class="green-button" @click="refreshTableInfo" :icon="Plus">刷新</el-button>
</div>
<div class="table-con">
<spurs-table
ref="spursTable"
v-bind="contentTableConfig"
:queryForm="queryForm"
:requestApi="getTableList"
@selection-change="handleSelectionChange"
>
<template #state="scope">
<el-tag type="warning" v-if="scope.row.state === 0">禁用</el-tag>
<el-tag v-else>启用</el-tag>
</template>
<template #handler="scope">
<el-button
size="small"
:icon="Edit"
link
@click="handleEditClick(scope.row)"
>编辑</el-button
>
<el-button
size="small"
:icon="Delete"
type="warning"
link
@click="deleteBtnClick(scope.row)"
>删除</el-button>
</template>
</spurs-table>
</div>
</div>
</template>
<script setup lang="ts">
import {reactive,ref} from 'vue'
import { Search,Plus,Delete,Edit } from '@element-plus/icons-vue'
import {contentTableConfig} from './tableConfig/index'// 引入配置
import SpursTable from '@/components/table/spursTable/index.vue'
import tableApi from '@/api/table'
const queryForm = reactive({
keyword: ''
})
const getTableList = (params: any) => {
return tableApi.packTableList(params);
};
const spursTable = ref <any> ()
const multipleSelection = ref<any>([])
const handleSelectionChange = (val:[]) => {
multipleSelection.value = val
console.log(multipleSelection.value);
}
const handleSearch = () => {
// 搜索
spursTable.value.handleSearch();
// console.log(spursTable.value.tableData);
// spursTable.value.tableData[0].nickName = "测试"
}
const refreshTableInfo = () => {
// 刷新把queryForm置空
queryForm.keyword="";
spursTable.value.refreshTableInfo();
}
const handleEditClick = (row: any) => {
console.log('点击了编辑按钮,数据为:', row)
// getTableData();
}
// 点击删除按钮触发事件
const deleteBtnClick = (row: any) => {
console.log('点击了删除按钮,数据为:', row)
}
</script>
5.SpursTable封装
直接贴代码 下面是一些说明
html
<template>
<el-table
ref="tableRef"
style="width: 100%"
v-loading="loading"
:data="tableData"
border
v-bind="$attrs"
>
<!-- 1.传入showSelectColumn时展示的全选列 -->
<template v-if="showSelectColumn">
<el-table-column type="selection" />
</template>
<!-- 2.传入showIndexColumn时展示的序号列 -->
<template v-if="showIndexColumn">
<el-table-column type="index" label="#" />
</template>
<!-- 3.propList里面的所有列 -->
<template v-for="item in propList" :key="item.prop">
<el-table-column v-bind="item" show-overflow-tooltip>
<!-- 传有slotName时展示的插槽列 -->
<template #default="scope" v-if="item.slotName">
<slot :name="item.slotName" :row="scope.row"></slot>
</template>
</el-table-column>
</template>
</el-table>
<Pagination
v-if="showPagination"
v-model:page="pagination.pageNum"
v-model:size="pagination.pageSize"
:total="total"
@pagination="getTableData"
/>
</template>
<script setup lang="ts">
import {withDefaults, defineExpose, ref} from 'vue'
import {ColumnOption} from "@/components/table/tableType";
import {useTable} from '@/hooks/useTable'
interface Props {
requestApi: Function // 请求表格数据的 api的axios方法 ==> 必传
queryForm?:any
propList: ColumnOption[] //表格行el-table-column配置项
showIndexColumn?: boolean //是否显示索引列
showSelectColumn?: boolean //是否显示全选列
showPagination?: boolean //是否显示分页
childrenProps?: object // 是否有子数据,树形数据才用得到
}
const props = withDefaults(defineProps<Props>(), {
showIndexColumn: false,
showSelectColumn: false,
showPagination: false,
childrenProps: () => ({})
})
const {
tableData,
pagination,
total,
loading,
getTableData,
handleSearch,//搜索
refreshTableInfo,//刷新
} = useTable(props.requestApi,props.queryForm)
const tableRef = ref <any> ()
defineExpose({
elementP: tableRef,
tableData,
handleSearch,
refreshTableInfo
})
// console.log(props);
</script>
<style scoped></style>
hooks/useTable 这里面的hook函数是获取表格数据和分页的 这里不做多的说明可以看
从零开始vue3+vite+ts+pinia+router4后台管理(5)-二次封装表格1.0
使用原生elementPlus表格的所有属性,方法,事件等,可以用属性透传**
v-bind="$attrs"
**来实现 vue官方文档透传 Attributes,也可以阅读文章vue3中的$attrs的使用来了解和使用它
比如我们要使用@selection-change="handleSelectionChange"
elementPlue表格获取勾选数据的方法
使用方法:
使用SpursTable的事件方法和数据怎么办?使用d**
efineExpose
** 把SpursTable需要的东西暴露出去
html
defineExpose({
tableData,
handleSearch,
refreshTableInfo
})
使用的时候
html
ref="spursTable"//模板上使用
const spursTable = ref <any> ()
spursTable.value.handleSearch();
spursTable.value.tableData
6.后话
关于二次表格的封装,什么才是完美的二次表格封装 如果你有更好的想法欢迎评论区交流