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

前言

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

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

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

实现

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

相关推荐
m0_748247552 小时前
Web 应用项目开发全流程解析与实战经验分享
开发语言·前端·php
m0_748255022 小时前
前端常用算法集合
前端·算法
真的很上进2 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
web130933203982 小时前
vue elementUI form组件动态添加el-form-item并且动态添加rules必填项校验方法
前端·vue.js·elementui
NiNg_1_2343 小时前
Echarts连接数据库,实时绘制图表详解
前端·数据库·echarts
如若1233 小时前
对文件内的文件名生成目录,方便查阅
java·前端·python
滚雪球~4 小时前
npm error code ETIMEDOUT
前端·npm·node.js
沙漏无语4 小时前
npm : 无法加载文件 D:\Nodejs\node_global\npm.ps1,因为在此系统上禁止运行脚本
前端·npm·node.js
supermapsupport4 小时前
iClient3D for Cesium在Vue中快速实现场景卷帘
前端·vue.js·3d·cesium·supermap
brrdg_sefg4 小时前
WEB 漏洞 - 文件包含漏洞深度解析
前端·网络·安全