elementUI表格树动态合并列问题处理(最终版,---新需求)

本文详细介绍了一种在前端框架中实现复杂表格数据动态合并与展示的方法,包括处理人、评估分数、加减分说明等字段的智能合并,以及动态表头的生成。通过具体的代码示例,展示了如何根据不同场景调整表格布局,确保数据清晰呈现。

在之前的一篇博客中针对这个问题,写了解决方法。现在是加了新需求,发现之前的算法有一些不足之处,现在进行纠正。

目前表格的需求是:

       评估人/评估分数/加减分说明,这个组合,根据评估人相同,就列合并;指标项目,评估人是为空的,这个组合都不合并;

      处理人/处理分数/加减分说明,这个组合,是动态表头,有可能只有一个组合,也有可能有2个组合,也有可能是3个组合;处理人是全合并,处理分数/加减分说明是指标项目,不合并,非指标项目全合并。

实现的效果图如下:

具体实现代码如下:(这里是以弹窗为例的。也可以不写在弹窗中。)

<template>
  <div style="height: 70%;">
    <el-row :gutter="20">
      <el-col :span="6"
              style="padding-top:5px;">
            <span>考核周期:
              <span>2019年11月考核</span>
            </span>
      </el-col>
      <el-col :span="4">
        <span>姓名:<span>张三</span></span>
      </el-col>
    </el-row>
    <el-row style="margin-top:20px;">
      <el-table :data="tableData1"
                ref="table"
                height="100%"
                :span-method="objectSpanMethod"
                :summary-method="getSummaries"
                :show-summary="true"
                style="width: 100%;margin-bottom: 20px;"
                default-expand-all
                row-key="id"
                @expand-change="expandChange"
                border
                class="showAll" size="small">
        <el-table-column width="200px"
                         prop="objectName"
                         label="名称">
        </el-table-column>
        <el-table-column prop="workStatus"
                         align="center"
                         width="60px"
                         label="状态"
                         show-overflow-tooltip>
          <template  slot-scope="scope">
            <span v-if="scope.row.workStatus=='1'">{{"提前"}}</span>
            <span v-else-if="scope.row.workStatus=='2'">{{"正常"}}</span>
            <span v-else-if="scope.row.workStatus=='3'">{{"滞后"}}</span>
            <span v-else-if="scope.row.workStatus=='4'">{{"已完成"}}</span>
            <span v-else-if="scope.row.workStatus=='5'">{{"暂停"}}</span>
            <span v-else-if="scope.row.workStatus=='6'">{{"中止"}}</span>
            <span v-else-if="scope.row.workStatus=='7'">{{"未启动"}}</span>
          </template>
        </el-table-column>
        <el-table-column width="65px"
                         prop="workProgress"
                         align="center"
                         label="进度(%)">
        </el-table-column>
        <el-table-column width="45px"
                         prop="timelong"
                         align="center"
                         label="工时">
        </el-table-column>
        <el-table-column width="80px"
                         prop="weight"
                         align="center"
                         label="权重(%)">
        </el-table-column>
        <el-table-column width="80px"
                         prop="selfRating"
                         align="center"
                         label="自评分">
        </el-table-column>
        <el-table-column prop="selfDiffScoreDescribe"
                         width="150px"
                         label="加减分说明">
        </el-table-column>
        <el-table-column prop="remarks"
                         width="350px"
                         label="工作内容">
        </el-table-column>
        <el-table-column prop="name"
                         label="评估人">
        </el-table-column>
        <el-table-column prop="kpiScore"
                         label="评估分数">
        </el-table-column>
        <el-table-column prop="assesseDiffScoreDescribe"
                         label="加减分说明"
                         width="300">
        </el-table-column>
        <el-table-column v-for="(item,index) in columns"
                         :key="index"
                         :label="item.label"
                         :prop="item.value"
                         :width="item.width"
        >
        </el-table-column>
      </el-table>
    </el-row>
  </div>
</template>

