EXTJS4.1MVC单表维护

EXTJS4.1做的单表维护,在同一个gridpanel中完成CRUD操作,数据处理方面借鉴webbuildder和pb中的处理,由于是刚开始学习extjs,可能有不合理的地方,希望大家批评,指正,下面是界面的效果图,还有MVC的相关代码。

我的一点小感受:

1。EXTJS的grid中可以设置CRUD的相关api, 但是这样的话增加,删除,修改的操作是在不同的事务里面,这样不利于事务的控制,比如说我修改了一条记录,又增加了一条记录,然后保存,会出现修改保存成功而增加保存失败,界面上会让用户感觉混乱;

而且这种方式提交时默认提交当前表格中的数据,不会将原始记录一起提交,后台无法比对数据,比如有10个字段,由于用户不知道哪个字段修改了,只能通过主键把所有字段都修改了;

再比如说当前要修改的记录已经被另一个用户修改,而你此时修改的话是不应该保存成功的,但是你通过主键去修改肯定是可以保存成功的,会造成数据覆盖的的情况发生。


2.我的这种做法主要是借鉴了pb里面的处理方式,将所有的增删改查的数据统一搜集发送到后台,后台统一操作,将操作控制在一个事务里面,后台执行顺序:删除所有要删除的数据, 删除所有在修改的记录集里面主键发生改变的记录,更新要修改的所有记录, 插入新增的记录和主键发生改变的新纪录。

修改和删除根据原始记录来查询,一旦根据原始记录没有查询出来,说明这条记录在另一个地方被修改或者删除了,程序会主动抛出异常,回滚事务。

	@Override
	public void saveOrgsByAll(List<Org> newDatasList,
			List<Org> deleteDatasList, List<Org> modifyDatasList)
			throws Exception {
		/*
		 * 执行顺序为删除,修改,新增
		 * 
		 */
		for (int i = 0; i < deleteDatasList.size(); i++) {
			int deleteResult;
			Org org = null;
			org = deleteDatasList.get(i);
			deleteResult = orgDao.deleteOrgByAll(org);
			if (deleteResult == 0) {
				throw new DeleteException();
			}
		}
		
		//主键冲突时先删除原始记录
		for (int i = 0; i < modifyDatasList.size(); i++) { 
			int deleteResult;
			Org org = null;
			org = modifyDatasList.get(i);

			if ( ! org.getHospcode().equals(org.getOrigin().getHospcode())     ) {

				deleteResult = orgDao.deleteOrgByAll(org.getOrigin());
				if (deleteResult == 0) {
					throw new DeleteException();
				}

			} 

		}
		

		for (int i = 0; i < newDatasList.size(); i++) {
			int andResult;
			Org org = null;
			org = newDatasList.get(i);
			andResult = orgDao.insertOrg(org);
			if (andResult == 0) {
				throw new InsertException();
			}
		}

		for (int i = 0; i < modifyDatasList.size(); i++) {
			int updateResult;
			int addResult;
			Org org = null;
			org = modifyDatasList.get(i);

			if ( ! org.getHospcode().equals(org.getOrigin().getHospcode())    ) {

				//主键冲突时先删除再插入,删除操作放在前面统一删除,避免主键发生冲突
				addResult = orgDao.insertOrg(org);
				if (addResult == 0) {
					throw new InsertException();
				}

			} else {
				updateResult = orgDao.updateOrgByAll(org);
				if (updateResult == 0) {
					throw new UpdateException();
				}
			}

		}

	}

 	<update id="updateRoleByAll" parameterType="com.sesan.slis.core.model.Role" >
 						update  af_role
		<set>
			<if test="roleId != origin.roleId">
				roleId = #{roleId, jdbcType=VARCHAR},
			</if>
			<if test="roleName != origin.roleName">
				roleName = #{roleName, jdbcType=VARCHAR},
			</if>
			<if test="description != origin.description">
				description = #{description, jdbcType=VARCHAR},
			</if>
			<if test="status != origin.status">
				status = #{status,jdbcType=INTEGER}
			</if>
 
		</set> 
		
		<where>
			<if test="origin.roleId !=null">
				and roleId    =#{origin.roleId, jdbcType=VARCHAR}
			</if>
			<if test="origin.roleId ==null">
				and roleId is null 
			</if>
			<if test="origin.roleName !=null">
				and roleName =#{origin.roleName, jdbcType=VARCHAR}
			</if>
			<if test="origin.roleName ==null">
				and roleName is null 
			</if>
			<if test="origin.description !=null">
				and description   =#{origin.description, jdbcType=VARCHAR}
			</if>
			<if test="origin.description ==null">
				and description   is null
			</if>			
			<if test="origin.status !=null">
				and status  =#{origin.status, jdbcType=VARCHAR}
			</if>
			<if test="origin.status ==null">
				and status  is null
			</if>		
		</where>




