1. 程式人生 > 程式設計 >vue3+el-table實現行列轉換

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,namULCzDrGMkV
e: '化學' } ] // 學生 —— 確定行 const student = [ { id: 1,name: '張三' },name: '李四' },name: '王五' },name: '趙六' } ] // 班級 —— 分類依據 const classes = [ { id: 1,name: '一年一班' },name: '一年二班' } ] // 考試 —— 分類依據 const exam = [ { id: 1,name: '期中考試' },name: '期末考試' } ] // 成績 —— 確定內容 const reportCard = [ // 序號 考試ID 班級ID 學生ID 科目ID 成績 { id: 1,examId: 1,classId: 1,studentId: 1,subjectId: 1,score: 100 },subjectId: 2,score: 98 },subjectId: 3,score: 90 },studentId: 2,{ id: 5,{ id: 6,score: 40 },{ id: 7,studentId: 3,score: 30 },{ id: 8,examIdULCzDrGMkV: 1,{ id: 9,studentId: 4,score: 64 },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。

// 計算各科目的平均分、最高分和最低分
coULCzDrGMkVnst 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 行列轉換內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!