首页 > 代码库 > 基于ExtJS 4.2.1 + Hibernate 4.1.7 + Spring MVC 3.2.8 的通用后台管理系统

基于ExtJS 4.2.1 + Hibernate 4.1.7 + Spring MVC 3.2.8 的通用后台管理系统

一、系统介绍

1、基于最新的ExtJS 4.2.1.883开发。

2、支持MySQL、SQL Server、Oracle、DB2等关系数据库。

3、本系统可作为OA、网站、电子政务、ERP、CRM等基于B/S架构的应用软件系统的快速开发框架。

 

源码有50多M(包括Jar包和SQL文件),点此获取。

 

二、特色功能
1、采用Spring MVC的静态加载缓存功能,在首页将Javascript文件、CSS文件和图片等静态资源文件加载进来放进内存,极大提高ExtJS的加载速度。
2、增加新的ExtJS Neptune Theme,让系统显得时髦,更具现代感,创造最佳的应用体验和多浏览器支持。

3、分别封装了模型层、控制层、业务逻辑层和数据持久层的通用操作模块,层次分明,大大减少代码冗余,二次开发效率高。

 

三、图片欣赏

1、修改信息


 

2、ExtJS的HtmlEditor的图片文件上传插件。


 

3、Grid列表,包含添加、删除、批量删除、修改、查看、图片查看等功能。


 

4、按条件查询列表。


 

5、导入Excel数据,支持xlsx和xls文件。


 

6、用户管理列表。


 

7、权限管理。不仅可管理各个功能模块的权限,也可以管理功能模块里的页面按钮权限。


 

8、报表统计。


 

9、采用开源的互动地图Javascript库Leaflet,处理自定义在线地图。Panel里包含2个组件,在2个组件间传递参数显示数据。

 

四、开发工具和采用技术
1、开发工具:MyEclipse 2014。
2、采用ExtJS 4.2.1.883商用版本。注:根据ExtJS License,只要不把ExtJS封装到工具软件里出售就不构成侵权,可放心用于网站开发。
3、采用Spring 3中最新最稳定的Spring MVC 3.2.8版本。
4、Spring MVC 3.2.8支持的最高Hibernate版本是4.1.7,更高的Hibernate版本和Spring MVC 3.2.8组合会遇到兼容问题。
5、Hibernate集成二级缓存框架Ehcache。
6、数据库是MySQL 5,Hibernate的Dialect可使程序移植到其他数据库。

7、采用开源的互动地图Javascript库Leaflet,处理自定义在线地图。

 

五、代码结构


部分代码作用:

1、BaseParameter、ExtJSBaseController、BaseService、BaseDao:分别封装了模型层、控制层、业务逻辑层和数据持久层的通用操作模块。

2、ListView、PageView和QueryResult:作为ExtJS的后台分页模块。

3、SystemInitListener:加载以XML格式的数据字典,放进内存供调用。
4、LoginFilter:处理登录各种情况,将session为null的操作重定向到登录页面。
5、CustomDateEditor:处理日期参数并注册到控制器里,否则Spring MVC的参数处理将出错。
6、ExceptionCode、ServiceException:处理异常信息。
7、CacheFactory:处理Ehcache二级缓存。

8、还有其他很多工具类等等。

 

六、技术要点讲解

1、处理POST和GET的中文乱码问题。

