vue+element ui实现复杂表格

8,736 阅读3分钟

「这是我参与2022首次更文挑战的第16天,活动详情查看:2022首次更文挑战」。

一、需求

根据项目需求,需要实现以下形式的一个表格。

二、实现

由于项目所使用的框架为 vue + element ui,所以我们可以考虑使用 element ui 的 Table 表格组件来实现。但是通过了解发现,该组件所需的数据结构似乎并不能满足该功能的实现,通过研究,我们可以自定义表格渲染所需的 JSON 数据,这样的话就能方便实现了,具体做法如下:

1、定义表格组件所需的 JSON 数据(tableData.json)

{
    "header_labels": {
        "header1_label": {
            "label": "商家1",
            "children": {
                "header1_col1": "",
                "header1_col2": "",
                "header1_col3": ""
            }
        },
        "header2_label": {
            "label": "商家2",
            "children": {
                "header2_col1": "",
                "header2_col2": "",
                "header2_col3": ""
            }
        },
        "header3_label": {
            "label": "商家3",
            "children": {
                "header3_col1": "",
                "header3_col2": "",
                "header3_col3": ""
            }
        }
    },
    "col_values": [
        {
            "header1_col1": "库存(件)",
            "header1_col2": "20000",
            "header2_col1": "库存(件)",
            "header2_col2": "21000",
            "header3_col1": "库存(件)",
            "header3_col2": "22000"
        },
        {
            "header1_col1": "总销量(件)",
            "header1_col2": "15000",
            "header2_col1": "总销量(件)",
            "header2_col2": "7850",
            "header3_col1": "总销量(件)",
            "header3_col2": "1500"
        },
        {
            "header1_col1": "日期",
            "header1_col2": "上装销量(件)",
            "header1_col3": "下装销量(件)",
            "header2_col1": "日期",
            "header2_col2": "上装销量(件)",
            "header2_col3": "下装销量(件)",
            "header3_col1": "日期",
            "header3_col2": "上装销量(件)",
            "header3_col3": "下装销量(件)"
        },
        {
            "header1_col1": "2022.02.01",
            "header1_col2": "400",
            "header1_col3": "300",
            "header2_col1": "2022.02.01",
            "header2_col2": "20",
            "header2_col3": "300",
            "header3_col1": "2022.02.01",
            "header3_col2": "40",
            "header3_col3": "30"
        },
        {
            "header1_col1": "2022.02.02",
            "header1_col2": "700",
            "header1_col3": "800",
            "header2_col1": "2022.02.02",
            "header2_col2": "30",
            "header2_col3": "800",
            "header3_col1": "2022.02.02",
            "header3_col2": "70",
            "header3_col3": "80"
        },
        {
            "header1_col1": "2022.02.03",
            "header1_col2": "900",
            "header1_col3": "1000",
            "header2_col1": "2022.02.03",
            "header2_col2": "40",
            "header2_col3": "1000",
            "header3_col1": "2022.02.03",
            "header3_col2": "90",
            "header3_col3": "100"
        },
        {
            "header1_col1": "2022.02.041",
            "header1_col2": "1100",
            "header1_col3": "1100",
            "header2_col1": "2022.02.04",
            "header2_col2": "50",
            "header2_col3": "1100",
            "header3_col1": "2022.02.04",
            "header3_col2": "110",
            "header3_col3": "110"
        },
        {
            "header1_col1": "2022.02.05",
            "header1_col2": "1300",
            "header1_col3": "1200",
            "header2_col1": "2022.02.05",
            "header2_col2": "60",
            "header2_col3": "1200",
            "header3_col1": "2022.02.05",
            "header3_col2": "130",
            "header3_col3": "120"
        },
        {
            "header1_col1": "2022.02.06",
            "header1_col2": "1400",
            "header1_col3": "1500",
            "header2_col1": "2022.02.06",
            "header2_col2": "70",
            "header2_col3": "1500",
            "header3_col1": "2022.02.06",
            "header3_col2": "140",
            "header3_col3": "150"
        },
        {
            "header1_col1": "2022.02.07",
            "header1_col2": "1700",
            "header1_col3": "1600",
            "header2_col1": "2022.02.07",
            "header2_col2": "80",
            "header2_col3": "1600",
            "header3_col1": "2022.02.07",
            "header3_col2": "170",
            "header3_col3": "160"
        }
    ]
}

