`
Small嗳TT
  • 浏览: 11339 次
  • 性别: Icon_minigender_1
  • 来自: 厦门
社区版块
存档分类
最新评论

TT随笔四 前台的CURD封装

 
阅读更多

这几天都没什么时间,趁今天周末,做宿舍里,整点代码。刚刚跟同事喝了几杯,头有点晕的说。

今天本来想整nutz的curd例子,一开始整的时候,发现,写前台代码的时候好烦,每次都要写好多的html、js,但基本上都是复制黏贴,于是就像在easyui的基础上,进一步进行封装,搞一个属于自己的常用的小插件出来,方便开发。于是我首先想到了最基础的CURD例子(其实应该还有Q:query的,待今后拓展)。

我的思路大概是这样的:需要一个列表用于显示数据,同时有4个按键:浏览、新增、修改、删除(我还加了两个默认的,分别为导入导出,做今后拓展用)。如此,我们就可以使用easyui的datagrid了,其toolbar用于添加按键相当不错。然后我们点了浏览、新增、修改按键的时候将会弹出相应的弹出框,点击删除按键不会有弹出框,而是提醒,是否进行数据的删除。于是,我们对于toolbar上面的按键基本上可以分这两种:1、打开弹出框;2、执行某个后台操作。于是,我想要定义自己的一个datagrid的jquery插件,只不过这个插件是基于easyui实现的。同时,定义了自己的插件的话,今后如果换了一套ui框架,改动的地方就只要改相应的插件中的实现就好了,而对业务代码没有影响。

我们先看看一个普通的easyui的datagrid大致上要怎么写,我们采用html标签写法:

	<table class="easyui-datagrid" title="Basic DataGrid" style="width:700px;height:250px"
			data-options="singleSelect:true,collapsible:true,url:'../datagrid/datagrid_data1.json',
			toolbar:'#toolbar'">
		<thead>
			<tr>
				<th data-options="field:'itemid',width:80">Item ID</th>
				<th data-options="field:'productid',width:100">Product</th>
				<th data-options="field:'listprice',width:80,align:'right'">List Price</th>
				<th data-options="field:'unitcost',width:80,align:'right'">Unit Cost</th>
				<th data-options="field:'attr1',width:250">Attribute</th>
				<th data-options="field:'status',width:60,align:'center'">Status</th>
			</tr>
		</thead>
	</table>
	<div id="toolbar">
		<a href="#" class="easyui-linkbutton" data-options="plain:true,iconCls:'icon-search'">浏览</a>
		<a href="#" class="easyui-linkbutton" data-options="plain:true,iconCls:'icon-add'">新增</a>
		<a href="#" class="easyui-linkbutton" data-options="plain:true,iconCls:'icon-edit'">修改</a>
		<a href="#" class="easyui-linkbutton" data-options="plain:true,iconCls:'icon-remove'">删除</a>
	</div>

然后我们为每个button绑定事件,这么下来,其实每一套CURD界面,大致上都是一样的代码,无非是改改id,改改字段名等等,虽然简单,但我是个懒人,你懂的。所以我决定在table上加上自己的解析属性,我想了下格式,决定这种格式挺不错的:

key1:value1;key2|key3:value2

为什么用这种格式呢?因为我首先想到的是这么去定义boolbar中的按键,我的标签属性如下:

tt-btns="r:浏览操作;c:新增操作;ua;修改操作;d:删除操作"

这样的话,就可以通过key来对应相关的操作了。默认的,crud对应于新增、浏览、修改、删除。根据此,我们可以先将toolbar的生成按此方式生成出来。这属于中间过程,我就不写了。

于是我在TT.js添加了公共的解析方法:

		
		/**
		 * Desc:解析attr。格式如下:
		 * 		key1:value1;key2|key3:value2
		 * @param {JQuery} jqObj
		 * @param {String} attrName
		 * @param {Object} defResult	默认值
		 * @returns {Object}
		 */
		parseAttr : function(jqObj, attrName, defResult){
			defResult = $tt.isEmpty(defResult) ? {} : defResult;
			var attrVal = jqObj.attr(attrName);
			if($tt.isEmpty(attrVal)) return defResult;
			attrVal = attrVal.trim();
			if($tt.isEmpty(attrVal)) return defResult;
			var result = {};
			var seq = [];
			//先根据";"号拆分
			var arr1 = attrVal.split(";");
			for(var i = 0; i < arr1.length; ++i){
				if($tt.isEmpty(arr1[i])) continue;
				arr1[i] = arr1[i].trim();
				if($tt.isEmpty(arr1[i])) continue;
				//接着根据":"号拆分
				var arr2 = arr1[i].split(":");
				if(arr2.length == 1){
					arr2[0] = arr2[0].trim();
					if($tt.isEmpty(arr2[0])) continue;
					result[arr2[0]] = arr2[0];
					seq.push(arr2[0]);
				}else if(arr2.length == 2){
					arr2[0] = arr2[0].trim();
					arr2[1] = arr2[1].trim();
					if($tt.isEmpty(arr2[0])) continue;
					//根据"|"来解析key
					var arr3 = arr2[0].split("|");
					for(var j = 0; j < arr3.length; ++j){
						if($tt.isEmpty(arr3[j])) continue;
						arr3[j] = arr3[j].trim();
						if($tt.isEmpty(arr3[j])) continue;
						result[arr3[j]] = arr2[1];
						seq.push(arr3[j]);
					}
				}else {
					
				}
			}
			result._seq = seq;
			result = $.extend(result, defResult);
			return result;
		}

 并且,在解析之后,将其值用jquery的data缓存起来便于今后使用(该方法在插件初始化的时候使用即可):

		/**
		 * Desc:通过解析元素的attr,并将其保存到data中。
		 * @param jqObj
		 * @param parseArr
		 */
		initJqData : function(jqObj, parseArr){
			for(var i = 0; i < parseArr.length; ++i){
				var key = parseArr[i].key;
				var defVal = parseArr[i].defVal;
				jqObj.data(key, $tt.parseAttr(jqObj, key, defVal));
			}
		}

 

 接下来是对于浏览、新增、修改弹出框的构想,其实这三个窗口基本上一样,只是字段只读与否等不同罢了,于是完全有理由将其整合在一个上。可以试想一下,这得剩下多少代码!界面会变的多整洁!

所以我的构想是,在open dialog的时候,传进操作类型,例如c(新增操作),根据操作类型来决定界面的显示情况,甚至于按键的操作。

其他的想法类推,我目前实现了4个插件:

jquery.tt.tbl.js

jquery.tt.dlg.js

jquery.tt.frm.js

jquery.tt.fld.js

好吧,我承认我很懒,都用缩写,有人会说,这别人不容易看懂。我嘛,觉得,1、目前我是自己用的;2、我写了缩写文档,在readme中,也没几个,如果记住了,反而不用敲那么多代码,敲多了还容易出错呢大笑。喷我吧哈哈~~

我贴下这几个插件的代码,不多,但希望大家给点意见或建议,我会加以改正的,毕竟一个人的思维是有限的。

 

!function ($) {
	//解析的attr名字
	var _keys = {
		btns : "tt-btns",				//按键组
		btnTxts : "tt-btnTxts",			//按键名称组
		btnIcons : "tt-btnIcons",		//按键图标组
		nSels : "tt-nSels",				//非必勾选数据组
		nSelMsgs : "tt-nSelMsgs",		//没勾选数据提示信息组
		confirms : "tt-confirms"
	};
//	var _btnSeqs = ["r", "c", "u", "d", "i", "e"];
	//默认按键图标组
	var _defBtnIcons = {r : "icon-search", c:"icon-add", u:"icon-edit", d:"icon-remove", i:"icon-import", e:"icon-export"};
	//默认按键名称组
	var _defBtnTxts = {r : "浏览", c:"新增", u:"修改", d:"删除", i:"导入", e:"导出"};
	//默认不需要选中数据组
	var _defNSels = {c : true, i : true};
	//默认没选择数据时的提示消息组
	var _defNSelMsgs = {r : "请先选择所要浏览的数据!", u : "请先选择所要修改的数据!", 
			d : "请先选择所要删除的数据!", e : "请先选择所要导出的数据!"};
	var _defConfirms = {d : "确定要删除选择的数据?"};

	//需要解析的数组
	var parseArr = [{
		key : _keys.btns, defVal : {}
	},{
		key : _keys.btnTxts, defVal : _defBtnTxts
	},{
		key : _keys.btnIcons, defVal : _defBtnIcons
	},{
		key : _keys.nSels, defVal : _defNSels
	},{
		key : _keys.nSelMsgs, defVal : _defNSelMsgs
	},{
		key : _keys.confirms, defVal : _defConfirms
	}];

	/**
	 * Desc:获取到数据集之后的过滤器。
	 * @param {Object} data
	 */
	function loadFilter(data){
    	var defResult = {total : 0, rows : []};
        if (data[$tt.RESULT_MSG_TYPE] == $tt.MSG_TYPE_INFO) {
        	//注意,如果加了loadFilter,那么loadFilter返回值一定是分页数据的格式,而不能是单纯的数据列表
            return data.value;
        }else{
        	$tt.alertCtrl(data);
        	return defResult;
        }
    }
	/**
	 * Desc:初始化datagrid的toolar的按键。
	 */
	function initBtn(jqTbl, type, execStr){
		var btnIcons = jqTbl.data(_keys.btnIcons);
		var btnTxts = jqTbl.data(_keys.btnTxts);
    	var confirms = jqTbl.data(_keys.confirms);
    	var nSels = jqTbl.data(_keys.nSels);
    	var nSelMsgs = jqTbl.data(_keys.nSelMsgs);
    	var nSelMsg = $tt.isEmpty(nSelMsgs[type]) ? "请选择数据!" : nSelMsgs[type];
    	var btnTxt = $tt.isEmpty(btnTxts[type]) ? type : btnTxts[type];
        return {
        	text : btnTxt,
        	iconCls : btnIcons[type],
        	handler : function(){
            	var row = {};
            	if(!nSels[type]){
    	            row = jqTbl.datagrid("getSelected");
    	            if($tt.isEmpty(row)) return $tt.alertWarn(nSelMsg);
            	}else{
            		row = {};
            	}
            	//处理execStr
            	if(!$tt.isEmpty(confirms[type])){//有提示确认框
            		$tt.confirm(confirms[type], function(){
            			$tt.handleExec(jqTbl, type, execStr, row);
            		});
            	}else{
                	$tt.handleExec(jqTbl, type, execStr, row);
            	}
            }
        };
	}
	/**
	 * Desc:初始化工具栏。
	 * @param {JQuery} jqObj
	 */
	function initToolbar(jqObj){
		var btns = jqObj.data(_keys.btns);
		var btnSeq = btns._seq;
		var btnArr = [];
		for(var i = 0; i < btnSeq.length; ++i){
			btnArr.push(initBtn(jqObj, btnSeq[i], btns[btnSeq[i]]));
			btnArr.push("-");
		}
		return btnArr;
	}
	/**
	 * Desc:插件初始化方法。
	 */
	$.fn.tbl = function(options){
		return this.each(function(){
			var $this = $(this);
			$tt.initJqData($this, parseArr);
			$this.data($tt.JQ_TYPE, $tt.JQ_TYPE_TBL);
			$this.datagrid({
	            loadFilter : loadFilter,	
				toolbar : initToolbar($this)
			});
		});
	};
	$(function(){$(".tt-tbl").tbl({});});
}(window.jQuery);

 

!function ($) {
	//解析的attr名字
	var _keys = {
		titles : "tt-titles",	//标题组
		acts : "tt-acts",		//调用后台的action组
		valids : "tt-valids",	//自定义验证组
		closes : "tt-closes"
	};
	//默认存在的标题组
	var _defTitles = {r : "浏览", c:"新增", u:"修改"};
	//默认标题
	var _defTitle = "弹出窗";
	
	//需要解析的数组
	var parseArr = [{
		key : _keys.titles, defVal : _defTitles
	},{
		key : _keys.acts, defVal : {}
	},{
		key : _keys.valids, defVal : {}
	},{
		key : _keys.closes, defVal : {}
	}];

	/**
	 * Desc:关闭弹出框。
	 */
	function closeDlg(jqDlg){
		jqDlg.find(".tt-fld").each(function(){//解决窗口关闭时,tooltip还在界面上的问题
			$(this).blur();
		});
		jqDlg.dialog("close");
	}
	/**
	 * Desc:初始化保存按键。
	 */
	function initSaveBtn(jqDlg, type){
		return {
	        text:'保存',
	        handler:function(){
	        	var frm = jqDlg.find("form");
	        	//表单验证
	            if(frm.form("validate") != true) return;
	        	//是否额外使用自定义验证函数
	        	var valids = jqDlg.data(_keys.valids);
	            if(!$tt.isEmpty(valids[type]) 
	            		&& $tt.getFunc(valids[type])(frm, frm.serialize()) != true){
	            	return;
	            }
	            var acts = jqDlg.data(_keys.acts);
	            if($tt.isEmpty(acts[type])) return;
	            //进行表单提交
	            $tt.submitFrm(frm, acts[type], function(){
	            	jqDlg.dialog("close");
	            	var jqSrc = jqDlg.data($tt.JQ_SRC);
	            	if($tt.isEmpty(jqSrc)) return;
	            	var jqType = jqSrc.data($tt.JQ_TYPE);
	            	switch(jqType){
	            	case $tt.JQ_TYPE_TBL : jqSrc.datagrid("load");break;
	            	}
	            	jqDlg.closeDlg(type);
	            });
	        }
		};
	}
	/**
	 * Desc:初始化关闭按键。
	 */
	function initCloseBtn(jqDlg){
		return {
	        text:'关闭',
	        handler:function(){
            	jqDlg.closeDlg("");
	        }
		};
	}
	/**
	 * Desc:初始化按键。
	 */
	function initBtns(jqDlg, type){
		var btnArr = [];
		switch(type){
		case "c" : btnArr.push(initSaveBtn(jqDlg, type));break;
		case "u" : btnArr.push(initSaveBtn(jqDlg, type));break;
		}
		btnArr.push(initCloseBtn(jqDlg));
		return btnArr;
	}
	/**
	 * Desc:插件初始化方法。
	 */
	$.fn.dlg = function(options){
		return this.each(function(){
			var $this = $(this);
			$tt.initJqData($this, parseArr);
			$this.data($tt.JQ_TYPE, $tt.JQ_TYPE_DLG);
			$this.dialog({
	            closed : true
			});
		});
	};
	/**
	 * Desc:打开窗口。
	 * @param {Object} data	要填充到窗口中的form中的数据。
	 * @param {String} type	操作类型。
	 */
	$.fn.openDlg = function(data, type){
		return this.each(function(){
			var $this = $(this);
			$this.find("form").loadFrm(data, type);
			var titles = $this.data(_keys.titles);
			var title = titles[type] || _defTitle;
			$this.dialog({
				title : title,
	            closed : false,
	            buttons : initBtns($this, type)
			});
		});
	};
	//关闭弹出窗
	$.fn.closeDlg = function(type){
		return this.each(function(){
			var $this = $(this);
			var jqFrm = $this.find("form");
			var closes = $this.data(_keys.closes);
			if(!$tt.isEmpty(closes[type])) $tt.getFunc(closes[type])(jqDlg, jqFrm);
			closeDlg($this);
		});
	};
	$(function(){$(".tt-dlg").dlg({});});
}(window.jQuery);

 

!function ($) {
	//解析的attr名字
//	var _keys = {};
	//需要解析的数组
	var parseArr = [];

	/**
	 * Desc:插件初始化方法。
	 */
	$.fn.frm = function(options){
		return this.each(function(){
			var $this = $(this);
			$tt.initJqData($this, parseArr);
			$this.data($tt.JQ_TYPE, $tt.JQ_TYPE_FRM);
		});
	};
	/**
	 * Desc:加载form表单。
	 * @param {Object} data	加载数据
	 * @param {String} type	操作类型
	 */
	$.fn.loadFrm = function(data, type){
		return this.each(function(){
			var $this = $(this);
			$this.form("clear");
			$this.find(".tt-fld").removeAttr("disabled");
			$this.find(".tt-fld").chgFld(type);
			$this.form("load", data);
			$this.find(".tt-fld").each(function(){
				$(this).blur();//目的在于取消验证信息的tooltip
			});
		});
	};
	$(function(){$(".tt-frm").frm({});});
}(window.jQuery);

 

!function ($) {
	//解析的attr名字
	var _keys = {
		hiddens : "tt-hiddens",			//隐藏组
		readonlys : "tt-readonlys",		//只读组
		requireds : "tt-requireds"		//必须组
	};
	//field的类型的attr名称,其值需要与easyui的对应的js中的方法名称保持一致,例如validatebox
	var _keyFldType = "tt-fldType";
	//需要解析的数组
	var parseArr = [{
		key : _keys.hiddens, defVal : {}
	},{
		key : _keys.readonlys, defVal : {r : true}
	},{
		key : _keys.requireds, defVal : {}
	}];
	/**
	 * Desc:插件初始化方法。
	 */
	$.fn.fld = function(options){
		return this.each(function(){
			var $this = $(this);
			$tt.initJqData($this, parseArr);
			$this.data($tt.JQ_TYPE, $tt.JQ_TYPE_FLD);
		});
	};
	/**
	 * Desc:改变输入框状态。
	 * @param {String} type	操作类型
	 */
	$.fn.chgFld = function(type){
		return this.each(function(){
			var $this = $(this);
			var fldType = $tt.getValByDef($this.attr(_keyFldType), "validatebox");
			var hiddens = $this.data(_keys.hiddens);//隐藏
			var readonlys = $this.data(_keys.readonlys);//只读
			var requireds = $this.data(_keys.requireds);//必须
			var options = {};
			if(!$tt.isEmpty(readonlys[type])) $this.attr("disabled", true);
			else $this.removeAttr("disabled");
			options.required = !$tt.isEmpty(requireds[type]);
			if(!$tt.isEmpty(hiddens[type])){
				options.required = false;
				$this.parents(".tt-fldGrp").hide();
			}else{
				$this.parents(".tt-fldGrp").show();
			}
			$this[fldType](options);
			$this[fldType]("validate");
		});
	};
	$(function(){$(".tt-fld").fld({});});
}(window.jQuery);

 在最后的btn的实现上,已经可以支持自定义拓展了,例如以下是Curd.jsp的主要代码:

    <table title="Curd列表" rownumbers="true" singleSelect="true" pagination="true"
    	url="tui/CurdCtrl/query4page"
    	class="tt-tbl" tt-btns="r|c|u:dlg->#dlgCurd;d:act->tui/CurdCtrl/delete;测试func:func->test;测试[默认]:test"
    	tt-nSels="测试" tt-nSelMsgs="r:请选择所要浏览的数据!">
      <thead>
        <tr>
          <th field="id" width="100">编号</th>
          <th field="name" width="100">姓名</th>
          <th field="address" width="100">住址</th>
          <th field="age" width="100">年龄</th>
        </tr>
      </thead>
    </table>
    
    <div id="dlgCurd" class="tt-dlg" data-options="width : 500"
    	tt-acts="c:tui/CurdCtrl/create;u:tui/CurdCtrl/update">
    	<form>
    		<div class="tt-fldGrp"><label>编号:<input class="tt-fld" name="id" tt-requireds="c;u" tt-hiddens="c;u"></label></div>
    		<div class="tt-fldGrp"><label>姓名:<input class="tt-fld" name="name" tt-requireds="c;u"></label></div>
    		<div class="tt-fldGrp"><label>住址:<input class="tt-fld" name="address" tt-readonlys="c"></label></div>
    		<div class="tt-fldGrp"><label>年龄:<input class="tt-fld" name="age"></label></div>
    	</form>
    </div>
  dlg->#dlgCurd    表示打开id为dlgCurd的弹出窗;
act->tui/CurdCtrl/delete    表示执行url操作;
func->test    表示调用test方法;
test    默认为func形式;
如何?这种写法是不是感觉整个世界就清静了!!几乎不需要再去写什么js代码了~~
对应自定义属性的解析上,还提供了一个公告的js方法放TT.js中:
		/**
		 * Desc:处理执行命令。格式如下:
		 * 		func->test;
		 * 		dlg->#dlg;
		 * 		act->tui/CurdCtrl/create;
		 * @param {JQuery} jqObj
		 * @param {String} type
		 * @param {String} execStr
		 * @param {Object} data
		 * @param {Function} callback
		 * @returns
		 */
		handleExec : function(jqObj, type, execStr, data, callback){
			if($tt.isEmpty(execStr)) return;
			execStr.trim();
			if($tt.isEmpty(execStr)) return;
			var arr1 = execStr.split("->");
			if(arr1.length == 1){
				arr1[0] = arr1[0].trim();
				if($tt.isEmpty(arr1[0])) return;
				$tt.getFunc(arr1[0])(jqObj, data);
			}else if(arr1.length == 2){
				arr1[0] = arr1[0].trim();
				arr1[1] = arr1[1].trim();
				if($tt.isEmpty(arr1[0]) || $tt.isEmpty(arr1[1])) return;
				switch(arr1[0]){
				case "func" : return $tt.getFunc(arr1[1])(jqObj, data);
				case "dlg" : 
					$(arr1[1]).data($tt.JQ_SRC, jqObj);
					return $(arr1[1]).openDlg(data, type);
				case "act" : return $tt.callAjax(arr1[1], data, callback);
				}
			}
		}
 这些插件是今天刚瞎整的,肯定还有很多地方需要调整与改进,例如jquery.tt.fld中,对于filed的隐藏目前实现的很是蛋疼,也不是很好,如果label和input没有被包含在一个.tt-fldGrp中的话,那就蛋疼了。希望大家给点意见和建议,有哪些我遗漏的地方希望大家指出!
 
ITEye的文件上传蛋疼死了,老是传不上去,工程只好改天补传了。
 
源码终于传上去了。
  • TT.zip (3.6 MB)
  • 下载次数: 17
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics