基于ExtJS 5+Hibernate 4.3+SpringMVC 4的通用后台管理系统 含源码

一、系统介绍

1、基于最新的ExtJS 5.1免费无限制版本开发。

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

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


源码有50多M(包括Jar包和SQL文件),点此获取咨询QQ:1345523590(咨询和技术支持)


二、特色功能
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、开发工具:Eclipse、MyEclipse等IDE。
2、采用ExtJS 5.1无限制免费版本,放心用于网站开发。
3、采用Spring MVC 4.0.9。
4、采用Hibernate 4.3.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。

[html]  view plain copy
  1. <filter>  
  2.     <filter-name>characterEncoding</filter-name>  
  3.     <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>  
  4.     <init-param>  
  5.     <param-name>encoding</param-name>  
  6.     <param-value>UTF-8</param-value>  
  7.     </init-param>  
  8.     <init-param>   
  9.                <param-name>forceEncoding</param-name>   
  10.                <param-value>true</param-value>   
  11.            </init-param>   
  12. </filter>  
  13. <filter-mapping>  
  14.     <filter-name>characterEncoding</filter-name>  
  15.     <url-pattern>/*</url-pattern>  
  16. </filter-mapping>  


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

[java]  view plain copy
  1. package core.web;  
  2.   
  3. import java.io.UnsupportedEncodingException;  
  4.   
  5. import javax.servlet.http.HttpServletRequest;  
  6. import javax.servlet.http.HttpServletRequestWrapper;  
  7.   
  8. public class GetHttpServletRequestWrapper extends HttpServletRequestWrapper {  
  9.   
  10.     private String charset = "UTF-8";  
  11.   
  12.     public GetHttpServletRequestWrapper(HttpServletRequest request) {  
  13.         super(request);  
  14.     }  
  15.   
  16.     /** 
  17.      * 获得被装饰对象的引用和采用的字符编码 
  18.      *  
  19.      * @param request 
  20.      * @param charset 
  21.      */  
  22.     public GetHttpServletRequestWrapper(HttpServletRequest request, String charset) {  
  23.         super(request);  
  24.         this.charset = charset;  
  25.     }  
  26.   
  27.     /** 
  28.      * 调用被包装的请求对象的getParameter方法获得参数,然后再进行编码转换 
  29.      */  
  30.     public String getParameter(String name) {  
  31.         String value = super.getParameter(name);  
  32.         value = value == null ? null : convert(value);  
  33.         return value;  
  34.     }  
  35.   
  36.     public String convert(String target) {  
  37.         try {  
  38.             return new String(target.trim().getBytes("ISO-8859-1"), charset);  
  39.         } catch (UnsupportedEncodingException e) {  
  40.             return target;  
  41.         }  
  42.     }  
  43. }  


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

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



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