2、JS 中定义表格数据渲染所需变量(tableData),并获取本地 JSON 数据(tableData.json)赋值给 tableData 变量。

<script>
import tableJsonData from '/static/tableData.json'// 引入本地数据
export default {
  data() {
    return {
      tableData: {}
    }
  },
  created() {
    this.tableData = tableJsonData
  }
}
</script>

3、HTML 页面渲染

<template>
  <div class="app-container">
    <!-- tableData['col_values']:表格数据 -->
    <el-table
      :data="tableData['col_values']"
      style="width: 100%;"
      border
      height="600"
    >
      <!-- tableData['header_labels']:表头数据 -->
      <el-table-column
        v-for="(value1,key1,index1) in tableData['header_labels']"
        :key="index1"
        :label="value1['label']"
        align="center"
      >
        <el-table-column
          v-for="(value2,key2,index2) in value1['children']"
          :key="index2"
          align="center"
        >
          <template slot-scope="scope">
            <span>{{ scope.row[key2] }}</span>
          </template>
        </el-table-column>
      </el-table-column>
    </el-table>
  </div>
</template>

4、页面效果

由上图可知,页面的基本效果已经出来了,但是仍然有一些问题需要优化:

  1. 第二行显然是多余的,我们希望隐藏掉;
  2. 第三行和第四行,我们希望显示值的部分做一个单元格合并;
  3. 显示文字的单元格样式做一个修改,使其对比更鲜明;

三、优化

1、隐藏空白行

对于第一点,可以通过设置 Table 组件提供的属性 “header-cell-class-name” 来解决。

首先在 el-table 标签上设置该属性如下:

    <el-table
      :data="tableData['col_values']"
      style="width: 100%;"
      border
      height="600"
      :header-cell-class-name="hiddenHeaderRow"
    >

在 methods 中写 hiddenHeaderRow 方法如下:

    hiddenHeaderRow({ rowIndex }) {
      if (rowIndex === 1) {//隐藏第二行
        return 'hiddenClass'
      }
    },

在 style 中写 hiddenClass 样式如下:

.hiddenClass{
  display: none;
}

效果如图,空白行已经被隐藏了:

2、合并单元格

对于第二点,Table 组件也提供了一个属性 "span-method",我们可以通过该属性来实现单元格的合并。

同样的,在 el-table 标签上设置该属性如下:

    <el-table
      :data="tableData['col_values']"
      style="width: 100%;"
      border
      height="600"
      :header-cell-class-name="hiddenHeaderRow"
      :span-method="arraySpanMethod2"
    >

在 methods 中实现 arraySpanMethod2 方法:

    arraySpanMethod2({ rowIndex, columnIndex }) {
      if (rowIndex <= 1) {
        if (columnIndex % 3 === 1) {
          return [1, 2]// 返回的是需要合并的行和列的数量,不是下标,是固定的
        } else if (columnIndex % 3 === 2) {
          return [0, 0]
        }
      }
    },

效果如图,单元格合并了:

3、样式修改

对于第三点,也可以通过 Table 组件的“cell-class-name”属性来进行修改。

在 el-table 标签上设置该属性如下:

    <el-table
      :data="tableData['col_values']"
      style="width: 100%;"
      border
      height="600"
      :header-cell-class-name="hiddenHeaderRow"
      :span-method="arraySpanMethod2"
      :cell-class-name="addClass2"
    >

在 methods 中实现 addClass2 方法:

    addClass2({ rowIndex, columnIndex }) {
      if (rowIndex <= 1 && columnIndex % 3 === 0) {
        return 'sonStyle'
      } else if (rowIndex === 2) {
        return 'sonStyle'
      }
    },

在 style 中定义 sonStyle 样式:

.sonStyle{
  font-weight: bold;
  background-color: #f0f9eb;
  font-size: 14px;
  color: #27a2ff;
}

最终效果如图:

关于复杂表格,之前还写过这样一篇文章:vue+element ui 动态实现多元化表格(包括竖型表格,多级表头,单元格合并,可编辑,可折叠以及自定义样式)