vue3+el-table实现行列转换

目录
  • 行列转换
  • 分析成绩单的组成
  • 用 vue3 + el-table 做的成绩单
  • 前端模拟数据
  • 使用 el-table 生成成绩单
    • 确定表头
    • 确定数据
    • 计算学生的总分和平均分
    • 计算排名
    • 计算各个学科的平均分。
    • 记录各个科目的最高分和最低分
    • 增加排序功能
    • 增加色彩区分

行列转换

为啥会出现行列转换的问题呢?因为用户想看的是一张多列的大表单,但是数据库里面保存的是“单列”的数据。于是就需要做一个转换,这样客户看得更方便清晰。

SQL有一种写法可以支持这种行列转换,但是写起来比如绕,不便于理解。
所以我个人还是倾向于在前端实现转换的操作,因为可以节省后端的性能资源。

这里以成绩单为例演示一下具体的实现方式。

分析成绩单的组成

这个又爱又恨的东东想必大家都不会陌生,这是一个比较典型的需要行列转换的情景。
先回顾一下成绩单的样子:(图片来自于网络,侵删)

成绩单

分析一下,可以得到如下几个基本元素:

  • 学科:语文、数学、物理、化学等。确定列数
  • 学生:确定行数
  • 年级班级:一年一班、一年二班等。分类依据
  • 考试:期中考试、期末考试等。分类依据
  • 成绩:确定数据内容。

元素的分类:

  • 分类依据:指的是生成一个大表单的查询条件,同类别的数据汇总成一个大表单。
  • 学科:确定列数,科目越多列数也就越多。
  • 学生:确定行数,学生越多行数也就越多。

我们再看看数据库里的设计,一般会设计几个基础表和一个成绩表。

  • 学科表
  • 学生表
  • 班级表
  • 考试表
  • 成绩表

篇幅有限,具体字段就不介绍了,说全的话也是挺复杂的。

用 vue3 + el-table 做的成绩单

行列转换后的成绩单

学科、学生、总分、平均分、最高分、最低分、名次都有了。还可以各种排序。下面我们来看看是如何实现的。

前端模拟数据

我们在前端模拟一下数据。(简化模式)

// 学科 —— 确定列
const subject = [
  { id: 1, name: '数学' },
  { id: 2, name: '语文' },
  { id: 3, name: '物理' },
  { id: 4, name: '化学' }
]

// 学生 —— 确定行
const student = [
  { id: 1, name: '张三' },
  { id: 2, name: '李四' },
  { id: 3, name: '王五' },
  { id: 4, name: '赵六' }
]

// 班级 —— 分类依据
const classes = [
  { id: 1, name: '一年一班' },
  { id: 2, name: '一年二班' }
]

// 考试 —— 分类依据
const exam = [
  { id: 1, name: '期中考试' },
  { id: 2, name: '期末考试' }
]

// 成绩 —— 确定内容
const reportCard = [
  // 序号   考试ID      班级ID       学生ID         科目ID        成绩
  { id: 1, examId: 1, classId: 1, studentId: 1, subjectId: 1, score: 100 },
  { id: 2, examId: 1, classId: 1, studentId: 1, subjectId: 2, score: 98 },
  { id: 3, examId: 1, classId: 1, studentId: 1, subjectId: 3, score: 90 },
  { id: 4, examId: 1, classId: 1, studentId: 2, subjectId: 1, score: 90 },
  { id: 5, examId: 1, classId: 1, studentId: 2, subjectId: 2, score: 90 },
  { id: 6, examId: 1, classId: 1, studentId: 2, subjectId: 3, score: 40 },
  { id: 7, examId: 1, classId: 1, studentId: 3, subjectId: 1, score: 30 },
  { id: 8, examId: 1, classId: 1, studentId: 3, subjectId: 2, score: 90 },
  { id: 8, examId: 1, classId: 1, studentId: 3, subjectId: 3, score: 40 },
  { id: 9, examId: 1, classId: 1, studentId: 4, subjectId: 1, score: 64 },
  { id: 8, examId: 1, classId: 1, studentId: 4, subjectId: 2, score: 90 },
  { id: 9, examId: 1, classId: 1, studentId: 4, subjectId: 3, score: 70 }
]

使用 el-table 生成成绩单

element-plus 提供了一个很强大的 表格组件 —— el-table,可以实现很多基础功能,比如排序、调整宽度、设置颜色、筛选等功能。那么我们可以使用这个组件来实现成绩单。

确定表头

