ElementUI Table 动态表头,动态数据,动态列 电脑版发表于:2024/5/13 11:50 表格显示的列不确定的时候就需要使用动态的方式,而不是预先绑定好。 [TOC] ## 前端的实现 #### 数据源 对于前端来说数据源和前面的一样,没什么区别,反正就是提供一下数据 ``` const data = [ { name: 'John Doe', age: 30, city: 'New York' }, { name: 'Jane Smith', age: 25, city: 'London' }, { name: 'Michael Jones', age: 40, city: 'Paris' } ]; ``` #### 动态表头 表头就不能写死了,需要绑定的来,相当于列就是动态的显示的,不是预先绑定好的 ``` const columns = [ { prop: 'name', label: '姓名' }, { prop: 'age', label: '年龄' }, { prop: 'city', label: '城市' } ]; ``` 然后这样绑定上去就可以动态的渲染出来表头了 ``` <el-table :data="data" :columns="columns"></el-table> ``` 上面那个不好写一些额外的列,比如还有一些固定的列和编辑列这种,所以可以把需要动态显示的列循环出来 ``` <el-table :data="state.tableData" v-loading="state.tableLoading" :header-cell-style="{ background: '#f8f8f9' }" style="width: 100%;margin-top:-10px"> <el-table-column prop="className" label="班级" show-overflow-tooltip min-width="219" /> <el-table-column prop="studentCount" sortable label="学生数" align="center" min-width="119" /> <!-- 循环出来需要动态显示的列 --> <el-table-column :label="item.label" min-width="139" :prop="item.prop" v-bind:key="index" v-for="(item, index) in state.dynamicColumns"> </el-table-column> <el-table-column header-align="center" align="center" label="操作" width="90px" fixed="right"> <template #default="scope"> <el-link :underline="false" @click="showDetails(scope.row)" type="primary">详情</el-link> </template> </el-table-column> </el-table> ``` ## 后端的实现 需要提供动态的数据源和动态的表头,c# 可以使用ExpandoObject 构建动态类型 #### 后台需要的相关实体 动态表头需要的实体 ``` public class ElementTableColumn { public string prop { get; set; } public string label { get; set; } } ``` 接口需要返回的实体 ``` public class ElementTableDynamic { /// <summary> /// 动态表头 /// </summary> public List<ElementTableColumn> Columns { get; set; } /// <summary> /// 动态数据 /// </summary> public List<ExpandoObject> Data { get; set; } } ``` #### 动态构建数据的逻辑 这里主要贴的是动态构建数据的逻辑,具体的业务逻辑需要换成自己的 ``` public ElementTableDynamic ClassMonthTaskReport(ClassStatisticsParams _params) { // 这里边是需要显示动态数据的 List<Tasks> tasks = BaseDal.Db.Queryable<Tasks>().OrderBy(a => a.OrderNum).OrderBy(a => a.CreateTime).Take(_params.TakeCount).ToList(); // 其他逻辑..... // 构建动态的表头 List<ElementTableColumn> elementTableColumns = new List<ElementTableColumn>(); foreach (Tasks taskItem in tasks) { ElementTableColumn elementTableColumn = new ElementTableColumn(); // elementTableColumn.label = taskItem.TaskName; // 列名显示这个短名称吧 elementTableColumn.label = taskItem.ReportName; // 动态列名,用id来拼一个名称,显示的可能会随时调整(当然也可以在数据库单独创建一列来存储一个列名,反正列都是动态的一般来说都没必要) elementTableColumn.prop = "completeShow" + taskItem.ID; elementTableColumns.Add(elementTableColumn); } List<ExpandoObject> expandoObjectList = new List<ExpandoObject>(); // 构建动态数据 foreach (VClassTeam item in vClassTeams) { //创建动态类型。表格这些需要动态显示 dynamic expandoObjectItem = new ExpandoObject(); // 转化成键值对方便动态赋值,这样给键值对赋值就会自动给expandoObjectItem赋值 var expandoDict = (IDictionary<string, object>)expandoObjectItem; // 固定列的数据 expandoDict["levelID"] = item.LevelID; expandoDict["gradeID"] = item.GradeID; expandoDict["classId"] = item.ID; expandoDict["className"] = item.ClassName; expandoDict["studentCount"] = studentClassList.Count(a => a.ClassID == item.ID); // 动态数据赋值 foreach (Tasks taskItem in tasks) { List<StuTaskDetail> stuTaskDetail_Item = stuTaskDetails.Where(a => a.ClassId == item.ID && a.TaskID == taskItem.ID).ToList(); // 给需要动态显示的列提供值(具体提供值的算法根据自己的业务逻辑来) expandoDict["completeShow" + taskItem.ID] = stuTaskDetail_Item.GroupBy(a => a.ClassId).Count() + "/" + stuTaskDetail_Item.Count(); } expandoObjectList.Add(expandoObjectItem); } ElementTableDynamic elementTableDynamic = new ElementTableDynamic(); elementTableDynamic.Columns = elementTableColumns; elementTableDynamic.Data = expandoObjectList; return elementTableDynamic; } ``` #### 接口返回的数据格式如下 ![](https://img.tnblog.net/arcimg/aojiancc2/8cb2f604d75f4731aa9948349fa1c60d.jpg) ## 前端需要复杂一些的模板解析 当数据不是简单的直接绑定一个列的时候,比如需要点击查询详情等,还需要提供详情的数据 比如前端需要这样绑定数据 ``` <el-table-column prop="managerOfficeMeetingCount" align="center" label="总经办会议" min-width="109"> <template #default="scope"> <span class="canclick" @click="showMonthDetails(scope.row.managerOfficeMeetingDetails, '总经办会议', scope.row.managerOfficeMeetingCount)">{{ scope.row.managerOfficeMeetingShow }}</span> </template> </el-table-column> ``` #### 后端 这样后端就还需要多提供几个数据,详情数据:managerOfficeMeetingDetails,条数:managerOfficeMeetingCount 等,后台大概的核心代码如下: ``` // 动态数据赋值 foreach (Tasks taskItem in tasks) { List<StuTaskDetail> stuTaskDetail_Item = stuTaskDetails.Where(a => a.ClassId == item.ID && a.TaskID == taskItem.ID).ToList(); // 给需要动态显示的列提供值(具体提供值的算法根据自己的业务逻辑来) expandoDict["completeShow" + taskItem.ID] = stuTaskDetail_Item.GroupBy(a => a.ClassId).Count() + "/" + stuTaskDetail_Item.Count(); expandoDict["completeCount" + taskItem.ID] = groupTasks_Item.GroupBy(a => a.ClassID).Count(); expandoDict["completeDetails" + taskItem.ID] = ProcGroupTasksDetails(item, schoolClassDtos, groupTasks_Item); } ``` 动态构建表头的时候就在加一个字段,方便前端解析的时候去取对应的数据 ``` // 构建动态的表头与属性对应的数据 List<ElementTableColumn> elementTableColumns = new List<ElementTableColumn>(); foreach (Tasks taskItem in tasks) { ElementTableColumn elementTableColumn = new ElementTableColumn(); elementTableColumn.label = taskItem.ReportName; elementTableColumn.prop = "completeShow" + taskItem.ID; // 多加一个字段,方便前端解析的时候去取 elementTableColumn.taskId = taskItem.ID+""; elementTableColumns.Add(elementTableColumn); } ``` ##### 这样修改后,后端返回的数据结构如下: 表头的数据结构: ![](https://img.tnblog.net/arcimg/aojiancc2/5f31fb162052485eb4c7a083735353a6.png) 具体数据的结构: ![](https://img.tnblog.net/arcimg/aojiancc2/b790d33321154cc0bc02a6fd8ffcdd2a.png) #### 前端 这种情况下详情数据前端解析的时候也要动态的来了,不能写死,要根据那个动态id来拼接出来取数据 ``` <!-- 循环出来需要动态显示的列 --> <el-table-column :label="item.label" min-width="139" align="center" :prop="item.prop" v-bind:key="index" v-for="(item, index) in state.dynamicColumns"> <template #default="scope"> <span class="canclick" @click="showMonthDetails(scope.row['completeDetails'+item.taskId], item.label,scope.row['completeCount'+item.taskId])">{{ scope.row[item.prop] }}</span> </template> </el-table-column> ``` ## 需要多级表头,合并表头的 效果如下: ![](https://img.tnblog.net/arcimg/aojiancc2/5595d1396fa5417c94d2d5d9032b2ba0.png) ##### element ui的多级表头格式如下,相当于就是有嵌套效果的 ``` <el-table-column label="地址"> <el-table-column prop="province" label="省份" width="120"> </el-table-column> <el-table-column prop="city" label="市区" width="120"> </el-table-column> </el-table-column> ``` ##### 后台表头的结构就需要变一下了,需要有一个子级 ``` public class ElementTableColumn { public string prop { get; set; } public string label { get; set; } public string taskId { get; set; } /// <summary> /// 可能有多级表头的情况 /// </summary> public List<ElementTableColumn> subTableColumn { get; set; } } ``` ##### 需要有子级的情况,后台构建的时候就要多添加一级 ``` // 构建动态的表头与属性对应的数据 List<ElementTableColumn> elementTableColumns = new List<ElementTableColumn>(); foreach (Tasks taskItem in tasks) { // 班级数量和具体的完成数量还要合并成一个单元来显示 ElementTableColumn elementTableColumnFather = new ElementTableColumn(); elementTableColumnFather.label = taskItem.ReportName; List<ElementTableColumn> subTableColumn = new List<ElementTableColumn>(); // 2024-5-29,说某个任务完成的班级数量单独作为一列显示出来,那么在加一列吧,放到那个显示的数据列前面 ElementTableColumn classCompleteCountTableColumn = new ElementTableColumn(); classCompleteCountTableColumn.label = "班级数"; classCompleteCountTableColumn.prop = "classCompleteCount" + taskItem.ID; classCompleteCountTableColumn.taskId = taskItem.ID + ""; subTableColumn.Add(classCompleteCountTableColumn); ElementTableColumn elementTableColumn = new ElementTableColumn(); elementTableColumn.label = "完成次数"; // 动态列名,用id来拼一个名称,显示的可能会随时调整 elementTableColumn.prop = "completeShow" + taskItem.ID; elementTableColumn.taskId = taskItem.ID + ""; subTableColumn.Add(elementTableColumn); elementTableColumnFather.subTableColumn = subTableColumn; elementTableColumns.Add(elementTableColumnFather); } ``` ##### 前台解析的时候多加一级循环即可 ``` <!-- 循环出来需要动态显示的列,需要合并一级的单元格 --> <el-table-column :label="fatheritem.label" align="center" v-for="(fatheritem, index) in state.dynamicColumns" :key="index"> <el-table-column :label="item.label" min-width="139" align="center" :prop="item.prop" v-bind:key="index" v-for="(item, index) in fatheritem.subTableColumn"> <template #default="scope"> <span class="canclick" @click="showMonthDetails(scope.row['completeDetails' + item.taskId], item.label, scope.row['completeCount' + item.taskId])">{{ scope.row[item.prop] }}</span> </template> </el-table-column> </el-table-column> ``` ##### 动态表头返回的数据结构的一部分如下 ![](https://img.tnblog.net/arcimg/aojiancc2/8b2006cc490b44daa11cb00ddf335e6d.png)