[javascript]  view plain copy
  1. Ext.define('Ext.ux.custom.ImageHtmlEditor', {  
  2.     extend : 'Ext.util.Observable',  
  3.     alias : 'widget.imagehtmleditor',  
  4.     langTitle : '插入图片',  
  5.     langIconCls : 'icon-image',  
  6.     init : function(view) {  
  7.         var scope = this;  
  8.         view.on('render'function() {  
  9.             scope.onRender(view);  
  10.         });  
  11.     },  
  12.   
  13.     /** 
  14.      * 添加"插入图片"按钮 
  15.      */  
  16.     onRender : function(view) {  
  17.         var scope = this;  
  18.         view.getToolbar().add({  
  19.             iconCls : scope.langIconCls,  
  20.             tooltip : {  
  21.                 title : scope.langTitle,  
  22.                 width : 160,  
  23.                 text : '上传本地图片或链接网络图片'  
  24.             },  
  25.             handler : function() {  
  26.                 scope.showImgWindow(view);  
  27.             }  
  28.         });  
  29.     },  
  30.   
  31.     /** 
  32.      * 显示"插入图片"窗体 
  33.      */  
  34.     showImgWindow : function(view) {  
  35.         var scope = this;  
  36.         Ext.create('Ext.window.Window', {  
  37.             width : 400,  
  38.             height : 310,  
  39.             title : scope.langTitle,  
  40.             layout : 'fit',  
  41.             autoShow : true,  
  42.             modal : true,  
  43.             resizable : false,  
  44.             maximizable : false,  
  45.             constrain : true,  
  46.             plain : true,  
  47.             enableTabScroll : true,  
  48.             border : false,  
  49.             items : [ {  
  50.                 xtype : 'tabpanel',  
  51.                 enableTabScroll : true,  
  52.                 bodyPadding : 10,  
  53.                 items : [ {  
  54.                     title : '上传本地图片',  
  55.                     items : [ {  
  56.                         xtype : 'form',  
  57.                         layout : 'column',  
  58.                         autoScroll : true,  
  59.                         border : false,  
  60.                         defaults : {  
  61.                             columnWidth : 1,  
  62.                             labelWidth : 80,  
  63.                             labelAlign : 'left',  
  64.                             padding : 5,  
  65.                             allowBlank : false  
  66.                         },  
  67.                         items : [ {  
  68.                             xtype : 'fileuploadfield',  
  69.                             fieldLabel : '选择文件',  
  70.                             afterLabelTextTpl : '<span style="color:#FF0000;">*</span>',  
  71.                             buttonText : '请选择...',  
  72.                             name : 'uploadAttachment',  
  73.                             emptyText : '请选择图片',  
  74.                             blankText : '图片不能为空',  
  75.                             listeners : {  
  76.                                 change : function(view, value, eOpts) {  
  77.                                     scope.uploadImgCheck(view, value);  
  78.                                 }  
  79.                             }  
  80.                         }, {  
  81.                             xtype : 'fieldcontainer',  
  82.                             fieldLabel : '图片大小',  
  83.                             layout : 'hbox',  
  84.                             defaultType : 'numberfield',  
  85.                             defaults : {  
  86.                                 flex : 1,  
  87.                                 labelWidth : 20,  
  88.                                 labelAlign : 'left',  
  89.                                 allowBlank : true  
  90.                             },  
  91.                             items : [ {  
  92.                                 fieldLabel : '宽',  
  93.                                 name : 'width',  
  94.                                 minValue : 1  
  95.                             }, {  
  96.                                 fieldLabel : '高',  
  97.                                 name : 'height',  
  98.                                 minValue : 1  
  99.                             } ]  
  100.                         }, {  
  101.                             xtype : 'textfield',  
  102.                             fieldLabel : '图片说明',  
  103.                             name : 'content',  
  104.                             allowBlank : true,  
  105.                             maxLength : 100,  
  106.                             emptyText : '简短的图片说明'  
  107.                         }, {  
  108.                             columnWidth : 1,  
  109.                             xtype : 'fieldset',  
  110.                             title : '上传须知',  
  111.                             layout : {  
  112.                                 type : 'table',  
  113.                                 columns : 1  
  114.                             },  
  115.                             collapsible : false,// 是否可折叠  
  116.                             defaultType : 'label',// 默认的Form表单组件  
  117.                             items : [ {  
  118.                                 html : '1、上传图片大小不超过2MB.'  
  119.                             }, {  
  120.                                 html : '2、支持以下格式的图片:jpg,jpeg,png,gif,bmp.'  
  121.                             } ]  
  122.                         } ],  
  123.                         buttons : [ '->', {  
  124.                             text : '保存',  
  125.                             action : 'btn_save',  
  126.                             iconCls : 'icon-save',  
  127.                             handler : function(btn) {  
  128.                                 scope.saveUploadImg(btn, view);  
  129.                             }  
  130.                         }, {  
  131.                             text : '取消',  
  132.                             iconCls : 'icon-cancel',  
  133.                             handler : function(btn) {  
  134.                                 btn.up('window').close();  
  135.                             }  
  136.                         }, '->' ]  
  137.                     } ]  
  138.                 }, {  
  139.                     title : '链接网络图片',  
  140.                     items : [ {  
  141.                         xtype : 'form',  
  142.                         layout : 'column',  
  143.                         autoScroll : true,  
  144.                         border : false,  
  145.                         defaults : {  
  146.                             columnWidth : 1,  
  147.                             labelWidth : 80,  
  148.                             labelAlign : 'left',  
  149.                             padding : 5,  
  150.                             allowBlank : false  
  151.                         },  
  152.                         items : [ {  
  153.                             xtype : 'textfield',  
  154.                             fieldLabel : '图片地址',  
  155.                             afterLabelTextTpl : '<span style="color:#FF0000;">*</span>',  
  156.                             name : 'url',  
  157.                             emptyText : '请填入支持外链的长期有效的图片URL',  
  158.                             blankText : '图片地址不能为空',  
  159.                             vtype : 'url'  
  160.                         }, {  
  161.                             xtype : 'fieldcontainer',  
  162.                             fieldLabel : '图片大小',  
  163.                             layout : 'hbox',  
  164.                             defaultType : 'numberfield',  
  165.                             defaults : {  
  166.                                 flex : 1,  
  167.                                 labelWidth : 20,  
  168.                                 labelAlign : 'left',  
  169.                                 allowBlank : true  
  170.                             },  
  171.                             items : [ {  
  172.                                 fieldLabel : '宽',  
  173.                                 name : 'width',  
  174.                                 minValue : 1  
  175.                             }, {  
  176.                                 fieldLabel : '高',  
  177.                                 name : 'height',  
  178.                                 minValue : 1  
  179.                             } ]  
  180.                         }, {  
  181.                             xtype : 'textfield',  
  182.                             fieldLabel : '图片说明',  
  183.                             name : 'content',  
  184.                             allowBlank : true,  
  185.                             maxLength : 100,  
  186.                             emptyText : '简短的图片说明'  
  187.                         } ],  
  188.                         buttons : [ '->', {  
  189.                             text : '保存',  
  190.                             action : 'btn_save',  
  191.                             iconCls : 'icon-save',  
  192.                             handler : function(btn) {  
  193.                                 scope.saveRemoteImg(btn, view);  
  194.                             }  
  195.                         }, {  
  196.                             text : '取消',  
  197.                             iconCls : 'icon-cancel',  
  198.                             handler : function(btn) {  
  199.                                 btn.up('window').close();  
  200.                             }  
  201.                         }, '->' ]  
  202.                     } ]  
  203.                 } ]  
  204.             } ]  
  205.         });  
  206.     },  
  207.   
  208.     /** 
  209.      * 上传图片验证 
  210.      */  
  211.     uploadImgCheck : function(fileObj, fileName) {  
  212.         var scope = this;  
  213.         // 图片类型验证  
  214.         if (!(scope.getImgTypeCheck(scope.getImgHZ(fileName)))) {  
  215.             globalObject.errTip('上传图片类型有误!');  
  216.             fileObj.reset();// 清空上传内容  
  217.             return;  
  218.         }  
  219.     },  
  220.   
  221.     /** 
  222.      * 获取图片后缀(小写) 
  223.      */  
  224.     getImgHZ : function(imgName) {  
  225.         // 后缀  
  226.         var hz = '';  
  227.         // 图片名称中最后一个.的位置  
  228.         var index = imgName.lastIndexOf('.');  
  229.         if (index != -1) {  
  230.             // 后缀转成小写  
  231.             hz = imgName.substr(index + 1).toLowerCase();  
  232.         }  
  233.         return hz;  
  234.     },  
  235.   
  236.     /** 
  237.      * 图片类型验证 
  238.      */  
  239.     getImgTypeCheck : function(hz) {  
  240.         var typestr = 'jpg,jpeg,png,gif,bmp';  
  241.         var types = typestr.split(',');// 图片类型  
  242.         for (var i = 0; i < types.length; i++) {  
  243.             if (hz == types[i]) {  
  244.                 return true;  
  245.             }  
  246.         }  
  247.         return false;  
  248.     },  
  249.   
  250.     /** 
  251.      * 上传图片 
  252.      */  
  253.     saveUploadImg : function(btn, view) {  
  254.         var scope = this;  
  255.         var windowObj = btn.up('window');// 获取Window对象  
  256.         var formObj = btn.up('form');// 获取Form对象  
  257.         if (formObj.isValid()) { // 验证Form表单  
  258.             formObj.form.doAction('submit', {  
  259.                 url : appBaseUri + '/sys/forestrytype/uploadAttachement',  
  260.                 method : 'POST',  
  261.                 submitEmptyText : false,  
  262.                 waitMsg : '正在上传图片,请稍候...',  
  263.                 timeout : 60000, // 60s  
  264.                 success : function(response, options) {  
  265.                     var result = options.result;  
  266.                     if (!result.success) {  
  267.                         globalObject.errTip(result.msg);  
  268.                         return;  
  269.                     }  
  270.                     var url = result.data;  
  271.                     var content = formObj.getForm().findField("content").getValue();  
  272.                     var width = formObj.getForm().findField("width").getValue();  
  273.                     var height = formObj.getForm().findField("height").getValue();  
  274.                     var values = {  
  275.                         url : appBaseUri + '/static/img/upload/' + url,  
  276.                         content : content,  
  277.                         width : width,  
  278.                         height : height  
  279.                     };  
  280.                     scope.insertImg(view, values);  
  281.                     windowObj.close();// 关闭窗体  
  282.                 },  
  283.                 failure : function(response, options) {  
  284.                     globalObject.errTip(options.result.msg);  
  285.                 }  
  286.             });  
  287.         }  
  288.     },  
  289.   
  290.     /** 
  291.      * 保存远程的图片 
  292.      */  
  293.     saveRemoteImg : function(btn, view) {  
  294.         var scope = this;  
  295.         var windowObj = btn.up('window');// 获取Window对象  
  296.         var formObj = btn.up('form');// 获取Form对象  
  297.         if (formObj.isValid()) {// 验证Form表单  
  298.             var values = formObj.getValues();// 获取Form表单的值  
  299.             scope.insertImg(view, values);  
  300.             windowObj.close();// 关闭窗体  
  301.         }  
  302.     },  
  303.   
  304.     /** 
  305.      * 插入图片 
  306.      */  
  307.     insertImg : function(view, data) {  
  308.         var url = data.url;  
  309.         var content = data.content;  
  310.         var width = data.width;  
  311.         var height = data.height;  
  312.         var str = '<img src="' + url + '" border="0" ';  
  313.         if (content != undefined && content != null && content != '') {  
  314.             str += ' title="' + content + '" ';  
  315.         }  
  316.         if (width != undefined && width != null && width != 0) {  
  317.             str += ' width="' + width + '" ';  
  318.         }  
  319.         if (height != undefined && height != null && height != 0) {  
  320.             str += ' height="' + height + '" ';  
  321.         }  
  322.         str += ' />';  
  323.         view.insertAtCursor(str);  
  324.     }  
  325. });  


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

