实战之订单模块MVVM架构实例教程
extjs学习(七)~~实战之订单模块 mvvm架构
目录
一、开发一个“商家订单”的功能~~ 效果图
二、开发架构 -- mvvm
三、开发思路- - mvvm
四、开发代码 ~~ 以下采用mvvm分层的开发模式。
五、代码分享
一、开发一个“商家订单”的功能~~ 效果图
二、开发架构 -- mvvm
【what】
model–view–viewmodel(mvvm) 是一个软件架构设计模式,由微软 wpf 和 silverlight 的架构师 ken cooper 和 ted peters 开发,是一种简化用户界面的事件驱动方式。由 john gossman(同样也是 wpf 和 silverlight 的架构师)于2005年在他的博客上发表。
三、开发思路- - mvvm
上一篇给大家演示了 反向思路,这次的话给大家演示正向思路,
正向思路:也就是从0开始开发,当我需要什么的时候就去开发使用什么组件。
first:订单模块主视图,因此需要一个主视图组件。— — order.js
second:主视图中,放一个表格,因此需要一个表格组件。表格的column跟bbar跟grid写在一个文件中 — — ordergrid.js
third:grid需要显示数据,因此需要一个数据源store --orderstore.js
four:而我们的store数据源需要一个model — —ordermodel.js
five:这一步很重要,就是将我们的数据绑定到viewmodel上,这一步是我们之前mvc架构没有的操作,参考下面的代码:
/** 1.绑定到主视图 2.通过bind属性绑定到具体的子视图 8*/ ext.define('admin.view.order.orderviewmodel', { extend: 'ext.app.viewmodel', alias: 'viewmodel.orderviewmodel', stores: { orderlists: { type: 'orderstore',//store reference ==store的属性 alias: 'store.orderstore', autoload: true //auto load } } });
//在主视图中,grid.js bind:'{orderlists}',
six:表格上面需要一个查询表单 — — ordergridqueryform.js
seven:查询触发函数,因此需要一个控制器 — —orderviewcontroller.js
eight:添加数据需要弹出一个外框组件 — —ordergridwindow.js
nine :弹窗组件中放入一个 表单 — —ordergridform.js
综上所述:九个步骤即可开发出这个模块
四、开发代码 ~~ 以下采用mvvm分层的开发模式。
first:订单模块主视图,因此需要一个主视图组件。— — order.js
/** *订单模块主视图 在主视图配置viewcontroller和viewmodel, 那么主视图的子视图也可以访问配置好viewcontroller和viewmodel。 */ ext.define('admin.view.order.order', { //1.修改文件路径 extend: 'ext.container.container', //2.继承的组件类型 //3.重写继承组件的属性: xtype: 'order', //这是第seven步骤开发的控制器,通过以下的开发进行绑定 controller: 'orderviewcontroller', //视图绑定viewcontroller //这是第five步骤开发的viewmodel,将viewmodel绑定到主视图 viewmodel : {type: 'orderviewmodel'}, //视图绑定viewmodel layout:'fit', margin: '20 20 20 20', items: [{ //这是第二步骤开发的表格组件 xtype: 'ordergrid' }] });
second:主视图中,放一个表格,因此需要一个表格组件。表格的column跟bbar跟grid写在一个文件中 — — ordergrid.js
/** *订单模块子视图 */ ext.define('admin.view.order.ordergrid', { //1.修改文件路径 extend: 'ext.grid.panel', //2.继承的组件类型 //3.重写继承组件的属性: xtype: 'ordergrid', id:'ordergrid', title:'订单列表', selmodel: ext.create('ext.selection.checkboxmodel'), bind:'{orderlists}', dockeditems: [{ xtype: 'toolbar', dock: 'top', items: [ext.apply({xtype: 'ordergridqueryform'})] }], columns: [ {text: 'id' ,sortable:true ,dataindex:'orderid',hidden:true}, {text: '订单编号' ,sortable:true ,dataindex:'ordernumber' ,width:100}, {text: '创建时间' ,sortable:true ,dataindex:'createtime' ,width:160 ,renderer: ext.util.format.daterenderer('y/m/d h:i:s')}, {text: '优先级',sortable:true ,dataindex:'level' ,width:75}, {text: '供应商id' ,sortable:true ,dataindex:'supplierid',hidden:true}, {text: '供应商名称' ,sortable:false ,dataindex:'suppname' ,width:120}, {text: '商品编号' ,sortable:false ,dataindex:'pid' ,hidden:true}, {text: '商品名称' ,sortable:false ,dataindex:'pname' ,width:120}, {text: '商品数量' ,sortable:true ,dataindex:'pnumber' ,width:75}, {text: '商品单价' ,sortable:false ,dataindex:'cost' ,width:75}, {text: '总价格' ,sortable:false ,dataindex:'totalprice' ,width:130}, {text: '是否入库' ,sortable:false ,dataindex:'isincomestyle' ,flex:1} ], // paging bar on the bottom bbar: ext.create('ext.pagingtoolbar', { bind:'{orderlists}', displayinfo: true, displaymsg: 'displaying topics {0} - {1} of {2}', emptymsg: "no topics to display", items:['-', { //xtype:'button', text: '新建订单', iconcls: 'x-fa fa-plus', handler: 'ordergridopenaddwindow'//绑定orderviewcontroller中的事件 }, { //xtype:'button', text: '修改订单', iconcls: 'x-fa fa-undo', handler: 'ordergridopeneditwindow'//绑定orderviewcontroller中的事件 } , { //xtype:'button', text: '删除订单', iconcls: 'x-fa fa-trash ', handler: 'ordergriddeletedate'//绑定orderviewcontroller中的事件 }, { //xtype:'button', text: '导出订单', iconcls: 'fa fa-share-alt', handler: 'ordergridexportxls'//绑定orderviewcontroller中的事件 }] }) });
third:grid需要显示数据,因此需要一个数据源store --orderstore.js
ext.define('admin.store.order.orderstore', { extend: 'ext.data.store', alias: 'store.orderstore', //1.store取别名(reference) model: 'admin.model.order.ordermodel',//2.设置model的全路径 //data:{ // 'items':[ // { 'orderid': 1, 'ordernumber': 'od0000001', "createtime":"2017/9/8 16:35:00", "level":"高" , 'goodsid' :10001, 'goodsname' :'a', 'goodsnum' :'50', 'goodsprice' :220, 'totalprice' :1100}, // { 'orderid': 2, 'ordernumber': 'od0000002', "createtime":"2017/9/8 16:35:00", "level":"中", 'goodsid' :10002, 'goodsname' :'b', 'goodsnum' :'40', 'goodsprice' :50, 'totalprice' :2000}, // { 'orderid': 3, 'ordernumber': 'od0000099', "createtime":"2017/9/8 16:35:00", "level":"中" , 'goodsid' :10003, 'goodsname' :'c', 'goodsnum' :'30', 'goodsprice' :150, 'totalprice' :4500}, // { 'orderid': 4, 'ordernumber': 'od0000100', "createtime":"2017/9/8 16:35:00", "level":"低" , 'goodsid' :10004, 'goodsname' :'d', 'goodsnum' :'20', 'goodsprice' :100, 'totalprice' :2200} // ] //}, proxy: { //type: 'memory', type: 'ajax', url: 'order/findpage.json', reader: { type: 'json', //rootproperty: 'items' rootproperty: 'content', totalproperty: 'totalelements' }, simplesortmode:true }, pagesize: 14, autoload: true, remotesort: true,//全局排序 sorters: { direction: 'desc', property: 'createtime' } });
four:而我们的store数据源需要一个model — —ordermodel.js.
ext.define('admin.model.order.ordermodel', { extend: 'admin.model.base', fields: [ {name:'orderid' ,type: 'int'}, {name:'ordernumber' ,type: 'string'}, {name:'createtime' ,type: 'date'}, {name:'level' ,type: 'string'}, {name:'supplierid' ,type: 'int'}, {name:'suppname' ,type: 'string'}, // {name:'goodsid' ,type: 'int'}, // {name:'goodsname' ,type: 'string'}, {name:'pnumber' ,type: 'int'}, // {name:'goodsprice' ,type: 'float'}, {name:'totalprice' ,type: 'float'} ] });
five:这一步很重要,就是将我们的数据绑定到viewmodel上,
/** 1.绑定到主视图 2.通过bind属性绑定到具体的子视图 8*/ ext.define('admin.view.order.orderviewmodel', { extend: 'ext.app.viewmodel', alias: 'viewmodel.orderviewmodel', stores: { orderlists: { type: 'orderstore',//store reference ==store的属性 alias: 'store.orderstore', autoload: true //auto load } } });
six:表格上面需要一个查询表单 — — ordergridqueryform.js
ext.define('admin.view.order.ordergridqueryform', { extend: 'ext.form.panel', alias: 'widget.ordergridqueryform', id:'myordergridqueryform', requires: [ 'ext.button.button', 'ext.form.field.text', 'ext.form.field.file', 'ext.form.field.htmleditor', 'ext.form.field.textarea', 'ext.form.field.time', 'ext.form.field.combobox', 'ext.form.field.date', 'ext.form.field.radio', 'ext.form.field.hidden' ], //viewmodel: {type: 'emailcompose'}, //cls: 'email-compose', controller: 'orderviewcontroller', layout: { type:'hbox', align:'stretchmax' }, bodypadding: 0, scrollable: false, defaults: { labelwidth: 65, labelseparator: ':' }, items:[{ xtype: 'textfield', fieldlabel: '订单编号', reference: 'ordersearchform-ordernumber', name:'ordernumber', //allowblank : false, padding:4 },{ xtype: 'datefield', format: 'y/m/d h:i:s', fieldlabel: '创建时间', editable:true, reference: 'ordersearchform-createtime', name:'createtime', padding:4 ,width:250 },{ xtype: 'combobox', reference: 'ordersearchform-isincome', editable:false, fieldlabel: '是否入库', name:'isincome', padding:4, store: ext.create('ext.data.store', { fields: ['value', 'name'], data : [ {"value":1, "name":"已入库"}, {"value":0, "name":"未入库"} ] }), querymode: 'local', displayfield: 'name', valuefield: 'value' },{ xtype: 'combobox', reference: 'ordersearchform-level', editable:false, fieldlabel: '优先级', name:'level', padding:4, store: ext.create('ext.data.store', { fields: ['value', 'name'], data : [ {"value":"high", "name":"高"}, {"value":"medium", "name":"中"}, {"value":"low", "name":"低"} ] }), querymode: 'local', displayfield: 'name', valuefield: 'value' },{ xtype: 'button', ui: 'gray', margin:5, //align:'right', iconcls: 'x-fa fa-search', text: '查询', handler: 'ordersearchformsubmit' } ,{ xtype: 'button', ui: 'gray', margin:5, //align:'right', iconcls: 'x-fa fa-search', text: '置空', handler: function() { this.up('form').getform().reset(); var store = this.up('gridpanel').getstore(); ext.getcmp('ordergrid').store.getproxy().extraparams ={ }; store.reload(); //ext.getcmp('ordergrid').store.getproxy().url = 'order/findpage'; } } ], //,{ // xtype: 'textfield', // fieldlabel: 'goodsid', // name:'goodsid' //} //,{ // xtype: 'textfield', // fieldlabel: 'goodsname', // name:'goodsname' //} //,{ // xtype: 'textfield', // fieldlabel: 'goodsnum', // name:'goodsnum' //} //,{ // xtype: 'textfield', // fieldlabel: 'goodsprice', // name:'goodsprice' //} /* bbar: { items: ['->',{ xtype: 'button', //ui: 'soft-red', text: '查询', handler: '' },{ xtype: 'button', //ui: 'gray', text: '取消', handler: '' }] } */ });
seven:查询触发函数,因此需要一个控制器 curd代码都在里面— —orderviewcontroller.js
ext.define('admin.view.order.orderviewcontroller', { extend: 'ext.app.viewcontroller', alias: 'controller.orderviewcontroller', ordergridopenaddwindow: function(btn) { var cfg = ext.apply({ xtype: 'ordergridwindow', items: [ext.apply({xtype: 'ordergridform'})] },{ title:'创建订单'//,width: 800//,height: 600 }); ext.create(cfg); }, ordergridformsubmit: function(btn) { var ordergridform = btn.up('form').getform(); var win = btn.up('window'); ordergridform.submit( { //waittitle : '请稍后...', //waitmsg : '正在保存订单信息,请稍后...', url : 'order/saveorupdate', method : 'post', success : function(form, action) { ext.msg.alert("提示",action.result.msg); win.close(); //必须ordergrid中增加对应id:'ordergrid'属性 ext.getcmp('ordergrid').store.reload(); }, failure : function(form, action) { win.close(); ext.msg.alert("提示",action.result.msg); } }); }, ordergridwindowclose: function(btn) { var win = btn.up('window'); if(win){ win.close(); } }, ordergridopeneditwindow: function(btn) { var grid = btn.up('gridpanel');//获取grid视图 var selmodel = grid.getselectionmodel();//获取grid的selectionmodel if (selmodel.hasselection()) {//判断是否选中记录 var record = selmodel.getselection()[0];//获取选中的第一条记录 //创建修改window和form var ordergridwindow = ext.widget('ordergridwindow',{ title:'修改订单', items: [{xtype: 'ordergridform'}] }); //让form加载选中记录 ordergridwindow.down("form").getform().loadrecord(record); }else{ ext.msg.alert('提示',"请选择一行数据进行修改!"); } }, ordergriddeletedate: function(btn) { var grid = btn.up('gridpanel'); var selmodel = grid.getselectionmodel(); if (selmodel.hasselection()) { ext.msg.confirm("警告", "确定要删除吗?", function (button) { if (button == "yes") { var selected = selmodel.getselection(); var selectids = []; //要删除的id ext.each(selected, function (record) { //alert(record.data.orderid); selectids.push(record.data.orderid); }) ext.ajax.request({ url : 'order/deletebyids', method : 'post', params : { ids:selectids }, success: function(response, options) { var json = ext.util.json.decode(response.responsetext); if(json.success){ ext.msg.alert('操作成功', json.msg); grid.getstore().reload(); }else{ ext.msg.alert('操作失败', json.msg); } } }); } }); }else{ ext.msg.alert('提示',"请选择一行数据进行删除!"); } }, ordersearchformsubmit:function(btn){ var store = btn.up('gridpanel').getstore(); //2.按照所选字段进行查询参数(条件)的扩展 var formvalues=btn.up('form').getform().getvalues(); // alert(formvalues["createtime"]); // alert(formvalues["ordernumber"]); //alert(formvalues["level"]); if (formvalues["createtime"]==''&&formvalues["ordernumber"]==''&&formvalues["level"]==''&&formvalues["isincome"]==''&&formvalues["isincome"]!=0) { store.getproxy().extraparams ={ }; store.reload(); }else if(formvalues["createtime"]==''){ store.getproxy().extraparams ={ }; //alert('createtime kong'); //1.清空所有查询条件 ext.apply(store.proxy.extraparams, { ordernumber:'', //createtime:'', level:'', isincome:'' }); ext.apply(store.proxy.extraparams, { ordernumber:this.lookupreference('ordersearchform-ordernumber').getvalue(), level:this.lookupreference('ordersearchform-level').getvalue(), isincome:this.lookupreference('ordersearchform-isincome').getvalue() //createtime:null, }); store.load({params: {start:0,limit:14,page:1}}); }else{ //1.清空所有查询条件 ext.apply(store.proxy.extraparams, { ordernumber:'', createtime:'', level:'', isincome:'' }); ext.apply(store.proxy.extraparams, { ordernumber:this.lookupreference('ordersearchform-ordernumber').getvalue(), level:this.lookupreference('ordersearchform-level').getvalue(), createtime:ext.util.format.date(this.lookupreference('ordersearchform-createtime').getvalue(), 'y/m/d h:i:s'), isincome:this.lookupreference('ordersearchform-isincome').getvalue() }); store.load({params: {start:0,limit:14,page:1}}); } //alert(store.proxy.extraparams.createtime); //myordergridqueryform.form.reset(); //btn.up('gridpanel').reset(); } , ordergridexportxls:function(){ //alert("导出功能"); /* ext.ajax.request( { url : 'order/excel/export', method : 'get', success : function(response, options) { var json = ext.util.json.decode(response.responsetext); if(json.success){ ext.msg.alert('操作成功', json.msg); grid.getstore().reload(); }else{ ext.msg.alert('操作失败', json.msg); } }, failure : function() { alert("failse"); } });*/ window.location.href = "order/excel/export"; } });
eight:添加数据需要弹出一个外框组件 — —ordergridwindow.js
ext.define('admin.view.order.ordergridwindow', { extend: 'ext.window.window', alias: 'widget.ordergridwindow', autoshow: true, modal: true, layout: 'fit', // width: 200, //height: 200, afterrender: function () { var me = this; me.callparent(arguments); me.syncsize(); // since we want to always be a %age of the viewport, we have to watch for // resize events. ext.on(me.resizelisteners = { resize: me.onviewportresize, scope: me, buffer: 50 }); }, dodestroy: function () { ext.un(this.resizelisteners); this.callparent(); }, onviewportresize: function () { this.syncsize(); }, syncsize: function () { var width = ext.element.getviewportwidth(), height = ext.element.getviewportheight(); // we use percentage sizes so we'll never overflow the screen (potentially // clipping buttons and locking the user in to the dialog). this.setsize(math.floor(width * 0.36), math.floor(height * 0.52)); this.setxy([ math.floor(width * 0.05), math.floor(height * 0.05) ]); } });
nine :弹窗组件中放入一个 表单 — —ordergridform.js
ext.define('admin.view.order.ordergridform', { extend: 'ext.form.panel', alias: 'widget.ordergridform', requires: [ 'ext.button.button', 'ext.form.field.text', 'ext.form.field.file', 'ext.form.field.htmleditor', 'ext.form.field.textarea', 'ext.form.field.time', 'ext.form.field.combobox', 'ext.form.field.date', 'ext.form.field.radio', 'ext.form.field.hidden' ], //viewmodel: {type: 'emailcompose'}, //cls: 'email-compose', controller: 'orderviewcontroller', layout: { type:'vbox', align:'stretch' }, bodypadding: 10, scrollable: true, defaults: { labelwidth: 75, labelseparator: ':' }, items: [{ xtype: 'hidden', fieldlabel: 'id', name:'orderid' }, { xtype: 'hidden', fieldlabel: 'isincome', name:'isincome' },{ xtype: 'textfield', fieldlabel: '订单编号', blanktext:'订单编号不能为空', allowblank: false, name:'ordernumber' },{ xtype: 'datefield', format: 'y/m/d h:i:s', fieldlabel: '创建时间', blanktext:'创建时间不能为空', editable:true, allowblank: false, name:'createtime' },{ xtype: 'combobox', fieldlabel: '订单优先级', blanktext:'优先级不能为空', editable:false, allowblank: false, name:'level', store: ext.create('ext.data.store', { fields: ['value', 'name'], data : [ {"value":"high", "name":"高"}, {"value":"medium", "name":"中"}, {"value":"low", "name":"低"} ] }), querymode: 'local', displayfield: 'name', valuefield: 'value' }, { xtype: 'combobox', fieldlabel: '选择供应商', blanktext:'供应商不能为空', allowblank: false, name:'supplierid', store: { type:'ordershowsupplierstore' }, querymode: 'local', displayfield: 'suppname', valuefield: 'id', listeners:{ expand: function(combo, record, index) { combo.store.reload(); } } } , { xtype: 'combobox', fieldlabel: '选择商品', blanktext:'商品名称不能为空', allowblank: false, name:'pid', store: { type:'ordershowproductstore' }, querymode: 'local', displayfield: 'pname', valuefield: 'pid', listeners:{ expand: function(combo, record, index) { combo.store.reload(); } } } /*,{ xtype: 'combobox', fieldlabel: '选择商品', editable:false, allowblank: false, name:'pid', store: ext.create('ext.data.store', { fields: ['value', 'name'], data : [ {"value":"11", "name":"小郑"}, {"value":"12", "name":"林霞"}, {"value":"13", "name":"zz"}, {"value":"14", "name":"欣欣"}, {"value":"15", "name":"小杰"}, ] }), querymode: 'local', displayfield: 'name', valuefield: 'value' } */ ,{ xtype: 'textfield', fieldlabel: '产品数量', regex:/^[0-9]*[1-9][0-9]*$/, regextext:'请输入正整数', blanktext:'数量不能为空', allowblank: false, name:'pnumber' }], bbar: { items: ['->',{ xtype: 'button', //ui: 'soft-red', text: '保存', handler: 'ordergridformsubmit' },{ xtype: 'button', //ui: 'gray', text: '取消', handler: 'ordergridwindowclose' }] } });
综上所述:九个步骤即可开发出这个模块