写完第一篇Hello World之后,我就一直在准备着弄弄MVC,整了2天终于弄好了,遇到问题,也只能靠自己.
其实就是用ExtJS5 搭个后台界面:
啥都不说了,上代码。
首先需要一个app.js来定义一个ExtJS的Application,Application里去启动运行ExtJS的程序
App.js
Ext.QuickTips.init(); Ext.Loader.setConfig({ enabled : true }); Ext.Loader.setPath({ 'Ext.ux' : 'extjs/ux', //'Ext.app' : 'extjs/app', 'Portal.view' : 'extjs/app', 'OA.util' : 'app/util' }); /** * 加载ExtJS插件文件 */ Ext.require( [ 'Portal.view.Portlet', 'Portal.view.PortalColumn', 'Portal.view.PortalPanel', 'Portal.view.PortalDropZone', 'Ext.ux.TabReorderer', 'Ext.ux.BoxReorderer', 'Ext.ux.TabCloseMenu', 'Ext.ux.PageSizePaging', 'OA.util.AppUtil' ] ); Ext.application({ requires: ['Ext.container.Viewport','OA.view.Viewport'], //项目名称简称 name: 'OA', appFolder: 'app', autoCreateViewport: true, //控制器注册 controllers:[ 'AppController', 'user.UserController' ], launch: function () { //Ext.create('OA.view.Viewport'); } });
app.js里有个autoCreateViewport属性,即自动创建Viewport容器,我们知道ExtJS的所有组件都是需要放进这个总容器里的,当然viewport不是必须的哈。在ExtJS4.x的时候,是需要显示的在launch里去create我们的viewport容器,autoCreateViewport可以接收true/false,true的话就会自动到app/view下查找Viewport.js文件,自动帮我们加载并帮我们创建viewport容器,这是5.x后的新特性。当然你手动create还是可行的。此外,autoCreateViewport也可以接收一个Viewport类的全路径,即指定自动加载哪个viewport.举个例子:autoCreateViewport: "OA.view,Viewport",这样,如果你的viewport不在默认app/view路径下时,就可以手动指定要加载的viewport的路径。
Viewport.js
/*********************全局视图容器类************************/ Ext.define("OA.view.Viewport",{ extend:'Ext.container.Viewport', requires:['Ext.container.Viewport','OA.view.MainPanel'], alias : 'widget.baseviewport', alternateClassName: ["OA.Viewport"], layout: 'fit', loadMask:{msg : '正在加载,请稍候...'}, items: [ { xtype: 'mainpanel' } ] });
Viewport里就放了一个mainpanel子组件,采用fit布局是为了让他能充满整个屏幕。
MainPanel.js
Ext.define("OA.view.MainPanel",{ extend:'Ext.panel.Panel', alias : 'widget.mainpanel', alternateClassName: ["OA.MainPanel"], requires: ['OA.view.TopPanel','OA.view.BottomPanel','OA.view.LeftPanel','OA.view.CenterPanel'], layout: 'border', border: 0, initComponent: function () { var me = this; me.items = [ { xtype: 'toppanel' }, { xtype: 'bottompanel' }, { xtype: 'leftpanel' }, { xtype: 'centerpanel' } ]; me.callParent(arguments); } });
MainPanel里采用border布局,分别放了4个panel,上下左右,ExtJS的经典布局,你懂的。
上下左右4个panel的代码我直接贴了,代码太多就不一一解释了。
TopPanel.js
/**********************顶部Panel***********************/ Ext.define("OA.view.TopPanel",{ extend:'Ext.panel.Panel', requires:["OA.view.ThemeMenu"], alias : 'widget.toppanel', alternateClassName: ["OA.TopPanel"], height: 50, split: false, border: 0, collapsible: false, titleCollapse: false, region: 'north', initComponent : function() { var me = this; me.items = [ { title: 'OA管理系统', xtype: 'panel', margins:'0 0 5 0', tbar:[ '->', '益达,欢迎您!', new Date().format("yyyy年MM月dd日 hh:mm:ss w"), { text:'修改密码' }, { text:'退出' }, { text: '换肤', //iconCls: 'theme', menu: {xtype: 'thememenu'} } ] } ]; me.callParent(arguments); } });
BottomPanel.js
/**********************底部Panel***********************/ Ext.define("OA.view.BottomPanel",{ extend:'Ext.panel.Panel', alias : 'widget.bottompanel', alternateClassName: ["OA.BottomPanel"], height: 20, border: 0, split: false, collapsible: false, titleCollapse: false, draggable: false, region: 'south', initComponent: function () { var me = this; Ext.apply(me, { tbar: ['->','OA by yida'] }); me.callParent(arguments); } });
LeftPanel.js
/*********************左侧Panel************************/ Ext.define("OA.view.LeftPanel",{ extend:'Ext.panel.Panel', requires:["OA.view.LeftTreePanel"], alias : 'widget.leftpanel', alternateClassName: ["OA.LeftPanel"], initComponent : function(){ Ext.apply(this,{ id : "leftPanel", title: '导航菜单', width: 200, height: "100%", border: true, collapsed: false, collapsible: true, animCollapse: true, collapseMode: 'mini', autoScroll: false, containerScroll: true, split: true, resizable: false, region: 'west', layout: 'accordion', layoutConfig : { titleCollapse: false, animate : true, //动态切换树空间 activeOnTop: true, multi:false } }); this.callParent(arguments); } });
CenterPanel.js
/*******************中间Panel**************************/ Ext.define("OA.view.CenterPanel",{ extend:'Ext.tab.Panel', requires:['Ext.tab.Panel'], alias : 'widget.centerpanel', alternateClassName: ["OA.CenterPanel"], id : "centerPanel", width: '100%', border: 0, enableTabScroll: true, autoScroll: true, activeItem: 0, region: 'center', layout: 'fit', initComponent : function() { var me = this; me.callParent(arguments); }, items: [ { //iconCls : 'icon-activity', title : '平台首页', xtype:'portalpanel', layout:'hbox', items : [ { xtype : 'portalcolumn', columnWidth : 0.7, items:[ {title: '新闻动态',height : 180}, {title: '最新通知',height : 180}, {title: '业绩报表',height : 180} ] }, { xtype : 'portalcolumn', columnWidth : 0.3, items:[ {title: '常用功能', height : 180}, {title: '待办事项',height : 180}, {title: '邮件列表', height : 180} ] } ] } ], listeners: { resize :function(p, width, height,oldWidth,oldHeight, eOpts) { p.updateLayout(); } } });
最后贴一下AppUtil.js工具类
/** * JavaScript-Date日期类型格式化<br/> * 使用示例: * var date = new Date(); * alert(date.format("yyyy-MM-dd hh:mm:ss")); * @param {} format * @return {} */ Date.prototype.format = function(format){ var _week = ['星期日','星期一','星期二','星期三','星期四','星期五','星期六']; var o = { "M+" : this.getMonth()+1, //month 月 "d+" : this.getDate(), //day 日 "h+" : this.getHours(), //hour 时 "m+" : this.getMinutes(), //minute 分 "s+" : this.getSeconds(), //second 秒 "q+" : Math.floor((this.getMonth()+3)/3), //quarter季度 "S" : this.getMilliseconds(), //millisecond毫秒 "w" : _week[this.getDay()+""] //星期几 } if(/(y+)/.test(format)) { format = format.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length)); } for(var k in o) { if(new RegExp("("+ k +")").test(format)) { format = format.replace(RegExp.$1, RegExp.$1.length==1 ? o[k] : ("00"+ o[k]).substr((""+ o[k]).length)); } } return format; } /** * 在原日期基础上添加或减去指定天(年,月,日,小时,分钟,秒)数<br/> * interval参数: * y 年 * q 季度 * m 月 * d 日 * w 周 * h 小时 * n 分钟 * s 秒 * ms 毫秒 * * number参数:时间间隔,必须为数字,为正数表示获取指定间隔的未来的日期,为负数表示过去的日期 * @param {} interval * @param {} number * @return {} */ Date.prototype.dateAdd = function(interval,number) { var d = this; var k={'y':'FullYear', 'q':'Month', 'm':'Month', 'w':'Date', 'd':'Date', 'h':'Hours', 'n':'Minutes', 's':'Seconds', 'ms':'MilliSeconds'}; var n={'q':3, 'w':7}; eval('d.set'+k[interval]+'(d.get'+k[interval]+'()+'+((n[interval]||1)*number)+')'); return d; } /** * 用于计算两个日期之间的时间间隔<br/> * 使用此方法还能比较两个日期的大小,如果返回值大于0,表示objDate2比较大, * 如果小于0,表示objDate2比较小 * @param {} interval * @param {} objDate2 * @return 返回相差的毫秒数 */ Date.prototype.dateDiff = function(interval,objDate2) { var d=this, i={}, t=d.getTime(), t2=objDate2.getTime(); i['y']=objDate2.getFullYear()-d.getFullYear(); i['q']=i['y']*4+Math.floor(objDate2.getMonth()/4)-Math.floor(d.getMonth()/4); i['m']=i['y']*12+objDate2.getMonth()-d.getMonth(); i['ms']=objDate2.getTime()-d.getTime(); i['w']=Math.floor((t2+345600000)/(604800000))-Math.floor((t+345600000)/(604800000)); i['d']=Math.floor(t2/86400000)-Math.floor(t/86400000); i['h']=Math.floor(t2/3600000)-Math.floor(t/3600000); i['n']=Math.floor(t2/60000)-Math.floor(t/60000); i['s']=Math.floor(t2/1000)-Math.floor(t/1000); return i[interval]; } /** * 获取数组中某元素的索引【索引从零开始计算】 * @param {} o * @return {} */ Array.prototype.indexOf = function(o){ for(var i = 0,len=this.length; i<len;i++){ if(this[i] == o){ return i; } } return -1; } /** * 删除数组中某元素 * @param {} o * @return {} */ Array.prototype.remove = function(o){ var index = this.indexOf(o); if(index != -1){ this.splice(index,1); } return this; } /** * 去除数组中重复数据 * @param {} o * @return {} */ function removeDuplicateArray(o){ var obj={},ret=[],i=0; for(var a in o){ obj[o[a]]=o[a]; } for(ret[i++] in obj); return ret; } /** * 去除字符串中重复字符 * @param {} str * @return {} */ function removeDuplicateString(str){ var obj={},ret=""; for(var i=0;i<str.length;i++){ var s=str.slice(i,i+1); obj[s]=s; } for(s in obj){ ret += obj[s]; }; return ret; } /** * 去除数组或字符串中的重复字符 * @param {} o * @return {} */ function removeDuplicate(o){ return typeof(o)=="object"?removeDuplicateArray(o):removeDuplicateString(o); } /** * 删除字符串中重复字符 * @param {} o * @return {} */ function removeRepeat(o) { return o.replace(/([\s\S]{1})(?:\1+)/g,'$1'); } /** * 系统工具类[静态常量和静态方法] */ Ext.define("OA.util.AppUtil",{ alternateClassName: ["OA.AppUtil"], requires: ["OA.util.JSLoader"], statics: { basePath : window.location.protocol + '//' + window.location.host + '/' + window.location.pathname.split('/')[1] + "/", jsBasePath : window.location.protocol + '//' + window.location.host + '/' + window.location.pathname.split('/')[1] + "/js/", imageBasePath : window.location.protocol + '//' + window.location.host + '/' + window.location.pathname.split('/')[1] + "/images/", cssBasePath : window.location.protocol + '//' + window.location.host + '/' + window.location.pathname.split('/')[1] + "/css/", extjsBasePath : window.location.protocol + '//' + window.location.host + '/' + window.location.pathname.split('/')[1] + "/extjs/", extjsThemePath : window.location.protocol + '//' + window.location.host + '/' + window.location.pathname.split('/')[1] + "/extjs/theme/", loginPage : window.location.protocol + '//' + window.location.host + '/' + window.location.pathname.split('/')[1] + "/login.jsp", indexPage : window.location.protocol + '//' + window.location.host + '/' + window.location.pathname.split('/')[1] + "/index.jsp", loginUrl : window.location.protocol + '//' + window.location.host + '/' + window.location.pathname.split('/')[1] + "/login.do", logOutUrl : window.location.protocol + '//' + window.location.host + '/' + window.location.pathname.split('/')[1] + "/logout.do", firstTreePanelId: 1, /** * ExtJS ajax封装<br/> * 调用示例: * ajax({ * url : "??.do", * params : { * id : "参数值", * name: "参数值" * }, * callback : function(json){} * }); * @param {} config */ ajax : function(config) { Ext.Ajax.request( { url : config.url, params : config.params, method : config.method || 'post', callback : function(options, success, response) { config.callback(Ext.JSON.decode(response.responseText)); } }); }, /** * 返回星期几的中文形式 * @param {} date * @return {} */ getDayOfWeek : function(date) { var week = [ "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六" ]; return week[day.getDay()]; }, /** * ExtJS组件的命令空间路径转换成组件的实际访问路径<br/> * 如:OA.view.user.UserPanel转换后basePath + app/view/user/UserPanel.js * * @param {} np * @return {} */ namespace2Path : function(np) { if(!np) { return null; } np = np + ""; return OA.util.AppUtil.basePath + np.replace(/\./g,"/").replace("OA","app") + ".js"; }, /** * 用户退出系统 */ logout : function() { Ext.Msg.confirm('提示', "您确认要退出系统吗?", function(btn) { if (btn == "yes") { OA.util.AppUtil.ajax({ url : OA.util.AppUtil.logOutUrl, params : {}, callback : function(json){ if(json && json.success) { window.location = OA.util.AppUtil.loginPage; } } }); } }); }, /** * 显示当前时间的时钟信息 * @param {} domId 时钟信息要显示在哪个dom元素上 * @param {} pattern 日期时间显示格式,如yyyy-MM-dd hh:mm:ss */ showColock : function(domId,pattern) { var date = new Date(); var colockEl = Ext.get(domId); if(!colockEl) { return; } colockEl.dom.innerHTML = date.format(pattern || "yyyy年MM月dd日 hh:mm:ss w"); }, /** * 动态添加Tab页 * @param {} node */ openPanel: function(node,centerPanelId) { var max = 5; var id = node.data.component || "_tab0"; var centerPanel = Ext.getCmp(centerPanelId); var grid = centerPanel.getComponent(node.data.id); if (!grid) { if (this.items && this.items.length >= max) { Ext.Msg.alert("错误", "<span style='font-size:14px;font-weight:600;color:red;'>最多能打开5个模块,请先关闭部分不再使用的模块!</span>"); } else { var tab = {}; centerPanel.getEl().mask("正在加载面板,请稍候..."); //var cla = Ext.decode("{cla:" + id + "}",true).cla; if(node.data.type == "url") { tab = Ext.create("Ext.panel.Panel",{ itemId: node.data.id, xtype: 'panel', title: node.data.text, layout: 'fit', closable: true, autoScroll: true, border: true, autoDestroy : true, closeAction : 'destory', html: '<iframe width="100%" height="100%" frameborder="0" src="' + id + '"></iframe>' //iconCls: 'modelIcon' }); } else if(node.data.type == "component") { tab = Ext.create("Ext.panel.Panel",{ itemId: node.data.id, xtype: 'panel', title: node.data.text, layout: 'fit', closable: true, autoScroll: true, border: true, autoDestroy : true, closeAction : 'destory', items:[ {xtype: id} ] }); } centerPanel.add(tab); centerPanel.setActiveTab(tab); centerPanel.doLayout(); centerPanel.getEl().unmask(); } } else { centerPanel.setActiveTab(tab); centerPanel.getEl().unmask(); } }, /** * 动态添加TreePanel */ addTreePanel : function(data,leftPanel) { Ext.getBody().unmask(); for (var i = 0; i < data.length; i++) { /*leftPanel.add(Ext.create("OA.view.LeftTreePanel",{ id: data[i].id, title: data[i].text, iconCls: data[i].iconCls }));*/ leftPanel.add({ xtype: "lefttreepanel", itemId: data[i].id, title: data[i].text, iconCls: data[i].iconCls }); } leftPanel.doLayout(); }, /** * 添加手风琴面板 * @param {} leftPanelId */ addAccordionPanel : function(data,leftPanelId) { var leftPanel = Ext.getCmp(leftPanelId); Ext.getBody().unmask(); for (var i = 0; i < data.length; i++) { if(i == 0) { leftPanel.add({ xtype: "panel", itemId: data[i].id, title: data[i].text, iconCls: data[i].iconCls, layout: 'fit', items: [ { xtype: "lefttreepanel", root: { id: data[i].id, expanded: true } } ] }); } else { leftPanel.add({ xtype: "panel", itemId: data[i].id, title: data[i].text, iconCls: data[i].iconCls, layout: 'fit' }); } } leftPanel.updateLayout(); }, /** * 更换皮肤 */ changeTheme: function(itemId, checked) { if (checked) { var css = OA.util.AppUtil.extjsThemePath; cssname = itemId.replace("Skin-", "").toLowerCase(); css += "ext-theme-" + cssname + "/ext-theme-" + (cssname == "default" ? "classic" : cssname) + "-all.css"; Ext.util.CSS.swapStyleSheet(null, css); var exp = new Date(); //Cookie保存30天 exp.setTime(exp.getTime() + 30 * 24 * 60 * 60 * 1000); Ext.util.Cookies.set("ThemeCSS", css, exp); Ext.util.Cookies.set("ThemeName", itemId, exp); } } } });
index.jsp是测试页面
demo里也附带了换肤功能实现
需要源代码的,请加Q-Q群 105098806