很久没有用过ExtJS,正好一个新的在线系统需要搭建一个运营平台,没有交互,没有页面设计,没有原型,我那点可怜的美感只能借助Extjs来搭建了。
1.通用的架子典型的Border布局+tree菜单+TabPanel的内容区的框架,模块清晰简单,菜单可配置。
- Ext.BLANK_IMAGE_URL="ext/resources/images/default/s.gif";
- Ext.QuickTips.init();
- /*菜单面板*/
- MenuPanel=function(config){
- config=config||{};
- var config_=Ext.applyIf({
- layout:'accordion',
- region:'west',
- split:true,
- width:200,
- collapsible:true,
- border:false,
- animate: true
- },config);
- MenuPanel.superclass.constructor.call(this,config_);
- };
- Ext.extend(MenuPanel,Ext.Panel,{});
- /*菜单模块面板*/
- ModulePanel=function(config){
- config=config||{};
- var nodes_loader=new Ext.tree.TreeLoader({dataUrl:config.dataUrl});
- ModulePanel.superclass.constructor.call(this,{
- animate: true,
- title:config.title,
- rootVisible:false,
- iconCls:'tab_icon',
- region:'west',
- split:true,
- width:200,
- collapsible:true,
- root:{
- },
- loader:nodes_loader
- });
- };
- Ext.extend(ModulePanel,Ext.tree.TreePanel,{
- setTargetOpen:function(contentPanel){
- this.on("click",function(node){
- if(!node.attributes.leaf){
- return true;
- }
- var tabid="module_tab_"+node.attributes.id;
- var exist_panel=contentPanel.getComponent(tabid);
- if(exist_panel){
- contentPanel.setActiveTab(exist_panel);
- }else{
- var iframe_in_tab="iframe_"+tabid;
- var iframe_html="<iframe width=100% height=100% id='"+iframe_in_tab+"'/>"
- var panel=new Ext.Panel({
- title:node.attributes.text,
- id:tabid,
- closable:true,
- iconCls:'tab_icon_2',
- html:iframe_html
- });
- contentPanel.add(panel);
- contentPanel.setActiveTab(panel);
- Ext.get(iframe_in_tab).set({
- src:node.attributes.url
- });
- }
- });
- }
- });
- /*主面板*/
- MainPanel=function(){
- MainPanel.superclass.constructor.call(this,{
- region:'center',
- margins:'0 5 5 0',
- resizeTabs: true,
- minTabWidth: 135,
- tabWidth: 135,
- enableTabScroll: true,
- activeTab: 0,
- items:[{
- title:'公告面板',
- closable:true,
- html:'管理平台欢迎您,如有问题,请联系coreycui,timwen,liubangchen'
- }],
- tbar:new Ext.Toolbar({
- items:[
- {xtype:'displayfield',value:'内容页面导航: ',style:'color:RED'},
- {xtype:'button',iconCls:'prev_icon',tooltip:'后退',handler:function(){
- var activePanel=this.findParentByType("toolbar",false).ownerCt.getActiveTab();
- var activePanelId=activePanel.getItemId();
- var iframe_in_tab=Ext.get("iframe_"+activePanelId);
- Ext.getDom("iframe_"+activePanelId).contentWindow.history.back();
- }},
- {xtype:'button',iconCls:'next_icon',tooltip:'前进',handler:function(){
- var activePanel=this.findParentByType("toolbar",false).ownerCt.getActiveTab();
- var activePanelId=activePanel.getItemId();
- var iframe_in_tab=Ext.get("iframe_"+activePanelId);
- Ext.getDom("iframe_"+activePanelId).contentWindow.history.forward();
- }},
- {xtype:'button',iconCls:'refresh_icon',tooltip:'刷新',handler:function(){
- var activePanel=this.findParentByType("toolbar",false).ownerCt.getActiveTab();
- var activePanelId=activePanel.getItemId();
- var iframe_in_tab=Ext.get("iframe_"+activePanelId);
- Ext.getDom("iframe_"+activePanelId).contentWindow.document.location.reload();
- }}
- ]
- })
- });
- }
- Ext.extend(MainPanel,Ext.TabPanel,{});
- Ext.onReady(function(){
- var contentPanel=new MainPanel();
- var menu_panel=new ModulePanel({
- title:'功能导航',
- dataUrl:'menu_node.jsp'
- });
- menu_panel.setTargetOpen(contentPanel);
- var viewport=new Ext.Viewport({
- layout:'border',
- items:[
- {region:'north',border:false,contentEl:'header',split:true},
- menu_panel,
- contentPanel
- ]
- });
- });
菜单控制基于另外的一个json页面,可方便的接入权限控制,配置如下:
- [
- {
- text:'直通车',children:[
- {text:'a1',leaf:true,url:'http://com/g/s?aid=index&g_ut=1',id:'a1'},
- {text:'a2',leaf:true,url:'http://com/g/s?aid=index&g_ut=2',id:'a2'},
- {text:'a3',leaf:true,url:'http://.com',id:'a3'},
- {text:'a4',leaf:true,url:'http://com',id:'a4'},
- {text:'a5',leaf:true,url:'http://0',id:'a5'},
- {text:'a6',leaf:true,url:'http://a1p',id:'a6'}
- ]
- },
- {
- text:'XX管理',children:[
- {text:'XX查询',leaf:true,url:'<%=request.getContextPath()%>/xxmanage/querycp.jsp',id:'xx_query'},
- {text:'XX审核',leaf:true,url:'<%=request.getContextPath()%>/xxmanage/querycp4manage.jsp',id:'xx_manage'}
- ]
- }
- ]
为了防止内存泄漏和全局变量泛滥,采用继承和组合的方式来组织整个页面的UI结构。继承的模式大致如下:
- MenuPanel=function(config){
- config=config||{};
- var config_=Ext.applyIf({
- layout:'accordion',
- region:'west',
- split:true,
- width:200,
- collapsible:true,
- border:false,
- animate: true
- },config);
- MenuPanel.superclass.constructor.call(this,config_);
- };
- Ext.extend(MenuPanel,Ext.Panel,{});
- MenuPanel.superclass.constructor.call(this,config_);
主要用来在子类的构造函数里面显示的调用父类的构造函数。
- var config_=Ext.applyIf();
2.Grid模块
- var columnModel=new Ext.grid.ColumnModel([
- {header:'日期',dataIndex:'stat_date'}
- /*............*/
- ]);
- var grid=new Ext.grid.GridPanel({
- cm:columnModel,
- store:store,
- renderTo:'grid',
- autoHeight:true,
- viewConfig:{
- forceFit:true
- },
- loadMask:{
- msg:'不要着急,休息一下,休息一下 :)..'
- },
- bbar:new Ext.PagingToolbar({
- pageSize:10,
- store:store,
- stripeRows:true,
- displayInfo:true,
- displayMsg:'显示第{0}条到第{1}条,一共{2}条',
- emptyMsg:'无记录'
- })
- });
- var form=new Ext.form.FormPanel({
- renderTo:'data_setting_div',
- labelAlign:'right',
- labelWidth:60,
- items:[
- {
- xtype: 'compositefield',
- fieldLabel: '日期范围',
- defaults:{
- width:150
- },
- items:[
- {xtype:'datefield',id:'startdate',name:'startdate'},
- {xtype:'displayfield',value:'--',width:10},
- {xtype:'datefield',name:'enddate',id:'enddate'},
- {xtype:'button',text:'查询',listeners:{
- click:function(){
- startDate=form.getForm().findField("startdate").getValue().format('Y-m-d')
- endDate=form.getForm().findField("enddate").getValue().format('Y-m-d');
- store.reload();
- }
- }}
- ]
- }
- ]
- });
- store.load({params:{start:0, limit:10,fromButton:'refresh'}});
强制单元格填充表格:
- viewConfig:{
- forceFit:true
- }
第一次渲染grid的时候必须显示load store,并且需要指定start和limit这两个分页参数,store不会去自动取在grid中配置的。
- store.load({params:{start:0, limit:10,fromButton:'refresh'}});
如果需要查询条件:
- {xtype:'button',text:'查询',listeners:{
- click:function(){
- startDate=form.getForm().findField("startdate").getValue().format('Y-m-d')
- endDate=form.getForm().findField("enddate").getValue().format('Y-m-d');
- store.reload();
- }
- }}
- var store=new Ext.data.Store({
- proxy:new Ext.data.HttpProxy({
- url:gridDataUrl
- }),
- reader:new Ext.data.JsonReader({
- totalProperty: 'totalCount',
- root:'list'
- },[
- {name:'stat_date'}
- /*{}*/
- ]),
- listeners:{
- beforeload:function(thiz,options){
- Ext.apply
- (
- thiz.lastOptions.params,
- {
- startDate : startDate,
- endDate : endDate
- }
- );
- }
- }
- );
- thiz.lastOptions.params
3.扩展Extjs HTMLEditor,图片文件上传并插入。
Extjs HTML Editor是比较坑爹的,自己扩展了一个图片插入的组件。使用方式如下:
- new Ext.form.HtmlEditor({
- fieldLabel:'正文',
- id:'news_details',
- name:'news_details',
- plugins:new FileInsertPlugin({url:FileUpload.url,MVC_BUS:'FileManager',MVC_ACTION:'upload'})
- ),
- FileInsertPlugin:
- /*coreycui*/
- FileInsertPlugin = function(config) {
- config=config||{};
- var editor;
- var win;
- /*创建图片*/
- var createImage = function() {
- var imageWidth = win.getImageWidth();
- var imageHeight = win.getImageHeight();
- var element = document.createElement("img");
- element.src = win.getImageSrc();
- element.alt = win.getImageAlt();
- if (imageWidth == null || imageWidth == '') {
- } else {
- element.style.width = imageWidth + "px"
- }
- if (imageHeight == null || imageHeight == '') {
- } else {
- element.style.height = imageHeight + "px"
- }
- return element;
- }
- // 把图片插入editor中
- var insertImageByBrowser = function() {
- if (Ext.isIE) {
- return function() {
- var selection = editor.doc.selection;
- var range = selection.createRange();
- range.pasteHTML(createImage().outerHTML);
- };
- } else {
- return function() {
- var selection = editor.win.getSelection();
- if (!selection.isCollapsed) {
- selection.deleteFromDocument();
- }
- selection.getRangeAt(0).insertNode(createImage());
- };
- }
- }();
- /*插入图片*/
- var insertImage = function() {
- editor.win.focus();
- insertImageByBrowser();
- editor.updateToolbar();
- editor.deferFocus();
- };
- /*图片成功回调*/
- var whenImgUploadSuccess=function(result){
- insertImage();
- win.close();
- };
- /*图片失败回调*/
- var whenImgUploadFailure=function(result){
- Msg.MessageBox.alert("图片上传失败",result.msg);
- win.close();
- }
- /*打开图片上传窗口*/
- var openImageWindow = function() {
- win=new FileUploadWindow(config);
- win.setSuccessCallback(whenImgUploadSuccess);
- win.setFailureCallback(whenImgUploadFailure);
- win.show(this);
- }
- var onRender=function(){
- editor.tb.add({
- itemId : 'image',
- cls : 'x-btn-icon x-edit-image',
- handler : openImageWindow,
- tooltip : {
- title : '图片',
- text : '插入图片',
- cls : 'x-html-editor-tip'
- }
- });
- }
- return {
- init : function(htmlEditor) {
- editor = htmlEditor;
- editor.on('render',onRender);
- }
- }
- }
插件的形式主要是在init中做文章,宿主会render后init这个插件:
- init : function(htmlEditor) {
- editor = htmlEditor;
- editor.on('render',onRender);
- var selection = editor.doc.selection;
- var range = selection.createRange();
- range.pasteHTML(createImage().outerHTML);
文件上传的窗口如下:
- /*文件上传窗口*/
- FileUploadWindow=function(config){
- config=config||{};
- /*成功的回调函数*/
- var successCallback;
- var failureCallback;
- var fileUrl;
- var fileUploadForm=new Ext.form.FormPanel({
- fileUpload:true,
- url:config.url,
- labelAlign:'right',
- labelWidth:60,
- defaults:{
- anchor:'-20'
- },
- items:[
- {fieldLabel:'本地文件',xtype:'textfield',name:'file',id:'file',inputType:'file'},
- {fieldLabel:'文件描述',xtype:'textfield',name:'filedesc',id:'filedesc',width:190},
- {fieldLabel:'高度',xtype:'textfield',name:'fileheight',id:'fileheight',width:190},
- {fieldLabel:'宽度',xtype:'textfield',name:'filewidth',id:'filewidth',width:190}
- ],
- buttons:[
- {text:'提交',handler:function(){
- fileUploadForm.getForm().submit({
- params:config,
- failure:function(form,action){
- if(failureCallback){
- failureCallback(action.result);
- }
- },
- success:function(form,action){
- fileUrl=action.result.fileUrl;
- if(successCallback){
- successCallback(action.result);
- }
- }
- });
- }}
- ]
- });
- Ext.apply(config,{
- width:325,
- title:'图片插入',
- autoHeight:true,
- items:[fileUploadForm]
- });
- FileUploadWindow.superclass.constructor.call(this,config);
- /*获取表单*/
- this.getForm=function(){
- return fileUploadForm;
- }
- /*获取基础表单*/
- this.getBasicForm=function(){
- return fileUploadForm.getForm();
- }
- /*设置成功回调函数*/
- this.setSuccessCallback=function(callbackfn){
- successCallback=callbackfn;
- }
- /*设置失败回调函数*/
- this.setFailureCallback=function(callbackfn){
- failureCallback=callbackfn;
- }
- this.getImageSrc=function(){
- return fileUrl;
- }
- this.getImageAlt=function(){
- return this.getBasicForm().findField('filedesc').getValue();
- }
- this.getImageWidth=function(){
- return this.getBasicForm().findField('filewidth').getValue();
- }
- this.getImageHeight=function(){
- return this.getBasicForm().findField('fileheight').getValue();
- }
- }
- Ext.extend(FileUploadWindow,Ext.Window,{});
事实上就是一个文件上传的扩展窗口,可以在初始化参数中指定文件上传的URL路径,将上传成功和失败的回调钩子的实现留给了调用者。向外提供了获取图片上传后的图片的属性。
4.一个增删改查的窗口抽取。
一个grid配上了一个用于增改的window。将window和grid的控制逻辑抽取出来,将window中的表单,记录新增的URL,记录修改的URL,记录内容获取的URL路径暴露出来。在grid的toolbar中新增如下功能按钮以及相对的数据:
- tbar:new Ext.Toolbar({
- items:[
- {xtype:'button',text:'新增新闻',iconCls:'add',handler:function(thiz,option){
- var newsForm=new NewsForm(mvcUrl);
- var newsAddWin=new AddOrEditWindow({autoScroll:true,width:800,height:500,title:'新增新闻',mvcUrl:mvcUrl},newsForm)
- newsAddWin.show();
- newsAddWin.setParams({
- MVC_BUS:'News',
- MVC_ACTION:'add'
- });
- newsAddWin.setSuccessCallback(function(){
- store.reload();
- });
- }},
- {xtype:'button',text:'编辑新闻',iconCls:'icon_window',handler:function(thiz,option){
- var selections=sm.getSelections();
- if(selections.length!=1){
- Ext.example.msg("警告","请选择一条记录!");
- }else{
- var id=selections[0].get("id");
- var newsForm=new NewsForm(mvcUrl);
- var newsAddWin=new AddOrEditWindow({autoScroll:true,width:800,height:500,title:'编辑新闻',mvcUrl:mvcUrl},newsForm)
- newsAddWin.show();
- newsAddWin.initFormData(config.getDataUrl,{id:id});
- newsAddWin.setParams({
- MVC_BUS:'News',
- MVC_ACTION:'edit'
- });
- newsAddWin.setSuccessCallback(function(){
- store.reload();
- });
- }
- }}
- ]
- })
新增和修改的主要是玩转一个AddOrEditWindow,我们将自己构造的表单作为参数传入该window:
- var newsAddWin=new AddOrEditWindow({autoScroll:true,width:800,height:500,title:'编辑新闻',mvcUrl:mvcUrl},newsForm)
当window用来编辑的时候,这个form还必须实现一个接口,obj是从记录获取URL中得到的数据对象,如下:
- this.setFields=function(obj){
- this.getForm().findField("news_title").setValue(obj.result.summary.news_title);
- this.getForm().findField("summary").setValue(obj.result.summary.summary);
- this.getForm().findField("status_combo").setValue(obj.result.summary.status);
- this.getForm().findField("news_order").setValue(obj.result.summary.news_order);
- editor.setSource(obj.result.details.news_details);
- if(obj.result.types){
- for(var i=0;i<obj.result.types.length;i++){
- var typeCheckboxId="news_type_"+obj.result.types[i];
- var checkBoxType=Ext.getCmp(typeCheckboxId);
- checkBoxType.setValue(true);
- }
- }
- newsAddWin.initFormData(config.getDataUrl,{id:id});
AddOrEditWindow的实现如下:
- AddOrEditWindow = function(config, form) {
- config = config || {};
- var thiz = this;
- var params = {};
- var successCallback;
- var failureCallback;
- var formPanel = form;
- var sumitForm = function() {
- formPanel.getForm().submit({
- params : params,
- success : function(form, action) {
- Ext.example.msg("提示", "操作成功");
- if (successCallback) {
- successCallback();
- }
- thiz.close();
- },
- failure : function(form, action) {
- Ext.example.msg("提示", "操作失败");
- if (failureCallback) {
- failureCallback();
- }
- thiz.close();
- }
- });
- }
- Ext.apply(config, {
- iconCls : 'icon_window',
- items : [ formPanel ],
- buttons : [ {
- xtype : 'button',
- text : '提交',
- iconCls : 'save',
- handler : function() {
- sumitForm();
- }
- }, {
- xtype : 'button',
- text : '关闭',
- iconCls : 'delete',
- handler : function() {
- thiz.close();
- }
- } ]
- });
- AddOrEditWindow.superclass.constructor.call(this, config);
- this.setParams = function(ps) {
- Ext.apply(params, ps);
- }
- this.initFormData = function(url, ps) {
- Ext.Ajax.request({
- url : url,
- params : ps,
- success : function(resp, options) {
- var respText = Ext.util.JSON.decode(resp.responseText);
- params.id = respText.result.id;
- formPanel.setFields(respText);
- },
- failure : function(resp, options) {
- thiz.close();
- }
- });
- }
- this.setSuccessCallback = function(fn) {
- successCallback = fn;
- }
- this.setFailureCallback = function(fn) {
- failureCallback = fn;
- }
- };
- Ext.extend(AddOrEditWindow, Ext.Window, {});
5.一些小技巧
给表格新增一个操作的列,如删除等等之类的。
- {xtype:'actioncolumn',width:70,items:[{
- icon: '../ext/selfimg/picture_delete.png',
- tooltip:'删除封面',
- handler:function(grid, rowIndex, colIndex){
- var rec = store.getAt(rowIndex);
- var id=rec.get("id");
- Ext.MessageBox.confirm("删除确认","确认是否删除?",function(btn){
- }
- });
- }
- }]}
type为:
- actioncolumn
6.与Jquery成为一对好基友
有的时候,需要和jquery一起使用,只需引入jquery的适配器。
- <script type="text/javascript" src="../ext/jquery-1.4.2.min.js"></script>
- <script type="text/javascript" src="../ext/ext-jquery-adapter.js"></script>
- <script type="text/javascript" src="../ext/xheditor-1.1.9-zh-cn.min.js"></script>
- <script type="text/javascript" src="../ext/ext-all.js"></script>
- <script type="text/javascript" src="../ext/ext-lang-zh_CN.js"></script>
如使用jquery的插件XKEditor编辑器。
- {xtype:'textarea',fieldLabel:'正文',id:'news_details_proxy',name:'news_details_proxy',height:400,anchor: '-20'}
- editor=$('#news_details_proxy').xheditor({html5Upload:false,upLinkUrl:fileUploadUrl,upLinkExt:"zip,rar,txt",upImgUrl:fileUploadUrl,upImgExt:"jpg,jpeg,gif,png",upFlashUrl:fileUploadUrl,upFlashExt:"swf",upMediaUrl:fileUploadUrl,upMediaExt:"avi"});
发现总是无法成功,textarea还是老样子。
原来是因为在表单render动态的新增了组件,嗲用了doLayout,
- var renderNewsTypes=function(){
- Ext.Ajax.request({
- url : '<%=request.getContextPath()%>/mvc?MVC_BUS=NewsTypes&MVC_ACTION=list',
- success : function(resp, options) {
- var respText = Ext.util.JSON.decode(resp.responseText);
- var types=respText.list;
- typesInited=true;
- for(var i=0;i<types.length;i++){
- typesCheckBoxes.push({xtype:'checkbox',boxLabel:types[i].type_name,inputValue:types[i].id,name:'news_type_'+types[i].id,id:'news_type_'+types[i].id});
- }
- thiz.get(0).insert(1,{fieldLabel:'新闻类型',xtype:"checkboxgroup",columns: 4,items:typesCheckBoxes,name:'news_type_group',id:'news_type_group'});
- thiz.get(0).remove(thiz.get(0).findById("news_types_proxy"));
- thiz.doLayout();
- },failure:function(){
- Ext.MessageBox.alert("警告","新闻内容拉取失败");
- }
- });
所以必须在每次layout后,重新将编辑器渲染一下:
- thiz.doLayout();
- editor=$('#news_details_proxy').xheditor({html5Upload:false,upLinkUrl:fileUploadUrl,upLinkExt:"zip,rar,txt",upImgUrl:fileUploadUrl,upImgExt:"jpg,jpeg,gif,png",upFlashUrl:fileUploadUrl,upFlashExt:"swf",upMediaUrl:fileUploadUrl,upMediaExt:"avi"});
7.一些不解
当文件AJAX上传的时候,服务器返回数据必须显示指定;
response.setContentType()"text/html";否则判断为失败。
浙公网安备 33010602011771号