运行效果:
此文介绍了根据操作左侧菜单在右面板展示相应内容。
一、主页
先看一下跳转主页的方式:由在webapp根目录下的index.jsp跳转至demo的index.jsp
下面是demo的index.jsp的代码
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%response.setHeader("Pragma","No-cache");response.setHeader("Cache-Control","no-cache");response.setHeader("Expires","0");request.setCharacterEncoding("UTF-8"); //String webRoot = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/"; %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>首页</title> <link href="../ExtJS4.2/resources/css/ext-all-neptune-rtl.css" rel="stylesheet"> <link href="../ExtJS4.2/css/icon.css" rel="stylesheet"> <script src="../ExtJS4.2/ext-all.js"></script> <script src="../ExtJS4.2/locale/ext-lang-zh_CN.js"></script> <script type="text/javascript" src="js/localXHR.js"></script> <script type="text/javascript" src="js/index.js"></script> </head> <body><div id="north-div" style="height: 73px; background-color: #101010;"><img alt="思考者日记网|束洋洋个人博客" src="image/shuyangyang_01.jpg"></div><div id="south-div" align="center">Copyright by 束洋洋© www.shuyangyang.com.cn</div><div id="div2"></div> </body> </html>
看样看到此页面在引用文件的地方会加载2个自定义的js,在body处定义了页面所包含的两个dom元素。
先贴出笔者看不懂的localXHR.js
if (document.location.protocol == 'file:') {Ext.override(Ext.data.Connection, {parseStatus: function() {return {success: true,isException: false};}});}
然后是index.js
Ext.Loader.setConfig({enabled : true });Ext.Loader.setPath({'Ext.ux' : '../ExtJS4.2/ux'//设置命名空间的路径 });Ext.require(['Ext.ux.TabCloseMenu']);//动态加载 Ext.onReady(function() {var rightPanel = Ext.create('Ext.tab.Panel', {activeTab : 0,border : false,autoScroll : true,//iconCls:'Applicationviewlist',region : 'center',items : [{title : '首页',iconCls: 'House',html: '<iframe frameborder="no" border=0 height="100%" width="100%" src="blank.jsp" scrolling="auto"></iframe>',}],plugins: [Ext.create('Ext.ux.TabCloseMenu',{//选项卡关闭插件closeTabText: '关闭当前',//The text for closing the current tab.closeOthersTabsText: '关闭其他',//The text for closing all tabs except the current one.closeAllTabsText: '关闭所有'//The text for closing all tabs. })]});var tree = Ext.create("Ext.panel.Panel", {height : '70%',//设置高度region : 'north',layout : {// layout-specific configs go heretype : 'accordion',titleCollapse : true,//允许通过点击标题栏的任意位置来展开/收缩子项Panelanimate : true,// 动画切换效果activeOnTop : false// 折叠展开是否改变菜单顺序 },layoutConfig : {animate : true},split : true});// 左边下部Panelvar detailsPanel = {id : 'details-panel',iconCls : "User",collapsible : true, // 是否折叠title : '信息',region : 'center',bodyStyle : 'padding-bottom:15px;background:#eee;',autoScroll : true,html : '<p class="details-info">用户名:束洋洋<br />部 门:管理部<br />登录IP:156.15.26.101</p>'};// 左边下部Panel结束var model = Ext.define("TreeModel", { // 定义树节点数据模型extend : "Ext.data.Model",fields : [{name : "id",type : "string"},{name : "text",type : "string"},{name : "iconCls",type : "string"},{name : "leaf",type : "boolean"},{name : 'url',type:"string"},{name : 'description',type:"string"}]});//组建树,返回Ext.tree.Panelvar buildTree = function(json) {return Ext.create('Ext.tree.Panel', {rootVisible : false,//false,隐藏根节点。border : false,//隐藏此树面板的边框lines:false,//false,树用的线置为disable。autoScroll:true,//自动显示滚动条useArrows:true,store : Ext.create('Ext.data.TreeStore', {//(必须的) Store树的数据源. 一个TreePanel必须绑定一个Ext.data.TreeStore。model : model,//定义当前store对象的Model数据模型root : {//当前store的根节点。expanded : true,//展开根节点。(因为根节点被设置为隐藏,所以在此可以不用设置此属性)children : json.children//子节点数组。 }}),listeners : {//配置监听'itemclick' : function(view, record, item, index, e) {//选项的单击事件。单击选项时触发。--Ext.view.View-event-itemclickvar leaf = record.get('leaf');//获取当前选项的leaf——是否为叶子节点//var typeId = record.get("TYPE_ID");var text = record.get('text');//获取当前选项的文本var icon = record.get('iconCls');//获取当前选项的图标var url = record.get('url');//获取当前选项的地址if (leaf) { //判断点击的是否是叶子节点,如果是,创建一个Panelvar panel = Ext.create('Ext.panel.Panel',{title : text,//标题closable : true,//该属性配置为true,这个窗体的'close'工具按钮便会展现出来并且允许用户通过点击这个按钮来关闭这个窗体,false则相反。iconCls : icon,//左侧图标html : '<iframe width="100%" height="100%" frameborder="0" src='+url+'></iframe>'//一个 HTML片段——这个Panel的内容 });rightPanel.add(panel);//右面板(Ext.tab.Panel)添加此组件rightPanel.setActiveTab(panel);//使此组件处于活动状态 }/*var id = record.get('id');var text = record.get('text');var leaf = record.get('leaf');if (leaf) {alert('id-' + id + ',text-' + text+ ',leaf-' + leaf);}*/},scope : this//The scope (this reference) in which the handler is executed. Defaults to the browser window. }});};// 整体架构容器Ext.create("Ext.container.Viewport", {layout : 'border',items : [ {region : 'north',//北方contentEl : 'north-div',//指定一个已存在的HTML元素, 或者一个已存在HTML元素的 id , 它们将被用作当前组件的内容.height : 73,bodyStyle : 'background-color:#BBCCEE;'}, {region : 'south',//南方contentEl : 'south-div',height : 20,bodyStyle : 'background-color:#BBCCEE;'}, {layout : 'border',title : '菜单',id : 'layout-browser',region : 'west',//西方border : false,split : true,margins : '2 0 5 5',width : 200,//minSize : 10,//无效设置//maxSize : 50,//无效设置autoScroll : false,collapsible : true, // 是否折叠//iconCls : "Application",items : [ tree, detailsPanel ]//包含树面板、个人简介面板。注:子组件的相对位置在子组件里设置,如在tree中设置了height属性,就指定了tree的相对高度。 },rightPanel],listeners : {//监听。这里用来添加树面板里的菜单afterrender : function() {//在组件渲染完成之后触发.Ext.AbstractComponentExt.getBody().mask('正在加载系统菜单....');//显示遮罩层Ext.Ajax.request({//向服务端发送ajax请求url:'../../menu/showMenu',//请求地址success : function(response) {//成功响应的回调函数。response:包含了响应数据的XMLHttpRequest对象.Ext.getBody().unmask();//隐藏先前应用的遮罩层.var json = Ext.JSON.decode(response.responseText);//获取返回的文本并解析为json格式。Ext.JSON.decode(..):解码(解析)JSON字符串对象。if(json.code == 'OK'){Ext.each(json.data, function(el) {//迭代一个数组或是可迭代的值,在每个元素上调用给定的回调函数 var panel = Ext.create('Ext.panel.Panel', {//创建一个Panelid : el.id,//定义idtitle : el.text,//定义标题iconCls:el.iconCls,//定义图标leaf :el.leaf,//定义leaf。Ext.data.NodeInterface-cfg-leaf:设置为true表明本节点没有子节点。 不会为本节点渲染展开图标或箭头。layout : 'fit'//定义布局。fit:当容器只包含一个子元素时, 子元素自动填满容器 });panel.add(buildTree(el));//当前Panel添加由此节点的子节点所创建的树面板tree.add(panel);//tree(1个Panel)添加当前Panel });}else{}},failure : function(request) {Ext.MessageBox.show({title : '操作提示',msg : "连接服务器失败",buttons : Ext.MessageBox.OK,icon : Ext.MessageBox.ERROR});},method : 'post'//请求的方式 });}}}); });
分析:
1.插件。当前页面使用了选项卡关闭插件(Ext.ux.TabCloseMenu),效果如下:
因此需要加载Ext.ux.TabCloseMenu类,如下:
Ext.Loader.setConfig({enabled : true });Ext.Loader.setPath({'Ext.ux' : '../ExtJS4.2/ux'//设置命名空间的路径 });Ext.require(['Ext.ux.TabCloseMenu']);//动态加载
这里有个小疑惑:采用Ext.require的方式加载类,还是“动态加载”吗?因为从表面上看在加载index.js时就会加载Ext.ux.TabCloseMenu类。不管怎样,这里把它加载就好。
2.页面布局
2.1 整体布局:整体采用Ext.container.Viewport的布局方式,其内部子组件采用border的布局方式,如下片段代码:
// 整体架构容器Ext.create("Ext.container.Viewport", {layout : 'border',items : [ {region : 'north',//北方contentEl : 'north-div',//指定一个已存在的HTML元素, 或者一个已存在HTML元素的 id , 它们将被用作当前组件的内容.height : 73,bodyStyle : 'background-color:#BBCCEE;'}, {region : 'south',//南方contentEl : 'south-div',height : 20,bodyStyle : 'background-color:#BBCCEE;'}, {layout : 'border',title : '菜单',id : 'layout-browser',region : 'west',//西方border : false,split : true,margins : '2 0 5 5',width : 200,//minSize : 10,//无效设置//maxSize : 50,//无效设置autoScroll : false,collapsible : true, // 是否折叠//iconCls : "Application",items : [ tree, detailsPanel ]//包含树面板、个人简介面板。注:子组件的相对位置在子组件里设置,如在tree中设置了height属性,就指定了tree的相对高度。 },rightPanel],
当Ext.container.Viewport采用border的布局方式,那么子组件的方位(东、南、西、北/中心)由子组件的region属性指定。上面的rightPanel组件在上面虽看不出,但在其内部定义了region : 'center'——在中心显示。
2.2 树面板(TreePanel)布局
此文中的树面板布局相对其他组件的布局相对复杂,为了方便说明,下面将树面板中accordion的布局方式去掉:
var tree = Ext.create("Ext.panel.Panel", {height : '70%',//设置高度region : 'north',/*layout : {// layout-specific configs go heretype : 'accordion',titleCollapse : true,//允许通过点击标题栏的任意位置来展开/收缩子项Panelanimate : true,// 动画切换效果activeOnTop : false// 折叠展开是否改变菜单顺序},*/layoutConfig : {animate : true},split : true});
去掉以后的显示效果:
上图说明了树面板的结构。在创建树面板的过程中,首先根据菜单的一级菜单创建一个Panel,然后再添加由此一级菜单的子菜单所创建的TreePanel,最后由tree(一个Panel)再把这个Panel添加进去。相应的代码如下:
Ext.each(json.data, function(el) {var panel = Ext.create('Ext.panel.Panel', {//创建一个Panelid : el.id,title : el.text,iconCls:el.iconCls,leaf :el.leaf,layout : 'fit' });panel.add(buildTree(el));//当前Panel添加TreePaneltree.add(panel);//tree添加当前Panel});
3.事件
在index.js涉及了2个事件:
一是在创建Ext.container.Viewport容器时所设置的‘afterrender’事件,这个事件用于向服务端发送请求以往tree面板中添加菜单;下面看一下代码 Ext.container.Viewport
listeners : {//监听。这里用来添加树面板里的菜单afterrender : function() {//在组件渲染完成之后触发。--Ext.container.ViewportExt.getBody().mask('正在加载系统菜单....');Ext.Ajax.request({url:'../../menu/showMenu',success : function(response) {//成功响应的回调函数。response:包含了响应数据的XMLHttpRequest对象.Ext.getBody().unmask();//隐藏先前应用的遮罩层.var json = Ext.JSON.decode(response.responseText);//获取返回的文本并解析为json格式。Ext.JSON.decode(..):解码(解析)JSON字符串对象。if(json.code == 'OK'){Ext.each(json.data, function(el) {//迭代一个数组或是可迭代的值,在每个元素上调用给定的回调函数var panel = Ext.create('Ext.panel.Panel', {id : el.id,title : el.text,iconCls:el.iconCls,leaf :el.leaf,layout : 'fit' });panel.add(buildTree(el));//当前Panel添加TreePaneltree.add(panel);//tree添加当前Panel });}else{}},failure : function(request) {Ext.MessageBox.show({title : '操作提示',msg : "连接服务器失败",buttons : Ext.MessageBox.OK,icon : Ext.MessageBox.ERROR});},method : 'post'//请求的方式 });}}
注:事件名可以加单引号'',也可以不加.
二是在创建树面板中中Ext.data.TreeStore的‘itemclick’事件——此事件是用来在点击左侧菜单时,向右边的面板(选项卡)添加一个选项卡面板,下面再看一下这里的代码
listeners : {//配置监听'itemclick' : function(view, record, item, index, e) {//选项的单击事件。单击选项时触发。--Ext.tree.Panelvar leaf = record.get('leaf');//var typeId = record.get("TYPE_ID");var text = record.get('text');var icon = record.get('iconCls');var url = record.get('url');//获取当前选项的地址if (leaf) { //判断点击的是否是叶子节点,如果是,创建一个Panelvar panel = Ext.create('Ext.panel.Panel',{title : text,//标题closable : true,//该属性配置为true,这个窗体的'close'工具按钮便会展现出来并且允许用户通过点击这个按钮来关闭这个窗体,false则相反。iconCls : icon,//左侧图标html : '<iframe width="100%" height="100%" frameborder="0" src='+url+'></iframe>'//一个 HTML片段——这个Panel的内容 });rightPanel.add(panel);//右面板(Ext.tab.Panel)添加此组件rightPanel.setActiveTab(panel);//使此组件处于活动状态 }},scope : this//The scope (this reference) in which the handler is executed. Defaults to the browser window.}
这个‘itemclick’事件来自于Ext.tree.Panel,从此事件可知,当点击左边的菜单时,首先会判定该菜单是否为叶子节点,如果是,则会创建一个Panel,这个Panel含有标题,图标,html等属性,然后右选项卡面板会将此面板添加进去并将其设置为可活动的。需注意的是,由于所添加的Panel里的html属性是一个iframe,所以在渲染时会把iframe里的src所在资源页面包含进来。
功能
一、用户管理
当点击菜单选项中的“用户管理”,那么在rightPanel(选项卡面板)会添加一个子选项卡,此子选项卡有一个html配置,它是一个iframe,其中的src为:/demo/UserMan/index.jsp——即此菜单的url值。这样“用户管理”这个子选项卡就会包含这个页面。下面看一下这个页面:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%response.setHeader("Pragma","No-cache");response.setHeader("Cache-Control","no-cache");response.setHeader("Expires","0");request.setCharacterEncoding("UTF-8"); //String webRoot = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/"; %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>用户管理</title> <link href="../../ExtJS4.2/resources/css/ext-all-neptune-rtl.css" rel="stylesheet"> <link href="../../ExtJS4.2/css/icon.css" rel="stylesheet"> <script src="../../ExtJS4.2/ext-all.js"></script> <script src="../../ExtJS4.2/locale/ext-lang-zh_CN.js"></script> <script type="text/javascript" src="../js/localXHR.js"></script> <script type="text/javascript" src="../js/userMan.js"></script> </head> <body> </body> </html>
可以看出这个页面除了引用文件说明什么也没有,这就对了,因为所有的内容在userMan.js中。下面看一下userMan.js
//引入扩展组件 Ext.Loader.setConfig({enabled: true});Ext.Loader.setPath('Ext.ux', '../../ExtJS4.2/ux/');Ext.require(['Ext.data.*','Ext.grid.*','Ext.util.*','Ext.tip.QuickTipManager','Ext.ux.data.PagingMemoryProxy','Ext.ux.ProgressBarPager' ]);Ext.onReady(function() {//创建列var columns = [{xtype: 'rownumberer'},//创建Ext.grid.RowNumberer的实例。Ext.grid.RowNumberer:它是一个为每行提供编号的列。{header:'编号',dataIndex:'id',hidden: true},{header:'用户名',dataIndex:'name'},{header:'年龄',dataIndex:'age'},//年龄{header:'性别',dataIndex:'sex',renderer:function(value){ if(value=='男'){ return "<span style='color:green;font-weight:bold';>男</span>"; } else { return "<span style='color:red;font-weight:bold';>女</span>"; }}},{header:'地址',dataIndex:'address'},{header:'身份证号码',dataIndex:'cardId',width:150},{header:'角色',dataIndex:'roleId'},{header:'部门',dataIndex:'deptId'}];//创建仓库var store = Ext.create("Ext.data.Store",{pageSize:20, //每页显示几条数据//采用代理方式向服务端发送请求,获取数据 proxy:{ type:'ajax', url:'/user/showUser', reader:{ type:'json', totalProperty:'total',//总记录数root:'data', //数据idProperty:'id' //主键 } }, fields:[ //字段。reader是根据这里的字段信息来解析返回的数据的。{name:'id'}, //mapping:0 这样的可以指定列显示的位置,0代表第1列,可以随意设置列显示的位置 {name:'name'}, {name:'age'}, {name:'sex'},{name:'address'},{name:'cardId'},{name:'roleId'},{name:'deptId'}] });var sm = Ext.create('Ext.selection.CheckboxModel');var grid = Ext.create("Ext.grid.Panel",{region: 'center',border: false,store: store,selModel: sm,//选择模式是“复选框” columns: columns,region: 'center', //框架中显示位置,单独运行可去掉此段loadMask:true, //显示遮罩和提示功能,即加载Loading…… forceFit:true, //自动填满表格 columnLines:false, //列的边框rowLines:true, //设置为false则取消行的框线样式 dockedItems: [{xtype:'toolbar',dock:'top',displayInfo: true,items:[{ xtype: 'textfield',name: 'name',fieldLabel: '用户名',labelAlign:'left',labelWidth:65},{ xtype: 'textfield',name: 'dept',fieldLabel: '部门',labelAlign:'left',labelWidth:35},{ xtype: 'textfield',name: 'role',fieldLabel: '角色',labelAlign:'left',labelWidth:35},{ xtype: 'button', text: '查询',iconCls:'Usermagnify'},{ xtype: 'button', text: '显示全部',iconCls:'Applicationformmagnify'}]},{xtype:'toolbar',dock:'top',displayInfo: true,items:['-',{ xtype: 'button', text: '增加用户',iconCls:'Useradd',listeners: {click:function(){Ext.Msg.alert("增加用户","增加用户啦");}}},'-',{ xtype: 'button', id:'editUser', text: '编辑用户',disabled:true,iconCls:'Useredit',//disabled:true 该button处于禁用状态 listeners: {click:function(){Ext.Msg.alert("编辑用户","编辑用户啦");}}},'-',{ xtype: 'button', id:'delUser', text: '删除用户',disabled:true,iconCls:'Userdelete',listeners: {click:function(){Ext.Msg.alert("删除用户","删除用户啦");}}},'-',{ xtype: 'button', id:'allotUser', text: '分配角色',disabled:true,iconCls:'Usergo',listeners: {click:function(){Ext.Msg.alert("分配","分配用户啦");}}},{ xtype: 'button', text: '导入',iconCls:'Databasecopy',listeners: {click:function(){Ext.Msg.alert("导入","导入用户啦");}}},'-',{ xtype: 'button', text: '导出',iconCls:'Pagewhiteoffice',menu:{//菜单属性items:[{//子组件的默认xtype是paneltext:'导出EXCEL',iconCls:'Pageexcel',listeners: {click:function(){Ext.Msg.alert("导出Excel","导出Excel");}}},{text:'导出CSV',iconCls:'Pagegreen',listeners: {click:function(){//Ext.getCmp('editUser').setDisabled(true);Ext.Msg.alert("导出CSV","导出CSV");}}}]}},'-',{ xtype: 'button', text: '打印',iconCls:'Printer',listeners: {click:function(){Ext.Msg.alert("打印","打印");}}}],},{xtype: 'pagingtoolbar',//分页工具栏store: store, // GridPanel使用相同的数据源dock: 'bottom',displayInfo: true,plugins: Ext.create('Ext.ux.ProgressBarPager'),//分页进度条emptyMsg: "没有记录" //没有数据时显示信息 }]});//加载数据store.load({params:{start:0,limit:20}}); // 表格配置结束//监听表格(为表格的选择模型添加selectionchange事件)grid.getSelectionModel().on({//getSelectionModel:返回grid的选择模型。on:是addListener的简写方法selectionchange: function(sm, selections) {//一个选项发生改变之后触发。参数:selections:选择的记录if (selections.length) {//如果有被选中的记录,则‘修改用户’、‘删除用户’、‘分配角色’三个按钮设置为可用状态Ext.getCmp('editUser').setDisabled(false);//getCmp:返回指定id的组件Ext.getCmp('delUser').setDisabled(false);Ext.getCmp('allotUser').setDisabled(false);} else {//反之,这三个按钮设置为禁用状态。Ext.getCmp('editUser').setDisabled(true);Ext.getCmp('delUser').setDisabled(true);Ext.getCmp('allotUser').setDisabled(true);}}});//表格右键菜单 var contextmenu = new Ext.menu.Menu({ id:'theContextMenu', items:[{ text:'增加用户',iconCls:'Useradd',handler:function(){ Ext.Msg.alert("系统提示","增加用户"); } },'-',{ text:'编辑用户',iconCls:'Useredit',handler:function(){ Ext.Msg.alert("系统提示","编辑用户"); } },'-',{ text:'删除用户',iconCls:'Userdelete',handler:function(){ Ext.Msg.alert("系统提示","删除用户"); } },'-',{ text:'分配角色',iconCls:'Usergo',handler:function(){ Ext.Msg.alert("系统提示","分配角色"); } },'-',{ text:'导入',iconCls:'Databasecopy',handler:function(){ Ext.Msg.alert("系统提示","导入"); } },'-',{ text:'导出',iconCls:'Pagewhiteoffice',menu:{items:[{text:'导出EXCEL',iconCls:'Pageexcel',listeners: {click:function(){Ext.Msg.alert("导出Excel","导出Excel");}}},{text:'导出CSV',iconCls:'Pagegreen',listeners: {click:function(){//Ext.getCmp('editUser').setDisabled(true);Ext.Msg.alert("导出CSV","导出CSV");}}}]}},'-',{ text:'打印',iconCls:'Printer',handler:function(){ Ext.Msg.alert("系统提示","打印"); } }] }); //为表格添加右键菜单事件grid.on("itemcontextmenu",function(view,record,item,index,e){ //on:addListener的简写方法. itemcontextmenu:选项的右键菜单事件 右键单击选项时触发。e.preventDefault(); //阻止浏览器默认行为处理事件。--Ext.EventObjectcontextmenu.showAt(e.getXY()); //contextmenu在特定的XY位置显示。 getXY:获取事件的页面坐标。--Ext.EventObject });//为表格添加双击事件grid.on("itemdblclick",function(grid, row){Ext.Msg.alert("系统提示","你双击啦!ID为:"+row.data.id); });// 整体架构容器Ext.create("Ext.container.Viewport", {layout : 'border',autoHeight: true,border: false,items : [grid]}); });
分析:
1.布局:虽然只有一个子组件grid,但依然采用的是Viewport的布局方式。
2.插件
Extjs4.2加了很多拓展插件(4.1版本没有),所在的命名空间为'Ext.ux',如下图:
这里就用到了其中的一个插件:ProgressBarPager--分页进度条,所以在此js需加载此类:
//引入扩展组件 Ext.Loader.setConfig({enabled: true});Ext.Loader.setPath('Ext.ux', '../../ExtJS4.2/ux/');Ext.require(['Ext.data.*','Ext.grid.*','Ext.util.*','Ext.tip.QuickTipManager','Ext.ux.data.PagingMemoryProxy','Ext.ux.ProgressBarPager' ]);
注:除了加载这个 'Ext.ux.ProgressBarPager'插件,对于其他的加载类笔者也不太明白为什么要加载?对于目前的功能,即使注释掉也没影响的,如下:
Ext.require([/*'Ext.data.*','Ext.grid.*','Ext.util.*','Ext.tip.QuickTipManager','Ext.ux.data.PagingMemoryProxy',*/'Ext.ux.ProgressBarPager' ]);
可能是再编辑一些更复杂的功能会需要把。
3.xtype: 'rownumberer'
在创建表格时的第一列就创建了'rownumberer'这个对象,在Extjs中,xtype:'..'是很常见的写法,它用于创建某个类的实例(有时没有这个,一般默认的xtype是panel)组件,api解释如下:
4.表格的事件
在显示用户数据的表格中涉及了2个事件:
一是某行是否被选中的事件,其定义如下:
//监听表格(为表格的选择模型添加selectionchange事件)grid.getSelectionModel().on({//getSelectionModel:返回grid的选择模型。on:是addListener的简写方法selectionchange: function(sm, selections) {//一个选项发生改变之后触发。参数:selections:选择的记录if (selections.length) {//如果有被选中的记录,则‘修改用户’、‘删除用户’、‘分配角色’三个按钮设置为可用状态Ext.getCmp('editUser').setDisabled(false);//getCmp:返回指定id的组件Ext.getCmp('delUser').setDisabled(false);Ext.getCmp('allotUser').setDisabled(false);} else {//反之,这三个按钮设置为禁用状态。Ext.getCmp('editUser').setDisabled(true);Ext.getCmp('delUser').setDisabled(true);Ext.getCmp('allotUser').setDisabled(true);}}});
从以上代码可看出,监听表格的某行是否被选中所监听的对象并不是表格本身,而是这个表格所属的选择模型(此表格的选择模型是复选框),在其所属的选择模型上增加‘selectonchange’事件。
二是表格的右键菜单事件,如下:
//表格右键菜单 var contextmenu = new Ext.menu.Menu({ id:'theContextMenu', items:[{ text:'增加用户',iconCls:'Useradd',handler:function(){ Ext.Msg.alert("系统提示","增加用户"); } },'-',{ text:'编辑用户',iconCls:'Useredit',handler:function(){ Ext.Msg.alert("系统提示","编辑用户"); } },'-',{ text:'删除用户',iconCls:'Userdelete',handler:function(){ Ext.Msg.alert("系统提示","删除用户"); } },'-',{ text:'分配角色',iconCls:'Usergo',handler:function(){ Ext.Msg.alert("系统提示","分配角色"); } },'-',{ text:'导入',iconCls:'Databasecopy',handler:function(){ Ext.Msg.alert("系统提示","导入"); } },'-',{ text:'导出',iconCls:'Pagewhiteoffice',menu:{items:[{text:'导出EXCEL',iconCls:'Pageexcel',listeners: {click:function(){Ext.Msg.alert("导出Excel","导出Excel");}}},{text:'导出CSV',iconCls:'Pagegreen',listeners: {click:function(){//Ext.getCmp('editUser').setDisabled(true);Ext.Msg.alert("导出CSV","导出CSV");}}}]}},'-',{ text:'打印',iconCls:'Printer',handler:function(){ Ext.Msg.alert("系统提示","打印"); } }] }); //为表格添加右键菜单事件grid.on("itemcontextmenu",function(view,record,item,index,e){ //on:addListener的简写方法. itemcontextmenu:选项的右键菜单事件 右键单击选项时触发。e.preventDefault(); //阻止浏览器默认行为处理事件。--Ext.EventObjectcontextmenu.showAt(e.getXY()); //contextmenu在特定的XY位置显示。 getXY:获取事件的页面坐标。--Ext.EventObject});
从以上代码可看出,在表格中设置右键呼出菜单的功能是先定义菜单项,然后为表格添加监听,其事件就是“itemcontextmenu”。需注意的是,由于在window系统下有自带的右键菜单事件,所以一般都要在处理函数中阻止默认的处理事件。
5.一个细节:menu的子组件的xtype默认是panel?
先贴出代码:
{ xtype: 'button', text: '导出',iconCls:'Pagewhiteoffice',menu:{//菜单属性items:[{//子组件的默认xtype是paneltext:'导出EXCEL',iconCls:'Pageexcel',listeners: {click:function(){Ext.Msg.alert("导出Excel","导出Excel");}}},{text:'导出CSV',iconCls:'Pagegreen',listeners: {click:function(){//Ext.getCmp('editUser').setDisabled(true);Ext.Msg.alert("导出CSV","导出CSV");}}}]}},'-',
以上代码的作用是设置按钮并为它添加菜单——就是menu属性,在button类的api这样介绍此属性:
上面代码中menu的值就是一个菜单对象,它又包含2个子组件,在Ext.menu.Menu的api中解释items说默认的子组件的xtype为panel,如下:
那么其中的text属性应该存在于panel的配置选项中,可是查询没有,在menu中也没有查询到text属性。然后全局查找:
觉得此属性的来源比较靠谱是button类中的此属性——毕竟此菜单的父容器就是button。解释是:按钮里的文字。
现在问题已确定了:items的子组件xtype是默认的panel吗?下面把代码修改成显示的指定xtype为panel,如下:
items:[{//子组件的默认xtype是panelxtype:'panel',text:'导出EXCEL',iconCls:'Pageexcel',listeners: {click:function(menu,item){console.log(menu);console.log(item);Ext.Msg.alert("导出Excel","导出Excel");}}}
然后运行,不能正常显示了:
再设置:xtype:'button',
也不能正常显示:
再设置:xtype:'menu',
这一组件直接没了:
最后去掉xtype这个配置,还原,通过调试来看一下是否能确定其身份:
控制台打印的menu的信息如下:
似乎也没有任何迹象表明其身份。所以现在是很奇怪的:items中的两个组件的text属性属于button类;其监听中的click事件属于menu类,那么它自己的xtype到底是什么呢?
二、部门管理
执行原理同上,这里直接贴dept.js
//引入扩展组件 Ext.Loader.setConfig({enabled: true});Ext.Loader.setPath('Ext.ux', '../../ExtJS4.2/ux/');Ext.require(['Ext.data.*','Ext.grid.*','Ext.util.*','Ext.tip.QuickTipManager','Ext.ux.data.PagingMemoryProxy','Ext.ux.ProgressBarPager' ]); Ext.onReady(function() {Ext.define("DeptModel", {extend: "Ext.data.TreeModel",fields: ["id", "text", "status","description"]});var store = Ext.create("Ext.data.TreeStore",{model: "DeptModel",proxy:{ type:'ajax', url:'../config/treegrid.json' ///dept/showDept }});var columns = [{text: '编号',flex: 1,dataIndex: 'id',hidden: true,sortable: true},{xtype: 'treecolumn', //this is so we know which column will show the treetext: '名称',flex: 2,sortable: true,dataIndex: 'text'},{text: '状态',flex: 1,dataIndex: 'status',sortable: true,renderer:function(value){ if(value==0){ return "<span style='color:green;font-weight:bold';>正常</span>"; } else { return "<span style='color:red;font-weight:bold';>停用</span>"; }}},{text: '描述',flex: 1,dataIndex: 'description',sortable: true}];//var sm = Ext.create('Ext.selection.CheckboxModel');var tree = Ext.create('Ext.tree.Panel', {renderTo: Ext.getBody(),rootVisible: false,region: 'center',//selModel: sm,loadMask:true, //显示遮罩和提示功能,即加载Loading…… columnLines:false, //列的边框border: false,forceFit:true, //自动填满表格 rowLines:true, //设置为false则取消行的框线样式 columns: columns,store:store});// 整体架构容器Ext.create("Ext.container.Viewport", {layout : 'border',autoHeight: true,border: false,items : [tree]}); });
数据源:treegrid.json
{"text": ".","children": [{"id": "1","text": "总部","status": 0,"expanded": true,"description":"总部根节点","leaf":false,"children": [{"id": "2","text": "开发部","status": 0,"description":"开发部","expanded": true,"children": [{"id": "3","text": "测试部","status": 1,"description":"开发部","leaf": true}, {"id": "4","text": "前端部","status": 0,"description":"开发部","leaf": true}, {"id": "5","text": "质量部","status": 1,"description":"开发部","leaf": true,}, {"id": "6","text": "版本控制部","status": 0,"description":"开发部","leaf": true,}]}, {"id": "7","text": "项目部","status": 1,"description":"开发部","expanded": true,"children": [{"id": "8","text": "实施部","status": 0,"description":"开发部","leaf": true}, {"id": "9","text": "运维部","status": 1,"description":"开发部",leaf:true}]}]},{"id": "10","text": "总部2","status": 0,"expanded": true,"description":"总部根节点","leaf":false,"children": [{"id": "11","text": "开发部2","status": 0,"description":"开发部","expanded": true,"children": [{"id": "12","text": "测试部2","status": 1,"description":"开发部","leaf": true}, {"id": "13","text": "前端部2","status": 0,"description":"开发部","leaf": true}, {"id": "14","text": "质量部2","status": 1,"description":"开发部","leaf": true,}, {"id": "15","text": "版本控制部2","status": 0,"description":"开发部","leaf": true,}]}, {"id": "16","text": "项目部2","status": 1,"description":"开发部","expanded": true,"children": [{"id": "17","text": "实施部2","status": 0,"description":"开发部","leaf": true}, {"id": "18","text": "运维部2","status": 1,"description":"开发部",leaf:true}]}]}] }