Element-Ui implements scoring table, scoring items, an example to master the table to merge rows and columns, computed to calculate the total score, the maximum score value of a certain level of classification, super detailed! Comment line by line.

 The effect picture is as follows. This is a demand of the company recently. Since there are few examples on the Internet, I decided to publish it for your reference after improving the code.

First of all, our database stores data in the form of id and pid, which can be divided into four grades of scoring items at most. In this example, the content of splicing data in the background is not involved, but the fake data written by the front end is displayed. It is necessary to implement a small backend Partners can use sql to query the corresponding format according to my data format and then reuse it.

I was very distressed when I received this request at first, because I didn’t write similar content, so I gave an excel to display it through the page, and finally I had to fill in the score, verify it and store it in the database. At first I thought it was through Tree-like table display, but this is far from the effect in the picture, and it is probably unacceptable. Let's see how I realized it below.

Page effect realization:

<template>
  <div class="pd_1 hasTableContainer">
    <el-table
      :data="testData"
      :span-method="objectSpanMethodS"
      border style="width: 100%"
      :header-cell-style="headerStyle">
      <el-table-column prop="levelTwoId" label="序号" align="center" width="100">
      </el-table-column>
      <el-table-column prop="levelOne" label="评估项目(一级)" align="center">
      </el-table-column>
      <el-table-column prop="levelTwo" label="评估项目(二级)" align="center">
      </el-table-column>
      <el-table-column prop="maxScore" label="最大分值" align="center">
        <template slot-scope="scope">
          <div v-if="scope.row.levelTwoId === '总分'">
            {
   
   {sum}}
            {
   
   {totalScore}}
          </div>
          <div v-if="scope.row.levelTwoId !== '总分'">
            {
   
   {scope.row.maxScore}}
          </div>
        </template>
      </el-table-column>
      <el-table-column prop="levelThree" label="评分标准" align="center" width="100">
      </el-table-column>
      <el-table-column prop="levelFour" align="center">
      </el-table-column>
      <el-table-column prop="score" label="分数" align="center">
        <template slot-scope="scope">
          <el-input
            placeholder="分数"
            v-model="scope.row.score"
            :disabled="scope.row.levelThree === '不具备'"
            type="number"
            step="1"
            min="0"
            @input="scope.row.score = inputVerification(scope.row)"
          ></el-input>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

Function and parameters:

