我写技术文章没那么多废话,直接上代码:

1.效果预览:

springboot vue element 动态表头 elementui动态多级表头_JSON


其中,superColumns与columns是根据后端返回的数据决定的,是动态的。

2.在vue里面的代码:

<el-table
          ref="multipleTableRef"
          v-loading="state.loading"
          :data="list"
          cell-style="text-align: center"
          header-cell-style="text-align: center"
        >
        
          <el-table-column label="学院" prop="gradeName" min-width="180" show-overflow-tooltip>
            <template #default="{ row }">
              <span>{{row.collegeName?row.collegeName:'没有学院'}}</span>
            </template>
          </el-table-column>
          <el-table-column :label="superColumn == '总计'?superColumn:superColumn+'级'" v-for="(superColumn,idx) in state.superColumns" :key="idx">
            <el-table-column
              :label="superColumn == '总计'?'总学生':'年级学生'"
              prop="data"
              min-width="120"
              :show-overflow-tooltip="true"
            >
              <template #default="{ row }">
                <span>{{ row[superColumn].total?row[superColumn].total:0 }}</span>
              </template>
            </el-table-column>
            <el-table-column label="困难学生">
              <el-table-column v-for="(column,idx) in state.columns" :key="idx"
                :label="getColumnName(column)"
                :prop="column"
                min-width="120"
                :show-overflow-tooltip="true"
              >
                <template #default="{ row }">
                  <span>{{ row[superColumn][column]?row[superColumn][column]:0 }}</span>
                </template>
              </el-table-column>
              <el-table-column
                label="合计"
                prop="data"
                min-width="120"
                :show-overflow-tooltip="true"
              >
                <template #default="{ row }">
                  <span>{{ row[superColumn].totalApplyQty }}</span>
                </template>
              </el-table-column>
            </el-table-column>
            <el-table-column
              v-if="superColumn != '总计'"
              label="困难学生比例"
              prop="data"
              min-width="120"
              :show-overflow-tooltip="true"
            >
              <template #default="{ row }">
                <span>{{ row[superColumn].totalApplyQty?(row[superColumn].totalApplyQty * 100 / row[superColumn].total).toFixed(2):"0.00" }}%</span>
              </template>
            </el-table-column>
            <el-table-column label="困难学生比例" v-if="superColumn == '总计'">
              <el-table-column v-for="(column,idx) in state.columns" :key="idx"
                :label="getColumnName(column)"
                :prop="column"
                min-width="120"
                :show-overflow-tooltip="true"
              >
                <template #default="{ row }">
                  <span>{{ row[superColumn].total?(row[superColumn][column]*100 / row[superColumn].total).toFixed(2):"0.00" }}%</span>
                </template>
              </el-table-column>
              <el-table-column
                label="合计"
                prop="data"
                min-width="120"
                :show-overflow-tooltip="true"
              >
                <template #default="{ row }">
                  <span>{{ row[superColumn].total?(row[superColumn].totalApplyQty *100 / row[superColumn].total).toFixed(2):"0.00" }}%</span>
                </template>
              </el-table-column>
            </el-table-column>
          </el-table-column>
        </el-table>

3.ts里面代码:

// 列表 + 模拟数据(后端返回的数据结构)
const list = ref<any>([
        {
            "title": "2020",
            "data": [
                {
                    "collegeNo": "0207",
                    "collegeName": "艺术学院",
                    "total": 1045,
                    "totalApplyQty": 1,
                    "KNDJ001": 0,
                    "KNDJ002": 0,
                    "KNDJ003": 0
                },
                {
                    "collegeNo": "0206",
                    "collegeName": "智能制造与能源工程学院",
                    "total": 1364,
                    "totalApplyQty": 1,
                    "KNDJ001": 0,
                    "KNDJ002": 0,
                    "KNDJ003": 0
                }
            ]
        },
        {
            "title": "2019",
            "data": [
                {
                    "collegeNo": "0206",
                    "collegeName": "智能制造与能源工程学院",
                    "total": 1364,
                    "totalApplyQty": 4,
                    "KNDJ001": 0,
                    "KNDJ002": 0,
                    "KNDJ003": 0
                }
            ]
        },
        {
            "title": "2018",
            "data": []
        },
        {
            "title": "总计",
            "data": [
                {
                    "collegeNo": "0206",
                    "collegeName": "智能制造与能源工程学院",
                    "total": 1364,
                    "totalApplyQty": 5,
                    "KNDJ001": 0,
                    "KNDJ002": 0,
                    "KNDJ003": 0
                },
                {
                    "collegeNo": "0207",
                    "collegeName": "艺术学院",
                    "total": 1045,
                    "totalApplyQty": 1,
                    "KNDJ001": 0,
                    "KNDJ002": 0,
                    "KNDJ003": 0
                }
            ]
        }
    ])

其中,KNDJ001、KNDJ002、KNDJ003、title是会变动的,就像KNDJ001、KNDJ002、KNDJ003其实是困难生的认定等级的编码,这次是KNDJ001、KNDJ002、KNDJ003,下次可能是张三李四王五。最后从代码库拉取的数据与KNDJ001、KNDJ002、KNDJ003等匹配获取它们代表的含义如:特别困难、困难、一般困难
4.对接口返回的数据处理 ,对第3条中的list 处理:

