什么🙃,产品给的表格太复杂,前端不知道怎么写,看这里 👉

前言

开发过程中,我们总会遇见各种各样的表格,既有行合并,又有列表合并,又有自定义表头,并且表头,表格需要自定义颜色、字体...

最难的是,表格还可能是动态的,那这些要如何实现?

现在就通过,一个实例,来实现上面这个表格

实现

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 进行判断

相关推荐
lixin1 分钟前
使用 MCP 协议扩展 Cursor 功能:原理解析与实战指南
前端
a cool fish(无名)11 分钟前
rust-模块树中引用项的路径
java·前端·rust
前端进阶者21 分钟前
天地图Marker跳一跳动画
前端
火柴就是我24 分钟前
每日见闻之Three.js 根据官方demo 理解相机位置
前端
JosieBook34 分钟前
【web应用】基于Vue3和Spring Boot的课程管理前后端数据交互过程
前端·spring boot·交互
刘大猫.41 分钟前
npm ERR! cb() never called!
前端·npm·node.js·npm install·npmm err·never called
咔咔一顿操作1 小时前
常见问题三
前端·javascript·vue.js·前端框架
上单带刀不带妹1 小时前
Web Worker:解锁浏览器多线程,提升前端性能与体验
前端·js·web worke
电商API大数据接口开发Cris1 小时前
Node.js + TypeScript 开发健壮的淘宝商品 API SDK
前端·数据挖掘·api
还要啥名字1 小时前
基于elpis下 DSL有感
前端