客戶管理的核心入口是分類導航樹。Winform CRM框架構建了「字典數據、地理屬性、個人分組、系統配置」四維融合的動態分類體系,並提供可視化配置界面讓用戶自定義可見節點。本文完整拆解其數據源整合、遞歸樹構建、條件標簽化查詢、用戶配置持久化的全鏈路實現。
樹節點 Tag 屬性攜帶查詢條件片段(如 Status='1'、Province='廣東'、GroupName='重點客戶'),選中節點時拼接為完整SQL Where子句,實現「點擊即查詢」的零代碼綁定。
從 SystemTree 表讀取分類結構,遞歸構建父子節點;葉節點從 DictData 表按類型加載字典項。
// 遞歸構建系統樹節點
private void AddSystemTree(List<SystemTreeNodeInfo> nodeList, TreeNode treeNode, int imgIndex) {
foreach (var nodeInfo in nodeList) {
if (ContainTree(nodeInfo.ID)) {
TreeNode subNode = new TreeNode(nodeInfo.TreeName, imgIndex, imgIndex);
subNode.Tag = nodeInfo.SpecialTag; // 標識字典類型
treeNode.Nodes.Add(subNode);
AddSystemTree(nodeInfo.Children, subNode, imgIndex + 1);
}
}
}
// 字典數據綁定到葉節點
private void AddDictData(TreeNode treeNode, int imgIndex) {
List<DictDataInfo> dict = BLLFactory<DictData>.Instance.FindByDictType(treeNode.Text);
foreach (var info in dict) {
if (ContainTree(info.ID)) {
TreeNode subNode = new TreeNode(info.Name, imgIndex, imgIndex);
if (treeNode.Tag != null)
subNode.Tag = string.Format("{0}='{1}'", treeNode.Tag, info.Value); // 拼接條件
treeNode.Nodes.Add(subNode);
}
}
}



避免全量省市庫導致樹過大、體驗差,僅從 Customer 表提取實際存在的省份/城市:
private void AddDictData(TreeNode treeNode, int imgIndex) {
if (treeNode.Text == "客戶省份") {
List<string> list = BLLFactory<Customer>.Instance.GetCustomersProvince();
foreach (string prov in list) {
TreeNode n = new TreeNode(prov, imgIndex, imgIndex);
n.Tag = string.Format("{0}='{1}'", treeNode.Tag, prov);
treeNode.Nodes.Add(n);
}
} else if (treeNode.Text == "客戶城市") {
List<string> list = BLLFactory<Customer>.Instance.GetCustomersCity();
foreach (string city in list) {
TreeNode n = new TreeNode(city, imgIndex, imgIndex);
n.Tag = string.Format("{0}='{1}'", treeNode.Tag, city);
treeNode.Nodes.Add(n);
}
}
}


從 CustomerGroup 表按當前用戶加載分組樹,並追加「未分組」「全部」兩個虛擬節點:
TreeNode myGroupNode = new TreeNode("個人分組", 4, 4);
List<CustomerGroupNodeInfo> list = BLLFactory<CustomerGroup>.Instance.GetTree(LoginUserInfo.Name);
AddCustomerGroupTree(list, myGroupNode, 3);
// 虛擬節點
myGroupNode.Nodes.Add(new TreeNode("未分組客戶", 3, 3));
myGroupNode.Nodes.Add(new TreeNode("全部客戶", 3, 3));
this.treeView1.Nodes.Add(myGroupNode);
選中處理:
if (e.Node.FullPath.IndexOf("個人分組") >= 0) {
if (e.Node.Text == "全部客戶") { treeConditionSql = ""; BindData(); }
else if (e.Node.Text == "未分組客戶") { isUserGroupName = true; BindDataWithGroup(null); }
else { isUserGroupName = true; BindDataWithGroup(e.Node.Text); }
}


同樣機制應用於訂單管理,按狀態、類型構建導航樹。


private void InitTree() {
userTreeList = BLLFactory<UserTreeSetting>.Instance.GetTreeSetting(treeCategory, LoginUserInfo.ID.ToString());
}
private bool ContainTree(string id) {
return userTreeList == null || userTreeList.Count == 0 || userTreeList.Contains(id); // 無配置則全顯示
}
foreach (var nodeInfo in propList) {
TreeNode subNode = new TreeNode(nodeInfo.TreeName, 1, 1);
subNode.Tag = nodeInfo.ID; // GUID標識
subNode.Checked = ContainTree(nodeInfo.ID); // 同步配置狀態
AddSystemTree(nodeInfo.Children, subNode, 2);
this.treeView1.Nodes.Add(subNode);
}
private void btnOK_Click(object sender, EventArgs e) {
List<string> nodeIdList = new List<string>();
foreach (TreeNode node in this.treeView1.Nodes) {
CollectCheckedNodeIds(node, nodeIdList);
}
BLLFactory<UserTreeSetting>.Instance.SaveTreeSetting(treeCategory, LoginUserInfo.ID.ToString(), nodeIdList);
}
private void CollectCheckedNodeIds(TreeNode node, List<string> list) {
if (node.Checked && node.Tag != null && !string.IsNullOrEmpty(node.Tag.ToString()))
list.Add(node.Tag.ToString());
foreach (TreeNode child in node.Nodes) CollectCheckedNodeIds(child, list);
}
ContainTree 過濾
foreach (var nodeInfo in propList) {
if (ContainTree(nodeInfo.ID)) { // 僅創建用戶勾選的節點
TreeNode subNode = new TreeNode(nodeInfo.TreeName, 1, 1);
AddSystemTree(nodeInfo.Children, subNode, 2);
this.treeView1.Nodes.Add(subNode);
}
}
該分類體系通過「多源數據統一Tag標簽化、遞歸樹構建解耦數據源、配置表驅動可見性」三大設計,實現了:
是Winform CRM框架中「以客戶為中心」導航入口的核心基礎設施。
