在上篇隨筆《Winform開發框架之客戶關系管理系統(CRM)的開發總結系列1-界面功能展示》中介紹了 我的整個CRM系統的概貌,本篇繼續本系列的文章,介紹如何基於我的《winform開發框架》的基礎上進 行CRM系統模塊的開發工作,希望對大家在系統模塊開發有所啟示或者幫助。
在我整個開發框架的體系結構中,我都希望開發的業務模塊盡可能重用,因此遵循這個要求,所有的 模塊除了一些基礎模塊外,盡可能和其他業務模塊沒有任何耦合關系,同時也可以動態對模塊進行加載 使用,和我在《Winform開發框架之插件化應用框架實現》的思想一樣,各個模塊之間可以動態組合起來 ,實現更多的業務整合。
1、CRM系統的工程項目介紹
本客戶關系管理系統,也是基於這個目的和基礎上進行模塊開發,在整個項目模塊開發過程中,將會 利用到整個Winform開發框架的相關組件模塊,包括基礎界面模塊、程序啟動模塊、權限管理模塊、字典 管理模塊、分頁控件、公用類庫、附件管理等公用模塊。
整個CRM系統的界面效果如下所示。
首先我們來看看CRM系統主要項目工程的布局和說明。
設計好CRM的相關數據庫表後,利用C#代碼生成工具Database2Sharp生成框架各層的代碼,模塊開始 開發的時候,可以一次性把所有業務表的代碼一次性生成,然後在整個新的解決方案(.sln)上進行遞增 式完善即可,如果是後續模塊的開發,則需要增量把生成的代碼,復制到相關的框架目錄即可,整理後 的業務邏輯層代碼結構如下所示
這個時候,我們生成了界面層以下的所有分層代碼,整個代碼生成後,一次性即可編譯通過,界面層 我們另外建立一個Winform項目工程WHC.CRM.UIDx ,然後添加相關的界面引用程序集 (如DevExpress的相關界面程序集)。處理完這些後,我們又可以利用C#代碼生成工具Database2Sharp 來實現界面的快速開發工作了,代碼生成工具生成界面的操作界面如下所示,具體生成界面的操作可以 參考隨筆《利用代碼生成工具Database2Sharp設計數據編輯界面》進行了解。
最後得到類似項目目錄結構的CRM系統界面模塊工程。
由於整個CRM系統包含很多界面元素,因此以上模塊的界面部分只是其中一部分,如果內容較多,可 以建立目錄進行分類管理,這樣會更加清晰。
2、CRM系統的界面層代碼分析
利用C#代碼生成工具Database2Sharp,可以快速生成所需要的框架界面代碼,包括集成各種已有模塊 的界面基類、導入導出模塊支持、高級查詢能功能模塊,各種實體類對應關系等內容,這些如果利用手 工操作,效率非常低下,而且容易出錯。即使利用一些代碼生成工具,如果沒有和現成的界面模塊進行 很好的整合,也需要花費大量的時間進行整理,下面通過幾個界面代碼的展示進行大致的了解。
1)列表顯示界面的集成和分頁整合
2)字典模塊的整合處理(通過擴展類方法實現)
3)導入導出模塊的整合
private string moduleName = "客戶合同信息"; /// <summary> /// 導入Excel的操作 /// </summary> private void btnImport_Click(object sender, EventArgs e) { string templateFile = string.Format("{0}-模板.xls", moduleName); FrmImportExcelData dlg = new FrmImportExcelData(); dlg.SetTemplate(templateFile, System.IO.Path.Combine(Application.StartupPath, templateFile)); dlg.OnDataSave += new FrmImportExcelData.SaveDataHandler(ExcelData_OnDataSave); dlg.OnRefreshData += new EventHandler(ExcelData_OnRefreshData); dlg.ShowDialog(); } void ExcelData_OnRefreshData(object sender, EventArgs e) { BindData(); } bool ExcelData_OnDataSave(DataRow dr) { string customerName = dr["客戶名稱"].ToString(); if (string.IsNullOrEmpty(customerName)) return false; CustomerInfo customerInfo = BLLFactory<Customer>.Instance.FindByName(customerName); if (customerInfo == null) { throw new ArgumentException(string.Format("客戶名稱【{0}】不存在,記錄已跳過", customerName)); } bool success = false; bool converted = false; DateTime dtDefault = Convert.ToDateTime("1900-01-01"); DateTime dt; ContractInfo info = new ContractInfo(); info.Customer_ID = customerInfo.ID;//客戶ID info.HandNo = dr["合同編號"].ToString(); info.ExpenditureType = dr["收支類型"].ToString(); info.ContractType = dr["合同類型"].ToString(); info.ContractName = dr["合同名稱"].ToString(); info.ContractMoney = dr["合同金額"].ToString().ToDecimal(); converted = DateTime.TryParse(dr["簽約日期"].ToString(), out dt); if (converted && dt > dtDefault) { info.SignDate = dt; } ........................................ success = BLLFactory<Contract>.Instance.Insert(info); return success; } /// <summary> /// 導出Excel的操作 /// </summary> private void btnExport_Click(object sender, EventArgs e) { string file = FileDialogHelper.SaveExcel(string.Format("{0}.xls", moduleName)); if (!string.IsNullOrEmpty(file)) { string where = GetConditionSql(); List<ContractInfo> list = BLLFactory<Contract>.Instance.Find(where); DataTable dtNew = DataTableHelper.CreateTable("序號|int,客戶名稱,合同編號,收支類型,合同類型,合同名稱,合同金額,公司簽約人,客戶簽約人,簽約日期,簽約地點,乙方名稱,合同開始日期,合同結束日期,結算情況,合同狀態,關聯項目,聯系人,聯系人電話,聯系人手機,合同內容, 備注說明,經辦人"); DataRow dr; int j = 1; for (int i = 0; i < list.Count; i++) { dr = dtNew.NewRow(); dr["序號"] = j++; dr["客戶名稱"] = BLLFactory<Customer>.Instance.GetCustomerName(list[i].Customer_ID);//轉義為客戶名稱 dr["合同編號"] = list[i].HandNo; dr["收支類型"] = list[i].ExpenditureType; dr["合同類型"] = list[i].ContractType; ...................................... dr["經辦人"] = list[i].Operator; dtNew.Rows.Add(dr); } try { string error = ""; AsposeExcelTools.DataTableToExcel2(dtNew, file, out error); if (!string.IsNullOrEmpty(error)) { MessageDxUtil.ShowError(string.Format("導出Excel出現錯誤:{0}", error)); } else { if (MessageDxUtil.ShowYesNoAndTips("導出成功,是否打開文件?") == System.Windows.Forms.DialogResult.Yes) { System.Diagnostics.Process.Start(file); } } } catch (Exception ex) { LogTextHelper.Error(ex); MessageDxUtil.ShowError(ex.Message); } } }
4)高級查詢模塊的整合
private FrmAdvanceSearch dlg; private void btnAdvanceSearch_Click(object sender, EventArgs e) { if (dlg == null) { dlg = new FrmAdvanceSearch(); dlg.FieldTypeTable = BLLFactory<Contract>.Instance.GetFieldTypeList(); dlg.ColumnNameAlias = BLLFactory<Contract>.Instance.GetColumnNameAlias(); dlg.DisplayColumns = "Customer_ID,HandNo,ExpenditureType,ContractType,ContractName,ContractMoney,CompanySigner,CustomerSigner,SignDate,SignLocation,PartyBName,StartDate,EndDate,Settlement,Status,Rel atedItems,Contact,ContactPhone,ContactMobile,Content,Note,Operator"; #region 下拉列表數據 //dlg.AddColumnListItem("UserType", Portal.gc.GetDictData("人員類型"));//字典列表 //dlg.AddColumnListItem("Sex", "男,女");//固定列表 //dlg.AddColumnListItem("Credit", BLLFactory<Contract>.Instance.GetFieldList("Credit"));//動態列表 #endregion dlg.ConditionChanged += new FrmAdvanceSearch.ConditionChangedEventHandler(dlg_ConditionChanged); } dlg.ShowDialog(); } void dlg_ConditionChanged(SearchCondition condition) { advanceCondition = condition; BindData(); }
5)編輯界面的基類繼承
3、CRM系統的界面層的用戶及權限信息傳遞
我們知道,每個業務模塊都可能需要獲取當前登錄的一些用戶信息和權限信息,以便達到更好的控制 和數據的顯示,如某些模塊,可能只需要顯示當前用戶的數據,由於CRM系統的界面是獨立開發,不整合 在啟動界面模塊裡面,那麼如何獲得用戶和權限控制信息呢?例如下面的CRM模塊裡面的界面,需要根據 當前用戶獲取到客戶的分組列表的。
首先我們可以在界面基類中實現一個接口,以便傳遞相關的用戶和權限信息。
namespace WHC.Framework.BaseUI { public partial class BaseForm : DevExpress.XtraEditors.XtraForm, IFunction {
其中IFunction的定義如下所示。
namespace WHC.Framework.BaseUI { /// <summary> /// 父窗體實現的權限控制接口 /// </summary> public interface IFunction { /// <summary> /// 初始化權限控制信息 /// </summary> void InitFunction(LoginUserInfo userInfo, Dictionary<string, string> functionDict); /// <summary> /// 是否具有訪問指定控制ID的權限 /// </summary> /// <param name="controlId">功能控制ID</param> /// <returns></returns> bool HasFunction(string controlId); /// <summary> /// 登陸用戶基礎信息 /// </summary> LoginUserInfo LoginUserInfo { get; set; } /// <summary> /// 登錄用戶具有的功能字典集合 /// </summary> Dictionary<string, string> FunctionDict { get; set; } /// <summary> /// 應用程序基礎信息 /// </summary> AppInfo AppInfo { get; set; } } }
其中就定義了接口進行用戶和權限信息的賦值。在界面按鈕構建相關模塊的功能界面窗體的時候,我 們可以為這些傳遞傳遞相關的對象信息。
private void barCRMContact_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { ChildWinManagement.LoadMdiForm(this, typeof(FrmCustomerContact)); } private void barCRMCustomer_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { ChildWinManagement.LoadMdiForm(this, typeof(FrmCustomerManage)); }
tableForm = (Form) Activator.CreateInstance(formType); tableForm.MdiParent = mainDialog; //如果窗體集成了IFunction接口(第一次創建需要設置) IFunction function = tableForm as IFunction; if (function != null) { //初始化權限控制信息 function.InitFunction(Portal.gc.LoginUserInfo, Portal.gc.FunctionDict); //記錄程序的相關信息 function.AppInfo = new AppInfo(Portal.gc.AppUnit, Portal.gc.AppName, Portal.gc.AppWholeName, Portal.gc.SystemType); }
查看本欄目