vue3+el-table实现行列转换

目录

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

行列转换
为啥会出现行列转换的问题呢?因为用户想看的是一张多列的大表单,但是数据库里面保存的是“单列”的数据。于是就需要做一个转换,这样客户看得更方便清晰。
SQL有一种写法可以支持这种行列转换,但是写起来比如绕,不便于理解。
所以我个人还是倾向于在前端实现转换的操作,因为可以节省后端的性能资源。
这里以成绩单为例演示一下具体的实现方式。

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

成绩单

分析一下,可以得到如下几个基本元素:
  • 学科:语文、数学、物理、化学等。确定列数
  • 学生:确定行数
  • 年级班级:一年一班、一年二班等。分类依据
  • 考试:期中考试、期末考试等。分类依据
  • 成绩:确定数据内容。
元素的分类:
  • 分类依据:指的是生成一个大表单的查询条件,同类别的数据汇总成一个大表单。
  • 学科:确定列数,科目越多列数也就越多。
  • 学生:确定行数,学生越多行数也就越多。
我们再看看数据库里的设计,一般会设计几个基础表和一个成绩表。
  • 学科表
  • 学生表
  • 班级表
  • 考试表
  • 成绩表
篇幅有限,具体字段就不介绍了,说全的话也是挺复杂的。

用 vue3 + el-table 做的成绩单
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, // 学生IDname: (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 = 0let ave = 0let count = 0for (const key in subject) {const fenshu = code['sub_' + subject[key].id]if (typeof fenshu !== 'undefined') {// 有成绩,计算总分和平均分count++total += fenshuave = Math.floor(total / count * 10) / 10 // 保留一位小数。// 如果保留两位小数的话可以这样 (total / count ).toFixed(2)}}code.totalScore = totalcode.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 = 0constgetSummaries = (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 = https://www.it610.com/article/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 = 0let sum = 0let max = 0let min = 99999const _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) / 10sums[index] = `${ave}(${max}-${min})`}} else {// 不计算sums[index] = ''}})sums[0] = '统计'return sums}

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

增加排序功能

这个是 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+el-table实现行列转换的文章就介绍到这了,更多相关vue3 行列转换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    推荐阅读