• 作者:老汪软件技巧
  • 发表时间:2024-09-10 04:01
  • 浏览量:

前言

最近发现有人在问,使用element-plus组件库的Table组件,想要把表格的数据显示由一条数据显示一行改为一列显示,应该怎么改这个表格组件,我直接惊呆了!下面谈谈这个需要该怎么实现。

场景介绍

根据下图场景,将表格数据由一行显示一条数据变为一列显示一条数据。

需求分析

其实看以上图片就能基本看出应该转成什么样的格式,如果还看不出,可以再结合以下数据,看看其中数据和表格之间的规律。

const list = [
  { "date": "2024-9-1", "pv": 1, "cv": 2, "uv": 3 },
  { "date": "2024-9-2", "pv": 4, "cv": 5, "uv": 6 },
  { "date": "2024-9-3", "pv": 7, "cv": 8, "uv": 9 }
]

确定渲染每一列的prop值

我们知道,elementplus组件库的table组件是根据表头的prop的值,确定该列显示哪个字段的值。

原始表格数据每列显示的prop的值如下columns所示:

"list">
  <el-table-column :prop="prop" :label="prop" 
    v-for="prop in columns" :key="prop" 
  />

// 行显示数据表格的列prop属性值
const columns = ref(['date', 'pv', 'cv', 'uv'])

由以上图片和数据推断,转换为列显示后的columns应该是以下格式:

// 列显示数据表格的prop属性值
const columns = ref(['date', '2024-9-1', '2024-9-2', '2024-9-3'])

以上columns数据是不是很像根据key进行数据分组,这里date字段就代表一个key值,后面跟着该字段的所有值,后面我们计算表头columns就是通过分组后的数据进行计算。

确定转换后每一条数据的格式

当推断出每一列的prop值,那么自然就可以知道每条数据的数据格式了!

const columns = ref(['date', 'pv', 'cv', 'uv'])
const list = [
  { "date": "2024-9-1", "pv": 1, "cv": 2, "uv": 3 },
  { "date": "2024-9-2", "pv": 4, "cv": 5, "uv": 6 },
  { "date": "2024-9-3", "pv": 7, "cv": 8, "uv": 9 }
]
// 以上表格数据应转换成以下数据
const columns = ref(['date', '2024-9-1', '2024-9-2', '2024-9-3'])
const changedList = [
  { "date": "pv", "2024-9-1": 1, "2024-9-2": 4, "2024-9-3": 7 },
  { "date": "cv", "2024-9-1": 2, "2024-9-2": 5, "2024-9-3": 8 },
  { "date": "uv", "2024-9-1": 3, "2024-9-2": 6, "2024-9-3": 9 }
]

知道了应该转成什么样的数据,那么下面就可以开始重写每条的数据格式了

重新构建数据格式

由于当前示例使用的是vue3,所以我就把格式化数据封装成一个组合式函数useTableDataLineToColumn,该函数对外返回columns表头数据和initData数据转换方法,该方法返回转换后的数据。直接上代码!后面也有相关解释。

useTableDataLineToColumn.js

_表格行数据变列数据_表格变换行列查重

import { ref, computed } from 'vue'
export const useTableDataLineToColumn = () => {
  // 一条数据的所有字段数组
  let props = ref([])
  // 每个字段的分组数据
  let groupData = ref([])
  // 计算表头
  const columns = computed(() => {
    return props.value.length > 0 ? [props.value[0], ...groupData.value[0]] : []
  })
  // 根据每条数据的字段对数据进行分组
  const dataToGroupByKey = (list) => {
    list.forEach((item) => {
      // 遍历每个字段
      props.value.forEach((key, index) => {
        // 把对应字段的值放到对应字段分组中
        groupData.value[index].push(item[key])
      })
    })
  }
  // 根据分组数据,转换成最终显示的数据个数
  const changeGroupData = () => {
    // 转换后的数据
    const list = []
    // 解构分组数据
    const [date, ...otherData] = groupData.value
    // 初始化每一行的数据, 除去表头,有几个key就算有几行
    props.value.slice(1).forEach((prop, i) => {
      list[i] = []
      // 设置第一列标题索引名称
      const data = { [props.value[0]]: prop }
      // 通过遍历每一列的日期,设置对应行的数据
      date.forEach((date, dateIndex) => {
        data[date] = otherData[i][dateIndex]
      })
      list[i] = data
    })
    return list
  }
  // 初始化分组数据
  const initGroup = (list) => {
    const firstData = list[0] || {}
    // 获取一条数组的所有字段
    props.value = Object.keys(firstData)
    // 初始化每个字段的分组数据
    for(let i = 0; i < props.value.length; i++) {
      groupData.value[i] = []
    }
  }
  
  return {
    columns,
    initData: (data = []) => {
      // 初始化分组
      initGroup(data)
      // 向分组加入数据
      dataToGroupByKey(data)
      // 初始化分组内的数据,转为列数据
      return changeGroupData()
    }
  }
}