3.在这里很想给大家介绍pb里面数据处理的方式,觉得pb在数据处理方便做的非常好:

1.修改或者删除数据可以通过主键,也可以通过主键和可更新的列,也可以通过主键和修改的列。(我借鉴的是第二种,只要我刚开始查询出来的记录中有一列发生改变(可能被其他用户修改),就不给更新,在我读的很多代码里面大多是直接通过主键,这种做法是最简单但却是最不安全的)

2..当主键发生冲突时,可以通过先删除原始记录再插入新纪录,也可以直接更新。(如果直接更新的话有时会失败,比如两条记录中主键发生互换,无论先更新哪一条都不会成功)


4.在pb的数据窗口里面有4个内存缓冲区,它们是:

主缓冲区(PrimaryBuffer):界面上能看到的数据,可能被用户修改。
过滤缓冲区(FilterBuffer):存放从主缓冲区中过滤掉的数据。
删除缓冲区(DeleteBuffer):存放从主缓冲区中删除掉的数据。
原始缓冲区(OriginalBuffer):存放从数据库里检索到的原始数据。

此外数据窗口上有行和列的修改状态

NotModified!:行和列的值是最初从数据库中检索出的值,

DataModified!:行和列的值在最初检索后发生了改变。

New!:行是新行单并未赋值。

NewModified!:新行且某些列被赋值。


在 pb中正是有了缓冲区和标志位的存在,pb的后台才会很容易按照事先约定的方式(第3点提到的)来生成CRUD的sql语句了。


其实这些技术在EXTJS的gridpanel中有的地方也有实现,比如删除的数据可以通过store.getRemovedRecords( )来得到,数据修改了可以通过record.dirty()来判断。但是想获取原始数据却没有办法(也可能是我没有找到,如果有的话希望各位高手能指导一下),而且新行的标志位也没有,所以我在程序中是通过自己添加新行标志和保存原始数据来实现的,这个处理方式我在webbuilder中看到过,所以就借鉴过来。

 

		store.load({
		    scope: store,
		    callback: function(records, operation, success) {
				if (success==true){
					this.each(function(b) {
						b.__origin = Ext.apply({}, b.data);
						b.__isNew = undefined
					})
				}
		    }
		});


view 层代码

Ext.define('rm.view.OrgMngMainView', {
    extend: 'Ext.panel.Panel',
    alias: 'widget.OrgMngMainView',
    requires: [
        'Ext.ux.CheckColumn'
    ],

    
    height: 471,
    width: 591,
    layout: {
        type: 'border'
    },
    title: '组织机构维护',
    closable: true,
    border : false,

    initComponent: function() {
        var me = this;

        Ext.applyIf(me, {
        	dockedItems: [
            {
                xtype: 'toolbar',
                dock: 'top',
                items: [{
    			xtype : 'button',
				iconCls : 'icon-add',
				text : '添加',
				action : 'addAction'
			},  {
				xtype : 'tbseparator'
			}, {
				xtype : 'button',
				iconCls : 'icon-delete',
				text : '删除',
				action : 'delAction'
			}, {
				xtype : 'button',
				iconCls : 'icon-save',
				text : '保存',
				action : 'saveAction'
			}  ,  {
				xtype : 'tbseparator'
			}, {
				xtype : 'button',
				iconCls : 'icon-refresh',
				text : '刷新',
				action : 'refreshAction'
			}
	 	]
            }
        ],
            items: [
                {
                    xtype: 'gridpanel',
                    region: 'center',
                    margin: '1 1 1 1',
                    title: '组织机构维护',
                    columnLines: true,
                    store: 'OrgStore',
                    columns: [{
        				xtype: 'rownumberer',
	    				text:'行号',
	    				width:50
		    					
		    			},{
		    	            xtype: 'gridcolumn',
		    	            dataIndex: 'hospcode',
		    	            text: '医院编码',
		    	            editor: {
		    	        		xtype: 'textfield'
		    	        	}
		    			},{
		    	            xtype: 'gridcolumn',
		    	            dataIndex: 'hospname',
		    	            text: '医院名称',
		    	            width: 300,
		    	            editor: {
		    	        		xtype: 'textfield'
		    	        	}
		    			},{
		    				
		    			    xtype: 'checkcolumn',
				            header: '启用',
				            dataIndex: 'status',
				            width: 60,
				            editor: {
				                xtype: 'checkbox',
				                cls: 'x-grid-checkheader-editor'
				            }

 
		 
		    			}
    				 ],
                    
	    	        plugins: [
				    	new Ext.grid.plugin.CellEditing({
				    		clicksToEdit: 1
				    	})   
				    ],	            
				    viewConfig: {
		                enableTextSelection: true
		            } 
                }
            ]
        });

        me.callParent(arguments);
    }

});