[java]  view plain copy
  1. @RequestMapping(value = "/uploadAttachement", method = RequestMethod.POST)  
  2. public void uploadAttachement(@RequestParam(value = "uploadAttachment", required = false) MultipartFile file, HttpServletRequest request, HttpServletResponse response) throws Exception {  
  3.     RequestContext requestContext = new RequestContext(request);  
  4.     JSONObject json = new JSONObject();  
  5.     if (!file.isEmpty()) {  
  6.         if (file.getSize() > 2097152) {  
  7.             json.put("msg", requestContext.getMessage("g_fileTooLarge"));  
  8.         } else {  
  9.             try {  
  10.                 String originalFilename = file.getOriginalFilename();  
  11.                 String fileName = sdf.format(new Date()) + ForestryUtils.getRandomString(3) + originalFilename.substring(originalFilename.lastIndexOf("."));  
  12.                 File filePath = new File(getClass().getClassLoader().getResource("/").getPath().replace("/WEB-INF/classes/""/static/img/upload/" + DateFormatUtils.format(new Date(), "yyyyMM")));  
  13.                 if (!filePath.exists()) {  
  14.                     filePath.mkdirs();  
  15.                 }  
  16.                 file.transferTo(new File(filePath.getAbsolutePath() + "\\" + fileName));  
  17.                 json.put("success"true);  
  18.                 json.put("data", DateFormatUtils.format(new Date(), "yyyyMM") + "/" + fileName);  
  19.                 json.put("msg", requestContext.getMessage("g_uploadSuccess"));  
  20.             } catch (Exception e) {  
  21.                 e.printStackTrace();  
  22.                 json.put("msg", requestContext.getMessage("g_uploadFailure"));  
  23.             }  
  24.         }  
  25.     } else {  
  26.         json.put("msg", requestContext.getMessage("g_uploadNotExists"));  
  27.     }  
  28.     writeJSON(response, json.toString());  
  29. }  


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

