最近项目中有一个需求,将树形结构的数据,以表格的形式展示在页面中,下图是最终呈现效果:
源码:
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>供电系统设备设施维护内容及要求</title> <style type="text/css"> table.hovertable { font-family: verdana, arial, sans-serif; font-size: 11px; color: #333333; border-width: 1px; border-color: #999999; border-collapse: collapse; } table.hovertable th { background-color: #c3dde0; border-width: 1px; padding: 8px; border-style: solid; border-color: #a9c6c9; } table.hovertable tr { background-color: #d4e3e5; } table.hovertable td { border-width: 1px; padding: 8px; border-style: solid; border-color: #a9c6c9; text-align: center; } table.hovertable td.td-left { text-align: left; } </style> </head> <body> @Html.TabTree(ViewData["Source"] as List<V_TabTree>, @classname: "hovertable") </body> </html>
后台代码:
namespace FLSoft.WebUI { public class SimpleModel { /// <summary> /// 大类名称 /// </summary> public String TypeName { get; set; } /// <summary> /// 子类名称 /// </summary> public String SubTypeName { get; set; } /// <summary> /// 描述与要求 /// </summary> public String Description { get; set; } } public class Data { public static List<V_TabTree> GetModels() { List<V_TabTree> _treeLists = new List<V_TabTree>(); List<SimpleModel> _datas = new List<SimpleModel>(); //用于检索数据的达表示 List<Func<SimpleModel, String>> _expressions = new List<Func<SimpleModel, String>>(); #region 基础数据 //_datas.Add(new SimpleModel() { TypeName = "", SubTypeName = "", Description = "" }); _datas.Add(new SimpleModel() { TypeName = "测试1", SubTypeName = "测试1-1", Description = "测试1-1-1" }); _datas.Add(new SimpleModel() { TypeName = "测试1", SubTypeName = "测试1-1", Description = "测试1-1-2" }); _datas.Add(new SimpleModel() { TypeName = "高压配电柜", SubTypeName = "真空断路器", Description = "固定牢固无松动,外表清洁完好,分合闸无异常" }); _datas.Add(new SimpleModel() { TypeName = "高压配电柜", SubTypeName = "“五防”功能", Description = "工作正常" }); _datas.Add(new SimpleModel() { TypeName = "高压配电柜", SubTypeName = "接线端子", Description = "无烧毁或松动" }); _datas.Add(new SimpleModel() { TypeName = "高压配电柜", SubTypeName = "微机综保", Description = "上下级联动协调" }); _datas.Add(new SimpleModel() { TypeName = "阀门", SubTypeName = "阀门保养", Description = "检查各零件部件的腐蚀、磨损程度,发现损坏则更换或整修" }); _datas.Add(new SimpleModel() { TypeName = "阀门", SubTypeName = "阀门保养", Description = "清除垃圾油污,并加注润滑脂1" }); _datas.Add(new SimpleModel() { TypeName = "阀门", SubTypeName = "阀门保养", Description = "清除垃圾油污,并加注润滑脂2" }); _datas.Add(new SimpleModel() { TypeName = "阀门", SubTypeName = "计量仪表", Description = "计量准确" }); _datas.Add(new SimpleModel() { TypeName = "阀门2", SubTypeName = "阀门保养", Description = "检查各零件部件的腐蚀、磨损程度,发现损坏则更换或整修" }); _datas.Add(new SimpleModel() { TypeName = "阀门2", SubTypeName = "阀门保养", Description = "清除垃圾油污,并加注润滑脂" }); _datas.Add(new SimpleModel() { TypeName = "电容器柜", SubTypeName = "电力电容 ", Description = "无漏油、过热、膨胀现象,绝缘正常" }); _datas.Add(new SimpleModel() { TypeName = "电容器柜", SubTypeName = "接触器 ", Description = "触头无烧损痕迹、闭合紧密" }); _datas.Add(new SimpleModel() { TypeName = "电容器柜", SubTypeName = "熔断器 ", Description = "无烧损痕迹" }); #endregion var _TypeNameGroups = _datas.GroupBy((u) => u.TypeName).ToList(); foreach (var typenameGroup in _TypeNameGroups) { } return _treeLists; } /// <summary> /// 构建数据源 /// </summary> /// <returns></returns> public static List<V_TabTree> GetData() { List<V_TabTree> _datas = new List<V_TabTree>(); // #region 深度测试 var 深度测试 = new V_TabTree("测试1", 5) { Childs = new List<V_TabTree>() { new V_TabTree("测试1-1",4){ Childs = new List<V_TabTree>(){ new V_TabTree("测试1-1-1",2){ Childs =new List<V_TabTree>(){ new V_TabTree("测试1-1-1-1"), new V_TabTree("测试1-1-1-2") } }, new V_TabTree("测试1-1-2",2){ Childs =new List<V_TabTree>(){ new V_TabTree("测试1-1-2-1"), new V_TabTree("测试1-1-2-2") } } } }, new V_TabTree("测试1-2",1){ Childs = new List<V_TabTree>(){ new V_TabTree("测试1-2-1",1){ Childs =new List<V_TabTree>(){ new V_TabTree("测试1-2-1-1") } } } } } }; #endregion #region 高压配电柜 var 高压配电柜 = new V_TabTree("高压配电柜", 4) { Childs = new List<V_TabTree>() { new V_TabTree("真空断路器",1){ Childs = new List<V_TabTree>(){ new V_TabTree("固定牢固无松动,外表清洁完好,分合闸无异常") } }, new V_TabTree("“五防”功能",1){ Childs = new List<V_TabTree>(){ new V_TabTree("工作正常") } }, new V_TabTree("接线端子",1){ Childs = new List<V_TabTree>(){ new V_TabTree("无烧毁或松动") } }, new V_TabTree("微机综保",1){ Childs = new List<V_TabTree>(){ new V_TabTree("上下级联动协调") } } } }; #endregion #region 阀门 var 阀门 = new V_TabTree("阀门", 4) { Childs = new List<V_TabTree>() { new V_TabTree("阀门保养",3){ Childs = new List<V_TabTree>(){ new V_TabTree("检查各零件部件的腐蚀、磨损程度,发现损坏则更换或整修"), new V_TabTree("清除垃圾油污,并加注润滑脂1"), new V_TabTree("清除垃圾油污,并加注润滑脂2") } }, new V_TabTree("计量仪表",1){ Childs = new List<V_TabTree>(){ new V_TabTree("计量准确") } } } }; #endregion #region 阀门2 var 阀门2 = new V_TabTree("阀门2", 2) { Childs = new List<V_TabTree>() { new V_TabTree("阀门保养",2){ Childs = new List<V_TabTree>(){ new V_TabTree("检查各零件部件的腐蚀、磨损程度,发现损坏则更换或整修"), new V_TabTree("清除垃圾油污,并加注润滑脂") } } } }; #endregion #region 电容器柜 var 电容器柜 = new V_TabTree("电容器柜", 3) { Childs = new List<V_TabTree>() { new V_TabTree("电力电容",1){ Childs = new List<V_TabTree>(){ new V_TabTree("无漏油、过热、膨胀现象,绝缘正常") } }, new V_TabTree("接触器",1){ Childs = new List<V_TabTree>(){ new V_TabTree("触头无烧损痕迹、闭合紧密") } }, new V_TabTree("熔断器",1){ Childs = new List<V_TabTree>(){ new V_TabTree("无烧损痕迹") } } } }; #endregion _datas.Add(深度测试); _datas.Add(高压配电柜); _datas.Add(阀门); _datas.Add(阀门2); _datas.Add(电容器柜); return _datas; } } /// <summary> /// 表格树数据模型 /// </summary> public class V_TabTree { #region 构造函数 public V_TabTree() { this.Depth = 0; this.Childs = new List<V_TabTree>(); } public V_TabTree(String value) : this() { this.Value = value; } public V_TabTree(String value, Int32 depth) : this(value) { this.Depth = depth; } #endregion /// <summary> /// 主键 /// </summary> public Int32 ID { get; set; } /// <summary> /// 所属父节点 /// </summary> public Int32 ParentID { get; set; } /// <summary> /// 节点内容 /// </summary> public String Value { get; set; } /// <summary> /// 子字节 /// </summary> public List<V_TabTree> Childs { get; set; } /// <summary> /// 节点深度 /// </summary> public Int32 Depth { get; private set; } /// <summary> /// 节点是否已生成 /// </summary> public Boolean IsGenerate { get; set; } /// <summary> /// 添加子节点 /// </summary> /// <param name="node"></param> public virtual void Addchildren(V_TabTree node) { this.Childs.Add(node); } /// <summary> /// 获取当前节点的深度 /// 参考: /// --| 菜单1 /// ----|菜单1.1 /// ------|菜单1.1.1 /// ------|菜单1.1.2 /// ----|菜单1.2 /// ------|菜单1.2.1 /// ------|菜单1.2.2 /// 如上是一个三级节点,但是顶级节点的深度为4,表示这个菜单1的所有子节点总和 /// </summary> /// <returns></returns> public virtual Int32 GetDepth() { Int32 _depth = 0; //获取当前节点的深度 if (this.Childs.Count > 1) { } return _depth; } } /// <summary> /// 创建HTML Table数据模型 /// </summary> public class V_Tab { public V_Tab() { this.Ths = new List<V_Tr>(); this.Trs = new List<V_Tr>(); } /// <summary> /// 表格标题 /// </summary> public List<V_Tr> Ths { get; set; } /// <summary> /// 表格内容 /// </summary> public List<V_Tr> Trs { get; set; } } /// <summary> /// 创建HTML Table 列数据模型 /// </summary> public class V_Td { public V_Td() { this.Colspan = 1; this.Rowspan = 1; } /// <summary> /// 显示内容 /// </summary> public String Value { get; set; } /// <summary> /// 标题合并列数 /// </summary> public Int32 Colspan { get; set; } /// <summary> /// 标题合并行数 /// </summary> public Int32 Rowspan { get; set; } } /// <summary> /// 创建HTML Table行数据模型 /// </summary> public class V_Tr { public V_Tr() { this.Tds = new List<V_Td>(); } /// <summary> /// 行样式名称 /// </summary> public String ClassName { get; set; } /// <summary> /// 列数据集合 /// </summary> public List<V_Td> Tds { get; set; } } /// <summary> /// TabTree 自定义控件 /// </summary> public static class MvcHtmlStringExtensions { /// <summary> /// 构建表格 /// </summary> /// <param name="helper"></param> /// <returns></returns> public static MvcHtmlString TabTree(this HtmlHelper helper, V_Tab values, String classname) { StringBuilder htmlStr = new StringBuilder(); if (values == null) return new MvcHtmlString("<TABLE></TABLE>"); htmlStr.Append("<TABLE class= " + classname + ">"); if (values.Ths != null) { foreach (var th in values.Ths) { htmlStr.Append("<TR>"); if (th.Tds != null) { foreach (var td in th.Tds) { htmlStr.Append("<TH colspan =" + td.Colspan + " rowspan =" + td.Rowspan + " >"); htmlStr.Append(td.Value + "</TH>"); } } htmlStr.Append("</TR>"); } } if (values.Trs != null) { foreach (var tr in values.Trs) { htmlStr.Append("<TR>"); if (tr.Tds != null) { foreach (var td in tr.Tds) { htmlStr.Append("<TD colspan =" + td.Colspan + " rowspan =" + td.Rowspan + " >"); htmlStr.Append(td.Value + "</TD>"); } } htmlStr.Append("</TR>"); } } htmlStr.Append("</TABLE>"); return new MvcHtmlString(htmlStr.ToString()); } /// <summary> /// 构建表格 /// </summary> /// <param name="helper"></param> /// <param name="tab"></param> /// <returns></returns> public static MvcHtmlString TabTree(this HtmlHelper helper, List<V_TabTree> source, String classname) { StringBuilder htmlStr = new StringBuilder(); V_Tab _tab = new V_Tab(); _tab.Trs = BuildTr(source); return TabTree(helper, _tab, classname); } /// <summary> /// 将树形的数据构建为HTML Table数据模型 /// </summary> /// <param name="values"></param> /// <returns></returns> private static List<V_Tr> BuildTr(List<V_TabTree> values) { List<V_Tr> _trs = new List<V_Tr>(); foreach (var value in values) { _trs.AddRange(GenerateTr(value)); //一个顶级节点的行数由value.Depth属性来确定 } return _trs; } /// <summary> /// 仅满足1级节点合并 /// </summary> /// <param name="value"></param> /// <returns></returns> private static List<V_Tr> BuildTr(V_TabTree value) { List<V_Tr> _trs = new List<V_Tr>(); //Depth属性用于判断当前的数据产生Tr的数量 for (int i = 0; i < value.Depth; i++) { V_Tr _tr = new V_Tr(); if (i == 0) { V_Td _td = new V_Td() { Value = value.Value, Rowspan = value.Depth }; _tr.Tds.Add(_td); } //检测是否包含子节点,如果包含 if (value.Childs != null && value.Childs.Count > 0) { //顶级节点的深度与其子节点数目相等时,后续表格的列都是一对一的 if (value.Depth == value.Childs.Count) { _tr.Tds.AddRange(BuildTd(value.Childs[i])); } else //如果几点深度大于子节点时,需要计算循环深度 { //父节点的级别与所有子节点的深度总和相等 } } _trs.Add(_tr); } return _trs; } /// <summary> /// 仅满足1-2级节点合并 /// </summary> /// <param name="parentNode"></param> /// <returns></returns> private static List<V_Tr> BuildTrEx(V_TabTree parentNode) { List<V_Tr> _trs = new List<V_Tr>(); V_Tr _tr = new V_Tr(); //当包含一个数据节点时,创建第一个TR _tr.Tds = BuildTd(parentNode); //填充TR包含的所有的TD _trs.Add(_tr); //填充TR集合 if (parentNode.Depth > 1) //判断Depth>1表示这个节点下面还包含TR行 { if (parentNode.Childs.Count > 1) //如果当前节点包含多个子节点,需要循环遍历 { //后续描述的"当前节点",是当前For循环上下文的节点,不是指的参数节点 var _childNodes = parentNode.Childs; for (int i = 0; i < _childNodes.Count; i++) //在循环子节点时,子节点的第一个项已经赋值给Parent节点了 { //所以只处理Depth>1的业务,parentNode的子节点(childNodes)还包含可以循环子节点 if (_childNodes[i].Depth > 1) //表示当前这个节点后续的TR行包含多个 { //继续循环当前节点的子节点(顶级节点的子节点的子节点判断,有点绕) for (int ii = 0; ii < _childNodes[i].Childs.Count; ii++) { if (ii > 0) { //因为是当前节点的二级节点,那么第一条数据被上父节点给取走了,循环就需要从i+1开始 _trs.AddRange(BuildTrEx(_childNodes[i].Childs[ii])); } } } else //如果当前节点的Depth =1 ,表示这个节点已经被赋值给Parent节点了, { if (i > 0) { _trs.AddRange(BuildTrEx(_childNodes[i])); //取子节点递归查找 } } } } else //二级节点只有一项时 { for (int i = 0; i < parentNode.Childs[0].Childs.Count - 1; i++) { _trs.AddRange(BuildTrEx(parentNode.Childs[0].Childs[i + 1])); } } } return _trs; } /// <summary> /// 多级节点嵌套合并 /// </summary> /// <param name="parentNode">父节点</param> /// <returns></returns> private static List<V_Tr> GenerateTr(V_TabTree parentNode) { List<V_Tr> _trs = new List<V_Tr>(); if (!parentNode.IsGenerate) { V_Tr _tr = new V_Tr(); _tr.Tds = BuildTd(parentNode); _trs.Add(_tr); } if (parentNode.Depth > 1) //有多级深度 { //检测其子节点是否包含深度,肯定是要添加一个循环遍历的 List<V_TabTree> childNodes = parentNode.Childs; for (int i = 0; i < childNodes.Count; i++) { _trs.AddRange(GenerateTr(childNodes[i])); } } return _trs; } private static List<V_Td> BuildTd(V_TabTree value) { List<V_Td> _tds = new List<V_Td>(); value.IsGenerate = true; V_Td _td = new V_Td() { Value = value.Value, Rowspan = value.Depth > 0 ? value.Depth : 1 }; _tds.Add(_td); if (value.Depth > 0) { _tds.AddRange(BuildTd(value.Childs[0])); } return _tds; } } }