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控件页面的动态加载 

本文转自博客园伍华聪的博客,原文链接:Winform开发框架之客户关系管理系统(CRM)的开发总结系列3-客户分类和配置管理实现,如需转载请自行联系原博主。



目录
相关文章
|
11天前
|
机器学习/深度学习 安全 搜索推荐
中国CRM市场深度解析:本土化定制开发的领军厂商与未来趋势
国内CRM软件企业正面临日益增长的本土定制需求,这不仅考验服务商的综合水平,也推动了市场的快速发展。本文将深入解析中国CRM市场的现状,探讨领军厂商的优势,并预测未来趋势,包括智能化、集成化、本土化与国际化并行及云服务模式的普及。
|
5天前
|
机器学习/深度学习 人工智能 安全
精选CRM软件:顶级客户关系管理工具深度剖析
本文综合评测2024年顶级CRM工具,依据功能性、用户体验、集成能力、数据安全、客户支持及成本效益六大标准,深度剖析纷享销客、Salesforce、Microsoft Dynamics 365、用友CRM和SAP CRM等软件,为企业选型提供参考。
|
3月前
|
开发框架 人工智能 数据挖掘
2024年国产CRM软件排行榜:客户关系管理工具
CRM软件是企业数字化战略的核心,通过全渠道互动、客户细分和销售赋能提升竞争力。2024年,国产CRM软件凭借本土化优势、成本效益、政策支持和市场机会展现出强劲竞争力。销售易、白码CRM、悟空CRM、用友CRM、金蝶CRM、珍客CRM、八百客CRM和销帮帮CRM等名列前茅。选择CRM时需考虑本土化服务、技术兼容性和长期合作潜力等因素。随着国产CRM软件的崛起,企业应根据需求选择最适合的解决方案,实现客户关系管理和业务增长的优化。
|
3月前
|
人工智能 搜索推荐 数据挖掘
中国CRM市场深度解析:本土化定制开发的领军厂商与未来趋势
在数字化转型的大潮中,客户关系管理系统(CRM)已成为企业增强竞争力和服务质量的关键工具。鉴于中国市场独特的文化背景和商业环境,高度定制化的CRM解决方案显得尤为重要。本文深入剖析了中国CRM市场的现状及其未来发展走向,同时重点介绍了销售易CRM、纷享销客等领军厂商如何通过深入理解本土需求、提供贴合国情的产品和服务来助力企业成长。这些解决方案不仅强化了客户互动的亲切感和有效性,还确保了数据的安全与合规,为企业提供了强大的竞争优势。随着人工智能、大数据等前沿技术的应用,未来的CRM系统将更加智能、高效,为企业创造更多价值。
|
3月前
|
搜索推荐
实现CRM与ERP系统无缝集成,优化客户关系管理
在当今竞争激烈的市场环境中,企业要想保持领先地位,必须高效地管理客户关系并优化内部资源。CRM(客户关系管理)系统与ERP(企业资源规划)系统的无缝集成,为企业提供了一种强大的工具,以实现这一目标
62 2
|
3月前
|
监控 搜索推荐 数据管理
crm客户管理系统来帮助企业管理和提高运营效率
crm系统帮助企业简化销售流程,提高工作效率。CRM 客户管理系统确实能够在多个方面帮助企业管理和提高运营效率,具体表现如下:
33 3
|
4月前
|
监控 数据挖掘 数据安全/隐私保护
ERP系统中的客户关系管理(CRM)
【7月更文挑战第25天】 ERP系统中的客户关系管理(CRM)
314 3
|
3月前
|
数据挖掘 BI API
简单了解CRM与SaaS系统
本文介绍了CRM(客户关系管理系统)和SaaS(软件即服务)的概念、应用场景、两者之间的关系以及CRM接口的作用和设置流程,强调了SaaS模式为CRM系统提供了灵活、便捷、经济高效的使用方式,以及CRM接口在数据集成、自动化流程、功能扩展和数据分析方面的重要性。
92 0
|
4月前
|
存储 Python
在计算机管理系统工程中,我们利用计算机技术来构建、优化和管理各种系统,如企业资源规划(ERP)、客户关系管理(CRM)等。
。在计算机管理系统工程中,我们利用计算机技术来构建、优化和管理各种系统,如企业资源规划(ERP)、客户关系管理(CRM)等。
|
4月前
|
供应链 搜索推荐 数据安全/隐私保护
本土化CRM系统推荐,企业CRM选型必看
在激烈竞争市场中,高效获客与深度运营现有客户至关重要,CRM系统成为企业数字化转型的关键。本文推荐五款优秀国产CRM系统助力企业发展:1、销售易CRM 2、简道云CRM 3、金蝶CRM 4、纷享销客CRM 5、销帮帮CRM