[javascript]  view plain copy
  1. Ext.define('Ext.ux.custom.GlobalGridPanel', {  
  2.     extend : 'Ext.grid.Panel',  
  3.     alias : 'widget.globalgrid',  
  4.     xtype : 'cell-editing',  
  5.     initComponent : function() {  
  6.         var me = this;  
  7.         var singleId;  
  8.           
  9.         var uniqueID = me.cName + (me.cId ? me.cId : '') + (me.myId ? me.myId : '');  
  10.   
  11.         this.cellEditing = Ext.create('Ext.grid.plugin.CellEditing', {  
  12.             clicksToEdit : 2  
  13.         });  
  14.   
  15.         var tbarMenus = new Array();  
  16.         if (globalObject.haveActionMenu(me.cButtons, 'Add')) {  
  17.             tbarMenus.push({  
  18.                 xtype : 'button',  
  19.                 itemId : 'btnAdd',  
  20.                 iconCls : 'icon-add',  
  21.                 text : '添加',  
  22.                 scope : this,  
  23.                 handler : me.onAddClick  
  24.             });  
  25.         }  
  26.         if (globalObject.haveActionMenu(me.cButtons, 'Import')) {  
  27.             tbarMenus.push({  
  28.                 xtype : 'button',  
  29.                 itemId : 'btnImport',  
  30.                 iconCls : 'icon-excel',  
  31.                 text : '导入',  
  32.                 scope : this,  
  33.                 handler : me.onImportClick  
  34.             });  
  35.         }  
  36.         if (globalObject.haveActionMenu(me.cButtons, 'Delete')) {  
  37.             tbarMenus.push({  
  38.                 xtype : 'button',  
  39.                 itemId : 'btnDelete',  
  40.                 iconCls : 'icon-delete',  
  41.                 text : '删除',  
  42.                 scope : this,  
  43.                 disabled : true,  
  44.                 handler : me.onDeleteClick  
  45.             });  
  46.         }  
  47.         if (globalObject.haveActionMenu(me.cButtons, 'Export')) {  
  48.             tbarMenus.push({  
  49.                 xtype : 'splitbutton',  
  50.                 itemId : 'btnImport',  
  51.                 text : '导出',  
  52.                 scope : this,  
  53.                 handler : function() {  
  54.                     me.onExportClick(false);  
  55.                 },  
  56.                 menu : [ {  
  57.                     text : '导出(包括隐藏列)',  
  58.                     handler : function() {  
  59.                         me.onExportClick(true);  
  60.                     }  
  61.                 }, {  
  62.                     text : '导出选中数据',  
  63.                     handler : function() {  
  64.                         me.onExportClick(falsetrue);  
  65.                     }  
  66.                 }, {  
  67.                     text : '导出选中数据(包括隐藏列)',  
  68.                     handler : function() {  
  69.                         me.onExportClick(truetrue);  
  70.                     }  
  71.                 } ]  
  72.             });  
  73.         }  
  74.   
  75.         if (tbarMenus.length == 0)  
  76.             me.hideTBar = true;  
  77.         this.ttoolbar = Ext.create('Ext.toolbar.Toolbar', {  
  78.             hidden : me.hideTBar || false,  
  79.             items : tbarMenus  
  80.         });  
  81.   
  82.         Ext.apply(this, {  
  83.             stateful : me.cName ? true : false,  
  84.             stateId : me.cName ? (uniqueID + 'gird') : null,  
  85.             enableColumnMove : me.cName ? true : false,  
  86.             plugins : this.plugins,  
  87.             selModel : Ext.create('Ext.selection.CheckboxModel'),  
  88.             border : false,  
  89.             tbar : this.ttoolbar,  
  90.             bbar : me.hideBBar ? null : Ext.create('Ext.PagingToolbar', {  
  91.                 store : me.getStore(),  
  92.                 displayInfo : true  
  93.             }),  
  94.             listeners : {  
  95.                 itemdblclick : function(dataview, record, item, index, e) {  
  96.                     me.onViewClick();  
  97.                 }  
  98.             }  
  99.         });  
  100.         this.getSelectionModel().on('selectionchange'function(sm, records) {  
  101.             if (me.down('#btnDelete'))  
  102.                 me.down('#btnDelete').setDisabled(sm.getCount() == 0);  
  103.         });  
  104.   
  105.         this.callParent(arguments);  
  106.     },  
  107.     createStore : function(config) {  
  108.         Ext.applyIf(this, config);  
  109.   
  110.         return Ext.create('Ext.data.Store', {  
  111.             model : config.modelName,  
  112.             // autoDestroy: true,  
  113.             // autoLoad: true,  
  114.             remoteSort : true,  
  115.             pageSize : globalPageSize,  
  116.             proxy : {  
  117.                 type : 'ajax',  
  118.                 url : config.proxyUrl,  
  119.                 extraParams : config.extraParams || null,  
  120.                 reader : {  
  121.                     type : 'json',  
  122.                     root : 'data',  
  123.                     totalProperty : 'totalRecord',  
  124.                     successProperty : "success"  
  125.                 }  
  126.             },  
  127.             sorters : [ {  
  128.                 property : config.sortProperty || 'id',  
  129.                 direction : config.sortDirection || 'DESC'  
  130.             } ]  
  131.         });  
  132.     },  
  133.     getTabId : function() {  
  134.         return this.up('panel').getId();  
  135.     },  
  136.     onAddClick : function() {  
  137.     },  
  138.     onEditClick : function() {  
  139.     },  
  140.     onImportClick : function() {  
  141.     },  
  142.     onViewClick : function() {  
  143.     },  
  144.     onDeleteClick : function() {  
  145.         var me = this;  
  146.         globalObject.confirmTip('删除的记录不可恢复,继续吗?'function(btn) {  
  147.             if (btn == 'yes') {  
  148.                 var s = me.getSelectionModel().getSelection();  
  149.                 var ids = [];  
  150.                 var idProperty = me.idProperty || 'id';  
  151.                 for (var i = 0, r; r = s[i]; i++) {  
  152.                     ids.push(r.get(idProperty));  
  153.                 }  
  154.                 Ext.Ajax.request({  
  155.                     url : me.proxyDeleteUrl,  
  156.                     params : {  
  157.                         ids : ids.join(',') || singleId  
  158.                     },  
  159.                     success : function(response) {  
  160.                         if (response.responseText != '') {  
  161.                             var res = Ext.JSON.decode(response.responseText);  
  162.                             if (res.success) {  
  163.                                 globalObject.msgTip('操作成功!');  
  164.                                 // Ext.example.msg('系统信息', '{0}', "操作成功!");  
  165.                                 me.getStore().reload();  
  166.                             } else {  
  167.                                 globalObject.errTip('操作失败!' + res.msg);  
  168.                             }  
  169.                         }  
  170.                     }  
  171.                 });  
  172.             }  
  173.         });  
  174.     },  
  175.     onExportClick : function(importHideColumn, onlySelected) {  
  176.         globalObject.exportToExcel(this, importHideColumn, onlySelected);  
  177.     }  
  178. });  


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

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



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