const state:any = reactive({
  columns:[],//动态表头的名
  superColumns:[],//动态表头的名 最高级
  kndjList:[],//困难等级数组
})
//处理学生处的数据
const dealXSCList = ( list :any) => {
  list.reverse();//为什么先反转,是因为返回数据都会有“总计”这一条数据,且它是数据最多的,以它为处理模板
  state.columns = [];
  state.superColumns = [];
  let arr:any = [];
  let templateData:any = {};//记录用来填充没数据的模板
  for(let i = 0; i < list.length ; i++){
    let item = list[i].data;
    if(state.superColumns.indexOf(list[i].title) < 0)state.superColumns.push(list[i].title);
    if(item.length > 0){
      for ( let j = 0; j < item.length ; j++){
        if(!arr[j]) {
          arr[j]= {};
          arr[j].collegeName = item[j].collegeName;
          arr[j][list[i].title] = item[j];
          templateData = cloneDeep(item[j]);
        }else{
          for(let n = 0;  n < arr.length ; n++){
            arr[n][list[i].title] = cloneDeep(clearDefaultData(templateData));
            if(arr[n].collegeName == item[j].collegeName){
              arr[n][list[i].title] = item[j];
            }
          }
        }
        for(let key in item[j]){
          if(key != 'collegeNo' && key != 'collegeName'){
            if(key != 'total' && key != 'totalApplyQty' && key != 'gradeName'){
              if(state.columns.indexOf(key) < 0) state.columns.push(key);
            }
          }
        }
      }
    }else{//缺的数据补0
      for(let n = 0; n < arr.length ; n++){
        arr[n][list[i].title] = cloneDeep(clearDefaultData(templateData));
      }
    }
  }
  console.log("学生处的数据",state.superColumns,state.columns,arr);
  //所有的数据总计
  let firstData:any = {};
  firstData.collegeName = "总计";
  for(let i = 0; i < arr.length ; i++){
    for(let key in arr[i]){
      if(key != 'collegeName'){
        if(!firstData[key])firstData[key]={};
        for(let ky in arr[i][key]){
          if(ky != 'collegeName' && ky != 'collegeNo'){
            if(!firstData[key][ky]) firstData[key][ky] = 0;
            firstData[key][ky] += arr[i][key][ky]*1
          }
        }
      }
    }
  }
  state.superColumns.sort().reverse();
  if(arr.length == 0){//当没有长度时候 清空是防止报错
    state.columns = [];
    state.superColumns = [];
  }else{
    arr.reverse();
    arr.unshift(firstData)
  }
  console.log("学生处的数据2",state.superColumns,state.columns,arr);
  return arr
}
//克隆数据
const cloneDeep = (row:any) =>{
  if(!row) return;
  return JSON.parse(JSON.stringify(row));
}
//把默认数据置为0
const clearDefaultData = ( row :any)=>{
  if(!row) return;
  for(let key in row){
    if(key != 'collegeName' && key != 'collegeNo' && key != 'gradeName'){
      row[key] = 0;
    }else{
      row[key] = "";
    }
  }
  return row
}  

//获得表头名
const getColumnName = (column: any)=>{
  let name = "无";
  if(column && state.kndjList.length > 0){
    state.kndjList.forEach((item:any)=>{
      if(item.dataCode == column){
        name = item.dataName;
      }
    })
  }
  return name;
}

其中,superColumns记录的是顶部的动态表头,columns 记录的是困难等级的动态表头(后端可能设置)。

处理的结果:

springboot vue element 动态表头 elementui动态多级表头_JSON_02


为什么把数据像总计、2020、2019、2018处理成key?是方便数据循环时,通过 row[superColumn]“ 可以拿到对应年的数据,再通过”row[superColumn].total“或者“row[superColumn][column] ”拿到对应的具体值。

处理的数据大概结构:

//list被处理后的结构:
[
	{
		”总计“:{
			"collegeNo":"0207",
			"collegeName":"艺术学院",
			"total":1045,
			"totalApplyQty":1,
			"KNDJ001":0,
			"KNDJ002":0,
			"KNDJ003":0
		},
		”2020“:{
			"collegeNo":"",
			"collegeName":"",
			"total":1045,
			"totalApplyQty":1,
			"KNDJ001":0,
			"KNDJ002":0,
			"KNDJ003":0
		},
		”2019“:{
			"collegeNo":"",
			"collegeName":"",
			"total":1045,
			"totalApplyQty":1,
			"KNDJ001":0,
			"KNDJ002":0,
			"KNDJ003":0
		},
	},
	{
		”总计“:{
			"collegeNo":"0206",
			"collegeName":"计算机学院",
			"total":1045,
			"totalApplyQty":1,
			"KNDJ001":0,
			"KNDJ002":0,
			"KNDJ003":0
		},
		”2020“:{
			"collegeNo":"",
			"collegeName":"",
			"total":1045,
			"totalApplyQty":1,
			"KNDJ001":0,
			"KNDJ002":0,
			"KNDJ003":0
		},
		”2019“:{
			"collegeNo":"",
			"collegeName":"",
			"total":1045,
			"totalApplyQty":1,
			"KNDJ001":0,
			"KNDJ002":0,
			"KNDJ003":0
		},
	},
]

5.最终的项目效果:

springboot vue element 动态表头 elementui动态多级表头_vue.js_03


如果不处理数据排序与数据补0,核心逻辑dealXSCList 代码可以20行搞定