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-客户分类和配置管理实现,如需转载请自行联系原博主。



目录
相关文章
|
3天前
|
搜索推荐 数据挖掘 BI
CRM客户管理对企业客户的关键作用
在消费低迷时期,CRM客户管理成为了企业生命线的重要组成部分。通过有效的CRM客户管理,企业可以更加深入地了解消费者需求和行为模式,制定更加精准的营销策略和服务方案,提升客户满意度和忠诚度,从而在激烈的市场竞争中脱颖而出。
32 16
|
1月前
|
数据采集 搜索推荐 数据挖掘
通过CRM系统洞察客户喜好,实现精准客户培育
在竞争激烈的市场中,企业需通过精准培育提升客户忠诚度和价值。CRM系统助力企业在客户生命周期各阶段进行精准培育,提高满意度与忠诚度,实现长期业务增长。本文探讨如何利用CRM洞察客户喜好,涵盖策略制定、实施步骤及案例分析。通过数据收集整合、客户细分画像、个性化沟通互动、内容营销教育及忠诚度计划等手段,企业能更好地理解客户需求,制定个性化培育策略,最终实现客户价值最大化。
|
1月前
|
人工智能 监控 数据管理
如何利用CRM系统降低获客成本并高效挖掘潜在客户
在竞争激烈的市场中,企业面临获客成本上升和竞争加剧的挑战。CRM系统通过优化客户数据管理、自动化营销流程和精准营销策略,有效降低获客成本。同时,借助数据驱动、内容营销和社交媒体营销等方法,CRM能帮助企业挖掘潜在客户,提升营销效率与转化率,助力企业在数字化时代脱颖而出,实现可持续发展。
|
1月前
|
监控 搜索推荐
构建长期客户关系:CRM全周期销售管理指南
在现代商业环境中,CRM(客户关系管理)系统作为企业销售和市场营销的核心工具,通过全周期管理体系帮助企业从潜在客户识别、需求挖掘、销售转化到客户维护和再销售,系统化管理销售流程,提升销售效率和客户满意度。本文探讨CRM销售全周期管理体系的重要性、关键组成部分及如何通过数据驱动决策、自动化工作流程、客户洞察、团队协作和绩效监控等手段提升销售绩效,助力企业在竞争中脱颖而出。
|
2月前
|
机器学习/深度学习 数据挖掘
利用CRM系统深度挖掘客户价值:策略与实践
在当今竞争激烈的商业环境中,客户关系管理(CRM)系统已成为企业不可或缺的工具。CRM通过整合多渠道客户数据,构建全面的客户视图,利用先进的数据分析技术洞察客户需求和行为,实现精准营销。同时,CRM系统帮助企业收集客户反馈,持续优化客户体验,最终将这些洞察转化为实际的业务增长,提升客户满意度和忠诚度,推动企业长期发展。
|
2月前
|
搜索推荐 数据挖掘 大数据
利用CRM系统实现老客户自动化运营与维护策略
在数字化时代,CRM系统成为企业洞察老客户需求、自动化运营和维护的核心工具。通过数据驱动的客户反馈收集、个性化服务与分层管理、自动化营销、客户关怀及忠诚度计划,企业能提升客户满意度与留存率,促进业务增长。CRM系统助力精准分析客户行为,优化营销策略,确保企业长期发展。
|
2月前
|
搜索推荐 数据挖掘 BI
CRM系统如何洞察客户需求
在当今以客户为中心的商业环境中,CRM系统作为强大的工具,帮助企业收集、分析和应用客户数据,构建全面客户视图,揭示行为模式,实现精准营销和个性化服务,提升客户满意度。通过客户细分、反馈收集和预测分析,CRM助力企业洞察需求、优化体验并推动业务增长。
|
2月前
|
安全 数据挖掘 数据安全/隐私保护
国产CRM品牌巡礼:系统品牌的核心优势与特色
本文深度解析国产CRM系统的四大知名品牌:销售易、神州云动、销帮帮和天衣云。 销售易:中国领先的CRM解决方案提供商,提供全渠道获客、智能化销售流程及AIGC技术应用,赢得500强企业信赖。 神州云动:以PaaS+SaaS模式、灵活定制和行业解决方案著称,支持企业实现客户关系管理的数字化和智能化。 销帮帮:面向中小企业的实用型CRM系统,提供销售跟踪、客户视图等功能,提高销售效率和客户满意度。 天衣云:专注于云端部署,提供快速部署、高安全性的CRM解决方案,确保企业信息安全。 各品牌各有特色,企业应根据自身需求选择合适的CRM系统,以实现客户关系的全面管理,提升业务效率和客户满意度。
|
2月前
|
存储 搜索推荐 数据可视化
提升签单效率:CRM系统在客户签约管理中的关键作用
在竞争激烈的商业环境中,CRM系统作为企业销售和客户服务的核心工具,对签单速度的提升至关重要。本文探讨了CRM系统如何通过集中管理客户信息、可视化销售漏斗、自动化工作流程、个性化营销策略、数据分析与洞察、跨部门协作、移动办公便捷性和快速响应客户反馈等八大方面,有效加速签单过程,提高销售效率。
|
3月前
|
搜索推荐 数据库 UED
CRM系统源码|客户管理系统源码开发
CRM系统通过提供个性化的用户体验、提高生产力、改善客户体验和增加销售额来助力企业成长。集成CRM能自动化数据输入,减少管理时间,提高销售代表的效率。此外,CRM还能增强客户互动,降低跳出率,增加透明度,确保整个公司的协调合作。
65 5