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

前言

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

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

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

实现

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

相关推荐
泯泷8 分钟前
那段文字是谁删的?Yjs 14 正式版之前,一套删除归属方案的实现与边界
前端·javascript·算法
野槐3 小时前
前端项目优化(有了我,会发生...)
前端
aa小小3 小时前
【无标题】
前端
计算机魔术师4 小时前
还在单线程跟 AI 聊天?Claude Code 并行多会话让你一个人干三个人的活
前端
志尊宝4 小时前
Vue3 零基础每日笔记(038):亲手封装 useDebounceFn 与 useThrottleFn——高频事件的流量阀
前端·javascript·vue·html·vue3
JavaGuide4 小时前
对标 MinIO!全新一代分布式文件系统正式发布
前端·后端
风骏时光牛马4 小时前
AI驱动自动化业务工作流搭建实践
前端
码云之上5 小时前
Skill 里的脚本终于能跑了,星悟接 CubeSandbox 的纪实
前端·人工智能·前端框架
IT_陈寒5 小时前
Vue computed属性这个坑,我居然踩了三次才爬出来
前端·人工智能·后端
烈风逍遥5 小时前
第三篇:组件化实践,SpTable 通用表格组件设计
前端·架构