[java]  view plain copy
  1. private static SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");  
  2.   
  3. @RequestMapping(value = "/importForestryFile", method = RequestMethod.POST)  
  4. public void importForestryFile(@RequestParam(value = "importedFile", required = false) MultipartFile file, HttpServletRequest request, HttpServletResponse response) throws Exception {  
  5.     RequestContext requestContext = new RequestContext(request);  
  6.     JSONObject json = new JSONObject();  
  7.     if (!file.isEmpty()) {  
  8.         if (file.getSize() > 2097152) {  
  9.             json.put("msg", requestContext.getMessage("g_fileTooLarge"));  
  10.         } else {  
  11.             try {  
  12.                 String originalFilename = file.getOriginalFilename();  
  13.                 String fileName = sdf.format(new Date()) + ForestryUtils.getRandomString(3) + originalFilename.substring(originalFilename.lastIndexOf("."));  
  14.                 File filePath = new File(getClass().getClassLoader().getResource("/").getPath().replace("/WEB-INF/classes/""/static/download/attachment/" + DateFormatUtils.format(new Date(), "yyyyMM")));  
  15.                 if (!filePath.exists()) {  
  16.                     filePath.mkdirs();  
  17.                 }  
  18.                 String serverFile = filePath.getAbsolutePath() + "\\" + fileName;  
  19.                 file.transferTo(new File(serverFile));  
  20.   
  21.                 String fileType = fileName.substring(fileName.lastIndexOf(".") + 1);  
  22.                 if (!fileType.equalsIgnoreCase("xls") && !fileType.equalsIgnoreCase("xlsx")) {  
  23.                     json.put("success"false);  
  24.                     json.put("msg", requestContext.getMessage("g_notValidExcel"));  
  25.                     writeJSON(response, json.toString());  
  26.                     return;  
  27.                 }  
  28.                 int count = 0;  
  29.                 StringBuilder stringBuilder = new StringBuilder();  
  30.                 InputStream xls = new FileInputStream(serverFile);  
  31.                 Workbook wb = null;  
  32.                 Sheet sheet = null;  
  33.                 Row currentRow = null;  
  34.                 Row headRow = null;  
  35.                 Cell currentCell = null;  
  36.                 if (fileType.equals("xls")) {  
  37.                     wb = new HSSFWorkbook(xls);  
  38.                 } else if (fileType.equals("xlsx")) {  
  39.                     wb = new XSSFWorkbook(xls);  
  40.                 }  
  41.                 sheet = wb.getSheetAt(0);// excel中至少会存在一个sheet页  
  42.                 int rowNum = sheet.getPhysicalNumberOfRows();// 物理有效行数  
  43.                 Object[] rowValues = null;// excel中一行树木信息  
  44.                 List<Object[]> models = new ArrayList<Object[]>();// excel中全部树木信息  
  45.                 if (rowNum > 1) {  
  46.                     headRow = sheet.getRow(0);  
  47.                     columns: for (int i = 1; i < rowNum; i++) {  
  48.                         currentRow = sheet.getRow(i);  
  49.                         if (currentRow != null) {  
  50.                             rowValues = new Object[5];  
  51.                             // int cellNum = currentRow.getLastCellNum();// 总单元格数目  
  52.                             for (short j = 0; j < 5; j++) {  
  53.                                 try {  
  54.                                     currentCell = currentRow.getCell(j);  
  55.                                     Object obj = null;  
  56.                                     if (currentCell == null) {  
  57.                                         obj = "";  
  58.                                     } else {  
  59.                                         switch (currentCell.getCellType()) {  
  60.                                             case Cell.CELL_TYPE_BLANK:  
  61.                                                 obj = "";  
  62.                                                 break;  
  63.                                             case Cell.CELL_TYPE_STRING:  
  64.                                                 obj = currentCell.getRichStringCellValue();  
  65.                                                 break;  
  66.                                             case Cell.CELL_TYPE_NUMERIC:  
  67.                                                 if (HSSFDateUtil.isCellDateFormatted(currentCell)) {  
  68.                                                     double d = currentCell.getNumericCellValue();  
  69.                                                     Date date = HSSFDateUtil.getJavaDate(d);  
  70.                                                     obj = sdfDate.format(date);  
  71.                                                 } else {  
  72.                                                     NumberFormat nf = NumberFormat.getInstance();  
  73.                                                     nf.setGroupingUsed(false);//true时的格式:1,234,567,890  
  74.                                                     obj = nf.format(currentCell.getNumericCellValue());  
  75.                                                 }  
  76.                                                 break;  
  77.                                             default:  
  78.                                                 obj = "";  
  79.                                                 break;  
  80.                                         }  
  81.                                     }  
  82.                                     String cellVal = obj.toString();  
  83.                                     rowValues[j] = cellVal;  
  84.                                 } catch (IllegalStateException e) {  
  85.                                     rowValues = null;  
  86.                                     stringBuilder.append("第" + i + "行," + headRow.getCell(j).getRichStringCellValue() + "列输入了非法值,未导入成功!");  
  87.                                     continue columns;  
  88.                                 } catch (NullPointerException e) {  
  89.                                     rowValues = null;  
  90.                                     stringBuilder.append("第" + i + "行," + headRow.getCell(j).getRichStringCellValue() + "列输入了空值,未导入成功!");  
  91.                                     continue columns;  
  92.                                 } catch (Exception e) {  
  93.                                     rowValues = null;  
  94.                                     stringBuilder.append(e.getMessage());  
  95.                                     continue columns;  
  96.                                 }  
  97.                             }  
  98.                             if (rowValues != null) {  
  99.                                 models.add(rowValues);  
  100.                             }  
  101.                         }  
  102.                     }  
  103.                 } else if (rowNum <= 1 && rowNum > 0) {// 表示模版中只存在头部信息  
  104.                     json.put("success"false);  
  105.                     json.put("msg""Excel表格中没有需要导入 的内容!");  
  106.                     writeJSON(response, json.toString());  
  107.                     return;  
  108.                 } else if (rowNum <= 0) {// 表示这是一个空sheet页  
  109.                     json.put("success"false);  
  110.                     json.put("msg""所导入文件格式不正确,请下载模板!");  
  111.                     writeJSON(response, json.toString());  
  112.                     return;  
  113.                 }  
  114.                 List<Forestry> list = objectToForestry(models);// Object-->Forestry  
  115.                 for (int i = 0; i < list.size(); i++) {  
  116.                     if (StringUtils.isBlank(list.get(i).getEpcId()) || StringUtils.isBlank(list.get(i).getName())) {  
  117.                         stringBuilder.append("第" + (i + 1) + "行记录的必填项有空值,导入失败。");  
  118.                         continue;  
  119.                     }  
  120.                     Forestry checkForestryEpcId = forestryService.getByProerties("epcId", list.get(i).getEpcId());  
  121.                     if (checkForestryEpcId != null) {  
  122.                         stringBuilder.append("第" + (i + 1) + "行记录的epc编码已存在,导入失败。");  
  123.                         continue;  
  124.                     }  
  125.                     if (list.get(i).getForestryType() == null) {  
  126.                         stringBuilder.append("第" + (i + 1) + "行记录的种类为空或不存在,导入失败。");  
  127.                         continue;  
  128.                     }  
  129.                     forestryService.persist(list.get(i));  
  130.                     count++;  
  131.                 }  
  132.   
  133.                 json.put("success"true);  
  134.                 json.put("msg", count + "条记录导入完成。" + stringBuilder.toString());  
  135.             } catch (Exception e) {  
  136.                 e.printStackTrace();  
  137.                 json.put("success"false);  
  138.                 json.put("msg", requestContext.getMessage("g_operateFailure"));  
  139.                 writeJSON(response, json.toString());  
  140.             }  
  141.         }  
  142.     } else {  
  143.         json.put("success"false);  
  144.         json.put("msg", requestContext.getMessage("g_uploadNotExists"));  
  145.     }  
  146.     writeJSON(response, json.toString());  
  147. }  
  148.   
  149. private List<Forestry> objectToForestry(List<Object[]> models) {  
  150.     List<Forestry> forestryList = new ArrayList<Forestry>();  
  151.     Forestry forestry = null;  
  152.     for (int i = 0; i < models.size(); i++) {  
  153.         try {  
  154.             forestry = new Forestry();  
  155.             forestry.setEpcId(models.get(i)[0].toString());  
  156.             forestry.setName(models.get(i)[1].toString());  
  157.             if (StringUtils.isBlank(models.get(i)[2].toString())) {  
  158.                 forestry.setPlantTime(null);  
  159.             } else {  
  160.                 forestry.setPlantTime(sdfDate.parse(models.get(i)[2].toString()));  
  161.             }  
  162.             if (StringUtils.isBlank(models.get(i)[3].toString())) {  
  163.                 forestry.setEntryTime(null);  
  164.             } else {  
  165.                 forestry.setEntryTime(sdfDate.parse(models.get(i)[3].toString()));  
  166.             }  
  167.             ForestryType forestryType = forestryTypeService.getByProerties("name", models.get(i)[4].toString());  
  168.             forestry.setForestryType(forestryType);  
  169.             forestryList.add(forestry);  
  170.         } catch (Exception e) {  
  171.             e.printStackTrace();  
  172.             continue;  
  173.         }  
  174.     }  
  175.     return forestryList;  
  176. }  


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

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


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