<script>
export default {
  name: "itemConfiguration",
  data() {
    return {
      testData: [
        {
          levelTwoId: "1",
          levelOne: "设备购置情况",
          levelTwo: "设备使用年限(折旧系统)",
          maxScore: null,
          levelThree: "已使用6年",
          scoreAssociatedId: 5,
          score: null
        },
        {
          levelTwoId: "1",
          levelOne: "设备购置情况",
          levelTwo: "设备使用年限(折旧系统)",
          maxScore: null,
          levelThree: "已使用7年",
          scoreAssociatedId: 6,
          score: null
        },
        {
          levelTwoId: "1",
          levelOne: "设备购置情况",
          levelTwo: "设备使用年限(折旧系统)",
          maxScore: null,
          levelThree: "已使用8年",
          scoreAssociatedId: 7,
          score: null
        },
        {
          levelTwoId: "2",
          levelOne: "设备使用情况",
          levelTwo: "运行环境(风险系数)",
          maxScore: null,
          levelThree: "生产环境",
          scoreAssociatedId: 8,
          score: null
        },
        {
          levelTwoId: "2",
          levelOne: "设备使用情况",
          levelTwo: "运行环境(风险系数)",
          maxScore: null,
          levelThree: "灾备环境",
          scoreAssociatedId: 9,
          score: null
        },
        {
          levelTwoId: "3",
          levelOne: "设备使用情况",
          levelTwo: "设备故障",
          maxScore: null,
          levelThree: "每故障一次扣5分,扣完为止。",
          scoreAssociatedId: 10,
          score: null
        },
        {
          levelTwoId: "4",
          levelOne: "设备性能情况",
          levelTwo: "核心部件是否具备扩容能力(例如服务器的CPU、存储与网络引擎板)",
          maxScore: null,
          levelThree: "具备",
          levelFour: "且扩容费用/设备单价≤10%",
          scoreAssociatedId: 11,
          score: null
        },
        {
          levelTwoId: "4",
          levelOne: "设备性能情况",
          levelTwo: "核心部件是否具备扩容能力(例如服务器的CPU、存储与网络引擎板)",
          maxScore: null,
          levelThree: "具备",
          levelFour: "且扩容费用/设备单价≤30%",
          scoreAssociatedId: 12,
          score: null
        },
        {
          levelTwoId: "4",
          levelOne: "设备性能情况",
          levelTwo: "核心部件是否具备扩容能力(例如服务器的CPU、存储与网络引擎板)",
          maxScore: null,
          levelThree: "不具备",
          scoreAssociatedId: 13,
          score: 0
        },
        {
          levelTwoId: "总分",
          levelOne: null,
          levelTwo: null,
          maxScore: null,
          levelThree: "1.评分≥90      优\n2.90>评分≥60   良\n3.评分<60       差",
          scoreAssociatedId: null,
          score: null
        }
      ],
      // 一级评估项中需要合并行的数组
      levelOneMergeRowArrS: [],
      // 二级评估项中需要合并行的数组
      levelTwoMergeRowArrS: [],
      // 三级评估项中需要合并行的数组
      levelThreeMergeRowArrS: [],
      // 二级评估项关联的最大分数
      levelTwoMaxScore: {},
      // 二级评估项与他的子级评分标准关系对象,用于计算最大分数
      levelTwoSubScore: new Map(),
      // 总分
      totalScore: 0
    }
  },
  created() {
  },
  computed: {
    sum: function() {
      let total = 0;
      // 计算总分
      this.testData.forEach((data, index) => {
        total += parseInt(data.score ? data.score : 0);
      })
      this.totalScore = total;
    }
  },
  mounted() {
    // 根据数据中的属性,获取需要合并的对象的索引数组
    const getOrderIndexArr = (data, prop) => {
      const obj = {};
      // 遍历数据对不同层级的数据做筛选 reduce做累加操作,object.values只取累加后对象中的值
      const indexArr = Object.values(data.reduce((obj, item, i) => {
        // 如果为最后一项不做处理,因为总分这一行数据是后期添加的死数据不参加行的合并
        if (item.levelTwoId === '总分') {
          return obj;
        }
        // 记录当前对象在数组中的索引,也就是我们table展示时的行号
        item.rowIndex = i;
        // 提取出对应prop(传过来的层级)中所有数据,并记录prop一样的重复数据行号用来合并行
        if (prop !== 'levelThree' ? !obj[item[prop]] : !obj[item[prop] +"_"+ item.levelTwoId]) {
          // levelThree判断主要是因为有四级评估项时他的三级评估项一定是“具备”所以这样需要带上id去重
          prop !== 'levelThree' ? obj[item[prop]] = [] : obj[item[prop] +"_"+ item.levelTwoId] = [];
        }
        const key = prop !== 'levelThree' ? item[prop] : item[prop] +"_"+ item.levelTwoId;
        obj[key].push(i);
        // 记录所有评分标准的分数,并与二级评分项关联,用作后端添加
        if (prop === 'levelTwo') {
          const scoreMap = this.levelTwoSubScore.get(item[prop]) || new Map();
          scoreMap.set(item.scoreAssociatedId, item.score || 0);
          this.levelTwoSubScore.set(item[prop], scoreMap);
        }
        return obj;
      }, {}));
      // 过滤出需要合并行的数据
      return indexArr.filter(arr => arr.length > 1);
    };
    // 获取需要合并的一级对象的行索引数组
    const levelOneMergeRowArrS = getOrderIndexArr(this.testData, 'levelOne');
    // 获取需要合并的二级对象的行索引数组
    const levelTwoMergeRowArrS = getOrderIndexArr(this.testData, 'levelTwo');
    // 获取需要合并的三级对象的行索引数组
    const levelThreeMergeRowArrS = getOrderIndexArr(this.testData, 'levelThree');
    // 计算每个二级评估项目中现存的最大分数
    for (const item of this.testData) {
      if (item.levelTwoId === '总分') {
        continue;
      }
      // 获取当前二级评估项目中已有的最大分数
      const score = this.levelTwoMaxScore[item.levelTwoId];
      // 更新二级评估项目中的最大分数
      this.levelTwoMaxScore[item.levelTwoId] = score ? Math.max(parseInt(score), parseInt(item.score)) : (item.score || 0);
      // 将当前二级评估项目的最大分数赋值给对象的maxScore属性
      item.maxScore = this.levelTwoMaxScore[item.levelTwoId];
    }
    // 将需要合并的一级对象的索引数组赋值给实例对象的OrderIndexArrS属性
    this.levelOneMergeRowArrS = levelOneMergeRowArrS;
    // 将需要合并的二级对象的索引数组赋值给实例对象的TWOIndexArrS属性
    this.levelTwoMergeRowArrS = levelTwoMergeRowArrS;
    // 将需要合并的三级对象的索引数组赋值给实例对象的THREEIndexArrS属性
    this.levelThreeMergeRowArrS = levelThreeMergeRowArrS;
  },
  methods: {
    /**
     * 该方法是el-table标签中:span-method="objectSpanMethodS"指定的
     * 用于控制表格中单元格的合并行和列数
     * @param {Object} param0 - 对象包含row, column, rowIndex, columnIndex四个属性
     * @returns {Object} - 返回一个包含rowspan和colspan的对象,用于控制单元格的合并行和列数
     */
    objectSpanMethodS({ row, column, rowIndex, columnIndex }) {
      /**
       * 辅助函数checkColspan用于检查当前单元格是否需要合并
       * @param {Array} indexArr - 包含需要合并的行的索引数组
       * @returns {Object} - 返回一个包含rowspan和colspan的对象,用于控制单元格的合并行和列数
       */
      const checkColspan = (indexArr) => {
        // 遍历需要合并行的数组
        for (let i = 0; i < indexArr.length; i++) {
          const element = indexArr[i];
          // 对头行做合rowspan合并,其他的不显示
          for (let j = 0; j < element.length; j++) {
            const item = element[j];
            if (rowIndex === item) {
              return j === 0 ? { rowspan: element.length, colspan: 1 } : { rowspan: 0, colspan: 0 };
            }
          }
        }
        return { rowspan: 1, colspan: 1 };
      };
      // 如果是最后一行则将第一个单元格合并列三个,第五个单元合并列并三个
      if (rowIndex === this.testData.length - 1) {
        if (columnIndex === 0 || columnIndex === 4) {
          return { rowspan: 1, colspan: 3 };
        } else if (columnIndex !== 3){
          return { rowspan: 0, colspan: 0 };
        }
      }
      if (columnIndex === 1) {
        // 对一级评估项进行检查
        return checkColspan(this.levelOneMergeRowArrS);
      } else if (columnIndex === 2 || columnIndex === 0 || columnIndex === 3) {
        // 对二级评估项进行检查
        return checkColspan(this.levelTwoMergeRowArrS);
      } else if (columnIndex === 4) {
        // 对三级评估项进行检查,因为这里有的数据是没有四级评分项,只有三级评分项为“具备”的才需要合并行,其他的则合并行
        return row.levelThree !== '具备' ? { rowspan: 1, colspan: 2 } : checkColspan(this.levelThreeMergeRowArrS);
      } else if (columnIndex === 5) {
        // 对四级评估项进行检查,如果三级评分项为“具备”则展示四级评分项,否则不展示
        return row.levelThree === '具备' ? { rowspan: 1, colspan: 1 } : { rowspan: 0, colspan: 0 };
      }
    },
    // 用于检查输入的分数是否有效,并更新当前二级评估项目的最大分数
    inputVerification(row) {
      // 从行对象中解构出id和score
      let { levelTwoId, score } = row;
      // 正则表达式用于匹配正整数
      const regex = /^\d+$/;
      // 设置分数,这里主要是做记录,用于计算一个二级评估项目下的最大分数
      this.levelTwoSubScore.get(row.levelTwo).set(row.scoreAssociatedId, Number(regex.test(score) ? score : 0));
      if (!regex.test(score)) {  // 如果输入的分数不合法返回0
        score = 0;
      }
      // 初始化为负无穷大
      let maxValue = -Infinity;
      // 计算当前评分标准所属的二级评估项目下的最大分数
      for (let [key, value] of this.levelTwoSubScore.get(row.levelTwo)) {
        if (value > maxValue) {
          maxValue = value;
        }        
      }
      // 更新二级评估项目与最大分数关系对象(对象扩展运算符{...}可以将对象进行展开操作,这段代码是在其基础上,动态添加或更新一个属性,属性名为id变量的值,属性值为newMaxScore。)
      this.levelTwoMaxScore = { ...this.levelTwoMaxScore, [levelTwoId]: maxValue };
      // 查找数据中id与输入的id相同的对象
      const data = this.testData.find(d => d.levelTwoId === levelTwoId);
      if (data) {  // 如果找到了该对象更新对象中的最大分数,用作页面显示
        data.maxScore = maxValue;
      }
      return Number(score);
    },
    headerStyle({row, column, rowIndex, columnIndex}) {
      row[4].colSpan = 2 //第四个表头占两格
      row[5].colSpan = 0 //第五个表头占零格
      if (columnIndex === 5) { //隐藏第五个表头
        return 'display: none'
      }
    }
  }
}
</script>

CSS styles:

<style lang="scss" scoped>
.hasTableContainer{
  ::v-deep .el-table{
    .cell{
      white-space: pre-line;
    }
  }
}
</style>

Guess you like

Origin blog.csdn.net/TangBoBoa/article/details/130193521