using DevExpress.Data; using DevExpress.XtraTreeList; using DevExpress.XtraTreeList.Nodes; using Gs.DevApp.Entity; using Gs.DevApp.ToolBox; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Data; using System.Drawing; using System.Windows.Forms; namespace Gs.DevApp.DevFrm.Sys { /// /// 看板目录管理 - 树形结构维护界面(用户控件) /// public partial class SimpleKanBan : DevExpress.XtraEditors.XtraUserControl { private string _webServiceName = "MesSimpleManager/"; public SimpleKanBan() { InitializeComponent(); // 工具栏事件绑定 toolBarMenu1.btnLoadClick += ToolBarMenu1_btnLoadClick; toolBarMenu1.btnQueryClick += ToolBarMenu1_btnQueryClick; // TreeList 行号显示 tlMenu.IndicatorWidth = 50; tlMenu.CustomDrawNodeIndicator += (s, ee) => { if (ee.IsNodeIndicator) { var index = ee.Node.TreeList.GetVisibleIndexByNode(ee.Node); ee.Info.DisplayText = (index + 1).ToString(); } }; // 初始化加载数据 getPageList(); // 右键菜单事件 tlMenu.MouseDown += TlMenu_MouseDown; // 增加子项 toolStripMenuItemAdd.Click += (s, ee) => { string strGuid = ""; string strUpGuid = ""; if (tlMenu.FocusedNode != null) { strUpGuid = tlMenu.FocusedNode.GetValue("id").ToString(); } SimpleKanBanEdit frm = new SimpleKanBanEdit(strGuid, strUpGuid); frm.UpdateParent += (s2, ee2) => { getPageList(); TreeListNode node = tlMenu.FindNodeByKeyID(long.Parse(strUpGuid)); if (node != null) { node.Expanded = true; tlMenu.MakeNodeVisible(node); } }; frm.ShowDialog(); }; // 增加主项(根节点) toolStripMenuItemRoot.Click += (s, ee) => { string strGuid = ""; string strUpGuid = ""; SimpleKanBanEdit frm = new SimpleKanBanEdit(strGuid, strUpGuid); frm.UpdateParent += (s2, ee2) => { getPageList(); }; frm.ShowDialog(); }; // 删除节点 toolStripMenuItemDel.Click += (s, ee) => { if (tlMenu.FocusedNode != null) { string rowGuid = ""; string rowName = ""; rowGuid = tlMenu.FocusedNode.GetValue("id").ToString(); rowName = tlMenu.FocusedNode.GetValue("title").ToString(); if (string.IsNullOrEmpty(rowGuid)) { MsgHelper.Warning("请先选择你要操作的行!"); return; } if (!MsgHelper.AskQuestion("你选择了【" + rowName + "】,确定删除吗?")) return; List lst = new List(); lst.Add(rowGuid); try { string strJson = UtilityHelper.HttpPost("", _webServiceName + "DeleteModel", JsonConvert.SerializeObject(lst)); ReturnModel _rtn = UtilityHelper.ReturnToDynamic(strJson); if (_rtn.rtnCode > 0) { tlMenu.DeleteNode(tlMenu.FocusedNode); } else MsgHelper.ShowError("提示:" + _rtn.rtnMsg); } catch (Exception ex) { MsgHelper.ShowError("提示:" + ex.Message); } } }; // 创建刷新菜单项 ToolStripMenuItem toolStripMenuItemRefresh = new ToolStripMenuItem(); toolStripMenuItemRefresh.Text = "刷新"; toolStripMenuItemRefresh.Click += ToolBarMenu1_btnLoadClick; // 直接调用已有刷新事件 // 添加到右键菜单,不清空原有 Items cms1.Items.Add(toolStripMenuItemRefresh); // 编辑节点 toolStripMenuItemEdt.Click += (s, ee) => { string strGuid = ""; string strUpGuid = ""; if (tlMenu.FocusedNode != null) { strGuid = tlMenu.FocusedNode.GetValue("id").ToString(); var fidValue = tlMenu.FocusedNode.GetValue("parentId"); strUpGuid = fidValue != null ? fidValue.ToString() : ""; } SimpleKanBanEdit frm = new SimpleKanBanEdit(strGuid, strUpGuid); frm.UpdateParent += (s2, ee2) => { getPageList(); TreeListNode node = tlMenu.FindNodeByKeyID(long.Parse(strGuid)); if (node != null) { node.Expanded = true; tlMenu.MakeNodeVisible(node); } }; frm.ShowDialog(); }; } /// /// 查询事件 /// private void ToolBarMenu1_btnQueryClick(object sender, EventArgs e) { MsgHelper.ShowInformation("该窗体不支持查询,若想更新页面,请点击 刷新"); } /// /// 刷新事件 /// private void ToolBarMenu1_btnLoadClick(object sender, EventArgs e) { getPageList(); MsgHelper.ShowInformation("刷新成功!"); } /// /// 右键弹出菜单 /// private void TlMenu_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Right) { TreeListHitInfo hInfo = tlMenu.CalcHitInfo(new Point(e.X, e.Y)); TreeListNode node = hInfo.Node; tlMenu.FocusedNode = node; if (hInfo.HitInfoType == HitInfoType.Cell || hInfo.HitInfoType == HitInfoType.Row || hInfo.HitInfoType == HitInfoType.RowIndent || hInfo.HitInfoType == HitInfoType.RowIndicator) { if (node != null) { toolStripMenuItemAdd.Visible = true; toolStripMenuItemEdt.Visible = true; toolStripMenuItemDel.Visible = true; toolStripMenuItemRoot.Visible = false; cms1.Show(tlMenu, e.Location); } } else { toolStripMenuItemAdd.Visible = false; toolStripMenuItemEdt.Visible = false; toolStripMenuItemDel.Visible = false; toolStripMenuItemRoot.Visible = true; cms1.Show(tlMenu, e.Location); } } } /// /// 加载数据列表 - 树形结构显示 /// private void getPageList() { var pgq = new PageQueryModel(1, 999999, "a.node_level", "asc", "", "");//sort_order var json = JsonConvert.SerializeObject(pgq); try { var strReturn = UtilityHelper.HttpPost("", _webServiceName + "GetListPage", json); var dd = UtilityHelper.ReturnToTablePage(strReturn); var dt = dd.rtnData.list; // 获取原始 DataTable // 检测 parentId 是否存在且类型不对 if (dt.Columns.Contains("parentId") && dt.Columns["parentId"].DataType != typeof(long)) { // 1. 创建一个新的 Int64 类型的临时列 DataColumn newCol = new DataColumn("parentId_Fixed", typeof(long)); dt.Columns.Add(newCol); // 2. 遍历所有行,把 String 转成 Long foreach (DataRow row in dt.Rows) { object oldVal = row["parentId"]; // 只有非空值才转换,空值保持 DBNull (即根节点) if (oldVal != null && oldVal != DBNull.Value && !string.IsNullOrEmpty(oldVal.ToString())) { if (long.TryParse(oldVal.ToString(), out long val)) { row["parentId_Fixed"] = val; } } } // 3. 移除旧的 String 列 dt.Columns.Remove("parentId"); // 4. 把新列改名为 parentId newCol.ColumnName = "parentId"; } tlMenu.BeginUpdate(); // 1. 绑定处理过的数据 tlMenu.DataSource = dt; // 2. 绑定字段 (现在类型完全一致了) tlMenu.KeyFieldName = "id"; tlMenu.ParentFieldName = "parentId"; // 设置根节点 (Int64 类型的列,空值就是 DBNull) tlMenu.RootValue = DBNull.Value; string sortColName = "sortOrder"; if (tlMenu.Columns[sortColName] != null) { tlMenu.ClearSorting(); tlMenu.Columns[sortColName].SortOrder = System.Windows.Forms.SortOrder.Ascending; tlMenu.Columns[sortColName].SortIndex = 0; } tlMenu.ForceInitialize(); // 4. 调用展开逻辑 ApplyExpandState(); tlMenu.EndUpdate(); tlMenu.BestFitColumns(); } catch (Exception ex) { tlMenu.EndUpdate(); MsgHelper.Warning("加载失败:" + ex.Message); } } /// /// 根据 isExpanded (bool) 字段设置节点展开 /// private void ApplyExpandState() { // 1. 先全部折叠 tlMenu.CollapseAll(); // 2. 使用 DevExpress 高效迭代器遍历 tlMenu.NodesIterator.DoOperation(node => { // 获取值 (注意:列名必须与 DataTable 中一致,通常是 "isExpanded") object val = node.GetValue("isExpanded"); // 3. 判断并展开 if (val != null && val != DBNull.Value) { if ((bool)val) { node.Expanded = true; } } }); } /// /// 将动态数据转换为DataTable /// private DataTable ConvertToDataTable(dynamic sourceData) { DataTable dt = new DataTable(); if (sourceData == null) return dt; // 如果已经是DataTable,直接返回 if (sourceData is DataTable) return sourceData as DataTable; // 尝试从JSON数组转换 string jsonStr = JsonConvert.SerializeObject(sourceData); dt = JsonConvert.DeserializeObject(jsonStr); return dt; } /// /// 处理parentId字段,确保树形结构正确构建 /// 根节点(node_level=0 或 parentId为null/0)的parentId设为DBNull /// private void ProcessParentIdForTreeStructure(DataTable dt) { if (dt == null || dt.Rows.Count == 0) return; foreach (DataRow row in dt.Rows) { // 获取nodeLevel int nodeLevel = 0; if (dt.Columns.Contains("nodeLevel") && row["nodeLevel"] != DBNull.Value) { nodeLevel = Convert.ToInt32(row["nodeLevel"]); } // 获取parentId object parentIdValue = null; if (dt.Columns.Contains("parentId")) { parentIdValue = row["parentId"]; } // 如果是根节点(nodeLevel=0 或 parentId为null/0/空),将parentId设为DBNull bool isRootNode = nodeLevel == 0; bool hasNoParent = parentIdValue == null || parentIdValue == DBNull.Value || string.IsNullOrEmpty(parentIdValue.ToString()) || parentIdValue.ToString() == "0"; if (isRootNode || hasNoParent) { row["parentId"] = DBNull.Value; } } } /// /// 展开第一级节点(根节点) /// private void ExpandFirstLevelNodes() { foreach (TreeListNode node in tlMenu.Nodes) { node.Expanded = true; } } } }