介绍: 这是基于 vue3 + el-table 封装的通用表格组件 的 模板写法,想要参考tsx写法的可以到我另一篇博客喔~
【通用表格组件】vue3 + element-ui + tsx 实现通用表格组件
这里通用表格,和上一篇通用表单一样的(表格组件都在我博客里),配置完全可控,然后每个el-table-column 都是通过传入的数组来循环便利渲染,大部分常用实现也写在了下面,无法具体实现或需要你自己自定义开发的,都可以通过render来开发。
1、父组件调用方式
<CommonTable
show-index
show-check-box
key-
:loading="loading"
:max-height="390"
:table-label="tableHeaderData"
:row-class-name="tableSortRowClassName"
:data="tableData"
:option="tableOptionsData"
@operation="operationHandler"
@handle-selection-change="handleSelectionChange"
/>
export const tableHeaderData = [
{
label: '部门名称/用户名称',
prop: 'name',
},
{
label: '部门负责人',
prop: 'contactPerson',
},
]
export const tableOptionsData = {
label: '操作',
width: '300',
fixed: 'right',
children: [
{
label: '查看制作详情',
icon: 'el-icon-view',
methods: 'view',
permission: 'xxx',
render(row) {
return !== 0
}
}
]
}
2、组件源码
<template>
<div v-loading="loading">
<el-table
ref="table"
v-bind="{ ...props }"
:data="data"
style="width: 100%"
:row-class-name="tabRowClassName"
@select="handleSelectionChange"
:row-style="rowStyle"
@select-all="handleSelectionChange"
:cell-class-name="cellClassName"
header-row-class-name="custom-table-header"
:default-expand-all="expendAll"
row-key="id"
:max-height="maxHeight"
>
<!-- 单选框 -->
<el-table-column
v-if="showCheckBoX"
width="55"
type="selection"
:reserve-selection="keep"
:class-name="turnRadio ? `checkBoxRadio` : ``"
align="center"
></el-table-column>
<!-- 序号 -->
<el-table-column v-if="showTypeIndex" align="center" label="序号" width="50">
<template #default="{ $index }">{{ $index + 1 }}</template>
</el-table-column>
<!-- 表格 -->
<el-table-column
v-for="item in ((item) => )"
:width=" ? : ''"
:key="item[keyId]"
:align="!! ? : 'center'"
:label=""
:show-overflow-tooltip="overflowText"
:fixed=""
:prop=""
>
<template #default="{ row }">
<template v-if="">
<div>
<el-image class="table-img" :src="" :preview-src-list="[]">
<template #error>
<span>无</span>
</template>
</el-image>
</div>
</template>
<template v-else-if="">
<div style="cursor: pointer" @click="!! && handleClickon(, row)" v-html="(row)"></div>
</template>
<template v-else-if="">
<div v-if="typeof === 'function'">{{ (row[], row, item) }}</div>
</template>
<template v-else>
<div class="text-no-wrap" @click="!! && handleClickon(, row)">
{{ () == '[object Array]' ? propFilter(, row) : row[] }}
</div>
</template>
</template>
</el-table-column>
<el-table-column v-if="!!option" :width="" :label="" :fixed="" align="center">
<template #default="scope" v-if="!!">
<!-- 常规正常情况 -->
<el-button
type="text"
v-for="(item, index) in "
:key="index"
v-show="!buttonHidden(item, )"
v-bind="{ ... }"
:disabled="buttonDisabled(item, )"
@click="handleTableButton(, , index, scope.$index)"
:class="['btn-' + , 'btn-right']"
size="mini"
>
<template v-if="">
<div v-html="()"></div>
</template>
{{ !! ? : '' }}
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script lang="ts">
import { defineComponent, computed, PropType, ref, onMounted, nextTick, reactive, watch } from 'vue'
import { useStore } from 'store/index'
import { Utils } from '@/utils'
import { isNumber } from '@/utils/is'
interface PageInfo {
pageIndex: number
totalCount: number
pageSize: number
pageArr?: []
[ket: string]: any
}
interface ColumnI {
label: string
prop: string
width?: string | number
}
interface TableOption {
label: string
methods: string
props: object
style: object
render: Function
}
interface TableOptions {
label: string
width: string | number
fixed: boolean
popoverWidth?: number | string
children: Array<TableOption>
}
interface Props {
maxHeight: string | number
stateArr: Array<any>
tableLabel: Array<ColumnI>
option: TableOption
showCheckBoX: boolean
showTypeIndex: boolean
turnRadio: boolean
selectedIdArr: Array<any>
pageInfo: PageInfo
showPagination: boolean
overflowText: boolean
loading: boolean
keep: boolean
keyId: string
data: Array<any>
expendAll: boolean
}
export default defineComponent({
name: 'CommonTable',
props: {
maxHeight: {
type: [String, Number],
default: 600
},
stateArr: {
type: Array,
default: () => {
return []
}
},
tableLabel: {
// 表格展示
type: Array as PropType<Array<ColumnI>>,
default: () => {
return []
}
},
data: {
// 数据源
type: Array as PropType<Array<any>>,
default: () => {
return []
}
},
option: {
// 配置需要显示的操作菜单
type: Object as PropType<TableOption>
},
showCheckBoX: {
// 配置是否显示全选(复选框)
type: Boolean,
default: false
},
showTypeIndex: {
type: Boolean,
default: false
},
turnRadio: {
type: Boolean,
default: false
},
selectedIdArr: {
type: Array,
default: []
},
pageInfo: {
// 配置分页
type: Object,
default: () => {
return {
pageIndex: 1,
totalCount: 0,
pageSize: 10,
pageArr: []
}
}
},
showPagination: {
// 是否隐藏 分页显示
type: Boolean,
default: true
},
overflowText: {
// 是否 隐藏文字过长
type: Boolean,
default: false
},
loading: {
// loading 配置
type: Boolean,
default: false
},
keep: {
type: Boolean,
default: false
},
keyId: {
// 动态绑定 key 值
type: String,
default: 'id'
},
props: {
// 表格参数配置
type: Object,
default: function () {
return {
'show-header': true, // 显示表头e
'highlight-current-row': false, // 是否要高亮当前行
'tooltip-effect': 'dark', //
'max-height': 'auto', // Table 的最大高度。合法的值为数字或者单位为 px 的高度。
'empty-text': '没有数据', // 空数据显示状态
'element-loading-text': '加载中', // loading 加载
'header-cell-style': {
background: '#F2F4F7',
color: '#333',
fontSize: '13px'
}, // 表头样式
border: false, // 是否带有纵向边框
fit: true, // 列的宽度是否自撑开
stripe: false // 是否显示斑马纹
}
}
},
rowStyle: {
type: Object,
default: () => {
return {
height: '40px'
}
}
},
expendAll: {
type: Boolean,
default: false
},
/**
* 行内自定义class
*/
rowClassName: {
type: Function,
default: () => {
return () => {}
}
}
},
setup(props: any, { emit }) {
watch(
() => ,
() => {
if ( || ) {
nextTick(() => {
()
= []
if ( && ) {
((item: any) => {
if ( == [0]) {
(item, true)
}
})
} else if () {
((item: any) => {
if (()) {
(item, true)
()
}
})
}
})
}
},
{
deep: true,
immediate: true
}
)
watch(
() => ,
(val) => {
if ( || ) {
nextTick(() => {
()
= []
if ( && ) {
((item: any) => {
if ( == val[0]) {
(item, true)
}
})
} else if () {
((item: any) => {
if (()) {
(item, true)
()
}
})
}
})
}
},
{
deep: true
}
)
/**
* prop 单值 或者 数组过滤(此处为针对时间组,不作为通用处理)
*/
const propFilter = (prop: [Array<any> | object], row: any) => {
let res = ((total: string, cur: any) => {
if (row[cur]) {
return (total += row[cur] + '~')
} else {
return ''
}
}, '')
// (res)
return res ? (/~$/, '') : ''
}
const handleTableButton = (methods: any, row: object, index: number, rowIndex: number) => {
// 按钮事件
emit('handleTableButton', { methods, row, index, rowIndex })
}
const handleClickon = (methods: any, row: object) => {
if (typeof methods !== 'string') throw '方法名错误'
// 数据操作
emit(methods, { methods, row })
}
const curPageCheck = ref<Array<any>>([])
const handleSelectionChange = (val: Array<any>) => {
let arr = ((item) => parseInt())
let compare = (, arr)
if ( && ) {
// 选择项大于1时
if ( > 1) {
let del_row = ()
(del_row, false)
}
}
// 全选
if ( && ) {
if () {
emit('handleSelectionChange', val)
} else {
emit('handleSelectionChange', val, compare)
}
} else {
emit('handleSelectionChange', val)
}
}
const getList = (pages: any) => {
let { page, limit } = pages
if (!isNumber(page)) {
page =
}
if (!isNumber(limit)) {
limit =
}
const pageInfo = {
page,
limit
}
emit('handleGetList', pageInfo)
}
const getRowKeys = (row: any) => {
return
}
const table = ref<any>(null)
const selectAll = (val: any) => {
if ( && ) {
// 选择项大于1时
if ( > 1) {
= 1
}
}
emit('handleSelectionChange', val)
}
//斑马纹表格背景色
const tabRowClassName = ({ row, rowIndex }: any) => {
let index = rowIndex + 1
if (index % 2 == 0) {
return 'even-row'
} else {
return 'odd-row'
}
return ''
}
const cellClassName = ({ row, column, rowIndex, columnIndex }: any) => {
if ( === 2 && columnIndex < (props as any).) {
return 'height_light_cell'
} else {
return ''
}
}
const buttonHidden = (item: any, row?: any) => {
if (typeof === 'function') return (row) || false
if (!) return
}
const buttonDisabled = (item: any, row?: any) => {
if (typeof === 'function') return (row) || false
if (!) return
}
const store = useStore()
const buttonType = computed(() => )
const showVertical = ref<boolean>(false)
/**
* 单选框选中事件
*/
const rowClick = (row: any): void => {
emit('rowClick', row)
}
const radioId = ref<number | string>(-1)
return {
propFilter,
handleTableButton,
handleClickon,
handleSelectionChange,
getList,
getRowKeys,
tabRowClassName,
cellClassName,
buttonHidden,
buttonDisabled,
buttonType,
showVertical,
table,
radioId,
rowClick,
selectAll
}
}
})
</script>
<style lang="scss" scoped>
::v-deep .el-table__header,
::v-deep .el-table__body {
margin: 0;
}
.scrollBar {
@include scrollBar;
}
::v-deep .el-table::before {
height: 0;
}
::v-deep .el-button {
padding: 0;
border: none;
margin: 0 4px;
padding: 0 4px 0 8px;
border-left: 1px solid #e2e2e2;
font-size: 14px;
min-height: 14px;
&:first-child {
border-left: none;
}
}
::v-deep .el-button + .el-button {
margin-left: 0;
}
.btn-see {
color: #39a6ff;
}
.btn-handel {
color: #41a4bd;
}
.btn-edit {
color: #fcb0fb;
}
.btn-delete,
.btn-del {
color: #fd9090;
}
.btn-revoke {
color: #ffa913;
}
::v-deep .btn-right div {
margin-right: 5px;
}
.btn-right div:empty {
margin-right: 0px;
}
//斑马纹表格背景色
::v-deep(.el-table) .even-row {
--el-table-tr-background-color: #f5fafb;
}
::v-deep(.el-table) .odd-row {
--el-table-tr-background-color: #ffffff;
}
.el-table--border::after,
.el-table--group::after {
width: 0;
}
::v-deep .el-table td,
-leaf {
border: none;
}
::v-deep .el-table__fixed-right::before,
.el-table__fixed::before {
background-color: transparent;
}
::v-deep .custom-table-header {
th {
background-color: #62c4ee !important;
color: #fff !important;
}
}
.progress-line {
.el-progress-bar__outer {
height: 16px !important;
}
.el-progress-bar__outer,
.el-progress-bar__inner {
border-radius: 0 !important;
}
}
.text-no-wrap {
@include text-no-wrap;
cursor: pointer;
display: inline;
}
::v-deep(.el-table) {
-table__cell div,
-table__cell > .cell {
font-size: 14px;
}
-table__cell > .cell {
font-weight: normal;
}
.cell {
padding: 0 10px;
line-height: 39px;
}
.el-table__header-wrapper .checkBoxRadio .el-checkbox {
display: none;
}
.el-checkbox {
display: flex;
align-items: center;
justify-content: center;
}
.table-img {
width: 60px;
height: 60px;
object-fit: cover;
padding: 6px 0;
display: flex;
align-items: center;
margin: 0 auto;
justify-content: center;
}
}
::v-deep(.el-table--small .el-table__cell) {
padding: 0;
}
::v-deep(.el-dropdown-menu__item) {
padding: 0 !important;
.el-button {
width: 100%;
text-align: center;
padding: 0 8px;
margin: 0;
}
}
</style>