如何将递归Location列表渲染为带rowspan的格式化HTML表格?
解决方案:递归树形Location渲染带rowspan的HTML表格
这个需求的核心是准确计算每个节点对应的叶子节点总数(用来设置rowspan),同时通过递归逻辑逐层渲染表格行,确保父节点的单元格能跨占所有子节点对应的行数。
1. 修正叶子节点计数的扩展方法
你原来的GetDepth方法逻辑有点绕,咱们换个更清晰的实现:如果当前节点是叶子节点(没有子节点),返回1;否则递归累加所有子节点的叶子节点数量,这个数值就是该节点需要的rowspan值。
public static class LocationExtensions { // 计算当前节点下的叶子节点总数(即需要的rowspan值) public static int GetLeafNodeCount(this Location location) { // 叶子节点:没有子节点,返回1 if (location.ChildLocations == null || !location.ChildLocations.Any()) { return 1; } // 非叶子节点:累加所有子节点的叶子节点数 int count = 0; foreach (var child in location.ChildLocations) { count += child.GetLeafNodeCount(); } return count; } }
2. 视图中的递归渲染逻辑
在CSHTML视图里,我们可以用@helper定义一个递归渲染的辅助方法,它会根据当前节点的层级,决定如何输出<td>和<tr>标签,同时正确应用rowspan属性。
完整的视图代码如下:
@using WebApplication1.Controllers; @using YourNamespace.Extensions; // 替换成你的扩展方法所在命名空间 @model List<Location> @{ ViewBag.Title = "Home Page"; } <table class="table table-border"> @foreach (var rootLocation in Model) { // 渲染根节点的第一行 RenderLocationRow(rootLocation, 1); } </table> @helper RenderLocationRow(Location location, int currentLevel) { var isLeaf = location.ChildLocations == null || !location.ChildLocations.Any(); var rowSpan = location.GetLeafNodeCount(); // 第一行:输出当前层级的单元格,以及后续层级的第一个子节点单元格(如果有) <tr> @if (currentLevel == 1) { <td rowspan="@rowSpan">@location.Name</td> } else if (currentLevel == 2) { <td rowspan="@rowSpan">@location.Name</td> } else { <td>@location.Name</td> } // 如果不是叶子节点,渲染第一个子节点的后续内容 if (!isLeaf) { var firstChild = location.ChildLocations.First(); // 递归渲染第一个子节点,层级+1 RenderRemainingCells(firstChild, currentLevel + 1); } </tr> // 如果不是叶子节点,渲染剩下的子节点 if (!isLeaf && location.ChildLocations.Count > 1) { foreach (var child in location.ChildLocations.Skip(1)) { // 递归渲染其他子节点,层级保持当前层级(因为要补全该行的单元格) RenderLocationRow(child, currentLevel); } } } @helper RenderRemainingCells(Location location, int currentLevel) { var isLeaf = location.ChildLocations == null || !location.ChildLocations.Any(); var rowSpan = location.GetLeafNodeCount(); if (currentLevel == 2) { <td rowspan="@rowSpan">@location.Name</td> } else { <td>@location.Name</td> } // 如果不是叶子节点,继续递归渲染子节点 if (!isLeaf) { var firstChild = location.ChildLocations.First(); RenderRemainingCells(firstChild, currentLevel + 1); } }
3. 关键逻辑说明
GetLeafNodeCount方法:确保每个节点的rowspan等于其下所有叶子节点的数量,这样父单元格能准确跨占所有子节点对应的行数。RenderLocationRow:负责渲染每一行的起始单元格,以及处理多个同级子节点的换行逻辑。RenderRemainingCells:负责在同一行内渲染后续层级的单元格,确保树形结构的层级对应表格列的位置。
这个方案支持任意多级深度的Location结构,完全适配你实际场景的需求。
内容的提问来源于stack exchange,提问作者Mrug
相关产品推荐
相关产品推荐

