效果图:
在父组件使用表格组件
<table-list
ref="table"
:stripe="true"
:loading="loading"
:set-table-h="slotProps.setMainCardBodyH"
:table-data="tableData"
:columns="columns.tableList || []"
:radio="topicTypeListMapItem.radio"
show-opear
show-selection
:fix-width="240"
:page-data="pageData"
:disabled-select-key="topicTypeListMapItem.disabledSelectKey"
@changeSize="changeSize"
@changePage="changePage"
>
<!--预警灯-->
<template #warningLight="{ row }">
<div class="lightCireBlock">
<div>
<div v-for="(item,index) in row.warningLight" :key="'warningLight'+index">
<svg-icon v-if="Object.keys(columns.warningLight).includes(item)" :icon-class="columns.warningLight[item].icon" class="task-icon" />
<span
v-if="columns.warningLight[item] && columns.warningLight[item].hasNum && row[`warningLight${item}`] * 1 > 0"
class="fontBoxAll fontBox"
>{{ row[`warningLight${item}`] }}</span>
</div>
</div>
</div>
</template>
<!--标题-->
<template #taskTitle="{ row }">
<span class="td-cell lineCell" @click="editMainEvent(row)"> {{ row.taskTitle }}</span>
</template>
<!--状态-->
<template #status="{ row }">
<el-tag :disable-transitions="true" :class="columns.statusLists[row.status].className">{{ columns.statusLists[row.status].label }}</el-tag>
</template>
<!--操作-->
<template slot-scope="{ row }">
<div class="table-buttonList">
<template v-for="(item,index) in columns.btnList">
<el-button
v-if="item.isShow?item.isShow(row):true"
:key="index"
type="text"
@click="btnFn[item.clickFn.fn](row,...(item.clickFn.params?item.clickFn.params:[]))"
>{{ item.name }}</el-button>
</template>
</div>
</template>
</table-list>
然后用一个js文件来存储动态的参数
import { selectOverseerMeetingLeaType } from '@/api/common.js'
export const topicTypeListMap = new Map([
[
'keyWork',
{
name: '重点工作',
radio: false, // 表格数据是否单选
showImportBtn: true, // 是否显示批量导入按钮
showApprovalBtn: false, // 是否显示送立项按钮
showImportApprovalBtn: false, // 导入时是否显示送立项按钮
showItemTypeBtn: false, // 导入时是否显示选择事项类型按钮
showWorkRequireBtn: false, // 导入时是否显示批量填写工作要求按钮
showFoliCureBtn: false, // 是否显示送办理按钮
hasSubtasks: true, // 专题是否有子任务
isMeetingTopicType: false, // 是否是会议类专题
disabledSelectKey: [{ label: 'status', value: '0' }, { label: 'status', value: '1' }] // 表格哪些状态的数据禁选
}
],
[
'feasibleInstructions',
{
name: '督办件',
radio: false,
showImportBtn: true,
showApprovalBtn: true,
showImportApprovalBtn: true, // 导入时是否显示送立项按钮
showItemTypeBtn: true, // 导入时是否显示选择事项类型按钮
showWorkRequireBtn: false, // 导入时是否显示批量填写工作要求按钮
showFoliCureBtn: true,
hasSubtasks: false,
isMeetingTopicType: false,
disabledSelectKey: []
}
]
])
在组件中
props传的参数
props: {
// 表格数据
tableData: {
type: Array,
default: () => {
return []
}
},
// 表格字段
columns: {
type: Array,
default: () => {
return []
}
},
// 表格字段的宽度
fixWidth: {
type: Number,
default: 150
},
// 单选
radio: {
type: Boolean,
default: false
},
// 显示序号
showIndex: {
type: Boolean,
default: false
},
// 是否自定义高度
setTableH: {
type: Boolean,
default: true
},
// 显示勾选
showSelection: {
type: Boolean,
default: false
},
// 显示操作
showOpear: {
type: Boolean,
default: false
},
spanMethod: {
type: Function,
default() {
return () => {}
}
},
// 斑马线
stripe: {
type: Boolean,
default: false
},
// 分页
showPagination: {
type: Boolean,
default: true
},
setCheckbox: {
type: Boolean,
default: false
},
loading: { // 判断是否正在加载表格数据
type: Boolean,
default: false
},
align: {
type: String,
default: 'left'
},
recordCheck: { // 是否在翻页的时候记录之前选择的表格数据
type: Boolean,
default: true
},
// 分页数据
pageData: {
type: Object,
default: () => {
return {
page: 1,
rows: 15,
pageSize: [15, 25, 35],
total: 0
}
}
},
isImportData: { // 判断是否导入的数据
type: Boolean,
default: false
},
disabledSelectKey: { // 需要禁用的选择框的数据
type: Array,
default: () => {
return []
}
}
},
完整代码
<template>
<div ref="wrap" class="wrap">
<el-table
ref="table"
:key="'tableData'+tableData.length"
v-loading="loading"
element-loading-text="正在处理,请稍候!"
:data="tableData"
:stripe="stripe"
:span-method="spanMethod"
:header-row-class-name="headerRowClassName(radio)"
:height="setTableH?setTableHeight:'100%'"
class="table-list"
border
@sort-change="sortChange"
@selection-change="handleSelectionChange"
>
<template slot="empty">
<img src="@/assets/images/empty.png">
<p>暂无数据</p>
</template>
<!--选择框-->
<el-table-column v-if="showSelection" type="selection" width="42" :selectable="checkSelectable" />
<!--序号-->
<el-table-column v-if="showIndex" type="index" label="序号" width="55" align="center" />
<el-table-column
v-for="item in columns"
:key="item.lable"
:label="item.title"
:prop="item.slot || item.lable"
:min-width="item.minWidth || 140"
:width="item.width"
:sortable="item.sortable"
:show-overflow-tooltip="item.overflow"
:align="item.align"
>
<!-- 二级表头 -->
<el-table-column
v-for="subItem in item.columns"
:key="subItem.label"
:label="subItem.title"
:prop="subItem.slot || subItem.lable"
:min-width="subItem.minWidth || 140"
:width="subItem.width"
:sortable="subItem.sortable"
:show-overflow-tooltip="subItem.overflow"
:align="subItem.align"
>
<template slot="header">
<!-- <span v-if="item.isRequest" style="color:red">*</span> -->
<span> {{ subItem.title }}</span>
</template>
<template slot-scope="{ row }">
<slot :name="subItem.slot" :row="row">
<span class="td-cell"> {{ isImportData ? row[subItem.label].value : row[subItem.label] }}</span>
<template v-if="isImportData">
<el-popover
v-if="typeof(row[subItem.label].success) === 'boolean' && !row[subItem.label].success"
placement="top"
title=""
trigger="hover"
:content="row[subItem.label].message"
>
<i slot="reference" class="el-icon-warning" style="color:#F44336" />
</el-popover>
</template>
</slot>
</template>
</el-table-column>
<template slot="header">
<!-- <span v-if="item.isRequest" style="color:red">*</span> -->
<span> {{ item.title }}</span>
</template>
<template slot-scope="{ row }">
<slot :name="item.slot" :row="row">
<span class="td-cell">{{ isImportData ? row[item.label].value : row[item.label] }}</span>
<template v-if="isImportData">
<el-popover
v-if="typeof(row[item.label].success) === 'boolean' && !row[item.label].success"
placement="top"
title=""
trigger="hover"
:content="row[item.label].message"
>
<i slot="reference" class="el-icon-warning" style="color:#F44336" />
</el-popover>
</template>
</slot>
</template>
</el-table-column>
<!--操作按钮-->
<el-table-column v-if="showOpear" label="操作" prop="operation" :width="fixWidth" fixed="right">
<template #default="{ row, $index }">
<slot :row="row" :$index="$index" />
</template>
</el-table-column>
</el-table>
<el-pagination
v-if="showPagination && tableData.length>0"
background
:current-page="pageData.page"
:page-size="pageData.rows"
:page-sizes="pageData.pageSize"
layout="sizes, prev, pager, next, slot, jumper"
:total="pageData.total"
class="pagination"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
>
<span class="pagination-total">共{{ pageData.total }}条,</span>
</el-pagination>
</div>
</template>
<script>
import { clone } from 'lodash'
export default {
name: 'TableList',
props: {
// 表格数据
tableData: {
type: Array,
default: () => {
return []
}
},
// 表格字段
columns: {
type: Array,
default: () => {
return []
}
},
// 表格字段的宽度
fixWidth: {
type: Number,
default: 150
},
// 单选
radio: {
type: Boolean,
default: false
},
// 显示序号
showIndex: {
type: Boolean,
default: false
},
// 是否自定义高度
setTableH: {
type: Boolean,
default: true
},
// 显示勾选
showSelection: {
type: Boolean,
default: false
},
// 显示操作
showOpear: {
type: Boolean,
default: false
},
spanMethod: {
type: Function,
default() {
return () => {}
}
},
// 斑马线
stripe: {
type: Boolean,
default: false
},
// 分页
showPagination: {
type: Boolean,
default: true
},
setCheckbox: {
type: Boolean,
default: false
},
loading: { // 判断是否正在加载表格数据
type: Boolean,
default: false
},
align: {
type: String,
default: 'left'
},
recordCheck: { // 是否在翻页的时候记录之前选择的表格数据
type: Boolean,
default: true
},
// 分页数据
pageData: {
type: Object,
default: () => {
return {
page: 1,
rows: 15,
pageSize: [15, 25, 35],
total: 0
}
}
},
isImportData: { // 判断是否导入的数据
type: Boolean,
default: false
},
disabledSelectKey: { // 需要禁用的选择框的数据
type: Array,
default: () => {
return []
}
}
},
data() {
return {
saveMultipleSelection: {}, // 用来保存每个分页勾选的数据
multipleSelection: [],
setTableHeight: 300
}
},
computed: {
headerRowClassName() {
return (radio) => {
if (radio) {
return 'tableHead isRadio'
} else {
return 'tableHead'
}
}
}
},
watch: {
tableData: {
handler(newVal) {
this.$nextTick(() => {
if (this.multipleSelection && this.recordCheck) {
const idList = this.multipleSelection.map(item => { return item.businessKey })
newVal.forEach(item => {
if (idList.indexOf(item.businessKey) > -1) {
this.$refs.table.toggleRowSelection(item, true)
}
})
}
})
},
deep: true
},
saveMultipleSelection: {
handler(newVal) {
this.multipleSelection = Object.keys(newVal).reduce((prev, next) => {
prev = prev.concat(newVal[next])
return [...new Set(prev)]
}, [])
this.$EventBus.$emit('selection-change', this.multipleSelection)
},
deep: true
}
},
updated() {
this.$nextTick(() => {
this.setTableHeight = this.$refs.wrap.offsetHeight - document.querySelector('.el-table__header-wrapper').offsetHeight - 48 - 10
// 10是给分页腾一点空间
})
},
methods: {
handleSizeChange(e) {
this.$emit('changeSize', e)
},
handleCurrentChange(e) {
this.$emit('changePage', e)
},
clearSort() {
this.$refs.table.clearSort()
},
getSelection() {
return clone(this.$refs.table.selection)
},
sortChange(e) {
this.$emit('sortChange', e)
},
checkSelectable(row) {
let check = true
if (this.disabledSelectKey.length > 0) {
this.disabledSelectKey.forEach(item => {
if (row[item.label] === item.value) {
check = false
}
})
}
return check
},
handleSelectionChange(val) {
if (this.radio) { // 单选
// var newRows = val.filter((it, index) => {
// if (index === val.length - 1) {
// this.$refs.table.toggleRowSelection(it, true)
// return true
// } else {
// this.$refs.table.toggleRowSelection(it, false)
// return false
// }
// })
// this.saveMultipleSelection = newRows
this.$nextTick(() => {
var newRows = val.filter((it, index) => {
if (index === val.length - 1) {
this.$refs.table.toggleRowSelection(it, true)
return true
} else {
this.$refs.table.toggleRowSelection(it, false)
return false
}
})
if (val.length > 0) {
this.saveMultipleSelection = {}
this.$set(this.saveMultipleSelection, this.pageData.page, newRows)
}
// this.saveMultipleSelection = newRows
})
} else {
this.$nextTick(() => {
this.$set(this.saveMultipleSelection, this.pageData.page, val)
})
}
},
toggleRowSelection(row, bool) {
this.$refs.table.toggleRowSelection(row, bool)
}
}
}
</script>
<style scoped lang="scss">
.wrap{
height: 100%;
::v-deep .isRadio {
.el-checkbox{
display: none;
}
}
}
::v-deep .el-table ::-webkit-scrollbar{
height: 8px;
}
</style>