行列转换的一大特点是,表头(有多少列)并不是固定的,而是需要动态生成的。
以成绩单为例,表头列数由学科决定,学科越多,表头也就越多。

首先我们按照 el-table 的要求设置一下表头:

/**
 * 根据学科建立表头
 * * 学号、姓名、【各个学科】、总分、平均分、名次
 */
const createTableHead = () => {
  // 添加学生
  const head = [
  {
    prop: 'id',
    label: '学号',
    width: 120
  },
  {
    prop: 'name',
    label: '姓名',
    width: 120
  }]

  // 添加科目
  for (const key in subject) {
    const sub = subject[key]
    head.push({
      prop: 'sub_' + sub.id,
      label: sub.name,
      width: 120
    })
  }

  head.push({
    prop: 'totalScore',
    label: '总分',
    width: 120
  })
  head.push({
    prop: 'averageScore',
    label: '平均分',
    width: 120
  })
  head.push({
    prop: 'ranking',
    label: '名次',
    width: 120
  })

  return head
}

这里表头分为两种,一个是固定的,一个是根据科目动态生成的。
我们采用简单粗暴的方式,先直接添加固定列,然后遍历学科添加动态列。

确定数据

表头确定好了之后,我们需要确定一下 data 部分,正式开始行列转换。
还是按照 el-table 的需要来定义一下数据格式:

{
  id: 1,
  name: '张三',
  sub_1: 100,
  sub_2: 100,
  ...
  totalScore: 200,
  averageScore: 100,
  ranking: 1
}

中间可以有各个学科,属性命名规则: 前缀 “sub_” + 学科ID 。
这样便于后续添加数据。

遍历成绩表来填充数据。

/**
 * 行列转换
 */
const rowToCol = () => {
  // 对象形式的成绩列表
  const _code = {}
  // 数组形式的成绩列表
  const _arr = []

  // 遍历成绩单
  for (const key in reportCard) {
    const rep = reportCard[key]
    if(typeof _code[rep.studentId] === 'undefined') {
      // 没有记录。创建一行学生成绩,加入固定列的数据
      _code[rep.studentId] = {
        id: rep.studentId, // 学生ID
        name: (student.filter((item)=>item.id === rep.studentId)[0] || {name:''}).name, // 根据id获取学生姓名
        totalScore: 0, // 学生的各科总分,后续修改
        averageScore: 0, // 学生各科的平均分,后续修改
        ranking: 1 // 名次,后续修改
      }
    }
    // 记录各科分数
    _code[rep.studentId]['sub_' + rep.subjectId] = rep.score
  }

  // 计算总分、平均分
  下面介绍

  // 计算名次
  下面介绍

  return _arr
}

遍历成绩单的数据,先依据学生ID建立一个对象,然后根据学科确定各科成绩。

计算学生的总分和平均分

一般成绩单还需要得到学生的总分和平均分,我们可以遍历成绩统计总分和平均分。

  ...
  // 计算总分、平均分
  for(const key in _code) {
    const code = _code[key]
    // 遍历学科
    let total = 0
    let ave = 0
    let count = 0
    for (const key in subject) {
      const fenshu = code['sub_' + subject[key].id]
      if (typeof fenshu !== 'undefined') {
        // 有成绩,计算总分和平均分
        count++
        total += fenshu
        ave = Math.floor(total / count * 10) / 10 // 保留一位小数。
        // 如果保留两位小数的话可以这样 (total / count ).toFixed(2)
      }
    }
    code.totalScore = total
    code.averageScore = ave
    // 对象的数据转为数组的数据
    _arr.push(code)
  }

计算排名

成绩单有了,我们还需要做一个排名。
如果不需要排名次的话,可以跳过此步骤。

  let _ranking = 0
  _arr.sort((a, b) => {
    // 按照总分倒序
    return b.totalScore - a.totalScore
  }).forEach((item) => {
    // 设置名次
    _ranking++
    _arr.find((a) => a.id === item.id).ranking = _ranking
  })

计算各个学科的平均分。

这个也是比较常见的需求。
el-table 提供了自动求和的功能,同时也提供了自定义求和的方法,我们可以利用他来实现学科的平均分。

下面是官网给出的方法,本来是求和的,稍微修改一下就可以得到学科的平均分。

 // 计算各学科的平均分