1.1、POST的中文乱码处理可在web.xml上加上Spring提供的字符编码处理Filter。

 

	<filter>	    <filter-name>characterEncoding</filter-name>	    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>	    <init-param>		<param-name>encoding</param-name>		<param-value>UTF-8</param-value>	    </init-param>	    <init-param>                 <param-name>forceEncoding</param-name>                 <param-value>true</param-value>             </init-param> 	</filter>	<filter-mapping>	    <filter-name>characterEncoding</filter-name>	    <url-pattern>/*</url-pattern>	</filter-mapping>

 

 

 

 1.2、GET的中文乱码处理可继承HttpServletRequestWrapper建立一个类来处理request。不用在应用服务器里设置URIEncoding。

package core.web;import java.io.UnsupportedEncodingException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper;public class GetHttpServletRequestWrapper extends HttpServletRequestWrapper {	private String charset = "UTF-8";	public GetHttpServletRequestWrapper(HttpServletRequest request) {		super(request);	}	/**	 * 获得被装饰对象的引用和采用的字符编码	 * 	 * @param request	 * @param charset	 */	public GetHttpServletRequestWrapper(HttpServletRequest request, String charset) {		super(request);		this.charset = charset;	}	/**	 * 调用被包装的请求对象的getParameter方法获得参数,然后再进行编码转换	 */	public String getParameter(String name) {		String value = http://www.mamicode.com/super.getParameter(name);"ISO-8859-1"), charset);		} catch (UnsupportedEncodingException e) {			return target;		}	}}

 

2、开发ExtJS的HtmlEditor的图片文件上传插件。建议:不要在ExtJS里集成百度编辑器、KindEditor或CKEditor等HTML编辑器,因为在某种情况下会遇到界面扭曲、浏览器兼容问题。

2.1、ExtJS的图片文件上传插件界面如下。


 

2.2.1、ExtJS的图片文件上传插件Javascript代码如下。

 

Ext.define(‘Ext.ux.custom.ImageHtmlEditor‘, {	extend : ‘Ext.util.Observable‘,	alias : ‘widget.imagehtmleditor‘,	langTitle : ‘插入图片‘,	langIconCls : ‘icon-image‘,	init : function(view) {		var scope = this;		view.on(‘render‘, function() {			scope.onRender(view);		});	},	/**	 * 添加"插入图片"按钮	 */	onRender : function(view) {		var scope = this;		view.getToolbar().add({			iconCls : scope.langIconCls,			tooltip : {				title : scope.langTitle,				width : 160,				text : ‘上传本地图片或链接网络图片‘			},			handler : function() {				scope.showImgWindow(view);			}		});	},	/**	 * 显示"插入图片"窗体	 */	showImgWindow : function(view) {		var scope = this;		Ext.create(‘Ext.window.Window‘, {			width : 400,			height : 310,			title : scope.langTitle,			layout : ‘fit‘,			autoShow : true,			modal : true,			resizable : false,			maximizable : false,			constrain : true,			plain : true,			enableTabScroll : true,			border : false,			items : [ {				xtype : ‘tabpanel‘,				enableTabScroll : true,				bodyPadding : 10,				items : [ {					title : ‘上传本地图片‘,					items : [ {						xtype : ‘form‘,						layout : ‘column‘,						autoScroll : true,						border : false,						defaults : {							columnWidth : 1,							labelWidth : 80,							labelAlign : ‘left‘,							padding : 5,							allowBlank : false						},						items : [ {							xtype : ‘fileuploadfield‘,							fieldLabel : ‘选择文件‘,							afterLabelTextTpl : ‘<span style="color:#FF0000;">*</span>‘,							buttonText : ‘请选择...‘,							name : ‘uploadAttachment‘,							emptyText : ‘请选择图片‘,							blankText : ‘图片不能为空‘,							listeners : {								change : function(view, value, eOpts) {									scope.uploadImgCheck(view, value);								}							}						}, {							xtype : ‘fieldcontainer‘,							fieldLabel : ‘图片大小‘,							layout : ‘hbox‘,							defaultType : ‘numberfield‘,							defaults : {								flex : 1,								labelWidth : 20,								labelAlign : ‘left‘,								allowBlank : true							},							items : [ {								fieldLabel : ‘宽‘,								name : ‘width‘,								minValue : 1							}, {								fieldLabel : ‘高‘,								name : ‘height‘,								minValue : 1							} ]						}, {							xtype : ‘textfield‘,							fieldLabel : ‘图片说明‘,							name : ‘content‘,							allowBlank : true,							maxLength : 100,							emptyText : ‘简短的图片说明‘						}, {							columnWidth : 1,							xtype : ‘fieldset‘,							title : ‘上传须知‘,							layout : {								type : ‘table‘,								columns : 1							},							collapsible : false,// 是否可折叠							defaultType : ‘label‘,// 默认的Form表单组件							items : [ {								html : ‘1、上传图片大小不超过2MB.‘							}, {								html : ‘2、支持以下格式的图片:jpg,jpeg,png,gif,bmp.‘							} ]						} ],						buttons : [ ‘->‘, {							text : ‘保存‘,							action : ‘btn_save‘,							iconCls : ‘icon-save‘,							handler : function(btn) {								scope.saveUploadImg(btn, view);							}						}, {							text : ‘取消‘,							iconCls : ‘icon-cancel‘,							handler : function(btn) {								btn.up(‘window‘).close();							}						}, ‘->‘ ]					} ]				}, {					title : ‘链接网络图片‘,					items : [ {						xtype : ‘form‘,						layout : ‘column‘,						autoScroll : true,						border : false,						defaults : {							columnWidth : 1,							labelWidth : 80,							labelAlign : ‘left‘,							padding : 5,							allowBlank : false						},						items : [ {							xtype : ‘textfield‘,							fieldLabel : ‘图片地址‘,							afterLabelTextTpl : ‘<span style="color:#FF0000;">*</span>‘,							name : ‘url‘,							emptyText : ‘请填入支持外链的长期有效的图片URL‘,							blankText : ‘图片地址不能为空‘,							vtype : ‘url‘						}, {							xtype : ‘fieldcontainer‘,							fieldLabel : ‘图片大小‘,							layout : ‘hbox‘,							defaultType : ‘numberfield‘,							defaults : {								flex : 1,								labelWidth : 20,								labelAlign : ‘left‘,								allowBlank : true							},							items : [ {								fieldLabel : ‘宽‘,								name : ‘width‘,								minValue : 1							}, {								fieldLabel : ‘高‘,								name : ‘height‘,								minValue : 1							} ]						}, {							xtype : ‘textfield‘,							fieldLabel : ‘图片说明‘,							name : ‘content‘,							allowBlank : true,							maxLength : 100,							emptyText : ‘简短的图片说明‘						} ],						buttons : [ ‘->‘, {							text : ‘保存‘,							action : ‘btn_save‘,							iconCls : ‘icon-save‘,							handler : function(btn) {								scope.saveRemoteImg(btn, view);							}						}, {							text : ‘取消‘,							iconCls : ‘icon-cancel‘,							handler : function(btn) {								btn.up(‘window‘).close();							}						}, ‘->‘ ]					} ]				} ]			} ]		});	},	/**	 * 上传图片验证	 */	uploadImgCheck : function(fileObj, fileName) {		var scope = this;		// 图片类型验证		if (!(scope.getImgTypeCheck(scope.getImgHZ(fileName)))) {			globalObject.errTip(‘上传图片类型有误!‘);			fileObj.reset();// 清空上传内容			return;		}	},	/**	 * 获取图片后缀(小写)	 */	getImgHZ : function(imgName) {		// 后缀		var hz = ‘‘;		// 图片名称中最后一个.的位置		var index = imgName.lastIndexOf(‘.‘);		if (index != -1) {			// 后缀转成小写			hz = imgName.substr(index + 1).toLowerCase();		}		return hz;	},	/**	 * 图片类型验证	 */	getImgTypeCheck : function(hz) {		var typestr = ‘jpg,jpeg,png,gif,bmp‘;		var types = typestr.split(‘,‘);// 图片类型		for (var i = 0; i < types.length; i++) {			if (hz == types[i]) {				return true;			}		}		return false;	},	/**	 * 上传图片	 */	saveUploadImg : function(btn, view) {		var scope = this;		var windowObj = btn.up(‘window‘);// 获取Window对象		var formObj = btn.up(‘form‘);// 获取Form对象		if (formObj.isValid()) { // 验证Form表单			formObj.form.doAction(‘submit‘, {				url : appBaseUri + ‘/sys/forestrytype/uploadAttachement‘,				method : ‘POST‘,				submitEmptyText : false,				waitMsg : ‘正在上传图片,请稍候...‘,				timeout : 60000, // 60s				success : function(response, options) {					var result = options.result;					if (!result.success) {						globalObject.errTip(result.msg);						return;					}					var url = result.data;					var content = formObj.getForm().findField("content").getValue();					var width = formObj.getForm().findField("width").getValue();					var height = formObj.getForm().findField("height").getValue();					var values = {						url : appBaseUri + ‘/static/img/upload/‘ + url,						content : content,						width : width,						height : height					};					scope.insertImg(view, values);					windowObj.close();// 关闭窗体				},				failure : function(response, options) {					globalObject.errTip(options.result.msg);				}			});		}	},	/**	 * 保存远程的图片	 */	saveRemoteImg : function(btn, view) {		var scope = this;		var windowObj = btn.up(‘window‘);// 获取Window对象		var formObj = btn.up(‘form‘);// 获取Form对象		if (formObj.isValid()) {// 验证Form表单			var values = formObj.getValues();// 获取Form表单的值			scope.insertImg(view, values);			windowObj.close();// 关闭窗体		}	},	/**	 * 插入图片	 */	insertImg : function(view, data) {		var url = data.url;		var content = data.content;		var width = data.width;		var height = data.height;		var str = ‘<img src="http://www.mamicode.com/‘ + url + ‘" border="0" ‘;		if (content != undefined && content != null && content != ‘‘) {			str += ‘ title="‘ + content + ‘" ‘;		}		if (width != undefined && width != null && width != 0) {			str += ‘ width="‘ + width + ‘" ‘;		}		if (height != undefined && height != null && height != 0) {			str += ‘ height="‘ + height + ‘" ‘;		}		str += ‘ />‘;		view.insertAtCursor(str);	}});

 

 

 

2.2.2、ExtJS的图片文件上传插件Java后台代码如下。Spring MVC对文件上传已有直接处理,不用再自写文件上传组件。

 

	@RequestMapping(value = "http://www.mamicode.com/uploadAttachement", method = RequestMethod.POST)	public void uploadAttachement(@RequestParam(value = "http://www.mamicode.com/uploadAttachment", required = false) MultipartFile file, HttpServletRequest request, HttpServletResponse response) throws Exception {		RequestContext requestContext = new RequestContext(request);		JSONObject json = new JSONObject();		if (!file.isEmpty()) {			if (file.getSize() > 2097152) {				json.put("msg", requestContext.getMessage("g_fileTooLarge"));			} else {				try {					String originalFilename = file.getOriginalFilename();					String fileName = sdf.format(new Date()) + ForestryUtils.getRandomString(3) + originalFilename.substring(originalFilename.lastIndexOf("."));					File filePath = new File(getClass().getClassLoader().getResource("/").getPath().replace("/WEB-INF/classes/", "/static/img/upload/" + DateFormatUtils.format(new Date(), "yyyyMM")));					if (!filePath.exists()) {						filePath.mkdirs();					}					file.transferTo(new File(filePath.getAbsolutePath() + "\\" + fileName));					json.put("success", true);					json.put("data", DateFormatUtils.format(new Date(), "yyyyMM") + "/" + fileName);					json.put("msg", requestContext.getMessage("g_uploadSuccess"));				} catch (Exception e) {					e.printStackTrace();					json.put("msg", requestContext.getMessage("g_uploadFailure"));				}			}		} else {			json.put("msg", requestContext.getMessage("g_uploadNotExists"));		}		writeJSON(response, json.toString());	}

 

 

 

3、继承Ext.grid.Panel重写列表组件,在toolbar上定义全局通用的“添加”、“导入”、“删除”等功能点,减少了代码冗余。

 

Ext.define(‘Ext.ux.custom.GlobalGridPanel‘, {	extend : ‘Ext.grid.Panel‘,	alias : ‘widget.globalgrid‘,	xtype : ‘cell-editing‘,	initComponent : function() {		var me = this;		var singleId;				var uniqueID = me.cName + (me.cId ? me.cId : ‘‘) + (me.myId ? me.myId : ‘‘);		this.cellEditing = Ext.create(‘Ext.grid.plugin.CellEditing‘, {			clicksToEdit : 2		});		var tbarMenus = new Array();		if (globalObject.haveActionMenu(me.cButtons, ‘Add‘)) {			tbarMenus.push({				xtype : ‘button‘,				itemId : ‘btnAdd‘,				iconCls : ‘icon-add‘,				text : ‘添加‘,				scope : this,				handler : me.onAddClick			});		}		if (globalObject.haveActionMenu(me.cButtons, ‘Import‘)) {			tbarMenus.push({				xtype : ‘button‘,				itemId : ‘btnImport‘,				iconCls : ‘icon-excel‘,				text : ‘导入‘,				scope : this,				handler : me.onImportClick			});		}		if (globalObject.haveActionMenu(me.cButtons, ‘Delete‘)) {			tbarMenus.push({				xtype : ‘button‘,				itemId : ‘btnDelete‘,				iconCls : ‘icon-delete‘,				text : ‘删除‘,				scope : this,				disabled : true,				handler : me.onDeleteClick			});		}		if (globalObject.haveActionMenu(me.cButtons, ‘Export‘)) {			tbarMenus.push({				xtype : ‘splitbutton‘,				itemId : ‘btnImport‘,				text : ‘导出‘,				scope : this,				handler : function() {					me.onExportClick(false);				},				menu : [ {					text : ‘导出(包括隐藏列)‘,					handler : function() {						me.onExportClick(true);					}				}, {					text : ‘导出选中数据‘,					handler : function() {						me.onExportClick(false, true);					}				}, {					text : ‘导出选中数据(包括隐藏列)‘,					handler : function() {						me.onExportClick(true, true);					}				} ]			});		}		if (tbarMenus.length == 0)			me.hideTBar = true;		this.ttoolbar = Ext.create(‘Ext.toolbar.Toolbar‘, {			hidden : me.hideTBar || false,			items : tbarMenus		});		Ext.apply(this, {			stateful : me.cName ? true : false,			stateId : me.cName ? (uniqueID + ‘gird‘) : null,			enableColumnMove : me.cName ? true : false,			plugins : this.plugins,			selModel : Ext.create(‘Ext.selection.CheckboxModel‘),			border : false,			tbar : this.ttoolbar,			bbar : me.hideBBar ? null : Ext.create(‘Ext.PagingToolbar‘, {				store : me.getStore(),				displayInfo : true			}),			listeners : {				itemdblclick : function(dataview, record, item, index, e) {					me.onViewClick();				}			}		});		this.getSelectionModel().on(‘selectionchange‘, function(sm, records) {			if (me.down(‘#btnDelete‘))				me.down(‘#btnDelete‘).setDisabled(sm.getCount() == 0);		});		this.callParent(arguments);	},	createStore : function(config) {		Ext.applyIf(this, config);		return Ext.create(‘Ext.data.Store‘, {			model : config.modelName,			// autoDestroy: true,			// autoLoad: true,			remoteSort : true,			pageSize : globalPageSize,			proxy : {				type : ‘ajax‘,				url : config.proxyUrl,				extraParams : config.extraParams || null,				reader : {					type : ‘json‘,					root : ‘data‘,					totalProperty : ‘totalRecord‘,					successProperty : "success"				}			},			sorters : [ {				property : config.sortProperty || ‘id‘,				direction : config.sortDirection || ‘DESC‘			} ]		});	},	getTabId : function() {		return this.up(‘panel‘).getId();	},	onAddClick : function() {	},	onEditClick : function() {	},	onImportClick : function() {	},	onViewClick : function() {	},	onDeleteClick : function() {		var me = this;		globalObject.confirmTip(‘删除的记录不可恢复,继续吗?‘, function(btn) {			if (btn == ‘yes‘) {				var s = me.getSelectionModel().getSelection();				var ids = [];				var idProperty = me.idProperty || ‘id‘;				for (var i = 0, r; r = s[i]; i++) {					ids.push(r.get(idProperty));				}				Ext.Ajax.request({					url : me.proxyDeleteUrl,					params : {						ids : ids.join(‘,‘) || singleId					},					success : function(response) {						if (response.responseText != ‘‘) {							var res = Ext.JSON.decode(response.responseText);							if (res.success) {								globalObject.msgTip(‘操作成功!‘);								// Ext.example.msg(‘系统信息‘, ‘{0}‘, "操作成功!");								me.getStore().reload();							} else {								globalObject.errTip(‘操作失败!‘ + res.msg);							}						}					}				});			}		});	},	onExportClick : function(importHideColumn, onlySelected) {		globalObject.exportToExcel(this, importHideColumn, onlySelected);	}});

 

 

 

4、开发Excel数据导入模块,同时支持xls和xlsx文件,在Java后台代码对导入过程中各种条件判断和异常有严格的处理。

4.1、Excel数据导入模块的界面如下。


 

4.2、Excel数据导入模块的Java后台代码如下。

 

	private static SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");	@RequestMapping(value = "http://www.mamicode.com/importForestryFile", method = RequestMethod.POST)	public void importForestryFile(@RequestParam(value = "http://www.mamicode.com/importedFile", required = false) MultipartFile file, HttpServletRequest request, HttpServletResponse response) throws Exception {		RequestContext requestContext = new RequestContext(request);		JSONObject json = new JSONObject();		if (!file.isEmpty()) {			if (file.getSize() > 2097152) {				json.put("msg", requestContext.getMessage("g_fileTooLarge"));			} else {				try {					String originalFilename = file.getOriginalFilename();					String fileName = sdf.format(new Date()) + ForestryUtils.getRandomString(3) + originalFilename.substring(originalFilename.lastIndexOf("."));					File filePath = new File(getClass().getClassLoader().getResource("/").getPath().replace("/WEB-INF/classes/", "/static/download/attachment/" + DateFormatUtils.format(new Date(), "yyyyMM")));					if (!filePath.exists()) {						filePath.mkdirs();					}					String serverFile = filePath.getAbsolutePath() + "\\" + fileName;					file.transferTo(new File(serverFile));					String fileType = fileName.substring(fileName.lastIndexOf(".") + 1);					if (!fileType.equalsIgnoreCase("xls") && !fileType.equalsIgnoreCase("xlsx")) {						json.put("success", false);						json.put("msg", requestContext.getMessage("g_notValidExcel"));						writeJSON(response, json.toString());						return;					}					int count = 0;					StringBuilder stringBuilder = new StringBuilder();					InputStream xls = new FileInputStream(serverFile);					Workbook wb = null;					Sheet sheet = null;					Row currentRow = null;					Row headRow = null;					Cell currentCell = null;					if (fileType.equals("xls")) {						wb = new HSSFWorkbook(xls);					} else if (fileType.equals("xlsx")) {						wb = new XSSFWorkbook(xls);					}					sheet = wb.getSheetAt(0);// excel中至少会存在一个sheet页					int rowNum = sheet.getPhysicalNumberOfRows();// 物理有效行数					Object[] rowValues = null;// excel中一行树木信息					List<Object[]> models = new ArrayList<Object[]>();// excel中全部树木信息					if (rowNum > 1) {						headRow = sheet.getRow(0);						columns: for (int i = 1; i < rowNum; i++) {							currentRow = sheet.getRow(i);							if (currentRow != null) {								rowValues = new Object[5];								// int cellNum = currentRow.getLastCellNum();// 总单元格数目								for (short j = 0; j < 5; j++) {									try {										currentCell = currentRow.getCell(j);										Object obj = null;										if (currentCell == null) {											obj = "";										} else {											switch (currentCell.getCellType()) {												case Cell.CELL_TYPE_BLANK:													obj = "";													break;												case Cell.CELL_TYPE_STRING:													obj = currentCell.getRichStringCellValue();													break;												case Cell.CELL_TYPE_NUMERIC:													if (HSSFDateUtil.isCellDateFormatted(currentCell)) {														double d = currentCell.getNumericCellValue();														Date date = HSSFDateUtil.getJavaDate(d);														obj = sdfDate.format(date);													} else {														NumberFormat nf = NumberFormat.getInstance();														nf.setGroupingUsed(false);//true时的格式:1,234,567,890														obj = nf.format(currentCell.getNumericCellValue());													}													break;												default:													obj = "";													break;											}										}										String cellVal = obj.toString();										rowValues[j] = cellVal;									} catch (IllegalStateException e) {										rowValues = null;										stringBuilder.append("第" + i + "行," + headRow.getCell(j).getRichStringCellValue() + "列输入了非法值,未导入成功!");										continue columns;									} catch (NullPointerException e) {										rowValues = null;										stringBuilder.append("第" + i + "行," + headRow.getCell(j).getRichStringCellValue() + "列输入了空值,未导入成功!");										continue columns;									} catch (Exception e) {										rowValues = null;										stringBuilder.append(e.getMessage());										continue columns;									}								}								if (rowValues != null) {									models.add(rowValues);								}							}						}					} else if (rowNum <= 1 && rowNum > 0) {// 表示模版中只存在头部信息						json.put("success", false);						json.put("msg", "Excel表格中没有需要导入 的内容!");						writeJSON(response, json.toString());						return;					} else if (rowNum <= 0) {// 表示这是一个空sheet页						json.put("success", false);						json.put("msg", "所导入文件格式不正确,请下载模板!");						writeJSON(response, json.toString());						return;					}					List<Forestry> list = objectToForestry(models);// Object-->Forestry					for (int i = 0; i < list.size(); i++) {						if (StringUtils.isBlank(list.get(i).getEpcId()) || StringUtils.isBlank(list.get(i).getName())) {							stringBuilder.append("第" + (i + 1) + "行记录的必填项有空值,导入失败。");							continue;						}						Forestry checkForestryEpcId = forestryService.getByProerties("epcId", list.get(i).getEpcId());						if (checkForestryEpcId != null) {							stringBuilder.append("第" + (i + 1) + "行记录的epc编码已存在,导入失败。");							continue;						}						if (list.get(i).getForestryType() == null) {							stringBuilder.append("第" + (i + 1) + "行记录的种类为空或不存在,导入失败。");							continue;						}						forestryService.persist(list.get(i));						count++;					}					json.put("success", true);					json.put("msg", count + "条记录导入完成。" + stringBuilder.toString());				} catch (Exception e) {					e.printStackTrace();					json.put("success", false);					json.put("msg", requestContext.getMessage("g_operateFailure"));					writeJSON(response, json.toString());				}			}		} else {			json.put("success", false);			json.put("msg", requestContext.getMessage("g_uploadNotExists"));		}		writeJSON(response, json.toString());	}	private List<Forestry> objectToForestry(List<Object[]> models) {		List<Forestry> forestryList = new ArrayList<Forestry>();		Forestry forestry = null;		for (int i = 0; i < models.size(); i++) {			try {				forestry = new Forestry();				forestry.setEpcId(models.get(i)[0].toString());				forestry.setName(models.get(i)[1].toString());				if (StringUtils.isBlank(models.get(i)[2].toString())) {					forestry.setPlantTime(null);				} else {					forestry.setPlantTime(sdfDate.parse(models.get(i)[2].toString()));				}				if (StringUtils.isBlank(models.get(i)[3].toString())) {					forestry.setEntryTime(null);				} else {					forestry.setEntryTime(sdfDate.parse(models.get(i)[3].toString()));				}				ForestryType forestryType = forestryTypeService.getByProerties("name", models.get(i)[4].toString());				forestry.setForestryType(forestryType);				forestryList.add(forestry);			} catch (Exception e) {				e.printStackTrace();				continue;			}		}		return forestryList;	}

 

 

 

5、开发权限管理。不仅可以管理功能模块的权限,也可以管理功能模块里面各个按钮的权限。

5.1、权限管理的界面如下。

 

5.2、权限管理的代码如下。

 

// 权限管理Ext.define(‘Forestry.app.systemManage.AuthorizationManagement‘, {	extend : ‘Ext.panel.Panel‘,	initComponent : function() {		var me = this;		Ext.apply(this, {			layout : ‘border‘,			items : [ Ext.create(‘Forestry.app.systemManage.AuthorizationManagement.SysUserGrid‘, {				cButtons : me.cButtons,				cName : me.cName			}), Ext.create(‘Forestry.app.systemManage.AuthorizationManagement.MenuTree‘) ]		});		this.callParent(arguments);	}});// 角色列表Ext.define(‘Forestry.app.systemManage.AuthorizationManagement.SysUserGrid‘, {	extend : ‘Ext.grid.Panel‘,	id : ‘authorizationmanagement-sysusergrid‘,	region : ‘west‘,	width : ‘18%‘,	initComponent : function() {		var me = this;		Ext.define(‘SysUserRoleList‘, {			extend : ‘Ext.data.Model‘,			idProperty : ‘role‘,			fields : [ {				name : ‘role‘,				type : ‘short‘			}, ‘roleName‘ ]		});		var sysusergridstore = Ext.create(‘Ext.data.Store‘, {			model : ‘SysUserRoleList‘,			// autoDestroy: true,			autoLoad : true,			remoteSort : true,			pageSize : globalPageSize,			proxy : {				type : ‘ajax‘,				url : appBaseUri + ‘/sys/sysuser/getRoleNameList‘,				extraParams : me.extraParams || null,				reader : {					type : ‘json‘,					root : ‘data‘,					totalProperty : ‘totalRecord‘,					successProperty : "success"				}			}		});		var sysusergridcolumns = [ {			text : "roleId",			dataIndex : ‘role‘,			hidden : true,			sortable : false,			editor : {				allowBlank : false			}		}, {			text : "角色",			dataIndex : ‘roleName‘,			sortable : false,			width : ‘85%‘,			editor : {				allowBlank : false			}		} ];		Ext.apply(this, {			store : sysusergridstore,			selModel : Ext.create(‘Ext.selection.CheckboxModel‘),			columns : sysusergridcolumns,			listeners : {				‘itemclick‘ : function(item, record) {					me.currentRole = record.get(‘role‘);					Ext.getCmp(‘authorizationmanagement-rolemenu‘).getStore().load({						params : {							‘role‘ : me.currentRole						}					});				}			}		});		this.callParent(arguments);	}});// 树形菜单Ext.define(‘Forestry.app.systemManage.AuthorizationManagement.MenuTree‘, {	extend : ‘Ext.tree.Panel‘,	id : ‘authorizationmanagement-rolemenu‘,	plain : true,	border : true,	region : ‘center‘,	autoScroll : true,	initComponent : function() {		var me = this;		var menutreestore = Ext.create(‘Ext.data.TreeStore‘, {			autoLoad : true,			proxy : {				type : ‘ajax‘,				url : appBaseUri + ‘/sys/authority/getAuthorizationList‘,				reader : {					type : ‘json‘,					root : ‘children‘				}			}		});		Ext.apply(this, {			// title : ‘菜单权限‘,			store : menutreestore,			rootVisible : false,			tbar : [ {				xtype : ‘button‘,				iconCls : ‘icon-save‘,				text : ‘保存菜单权限‘,				scope : this,				handler : me.saveMenuPermission			} ]		});		this.callParent(arguments);	},	saveMenuPermission : function() {		var me = this;		var roleId = Ext.getCmp(‘authorizationmanagement-sysusergrid‘).currentRole;		if (!roleId) {			globalObject.infoTip(‘请先选择角色!‘);			return;		};		var s = me.getChecked();		var ids = [];		for (var i = 0, r; r = s[i]; i++) {			if (r.get(‘id‘) != ‘root‘)				ids.push(r.get(‘id‘));		}		me.setLoading(‘权限保存中...‘);		Ext.Ajax.request({			url : appBaseUri + ‘/sys/roleauthority/saveRoleAuthority‘,			params : {				ids : ids.join(‘,‘),				role : roleId			},			success : function(response) {				me.setLoading(false);				var res = Ext.JSON.decode(response.responseText);				if (res && !res.success) {					Ext.Msg.alert(‘出错信息‘, res.msg);				} else {					globalObject.msgTip(‘保存成功!‘);				}			},			failure : function(response, opts) {				me.setLoading(false);				Ext.Msg.alert(‘出错信息‘, ‘操作失败!‘);			}		});	}});

 

 

 

源码有50多M(包括Jar包和SQL文件),点此获取。