Ext.define('rm.model.OrgModel', {
    extend: 'Ext.data.Model',

    requires: [
        'Ext.data.Field'
    ],

    fields: [
        {
            name: 'hospcode',
            type: 'string'
        },
        {
            name: 'hospname',
            type: 'string'
        },
        {
            name: 'status', type: 'boolean'
        },
        {
            name:'text' ,
            convert: function(value, record) {
                
                return record.get("hospcode")+"|"+record.get("hospname");
            }
        
        }
    ]
});


Ext.define('rm.store.OrgStore', {
    extend: 'Ext.data.Store',

    requires: [
        'rm.model.OrgModel',
        'Ext.data.proxy.Ajax',
        'Ext.data.reader.Json'
    ],

    constructor: function(cfg) {
        var me = this;
        cfg = cfg || {};
        me.callParent([Ext.apply({
            model: 'rm.model.OrgModel',
            storeId: 'OrgStore',
            proxy: {
                type: 'ajax',
                url: 'org/getOrgs',
                reader: {
                    type: 'json'
                }
            }
        }, cfg)]);
    }
});

Ext.define('rm.controller.OrgMainController', {
    extend: 'Ext.app.Controller',

    refs: [
        {
            ref: 'window',
            selector: 'OrgMngMainView'
        }
    ],

    initWindow: function(component, eOpts) {
        this.refresh();

    },
    
    refresh :function(  button, e, eOpts){
        var window = this.getWindow();
        var grid = window.down("gridpanel");
        var store = grid.getStore();
        store.load({
            scope: store,
            callback: function(records, operation, success) {
                if (success==true){
                    this.each(function(b) {
                        b.__origin = Ext.apply({}, b.data);
                        b.__isNew = undefined
                    })
                }
            }
        });
    },
    add:function(  button, e, eOpts){
        var window = this.getWindow();   
        var grid =  window.down('gridpanel');
        var store = grid.getStore();
        var row ;
        var record;
        var edit = grid.plugins[0] ;
        var gridsel = grid.getSelectionModel().getSelection( ) ;
        if (gridsel.length>0){
            row =store.indexOf( gridsel[0] );
        }else{
            row = 0;
        }
        
        
        store.insert(row,{ });
        grid.getSelectionModel().select(row);//滚动到当前行
        record=store.getAt(row);
        record.__isNew=true;
        record.commit();
        edit.startEditByPosition({row:row,column:1});
    },

    delete:function(  button, e, eOpts){
        var window = this.getWindow();
        var grid =  window.down('gridpanel');
        var store = grid.getStore();
        var sels=    grid.getSelectionModel().getSelection( ) ;
        var row;
        var i;
        if  (sels.length<=0){
            return ;
        }
        store.remove(sels);
    },
    save :function(  button, e, eOpts){
        var window = this.getWindow();
        var grid =  window.down('gridpanel');
        var store = grid.getStore();    
        var j = store.getCount();
        var i;
        var record;
        var data;
        var m=[],n=[],d=[];
        var deleteRerords  ;
        //获取新增或修改的记录
        for(i=j-1;i>=0;i--){
            record=store.getAt(i);
            if(record.dirty){
                data = Ext.apply({},record.data )
                if(record.__isNew){
                    n.push(data);
                }else{
                    data.origin=record.__origin;
                    m.push(data);
                }
            }else if( record.__isNew ){
                store.remove(record);
            }
        }
        //获取删除的记录
        deleteRerords = store.getRemovedRecords( );
        for(i=0 ;i<deleteRerords.length;i++){
            
            if( !deleteRerords[i].__isNew ){
                d.push(deleteRerords[i].__origin);
                
            }
            
        }
        
        //ajax提交数据
        Ext.Ajax.request({
            scope: this,
            url:   'org/saveOrgs',
            params: {
                newDatas:Ext.encode(n),
                deleteDatas:Ext.encode(d),
                modifyDatas:Ext.encode(m)
            },
            success : function(response, options){
                this.refresh( );
            }
        });
       
    },
 
    init: function(application) {
        this.control({
            
            'OrgMngMainView button[action=addAction]' : {
                click : this.add
            },
            'OrgMngMainView button[action=delAction]' : {
                click : this.delete
            },
            'OrgMngMainView button[action=saveAction]' : {
                click : this.save
            },
            'OrgMngMainView button[action=refreshAction]' : {
                click : this.refresh
            },
            "OrgMngMainView": {
                afterrender: this.initWindow
            }   
        });
    }

});




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值