let i = 0
const  getSummaries = (param) => {
  i++
  if (i < 7) return []

  const { columns, data } = param;
  const sums = [];

  columns.forEach((column, index) => {
    if (index === 0) {
      sums[index] = '平均分'
      return
    }
    const values = data.map(item => Number(item[column.property]));
    if (!values.every(value => isNaN(value))) {
      sums[index] = values.reduce((prev, curr) => {
        const value = Number(curr)
        if (!isNaN(value)) {
          return prev + curr
        } else {
          return prev
        }
      }, 0);
      sums[index] =  Math.floor(sums[index] / values.length * 10) / 10
    } else {
      sums[index] = 'N/A';
    }
  })

  return sums

跟踪了一下,发现这个函数会被调用七次,这个次数似乎和行数、列数没有关系。
而且前几次的 return 没啥作用,最后一次才会有效,所以加了一个判断。

记录各个科目的最高分和最低分

一张成绩单还可以计算各种数据,比如看看学科的最高分数、最低分数。
一般也可以关注一下这些统计数据,同样我么可以用自定义求和的方法来实现需求,我么改进一下 getSummaries。

// 计算各科目的平均分、最高分和最低分
const getSummaries = ({ columns }) => {
  i++
  if (i < 7) return []

  const sums = [];

  columns.forEach((item, index) => {
    if (item.property.indexOf('sub_') >= 0 ){
      const subjectId = item.property.replace('sub_', '')*1
      // 学科,计算平均值
      let ave = 0
      let sum = 0
      let max = 0
      let min = 99999
      const _arr = reportCard.filter((r) => r.subjectId === subjectId)
      _arr.forEach((item, index) => {
        sum += item.score // 求和
        if (max < item.score) max = item.score // 记录最高分
        if (min > item.score) min = item.score // 记录最低分
      })
      if (_arr.length === 0) {
        sums[index] = '-' // 没有成绩
      } else {
        // 计算平均分
        ave = Math.floor(sum/_arr.length * 10) / 10
        sums[index] = `${ave}(${max}-${min})`
      }
    } else {
      // 不计算
      sums[index] = ''
    }
  })
  sums[0] = '统计'
  return sums

}

这次我么直接同 reportCard 来统计平均分、最高分和最低分。
同理,我么还可以统计一下及格人数、各个分段的人数。

增加排序功能

这个是 el-table 自带的功能,我们加上就好。

<el-table
    :data="tableData"
    style="width: 100%;height: 300px;"
    :default-sort = "{prop: 'totalScore', order: 'descending'}"
    :row-class-name="tableRowClassName"
    border
    show-summary
    :summary-method="getSummaries"
  >
    <el-table-column
      v-for="(item, index) in tableHead"
      :key="'s'+ index"
      fixed
      sortable
      :prop="item.prop"
      :label="item.label"
      :width="item.width">
    </el-table-column>
  </el-table>

el-table 的属性设置。default-sort 默认按照 总分倒序显示。

增加色彩区分

如果想要把平均分低于60的着重表示出来怎么办?el-table 也提供了功能,我们做一下判断设置css即可。

// 颜色
const tableRowClassName = ({row, rowIndex}) => {
  if (row.averageScore < 60) { // 平均分不及格的同学
    return 'warning-row';
  } else if (row.averageScore > 95) { // 平均分超过95的同学
    return 'success-row';
  }
  return '';
}

源码
https://gitee.com/naturefw/nf-vite2-element

小结

优点:

  • 不需要后端做行列转换,只需要提供基础数据即可,节省后端性能资源。
  • 功能比较全面,我能想到的基本都加上了。

缺点:

  • 没能做成通用的形式,其他行列转换的需求还需要编写代码。

到此这篇关于vue3+el-table实现行列转换的文章就介绍到这了,更多相关vue3 行列转换内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • vue实现行列转换的一种方法

    行列转换是一个老生常谈的问题,这几天逛知乎有遇到了这个问题.一个前端说,拿到的数据是单列的需要做转换才能够绑定,折腾了好久才搞定,还说这个应该后端直接出数据,不应该让前端折腾. 这个嘛,行列转换在后端也不是很好解决的问题,而且还有一个性能的问题,综合考虑,我还是觉得应该由前端进行行列转换.光说不练假把式,所以拿出来代码仅作为抛砖引玉,供大家参考一下,如果有更好的方法,欢迎一起来探讨. 1.模板设计. <div id="app"> <table class="

  • vue3+el-table实现行列转换

    目录 行列转换 分析成绩单的组成 用 vue3 + el-table 做的成绩单 前端模拟数据 使用 el-table 生成成绩单 确定表头 确定数据 计算学生的总分和平均分 计算排名 计算各个学科的平均分. 记录各个科目的最高分和最低分 增加排序功能 增加色彩区分 行列转换 为啥会出现行列转换的问题呢?因为用户想看的是一张多列的大表单,但是数据库里面保存的是"单列"的数据.于是就需要做一个转换,这样客户看得更方便清晰. SQL有一种写法可以支持这种行列转换,但是写起来比如绕,不便于理

  • vue3 elmentPlus table实现列宽可拖拽功能

    el-table 里边的border属性,设置之后表格即可直接拖拽,下文内容是相关扩展功能的实现 最近公司项目里边需求要让表格的宽度可拖动,我们的公司的项目有vue2的也有vue3的,表格分别使用了element UI和element Plus,前者的社区比较丰富,我们使用了mizuka-wu/el-table-draggable 但是对于后者,我查到社区相关的插件并不多,但也找到了guolaopi/element-plus-table-dragable-demo 把demo下载下来之后,我发现

  • Oracle实现行列转换的方法分析

    本文实例讲述了Oracle实现行列转换的方法.分享给大家供大家参考,具体如下: 1.固定列数的行列转换 如: student subject grade --------- ---------- -------- student1 语文 80 student1 数学 70 student1 英语 60 student2 语文 90 student2 数学 80 student2 英语 100 -- 转换为: 语文 数学 英语 student1 80 70 60 student2 90 80 10

  • 一个简单的SQL 行列转换语句

    一个简单的SQL 行列转换 Author: eaglet 在数据库开发中经常会遇到行列转换的问题,比如下面的问题,部门,员工和员工类型三张表,我们要统计类似这样的列表 部门编号 部门名称 合计 正式员工 临时员工 辞退员工 1 A 30 20 10 1 这种问题咋一看摸不着头绪,不过把思路理顺后再看,本质就是一个行列转换的问题.下面我结合这个简单的例子来实现行列转换. 下面3张表 复制代码 代码如下: if exists ( select * from sysobjects where id =

  • Sql实现行列转换方便了我们存储数据和呈现数据

    从MS Sql Server 2005微软就推出了pivot和unpivot实现行列转换,这极大的方便了我们存储数据和呈现数据.今天就对这两个关键字进行分析,结合实例讲解如何存储数据,如何呈现数据. 例如学生选课和成绩系统中就有一张表,该表存储了学生的课程成绩,我们无法去预料课程的多少,因此一般表会设计为下面这样: 图1  最后一列是课程编号,这样无论开学之后还会不会增加课程供学生选择,都没有关系.那么我们要呈现给用户看的报表一般是这样的: 图2  可以看到存储数据的时候采用的是列式存储,最终呈

  • 浅析SQL语句行列转换的两种方法 case...when与pivot函数的应用

    /*创建数据库*/ CREATE DATABASE tmp go USE tmp go /*创建数据库测试表*/ CREATE TABLE [Scores]     (       [ID] INT IDENTITY(1, 1)                PRIMARY KEY ,       [Student] VARCHAR(20) ,       [Subject] VARCHAR(30) ,       [Score] FLOAT     ) go TRUNCATE TABLE Sc

  • sql server通过pivot对数据进行行列转换的方法

    脚本: /* 说明:sql server如何通过pivot对数据进行行列转换 */ create table #t ( [员工工号] nvarchar(50), [员工姓名] nvarchar(50), [月份] nvarchar(50), [工资] int ) insert into #t values(N'01', N'张三', N'2020年01月', 2400) ,(N'02', N'李四', N'2020年01月', 3000) ,(N'01', N'张三', N'2020年02月',

  • SQL Server 使用 Pivot 和 UnPivot 实现行列转换的问题小结

    对于行列转换的数据,通常也就是在做报表的时候用的比较多,之前也零零散散的看了一些,今天就来总结一下. 先创建一个用于演示的临时表: create table #temp ( 年份 nvarchar(10) null, 月份 nvarchar(10) null, 数量 int null ) insert into #temp(年份,月份,数量) select '2015','1','5645' union select '2015','2','1234' union select '2015','

  • SQL Server使用PIVOT与unPIVOT实现行列转换

    一.sql行转列:PIVOT 1.基本语法: create table #table1 ( id int ,code varchar(10) , name varchar(20) ); go insert into #table1 ( id,code, name ) values ( 1, 'm1','a' ), ( 2, 'm2',null ), ( 3, 'm3', 'c' ), ( 4, 'm2','d' ), ( 5, 'm1','c' ); go select * from #tabl

随机推荐