我在本系列隨筆的開始,介紹了CRM系統一個重要的客戶分類的展現界面,其中包含了從字典中加載分類、從已有數據中加載分類、以及分組列表中加載分類等方式的實現,以及能夠動態對這些節點進行配置,實現客戶分類的界面配置處理。本文主要從邏輯代碼實現的角度上解說以上功能的實現,介紹常規字典模塊的動態加載、客戶省份城市的動態加載、客戶分組管理、客戶分類配置管理等模塊的具體實現。html
通常狀況下,咱們對客戶的分類都須要動態加載,對這個客戶分類的管理,包括下面幾種分類。node
以上節點是從字典模塊的數據裏面進行動態加載的,根據節點的不一樣,顯示的內容不一樣。數據庫
首先咱們須要在數據庫裏面創建一個表,用來記錄須要顯示的大的分類節點,如客戶狀態、客戶類型、客戶級別這些層次的節點,以下所示。框架
根據這個表的內容指引,咱們在動態加載裏面的子節點。函數
TreeNode topNode = new TreeNode("所有客戶", 0, 0); this.treeView1.Nodes.Add(topNode); List<SystemTreeNodeInfo> propList = BLLFactory<SystemTree>.Instance.GetTree("客戶屬性分類"); foreach (SystemTreeNodeInfo 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); } } this.treeView1.ExpandAll(); for (int i = 0; i < this.treeView1.Nodes.Count; i++) { TreeNode node = this.treeView1.Nodes[i]; AddDictData(node, 3); }
其中使用遞歸函數進行建立樹節點,也就是樹節點能夠是多層級的。工具
/// <summary> /// 從系統樹形表裏面獲取數據,綁定客戶屬性分類和客戶狀態分類 /// </summary> private void AddSystemTree(List<SystemTreeNodeInfo> nodeList, TreeNode treeNode, int i) { foreach (SystemTreeNodeInfo nodeInfo in nodeList) { if (ContainTree(nodeInfo.ID)) { TreeNode subNode = new TreeNode(nodeInfo.TreeName, i, i); subNode.Tag = nodeInfo.SpecialTag;//用來作必定的標識 treeNode.Nodes.Add(subNode); AddSystemTree(nodeInfo.Children, subNode, i + 1); } } }
上面代碼首先從一個SystemTree的業務對象裏面加載列表信息,而後經過一個遞歸函數AddSystemTree實現節點的加載。post
加載大的樹節點完畢後,咱們就從字典中獲取對應的字典項目屬性進行加載了,咱們無論上面的樹節點是集成,咱們只須要知道,上面每個節點都從數據庫獲取對應的項目進行綁定便可,從字典加載子節點的代碼邏輯以下所示。學習
List<DictDataInfo> dict = BLLFactory<DictData>.Instance.FindByDictType(treeNode.Text); foreach (DictDataInfo info in dict) { if (ContainTree(info.ID)) { TreeNode subNode = new TreeNode(info.Name, i, i); if (treeNode.Tag != null) { subNode.Tag = string.Format("{0}='{1}' ", treeNode.Tag, info.Value); } treeNode.Nodes.Add(subNode); } }
除了從數據字典中加載的節點數據,還有一種如客戶省份、客戶城市,咱們知道這些數據很大,咱們若是在樹列表裏面展現全國的城市,那麼確定是很差的用戶體驗,想一想要在全國幾百個城市找一個出來可不容易。this
因而,能夠設計從已有客戶所在的省份、所在的城市,把他們動態加載出來,數據就少不少,友好不少,界面效果圖以下所示。spa
剛纔咱們看到了,從數據字典中動態加載子節點的操做了,其實這個和上面的操做相似,只是獲取數據源的地方不一樣而已,咱們能夠根據樹的節點(特殊節點)來對數據源進行不一樣的加載,具體以下代碼所示。
/// <summary> /// 從數據庫獲取對應字典數據,並綁定到相關節點上 /// </summary> private void AddDictData(TreeNode treeNode, int i) { string nodeText = treeNode.Text; if (nodeText == "客戶省份") { List<string> provinceList = BLLFactory<Customer>.Instance.GetCustomersProvince(); foreach (string province in provinceList) { TreeNode subNode = new TreeNode(province, i, i); if (treeNode.Tag != null) { subNode.Tag = string.Format("{0}='{1}' ", treeNode.Tag, province); } treeNode.Nodes.Add(subNode); } } else if (nodeText == "客戶城市") { List<string> cityList = BLLFactory<Customer>.Instance.GetCustomersCity(); foreach (string city in cityList) { TreeNode subNode = new TreeNode(city, i, i); if (treeNode.Tag != null) { subNode.Tag = string.Format("{0}='{1}' ", treeNode.Tag, city); } treeNode.Nodes.Add(subNode); } }
經過預先在節點裏面定義一些屬性,咱們就能構建一個能夠查詢出正確數據的過濾語句了,而後在樹的AfterSelect事件裏面實現對條件語句的查詢便可。
string treeConditionSql = ""; private void treeView1_AfterSelect(object sender, TreeViewEventArgs e) { if (e.Node != null) { //須要清空查詢輸入條件 this.customGridLookUpEdit1.EditValue = null; if (e.Node.Tag != null && !string.IsNullOrEmpty(e.Node.Tag.ToString())) { treeConditionSql = e.Node.Tag.ToString(); BindData(); } .....................
樹的動態加載在不少地方均可以用到,例以下面的界面中,我對訂單的各類屬性狀態進行了分類,方便操做。
除了上面兩種,還有一種來自我的的客戶組別的數據表數據,咱們從其中獲取到對應的客戶分組信息,而後在客戶分組節點中展現出來,選擇對應的我的分組就能夠獲取對應的客戶。
上面的我的分組來自對客戶的我的分組表裏面,它的管理界面以下所示。
我的分組的子節點加載操做代碼以下所示,其中除了加載已有的客戶分組外,還增長兩個分組名稱,如「未分組客戶」和「所有客戶」,方便操做。
TreeNode myGroupNode = new TreeNode("我的分組", 4, 4); List<CustomerGroupNodeInfo> groupList = BLLFactory<CustomerGroup>.Instance.GetTree(LoginUserInfo.Name); AddCustomerGroupTree(groupList, myGroupNode, 3); //添加一個未分類和所有客戶的組別 myGroupNode.Nodes.Add(new TreeNode("未分組客戶", 3, 3)); myGroupNode.Nodes.Add(new TreeNode("所有客戶", 3, 3)); this.treeView1.Nodes.Add(myGroupNode); myGroupNode.ExpandAll();
/// <summary> /// 獲取客戶分組並綁定 /// </summary> private void AddCustomerGroupTree(List<CustomerGroupNodeInfo> nodeList, TreeNode treeNode, int i) { foreach (CustomerGroupNodeInfo nodeInfo in nodeList) { if (ContainTree(nodeInfo.ID)) { TreeNode subNode = new TreeNode(nodeInfo.Name, i, i); treeNode.Nodes.Add(subNode); AddCustomerGroupTree(nodeInfo.Children, subNode, i); } } }
而後在AfterSelect事件中處理便可實現對應數據的查詢操做了。
else 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 BindDataWithGroup(string groupName) { //entity this.winGridViewPager1.DisplayColumns = displayColumns; this.winGridViewPager1.ColumnNameAlias = BLLFactory<Customer>.Instance.GetColumnNameAlias();//字段列顯示名稱轉義 List<CustomerInfo> list = BLLFactory<Customer>.Instance.FindByGroupName(LoginUserInfo.Name, groupName, this.winGridViewPager1.PagerInfo); this.winGridViewPager1.DataSource = new WHC.Pager.WinControl.SortableBindingList<CustomerInfo>(list); this.winGridViewPager1.PrintTitle = "客戶信息列表"; }
上面的代碼中用到了當前用戶的登錄名稱做爲一個標識(LoginUserInfo.Name),用來僅僅獲取當前用戶的分組信息的。
從上面對客戶的分類,咱們看到已經有不少大的類別了,每一個類別展開還有好幾項,這樣就構成了一個很大的樹,可是有時候有些客戶可能不必定對全部的分類節點都感興趣,若是可以給客戶一個選擇配置的機會,會顯得更加友好
上面咱們提供了一個單獨的界面元素配置窗口給用戶進行自定義的樹節點配置,咱們約定默認(在用戶尚未保存配置的時候)是把全部節點勾選上去,若是用戶選定並保存了,那麼以用戶配置的爲準來加載樹列表。
下面咱們來看看具體如何實現這個操做的。
首先咱們在用戶初始化樹的時候,把用戶的保存列表獲取到,並保存在一個局部變量裏面,方便對節點進行判斷,以下代碼所示。
private void InitTree() { userTreeList = BLLFactory<UserTreeSetting>.Instance.GetTreeSetting(treeCategory, LoginUserInfo.ID.ToString());
而後咱們編寫一個函數,用來判斷是否須要勾選上去。剛纔說到,默認若是沒有保存,則須要勾選上去。
/// <summary> /// 若是列表爲空或包含指定ID,則認爲包含 /// </summary> /// <param name="id">樹ID節點</param> /// <returns></returns> private bool ContainTree(string id) { bool result = false; if (userTreeList == null || userTreeList.Count == 0 || userTreeList.Contains(id)) { result = true; } return result; }
而後咱們添加每一個樹節點的時候,使用這個函數判斷是否勾選上去便可,注意每一個節點的Tag使用了一個GUID做爲記錄,方便保存。
List<SystemTreeNodeInfo> propList = BLLFactory<SystemTree>.Instance.GetTree("客戶屬性分類"); foreach (SystemTreeNodeInfo nodeInfo in propList) { TreeNode subNode = new TreeNode(nodeInfo.TreeName, 1, 1); subNode.Tag = nodeInfo.ID; subNode.Checked = ContainTree(nodeInfo.ID); AddSystemTree(nodeInfo.Children, subNode, 2); this.treeView1.Nodes.Add(subNode); } this.treeView1.ExpandAll();
最後,保存節點的時候,咱們遍歷每一個節點的Tag的GUID內容,而後把它保存到用戶配置表裏面便可。
private void btnOK_Click(object sender, EventArgs e) { List<string> nodeIdList = new List<string>(); foreach (TreeNode node in this.treeView1.Nodes) { if (node.Checked && node.Tag != null && !string.IsNullOrEmpty(node.Tag.ToString())) { nodeIdList.Add(node.Tag.ToString()); } nodeIdList.AddRange(GetNodeIdList(node)); } bool result = BLLFactory<UserTreeSetting>.Instance.SaveTreeSetting(treeCategory, LoginUserInfo.ID.ToString(), nodeIdList); if (result) { ProcessDataSaved(null, null); MessageDxUtil.ShowTips("保存成功"); } this.Close(); }
經過以上這些操做,咱們就能在配置界面中,顯示用戶的選擇節點,而後能夠保存用戶的選擇內容到一個單獨的配置表裏面,在正式的樹列表中,咱們用一樣的方法來判斷用戶是否勾選了對應的節點,若是沒有勾選,那麼咱們不要建立這個節點便可,以下面的代碼所示。
List<SystemTreeNodeInfo> propList = BLLFactory<SystemTree>.Instance.GetTree("客戶屬性分類"); foreach (SystemTreeNodeInfo 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); } }
以上就是個人CRM系統模塊裏面的一些經常使用界面元素具體實現邏輯,但願對你們分析學習有幫助。
本CRM系統主要是基於個人《Winform開發框架》基礎上進行的模塊開發,其中整合了整個框架體系裏面的權限管理模塊、字典管理模塊、Winform分頁控件、公用類庫、自動更新模塊、附件管理模塊、人員管理模塊,以及後續可能須要整合的流程管理模塊、郵件收發服務模塊、信息通知模塊等一系列內容,但願開發出一個高效、易用的客戶管理系統,同時也但願藉此係統的開發實踐,進一步改進個人代碼生成工具,以及進一步完善Winform開發框架各模塊的內容,達到新的一個高度。
《Winform開發框架》的主要功能概覽以下圖所示。
個人該CRM系統系列的幾篇隨筆連接以下,供閱讀。
Winform開發框架之客戶關係管理系統(CRM)的開發總結系列1-界面功能展現
Winform開發框架之客戶關係管理系統(CRM)的開發總結系列2-基於框架的開發過程