Winform開發框架之客戶關系管理系統(CRM)的開發總結系列3-客戶分類和配置管理實現


我在本系列隨筆的開始,介紹了CRM系統一個重要的客戶分類的展示界面,其中包含了從字典中加載分類、從已有數據中加載分類、以及分組列表中加載分類等方式的實現,以及可以動態對這些節點進行配置,實現客戶分類的界面配置處理。本文主要從邏輯代碼實現的角度上解說以上功能的實現,介紹常規字典模塊的動態加載、客戶省份城市的動態加載、客戶分組管理、客戶分類配置管理等模塊的具體實現。

一般情況下,我們對客戶的分類都需要動態加載,對這個客戶分類的管理,包括下面幾種分類。

1、常規字典模塊的動態加載

  

以上節點是從字典模塊的數據里面進行動態加載的,根據節點的不同,顯示的內容不同。

首先我們需要在數據庫里面建立一個表,用來記錄需要顯示的大的分類節點,如客戶狀態、客戶類型、客戶級別這些層次的節點,如下所示。

根據這個表的內容指引,我們在動態加載里面的子節點。

            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實現節點的加載。

加載大的樹節點完畢后,我們就從字典中獲取對應的字典項目屬性進行加載了,我們不管上面的樹節點是集成,我們只需要知道,上面每一個節點都從數據庫獲取對應的項目進行綁定即可,從字典加載子節點的代碼邏輯如下所示。

                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);
                    }
                }

2、客戶省份、客戶城市的動態加載

除了從數據字典中加載的節點數據,還有一種如客戶省份、客戶城市,我們知道這些數據很大,我們如果在樹列表里面展示全國的城市,那么肯定是不好的用戶體驗,想想要在全國幾百個城市找一個出來可不容易。

於是,可以設計從已有客戶所在的省份、所在的城市,把他們動態加載出來,數據就少很多,友好很多,界面效果圖如下所示。

   

剛才我們看到了,從數據字典中動態加載子節點的操作了,其實這個和上面的操作類似,只是獲取數據源的地方不同而已,我們可以根據樹的節點(特殊節點)來對數據源進行不同的加載,具體如下代碼所示。

        /// <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();
                }
.....................

樹的動態加載在很多地方都可以用到,例如下面的界面中,我對訂單的各種屬性狀態進行了分類,方便操作。

3、客戶分組的管理

除了上面兩種,還有一種來自個人的客戶組別的數據表數據,我們從其中獲取到對應的客戶分組信息,然后在客戶分組節點中展示出來,選擇對應的個人分組就可以獲取對應的客戶。

上面的個人分組來自對客戶的個人分組表里面,它的管理界面如下所示。

個人分組的子節點加載操作代碼如下所示,其中除了加載已有的客戶分組外,還增加兩個分組名稱,如“未分組客戶”和“全部客戶”,方便操作。

            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),用來僅僅獲取當前用戶的分組信息的。

4、客戶分類的配置管理

從上面對客戶的分類,我們看到已經有很多大的類別了,每個類別展開還有好幾項,這樣就構成了一個很大的樹,但是有時候有些客戶可能不一定對所有的分類節點都感興趣,如果能夠給客戶一個選擇配置的機會,會顯得更加友好

 

 上面我們提供了一個單獨的界面元素配置窗口給用戶進行自定義的樹節點配置,我們約定默認(在用戶還沒有保存配置的時候)是把所有節點勾選上去,如果用戶選定並保存了,那么以用戶配置的為准來加載樹列表。

下面我們來看看具體如何實現這個操作的。

首先我們在用戶初始化樹的時候,把用戶的保存列表獲取到,並保存在一個局部變量里面,方便對節點進行判斷,如下代碼所示。

        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-基於框架的開發過程 

Winform開發框架之客戶關系管理系統(CRM)的開發總結系列3-客戶分類和配置管理實現 

Winform開發框架之客戶關系管理系統(CRM)的開發總結系列4-Tab控件頁面的動態加載 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM