基于vue3 ts element plus table表格二次封装详细步骤
AI 概述
效果图1. Table 组件封装2. 页面引用基本表格带有分页的表格3.表格配置项4.参数类型声明参数介绍Table 属性Options 配置项Column 配置项paginationConfig 配置项Buttons 配置项Table 事件其他
最近公司项目中频繁会使用到 table 表格,而且前端技术这一块也用到了 vue3 和 TypeScript 来开发,所以基于...
目录
文章目录隐藏
最近公司项目中频繁会使用到 table 表格,而且前端技术这一块也用到了 vue3 和 TypeScript 来开发,所以基于 element plus table 做了一个二次封装的组件。
效果图

1. Table 组件封装
src/components/Table/index.vue
<template>
<div>
<el-table
:data="tableData"
v-bind="_options"
@selection-change="handleSelectionChange"
@row-click="handleRowClick"
@cell-click="handleCellClick">
<template v-for="(col, index) in columns" :key="index">
<!---复选框, 序号 (START)-->
<el-table-column
v-if="col.type === 'index' || col.type === 'selection' || col.type === 'expand'"
:align="col.align"
:label="col.label"
:type="col.type"
:index="indexMethod"
:width="col.width" />
<!---复选框, 序号 (END)-->
<!---图片 (START)-->
<el-table-column
v-else-if="col.type === 'image'"
:align="col.align"
:label="col.label"
:width="col.width">
<template #default="{ row }">
<!-- 如需更改图片 size,可自行配置参数 -->
<el-image
preview-teleported
:hide-on-click-modal="true"
:preview-src-list="[row[col.prop!]]"
:src="row[col.prop!]"
fit="cover"
style="width:40px;height:40px;border-radius:8px" />
</template>
</el-table-column>
<!---图片 (END)-->
<!-- 自定义 slot (START) -->
<el-table-column
:show-overflow-tooltip="col.showOverflowTooltip"
v-else-if="col.slot"
:align="col.align"
:label="col.label"
:width="col.width">
<template #default="scope">
<slot :name="col.slot" :row="scope.row" :index="scope.$index"></slot>
</template>
</el-table-column>
<!-- 自定义 slot (END) -->
<!-- 如果传递按钮数组,就展示按钮组 START-->
<el-table-column
v-else-if="col.buttons?.length"
:align="col.align"
:label="col.label"
:width="col.width">
<template #default="scope">
<el-button-group>
<el-button
v-for="(btn, index) in col.buttons"
size="small"
:key="index"
:type="btn.type"
@click="handleAction(btn.command, scope)"
>{{ btn.name }}</el-button
>
</el-button-group>
</template>
</el-table-column>
<!-- 如果传递按钮数组,就展示按钮组 END-->
<!-- 默认渲染列 (START) -->
<el-table-column
:show-overflow-tooltip="col.showOverflowTooltip"
v-else
:label="col.label"
:prop="col.prop"
:align="col.align"
:width="col.width" />
<!-- 默认渲染列 (END) -->
</template>
</el-table>
<!-- 分页器 -->
<div v-if="_options.showPagination" class="mt20">
<el-pagination
v-bind="_paginationConfig"
@size-change="pageSizeChange"
@current-change="currentPageChange" />
</div>
</div>
</template>
<script lang="ts" setup>
interface TableProps {
tableData: Array<object> // table 的数据
columns: Table.Column[] // 每列的配置项
options?: Table.Options
}
const props = defineProps<TableProps>()
// 设置 option 默认值,如果传入自定义的配置则合并 option 配置项
const _options: ComputedRef<Table.Options> = computed(() => {
const option = {
stripe: false,
tooltipEffect: 'dark',
showHeader: true,
showPagination: false,
rowStyle: () => 'cursor:pointer' // 行样式
}
return Object.assign(option, props?.options)
})
// 合并分页配置
const _paginationConfig = computed(() => {
const config = {
total: 0,
currentPage: 1,
pageSize: 10,
pageSizes: [10, 20, 30, 40, 50, 100],
layout: 'total, sizes, prev, pager, next, jumper'
}
return Object.assign(config, _options.value.paginationConfig)
})
interface EmitEvent {
(e: 'selection-change', params: any): void // 当选择项发生变化时会触发该事件
(e: 'row-click', row: any, column: any, event: Event): void // 当某一行被点击时会触发该事件
(e: 'cell-click', row: any, column: any, cell: any, event: Event): void // 当某个单元格被点击时会触发该事件
(e: 'command', command: Table.command, row: any): void // 按钮组事件
(e: 'size-change', pageSize: number): void // pageSize 事件
(e: 'current-change', currentPage: number): void // currentPage 按钮组事件
(e: 'pagination-change', currentPage: number, pageSize: number): void // currentPage 或者 pageSize 改变触发
}
const emit = defineEmits<EmitEvent>()
// 自定义索引
const indexMethod = (index: number) => {
const tabIndex = index + (_paginationConfig.value.currentPage - 1) * _paginationConfig.value.pageSize + 1
return tabIndex
}
// 切换 pageSize
const pageSizeChange = (pageSize: number) => {
emit('size-change', pageSize)
emit('pagination-change', 1, pageSize)
}
// 切换 currentPage
const currentPageChange = (currentPage: number) => {
emit('current-change', currentPage)
emit('pagination-change', currentPage, _paginationConfig.value.pageSize)
}
// 按钮组事件
const handleAction = (command: Table.command, scope: any) => {
emit('command', command, scope.row)
}
// 多选事件
const handleSelectionChange = (val: any) => {
emit('selection-change', val)
}
// 当某一行被点击时会触发该事件
const handleRowClick = (row: any, column: any, event: Event) => {
emit('row-click', row, column, event)
}
// 当某个单元格被点击时会触发该事件
const handleCellClick = (row: any, column: any, cell: any, event: Event) => {
emit('cell-click', row, column, cell, event)
}
</script>
<style lang="scss" scoped>
:deep(.el-image__inner) {
transition: all 0.3s;
cursor: pointer;
&:hover {
transform: scale(1.2);
}
}
</style>
2. 页面引用
src/views/table/index.vue
基本表格
<template>
<div class="p10">
<h1 class="mb-5 text-base text-gray-100 bg-red-400 p-2 border-4 border-green-400 rounded-md">
element-plus table 表格二次封装 <span class="ml-6">(此标题样式使用 Tailwind Css 生成)</span>
</h1>
<el-card class="box-card mb35">
<template #header>
<div class="card-header">
<span>基本表格</span>
</div>
</template>
<Table
:columns="tableColumn"
:table-data="tableData"
@selection-change="handleSelection"
@command="handleAction">
<template #date="{ row }">
<span> {{ row.date }}</span>
</template>
<!-- 如果不传入按钮组的数据就使用自定义插槽的方式 -->
<!-- <template #action="{ row, index }">
<div>
<el-button type="success">添加</el-button>
<el-button type="warning" @click="handleDelete(row, index)">删除</el-button>
</div>
</template> -->
</Table>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { tableColumn } from '@/config/table'
import { ElMessageBox, ElMessage } from 'element-plus'
// 本项目 Table 组件自动引入,如复制此代码,需引入 Table 组件后使用
interface User {
date: string
name: string
address: string
}
// 基本表格数据
const tableData: User[] = [
{
date: '2016-05-022016-05-022016-05-022016-05-022016-05-02',
name: '佘太君',
address: '上海市普陀区金沙江路 1516 弄'
},
{
date: '2016-05-04',
name: '王小虎',
address: '上海市普陀区金沙江路 1517 弄'
},
{
date: '2016-05-01',
name: '王小帅',
address: '上海市普陀区金沙江路 1519 弄'
},
{
date: '2016-05-03',
name: '王小呆',
address: '上海市普陀区金沙江路 1516 弄'
}
]
const handleSelection = (val: User[]) => {
console.log('父组件接收的多选数据', val)
}
const handleAction = (command: Table.command, row: User) => {
switch (command) {
case 'edit':
alert('点击了编辑')
break
case 'delete':
console.log('row', row)
ElMessageBox.confirm('确认删除吗?', '提示').then(() => {
ElMessage(JSON.stringify(row))
})
break
default:
break
}
}
</script>
<style lang="scss" scoped></style>
带有分页的表格
<template>
<div class="p-5">
<h1 class="mb-5 text-base text-gray-100 bg-red-400 p-2 border-4 border-green-400 rounded-md">
element-plus table 表格二次封装 <span class="ml-6">(此标题样式使用 Tailwind Css 生成)</span>
</h1>
<!-- 带有分页的表格 -->
<el-card class="box-card mb35">
<template #header>
<div class="card-header">
<span>带有分页的表格</span>
</div>
</template>
<Table
:columns="tableDemoColumn"
:table-data="tableDemoList"
:options="options"
@pagination-change="handlePaginationChange"
@command="handleAction">
<!-- 使用插槽格式化年龄 -->
<template #gender="{ row }">
<span> {{ row.gender ? '男' : '女' }}</span>
</template>
</Table>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { tableDemoColumn } from '@/config/table'
import { ElMessageBox, ElMessage } from 'element-plus'
import { getDemoList } from '@/service/api/table'
// 本项目 Table 组件自动引入,如复制此代码,需引入 Table 组件后使用
const route = useRoute()
const router = useRouter()
interface State {
tableDemoList: TableDemoItem[]
pageInfo: PageInfo
options: Table.Options
}
const state = reactive<State>({
pageInfo: {
currentPage: 1,
pageSize: 10,
total: 0
},
tableDemoList: [],
options: { showPagination: true, height: 600 }
})
const params: TableDemoParams = {
page: 1,
pageSize: 10
}
watch(
() => route.query,
async (newval) => {
const { page, pageSize } = newval
params.page = Number(page) || params.page
params.pageSize = Number(pageSize) || params.pageSize
const { items, pageInfo } = await getDemoList(params)
state.tableDemoList = items
state.options.paginationConfig = pageInfo
},
{ immediate: true }
)
// pageSize 或者 currentPage 改变触发
const handlePaginationChange = (page: number, pageSize: number) => {
router.push({
path: route.path,
query: {
page,
pageSize
}
})
}
// 这里的 UserInfo 类型是 api 声明文件里定义的类型, 请忽略
const handleAction = (command: Table.command, row: UserInfo) => {
switch (command) {
case 'edit':
alert('点击了编辑')
break
case 'delete':
console.log('row', row)
ElMessageBox.confirm('确认删除吗?', '提示').then(() => {
ElMessage(JSON.stringify(row.name))
})
break
default:
break
}
}
const { tableDemoList, options } = toRefs(state)
</script>
<style lang="scss" scoped></style>
3.表格配置项
src/config/table.ts 参数可参考下面参数介绍:
// 基本表格配置
export const tableColumn = [
{ type: 'selection', width: '50', label: 'No.' },
{ type: 'index', width: '50', label: 'No.' },
{ prop: 'name', label: '名字' },
{ prop: 'date', slot: 'date', label: '日期', showOverflowTooltip: true },
{ prop: 'address', label: '地址', showOverflowTooltip: true },
{
width: '120',
label: '操作',
buttons: [
{ name: '编辑', type: 'success', command: 'edit' },
{ name: '删除', type: 'danger', command: 'delete' }
]
}
] as Table.Column[]
// 带有分页的表格配置
export const tableDemoColumn = [
{ type: 'index', width: '65', label: 'No.', align: 'center' },
{ prop: 'avatar', type: 'image', label: '头像', width: '100', align: 'center' },
{ prop: 'name', label: '姓名', width: '100' },
{ prop: 'age', label: '年龄', width: '90', align: 'center' },
{ prop: 'gender', label: '性别', width: '90', slot: 'gender', align: 'center' },
{ prop: 'mobile', label: '手机号', width: '180' },
{ prop: 'email', label: '邮箱', showOverflowTooltip: true },
{ prop: 'address', label: '地址', showOverflowTooltip: true },
{
width: '120',
label: '操作',
buttons: [
{ name: '编辑', type: 'success', command: 'edit' },
{ name: '删除', type: 'danger', command: 'delete' }
]
}
] as Table.Column[]
4.参数类型声明
src/typings/table/index.d.ts 声明为全局的类型,方便使用。
// table 表格
declare namespace Table {
type Type = 'selection' | 'index' | 'expand' | 'image'
type Size = 'large' | 'default' | 'small'
type Align = 'center' | 'left' | 'right'
type command = string | number
interface ButtonItem {
name: string,
command: command,
type?: 'primary' | 'success' | 'warning' | 'danger' | 'info',
}
interface Column {
type?: Type,
// 对应列的类型。 如果设置了 selection 则显示多选框; 如果设置了 index 则显示该行的索引(从 1 开始计算); 如果设置了 expand 则显示为一个可展开的按钮
label: string,
prop?: string,
slot?: string
width?: string,
align?: Align,
showOverflowTooltip?: boolean,
buttons?: ButtonItem[],
}
interface Options {
height?: string | number,
// Table 的高度, 默认为自动高度。 如果 height 为 number 类型,单位 px;如果 height 为 string 类型,则这个高度会设置为 Table 的 style.height 的值,Table 的高度会受控于外部样式。
stripe?: boolean, // 是否为斑马纹 table
maxHeight?: string | number, // Table 的最大高度。 合法的值为数字或者单位为 px 的高度。
size?: Size // Table 的尺寸
showHeader?: boolean // 是否显示表头,
tooltipEffect?: 'dark' | 'light' // tooltip effect 属性
showPagination?: boolean, // 是否展示分页器
paginationConfig?: Pagination, // 分页器配置项,详情见下方 paginationConfig 属性,
rowStyle?: ({ row, rowIndex }) => stirng | object // 行的 style 的回调方法,也可以使用一个固定的 Object 为所有行设置一样的 Style。
}
interface Pagination {
total?: number, // 总条目数
currentPage: number, // 当前页数,支持 v-model 双向绑定
pageSize: number, // 每页显示条目个数,支持 v-model 双向绑定
pageSizes?: number[], // 每页显示个数选择器的选项设置
layout?: string, // 组件布局,子组件名用逗号分隔
background?: boolean // 是否为分页按钮添加背景色
}
}
参数介绍
Table 属性
| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
|---|---|---|---|---|
| tableData | table 表格的数据 | Array<object> | 是 | — |
| options | 自定义配置 | object | 否 | — |
| columns | 列 column 的配置数组 | object | 是 | — |
Options 配置项
| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
|---|---|---|---|---|
| height | Table 的高度, 默认为自动高度。 如果 height 为 number 类型,单位 px;如果 height 为 string 类型,则这个高度会设置为 Table 的 style.height 的值,Table 的高度会受控于外部样式。 | string / number | 否 | — |
| maxHeight | Table 的最大高度。 合法的值为数字或者单位为 px 的高度。 | string / number | 否 | — |
| size | 字段名称 对应列内容的字段名 | large / default /small | 否 | — |
| showHeader | 是否显示表头 | string | 否 | true |
| tooltipEffect | tooltip effect 属性 | dark / light | 否 | dark |
| stripe | 是否为斑马纹 table | boolean | 否 | false |
| showPagination | 是否展示分页器 | boolean | 否 | false |
| paginationConfig | 分页器配置项,详情见下方 paginationConfig 配置,(当 showPagination 为 true,该配置项必传) | Pagination | 否 | — |
| rowStyle | 行的 style 的回调方法,也可以使用一个固定的 Object 为所有行设置一样的 Style。 | function({ row, rowIndex }) / object | 否 | () => ‘cursor:pointer’ |
本项目中 rowStyle 需默认设置为 cursor:pointer,不需要可删除此默认选项
Column 配置项
| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
|---|---|---|---|---|
| type | 对应列的类型。 如果设置了 selection 则显示多选框; 如果设置了 index 则显示该行的索引(从 1 开始计算); 如果设置了 expand 则显示为一个可展开的按钮;如果设置 image,则显示图片。(Column 可以设置多个 type) | selection / index / expand / image | 否 | — |
| label | 每一列的标题 | string | 是 | — |
| prop | 字段名称 对应列内容的字段名 | string | 否 | — |
| slot | 插槽名称,自定义列的内容 作用域参数为 { row, $index } |
string | 否 | — |
| width | 对应列的宽度 | string / number | 否 | — |
| align | 对齐方式 | left / center / right | 否 | left |
| showOverflowTooltip | 当内容过长被隐藏时显示 tooltip | boolean | 否 | false |
| buttons | 按钮组的内容 | Array<object> | 否 | — |
paginationConfig 配置项
| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
|---|---|---|---|---|
| total | 总条目数 | number | 是 | 0 |
| currentPage | 当前页数,支持 v-model 双向绑定 | number | 是 | 1 |
| pageSize | 每页显示条目个数,支持 v-model 双向绑定 | number | 是 | 10 |
| pageSizes | 每页显示个数选择器的选项设置 | number[] | 否 | [10, 20, 30, 40, 50, 100] |
| layout | 组件布局,子组件名用逗号分隔 | string | 否 | ‘total, sizes, prev, pager, next, jumper’ |
| background | 是否为分页按钮添加背景色 | boolean | 否 | false |
Buttons 配置项
| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
|---|---|---|---|---|
| name | 按钮显示的名称 | string | 是 | — |
| command | 派发到 command 回调函数的指令参数 | string/number | 是 | — |
| type | 当前按钮的类型 | primary / success / warning / danger / info | 否 | — |
Table 事件
| 事件名 | 说明 | 回调参数 |
|---|---|---|
| selection-change | 当选择项发生变化时会触发该事件 | selection |
| row-click | 当某一行被点击时会触发该事件 | row, column, event |
| cell-click | 当某个单元格被点击时会触发该事件 | row, column, cell, event |
| command | 点击按钮组某个按钮触发的事件回调 | command, row |
| size-change | pageSize 改变时触发 | pageSize |
| current-change | currentPage 改变时触发 | currentPage |
| pagination-change | currentPage 或者 pageSize 改变时触发 | currentPage ,pageSize |
其他
此文档只提供基本的封装思路,如需使用到更多的业务场景,可自行扩展。如: 搜索,api 请求等
作者:zhfy 啊
原文:点击这里
以上关于基于vue3 ts element plus table表格二次封装详细步骤的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。
声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » 基于vue3 ts element plus table表格二次封装详细步骤
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » 基于vue3 ts element plus table表格二次封装详细步骤
微信
支付宝