一、系统介绍
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 = super.getParameter(name); value = value == null ? null : convert(value); return value; } public String convert(String target) { try { return new String(target.trim().getBytes("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="‘ + 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 = "/uploadAttachement", method = RequestMethod.POST) public void uploadAttachement(@RequestParam(value = "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 = "/importForestryFile", method = RequestMethod.POST) public void importForestryFile(@RequestParam(value = "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文件),点此获取。
基于ExtJS 4.2.1 + Hibernate 4.1.7 + Spring MVC 3.2.8 的通用后台管理系统