前言
开发过程中,我们总会遇见各种各样的表格,既有行合并,又有列表合并,又有自定义表头,并且表头,表格需要自定义颜色、字体...
最难的是,表格还可能是动态的,那这些要如何实现?
现在就通过,一个实例,来实现上面这个表格
实现
shell
npm init vite-app vue3-table
进入文件夹中
shell
cd vue3-table
安装下依赖
shell
npm install
启动
shell
npm run dev
表格如果使用 table tr td
慢慢画,太麻烦了
我们引用 Element Plus
shell
npm install element-plus --save
main.js
引入下 Element Plus
javascript
import { createApp } from 'vue'
import App from './App.vue'
import './index.css'
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
const app = createApp(App);
app.use(ElementPlus);
app.mount("#app");
修改下 App.vue
,使用 el-table
来构建表格
js
<!--
@description: 复杂表格实现
-->
<template>
<div style="padding: 20px;">
<el-table :data="tableData" border>
<el-table-column prop="arg1" width="100" align="center" />
<el-table-column prop="arg2" width="100" align="center" />
<el-table-column v-for="(item, index) in tableHead" :key="index" :label="item" :prop="item" align="center">
<template #default="scope">
<div>{{ scope.row[item] }}</div>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script setup lang='ts'>
/* ------------------------ 导入 与 引用 ----------------------------------- */
import { ref } from 'vue'
const tableHead = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
const tableData = ref([
{
arg1: '目标',
arg2: '季度目标',
'1月': 1,
'2月': 2,
'3月': 3,
'4月': 4,
'5月': 5,
'6月': 6,
'7月': 7,
'8月': 8,
'9月': 9,
'10月': 10,
'11月': 11,
'12月': 12
},
{
arg1: '目标',
arg2: '月度目标',
'1月': 1,
'2月': 2,
'3月': 3,
'4月': 4,
'5月': 5,
'6月': 6,
'7月': 7,
'8月': 8,
'9月': 9,
'10月': 10,
'11月': 11,
'12月': 12
},
{
arg1: '总销售额',
},
])
</script>
为什么要这么写呢?我们月份可能是不定顺序的,不一定今年1月到12月,也有可能是从是从2月开始到下一年1月,也有可能只需要显示6个月份就够了呢
所以我们采用动态生成的方式
接下来,我们来实现自定义表头
可以采用嵌套 el-table-column
,加入表头插槽的方式来修改表头内容
剩下的就是合并表格,前面的目标都是一样的,我们可以合并起来(列表合并),总销售额是不是应该和月份/参数一样宽啊,那也得合并(行合并)
还有季度目标,一个是季度不就 3个月吗,那也得合并
我们使用 span-method
对表格进行合并
javascript
// 合并处理
const objectSpanMethod = ({ row, column, rowIndex, columnIndex }) => {
// 第一行,第一二列
if (columnIndex === 0 && rowIndex < 2) {
// 第一二列单元格行合并
if (rowIndex === 0) {
return {
rowspan: 2,
colspan: 1
}
} else { // 其他这个范围内的单元格隐藏
return {
rowspan: 0,
colspan: 0
}
}
}
// 第一列
if (rowIndex === 0) {
// 从第二列开始,每隔3列合并
if (columnIndex > 1 && (columnIndex - 2) % 3 === 0) {
return {
rowspan: 1,
colspan: 3
}
} else if (columnIndex > 1) { // 从第二列开始,其他不合并的都隐藏
return {
rowspan: 0,
colspan: 0
}
}
}
// 第二列
if (rowIndex === 2) {
// 第一行,2列合并
if (columnIndex === 0) {
return [1, 2]
} else if (columnIndex === 3) { // 第三列合并为 12列
return [1, 12]
} else {
return [0, 0] // 其他这个范围内的单元格隐藏
}
}
}
其中 rowIndex
表示当前列数,columnIndex
表示当前行数
return [列数,行数]
,return [2, 1]
行合并两个
return [0, 0]
表示隐藏单元格
那销售额要怎么弄呢,单元格是合并了,那统计数据怎么弄呢
我们可以这样改
scope.$index
对应行数,上面的意思是第三行的话就显示为统计值 72
最后的最后,我们来改下,单元格颜色,当然修改其他样式也是可以的
javascript
// 单元格样式处理
// 表格内容
const tableCellClassName = ({ row, column, rowIndex, columnIndex }) => {
if (rowIndex === 0 && columnIndex === 0) {
return 'warning'
}
if (rowIndex === 2 && columnIndex === 0) {
return 'success'
}
}
// 表头
const tableHeaderCellClassName = ({ row, column, rowIndex, columnIndex }) => {
if (rowIndex === 0 && columnIndex === 0) {
return 'header'
}
}
css
.el-table .warning {
background-color: #fef0f0;
}
.el-table .success {
background-color: #faf7f2;
}
.el-table .header {
background-color: #ecf5ff!important;
}
表头得加 important
不然加上去,会覆盖失效
基本使用和合并差不多,合并返回的合并数的数组
而这个两个方法返回的是 "类名"
完整代码
js
<!--
@description: 复杂表格实现
-->
<template>
<div style="padding: 20px;">
<el-table
:data="tableData"
border
:header-cell-style="head"
:span-method="objectSpanMethod"
:cell-class-name="tableCellClassName"
:header-cell-class-name="tableHeaderCellClassName">
<el-table-column>
<template #header="">
<div class="first-head">
<div class="month">月份</div>
<div class="line"></div>
<div class="arg">参数</div>
</div>
</template>
<el-table-column prop="arg1" width="100" align="center" />
<el-table-column prop="arg2" width="100" align="center" />
</el-table-column>
<el-table-column v-for="(item, index) in tableHead" :key="index" :label="item" :prop="item" align="center">
<template #default="scope">
<div class="p-x-10px">{{ scope.$index < 2 ? scope.row[item] : '72' }}</div>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script setup lang='ts'>
/* ------------------------ 导入 与 引用 ----------------------------------- */
import { ref } from 'vue'
const tableHead = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
const tableData = ref([
{
arg1: '目标',
arg2: '季度目标',
'1月': 1,
'2月': 2,
'3月': 3,
'4月': 4,
'5月': 5,
'6月': 6,
'7月': 7,
'8月': 8,
'9月': 9,
'10月': 10,
'11月': 11,
'12月': 12
},
{
arg1: '目标',
arg2: '月度目标',
'1月': 1,
'2月': 2,
'3月': 3,
'4月': 4,
'5月': 5,
'6月': 6,
'7月': 7,
'8月': 8,
'9月': 9,
'10月': 10,
'11月': 11,
'12月': 12
},
{
arg1: '总销售额',
},
])
// 表头处理
const head = ({ row, column, rowIndex, columnIndex }) => {
if (rowIndex === 0) {
//这里为了是将第二列的表头隐藏,就形成了合并表头的效果
return { padding: 0 }
}
if (rowIndex === 1) {
//这里为了是将第二列的表头隐藏,就形成了合并表头的效果
return { display: 'none', padding: 0 }
}
}
// 合并处理
const objectSpanMethod = ({ row, column, rowIndex, columnIndex }) => {
// 第一行,第一二列
if (columnIndex === 0 && rowIndex < 2) {
// 第一二列单元格行合并
if (rowIndex === 0) {
return {
rowspan: 2,
colspan: 1
}
} else { // 其他这个范围内的单元格隐藏
return {
rowspan: 0,
colspan: 0
}
}
}
// 第一列
if (rowIndex === 0) {
// 从第二列开始,每隔3列合并
if (columnIndex > 1 && (columnIndex - 2) % 3 === 0) {
return {
rowspan: 1,
colspan: 3
}
} else if (columnIndex > 1) { // 从第二列开始,其他不合并的都隐藏
return {
rowspan: 0,
colspan: 0
}
}
}
// 第二列
if (rowIndex === 2) {
// 第一行,2列合并
if (columnIndex === 0) {
return [1, 2]
} else if (columnIndex === 3) { // 第三列合并为 12列
return [1, 12]
} else {
return [0, 0] // 其他这个范围内的单元格隐藏
}
}
}
// 单元格样式处理
// 表格内容
const tableCellClassName = ({ row, column, rowIndex, columnIndex }) => {
if (rowIndex === 0 && columnIndex === 0) {
return 'warning'
}
if (rowIndex === 2 && columnIndex === 0) {
return 'success'
}
}
// 表头
const tableHeaderCellClassName = ({ row, column, rowIndex, columnIndex }) => {
if (rowIndex === 0 && columnIndex === 0) {
return 'header'
}
}
</script>
<style>
.line {
width: 100%;
background-color: #ebeef5;
height: 1px;
transform: rotate(16deg);
}
.month {
margin-left: 110px;
}
.arg {
margin-left: 40px;
}
.el-table .warning {
background-color: #fef0f0;
}
.el-table .success {
background-color: #faf7f2;
}
.el-table .header {
background-color: #ecf5ff!important;
}
</style>
小结
行列合并我们使用到了 span-method
这样的一个方法
其中,return [合并的行数,合并的列数]
,return [0, 0] 隐藏单元格
修改单元格样式,主要是通过注入类名,从而达到覆盖原有样式的效果,主要分两个部分
- 修改表头
header-cell-class-name
- 修改表体
cell-class-name
还有就是动态表格的部分,可以通过 scope.$index
判断当前所在行数
如果你需要判断当前所在列数,可以通过 v-for
中的 index
进行判断