Spring3+Hibernate4+SpringMVC整合Ext:开发Ext界面

jopen 11年前

前言

Spring3+Hibernate4+SpringMVC整合Ext:项目架构搭建中已经介绍了Spring3、Hibernate4和SpringMVC的整合,下面介绍的是开发一个典型的Ext后台管理界面:

Spring3+Hibernate4+SpringMVC整合Ext:开发Ext界面

        开发这个界面使用了Ext MVC,好多人都叫我用MVC方法去实现,其实MVC不是每个场景都适合。就我现在这个界面的场景而言并不适合用MVC开发,本来一个js文件就可以处理硬生生的分成了好几个文件,效果不是很好。代码是通过MVC实现的,MVC具体怎么样用大家可以去参考下官方的例子,在这里就不在赘述。下面的代码通过mvc的方式进行分层展示:

前台界面


页面

<%@ page language="java" pageEncoding="UTF-8"%>  <%   String extLibPath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+"/ext4";   String ctx = request.getContextPath();   pageContext.setAttribute("extLibPath", extLibPath);   pageContext.setAttribute("ctx", ctx);  %>  <html>   <head>    <title>业务基础平台</title>       </head>   <body>    <div id="loading-tip" style="border-radius:3px;position: absolute;z-index: 1;border: solid 1px #ccc;background-color: #fff;padding: 10px;">     <div class="loading-indicator" style="color: #444;font: bold 13px tahoma, arial, helvetica;padding: 10px;height: auto;">      <img src="${ctx}/images/loading32.gif" width="31" height="31"       style="margin-right: 8px; float: left; vertical-align: top;" />      业务基础平台V1.0      <br />      <span id="loading-msg" style="font: normal 10px arial, tahoma, sans-serif;">加载样式和图片...</span>     </div>    </div>    <script type="text/javascript">     var extLibPath = "${extLibPath}";     var ctx = "${ctx}";     var tip = document.getElementById("loading-tip");     tip.style.top = (document.body.scrollHeight - tip.style.top - 100) / 2;     tip.style.left = (document.body.scrollWidth - 200) / 2;    </script>    <link rel="stylesheet" type="text/css" href="${extLibPath}/resources/css/ext-all.css" />    <link rel="stylesheet" type="text/css" href="icon.css" />    <script type="text/javascript">     document.getElementById("loading-msg").innerHTML = "加载核心组件...";    </script>    <script type="text/javascript" src="${extLibPath}/ext-all-debug.js"></script>    <script type="text/javascript" src="${extLibPath}/locale/ext-lang-zh_CN.js"></script>    <script type="text/javascript" src="app.js"></script>   </body>  </html>
这个是index.jsp页面,整个界面的代码就在app.js里面:
Ext.Loader.setConfig({     enabled : true    });    Ext.Loader.setPath({     'Ext.ux' : extLibPath + '/examples/ux',     'Ext.app' : extLibPath + '/examples/app',     'Fes' : 'module'    });    Ext.require(['Fes.MsgBox']);    Ext.Ajax.on('requestexception', function(conn, response, options) {     var msg = '访问系统资源时发生异常<br/>' + '异常状态:' + response.status + '('       + response.statusText + ')<br/>' + '异常信息:'       + response.responseText     Ext.Msg.show({        title : '系统异常',        msg : msg,        width : 400,        icon : Ext.MessageBox.ERROR,        buttonText : {         ok : '  提交错误报告  '        },        buttons : Ext.MessageBox.OKCANCEL       });    });  Ext.get('loading-msg').update('加载系统组件...');  Ext.create('Fes.desktop.Desktop');
在app.js的文件中主要是做了一些全局的配置,配置了ext的动态加载和ajax访问时异常的处理,最后创建“Fes.desktop.Desktop”组件开始初始化界面

控制层


