Vue3+elementUI:强大好用的el-tabel+el-pagination封装,后台管理系统必备
应用场景:
在编写后台管理系统时,常常会用到表格和翻页器,每次都需要重复的写很麻烦,并且普通表格只需要渲染对应的prop就可以了,完全可以用一个v-for判断去解决,因此一个自带翻页器的table组件就应运而生了
实现功能:
(带有新增的是和Vue2版本不同的)
- 在渲染表格时,只需要传递列表配置即可无脑渲染,无需疯狂cv然后改一些鸡毛蒜皮的小东西
- 根据字段判断是否自定义插槽,使用具名插槽去单独编译
- 分页器和翻页的方法都绑定到同一个方法,父组件接收一个渲染列表的方法即可
- emptyText字段可以自动填写当内容为空时的默认值(新增)
- 这里的分页器是单独封装了一个组件用于不需要表单的情况(选用了ruoyi的组件),如果没有对应的应用场景可以去掉(新增)
- 提供了size可以自定义修改组件样式大小等(新增)
- 可自动渲染序号(从1开始),并且自定义序号列的宽度(新增)
- 可修改行样式和单元格样式(新增)
- 高度可修改(新增)
- 新增多选和回写功能(新增)
实现代码
1. 分页器组件pagination的封装
<template>
<div :class="{ 'hidden': hidden }" class="pagination-container">
<el-pagination
:background="background"
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:layout="layout"
:page-sizes="pageSizes"
:pager-count="pagerCount"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</template>
<script setup>
import {scrollTo} from '@/utils/scroll-to'
const props = defineProps({
total: {
required: true,
type: Number
},
page: {
type: Number,
default: 1
},
limit: {
type: Number,
default: 10
},
pageSizes: {
type: Array,
default() {
return [5, 10, 20, 30, 50]
}
},
// 移动端页码按钮的数量端默认值5
pagerCount: {
type: Number,
default: document.body.clientWidth < 992 ? 5 : 7
},
layout: {
type: String,
default: 'total, sizes, prev, pager, next, jumper'
},
background: {
type: Boolean,
default: true
},
autoScroll: {
type: Boolean,
default: true
},
hidden: {
type: Boolean,
default: false
}
})
const emit = defineEmits();
const currentPage = computed({
get() {
return props.page
},
set(val) {
emit('update:page', val)
}
})
const pageSize = computed({
get() {
return props.limit
},
set(val) {
emit('update:limit', val)
}
})
function handleSizeChange(val) {
if (currentPage.value * val > props.total) {
currentPage.value = 1
}
emit('update:limit', val)
emit('pagination', {page: currentPage.value, limit: val})
if (props.autoScroll) {
scrollTo(0, 800)
}
}
function handleCurrentChange(val) {
emit('update:page', val)
emit('pagination', {page: val, limit: pageSize.value})
if (props.autoScroll) {
scrollTo(0, 800)
}
}
</script>
<style scoped>
.pagination-container {
background: #fff;
padding: 32px 16px;
}
.pagination-container.hidden {
display: none;
}
</style>
2.Table组件的封装
HTML部分
<template>
<div class="my-table" :class="`my-table--${size}`">
<el-table
:ref="tableRef || 'filterTable'"
:row-class-name="tableRowClassName"
v-loading="loading"
:data="tableData"
:border="border"
:cell-style="cellStyle"
:height="height"
:max-height="maxHeight"
tooltip-effect="dark"
@selection-change="selectionChange"
@sort-change="sortChange"
>
<el-table-column v-if="selection" type="selection" width="45" fixed align="center" :selectable="checkSelect">
</el-table-column>
<el-table-column v-if="showIndex" label="序号" :width="indexWidth" type="index" align="center">
<template #default="scope">
<span>{{ (currentPage - 1) * pageSize + scope.$index + 1 }}</span>
</template>
</el-table-column>
<template v-for="(item, index) in columns">
<!-- solt 自定义列-->
<!--
自定义列的使用方法:
在使用插槽时#后跟slotType
<template #createTime="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
-->
<template v-if="item.type === 'slot'">
<el-table-column
:class-name="item.class"
:key="index"
:width="item.width"
:min-width="item.minWidth"
:prop="item.prop"
:label="item.label"
:align="item.align ? item.align : 'center'"
:fixed="item.fixed ? item.fixed : false"
>
<template #default="scope">
<slot :name="item.slotType" :row="scope.row" :index="scope.$index" :column="scope.column"/>
</template>
</el-table-column>
</template>
<!--普通表格-->
<template v-else>
<el-table-column
:show-overflow-tooltip="item.showOverflowTooltip === undefined ? true : item.showOverflowTooltip"
v-if="item.visible !== false"
:key="index"
:sortable="item.sortable"
:prop="item.prop"
:label="item.label"
:formatter="item.formatter || ((row)=>formatEmpty(row,item))"
:width="item.width"
:min-width="item.minWidth"
:align="item.align ? item.align : 'center'"
:fixed="item.fixed ? item.fixed : false"
></el-table-column>
</template>
</template>
</el-table>
<pagination
v-if="showPagination"
v-show="total > 0"
:total="total"
:autoScroll="autoScroll"
:pagerCount="pagerCount"
v-model:page="currentPage"
v-model:limit="pageSize"
:page-sizes="pageSizes"
@pagination="handlePagination"
/>
</div>
</template>
js部分(setup语法糖)
<script setup>
const props = defineProps({
// 表单数据
tableData: {
type: Array,
default() {
return []
}
},
// 表单大小
size: {
type: String,
default: 'normal'
},
// 表单ref
tableRef: {
type: String,
default: ''
},
// 数据列表配置
columns: {
type: Array,
default() {
return []
}
},
// 是否开启翻页时自动回到顶部
autoScroll: {
type: Boolean,
default: true
},
// 序号的宽度
indexWidth: {
type: Number,
default: 80
},
// 行样式
tableRowClassName: {
type: Function,
default() {
return () => {
}
}
},
// 移动端页码按钮的数量端默认值5
pagerCount: {
type: Number,
default: document.body.clientWidth < 992 ? 5 : 7
},
// 分页
pageSizes: {
type: Array,
default() {
return [10, 20, 30, 50]
}
},
// 边框
border: {type: Boolean, default: false},
// 高度
height: {type: [Number, String], default: null},
// 最大高度
maxHeight: {type: [Number, String], default: null},
// 加载状态
loading: {type: Boolean, default: false},
// 是否多选
selection: {type: Boolean, default: false},
// 单元格的 style 的回调方法
cellStyle: {
type: Function,
default() {
return () => {
}
}
},
// 是否展示翻页组件
showPagination: {type: Boolean, default: true},
// 是否展示序号
showIndex: {type: Boolean, default: false},
// 总数
total: {
type: Number,
default: 20
},
// pagination的page
page: {
type: Number,
default: 1
},
// pagination的limit
limit: {
type: Number,
default: 10
},
// 查询是否能选中的数据
checkData: {
type: Array,
default() {
return []
}
}
})
const emit = defineEmits();
const currentPage = computed({
get() {
return props.page
},
set(val) {
emit('update:page', val)
}
})
const pageSize = computed({
get() {
return props.limit
},
set(val) {
emit('update:limit', val)
}
})
const formatEmpty = (row, item) => {
return row[item.prop] || row[item.prop] === 0 ? row[item.prop] : (item.emptyText || '')
}
function checkSelect(row) {
let list = props.checkData
let isSelect = true
for (let i of list) {
if (row.id === i.id) {
isSelect = false
return
}
}
return isSelect
}
function selectionChange(selection) {
emit('selectionChange', selection)
}
function sortChange(filters) {
emit('sortChange', filters)
}
function handlePagination(data) {
if (data.page !== currentPage.value || data.limit !== props.limit) {
emit('update:page', data.page)
emit('update:limit', data.limit)
emit('pagination', data)
}
}
</script>
使用方法
示例
使用序号
<hc-table :tableData="activityList" :columns="columns" :total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
show-index
:loading="loading"
@pagination="getList"
>
<template #activityTime="scope">
{{ scope.row.startTime }} - {{ scope.row.endTime }}
</template>
....
</template>
</hc-table>
其中activityList为列表数据,total,page,limit和正常使用分页器一样传值,show-index为展示序号,loading为加载状态,getList为翻页方法,columns数据结构如下
// 主要列信息
const columns = [
// 默认展示prop中的值
{prop: 'name', label: '活动名称'},
// 自定义列 展示slotType插槽中的值
{type: 'slot', slotType: 'activityTime', label: '活动时间', width: '165'},
...
]
使用默认值
当对应字段中没有值(null,'',undefined)的时候,默认展示emptyText中的内容
// 主要列信息
const columns = [
{prop: 'time', label: '成团时间',emptyText:'--'},
...
]
转载自:https://juejin.cn/post/7278244755834077240