<script>
  export default {
    name: 'table-col',
    props: {
      // 弹窗是否打开
      visible: {
        type: Boolean,
        required: false,
        default: false
      },
      // 打开弹窗传递的id
      rowID: {
        type: String,
        required: false,
        default: ''
      }
    },
    created () {
    },
    data () {
      return {
        allRowNum:0, // 所有项目的行数
        spanArrSum:0, //非指标项目的行数
        spanArrFirstSum:0, //首次进入非指标项目的行数
        spanArrSumArr:[], // 处理分数、加减分说明等col合并数组
        allRowNumArr:[], // 处理人col合并数组
        spanArr: [], // 表格动态变化过程中的col合并数组
        objectListTable: [], // 非指标项目集合
        // 处理人数据集合
        handlerInfoList: [
          {approverDiffScoreDescribe1: null,approverName1: "张三",level1: 0,score1: 100},
          { approverDiffScoreDescribe1: null, approverName1: '谌小仲', level1: 0, score1: 100 },
          { approverDiffScoreDescribe1: null, approverName1: '王五', level1: 0, score1: 100 },
        ],
        tableData1: [], // 表格数据
        
      };
    },
    watch: {
      visible: {
        handler(n) {
          if (n) {
            this.objectListTable = [] // 非指标项目集合
            this.columns= [] // 动态表头
            this.tableData1 = [] // 表格数据绑定
            this.spanArrSum = 0; // 非指标项目的行数
            this.spanArrFirstSum = 0; // //首次进入非指标项目的行数
            this.allRowNum = 0  //所有项目的行数
            this.tableData1 = this.tableData  // 请求回来的数据赋值给this.tableData1
            // 为表格中的数据都增加一个show字段,用于判断树是否显示
            this.tableData1.forEach((item, index) => {
              item.show = true
              if(item.children){
                this.allRowNum += (item.children.length+1)
              }
              if(item.isFixed == false){
                this.objectListTable.push(item)
              }
            })
            let arrObject = []
            this.objectListTable.forEach((item, index) => {
              arrObject.push(item)
              if (item.children ) {
                this.spanArrSum += (item.children.length+1)
                this.spanArrFirstSum += (item.children.length+1)
                item.children.forEach((it, index) => {
                  arrObject.push(it)
                })
              }
            })
           // 调用表格数据合并的计算方法
            this.setdates(arrObject)
            this.handlerInfoList.forEach((item,index) => {
              console.log(item,'item1111111')
              var approverName = 'approverName' + (index + 1);
              var score = 'score' + (index + 1);
              var approverDiffScoreDescribe = 'approverDiffScoreDescribe' + (index + 1)
              this.columns.push({ label: '处理人', value: approverName, width: '100px' })
              this.columns.push({ label: '处理分数', value: score, width: '100px' })
              this.columns.push({ label: '加减分说明', value: approverDiffScoreDescribe, width: '100px' })
            })
          }
        },
        deep: true,
        immediate: true
      },
    },
    methods: {
      /** elementUI表格绑定的合并方法 */
      objectSpanMethod ({ row, column, rowIndex, columnIndex }) {
        if (columnIndex === 8 || columnIndex === 9 || columnIndex === 10) {
          let _row = this.spanArr[rowIndex]
          if (rowIndex < this.spanArrFirstSum) {
            if (_row) {
              return {
                rowspan: _row == 99 ? 0 : _row,
                colspan: _row==99 ?0:1
              }
            } else {
              return {
                rowspan: 0,
                colspan: 0
              }
            }
          }
          else{
              return {
                rowspan: 1,
                colspan: 1
              }
          }
        }
        else if (columnIndex === 11 || columnIndex === 14 || columnIndex === 17) {
          let row2 = this.allRowNumArr[rowIndex]
          return {
            rowspan: row2,
            colspan: 1
          }
        }
        else if ((columnIndex === 12 || columnIndex === 13 || columnIndex === 15 ||
          columnIndex === 16 || columnIndex === 18 || columnIndex === 19)) {
          if (rowIndex < this.spanArrFirstSum) {
            let _row1 = this.spanArrSumArr[rowIndex]
            // console.log(_row1,'ffffffffff_row1')
            return {
              rowspan: _row1,
              colspan: 1
            }
          } else{
            return {
              rowspan: 1,
              colspan: 1
            }
          }
        }
      },
      /**  合计方法 */
      getSummaries (param) {
        const { columns, data } = param;
        const sums = [];
        columns.forEach((column, index) => {
          if (index === 0) {
            sums[index] = '合计';
            return;
          }
          let values = []
          data.map(item => {
            values.push(Number(item[column.property]))
            if (item.children) {
              item.children.map(it => {
                values.push(Number(it[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] += 0;
          } else {
            // sums[index] = '';
            // sums[index].toString()
          }
        });

        for (var i = 0; i < sums.length; i++) {
          if (i === 5 || i === 4 || i === 0) {
            sums[i] = sums[i]
          } else {
            sums[i] = ''
          }
        }
        return sums;
      },
      /** 表格中树展开或关闭触发事件 */
      expandChange (row, expandedRows) {
        let arrObject = [] // 接收表格中树数据的转化
        this.spanArr = [] // 合并表格行数rowspan数据集合
        this.objectListTable.forEach((item, index) => { // objectListTable是排除不合并表格数据的集合
          // show字段主要是用来区分当前表格中哪些树是展开的
          if (row.id === item.id) {
            // 如果是当前点击的行,就将当前是否展开的属性赋值给show这个字段
            item.show = expandedRows
          }
          // 得到表格中需要合并数据的集合
          arrObject.push(item)
          // 如果字段show为true(树展开)
          if (item.show) {
            item.children.forEach((it, index) => {
              arrObject.push(it)
            })
          } else {

            // 节点是关闭状态  如果数据存在子节点
            if (item.children) {
                // 其他情况,push任意值进数组,但不能不push
                item.children.forEach((it, index) => {
                  arrObject.push(22)
                })
              }
            }
        })
        // 获取上半部分需要合并的行数
        if (!row.isFixed) {
          if (row.show) {
            this.spanArrSum += row.children.length
          } else {
            this.spanArrSum -= row.children.length
          }
        }
        // 获取当前表格所呈现出来的所有行数,即处理人需要合并的行数
        if(expandedRows){
          this.allRowNum += row.children.length
        }else {
          this.allRowNum -= row.children.length
        }
        // 调用合并列数的方法  0 为不合并
        this.setdates(arrObject)
      },
      /** 合并列数的方法 */
      setdates: function (arr) {
        console.log(arr, '合并列数方法中拿到的数组')
        // 转化数组,得到name的集合,如果name不存在,代表需要隐藏,这时候用22这个数字进行代替
        var kpiObjArr = []
        for (var i = 0, len = arr.length; i < len; i++) {
          if (arr[i] == 22) {
            kpiObjArr.push(22)
          } else {
            kpiObjArr.push(arr[i].name)
          }
        }
        console.log(kpiObjArr, 'kpiObjArrkpiObjArrkpiObjArrkpiObjArr')
        this.spanArrSumArr = [] // 清空处理分数、加减分说明等col合并数组
        // 获取处理分数、加减分说明等col合并数组
        for(let i =0 ; i < this.spanArrSum ; i++){
          if(i===0){
            this.spanArrSumArr.push(this.spanArrSum)
          }else{
            this.spanArrSumArr.push(0)
          }
        }
        this.allRowNumArr = [] // 清空处理人col合并数组
        // 获取处理人col合并数组
        for(let i =0 ; i < this.allRowNum ; i++){
          if(i===0){
            this.allRowNumArr.push(this.allRowNum)
          }else{
            this.allRowNumArr.push(0)
          }
        }
        // 获取最终的col合并集合
        this.spanArr = this.arrFunction(kpiObjArr,0)
      },
      /** 数组数据处理方法 */
      arrFunction(list,index){
          var a=0;
          var reg = new RegExp("[\\D]");
          for (var i=index;i<list.length;i++){
            if( reg.test(list[i])){
              a=i;
              break;
            }
          }
          var s=index;
          for (var i=index+1;i<list.length;i++){
            s++; //记录终结点
            if(list[a]==list[i]){
              list[i]=0;
            }else if(reg.test(list[i])&&list[a]!=list[i]){
              break;
            }
          }
          var count=0;
        list[index] = 1
          for (var i = index + 1; i < list.length; i++) {
            if (list[i] ==0) {
              count++;
              list[i] = 22;
              list[index+count] = 0;
              list[index]=count+1;
            }
            if(list[i]==22){
              list[i] = 99;
            }
          }
          if(s<list.length-1){
            list=this.arrFunction(list,s);
          }
          return list;
      }
    }
  }
</script>

<style lang="scss" scoped>
</style>

代码到此就完成了。

其中需要注意的就是row与col的合并,上面代码中,例入spanArr=[1,99,99,3,0,0]     99就代表row为0,col也为0,是被收起来的树节点的子节点,需要被隐藏掉;这种场景代表项目1为关闭,隐藏项目1下的子节点,项目2为打开,col需要合并为3,0,0,row都为1,0代表的是col此时不合并。

需要注意的第二点就是:当得到的人名组合为kpiObjArr=[a,22,22,b,b,b],如何将这个数组变为[1,99,99,3,0,0]这样的格式,22上述代码中已写过备注,代表被收起来的子节点,需要被隐藏。【kpiObjArr的场景非常多,这里再说几个例子 :

kpiObjArr=[a,22,22,a,a,a,b,b,b]          --------------------------------------------------spanArr=[4,0,0,0,99,99,3,0,0]

kpiObjArr=[a,22,22,a,22,22,b,22,22]------------------------------------------------------spanArr=[2,0,99,99,99,99,1,99,99]

需要找到规律,来将这个数组进行转化成功,最后再表格自带的合并方法中进行渲染,

下面补充一下,后台返回的tableData的数据格式(数据多,所以没有写在上面进行定义)

// 后台返回的表格数据
tableData: [
  {
    id: '1191670059258875904',
    isFixed: false,
    approverName1: '张三',
    approverName2: '王五',
    approverName3: '李四',
    approverDiffScoreDescribe1: '',
    approverDiffScoreDescribe2: '',
    approverDiffScoreDescribe3: '',
    score1: 100,
    score2: 100,
    score3: 100,
    kpiScore: 99,
    name: "王五",
    num: 2,
    objectName: "项目1",
    remarks: null,
    selfRating: null,
    timelong: null,
    weight: null,
    workProgress: null,
    workStatus: null,
    assesseDiffScoreDescribe: 'null',
    children:[
      {
        id: "11916698122256666",
        name: "王五",
        objectName: "工作项1",
        remarks: "垃圾数据22019-11-05周报:已完成",
        selfDiffScoreDescribe: null,
        selfRating: 100,
        timelong: 8,
        weight: 100,
        workProgress: 100,
        workStatus: "4",
        approverName1: '',
        approverName2: '',
        approverName3: '',
        approverDiffScoreDescribe1: '',
        approverDiffScoreDescribe2: '',
        approverDiffScoreDescribe3: '',
        score1: null,
        score2: null,
        score3: null,
      },
      {
        id: "2222222333322999999",
        name: "王五",
        objectName: "工作项2",
        remarks: "垃圾数据22019-11-05周报:已完成",
        selfDiffScoreDescribe: null,
        selfRating: 100,
        timelong: 8,
        weight: 100,
        workProgress: 100,
        workStatus: "4",
        approverName1: '张三',
        approverName2: '王五',
        approverName3: '李四',
        approverDiffScoreDescribe1: '',
        approverDiffScoreDescribe2: '',
        approverDiffScoreDescribe3: '',
        score1: 100,
        score2: 100,
        score3: 100,
      }
    ]
  },
  {
    id: '11916700592588759042',
    isFixed: false,
    approverName1: '张三',
    approverName2: '王五',
    approverName3: '李四',
    approverDiffScoreDescribe1: '',
    approverDiffScoreDescribe2: '',
    approverDiffScoreDescribe3: '',
    score1: 100,
    score2: 100,
    score3: 100,
    kpiScore: 88,
    name: "王五",
    num: 2,
    objectName: "项目2",
    remarks: null,
    selfRating: null,
    timelong: null,
    weight: null,
    workProgress: null,
    workStatus: null,
    assesseDiffScoreDescribe: 'null333',
    children:[
      {
        id: "119166981222566662",
        name: "王五",
        objectName: "工作项1",
        remarks: "垃圾数据22019-11-05周报:已完成",
        selfDiffScoreDescribe: null,
        selfRating: 100,
        timelong: 8,
        weight: 100,
        workProgress: 100,
        workStatus: "4",
        approverName1: '',
        approverName2: '',
        approverName3: '',
        approverDiffScoreDescribe1: '',
        approverDiffScoreDescribe2: '',
        approverDiffScoreDescribe3: '',
        score1: null,
        score2: null,
        score3: null,
      },
      {
        id: "22222223333229999992",
        name: "王五",
        objectName: "工作项2",
        remarks: "垃圾数据22019-11-05周报:已完成",
        selfDiffScoreDescribe: null,
        selfRating: 100,
        timelong: 8,
        weight: 100,
        workProgress: 100,
        workStatus: "4",
        approverName1: '张三',
        approverName2: '王五',
        approverName3: '李四',
        approverDiffScoreDescribe1: '',
        approverDiffScoreDescribe2: '',
        approverDiffScoreDescribe3: '',
        score1: 100,
        score2: 100,
        score3: 100,
      },
      {
        id: "00000000002",
        name: "王五",
        objectName: "工作项3",
        remarks: "垃圾数据22019-11-05周报:已完成",
        selfDiffScoreDescribe: null,
        selfRating: 100,
        timelong: 8,
        weight: 100,
        workProgress: 100,
        workStatus: "4",
        approverName1: '张三',
        approverName2: '王五',
        approverName3: '李四',
        approverDiffScoreDescribe1: '',
        approverDiffScoreDescribe2: '',
        approverDiffScoreDescribe3: '',
        score1: 100,
        score2: 100,
        score3: 100,
      }
    ]
  },
  {
    id: '11916700592588759042a',
    isFixed: false,
    approverName1: '张三',
    approverName2: '王五',
    approverName3: '李四',
    approverDiffScoreDescribe1: '',
    approverDiffScoreDescribe2: '',
    approverDiffScoreDescribe3: '',
    score1: 100,
    score2: 100,
    score3: 100,
    kpiScore: 100,
    name: "aaa兰",
    num: 2,
    objectName: "项目3",
    remarks: null,
    selfRating: null,
    timelong: null,
    weight: null,
    workProgress: null,
    workStatus: null,
    assesseDiffScoreDescribe: null,
    children:[
      {
        id: "119166981222566662a",
        name: "aaa兰",
        objectName: "工作项1",
        remarks: "垃圾数据22019-11-05周报:已完成",
        selfDiffScoreDescribe: null,
        selfRating: 100,
        timelong: 8,
        weight: 100,
        workProgress: 100,
        workStatus: "4",
        approverName1: '',
        approverName2: '',
        approverName3: '',
        approverDiffScoreDescribe1: '',
        approverDiffScoreDescribe2: '',
        approverDiffScoreDescribe3: '',
        score1: null,
        score2: null,
        score3: null,
      },

      {
        id: "00000000002a",
        name: "aaa兰",
        objectName: "工作项2",
        remarks: "垃圾数据22019-11-05周报:已完成",
        selfDiffScoreDescribe: null,
        selfRating: 100,
        timelong: 8,
        weight: 100,
        workProgress: 100,
        workStatus: "4",
        approverName1: '张三',
        approverName2: '王五',
        approverName3: '李四',
        approverDiffScoreDescribe1: '',
        approverDiffScoreDescribe2: '',
        approverDiffScoreDescribe3: '',
        score1: 100,
        score2: 100,
        score3: 100,
      }
    ]
  },
  {
    id: '9',
    isFixed: true,
    approverName1: '',
    approverName2: '',
    approverName3: '',
    approverDiffScoreDescribe1: '',
    approverDiffScoreDescribe2: '',
    approverDiffScoreDescribe3: '',
    score1: null,
    score2: null,
    score3: null,
    kpiScore: '',
    name: "",
    num: 2,
    objectName: "指标项目1",
    remarks: null,
    selfRating: null,
    timelong: null,
    weight: null,
    workProgress: null,
    workStatus: null,
    assesseDiffScoreDescribe: null,
    children:[
      {
        id: "99",
        approverName1: '张三',
        approverName2: '王五',
        approverName3: '李四',
        approverDiffScoreDescribe1: '',
        approverDiffScoreDescribe2: '',
        approverDiffScoreDescribe3: '',
        score1: 50,
        score2: 40,
        score3: 60,
        name: "",
        objectName: "指标1",
        remarks: "垃圾数据22019-11-05周报:已完成",
        selfDiffScoreDescribe: null,
        selfRating: 100,
        timelong: 8,
        weight: 100,
        workProgress: 100,
        workStatus: "4",
        kpiScore: '',
      },
      {
        id: "999",
        approverName1: '张三',
        approverName2: '王五',
        approverName3: '李四',
        approverDiffScoreDescribe1: '',
        approverDiffScoreDescribe2: '',
        approverDiffScoreDescribe3: '',
        score1: 101,
        score2: 102,
        score3: 103,
        name: "",
        objectName: "指标2",
        remarks: "垃圾数据22019-11-05周报:已完成",
        selfDiffScoreDescribe: null,
        selfRating: 100,
        timelong: 8,
        weight: 100,
        workProgress: 100,
        workStatus: "4",
        kpiScore: '',
      }
    ]
  },
  {
    id: '91',
    isFixed: true,
    approverName1: '',
    approverName2: '',
    approverName3: '',
    approverDiffScoreDescribe1: '',
    approverDiffScoreDescribe2: '',
    approverDiffScoreDescribe3: '',
    score1: null,
    score2: null,
    score3: null,
    kpiScore: '',
    name: "",
    num: 2,
    objectName: "指标项目2",
    remarks: null,
    selfRating: null,
    timelong: null,
    weight: null,
    workProgress: null,
    workStatus: null,
    assesseDiffScoreDescribe: null,
    children:[
      {
        id: "991",
        approverName1: '张三',
        approverName2: '王五',
        approverName3: '李四',
        approverDiffScoreDescribe1: '',
        approverDiffScoreDescribe2: '',
        approverDiffScoreDescribe3: '',
        score1: 50,
        score2: 40,
        score3: 60,
        name: "",
        objectName: "指标项1",
        remarks: "垃圾数据22019-11-05周报:已完成",
        selfDiffScoreDescribe: null,
        selfRating: 100,
        timelong: 8,
        weight: 100,
        workProgress: 100,
        workStatus: "4",
        kpiScore: '',
      },
      {
        id: "9991",
        approverName1: '张三',
        approverName2: '王五',
        approverName3: '李四',
        approverDiffScoreDescribe1: '',
        approverDiffScoreDescribe2: '',
        approverDiffScoreDescribe3: '',
        score1: 101,
        score2: 102,
        score3: 103,
        name: "",
        objectName: "指标项2",
        remarks: "垃圾数据22019-11-05周报:已完成",
        selfDiffScoreDescribe: null,
        selfRating: 100,
        timelong: 8,
        weight: 100,
        workProgress: 100,
        workStatus: "4",
        kpiScore: '',
      }
    ]
  },
],

 

<think> 根据用户问题,需要解决ElementUIel-table开启多选框时,当已选中的数据在第二页(或非第一页)时,第一页的表头全选框没有显示为部分选中状态(即没有显示横杠)的问题。 原因分析:ElementUI的表格多选框状态(全选、部分选、未选)是由内部根据当前页的选中状态和所有数据的选中状态来决定的。当已选中的数据不在当前页(比如在第二页)时,第一页没有选中的数据,因此表头全选框会显示为未选中状态,但实际上已有数据被选中(在第二页),所以应该显示部分选中状态(横杠)。 解决方案:我们需要在数据回显后,手动更表头全选框的状态。ElementUI并没有直接提供API来更这个状态,但可以通过以下思路: 1. 在数据回显时,将已选中的行通过toggleRowSelection方法设置为选中(即使这些行不在当前页,也要调用,但注意不在当前页的行不会影响当前页的显示)。 2. 关键点:我们需要在每次翻页或者数据变化时,检查当前页的选中状态,然后更表头全选框的状态。 但是,ElementUI的表头全选框状态是由内部计算的,我们无法直接修改。因此,我们可以采用另一种方法:监听表格的select-all事件,并在每次选择变化时,手动设置表头全选框的状态。然而,这比较复杂。 另一种思路:利用ElementUI的header-cell-class-name属性,通过动态改变表头全选框的样式来控制显示。但这样需要修改内部样式,不够优雅。 实际上,ElementUI提供了一个方法:doLayout。但这个方法主要是重渲染表格,并不解决我们的问题。 经过查阅,更适的做法是:在数据回显后,我们手动设置当前页的选中状态,并且通过一个计算属性或者变量来记录总的选中数据。然后,通过设置表格的select-all事件,在事件中根据总选中数据的状态来更表头全选框的状态。 然而,ElementUI已经内置了处理,只是它只根据当前页的选中情况来显示。所以当有跨页选中时,表头全选框应该显示为横杠(即不确定状态)。但为什么没有显示?可能是因为我们没有正确设置。 实际上,ElementUI要求我们在设置选中行的时候,需要确保当前页的选中状态正确,并且它内部会计算总数据中是否有选中,如果有选中且不是全部选中,则表头全选框为横杠。因此,问题在于我们可能没有在数据更后正确触发更。 在用户提供的引用[1]中,他们使用了$nextTick来确保在数据渲染后设置选中。但是,当数据在第二页时,第一页并没有这些数据,所以设置选中不会对第一页产生效果。因此,我们需要在翻页到第二页时再次设置选中,但这并不能解决第一页表头全选框的状态问题。 解决方案:我们可以通过计算总选中条数和当前页的选中情况,然后手动设置表头全选框的状态。具体步骤: 1. 给表格添加一个ref,例如"multipleTable" 2. 在数据回显后,我们不仅要设置当前页的选中状态(如果当前页有选中的行),还需要更表头全选框的状态。 3. 我们可以通过监听表格的current-change事件(分页切换)和select事件,来更表头全选框的状态。 但是,ElementUI并没有提供直接设置表头全选框状态的API。我们可以通过操作DOM来修改,但这样不推荐。 另一种方案:使用header-cell-class-name属性,当有数据被选中且不在当前页时,给表头全选框的单元格添加一个类,然后通过CSS来设置背景图片(横杠)。但这样需要知道ElementUI内部的结构,且不稳定。 实际上,ElementUI官方有一个issue讨论过这个问题:https://github.com/ElemeFE/element/issues/13963 其中提到,可以通过设置indeterminate属性来实现。我们可以这样: 在el-table-column type="selection"上,有一个属性:indeterminate="indeterminate",然后我们在数据中定义一个indeterminate变量,通过计算总选中条数(大于0且小于总条数)来设置这个变量。 但是,查看文档,el-table-column并没有indeterminate这个属性。所以这个方法不可行。 最终,我们可能需要通过自定义表头多选框来实现。 步骤: 1. 自定义表头多选框,使用slot="header"来自定义表头。 2. 在自定义表头中,使用一个el-checkbox,然后通过计算属性判断当前是否是部分选中状态(即indeterminate状态)。 3. 这样我们就可以完全控制表头多选框的显示。 具体实现: 修改el-table-column的type="selection"部分,使用自定义表头: ```html <el-table-column type="selection" width="55"> <template slot="header"> <el-checkbox v-model="checkAll" :indeterminate="isIndeterminate" @change="handleCheckAllChange" ></el-checkbox> </template> <template slot-scope="scope"> <el-checkbox v-model="scope.row.checked" @change="handleSelectChange(scope.row)" ></el-checkbox> </template> </el-table-column> ``` 然后,在数据中定义: checkAll: false, // 全选状态 isIndeterminate: false, // 是否部分选中 通过监听选中数据的变化,更checkAll和isIndeterminate: - 当选中数据为0时,checkAll为false,isIndeterminate为false。 - 当选中数据大于0且小于总数据条数时,checkAll为false,isIndeterminate为true。 - 当选中数据等于总数据条数时,checkAll为true,isIndeterminate为false。 这样,无论选中的数据在哪一页,表头的多选框都能正确显示状态。 但是,这种方法需要我们自己处理全选和反选逻辑(包括分页全选和跨页全选),并且需要给每一行数据添加一个checked属性(或者使用一个数组来维护选中状态)。这相对于使用el-table内置的多选功能来说,需要更多的工作。 因此,如果项目允许,可以采用自定义的方式。否则,我们可能需要等待ElementUI修复这个问题(目前似乎没有直接的方法)。 但是,在ElementUI的官方文档中,有一个属性:select-on-indeterminate。该属性的默认值为true,表示当表头全选框处于不确定状态(横杠)时,点击会选中所有行。我们可以尝试设置这个属性为false,但并不能解决显示问题。 结论:如果使用ElementUI内置的多选功能,目前无法直接解决跨页时第一页表头全选框显示不确定状态的问题。因为内置的计算仅基于当前页和所有行的选中状态,但不在当前页的选中行不会影响当前页的表头全选框状态(因为当前页没有选中行,所以表头全选框会显示为未选中,而不会显示横杠)。 因此,建议采用自定义表头多选框的方式来完全控制多选状态。虽然复杂,但是可行。 下面给出一个简化的实现步骤: 1. 修改表格列定义,使用自定义表头和自定义列模板。 2. 维护一个选中数组selectedRows(存放所有选中的行数据)或者selectedIds(存放选中行的id)。 3. 计算checkAll和isIndeterminate: checkAll = selectedRows.length === total && total>0 isIndeterminate = selectedRows.length>0 && selectedRows.length<total 4. 处理表头全选事件:当点击表头全选时,如果当前是未选中或部分选中,则选中所有页的行;如果当前是选中状态,则取消所有选中。 5. 处理行内多选框事件:当某行的多选框被点击时,更selectedRows。 注意:全选所有页需要获取所有数据(如果数据是分页加载的,则需要先获取所有数据,或者通过后端接口全选)。因此,全选功能可能涉及性能问题。 由于用户的需求是数据回显,所以我们需要在回显时,将已选中的行加入selectedRows,并更checkAll和isIndeterminate。这样,无论在哪一页,表头的多选框状态都是正确的。 示例代码: HTML部分: ```html <el-table :data="currentPageData" @selection-change="handleSelectionChange"> <el-table-column width="55"> <template slot="header"> <el-checkbox v-model="checkAll" :indeterminate="isIndeterminate" @change="handleCheckAllChange" ></el-checkbox> </template> <template slot-scope="scope"> <el-checkbox v-model="selectedIds[scope.row.id]" @change="handleSelectChange(scope.row)" ></el-checkbox> </template> </el-table-column> <!-- 其他列 --> </el-table> ``` JS部分: ```javascript data() { return { checkAll: false, isIndeterminate: false, selectedIds: {}, // 用于记录选中状态,key为id,value为true/false // 或者用数组 selectedIds: [],但数组在查找时效率较低,所以用对象 total: 0, // 总数据条数 currentPageData: [] // 当前页数据 }; }, computed: { // 可以计算选中的数量 selectedCount() { return Object.keys(this.selectedIds).filter(id => this.selectedIds[id]).length; } }, watch: { selectedCount(newVal) { this.isIndeterminate = newVal > 0 && newVal < this.total; this.checkAll = newVal === this.total && this.total>0; } }, methods: { handleCheckAllChange(val) { // 全选或取消全选 // 这里需要处理:全选当前页还是全选所有页?根据需求,一般是全选所有页。 // 但是,如果数据量很大,全选所有页需要从后端获取所有数据,可能不现实。 // 所以,这里需要根据具体情况实现,比如调用全选接口,或者在前端处理(如果数据已全部加载) // 如果数据已全部加载,我们可以遍历所有数据,设置selectedIds[item.id]=val // 如果分页加载,则可能需要调用接口,然后更selectedIds(只记录选中的id) // 这里简化处理:假设数据已全部加载在allData中 if (val) { // 全选 this.allData.forEach(item => { this.$set(this.selectedIds, item.id, true); }); } else { // 取消全选 this.allData.forEach(item => { this.$set(this.selectedIds, item.id, false); }); } }, handleSelectChange(row) { // 行选中状态改变,更selectedIds this.$set(this.selectedIds, row.id, !this.selectedIds[row.id]); }, handleSelectionChange(selection) { // 这个事件在表格内置的selection变化时触发,但因为我们自定义了,所以可能不需要 // 但如果我们同时使用内置的多选功能(比如通过toggleRowSelection),那么这里可以同步 // 不过,既然自定义了,这个事件可以不用 } } ``` 注意:上述代码中,我们使用了el-table,但是并没有使用其内置的多选功能(因为我们自定义了多选框列)。所以,我们需要自己维护选中状态。 另外,数据回显时,我们需要初始化selectedIds,将已选中的id在selectedIds中设置为true。 这种自定义方式可以完全控制表头全选框的状态,包括跨页选中的情况。但是,它需要自己处理全选逻辑,并且可能和分页加载数据有冲突(全选所有页需要获取所有数据),所以需要根据项目实际情况调整。 如果项目要求不能一次性加载所有数据,那么全选功能需要后端支持(比如传入所有id进行批量选中,或者通过一个字段标识为全选)。这超出了前端控制的范畴。 因此,如果数据量很大,且需要分页加载,那么全选功能通常只针对当前页(ElementUI默认如此),或者通过一个特殊操作(如点击全选按钮,弹窗提示是否全选所有页)来实现。 综上所述,针对用户的问题,如果希望正确显示表头全选框的状态(跨页选中时显示横杠),建议使用自定义表头多选框的方式,并自行维护选中状态。这样虽然复杂,但是可以满足需求。 如果用户不想这么复杂,也可以忽略表头全选框的状态,或者通过其他方式提示用户有跨页选中(比如在表格外部显示“已选择X条数据”)。 根据用户引用的内容,他们使用了toggleRowSelection来回显选中,但这种方法无法改变表头全选框的状态。因此,在无法修改ElementUI源码的情况下,自定义表头是推荐的解决方案。 相关引用: [^1]: 引用中使用了toggleRowSelection来回显选中,但只针对当前页的数据。对于不在当前页的数据,虽然设置了,但不会影响当前页的显示。 [^2]: 引用的内容是关于动态控制列的显示和隐藏,与多选框状态无关。 因此,针对用户的问题,给出自定义表头多选框的解决方案。 相关问题: 1. 如何实现ElementUI表格跨页多选? 2. ElementUI表格全选功能如何实现(包括所有页)? 3. 自定义ElementUI表格表头多选框的步骤是什么? </think> 在 ElementUIel-table 中,当多选框数据回显时遇到「已选中数据在第二页,第一页表头全选框未显示选中状态」的问题,可通过以下方案解决: ### 解决方案 ```html <template> <el-table ref="multipleTable" :data="tableData" @selection-change="handleSelectionChange" @select-all="handleSelectAll" > <el-table-column type="selection" width="55"></el-table-column> <!-- 其他列 --> </el-table> </template> <script> export default { data() { return { tableData: [], // 当前页数据 selectedRows: [], // 所有已选中的行数据(跨页存储) }; }, mounted() { this.loadData(); }, methods: { // 加载数据并回显选中状态 async loadData() { // 1. 获取当前页数据(模拟请求) this.tableData = await fetchPageData(); // 2. 在下一轮DOM更后回显选中状态 this.$nextTick(() => { this.tableData.forEach(row => { // 3. 判断当前行是否在已选中集中 const isSelected = this.selectedRows.some( selected => selected.id === row.id ); // 4. 通过ref操作table组件设置选中状态 this.$refs.multipleTable.toggleRowSelection(row, isSelected); }); }); }, // 监听全选事件(关键:更跨页选中状态) handleSelectAll(selection) { if (selection.length > 0) { // 全选当前页时并选中项 this.selectedRows = [ ...new Set([...this.selectedRows, ...selection]) ]; } else { // 取消全选时移除当前页所有项 const currentPageIds = this.tableData.map(row => row.id); this.selectedRows = this.selectedRows.filter( row => !currentPageIds.includes(row.id) ); } }, // 监听单选变化(更选中集) handleSelectionChange(selection) { this.selectedRows = selection; } } }; </script> ``` ### 核心原理说明 1. **跨页状态存储** 使用 `selectedRows` 独立存储所有已选项(不受分页影响),而非依赖 el-table 的临时状态[^1]。 2. **DOM 更时机** 在 `$nextTick` 中保证 DOM 渲染完成后再操作选中状态,避免找不到 DOM 元素[^1]。 3. **全选事件处理** 通过 `@select-all` 监听全选操作: - 全选时:将当前页数据并到 `selectedRows` - 取消全选时:从 `selectedRows` 移除当前页数据 (关键解决第一页全选框状态问题) 4. **精准回显逻辑** 遍历当前页数据,用 `toggleRowSelection(row, true)` 对已选项设置为选中状态[^2]。 ### 补充优化建议 1. **分页切换处理** 在分页切换事件中重调用 `loadData()` 方法: ```javascript handlePageChange(page) { currentPage = page; this.loadData(); } ``` 2. **性能优化** 对于大数据量使用 `row-key` 和唯一标识(如 id): ```html <el-table row-key="id" :data="tableData"> <!-- 列定义 --> </el-table> ``` 3. **后端支持方案** 若需后端记录选中状态: ```javascript async loadSelectionState() { const { selectedIds } = await api.getSelectedIds(); this.selectedRows = this.tableData.filter( row => selectedIds.includes(row.id) ); } ``` > **注意**:ElementUI 的表头全选框状态由当前页选中状态决定。上述方案通过独立维护跨页选中数据,并在每次分页时同步状态,确保 UI 正确显示[^1][^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值