1.在initGroup方法中,可提取表格第一条数据,获取所有key,并把与key对应的groupData分组索引值初始化为空数组。

初始化后数据格式大致如下:

const props = ['date', 'pv', 'cv', 'uv']
// 索引数据和以上`keys`的索引一一对应
const groupData = [
  [],
  [],
  [],
  []
]

2.由于每个字段的分组已经初始化完成,在dataToGroupByKey方法里直接遍历表格的每条数据,把对应字段的值加入的对应分组当中

初始化后数据格式大致如下:

const props = ['date', 'pv', 'cv', 'uv']
// 索引数据和以上`keys`的索引一一对应
const groupData = [
  ['2024-9-1''2024-9-2''2024-9-3'],
  [147],
  [258],
  [369]
]

3.计算表头数据都加入到分组下后,表头通过取字段和分组的第一项,即可计算出来:

const columns = computed(() => { 
  return props.value.length > 0 ? [props.value[0], ...groupData.value[0]] : []
}

4.最后通过changeGroupData方法把除了表头分组数据外的所有分组数据转为表格最后需要显示的数据格式。

组合函数用法

Page.vue


<script setup>
import { ref, onMounted } from 'vue' 
import { getListApi } from '@/api'
import { useTableDataLineToColumn } from '@/hooks/useTableDataLineToColumn'
const { initData, columns } = useTableDataLineToColumn()
// 转换后数据
const exchangeList = ref([])
// 获取表格数据
const getList = async () => {
  const res = await getListApi()
  exchangeList.value = initData(res.data)
}
onMounted(() => {
  getList()
})
script>

需要自定义表格标签名用法方法一

表格转为列数据后,第一列即为标签名,所以只需渲染表格列时,如果是第一列则改为映射名称即可。


<script setup>
// ...省略以上代码
const { initData, columns } = useTableDataLineToColumn()
const labelMap =  {
  date: '日期',
  pv: '浏览量',
  cv: '点击量',
  uv: '参与量'
}
// ...省略以下代码
script>

方法二

修改useTableDataLineToColumn组合函数代码,初始化时传递一个标签名与属性名的labelMap映射对象进来。

可以判断如果没有传递labelMap对象,则显示prop为标签名,否则显示映射名称:

// 修改用法
const { initData, columns } = useTableDataLineToColumn({
  labelMap: {
    date: '日期',
    pv: '浏览量',
    cv: '点击量',
    uv: '参与量'
  }
})
export const useTableDataLineToColumn = ({
  // 增加
  labelMap = null
}) => {
  
  // 增加
  const getLabel = (key) => {
    return labelMap ? labelMap[key] : key
  }
 // 修改表头
  const columns = computed(() => {
    return props.value.length > 0 ? [getLabel[props.value[0]], ...groupData.value[0]] : []
  })
  const changeGroupData = () => {
    // ...
    // 设置第一列标题索引名称
    const data = { [getLabel[props.value[0]]]: getLabel[prop] }
    // ...
  }
}

总结

其实解决这个需求最重要的是知道需要把数据转成什么样子,那么问题就变得简单了,实现转换数据格式的方法也可以有多种,所以最重要的还是要知道解题的思路。