[javascript]  view plain copy
  1. // 权限管理  
  2. Ext.define('Forestry.app.systemManage.AuthorizationManagement', {  
  3.     extend : 'Ext.panel.Panel',  
  4.     initComponent : function() {  
  5.         var me = this;  
  6.         Ext.apply(this, {  
  7.             layout : 'border',  
  8.             items : [ Ext.create('Forestry.app.systemManage.AuthorizationManagement.SysUserGrid', {  
  9.                 cButtons : me.cButtons,  
  10.                 cName : me.cName  
  11.             }), Ext.create('Forestry.app.systemManage.AuthorizationManagement.MenuTree') ]  
  12.         });  
  13.         this.callParent(arguments);  
  14.     }  
  15. });  
  16.   
  17. // 角色列表  
  18. Ext.define('Forestry.app.systemManage.AuthorizationManagement.SysUserGrid', {  
  19.     extend : 'Ext.grid.Panel',  
  20.     id : 'authorizationmanagement-sysusergrid',  
  21.     region : 'west',  
  22.     width : '18%',  
  23.     initComponent : function() {  
  24.         var me = this;  
  25.         Ext.define('SysUserRoleList', {  
  26.             extend : 'Ext.data.Model',  
  27.             idProperty : 'role',  
  28.             fields : [ {  
  29.                 name : 'role',  
  30.                 type : 'short'  
  31.             }, 'roleName' ]  
  32.         });  
  33.         var sysusergridstore = Ext.create('Ext.data.Store', {  
  34.             model : 'SysUserRoleList',  
  35.             // autoDestroy: true,  
  36.             autoLoad : true,  
  37.             remoteSort : true,  
  38.             pageSize : globalPageSize,  
  39.             proxy : {  
  40.                 type : 'ajax',  
  41.                 url : appBaseUri + '/sys/sysuser/getRoleNameList',  
  42.                 extraParams : me.extraParams || null,  
  43.                 reader : {  
  44.                     type : 'json',  
  45.                     root : 'data',  
  46.                     totalProperty : 'totalRecord',  
  47.                     successProperty : "success"  
  48.                 }  
  49.             }  
  50.         });  
  51.         var sysusergridcolumns = [ {  
  52.             text : "roleId",  
  53.             dataIndex : 'role',  
  54.             hidden : true,  
  55.             sortable : false,  
  56.             editor : {  
  57.                 allowBlank : false  
  58.             }  
  59.         }, {  
  60.             text : "角色",  
  61.             dataIndex : 'roleName',  
  62.             sortable : false,  
  63.             width : '85%',  
  64.             editor : {  
  65.                 allowBlank : false  
  66.             }  
  67.         } ];  
  68.         Ext.apply(this, {  
  69.             store : sysusergridstore,  
  70.             selModel : Ext.create('Ext.selection.CheckboxModel'),  
  71.             columns : sysusergridcolumns,  
  72.             listeners : {  
  73.                 'itemclick' : function(item, record) {  
  74.                     me.currentRole = record.get('role');  
  75.                     Ext.getCmp('authorizationmanagement-rolemenu').getStore().load({  
  76.                         params : {  
  77.                             'role' : me.currentRole  
  78.                         }  
  79.                     });  
  80.                 }  
  81.             }  
  82.         });  
  83.         this.callParent(arguments);  
  84.     }  
  85. });  
  86.   
  87. // 树形菜单  
  88. Ext.define('Forestry.app.systemManage.AuthorizationManagement.MenuTree', {  
  89.     extend : 'Ext.tree.Panel',  
  90.     id : 'authorizationmanagement-rolemenu',  
  91.     plain : true,  
  92.     border : true,  
  93.     region : 'center',  
  94.     autoScroll : true,  
  95.     initComponent : function() {  
  96.         var me = this;  
  97.         var menutreestore = Ext.create('Ext.data.TreeStore', {  
  98.             autoLoad : true,  
  99.             proxy : {  
  100.                 type : 'ajax',  
  101.                 url : appBaseUri + '/sys/authority/getAuthorizationList',  
  102.                 reader : {  
  103.                     type : 'json',  
  104.                     root : 'children'  
  105.                 }  
  106.             }  
  107.         });  
  108.         Ext.apply(this, {  
  109.             // title : '菜单权限',  
  110.             store : menutreestore,  
  111.             rootVisible : false,  
  112.             tbar : [ {  
  113.                 xtype : 'button',  
  114.                 iconCls : 'icon-save',  
  115.                 text : '保存菜单权限',  
  116.                 scope : this,  
  117.                 handler : me.saveMenuPermission  
  118.             } ]  
  119.         });  
  120.         this.callParent(arguments);  
  121.     },  
  122.     saveMenuPermission : function() {  
  123.         var me = this;  
  124.         var roleId = Ext.getCmp('authorizationmanagement-sysusergrid').currentRole;  
  125.         if (!roleId) {  
  126.             globalObject.infoTip('请先选择角色!');  
  127.             return;  
  128.         };  
  129.         var s = me.getChecked();  
  130.         var ids = [];  
  131.         for (var i = 0, r; r = s[i]; i++) {  
  132.             if (r.get('id') != 'root')  
  133.                 ids.push(r.get('id'));  
  134.         }  
  135.         me.setLoading('权限保存中...');  
  136.         Ext.Ajax.request({  
  137.             url : appBaseUri + '/sys/roleauthority/saveRoleAuthority',  
  138.             params : {  
  139.                 ids : ids.join(','),  
  140.                 role : roleId  
  141.             },  
  142.             success : function(response) {  
  143.                 me.setLoading(false);  
  144.                 var res = Ext.JSON.decode(response.responseText);  
  145.                 if (res && !res.success) {  
  146.                     Ext.Msg.alert('出错信息', res.msg);  
  147.                 } else {  
  148.                     globalObject.msgTip('保存成功!');  
  149.                 }  
  150.             },  
  151.             failure : function(response, opts) {  
  152.                 me.setLoading(false);  
  153.                 Ext.Msg.alert('出错信息''操作失败!');  
  154.             }  
  155.         });  
  156.     }  
  157. });  

源码有50多M(包括Jar包和SQL文件)

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值