控制层的作用是对界面的行为进行控制、逻辑判断和事件响应
module/desktop/Desktop.js
Ext.define('Fes.desktop.Desktop', {     extend : 'Ext.app.Application',     appFolder : 'module/desktop',       name : 'Desktop',       controllers : ['Desktop'],          enableQuickTips : true,             autoCreateViewport: true      });
Fes.desktop.Desktop继承Ext.app.Application,这个是Ext MVC中规定要继承的父类。配置了autoCreateViewport为true时Ext会自动创建一个Desktop.view.Viewport的组件,这个规则是appname+view .Viewport。

module/desktop/controller/Desktop.js
Ext.define('Desktop.controller.Desktop', {   extend : 'Ext.app.Controller',     models : ['Node'],     refs : [{      ref : 'navigation',      selector : 'navigation'     }, {      ref : 'container',      selector : 'fescontainer'     }],     init : function() {    var me = this;    this.control({       'viewport' : {        render : me.onRender       },       scope : me      });   },     onRender : function() {    var me = this;    Ext.get('loading-msg').update('正在加载菜单...');    Ext.Ajax.request({       url : 'resource/root',// 获取面板的地址       method : 'GET',       callback : function(options, success, response) {        me.createTree(Ext.JSON.decode(response.responseText));       }      });   },     createTree : function(datas) {    var me = this;    Ext.each(datas, function(data) {       var tree = Ext.create("Ext.tree.Panel", {          title : data.text,          iconCls : data.iconCls,          useArrows : true,          autoScroll : true,          rootVisible : false,          viewConfig : {           loadingText : "正在加载..."          },          store : me.createTreeStore(data.id)         });       tree.on('itemclick', me.onTreeItemClick, me);       me.getNavigation().add(tree);      });    Ext.get('loading-msg').update('加载完成.');    Ext.Function.defer(function() {       Ext.get('loading-tip').remove();      }, 1000);   },     onTreeItemClick : function(view, node) {    console.log(node.data.component);    var tab = this.getContainer();    if (node.isLeaf()) { // 判断是否是根节点     if (node.data.type === 'URL') { // 判断资源类型      var panel = Ext.create('Ext.panel.Panel', {       title : node.data.text,       closable : true,       iconCls : 'icon-activity',       html : '<iframe width="100%" height="100%" frameborder="0" src="http://www.baidu.com"></iframe>'      });      tab.add(panel);      tab.setActiveTab(panel);     } else if (node.data.type === 'COMPONENT') {      var panel = Ext.create(node.data.component, {         title : node.data.text,         closable : true,         iconCls : 'icon-activity'        });      tab.add(panel);      tab.setActiveTab(panel);     }    }   },     createTreeStore : function(id) {    var me = this;    return Ext.create("Ext.data.TreeStore", {       defaultRootId : id, // 默认的根节点id       model : this.getNodeModel().$className,       proxy : {        type : 'ajax', // 获取方式        url : 'resource/child' // 获取树节点的地址       },       clearOnLoad : true,       nodeParam : 'id'// 设置传递给后台的参数名,值是树节点的id属性      });   }  });
这个是整个界面的控制层,在init的方法中为viewport添加了一个render事件,在整个界面渲染完成后开始加载菜单。在菜单加载完成之后添加菜单的点击事件。

视图层


视图层只页面的展示,不做任务的逻辑操作和事件处理
module/desktop/view/Viewport.js
Ext.define('Desktop.view.Viewport', {     extend : 'Ext.container.Viewport',     requires : ['Desktop.view.Container', 'Desktop.view.Header',       'Desktop.view.Navigation'],       layout : 'border',     initComponent : function() {      var me = this;      Ext.apply(me, {         items : [Ext.create('Desktop.view.Container'),           Ext.create('Desktop.view.Header'),           Ext.create('Desktop.view.Navigation')]        });      this.callParent(arguments);     }    });
定义Desktop.view.Viewport组件,这个组件会在控制器中被创建。viewport中定义了整个界面的布局,包含了Desktop.view.Container,Desktop.view.HeaderDesktop.view.
</div>
Navigation3个组件。

module/desktop/view/Container.js
Ext.define('Desktop.view.Container', {        alias: 'widget.fescontainer',     extend : 'Ext.tab.Panel',     requires : ['Ext.app.Portlet', 'Ext.app.PortalColumn', 'Ext.app.PortalPanel',          'Ext.app.PortalDropZone', 'Ext.ux.TabReorderer','Ext.ux.TabCloseMenu'],     activeTab : 0,     enableTabScroll : true,     animScroll : true,     border : true,     autoScroll : true,     region : 'center',     split : true,     plugins : [      Ext.create('Ext.ux.TabReorderer'),      Ext.create('Ext.ux.TabCloseMenu',{                 closeTabText: '关闭面板',                 closeOthersTabsText: '关闭其他',                 closeAllTabsText: '关闭所有'                })     ],     items : [{        iconCls : 'icon-activity',        title : '平台首页',        xtype : 'portalpanel',        layout : 'column',        items : [{           xtype : 'portalcolumn',           columnWidth : 0.7,           items : [{              title : '新闻动态',              height : 150,              iconCls : 'icon-news'             }, {              title : '最新通知',              height : 150,              iconCls : 'icon-notice'             }, {              title : '业绩报表',              height : 150,              iconCls : 'icon-chart'             }, {              title : '邮件列表',              height : 150,              iconCls : 'icon-email-list'             }]          }, {           xtype : 'portalcolumn',           columnWidth : 0.3,           items : [{              title : '功能链接',              height : 150,              iconCls : 'icon-link'             }, {              title : '待办事项',              height : 150,              iconCls : 'icon-note'             }, {              title : '邮件列表',              height : 150,              iconCls : 'icon-email-list'             }, {              title : '邮件列表',              height : 150,              iconCls : 'icon-email-list'             }]          }]       }]    });
Desktop.view.Container是一个标签页集合的面板,是整个界面的操作区域。

module/desktop/view/Header.js
Ext.define('Desktop.view.Header', {   extend : 'Ext.panel.Panel',   height : 80,   html : '业务基础平台',   region : 'north',   split : true,   bbar : [{      iconCls : 'icon-user',      text : '管理员'     }, '-', {      text : Ext.Date.format(new Date(), 'Y年m月d日')     }, '->', {      text : '退出',      iconCls : 'icon-logout'     }],   bodyStyle : 'backgroud-color:#99bbe8;line-height : 50px;padding-left:20px;' +     'font-size:22px;color:#000000;font-family:黑体;font-weight:bolder;'+      'background: -webkit-gradient(linear, left top, left bottom, ' +     'color-stop(0%, rgba(153,187, 232, 0.4) ),' +     'color-stop(50%, rgba(153, 187, 232, 0.8) ),' +     'color-stop(0%, rgba(153, 187, 232, 0.4) ) );' ,   initComponent : function(){    this.callParent();   }  });
Desktop.view.Header是界面的头部

module/desktop/view/Navigation.js
Ext.define('Desktop.view.Navigation', {           alias: 'widget.navigation',     extend : 'Ext.panel.Panel',     region : 'west',     title : '系统菜单',     width : 250,     iconCls : "icon-tree",     autoScroll : false,     layout : 'accordion',     collapsible : true,     layoutConfig : {      animate : true     },     id : 'navigation',     split : true,     initComponent : function(){      this.callParent();     }    });
Desktop.view.Navigation是界面的导航部分,树形菜单的展示区域

模型层


模型层主要是定义了前后台数据交互的模型
module/desktop/model/Node.js
Ext.define('Desktop.model.Node', {       extend: 'Ext.data.Model',   fields : [{name : "id",type : "string"},     {name : "text",type : "string"},     {name : "iconCls",type : "string"},     {name : "leaf",type : "boolean"},     {name : 'type'},     {name : 'component'}]  });
Desktop.model.Node定义了菜单数据模型

后台处理


整个界面的数据交互个入口在module/desktop/controller/Desktop.js文件中的对定义了viewport进行的事件响应:
init : function() {    var me = this;    this.control({       'viewport' : {        render : me.onRender       },       scope : me      });   },     onRender : function() {    var me = this;    Ext.get('loading-msg').update('正在加载菜单...');    Ext.Ajax.request({       url : 'resource/root',// 获取面板的地址       method : 'GET',       callback : function(options, success, response) {        me.createTree(Ext.JSON.decode(response.responseText));       }      });   },     createTree : function(datas) {    var me = this;    Ext.each(datas, function(data) {       var tree = Ext.create("Ext.tree.Panel", {          title : data.text,          iconCls : data.iconCls,          useArrows : true,          autoScroll : true,          rootVisible : false,          viewConfig : {           loadingText : "正在加载..."          },          store : me.createTreeStore(data.id)         });       tree.on('itemclick', me.onTreeItemClick, me);       me.getNavigation().add(tree);      });    Ext.get('loading-msg').update('加载完成.');    Ext.Function.defer(function() {       Ext.get('loading-tip').remove();      }, 1000);   },
在viewport渲染完成后发送到resource/root请求获取菜单的根目录,后台java代码:
ResourceController.java
package com.avicit.fes.system.resource.controller;    import java.util.List;    import org.springframework.beans.factory.annotation.Autowired;  import org.springframework.stereotype.Controller;  import org.springframework.web.bind.annotation.RequestMapping;  import org.springframework.web.bind.annotation.RequestMethod;  import org.springframework.web.bind.annotation.RequestParam;  import org.springframework.web.bind.annotation.ResponseBody;    import com.avicit.fes.system.resource.service.ResourceService;  import com.avicit.fes.system.resource.vo.ResourceNode;    @Controller  @RequestMapping("/resource")  public class ResourceController {     @Autowired   private ResourceService resourceService;      @RequestMapping(value="root",method=RequestMethod.GET)   public @ResponseBody List<ResourceNode> root() throws Exception{    return this.resourceService.getRoot();   }      @RequestMapping(value="child",method=RequestMethod.GET)   public @ResponseBody List<ResourceNode> child(@RequestParam("id") Integer id) throws Exception{    return this.resourceService.getChildren(id);   }  }
在ResourceController类中定义了root方法拦截resource/root请求,在resourceService中获取菜单的根目录发送到页面中,下面看ResourceServiceImpl中的代码:
package com.avicit.fes.system.resource.service.impl;    import java.util.List;    import org.apache.commons.logging.Log;  import org.apache.commons.logging.LogFactory;  import org.springframework.beans.factory.annotation.Autowired;  import org.springframework.stereotype.Service;    import com.avicit.fes.system.resource.dao.ResourceDAO;  import com.avicit.fes.system.resource.entity.Resource;  import com.avicit.fes.system.resource.service.ResourceService;  import com.avicit.fes.system.resource.vo.ResourceNode;  import com.avicit.framework.util.ListUtils;    @Service("resourceService")  public class ResourceServiceImpl implements ResourceService {     protected Log logger = LogFactory.getLog(this.getClass());     @Autowired   protected ResourceDAO<Resource, Integer> resourceDAO;     @Override   public List<ResourceNode> getRoot() throws Exception {    return ListUtils.transform(this.resourceDAO.getRootResource(),ResourceNode.class);   }      public List<ResourceNode> getChildren(Integer id) throws Exception{    return ListUtils.transform(this.resourceDAO.getChildrenByParent(id),ResourceNode.class);   }    }
在ResourceServiceImpl类中getRoot方法中从后台数据库中获取到跟目录的数据返回给ResourceController,这样就完成了数据的交互。
在获取根目录之后创建树组件,并通过父节点获取子节点,从而完成了界面菜单的交互。

来自:http://blog.csdn.net/leecho571/article/details/8043264