前言
我寫代碼喜歡提取一些共通的東西出來,之前的一篇博客中說了如何用一個共通的viewModel和簡潔的后台代碼做查詢頁面,所有的查詢頁面都要對應一個數據錄入的編輯及查看明細的頁面,那么今天我們就來實現這個頁面,同樣我們也要使用一個共通的viewModel完成前台UI與JSON數據交互的處理,同樣以超簡潔的后台代碼來處理保存。
需求分析
我們先弄明白我們要做怎么樣一個編輯的頁面。
1、最上面有一個共通的工具欄,有保存、撤消、審核、打印、還有上一條、下一條、第一條、最后一條的數據滾動按鈕,還有一些其它按鈕放在下拉按鈕中。
2、我們這個頁面支持一個主表和從表一起保存,同一個事務,首先要有主表的錄入
3、其次我們還要從表的錄入grid,從表可以增刪改,我們新增設計成從庫中選擇添加,當然也很容易實現直接新增一行。
4、然后我們可能主表中有些字段不常用,我們放在第二個tab頁簽中,如果還有從表還可以再增加頁簽
5、還有一個需求就是,我保存時,只保存改動過的東西,比如主表只改了合同名稱,從表就修改了一行,那么我們處理應該要主表只更新一個字段,從表中只修改一條數據。如果沒有值被修改時,保存按鈕不響應。
技術實現
前端要實現
1、頁面布局
2、綁定控件
3、UI與JSON數據交互的viewModel
后台web api要實現
1、主表(增、改)及從表(增、刪、改)在一個事務中保存
好我們還是在我們的mms區域中做示例,還是選擇一個跟我上一篇一樣的[材料接收的業務]
上一篇中我們已經創建了材料接收的控件器RecieveController.cs,其中已經寫了查詢的頁面Index及查詢的api Get方法,現在我們先添加編輯的頁面。
在mvc controller中添加Edit Actioin
using System; using System.Web.Mvc; using Zephyr.Core; using Zephyr.Models; using Zephyr.Web.Areas.Mms.Common; namespace Zephyr.Areas.Mms.Controllers { public class ReceiveController : Controller { //查詢頁面 public ActionResult Index() { ... } //編輯頁面 public ActionResult Edit(string id) { return View(); } } }
然后我們右擊這個action,添加一個對應的view頁面~/Views/Receive/Edit.cshtml
@{ ViewBag.Title = "Edit"; Layout = "~/Views/Shared/_Layout.cshtml"; } @section scripts{ <script src="~/Areas/Mms/ViewModels/mms.com.js"></script> <script src="~/Areas/Mms/ViewModels/mms.viewModel.edit.js"></script> <script type="text/javascript"> var viewModel = function (data) { var self = this; mms.viewModel.edit.apply(self, arguments); //繼承mms.viewModel.edit this.grid.OnAfterCreateEditor = function(editors){ //在grid行編輯開始時綁定金額=單價*數量的計算 及 加上數量的驗證 mms.com.bindCalcTotalMoney(self, "Num", "UnitPrice", "Money", "TotalMoney")(editors); $.fn.validatebox.defaults.rules.checkNum = { validator:function(value,param){ return parseFloat(value) <= parseFloat(editors['CheckNum'].target.numberbox('getValue')); }, message:'入庫數量不能大於驗收數量!' }; }; }; var data = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model)); ko.bindingViewModel(new viewModel(data)); </script> } <div class="z-toolbar"> <a href="#" plain="true" class="easyui-linkbutton" icon="icon-save" title="保存" data-bind="click:saveClick">保存</a> <a href="#" plain="true" class="easyui-linkbutton" icon="icon-undo" title="撤消" data-bind="click:rejectClick">撤消</a> <a href="#" plain="true" class="easyui-linkbutton" icon="icon-user-accept" title="審核" data-bind="click:auditClick,easyuiLinkbutton:approveButton" >審核</a> <a href="#" plain="true" class="easyui-linkbutton" icon="icon-printer" title="打印" data-bind="click:printClick">打印</a> <div class="datagrid-btn-separator"></div> <a href="#" class="easyui-splitbutton" data-options="menu:'#divother',iconCls:'icon-application_go'" title="其他">其他</a> <div class="datagrid-btn-separator"></div> <a href="#" plain="true" class="easyui-linkbutton" icon="icon-resultset_first" title="第一條" data-bind="click:firstClick,linkbuttonEnable:scrollKeys.firstEnable" ></a> <a href="#" plain="true" class="easyui-linkbutton" icon="icon-resultset_previous" title="上一條" data-bind="click:previousClick,linkbuttonEnable:scrollKeys.previousEnable"></a> <a href="#" plain="true" class="easyui-linkbutton" icon="icon-resultset_next" title="下一條" data-bind="click:nextClick,linkbuttonEnable:scrollKeys.nextEnable" ></a> <a href="#" plain="true" class="easyui-linkbutton" icon="icon-resultset_last" title="最后一條" data-bind="click:lastClick,linkbuttonEnable:scrollKeys.lastEnable" ></a> </div> <div id="divother" style="width:100px; display:none;"> <div data-options="iconCls:'icon-add'">新增</div> <div data-options="iconCls:'icon-cross'">刪除</div> <div data-options="iconCls:'icon-arrow_refresh'">刷新</div> </div> <div id="master" class="container_12" data-bind="inputwidth:0.9"> <div class="grid_1 lbl">單據編號</div> <div class="grid_3 val"><input type="text" data-bind="value:form.BillNo,readOnly:true" class="z-txt readonly"/></div> <div class="grid_1 lbl">單據日期</div> <div class="grid_3 val"><input type="text" data-bind="dateboxValue:form.BillDate" class="z-txt easyui-datebox" /></div> <div class="grid_1 lbl">經辦人</div> <div class="grid_3 val "><input type="text" data-bind="value:form.DoPerson" class="z-txt easyui-validatebox" /></div> <div class="clear"></div> <div class="grid_1 lbl required">供應商</div> <div class="grid_3 val"><input type="text" data-bind="lookupValue:form.SupplierCode" required="true" class="z-txt easyui-lookup" data-options="lookupType:'merchants',queryParams:{MerchantsProperty:'\'采購\''}"/></div> <div class="grid_1 lbl required">庫房</div> <div class="grid_3 val"><input type="text" data-bind="comboboxValue:form.WarehouseCode,datasource:dataSource.warehouseItems" class="z-txt easyui-combobox" required="true" /></div> <div class="grid_1 lbl required">收料日期 </div> <div class="grid_3 val"><input type="text" data-bind="dateboxValue:form.ReceiveDate" class="easyui-datebox z-txt" required="true" /></div> <div class="clear"></div> <div class="grid_1 lbl required">供應類型 </div> <div class="grid_3 val"><input type="text" data-bind="comboboxValue:form.SupplyType,datasource:dataSource.supplyType" class="easyui-combobox z-txt" required="true" /></div> <div class="grid_1 lbl required">付款方式</div> <div class="grid_3 val"><input type="text" data-bind="comboboxValue:form.PayKind,datasource:dataSource.payKinds" class="z-txt easyui-combobox" required="true"/></div> <div class="grid_1 lbl">合同名稱 </div> <div class="grid_3 val required"><input type="text" data-bind="value:form.ContractCode" class="z-txt" /></div> <div class="clear"></div> <div class="grid_1 lbl">原始票號</div> <div class="grid_3 val"><input type="text" data-bind="value:form.OriginalNum" class="z-txt" /></div> <div class="grid_1 lbl">金額</div> <div class="grid_3 val"><input type="text" id="TotalMoney" name="TotalMoney" data-bind="numberboxValue:form.TotalMoney,readOnly:true" class="z-txt easyui-numberbox readonly" data-options="min: 0, precision: 2"/></div> <div class="grid_1 lbl">備注</div> <div class="grid_3 val"><input type="text" id="Remark" name="Remark" data-bind="value:form.Remark" class="z-txt" /></div> <div class="clear"></div> </div> <div id="tt" class="easyui-tabs"> <div title="材料明細"> <table id="list" data-bind="datagrid:grid"> <thead> <tr> <th field="BillNo" hidden="true"></th> <th field="RowId" hidden="true"></th> <th field="MaterialCode" sortable="true" align="left" width="80" >材料編碼 </th> <th field="MaterialName" sortable="true" align="left" width="100" >材料名稱 </th> <th field="Model" sortable="true" align="left" width="100" >規格型號 </th> <th field="Material" sortable="true" align="left" width="80" >材質 </th> <th field="Unit" sortable="true" align="left" width="100" editor="{type: 'combobox', options:{data:data.dataSource.measureUnit}}">單位</th> <th field="CheckNum" sortable="true" align="right" width="70" editor="{type: 'numberbox',options:{min: 0}}">驗收數量</th> <th field="Num" sortable="true" align="right" width="70" editor="{type: 'numberbox',options:{min: 0,validType:'checkNum'}}">入庫數量</th> <th field="UnitPrice" sortable="true" align="right" width="70" editor="{type: 'numberbox',options:{min: 0, precision: 2}}" formatter="com.formatMoney">入庫單價</th> <th field="Money" sortable="true" align="right" width="70" editor="{type: 'numberbox',options:{min: 0, precision: 2}}" formatter="com.formatMoney">金額</th> <th field="Remark" sortable="true" align="left" width="200" editor="text">備注</th> </tr> </thead> </table> </div> <div title="表單信息" class="hide" style="padding-top:2px;"> <div class="container_12" id="BillDetail" data-bind="inputwidth:0.9,autoheight:181"> <div class="clear"></div> <div class="grid_1 lbl">審批狀態</div> <div class="grid_3 val"><input type="text" data-bind="value:form.ApproveState,readOnly:true" class="z-txt readonly"/></div> <div class="grid_1 lbl">審批意見</div> <div class="grid_3 val"><input type="text" data-bind="value:form.ApproveRemark,readOnly:true" class="z-txt readonly"/></div> <div class="grid_1 lbl">審批人 </div> <div class="grid_3 val"><input type="text" data-bind="value:form.ApprovePerson,readOnly:true" class="z-txt readonly"/></div> <div class="clear"></div> <div class="grid_1 lbl">審批日期</div> <div class="grid_3 val"><input type="text" data-bind="dateboxValue:form.ApproveDate,dateboxReadOnly:true" class="z-txt easyui-datebox readonly"/></div> <div class="grid_1 lbl">編制日期</div> <div class="grid_3 val"><input type="text" data-bind="dateboxValue:form.CreateDate,dateboxReadOnly:true" class="z-txt easyui-datebox readonly" /></div> <div class="grid_1 lbl">編制人</div> <div class="grid_3 val "><input type="text" data-bind="value:form.CreatePerson,readOnly:true" class="z-txt readonly" /></div> <div class="clear"></div> <div class="grid_1 lbl">修改日期</div> <div class="grid_3 val"><input type="text" data-bind="dateboxValue:form.UpdateDate,dateboxReadOnly:true" class="z-txt easyui-datebox readonly" /></div> <div class="grid_1 lbl">修改人</div> <div class="grid_3 val "><input type="text" data-bind="value:form.UpdatePerson,readOnly:true" class="z-txt readonly" /></div> </div> </div> </div>
代碼貼出來有換行變得很不整齊,沒辦法了,大家將就看吧。上面這估代碼我還是要解釋下,同樣data-bind是knouckout的寫法,easyui-xxx及data-optionis是easyui的寫法。還有三段腳本,第一個是引入項目共通腳本,第二個是引入編輯頁面共通的viewModel,第三個是繼承共通的viewModel再加上本頁面中的一些計算或驗證之類並綁定viewModel到頁面上。
我們再看看我們的編輯的共通viewModel,這段代碼同樣也就100行左右
/** * 模塊名:mms viewModel * 程序名: mms.viewModel.edit.js * Copyright(c) 2013-2015 liuhuisheng [ liuhuisheng.xm@gmail.com ] **/ var mms = mms || {}; mms.viewModel = mms.viewModel || {}; mms.viewModel.edit = function (data) { var self = this; this.dataSource = data.dataSource; //下拉框的數據源 this.urls = data.urls; //api服務地址 this.resx = data.resx; //中文資源 this.scrollKeys = ko.mapping.fromJS(data.scrollKeys); //數據滾動按鈕(上一條下一條) this.form = ko.mapping.fromJS(data.form||data.defaultForm); //表單數據 this.setting = data.setting; this.defaultRow = data.defaultRow; //默認grid行的值 this.defaultForm = data.defaultForm; //主表的默認值 this.grid = { size: { w: 6, h: 177 }, pagination: false, remoteSort: false, url: ko.observable(self.urls.getdetail + self.scrollKeys.current()) }; this.gridEdit = new com.editGridViewModel(self.grid); this.grid.onDblClickRow = self.gridEdit.begin; this.grid.onClickRow = self.gridEdit.ended; this.grid.toolbar = [{ text: '選擇在庫材料', iconCls: 'icon-search', handler: function () { mms.com.selectMaterial(self, { _xml: 'mms.material_dict' }); } }, '-', { text: '刪除材料', iconCls: 'icon-remove', handler: self.gridEdit.deleterow }]; this.rejectClick = function () { ko.mapping.fromJS(data.form, {}, self.form); self.gridEdit.reject(); com.message('success', self.resx.rejected); }; this.firstClick = function () { self.scrollTo(self.scrollKeys.first()); }; this.previousClick = function () { self.scrollTo(self.scrollKeys.previous()); }; this.nextClick = function () { self.scrollTo(self.scrollKeys.next()); }; this.lastClick = function () { self.scrollTo(self.scrollKeys.last()); }; this.scrollTo = function (id) { if (id == self.scrollKeys.current()) return; com.setLocationHashId(id); com.ajax({ type: 'GET', url: self.urls.getmaster + id, success: function (d) { ko.mapping.fromJS(d, {}, self); ko.mapping.fromJS(d, {}, data); } }); self.grid.url(self.urls.getdetail + id); self.grid.datagrid('loaded'); }; this.saveClick = function () { self.gridEdit.ended(); //結束grid編輯狀態 var post = { //傳遞到后台的數據 form: com.formChanges(self.form, data.form, self.setting.postFormKeys), list: self.gridEdit.getChanges(self.setting.postListFields) }; if ((self.gridEdit.ended() && com.formValidate()) && (post.form._changed || post.list._changed)) { com.ajax({ url: self.urls.edit, data: ko.toJSON(post), success: function (d) { com.message('success', self.resx.editSuccess); ko.mapping.fromJS(post.form, {}, data.form); //更新舊值 self.gridEdit.accept(); } }); } }; this.auditClick = function () { var updateArray = ['ApproveState', 'ApproveRemark']; mms.com.auditDialog(this.form, function (d) { com.ajax({ type: 'POST', url: self.urls.audit + self.scrollKeys.current(), data: JSON.stringify(d), success: function () { com.message('success', d.status == "passed" ? self.resx.auditPassed : self.resx.auditReject); if (data.form) for (var i in updateArray) data.form[updateArray[i]] = self.form[updateArray[i]](); } }); }); }; this.approveButton = { iconCls: ko.computed(function () { return self.form.ApproveState() == "passed" ? "icon-user-reject" : "icon-user-accept"; }), text: ko.computed(function () { return self.form.ApproveState() == "passed" ? "取消審核" : "審核"; }) }; this.printClick = function () { com.openTab('打印報表', '/report?p1=0002&p2=2012-1-1', 'icon-printer_color'); }; };
這段代碼利用了很多的ko的mapping組件去更新數據對象。需要了解下kouckoujs才比較好理解。
這個viewModel中上面也定義了很多變量,我基本都有注釋,接下來this.grid是我賦給明細表格的屬性,除了這些,我data-bind=”datagrid:grid”綁定時還有給它默認的屬性。這里面對明細grid的增刪改操作的對象this.gridEdit = new com.editGridViewModel(self.grid); 利用到我的另一個共通的grid編輯的viewModel。這里面已經實現了對easyui datagrid的操作,我這里了給大家共享下
com.editGridViewModel = function (grid) { var self = this; this.begin = function (index, row) { if (index== undefined || typeof index === 'object') { row = grid.datagrid('getSelected'); index = grid.datagrid('getRowIndex', row); } self.editIndex = self.ended() ? index : self.editIndex; grid.datagrid('selectRow', self.editIndex).datagrid('beginEdit', self.editIndex); }; this.ended = function () { if (self.editIndex == undefined) return true; if (grid.datagrid('validateRow', self.editIndex)) { grid.datagrid('endEdit', self.editIndex); self.editIndex = undefined; return true; } grid.datagrid('selectRow', self.editIndex); return false; }; this.addnew = function (rowData) { if (self.ended()) { if (Object.prototype.toString.call(rowData) != '[object Object]') rowData = {}; rowData = $.extend({_isnew:true},rowData); grid.datagrid('appendRow', rowData); self.editIndex = grid.datagrid('getRows').length - 1; grid.datagrid('selectRow', self.editIndex); self.begin(self.editIndex, rowData); } }; this.deleterow = function () { var selectRow = grid.datagrid('getSelected'); if (selectRow) { var selectIndex = grid.datagrid('getRowIndex', selectRow); if (selectIndex == self.editIndex) { grid.datagrid('cancelEdit', self.editIndex); self.editIndex = undefined; } grid.datagrid('deleteRow', selectIndex); } }; this.reject = function () { grid.datagrid('rejectChanges'); }; this.accept = function () { grid.datagrid('acceptChanges'); var rows = grid.datagrid('getRows'); for (var i in rows) delete rows[i]._isnew; }; this.getChanges = function (include, ignore) { if (!include) include = [], ignore = true; var deleted = utils.filterProperties(grid.datagrid('getChanges', "deleted"), include, ignore), updated = utils.filterProperties(grid.datagrid('getChanges', "updated"), include, ignore), inserted = utils.filterProperties(grid.datagrid('getChanges', "inserted"), include, ignore); var changes = { deleted: deleted, inserted: utils.minusArray(inserted, deleted), updated: utils.minusArray(updated, deleted) }; changes._changed = (changes.deleted.length + changes.updated.length + changes.inserted.length)>0; return changes; }; this.isChangedAndValid = function () { return self.ended() && self.getChanges()._changed; }; };
grid的編輯實現之后接下來就是一些按鈕的實現。這里主要說一下保存按鈕
this.saveClick = function () { … }
這里面第一句話就是調用grid編輯的對象去結束行編輯,然后取得傳到后台的數據,post={form:xxx,list:xxxx},form是指主表的數據,而且過濾掉了未改變的字段,而list是指我明細grid的編輯的數據結果,可以看com.editGridViewModel中的getChanges的方法,它的結構應該是list={deleted:xxx,inserted:xxx,updated:xxx};
然后我們還要判斷主表輸入驗證是否通過,grid的輸入驗證是否通過,及它們是否有修改,滿足條件才去ajax請求保存數據。
前端就說到這里,那么我們的viewModel還需要一些參數,我們還是從后台mvc controller中返回。回過頭再編輯Edit的action方法,傳遞我們viewModel中需要的參數
public ActionResult Edit(string id) { var userName = MmsHelper.GetUserName(); var currentProject = MmsHelper.GetCurrentProject(); var data = new ReceiveApiController().GetEditMaster(id); var codeService = new sys_codeService(); var model = new { form = data.form, scrollKeys = data.scrollKeys, urls = new { getdetail = "/api/mms/receive/getdetail/", //獲取明細數據api
getmaster = "/api/mms/receive/geteditmaster/", //獲取主表數據及數據滾動數據api edit = "/api/mms/receive/edit/", //數據保存api audit = "/api/mms/receive/audit/", //審核api getrowid = "/api/mms/receive/getnewrowid/" //獲取新的明細數據的主鍵(日語叫采番) }, resx = new { rejected = "已撤消修改!", editSuccess = "保存成功!", auditPassed ="單據已通過審核!", auditReject = "單據已取消審核!" }, dataSource = new{ measureUnit = codeService.GetMeasureUnitListByType(), supplyType = codeService.GetValueTextListByType("SupplyType"), payKinds = codeService.GetValueTextListByType("PayType"), warehouseItems = new mms_warehouseService().GetWarehouseItems(currentProject) }, defaultForm = new mms_receive().Extend(new { //定義主表數據的默認值 BillNo = id, BillDate = DateTime.Now, DoPerson = userName, ReceiveDate = DateTime.Now, SupplyType = codeService.GetDefaultCode("SupplyType"), PayKind = codeService.GetDefaultCode("PayType"), }), defaultRow = new { //定義從表數據的默認值 CheckNum = 1, Num = 1, UnitPrice = 0, Money = 0, PrePay = 0 }, setting = new { postFormKeys = new string[] { "BillNo" }, //主表的主鍵 postListFields = new string[] { "BillNo", "RowId", //定義從表中哪些字段要傳遞到后台 "MaterialCode", "Unit", "CheckNum", "Num", "UnitPrice", "PrePay", "Money", "Remark" } } }; return View(model); }
上面定義的這些數據,就是我們共通的viewModel中需要的數據,根據這些數據我們的viewModel就能創建出不同的實例了。
接下來我們就開始實現Web Api服務,包括出現的urls當然的查詢主表單條數據,查詢明細數據,保存等。我們在ReceiveApiController中添加以下方法:
public class ReceiveApiController : ApiController { // GET api/mms/send/geteditmaster 取得編輯頁面中的主表數據及上一頁下一頁主鍵 public dynamic GetEditMaster(string id) { var projectCode = MmsHelper.GetCookies("CurrentProject"); var masterService = new mms_receiveService(); return new{ form = masterService.GetModel(ParamQuery.Instance().AndWhere("BillNo", id)), scrollKeys = masterService.ScrollKeys("BillNo", id, ParamQuery.Instance().AndWhere("ProjectCode", projectCode)) }; } // 地址:GET api/mms/send/getnewrowid 預取得新的明細表的行號 public string GetNewRowId(int id) { var service = new mms_receiveDetailService(); return service.GetNewKey("RowId", "maxplus",id); } // 地址:GET api/mms/send/getdetail 功能:取得收料單明細信息 public dynamic GetDetail(string id) { var query = RequestWrapper .InstanceFromRequest() .SetRequestData("BillNo",id) .LoadSettingXmlString(@" <settings defaultOrderBy='MaterialCode'> <select> A.*, B.MaterialName,B.Model,B.Material </select> <from> mms_receiveDetail A left join mms_materialInfo B on B.MaterialCode = A.MaterialCode </from> <where> <field name='BillNo' cp='equal'></field> </where> </settings>"); var pQuery = query.ToParamQuery(); var ReceiveService = new mms_receiveService(); var result = ReceiveService.GetDynamicListWithPaging(pQuery); return result; } // 地址:POST api/mms/send 功能:保存收料單數據 [System.Web.Http.HttpPost] public void Edit(dynamic data) { var formWrapper = RequestWrapper.Instance().LoadSettingXmlString(@" <settings> <table> mms_receive </table> <where> <field name='BillNo' cp='equal'></field> </where> </settings>"); var listWrapper = RequestWrapper.Instance().LoadSettingXmlString(@" <settings> <columns ignore='MaterialName,Model,Material'></columns> <table> mms_receiveDetail </table> <where> <field name='BillNo' cp='equal'></field> <field name='RowId' cp='equal'></field> </where> </settings>"); var service = new mms_receiveService(); var result = service.Edit(formWrapper, listWrapper, data); } }
同樣,這段代碼也是利用我的框架中的RequestWrapper寫出來的,我這里就不再解釋RequestWrapper這個對象了,在我的上一篇博客中有解釋過:
一個共通的viewModel搞定所有的分頁查詢一覽及數據導出:http://www.cnblogs.com/xqin/archive/2013/06/03/3114634.html
我這里只說一下最后一個保存的方法:
傳遞到后台的data應該是這種結構 data={form:{a:’’,b:’’,…},list:{deleted: [{…},{…},…],inserted: [{},{},…],updated: [{},{},…]}};
我再定義兩個RequestWrapper對象配置這個保存操作,告訴框架我這些數據應該如果去保存。然后把這個配置信息及我的數據傳給框架的共通方法去處理。這樣,上面的Edit方法就簡單的完成了這個主從表一起保存的復雜的處理。
這樣,我們的一個復雜的查看、編輯頁面就完成了。可以跟上一個查詢的頁面連接在一起。在查詢頁面雙擊或編輯時會打開編輯頁面的新的tab頁。
效果展示
選擇在庫存材料
我們在主表中修改幾個字段,從表中新增一條,刪除一條,修改一條
測試數據滾動 上一條 下一條 第一條 最后一條都沒問題 就不截圖了。
打印報表,我這里直接寫死了一個測試報表在viewModel中,可以寫成從后台傳過來的
還有一些不常用的其它按鈕我放在其它下拉按鈕中。
審核按鈕在查詢與編輯頁面中都有,審核之后,審核按鈕變成取消審核按鈕
后述
和上一篇一樣,我們有了共通的edit viewModel之后,就可以非常簡潔的代碼完成一個比較復雜的數據編輯頁面了,這個頁面是我參照ERP系統中的編輯頁面完成的。開發一個新的編輯頁面我們要做的只有:
1、前台頁面布局修改一下
2、可以繼承共通的viewModel,然后可能還要可以加上一些驗證及頁面中前台的一些處理。如果沒有特殊的東西可以直接用共通的viewModel即可。
3、后台對應一個獲取主表及數據滾動的數據、獲取從表數據、及保存的Web Api服務,利用框架都只有幾行代碼,很簡單。
這樣就完成了。
這個編輯頁面和我之前寫的查詢頁面都屬性於典型的業務頁面,基本上可以搞定企業信息化中的60%以上的頁面了。寫完這兩篇,大家對我的框架應該有一定了解了,接下來准備寫一些框架中的實現或者系統管理模塊。因為在這種開發模式下前台后都有一定的工作量,我可能會分前台后台分開來寫,謝謝大家的支持!