Ext [DDTabPanel、FoodImageField、ImageChooser]扩展组件

最后更新于:2022-04-01 12:01:29

**Ext 扩展组件** 开发环境: System:Windows WebBrowser:IE6+、Firefox3+ JavaEE Server:tomcat5.0.2.8、tomcat6 IDE:eclipse、MyEclipse 6.5 开发依赖库: JavaEE5、ext 2.2.2 Email:hoojo_@126.com Blog:[http://blog.csdn.net/IBM_hoojo](http://blog.csdn.net/IBM_hoojo) [http://hoojo.cnblogs.com/](http://hoojo.cnblogs.com/) ### 一、Ext.ux.panel.DDTabPanel组件 可以拖动tabPanel的组件 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe122b076ec.gif) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe122b37ab4.gif) 需要用到的文件 DDTabPanel组件文件:Ext.ux.panel.DDTabPanel.js DDTabPanel运行示例文件:Ext.hoo.form.DDTabPanel.js 代码如下 ddTabPanelExample.htm ~~~ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Basic Component -- DDTabPanel</title> <meta http-equiv="pragma" content="no-cache"/> <meta http-equiv="cache-control" content="no-cache"/> <meta http-equiv="expires" content="0"/> <meta http-equiv="content-Type" content="text/html; charset=utf-8"/> <meta http-equiv="author" content="hoojo"/> <meta http-equiv="email" content="hoojo_@126.com"/> <meta http-equiv="ext-lib" content="version 3.2"/> <meta http-equiv="blog" content="http://blog.csdn.net/IBM_hoojo"/> <link rel="stylesheet" type="text/css" href="../ext2/resources/css/ext-all.css"/> <link rel="stylesheet" type="text/css" href="../css/DDTabPanel.css"/> <script type="text/javascript" src="../ext2/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="../ext2/ext-all.js"></script> <script type="text/javascript" src="../ext2/build/locale/ext-lang-zh_CN-min.js"></script> <script type="text/javascript" src="Ext.ux.panel.DDTabPanel.js"></script> <script type="text/javascript" src="Ext.hoo.form.DDTabPanel.js"></script> </head> <body> </body> </html> ~~~ DDTabPanel.css ~~~ .dd-arrow-down.dd-arrow-down-invisible { display: none; visibility: hidden; } .dd-arrow-down { background-image: url(../images/dd-arrow-down.gif); display: block; visibility: visible; z-index: 20000; position: absolute; width: 16px; height: 16px; top: 0; left: 0; background-repeat: no-repeat; } html, body { font: 10pt "Segoe UI","Tahoma","Helvetica","Arial",sans-serif; padding: 5px; } #container { margin: 5px 10px; } ~~~ dd-arrow-down.gif  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe122b65049.gif) Ext.hoo.form.DDTabPanel.js ~~~ /** * @function 可以拖拽的tabPanel * @auhor: hoojo * @createDate: Sep 16, 2010 9:25:12 PM * @blog: blog.csdn.net/IBM_hoojo * @email: hoojo_@126.com * @class Ext.hoo.form.DDTabPanel * @extends Ext.ux.panel.DDTabPanel */ Ext.ns("Ext.hoo.form"); Ext.hoo.form.DDTabPanel = Ext.extend(Ext.ux.panel.DDTabPanel, { constructor: function () { Ext.hoo.form.DDTabPanel.superclass.constructor.call(this, { renderTo: Ext.getBody(), height: 500, items: [{ title: "我的主页", html: "这是一个主页" }, { title: "站内新闻", html: "重大新闻" }, { title: "关于我们", html: "网址建设" }] }); } }); Ext.onReady(function () { Ext.BLANK_IMAGE_URL = "../ext2/resources/images/default/s.gif"; Ext.QuickTips.init(); Ext.form.Field.prototype.msgTarget = "qtip"; new Ext.hoo.form.DDTabPanel(); }); ~~~ Ext.ux.panel.DDTabPanel.js ~~~ /*global Ext*/ Ext.namespace('Ext.ux.panel'); /** * <p>A tab panel which supports drag and drop behaviour for tabs. Usage and configuration are identical to {@link Ext.TabPanel}, with the sole exception of two extra configuration options to adjust the drop arrow indicator position.</p> * <p>This extension can also be created using the <b>ddtabpanel</b> xtype.<br/> </p> * <p>Based on the code of <a href="http://extjs.com/forum/member.php?u=22731">thommy</a> and <a href="http://extjs.com/forum/member.php?u=37284">rizjoj</a> in the topic <a href="http://extjs.com/forum/showthread.php?t=23264">Draggable Panel in a TabPanel</a>.</p> * <p>Demo link: <a href="http://extjs-ux.org/repo/authors/Matti/trunk/Ext/ux/panel/DDTabPanel/demo.html">http://extjs-ux.org/repo/authors/Matti/trunk/Ext/ux/panel/DDTabPanel/demo.html</a> * <br />Forum thread: <a href="http://extjs.com/forum/showthread.php?p=264712">http://extjs.com/forum/showthread.php?p=264712</a><br/> </p> * <b>CSS Styles:</b> * <pre><code>.dd-arrow-down.dd-arrow-down-invisible { display: none; visibility: hidden; } .dd-arrow-down { background-image: url( <your_down_arrow_image> ); display: block; visibility: visible; z-index: 20000; position: absolute; width: 16px; height: 16px; top: 0; left: 0; }</code></pre> * <br /><b>Example Usage:</b> * <pre><code>var tabs = new Ext.ux.panel.DDTabPanel({ renderTo: Ext.getBody(), items: [{ title: 'Tab 1', html: 'A simple tab' },{ title: 'Tab 2', html: 'Another one' }] });</code></pre> * @class Ext.ux.panel.DDTabPanel * @extends Ext.TabPanel * @author Original by <a href="http://extjs.com/forum/member.php?u=22731">thommy</a> and <a href="http://extjs.com/forum/member.php?u=37284">rizjoj</a><br />Published and polished by: Mattias Buelens (<a href="http://extjs.com/forum/member.php?u=41421">Matti</a>) * @license Licensed under the terms of the Open Source <a href="http://www.gnu.org/licenses/lgpl.html">LGPL 3.0 license</a>. Commercial use is permitted to the extent that the code/component(s) do NOT become part of another Open Source or Commercially licensed development library or toolkit without explicit permission. * @version 1.0.2 (Dec 18, 2008) */ Ext.ux.panel.DDTabPanel = Ext.extend(Ext.TabPanel, { /** * @cfg {Number} arrowOffsetX * The horizontal offset for the drop arrow indicator, in pixels (defaults to -9). */ arrowOffsetX: -9, /** * @cfg {Number} arrowOffsetY * The vertical offset for the drop arrow indicator, in pixels (defaults to -8). */ arrowOffsetY: -8, // Overwritten: assign the drag and drop group id /** @private */ initComponent: function() { Ext.ux.panel.DDTabPanel.superclass.initComponent.call(this); this.ddGroupId = 'dd-tabpanel-group-' + Ext.ux.panel.DDTabPanel.superclass.getId.call(this); }, // Overwritten: declare the tab panel as a drop target /** @private */ initEvents: function(){ Ext.ux.panel.DDTabPanel.superclass.initEvents.call(this); // Create a drop target for this tab panel var tabsDDGroup = this.ddGroupId; this.dd = new Ext.ux.panel.DDTabPanel.DropTarget(this, { ddGroup: tabsDDGroup }); // Create a drop arrow indicator this.arrow = Ext.DomHelper.append( Ext.getBody(), '<div class="dd-arrow-down dd-arrow-down-invisible"></div>', true ); //this.arrow.dom.style.display = "none";//初始化的时候隐藏 this.arrow.setStyle({display: "none"}); }, // Overwritten: init the drag source after (!) rendering the tab /** @private */ initTab: function(tab, index){ Ext.ux.panel.DDTabPanel.superclass.initTab.call(this, tab, index); // Set the initial tab position tab.position = (index + 1) * 2; // 2, 4, 6, 8, ... (2n) tab.on('render', function(tab){ // Make this tab a drag source var id = this.id + '__' + tab.id; var tabsDDGroup = this.ddGroupId; tab.ds = new Ext.dd.DragSource(id, { ddGroup: tabsDDGroup, dropEl: tab, dropElHeader: Ext.get(id, true) }); // Activate this tab before starting the drag action tab.ds.beforeDragEnter = function(target, event, id){ target.tabpanel.activate(this.dropEl); //target.tabpanel.arrow.dom.style.display = "block";//显示放入的时候,显示图标 //target.tabpanel.arrow.setStyle({display: "block"}); }; // Activate this tab on mouse down // Fixed bug which prevents a tab from being activated by clicking it tab.ds.onMouseDown = (function(event){ this.activate(tab); }).createDelegate(this); }, this); // Force the tab to render tab.show(); } }); // Ext.ux.panel.DDTabPanel.DropTarget // Implements the drop behavior of the tab panel /** @private */ Ext.ux.panel.DDTabPanel.DropTarget = Ext.extend(Ext.dd.DropTarget, { constructor: function(tabpanel, config){ this.tabpanel = tabpanel; // The drop target is the header area of the given tab panel var target = Ext.select('div.x-tab-panel-header', false, tabpanel.getEl().dom).elements[0]; Ext.ux.panel.DDTabPanel.DropTarget.superclass.constructor.call(this, target, config); }, notifyOver: function(dd, e, data){ var tabs = this.tabpanel.items; var last = tabs.length; if (last < 2) { return 'x-dd-drop-nodrop'; } var larrow = this.tabpanel.arrow; // Getting the absolute X,Y coordinates by encapsulating the dom // element into an Ext.Element and using getX() and getY() methods. var panelDom = new Ext.Element(this.el.dom); var tabPanelLeft = panelDom.getX(); var tabPanelTop = panelDom.getY(); var left; var eventPosX = e.getPageX(); for (var i = 0; i < last; i++) { var tab = tabs.itemAt(i); // Is this tab target of the drop operation? var tabDom = tab.ds.dropElHeader.dom; // Getting the absolute X,Y coordinates by encapsulating the dom // element into an Ext.Element and using getX() and getY() methods. var tabLeft = new Ext.Element(tabDom).getX(); var tabMiddle = tabLeft + tabDom.clientWidth / 2; if (eventPosX <= tabMiddle) { left = tabLeft; break; } } if (typeof(left) == 'undefined') { var lastTab = tabs.itemAt(last - 1); var dom = lastTab.ds.dropElHeader.dom; left = (tabPanelLeft + dom.offsetLeft + dom.clientWidth) + 3; } larrow.setTop(tabPanelTop + this.tabpanel.arrowOffsetY); larrow.setLeft(left + this.tabpanel.arrowOffsetX); larrow.removeClass('dd-arrow-down-invisible'); larrow.setStyle({display: "block"}); return 'x-dd-drop-ok'; }, notifyDrop: function(dd, e, data){ this.tabpanel.arrow.addClass('dd-arrow-down-invisible'); var tabPanelOffset = this.tabpanel.el.dom.offsetLeft; var tabs = this.tabpanel.items; // At this point the items in 'tabs' are sorted by their positions var tabDom = new Ext.Element(this.tabpanel.el.dom); // Getting the absolute X,Y coordinates by encapsulating the dom // element into an Ext.Element and using getX() and getY() methods. var eventPosX = e.getPageX() - tabDom.getX(); var last = tabs.length; var newPos = last; dd.dropEl.position = last * 2 + 1; // default: 'behind the rest' for (var i = 0; i < last; i++) { var tab = tabs.itemAt(i); // Is this tab target of the drop operation? var dom = tab.ds.dropElHeader.dom; var tabLeft = tabPanelOffset + dom.offsetLeft; var tabRight = tabLeft + dom.clientWidth; var tabMiddle = tabLeft + dom.clientWidth / 2; if (eventPosX <= tabRight) { dd.dropEl.position = eventPosX > tabMiddle ? tab.position + 1 : tab.position - 1; newPos = eventPosX > tabMiddle ? i + 1 : i; break; } } dd.proxy.hide(); dd.el.dom.parentNode.insertBefore(dd.el.dom, dd.el.dom.parentNode.childNodes[newPos]); // Sort tabs by their actual position tabs.sort('ASC', function(a, b){ return a.position - b.position; }); // Adjust tab position values tabs.each(function(tab, index){ tab.position = (index + 1) * 2; }); var larrow = this.tabpanel.arrow; larrow.setStyle({display: "none"}); //this.tabpanel.arrow.dom.style.display = "none"; return true; }, notifyOut: function(dd, e, data) { this.tabpanel.arrow.addClass('dd-arrow-down-invisible'); } }); Ext.reg('ddtabpanel', Ext.ux.panel.DDTabPanel); ~~~ ### 二、Images choose选择控件 可以选择图片、过滤图片,及显示图片详情 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe122b811ab.gif) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe122bc3d72.gif) 需要用到的文件 Ext.ux.ImageChooser.css style.css chooser.js chooser组件:Ext.ux.form.ImageField.js Ext.ux.ImageChooser.js 示例:Ext.hoo.form.FoodImageField.js   代码如下 imageSelectedExample.htm ~~~ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Basic Component -- imageChooser</title> <meta http-equiv="pragma" content="no-cache"/> <meta http-equiv="cache-control" content="no-cache"/> <meta http-equiv="expires" content="0"/> <meta http-equiv="content-Type" content="text/html; charset=utf-8"/> <meta http-equiv="author" content="hoojo"/> <meta http-equiv="email" content="hoojo_@126.com"/> <meta http-equiv="ext-lib" content="version 3.2"/> <meta http-equiv="blog" content="http://blog.csdn.net/IBM_hoojo"/> <link rel="stylesheet" type="text/css" href="../ext2/resources/css/ext-all.css"/> <link rel="stylesheet" type="text/css" href="../css/Ext.ux.ImageChooser.css"/> <link rel="stylesheet" type="text/css" href="../css/style.css"/> <script type="text/javascript" src="../ext2/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="../ext2/ext-all.js"></script> <script type="text/javascript" src="../ext2/build/locale/ext-lang-zh_CN-min.js"></script> <script type="text/javascript" src="Ext.ux.form.ImageField.js"></script> <script type="text/javascript" src="Ext.ux.ImageChooser.js"></script> <script type="text/javascript" src="chooser.js"></script> <script type="text/javascript" src="Ext.hoo.form.FoodImageField.js"></script> </head> <body> <div id="show" style="margin: 50px;"></div> <div id="showField" style="margin: 50px;"></div> <div id="buttons" style="margin:20px;"></div> <div id="images" style="margin:20px;width:600px;"></div> </body> </html> ~~~ Ext.ux.ImageChooser.css ~~~ /* * Ext JS Library 2.0 * Copyright(c) 2006-2007, Ext JS, LLC. * licensing@extjs.com * * http://extjs.com/license */ #img-chooser-dlg .details{ padding: 10px; text-align: center; } #img-chooser-dlg .details-info{ border-top: 1px solid #cccccc; font: 11px Arial, Helvetica, sans-serif; margin-top: 5px; padding-top: 5px; text-align: left; } #img-chooser-dlg .details-info b{ color: #555555; display: block; margin-bottom: 4px; } #img-chooser-dlg .details-info span{ display: block; margin-bottom: 5px; margin-left: 5px; } #img-chooser-view{ background: white; font: 11px Arial, Helvetica, sans-serif; } #img-chooser-view .thumb{ background: #dddddd; padding: 3px; } #img-chooser-view .thumb img{ height: 30px; width: 40px; } #img-chooser-view .thumb-wrap{ float: left; margin: 4px; margin-right: 0; padding: 5px; } #img-chooser-view .thumb-wrap span{ display: block; overflow: hidden; text-align: center; } #img-chooser-view .x-view-over{ border:1px solid #dddddd; background: #efefef url(../resources/images/default/grid/row-over.gif) repeat-x left top; padding: 4px; } #img-chooser-view .x-view-selected{ background: #DFEDFF; border: 1px solid #6593cf; padding: 4px; } #img-chooser-view .x-view-selected .thumb{ background:transparent; } #img-chooser-view .x-view-selected span{ color:#1A4D8F; } #img-chooser-view .loading-indicator { font-size:11px; background-image:url('../resources/images/grid/loading.gif'); background-repeat: no-repeat; background-position: left; padding-left:20px; margin:10px; } ~~~ style.css ~~~ .x-form-field-wrap .x-form-trigger{ background:transparent url(../ext2/resources/images/default/form/search-trigger.gif) no-repeat 0 0 !important; } .x-form-imagefield { text-align:right; padding-right:17px; } .ext-safari .x-form-field-wrap .x-form-trigger { right:-17px !important; } .x-form-imagefield-image { width:34px; height:34px; background:#fff; border: 1px solid #B5B8C8; } .images-view .x-window-body{ background: #ffffff; color: #000000; } .images-view .thumb{ border:1px solid #dddddd; padding: 0px; height: 34px; width: 34px; } .images-view .thumb-wrap{ float: left; margin: 4px; margin-right: 0; padding: 5px; cursor: pointer; } .images-view .x-view-over{ border:1px solid #cccccc; background: #eeeeee; padding: 4px; } .images-view .x-view-selected{ background: #ccddee; border:1px solid #6699cc; padding: 4px; } .images-view .x-view-selected .thumb{ border:1px solid #6699cc; } ~~~ chooser.js ~~~ /* * Ext JS Library 2.2.1 * Copyright(c) 2006-2009, Ext JS, LLC. * licensing@extjs.com * * http://extjs.com/license */ /* * Ext JS Library 2.0 * Copyright(c) 2006-2007, Ext JS, LLC. * licensing@extjs.com * * http://extjs.com/license */ var ImageChooser = function(config){ this.config = config; } ImageChooser.prototype = { // cache data by image name for easy lookup lookup : {}, show : function(el, callback){ if(!this.win){ this.initTemplates(); this.store = new Ext.data.JsonStore({ url: this.config.url, root: 'images', fields: [ 'name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date', dateFormat:'timestamp'} ], listeners: { 'load': {fn:function(){ this.view.select(0); }, scope:this, single:true} } }); this.store.load(); var formatSize = function(data){ if(data.size < 1024) { return data.size + " bytes"; } else { return (Math.round(((data.size*10) / 1024))/10) + " KB"; } }; var formatData = function(data){ data.shortName = data.name.ellipse(15); data.sizeString = formatSize(data); data.dateString = new Date(data.lastmod).format("m/d/Y g:i a"); this.lookup[data.name] = data; return data; }; this.view = new Ext.DataView({ tpl: this.thumbTemplate, singleSelect: true, overClass:'x-view-over', itemSelector: 'div.thumb-wrap', emptyText : '<div style="padding:10px;">No images match the specified filter</div>', store: this.store, listeners: { 'selectionchange': {fn:this.showDetails, scope:this, buffer:100}, 'dblclick' : {fn:this.doCallback, scope:this}, 'loadexception' : {fn:this.onLoadException, scope:this}, 'beforeselect' : {fn:function(view){ return view.store.getRange().length > 0; }} }, prepareData: formatData.createDelegate(this) }); var cfg = { title: 'Choose an Image', id: 'img-chooser-dlg', layout: 'border', minWidth: 500, minHeight: 300, modal: true, closeAction: 'hide', border: false, items:[{ id: 'img-chooser-view', region: 'center', autoScroll: true, items: this.view, tbar:[{ text: 'Filter:' },{ xtype: 'textfield', id: 'filter', selectOnFocus: true, width: 100, listeners: { 'render': {fn:function(){ Ext.getCmp('filter').getEl().on('keyup', function(){ this.filter(); }, this, {buffer:500}); }, scope:this} } }, ' ', '-', { text: 'Sort By:' }, { id: 'sortSelect', xtype: 'combo', typeAhead: true, triggerAction: 'all', width: 100, editable: false, mode: 'local', displayField: 'desc', valueField: 'name', lazyInit: false, value: 'name', store: new Ext.data.SimpleStore({ fields: ['name', 'desc'], data : [['name', 'Name'],['size', 'File Size'],['lastmod', 'Last Modified']] }), listeners: { 'select': {fn:this.sortImages, scope:this} } }] },{ id: 'img-detail-panel', region: 'east', split: true, width: 150, minWidth: 150, maxWidth: 250 }], buttons: [{ id: 'ok-btn', text: 'OK', handler: this.doCallback, scope: this },{ text: 'Cancel', handler: function(){ this.win.hide(); }, scope: this }], keys: { key: 27, // Esc key handler: function(){ this.win.hide(); }, scope: this } }; Ext.apply(cfg, this.config); this.win = new Ext.Window(cfg); } this.reset(); this.win.show(el); this.callback = callback; this.animateTarget = el; }, initTemplates : function(){ this.thumbTemplate = new Ext.XTemplate( '<tpl for=".">', '<div class="thumb-wrap" id="{name}">', '<div class="thumb"><img src="{url}" title="{name}"></div>', '<span>{shortName}</span></div>', '</tpl>' ); this.thumbTemplate.compile(); this.detailsTemplate = new Ext.XTemplate( '<div class="details">', '<tpl for=".">', '<img src="{url}"><div class="details-info">', '<b>Image Name:</b>', '<span>{name}</span>', '<b>Size:</b>', '<span>{sizeString}</span>', '<b>Last Modified:</b>', '<span>{dateString}</span></div>', '</tpl>', '</div>' ); this.detailsTemplate.compile(); }, showDetails : function(){ var selNode = this.view.getSelectedNodes(); var detailEl = Ext.getCmp('img-detail-panel').body; if(selNode && selNode.length > 0){ selNode = selNode[0]; Ext.getCmp('ok-btn').enable(); var data = this.lookup[selNode.id]; detailEl.hide(); this.detailsTemplate.overwrite(detailEl, data); detailEl.slideIn('l', {stopFx:true,duration:.2}); }else{ Ext.getCmp('ok-btn').disable(); detailEl.update(''); } }, filter : function(){ var filter = Ext.getCmp('filter'); this.view.store.filter('name', filter.getValue()); this.view.select(0); }, sortImages : function(){ var v = Ext.getCmp('sortSelect').getValue(); this.view.store.sort(v, v == 'name' ? 'asc' : 'desc'); this.view.select(0); }, reset : function(){ if(this.win.rendered){ Ext.getCmp('filter').reset(); this.view.getEl().dom.scrollTop = 0; } this.view.store.clearFilter(); this.view.select(0); }, doCallback : function(){ var selNode = this.view.getSelectedNodes()[0]; var callback = this.callback; var lookup = this.lookup; this.win.hide(this.animateTarget, function(){ if(selNode && callback){ var data = lookup[selNode.id]; callback(data); } }); }, onLoadException : function(v,o){ this.view.getEl().update('<div style="padding:10px;">Error loading images.</div>'); } }; String.prototype.ellipse = function(maxLength){ if(this.length > maxLength){ return this.substr(0, maxLength-3) + '...'; } return this; }; ~~~   Ext.ux.form.ImageField.js ~~~ Ext.namespace('Ext.ux.form'); /** * @class Ext.form.ImageField * @extends Ext.BoxComponent * Class for form image fields that provides event handling value handling and other functionality. * @constructor * Creates a new ImageField * @param {Object} config Configuration options */ Ext.ux.form.ImageField = Ext.extend(Ext.BoxComponent, { /** * @cfg {String} fieldLabel The label text to display next to this field (defaults to '') */ /** * @cfg {String} labelStyle A CSS style specification to apply directly to this field's label (defaults to the * container's labelStyle value if set, or ''). For example, <code>labelStyle: 'font-weight:bold;'</code>. */ /** * @cfg {String} labelSeparator The standard separator to display after the text of each form label (defaults * to the value of {@link Ext.layout.FormLayout#labelSeparator}, which is a colon ':' by default). To display * no separator for this field's label specify empty string ''. */ /** * @cfg {Boolean} hideLabel True to completely hide the label element (defaults to false) */ /** * @cfg {String} clearCls The CSS class used to provide field clearing (defaults to 'x-form-clear-left') */ /** * @cfg {String} itemCls An additional CSS class to apply to the wrapper's form item element of this field (defaults * to the container's itemCls value if set, or ''). Since it is applied to the item wrapper, it allows you to write * standard CSS rules that can apply to the field, the label (if specified) or any other element within the markup for * the field. NOTE: this will not have any effect on fields that are not part of a form. */ /** * @cfg {String} inputType The type attribute for this field -- this is required for all form fields * to render properly in a FormLayout as it does check this value to determine whether of not to render it. * 'image' is the default and only value for this property. */ inputType : 'image', /** * @cfg {Mixed} value A value to initialize this field with (defaults to ''). */ value : '', /** * @cfg {String} name The field's HTML name attribute (defaults to ""). */ name : '', /** * @cfg {String} cls A custom CSS class to apply to the field's underlying element (defaults to ""). */ /** * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-imagefield-invalid") */ invalidClass : "x-form-imagefield-invalid", /** * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided * (defaults to "The value in this field is invalid") */ invalidText : "This field is required", /** * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable automatic validation (defaults to false). */ validationEvent : 'change', /** * @cfg {Number} validationDelay The length of time in milliseconds after a validation event occurs until validation * is initiated (defaults to 250) */ validationDelay : 250, /** * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to * {tag: "input", type: "text", size: "20", autocomplete: "off"}) */ defaultAutoCreate : {tag: "div"}, /** * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-image") */ fieldClass : "x-form-imagefield", /** * @cfg {String} msgTarget The location where error text should display. Should be one of the following values * (defaults to 'qtip'): */ msgTarget : 'qtip', /** * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field * (defaults to 'normal'). */ msgFx : 'normal', /** * @cfg {Boolean} disabled True to disable the field (defaults to false). */ disabled : false, /** * @cfg {Boolean} optional True allow the image field to not have a value (value == '') * Set this to true when the image field is not required to be specified * (defaults to false) */ optional : false, /** * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false) */ hideTrigger : false, /** * @cfg {String} triggerClass A CSS class to apply to the trigger */ triggerClass : '', /** * @cfg {String} defaultImage The default image to display in the field (default to Ext.BLANK_IMAGE_URL) */ defaultImage: Ext.BLANK_IMAGE_URL, /** * @cfg {Number} browserWidth The width of the image browser window */ browserWidth: 300, /** * @cfg {Number} browserHeight The height of the image browser window */ browserHeight: 300, /** * @cfg {String} browserTitle The title of the image browser window */ browserTitle: '请选择图片', /** * @cfg {Boolean} alwaysLoadStore True reload the data store every time the image browser opens */ alwaysLoadStore: false, /** * @cfg {Object} windowConfig Additional configuration for the image browser window */ windowConfig: {}, /** * @cfg {Object} view The {Ext.DataView} of the image browser */ view: {}, /** * @cfg {String} valueField The data store field to return as the field's value */ valueField : 'url', // Private isStoreLoaded: false, // private isFormField : true, // Private selections: [], // Private selectedRecords: [], // private initComponent : function(){ Ext.ux.form.ImageField.superclass.initComponent.call(this); this.addEvents( /** * @event change * Fires if the field value has changed. * @param {Ext.ux.form.ImageField} this * @param {String} newValue The new value * @param {String} oldValue The original value */ 'change', /** * @event invalid * Fires after the field has been marked as invalid. * @param {Ext.ux.form.ImageField} this * @param {String} msg The validation message */ 'invalid', /** * @event valid * Fires after the field has been validated with no errors. * @param {Ext.ux.form.ImageField} this */ 'valid', /** * @event expand * Fires when the image browser is expanded * @param {Ext.ux.form.ImageField} this * @param {Ext.DataView} view The Ext.DataView of the image browser */ 'expand', /** * @event collapse * Fires when the image browser is collapsed * @param {Ext.ux.form.ImageField} this * @param {Ext.DataView} view The Ext.DataView of the image browser */ 'collapse' ); // if store was auto loaded, mark it as loaded if (this.view.store.autoLoad) { this.isStoreLoaded = true; } }, /** * Returns the name attribute of the field if available * @return {String} name The field name */ getName: function(){ return this.rendered && this.hiddenField.dom.name ? this.hiddenField.dom.name : ''; }, getSelectedRecords : function(){ this.selections = this.view.getSelectedIndexes(); this.selectedRecords = this.view.getSelectedRecords(); return this.selectedRecords; }, // private onRender : function(ct, position){ Ext.ux.form.ImageField.superclass.onRender.call(this, ct, position); if(!this.el){ var cfg = this.getAutoCreate(); this.el = ct.createChild(cfg, position); } this.imageEl = this.el.insertFirst({tag: 'img', src: this.defaultImage }); // create hidden field to hold the value for the image field this.hiddenField = this.imageEl.insertSibling({tag:'input', type:'hidden', name: this.name, id: this.id + '-hidden'}, 'before'); this.el.addClass([this.fieldClass, this.cls]); this.imageEl.addClass(this.fieldClass + '-image'); this.initValue(); // wrap it up this.wrap = this.imageEl.wrap({cls: "x-form-field-wrap"}); this.trigger = this.wrap.createChild({tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger"}); if(this.hideTrigger){ this.trigger.setDisplayed(false); } this.initTrigger(); }, // private initTrigger : function(){ this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true}); this.trigger.addClassOnOver('x-form-trigger-over'); this.trigger.addClassOnClick('x-form-trigger-click'); }, // private onDestroy : function(){ if(this.trigger){ this.trigger.removeAllListeners(); this.trigger.remove(); } this.wrap.remove(); Ext.ux.form.ImageField.superclass.onDestroy.call(this); }, // private onDisable : function(){ this.wrap.addClass('x-item-disabled'); this.hiddenField.dom.disabled = true; }, // private onEnable : function(){ this.wrap.removeClass('x-item-disabled'); this.hiddenField.dom.disabled = false; }, // private onShow : function(){ this.wrap.dom.style.display = ''; this.wrap.dom.style.visibility = 'visible'; }, // private onHide : function(){ this.wrap.dom.style.display = 'none'; }, // private onSelect: function(){ var selectedRecords = ''; var returnValue = (this.getSelectedRecords().length > 0) ? this.selectedRecords[0].get(this.valueField) : ''; if (returnValue !== this.value) { this.setValue(returnValue); } this.window.hide(); this.fireEvent('collapse', this, this.view); }, /** * The function that should handle the trigger's click event. This method does nothing by default until overridden * by an implementing function. * @method * @param {EventObject} e */ onTriggerClick : function(e){ if(this.disabled){ return; } // load the data store if (!this.isStoreLoaded) { this.view.store.load(); this.isStoreLoaded = true; } else if (this.alwaysLoadStore === true) { this.view.store.reload(); } // setup window with forced config this.windowConfig = Ext.apply(this.windowConfig, { title: this.browserTitle, width: this.browserWidth, height: this.browserHeight, draggable: false, resizable: false, closable: false, autoScroll: true, layout: 'fit', bbar: [{ text: '选择', handler: this.onSelect, scope: this },'->',{ text: '取消', handler: function(){ this.view.clearSelections(); this.window.hide(); this.fireEvent('collapse', this, this.view); }, scope: this }], items: this.view },{ shadow: false, frame: true }); // create the image browser window if(!this.window){ this.window = new Ext.Window(this.windowConfig); this.window.setPagePosition(this.trigger.getRight(), this.trigger.getTop()); this.view.on('dblclick', this.onSelect, this); } // show the image browser window this.window.show(); this.fireEvent('expand', this, this.view); }, // private initValue : function(){ if(this.value !== undefined){ this.hiddenField.dom.value = (this.value === null || this.value === undefined ? '' : this.value); } else { this.hiddenField.dom.value = ''; } }, /** * Returns true if this field has been changed since it was originally loaded and is not disabled. */ isDirty : function() { if(this.disabled) { return false; } return String(this.getValue()) !== String(this.originalValue); }, // private afterRender : function(){ Ext.ux.form.ImageField.superclass.afterRender.call(this); this.initEvents(); }, /** * Resets the current field value to the originally loaded value and clears any validation messages */ reset : function(){ this.setValue(this.originalValue); this.clearInvalid(); }, // private initEvents : function(){ if(this.validationEvent !== false){ this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay}); } // reference to original value for reset this.originalValue = this.getValue(); }, /** * Returns whether or not the field value is currently valid * @param {Boolean} preventMark True to disable marking the field invalid * @return {Boolean} True if the value is valid, else false */ isValid : function(preventMark){ if(this.disabled){ return true; } var restore = this.preventMark; this.preventMark = preventMark === true; var v = this.validateValue(this.processValue(this.getRawValue())); this.preventMark = restore; return v; }, /** * Validates the field value * @return {Boolean} True if the value is valid, else false */ validate : function(){ if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){ this.clearInvalid(); return true; } return false; }, // protected - should be overridden by subclasses if necessary to prepare raw values for validation processValue : function(value){ return value; }, // private validateValue : function(value){ if (this.hiddenField.dom.value === '') { this.markInvalid(); return false; } else { return true; } }, /** * Mark this field as invalid, using {@link #msgTarget} to determine how to display the error and * applying {@link #invalidClass} to the field's element. * @param {String} msg (optional) The validation message (defaults to {@link #invalidText}) */ markInvalid : function(msg){ if(!this.rendered || this.preventMark){ // not rendered return; } this.el.addClass(this.invalidClass); msg = msg || this.invalidText; switch(this.msgTarget){ case 'qtip': this.el.dom.qtip = msg; this.el.dom.qclass = 'x-form-invalid-tip'; if(Ext.QuickTips){ // fix for floating editors interacting with DND Ext.QuickTips.enable(); } break; case 'title': this.el.dom.title = msg; break; case 'under': if(!this.errorEl){ var elp = this.getErrorCt(); this.errorEl = elp.createChild({cls:'x-form-invalid-msg'}); this.errorEl.setWidth(elp.getWidth(true)-20); } this.errorEl.update(msg); Ext.ux.form.ImageField.msgFx[this.msgFx].show(this.errorEl, this); break; case 'side': if(!this.errorIcon){ var elp = this.getErrorCt(); this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'}); } this.alignErrorIcon(); this.errorIcon.dom.qtip = msg; this.errorIcon.dom.qclass = 'x-form-invalid-tip'; this.errorIcon.show(); this.on('resize', this.alignErrorIcon, this); break; default: var t = Ext.getDom(this.msgTarget); t.innerHTML = msg; t.style.display = this.msgDisplay; break; } this.fireEvent('invalid', this, msg); }, // private getErrorCt : function(){ return this.el.findParent('.x-form-element', 5, true) || // use form element wrap if available this.el.findParent('.x-form-field-wrap', 5, true); // else direct field wrap }, // private alignErrorIcon : function(){ this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]); }, /** * Clear any invalid styles/messages for this field */ clearInvalid : function(){ if(!this.rendered || this.preventMark){ // not rendered return; } this.el.removeClass(this.invalidClass); switch(this.msgTarget){ case 'qtip': this.el.dom.qtip = ''; break; case 'title': this.el.dom.title = ''; break; case 'under': if(this.errorEl){ Ext.ux.form.ImageField.msgFx[this.msgFx].hide(this.errorEl, this); } break; case 'side': if(this.errorIcon){ this.errorIcon.dom.qtip = ''; this.errorIcon.hide(); this.un('resize', this.alignErrorIcon, this); } break; default: var t = Ext.getDom(this.msgTarget); t.innerHTML = ''; t.style.display = 'none'; break; } this.fireEvent('valid', this); }, /** * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}. * @return {Mixed} value The field value */ getRawValue : function(){ var v = this.rendered ? this.hiddenField.getValue() : Ext.value(this.value, ''); return v; }, /** * Returns the normalized data value (undefined will be returned as ''). To return the raw value see {@link #getRawValue}. * @return {Mixed} value The field value */ getValue : function(){ if(!this.rendered) { return this.value; } var v = this.hiddenField.getValue(); if(v === undefined){ v = ''; } return v; }, /** * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}. * @param {Mixed} value The value to set */ setRawValue : function(v){ return this.hiddenField.dom.value = (v === null || v === undefined ? '' : v); }, /** * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}. * @param {Mixed} value The value to set */ setValue : function(v){ var original = this.value; this.value = v; if(this.rendered){ this.hiddenField.dom.value = (v === null || v === undefined ? '' : v); this.imageEl.dom.src = (v === null || v === undefined ? '' : v); this.fireEvent('change', this, original, v); this.validate(); } } }); // anything other than normal should be considered experimental Ext.ux.form.ImageField.msgFx = { normal : { show: function(msgEl, f){ msgEl.setDisplayed('block'); }, hide : function(msgEl, f){ msgEl.setDisplayed(false).update(''); } }, slide : { show: function(msgEl, f){ msgEl.slideIn('t', {stopFx:true}); }, hide : function(msgEl, f){ msgEl.slideOut('t', {stopFx:true,useDisplay:true}); } }, slideRight : { show: function(msgEl, f){ msgEl.fixDisplay(); msgEl.alignTo(f.el, 'tl-tr'); msgEl.slideIn('l', {stopFx:true}); }, hide : function(msgEl, f){ msgEl.slideOut('l', {stopFx:true,useDisplay:true}); } } }; Ext.reg('imagefield', Ext.ux.form.ImageField); ~~~ Ext.ux.ImageChooser.js ~~~ Ext.ux.ImageChooser = function(config) { this.config = config; this.initTemplates(); this.store = new Ext.data.JsonStore({ url: this.config.url, root: 'images', fields: [ 'name', 'url' ], listeners: { //'load': {fn:function(){ this.view.select(0); }, scope:this, single:true} } }); this.store.load(); this.view = new Ext.DataView({ tpl: this.thumbTemplate, singleSelect: true, overClass:'x-view-over', itemSelector: 'div.thumb-wrap', emptyText : '<div style="padding:10px;">没有图片,请上传</div>', store: this.store, listeners: { 'selectionchange': {fn:this.setHideValue, scope:this, buffer:100}, 'dblclick' : {fn:this.canelSelect, scope:this, buffer:100} } }); var cfg = { id: 'img-chooser-dlg', layout: 'border', border: false, items:[{ id: 'img-chooser-view', region: 'center', autoScroll: true, items: this.view }//,{xtype:'textfield',id: this.hideId} ] }; Ext.apply(cfg, this.config); this.hideId = this.config.hideId; Ext.ux.ImageChooser.superclass.constructor.call(this, cfg); if(this.hideId) this.add({xtype:'hidden',id: this.hideId}); }; Ext.extend(Ext.ux.ImageChooser, Ext.Panel, { initTemplates : function(){ this.thumbTemplate = new Ext.XTemplate( '<tpl for=".">', '<div class="thumb-wrap" id="{name}">', '<div class="thumb"><img src="{url}" title="{name}"></div>', '</div>',//<span>{shortName}</span> '</tpl>' ); this.thumbTemplate.compile(); }, setHideValue : function(){ var selNodes = this.view.getSelectedNodes(); var hideObj = Ext.getCmp(this.hideId); if(selNodes&&selNodes.length>0) hideObj.setValue(selNodes[0].id); }, setValue : function(value){ if(value) this.view.select(value); }, canelSelect : function(view,index,node,e){ view.deselect(index); var hideObj = Ext.getCmp(this.hideId); hideObj.setValue(''); } }); ~~~   Ext.hoo.form.FoodImageField.js ~~~ /** * @function 可以选择图片的field * @auhor: hoojo * @createDate: Sep 17, 2010 10:59:58 PM * @blog: blog.csdn.net/IBM_hoojo * @email: hoojo_@126.com * @class Ext.hoo.form.FoodImageField * @extends Ext.ux.ImageChooser */ Ext.ns("Ext.hoo.form"); Ext.hoo.form.FoodImageChooser = Ext.extend(Ext.ux.ImageChooser, { constructor: function () { Ext.hoo.form.FoodImageChooser.superclass.constructor.call(this, { renderTo: "show", hideId: 'iconImagesURL', //fieldLabel: '图标', url: 'images.json', height: 120, width: 225 }); } }); Ext.hoo.form.FoodImageField = Ext.extend(Ext.ux.form.ImageField, { constructor: function () { this.store = new Ext.data.JsonStore({ autoLoad: true, url: "images.json", root: 'images', fields: [ 'name', 'url' ], listeners: { //'load': {fn:function(){ this.view.select(0); }, scope:this, single:true} } }); this.tpl = new Ext.XTemplate( '<tpl for=".">', '<div class="thumb-wrap" id="{name}">', '<div class="thumb"><img src="{url}" title="{name}"></div>', '</div>',//<span>{shortName}</span> '</tpl>' ); this.view = new Ext.DataView({ singleSelect: true, emptyText : '<div style="padding:10px;">没有图片,请上传</div>', store: this.store, tpl: this.tpl }); Ext.hoo.form.FoodImageField.superclass.constructor.call(this, { renderTo: "showField", //fieldLabel: '图标', defaultImage: '../images/2.png', height: 120, width: 225 }); } }); Ext.onReady(function () { Ext.BLANK_IMAGE_URL = "../ext2/resources/images/default/s.gif"; Ext.QuickTips.init(); Ext.form.Field.prototype.msgTarget = "qtip"; //new Ext.hoo.form.FoodImageChooser(); //new Ext.hoo.form.FoodImageField(); var chooser, btn; function insertImage(data){ Ext.DomHelper.append('images', { tag: 'img', src: data.url, style:'margin:10px;visibility:hidden;' }, true).show(true).frame(); btn.focus(); }; function choose(btn){ if(!chooser){ chooser = new ImageChooser({ url:'images2.json', width:515, height:350 }); } chooser.show(btn.getEl(), insertImage); }; btn = new Ext.Button({ text: "Insert Image", handler: choose, renderTo: 'buttons' }); }); ~~~ Images.json 测试数据 ~~~ {images:[{ name:'水果1号',url:'../images/2.png' },{ name:'水果2号',url:'../images/2.png' },{ name:'水果3号',url:'../images/2.png' },{ name:'水果4号',url:'../images/2.png' },{ name:'水果5号',url:'../images/2.png' },{ name:'水果6号',url:'../images/2.png' }]} ~~~ Images2.json ~~~ {images:[{ name:'水果1号',url:'../images/2.png',size: 22.2, lastmod: 2009-06-05 },{ name:'水果2号',url:'../images/2.png',size: 52.2, lastmod: 2009-08-07 },{ name:'水果3号',url:'../images/2.png',size: 44.2, lastmod: 2009-03-06 },{ name:'水果4号',url:'../images/2.png',size: 25.7, lastmod: 2009-06-04 },{ name:'水果5号',url:'../images/2.png',size: 55.3, lastmod: 2010-06-22 },{ name:'水果6号',url:'../images/2.png',size: 77.8, lastmod: "2009-09-15" }]} ~~~  
';

ExtJS Form扩展组件[ColorFiled, DateTimeFiled, IconCombo, MultiComboBox, DynamicTreeCombox]

最后更新于:2022-04-01 12:01:26

支持Form颜色选择组件、日期时间选择组件、带图标的下拉列表、多选下来列表、动态下拉列表树等组件 开发环境: System:Windows WebBrowser:IE6+、Firefox3+ JavaEE Server:tomcat5.0.2.8、tomcat6 IDE:eclipse、MyEclipse 8 开发依赖库: JavaEE5、ext 2.2.2 Email:hoojo_@126.com Blog:[http://blog.csdn.net/IBM_hoojo](http://blog.csdn.net/IBM_hoojo) [http://hoojo.cnblogs.com/](http://hoojo.cnblogs.com/) ### 一、ColoFieldr组件 可以选择一些常用的颜色 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe1229e2976.gif) 需要用到的文件 ColorField组件文件:Ext.form.ColorField.js ColorField运行示例文件:Ext.hoo.for.ColorField.js 代码如下 Color.html ~~~ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>form Component -- DateTimeField</title> <meta http-equiv="pragma" content="no-cache"/> <meta http-equiv="cache-control" content="no-cache"/> <meta http-equiv="expires" content="0"/> <meta http-equiv="content-Type" content="text/html; charset=utf-8"/> <meta http-equiv="author" content="hoojo"/> <meta http-equiv="email" content="hoojo_@126.com"/> <meta http-equiv="ext-lib" content="version 2.2"/> <meta http-equiv="blog" content="http://blog.csdn.net/IBM_hoojo"/> <link rel="stylesheet" type="text/css" href="../ext2/resources/css/ext-all.css"/> <script type="text/javascript" src="../ext2/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="../ext2/ext-all.js"></script> <script type="text/javascript" src="../ext2/build/locale/ext-lang-zh_CN-min.js"></script> <script type="text/javascript" src="Ext.form.ColorField.js"></script> <script type="text/javascript" src="Ext.hoo.for.ColorField.js"></script> </head> <body> <div id="show" style="float: left; margin: 100px 0 0 100px;"></div> </body> </html> ~~~   Ext.form.ColorField.js ~~~ /** * @class Ext.form.ColorField * @extends Ext.form.TriggerField * Provides a very simple color form field with a ColorMenu dropdown. * Values are stored as a six-character hex value without the '#'. * I.e. 'ffffff' * @constructor * Create a new ColorField * <br />Example: * <pre><code> var cf = new Ext.form.ColorField({ fieldLabel: 'Color', hiddenName:'pref_sales', showHexValue:true }); </code></pre> * @param {Object} config */ Ext.form.ColorField = function(config){ Ext.form.ColorField.superclass.constructor.call(this, config); //this.on('render', this.handleRender); }; Ext.extend(Ext.form.ColorField, Ext.form.TriggerField, { /** * @cfg {Boolean} showHexValue * True to display the HTML Hexidecimal Color Value in the field * so it is manually editable. */ showHexValue : false, /** * @cfg {String} triggerClass * An additional CSS class used to style the trigger button. The trigger will always get the * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-color-trigger' * which displays a calendar icon). */ triggerClass : 'x-form-color-trigger', /** * @cfg {String/Object} autoCreate * A DomHelper element spec, or true for a default element spec (defaults to * {tag: "input", type: "text", size: "10", autocomplete: "off"}) */ // private defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off", maxlength:"6"}, /** * @cfg {String} lengthText * A string to be displayed when the length of the input field is * not 3 or 6, i.e. 'fff' or 'ffccff'. */ lengthText: "Color hex values must be either 3 or 6 characters.", //text to use if blank and allowBlank is false blankText: "Must have a hexidecimal value in the format ABCDEF.", /** * @cfg {String} color * A string hex value to be used as the default color. Defaults * to 'FFFFFF' (white). */ defaultColor: 'FFFFFF', maskRe: /[a-f0-9]/i, // These regexes limit input and validation to hex values regex: /[a-f0-9]/i, //private curColor: 'ffffff', initComponent:function(){ Ext.form.ColorField.superclass.initComponent.call(this); this.addEvents('click','change', 'select'); }, onChange: function (field, newVal, oldVal) { alert(newVal); }, onRender : function(ct, position){ Ext.form.ColorField.superclass.onRender.call(this, ct, position); this.handleRender(); }, // private validateValue : function(value){ if(!this.showHexValue) { return true; } if(value.length<1) { this.el.setStyle({ 'background-color':'#' + this.defaultColor }); if(!this.allowBlank) { this.markInvalid(String.format(this.blankText, value)); return false } return true; } if(value.length!=3 && value.length!=6 ) { this.markInvalid(String.format(this.lengthText, value)); return false; } this.setColor(value); return true; }, // private validateBlur : function(){ return !this.menu || !this.menu.isVisible(); }, // Manually apply the invalid line image since the background // was previously cleared so the color would show through. markInvalid : function( msg ) { Ext.form.ColorField.superclass.markInvalid.call(this, msg); this.el.setStyle({ 'background-image': 'url(../lib/resources/images/default/grid/invalid_line.gif)' }); }, /** * Returns the current color value of the color field * @return {String} value The hexidecimal color value */ getValue : function(){ return this.curValue || this.defaultValue || "FFFFFF"; }, /** * Sets the value of the color field. Format as hex value 'FFFFFF' * without the '#'. * @param {String} hex The color value */ setValue : function(hex){ Ext.form.ColorField.superclass.setValue.call(this, hex); this.setColor(hex); }, /** * Sets the current color and changes the background. * Does *not* change the value of the field. * @param {String} hex The color value. */ setColor : function(hex) { this.curColor = hex; this.el.setStyle( { 'background-color': '#' + hex, 'background-image': 'none' }); if(!this.showHexValue) { this.el.setStyle({ 'text-indent': '-100px' }); if(Ext.isIE) { this.el.setStyle({ 'margin-left': '100px' }); } } }, handleRender: function() { this.setDefaultColor(); }, setDefaultColor : function() { this.setValue(this.defaultColor); }, // private menuListeners : { select: function(m, d){ this.setValue(d); }, show : function(){ // retain focus styling this.onFocus(); }, hide : function(){ this.focus(); var ml = this.menuListeners; this.menu.un("select", ml.select, this); this.menu.un("show", ml.show, this); this.menu.un("hide", ml.hide, this); } }, //private handleSelect : function(palette, selColor) { this.setValue(selColor); this.fireEvent("click", this, selColor); this.fireEvent("change", this, selColor); this.fireEvent("select", this, selColor); }, // private // Implements the default empty TriggerField.onTriggerClick function to display the ColorPicker onTriggerClick : function(){ if(this.disabled){ return; } if(this.menu == null){ this.menu = new Ext.menu.ColorMenu(); this.menu.palette.on('select', this.handleSelect, this ); } this.menu.on(Ext.apply({}, this.menuListeners, { scope:this })); this.menu.show(this.el, "tl-bl?"); } }); ~~~   Ext.hoo.for.ColorField.js ~~~ /** * @function 颜色选择器 * @auhor: hoojo * @createDate: Sep 11, 2010 10:35:15 PM * @blog: blog.csdn.net/IBM_hoojo * @email: hoojo_@126.com * @class Ext.hoo.form.ColorField * @extends Ext.form.ColorField */ Ext.ns("Ext.hoo.form"); Ext.hoo.form.ColorField = Ext.extend(Ext.form.ColorField, { constructor: function () { Ext.hoo.form.ColorField.superclass.constructor.call(this, { //fieldLabel: "plase selected", renderTo: "show", width: 160, defaultColor: "00FF00", curColor: "00FF00", showHexValue: true, listeners: { "select": function (f, v) { alert(this.getValue() + "##" + v); } } }); } }); Ext.onReady(function () { Ext.BLANK_IMAGE_URL = "../ext2/resources/images/default/s.gif"; Ext.QuickTips.init(); Ext.form.Field.prototype.msgTarget = "qtip"; new Ext.hoo.form.ColorField(); }); ~~~ ### 二、日期时间选择控件 可以选择日期、时间 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe122a1a9ce.gif) 需要用到的文件 Spinner.js Spinner.css SpinnerField.js 日期时间组件:Ext.ux.form.DateTimeField.js 示例:Ext.hoo.form.DateTimeField.js 代码如下 dateTimeFieldExample.htm ~~~ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>form Component -- MultiComboBox</title> <meta http-equiv="pragma" content="no-cache"/> <meta http-equiv="cache-control" content="no-cache"/> <meta http-equiv="expires" content="0"/> <meta http-equiv="content-Type" content="text/html; charset=utf-8"/> <meta http-equiv="author" content="hoojo"/> <meta http-equiv="email" content="hoojo_@126.com"/> <meta http-equiv="ext-lib" content="version 3.2"/> <meta http-equiv="blog" content="http://blog.csdn.net/IBM_hoojo"/> <link rel="stylesheet" type="text/css" href="../ext/resources/css/ext-all.css"/> <link rel="stylesheet" type="text/css" href="../css/Spinner.css"/> <script type="text/javascript" src="../ext/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="../ext/ext-all.js"></script> <script type="text/javascript" src="../ext/build/locale/ext-lang-zh_CN-min.js"></script> <script type="text/javascript" src="Spinner.js"></script> <script type="text/javascript" src="SpinnerField.js"></script> <script type="text/javascript" src="Ext.ux.form.DateTimeField.js"></script> <script type="text/javascript" src="Ext.hoo.form.DateTimeField.js"></script> </head> <body> <div id="show" style="float: left; margin: 100px 0 0 100px;"></div> </body> </html> ~~~ Spinner.css ~~~ /*! * Ext JS Library 3.1.1 * Copyright(c) 2006-2010 Ext JS, LLC * licensing@extjs.com * http://www.extjs.com/license */ .x-form-spinner-proxy{ /*background-color:#ff00cc;*/ } .x-form-field-wrap .x-form-spinner-trigger { background:transparent url('../images/spinner.gif') no-repeat 0 0; } .x-form-field-wrap .x-form-spinner-overup{ background-position:-17px 0; } .x-form-field-wrap .x-form-spinner-clickup{ background-position:-34px 0; } .x-form-field-wrap .x-form-spinner-overdown{ background-position:-51px 0; } .x-form-field-wrap .x-form-spinner-clickdown{ background-position:-68px 0; } .x-trigger-wrap-focus .x-form-spinner-trigger{ background-position:-85px 0; } .x-trigger-wrap-focus .x-form-spinner-overup{ background-position:-102px 0; } .x-trigger-wrap-focus .x-form-spinner-clickup{ background-position:-119px 0; } .x-trigger-wrap-focus .x-form-spinner-overdown{ background-position:-136px 0; } .x-trigger-wrap-focus .x-form-spinner-clickdown{ background-position:-153px 0; } .x-trigger-wrap-focus .x-form-trigger{ border-bottom: 1px solid #7eadd9; } .x-form-field-wrap .x-form-spinner-splitter { line-height:1px; font-size:1px; background:transparent url('../images/spinner-split.gif') no-repeat 0 0; position:absolute; cursor: n-resize; } .x-trigger-wrap-focus .x-form-spinner-splitter{ background-position:-14px 0; } .x-date-bottom { font-size: 12px; } .x-date-menu { height: 200px; } ~~~ 图片 spinner.gif ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe122a492ab.gif) spinner-split.gif ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe122a668ab.gif) Spinner.js ~~~ /*! * Ext JS Library 3.1.1 * Copyright(c) 2006-2010 Ext JS, LLC * licensing@extjs.com * http://www.extjs.com/license */ /** * @class Ext.ux.Spinner * @extends Ext.util.Observable * Creates a Spinner control utilized by Ext.ux.form.SpinnerField */ Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { incrementValue: 1, alternateIncrementValue: 5, triggerClass: 'x-form-spinner-trigger', splitterClass: 'x-form-spinner-splitter', alternateKey: Ext.EventObject.shiftKey, defaultValue: 0, accelerate: false, constructor: function(config){ Ext.ux.Spinner.superclass.constructor.call(this, config); Ext.apply(this, config); this.mimicing = false; }, init: function(field){ this.field = field; field.afterMethod('onRender', this.doRender, this); field.afterMethod('onEnable', this.doEnable, this); field.afterMethod('onDisable', this.doDisable, this); field.afterMethod('afterRender', this.doAfterRender, this); field.afterMethod('onResize', this.doResize, this); field.afterMethod('onFocus', this.doFocus, this); field.beforeMethod('onDestroy', this.doDestroy, this); }, doRender: function(ct, position){ var el = this.el = this.field.getEl(); var f = this.field; if (!f.wrap) { f.wrap = this.wrap = el.wrap({ cls: "x-form-field-wrap" }); } else { this.wrap = f.wrap.addClass('x-form-field-wrap'); } this.trigger = this.wrap.createChild({ tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass }); if (!f.width) { this.wrap.setWidth(el.getWidth() + this.trigger.getWidth()); } this.splitter = this.wrap.createChild({ tag: 'div', cls: this.splitterClass, style: 'width:13px; height:2px;' }); this.splitter.setRight((Ext.isIE) ? 1 : 2).setTop(10).show(); this.proxy = this.trigger.createProxy('', this.splitter, true); this.proxy.addClass("x-form-spinner-proxy"); this.proxy.setStyle('left', '0px'); this.proxy.setSize(14, 1); this.proxy.hide(); this.dd = new Ext.dd.DDProxy(this.splitter.dom.id, "SpinnerDrag", { dragElId: this.proxy.id }); this.initTrigger(); this.initSpinner(); }, doAfterRender: function(){ var y; if (Ext.isIE && this.el.getY() != (y = this.trigger.getY())) { this.el.position(); this.el.setY(y); } }, doEnable: function(){ if (this.wrap) { this.wrap.removeClass(this.field.disabledClass); } }, doDisable: function(){ if (this.wrap) { this.wrap.addClass(this.field.disabledClass); this.el.removeClass(this.field.disabledClass); } }, doResize: function(w, h){ if (typeof w == 'number') { this.el.setWidth(w - this.trigger.getWidth()); } this.wrap.setWidth(this.el.getWidth() + this.trigger.getWidth()); }, doFocus: function(){ if (!this.mimicing) { this.wrap.addClass('x-trigger-wrap-focus'); this.mimicing = true; Ext.get(Ext.isIE ? document.body : document).on("mousedown", this.mimicBlur, this, { delay: 10 }); this.el.on('keydown', this.checkTab, this); } }, // private checkTab: function(e){ if (e.getKey() == e.TAB) { this.triggerBlur(); } }, // private mimicBlur: function(e){ if (!this.wrap.contains(e.target) && this.field.validateBlur(e)) { this.triggerBlur(); } }, // private triggerBlur: function(){ this.mimicing = false; Ext.get(Ext.isIE ? document.body : document).un("mousedown", this.mimicBlur, this); this.el.un("keydown", this.checkTab, this); this.field.beforeBlur(); this.wrap.removeClass('x-trigger-wrap-focus'); this.field.onBlur.call(this.field); }, initTrigger: function(){ this.trigger.addClassOnOver('x-form-trigger-over'); this.trigger.addClassOnClick('x-form-trigger-click'); }, initSpinner: function(){ this.field.addEvents({ 'spin': true, 'spinup': true, 'spindown': true }); this.keyNav = new Ext.KeyNav(this.el, { "up": function(e){ e.preventDefault(); this.onSpinUp(); }, "down": function(e){ e.preventDefault(); this.onSpinDown(); }, "pageUp": function(e){ e.preventDefault(); this.onSpinUpAlternate(); }, "pageDown": function(e){ e.preventDefault(); this.onSpinDownAlternate(); }, scope: this }); this.repeater = new Ext.util.ClickRepeater(this.trigger, { accelerate: this.accelerate }); this.field.mon(this.repeater, "click", this.onTriggerClick, this, { preventDefault: true }); this.field.mon(this.trigger, { mouseover: this.onMouseOver, mouseout: this.onMouseOut, mousemove: this.onMouseMove, mousedown: this.onMouseDown, mouseup: this.onMouseUp, scope: this, preventDefault: true }); this.field.mon(this.wrap, "mousewheel", this.handleMouseWheel, this); this.dd.setXConstraint(0, 0, 10) this.dd.setYConstraint(1500, 1500, 10); this.dd.endDrag = this.endDrag.createDelegate(this); this.dd.startDrag = this.startDrag.createDelegate(this); this.dd.onDrag = this.onDrag.createDelegate(this); }, onMouseOver: function(){ if (this.disabled) { return; } var middle = this.getMiddle(); this.tmpHoverClass = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-overup' : 'x-form-spinner-overdown'; this.trigger.addClass(this.tmpHoverClass); }, //private onMouseOut: function(){ this.trigger.removeClass(this.tmpHoverClass); }, //private onMouseMove: function(){ if (this.disabled) { return; } var middle = this.getMiddle(); if (((Ext.EventObject.getPageY() > middle) && this.tmpHoverClass == "x-form-spinner-overup") || ((Ext.EventObject.getPageY() < middle) && this.tmpHoverClass == "x-form-spinner-overdown")) { } }, //private onMouseDown: function(){ if (this.disabled) { return; } var middle = this.getMiddle(); this.tmpClickClass = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-clickup' : 'x-form-spinner-clickdown'; this.trigger.addClass(this.tmpClickClass); }, //private onMouseUp: function(){ this.trigger.removeClass(this.tmpClickClass); }, //private onTriggerClick: function(){ if (this.disabled || this.el.dom.readOnly) { return; } var middle = this.getMiddle(); var ud = (Ext.EventObject.getPageY() < middle) ? 'Up' : 'Down'; this['onSpin' + ud](); }, //private getMiddle: function(){ var t = this.trigger.getTop(); var h = this.trigger.getHeight(); var middle = t + (h / 2); return middle; }, //private //checks if control is allowed to spin isSpinnable: function(){ if (this.disabled || this.el.dom.readOnly) { Ext.EventObject.preventDefault(); //prevent scrolling when disabled/readonly return false; } return true; }, handleMouseWheel: function(e){ //disable scrolling when not focused if (this.wrap.hasClass('x-trigger-wrap-focus') == false) { return; } var delta = e.getWheelDelta(); if (delta > 0) { this.onSpinUp(); e.stopEvent(); } else if (delta < 0) { this.onSpinDown(); e.stopEvent(); } }, //private startDrag: function(){ this.proxy.show(); this._previousY = Ext.fly(this.dd.getDragEl()).getTop(); }, //private endDrag: function(){ this.proxy.hide(); }, //private onDrag: function(){ if (this.disabled) { return; } var y = Ext.fly(this.dd.getDragEl()).getTop(); var ud = ''; if (this._previousY > y) { ud = 'Up'; } //up if (this._previousY < y) { ud = 'Down'; } //down if (ud != '') { this['onSpin' + ud](); } this._previousY = y; }, //private onSpinUp: function(){ if (this.isSpinnable() == false) { return; } if (Ext.EventObject.shiftKey == true) { this.onSpinUpAlternate(); return; } else { this.spin(false, false); } this.field.fireEvent("spin", this); this.field.fireEvent("spinup", this); }, //private onSpinDown: function(){ if (this.isSpinnable() == false) { return; } if (Ext.EventObject.shiftKey == true) { this.onSpinDownAlternate(); return; } else { this.spin(true, false); } this.field.fireEvent("spin", this); this.field.fireEvent("spindown", this); }, //private onSpinUpAlternate: function(){ if (this.isSpinnable() == false) { return; } this.spin(false, true); this.field.fireEvent("spin", this); this.field.fireEvent("spinup", this); }, //private onSpinDownAlternate: function(){ if (this.isSpinnable() == false) { return; } this.spin(true, true); this.field.fireEvent("spin", this); this.field.fireEvent("spindown", this); }, spin: function(down, alternate){ var v = parseFloat(this.field.getValue()); var incr = (alternate == true) ? this.alternateIncrementValue : this.incrementValue; (down == true) ? v -= incr : v += incr; v = (isNaN(v)) ? this.defaultValue : v; v = this.fixBoundries(v); this.field.setRawValue(v); }, fixBoundries: function(value){ var v = value; if (this.field.minValue != undefined && v < this.field.minValue) { v = this.field.minValue; } if (this.field.maxValue != undefined && v > this.field.maxValue) { v = this.field.maxValue; } return this.fixPrecision(v); }, // private fixPrecision: function(value){ var nan = isNaN(value); if (!this.field.allowDecimals || this.field.decimalPrecision == -1 || nan || !value) { return nan ? '' : value; } return parseFloat(parseFloat(value).toFixed(this.field.decimalPrecision)); }, doDestroy: function(){ if (this.trigger) { this.trigger.remove(); } if (this.wrap) { this.wrap.remove(); delete this.field.wrap; } if (this.splitter) { this.splitter.remove(); } if (this.dd) { this.dd.unreg(); this.dd = null; } if (this.proxy) { this.proxy.remove(); } if (this.repeater) { this.repeater.purgeListeners(); } } }); //backwards compat Ext.form.Spinner = Ext.ux.Spinner; ~~~ SpinnerField.js ~~~ /*! * Ext JS Library 3.1.1 * Copyright(c) 2006-2010 Ext JS, LLC * licensing@extjs.com * http://www.extjs.com/license */ Ext.ns('Ext.ux.form'); /** * @class Ext.ux.form.SpinnerField * @extends Ext.form.NumberField * Creates a field utilizing Ext.ux.Spinner * @xtype spinnerfield */ Ext.ux.form.SpinnerField = Ext.extend(Ext.form.NumberField, { actionMode: 'wrap', deferHeight: true, autoSize: Ext.emptyFn, onBlur: Ext.emptyFn, adjustSize: Ext.BoxComponent.prototype.adjustSize, constructor: function(config) { var spinnerConfig = Ext.copyTo({}, config, 'incrementValue,alternateIncrementValue,accelerate,defaultValue,triggerClass,splitterClass'); var spl = this.spinner = new Ext.ux.Spinner(spinnerConfig); var plugins = config.plugins ? (Ext.isArray(config.plugins) ? config.plugins.push(spl) : [config.plugins, spl]) : spl; Ext.ux.form.SpinnerField.superclass.constructor.call(this, Ext.apply(config, {plugins: plugins})); }, // private getResizeEl: function(){ return this.wrap; }, // private getPositionEl: function(){ return this.wrap; }, // private alignErrorIcon: function(){ if (this.wrap) { this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]); } }, validateBlur: function(){ return true; } }); Ext.reg('spinnerfield', Ext.ux.form.SpinnerField); //backwards compat Ext.form.SpinnerField = Ext.ux.form.SpinnerField; ~~~ Ext.ux.form.DateTimeField.js ~~~ Ext.ns('Ext.ux.form'); Ext.ux.form.TimePickerField = function(config){ Ext.ux.form.TimePickerField.superclass.constructor.call(this, config); } Ext.extend(Ext.ux.form.TimePickerField, Ext.form.Field, { defaultAutoCreate: { tag: 'div' }, cls: 'x-form-timepickerfield', hoursSpinner: null, minutesSpinner: null, secondsSpinner: null, spinnerCfg: { width: 40 }, spinnerFixBoundries: function(value){ if (value < this.field.minValue) { value = this.field.maxValue; } if (value > this.field.maxValue) { value = this.field.minValue; } return this.fixPrecision(value); }, onRender: function(ct, position){ Ext.ux.form.TimePickerField.superclass.onRender.call(this, ct, position); this.rendered = false; this.date = new Date(); var values = {}; if (this.value) { values = this._valueSplit(this.value); this.date.setHours(values.h); this.date.setMinutes(values.m); this.date.setSeconds(values.s); delete this.value; } else { values = { h: this.date.getHours(), m: this.date.getMinutes(), s: this.date.getSeconds() }; } var spinnerWrap = Ext.DomHelper.append(this.el, { tag: 'div' }); var cfg = Ext.apply({}, this.spinnerCfg, { renderTo: spinnerWrap, readOnly: this.readOnly, disabled: this.disabled, listeners: { spin: { fn: this.onSpinnerChange, scope: this }, valid: { fn: this.onSpinnerChange, scope: this }, afterrender: { fn: function(spinner){ spinner.wrap.applyStyles('float: left'); }, single: true } } }); this.hoursSpinner = new Ext.ux.form.SpinnerField(Ext.apply({}, cfg, { minValue: 0, maxValue: 23, cls: 'first', value: values.h })); this.minutesSpinner = new Ext.ux.form.SpinnerField(Ext.apply({}, cfg, { minValue: 0, maxValue: 59, value: values.m })); this.secondsSpinner = new Ext.ux.form.SpinnerField(Ext.apply({}, cfg, { minValue: 0, maxValue: 59, value: values.s })); Ext.DomHelper.append(spinnerWrap, { tag: 'div', cls: 'x-form-clear-left' }); this.rendered = true; }, _valueSplit: function(v){ var split = v.split(':'); return { h: split.length > 0 ? split[0] : 0, m: split.length > 1 ? split[1] : 0, s: split.length > 2 ? split[2] : 0 }; }, onSpinnerChange: function(){ if (!this.rendered) { return; } this.fireEvent('change', this, this.getRawValue()); }, disable: function(){ Ext.ux.form.TimePickerField.superclass.disable.call(this); this.hoursSpinner.disable(); this.minutesSpinner.disable(); this.secondsSpinner.disable(); }, enable: function(){ Ext.ux.form.TimePickerField.superclass.enable.call(this); this.hoursSpinner.enable(); this.minutesSpinner.enable(); this.secondsSpinner.enable(); }, setReadOnly: function(r){ Ext.ux.form.TimePickerField.superclass.setReadOnly.call(this, r); this.hoursSpinner.setReadOnly(r); this.minutesSpinner.setReadOnly(r); this.secondsSpinner.setReadOnly(r); }, clearInvalid: function(){ Ext.ux.form.TimePickerField.superclass.clearInvalid.call(this); this.hoursSpinner.clearInvalid(); this.minutesSpinner.clearInvalid(); this.secondsSpinner.clearInvalid(); }, getRawValue: function(){ if (!this.hoursSpinner) { this.date = new Date(); return { h: this.date.getHours(), m: this.date.getMinutes(), s: this.date.getSeconds() }; } else { return { h: this.hoursSpinner.getValue(), m: this.minutesSpinner.getValue(), s: this.secondsSpinner.getValue() }; } }, setRawValue: function(v){ this.hoursSpinner.setValue(v.h); this.minutesSpinner.setValue(v.m); this.secondsSpinner.setValue(v.s); }, isValid: function(preventMark){ return this.hoursSpinner.isValid(preventMark) && this.minutesSpinner.isValid(preventMark) && this.secondsSpinner.isValid(preventMark); }, validate: function(){ return this.hoursSpinner.validate() && this.minutesSpinner.validate() && this.secondsSpinner.validate(); }, getValue: function(){ var v = this.getRawValue(); return String.leftPad(v.h, 2, '0') + ':' + String.leftPad(v.m, 2, '0') + ':' + String.leftPad(v.s, 2, '0'); }, setValue: function(value){ if (!this.rendered) { this.value = value; return; } value = this._valueSplit(value); this.setRawValue(value); this.validate(); } }); Ext.form.TimePickerField = Ext.ux.form.TimePickerField; Ext.reg('timepickerfield', Ext.form.TimePickerField); Ext.ns('Ext.ux.form'); Ext.DateTimePicker = Ext.extend(Ext.DatePicker, { timeFormat: 'g:i:s A', timeLabel: '时间', timeWidth: 100, initComponent: function(){ Ext.DateTimePicker.superclass.initComponent.call(this); this.id = Ext.id(); }, onRender: function(container, position){ Ext.DateTimePicker.superclass.onRender.apply(this, arguments); var table = Ext.get(Ext.DomQuery.selectNode('table tbody', container.dom)); var tfEl = Ext.DomHelper.insertBefore(table.last(), { tag: 'tr', children: [{ tag: 'td', cls: 'x-date-bottom', html: this.timeLabel, style: 'width:30;' }, { tag: 'td', cls: 'x-date-bottom ux-timefield', style: "height: 20px;", colspan: '2' }] }, true); this.tf.render(table.child('td.ux-timefield')); var p = this.getEl().parent('div.x-layer'); if (p) { p.setStyle("height", p.getHeight() + 31); } }, setValue: function(value){ var old = this.value; if (!this.tf) { this.tf = new Ext.ux.form.TimePickerField(); this.tf.ownerCt = this; } this.value = this.getDateTime(value); }, getDateTime: function(value){ if (this.tf) { var dt = new Date(); var timeval = this.tf.getValue(); value = Date.parseDate(value.format(this.dateFormat) + ' ' + this.tf.getValue(), this.format); } return value; }, selectToday: function(){ if (this.todayBtn && !this.todayBtn.disabled) { this.value = this.getDateTime(new Date()); this.fireEvent("select", this, this.value); } } }); Ext.reg('datetimepickerfield', Ext.DateTimePicker); if (parseInt(Ext.version.substr(0, 1), 10) > 2) { Ext.menu.DateTimeItem = Ext.DateTimePicker; Ext.override(Ext.menu.DateMenu, { initComponent: function(){ this.on('beforeshow', this.onBeforeShow, this); if (this.strict = (Ext.isIE7 && Ext.isStrict)) { this.on('show', this.onShow, this, { single: true, delay: 20 }); } Ext.apply(this, { plain: true, showSeparator: false, items: this.picker = new Ext.DatePicker(Ext.apply({ internalRender: this.strict || !Ext.isIE, ctCls: 'x-menu-date-item' }, this.initialConfig)) }); Ext.menu.DateMenu.superclass.initComponent.call(this); this.relayEvents(this.picker, ["select"]); this.on('select', this.menuHide, this); if (this.handler) { this.on('select', this.handler, this.scope || this); } } }); } else { Ext.menu.DateTimeItem = function(config){ Ext.menu.DateTimeItem.superclass.constructor.call(this, new Ext.DateTimePicker(config), config); this.picker = this.component; alert(this.picker); this.addEvents('select'); this.picker.on("render", function(picker){ picker.getEl().swallowEvent("click"); picker.container.addClass("x-menu-date-item"); }); this.picker.on("select", this.onSelect, this); }; Ext.extend(Ext.menu.DateTimeItem, Ext.menu.DateMenu, { onSelect: function(picker, date){ this.fireEvent("select", this, date, picker); Ext.menu.DateTimeItem.superclass.handleClick.call(this); } }); } Ext.menu.DateTimeMenu = function(config){ Ext.menu.DateTimeMenu.superclass.constructor.call(this, config); this.plain = true; var di = new Ext.menu.DateTimeItem(config); this.add(di); this.picker = di; this.relayEvents(di, ["select"]); this.on('beforeshow', function(){ if (this.picker) { this.picker.hideMonthPicker(true); } }, this); }; Ext.extend(Ext.menu.DateTimeMenu, Ext.menu.Menu, { cls: 'x-date-menu', beforeDestroy: function(){ this.picker.destroy(); }, hide: function(deep){ if (this.picker.tf.innerList) { if ((Ext.EventObject.within(this.picker.tf.innerList)) || (Ext.get(Ext.EventObject.getTarget()) == this.picker.tf.innerList)) return false; } if (this.el && this.isVisible()) { this.fireEvent("beforehide", this); if (this.activeItem) { this.activeItem.deactivate(); this.activeItem = null; } this.el.hide(); this.hidden = true; this.fireEvent("hide", this); } if (deep === true && this.parentMenu) { this.parentMenu.hide(true); } } }); Ext.ux.form.DateTimeField = Ext.extend(Ext.form.DateField, { dateFormat: 'Y-m-d', timeFormat: 'H:i:s', defaultAutoCreate: { tag: "input", type: "text", size: "20", autocomplete: "off" }, initComponent: function(){ Ext.ux.form.DateTimeField.superclass.initComponent.call(this); this.format = this.dateFormat + ' ' + this.timeFormat; this.afterMethod('afterRender', function(){ this.getEl().applyStyles('top:0'); }); }, getValue: function(){ return this.parseDate(Ext.form.DateField.superclass.getValue.call(this)) || ''; }, onTriggerClick: function(){ if (this.disabled) { return; } if (this.menu == null) { this.menu = new Ext.menu.DateTimeMenu(); } Ext.apply(this.menu.picker, { minDate: this.minValue, maxDate: this.maxValue, disabledDatesRE: this.ddMatch, disabledDatesText: this.disabledDatesText, disabledDays: this.disabledDays, disabledDaysText: this.disabledDaysText, format: this.format, timeFormat: this.timeFormat, dateFormat: this.dateFormat, showToday: this.showToday, minText: String.format(this.minText, this.formatDate(this.minValue)), maxText: String.format(this.maxText, this.formatDate(this.maxValue)) }); if (this.menuEvents) { this.menuEvents('on'); } else { this.menu.on(Ext.apply({}, this.menuListeners, { scope: this })); } this.menu.picker.setValue(this.getValue() || new Date()); this.menu.show(this.el, "tl-bl?"); } }); Ext.reg('datetimefield', Ext.ux.form.DateTimeField); ~~~ Ext.hoo.form.DateTimeField.js ~~~ /** * @function 可以选择日期的DateTimeField * @auhor: hoojo * @createDate: Sep 16, 2010 9:25:12 PM * @blog: blog.csdn.net/IBM_hoojo * @email: hoojo_@126.com * @class Ext.hoo.form.DateTimeField * @extends Ext.ux.form.DateTimeField */ Ext.ns("Ext.hoo.form"); Ext.hoo.form.DateTimeField = Ext.extend(Ext.ux.form.DateTimeField, { constructor: function () { Ext.hoo.form.DateTimeField.superclass.constructor.call(this, { renderTo: "show", height: 222, dateFormat: 'Y-m-d', timeFormat: 'H:i:s', listeners: { select: function () { alert(this.getValue() + "##" + this.getRawValue()); } } }); } }); Ext.onReady(function () { Ext.BLANK_IMAGE_URL = "../ext2/resources/images/default/s.gif"; Ext.QuickTips.init(); Ext.form.Field.prototype.msgTarget = "qtip"; new Ext.hoo.form.DateTimeField(); }); ~~~ ### 三、带图标的下列列表 效果图如下 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe122a8a7fa.gif) 需要的文件 combo_icon.gif iconCombobox 插件文件: Ext.ux.IconCombo.js 示例文件: Ext.hoo.form.WebBroserIconComboBox.js   代码如下 ~~~ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>form Component -- iconCombo</title> <meta http-equiv="pragma" content="no-cache"/> <meta http-equiv="cache-control" content="no-cache"/> <meta http-equiv="expires" content="0"/> <meta http-equiv="content-Type" content="text/html; charset=utf-8"/> <meta http-equiv="author" content="hoojo"/> <meta http-equiv="email" content="hoojo_@126.com"/> <meta http-equiv="ext-lib" content="version 2.2"/> <meta http-equiv="blog" content="http://blog.csdn.net/IBM_hoojo"/> <link rel="stylesheet" type="text/css" href="../ext2/resources/css/ext-all.css"/> <link rel="stylesheet" type="text/css" href="../css/iconCombo.css"/> <style type="text/css"> .ffCls { background: url(../images/combo_icon.gif) no-repeat 1px -15px; } .ieCls { background: url(../images/combo_icon.gif) no-repeat 1px 2px; } .operaCls { background: url(../images/combo_icon.gif) no-repeat 1px -68px; } .chromeCls { background: url(../images/combo_icon.gif) no-repeat 1px -85px; } .maxCls { background: url(../images/combo_icon.gif) no-repeat 1px -32px; } .ttCls { background: url(../images/combo_icon.gif) no-repeat 1px -50px; } .sfCls { background: url(../images/combo_icon.gif) no-repeat 1px -103px; } .twCls { background: url(../images/combo_icon.gif) no-repeat 1px -121px; } .flockCls { background: url(../images/combo_icon.gif) no-repeat 1px -139px; } </style> <script type="text/javascript" src="../ext2/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="../ext2/ext-all.js"></script> <script type="text/javascript" src="../ext2/build/locale/ext-lang-zh_CN-min.js"></script> <script type="text/javascript" src="Ext.ux.IconCombo.js"></script> <script type="text/javascript" src="Ext.hoo.form.WebBroserIconComboBox.js"></script> </head> <body> <div id="show" style="padding: 100px 0 0 300px;"></div> </body> </html> ~~~   Ext.ux.IconCombo.js 文件代码 ~~~ /** * @class Ext.ux.IconCombo * @extends Ext.form.ComboBox * @param {Object} config Configuration options */ Ext.ux.IconCombo = function(config) { // call parent constructor Ext.ux.IconCombo.superclass.constructor.call(this, config); this.tpl = config.tpl || '<tpl for="."><div class="x-combo-list-item">' + '<table><tbody><tr>' + '<td>' + '<div class="{' + this.iconClsField + '}"></div></td>' + '<td>{' + this.displayField + '}</td>' + '</tr></tbody></table>' + '</div></tpl>'; }; Ext.extend(Ext.ux.IconCombo, Ext.form.ComboBox, { defaultIconCls: "ux-icon-combo-icon", emptyText: "plase selected", setIconCls : function() { var record = this.store.query(this.valueField, this.getValue()).itemAt(0); if (record) { this.flag.className = record.get(this.iconClsField) || defaultIconCls || "ux-icon-combo-icon"; //this.flag.style.backgroundImage = rec.get(this.iconClsField); } }, setValue : function(value) { Ext.ux.IconCombo.superclass.setValue.call(this, value); this.setIconCls(); }, onRender: function(ct, position) { Ext.ux.IconCombo.superclass.onRender.call(this, ct, position); var wrap = this.el.up("div.x-form-field-wrap"); this.wrap.applyStyles({ position : "relative" }); this.el.addClass("ux-icon-combo-input"); //textfield中显示的图片div this.flag = Ext.DomHelper.append(wrap, { tag : "div", style : "position:absolute; left: 5px; top: 1px;" }); //默认图片样式 this.flag.className = this.defaultIconCls; } }); ~~~ Ext.hoo.form.WebBroserIconComboBox.js ~~~ /** * @function 带图片的combobox * @auhor: hoojo * @createDate: Sep 10, 2010 11:16:12 PM * @blog: blog.csdn.net/IBM_hoojo * @email: hoojo_@126.com * @class Ext.hoo.form.WebBroserIconComboBox * @extends Ext.ux.IconCombo */ Ext.ns("Ext.hoo.form"); Ext.hoo.form.WebBroserIconComboBox = Ext.extend(Ext.ux.IconCombo, { constructor: function () { Ext.hoo.form.WebBroserIconComboBox.superclass.constructor.call(this, { /*fieldLabel: "web broser type", labelWidth: 73,*/ renderTo: /*Ext.getBody(),*/"show", width: 200, store: new Ext.data.SimpleStore({ fields: ["shortName", "name", "iconCls"], data: [ ["ff", "Mozilla Firefox" , "ffCls"], ["IE", "Internet Explorer" , "IECls"], ["opera", "Opera" , "operaCls"], ["chrome", "Google Chrome" , "chromeCls"], ["max", "Maxthon" , "maxCls"], ["tt", "TecentTraveler" , "ttCls"], ["sf", "Safari" , "sfCls"], ["tw", "TheWorld" , "twCls"], ["flock", "Flock" , "flockCls"] ] }), valueField: "shortName", displayField: "name", iconClsField: "iconCls", triggerAction: "all", editable: false, mode: "local" }); this.on("select", function (field, e) { alert(field.getValue() + "###" + field.getRawValue()); }, this); } }); Ext.onReady(function () { Ext.BLANK_IMAGE_URL = "../ext2/resources/images/default/s.gif"; Ext.QuickTips.init(); Ext.form.Field.prototype.msgTarget = "qtip"; new Ext.hoo.form.WebBroserIconComboBox(); }); ~~~ ### 四、多选下列列表组件 可以同时选择多个选项 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe122aab618.gif) 核心文件 多选下拉列表组件:Ext.form.MultiComboBox.js 示例文件:Ext.hoo.form.InterestMultiComboBox.js   代码如下 ~~~ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>form Component -- MultiComboBox</title> <meta http-equiv="pragma" content="no-cache"/> <meta http-equiv="cache-control" content="no-cache"/> <meta http-equiv="expires" content="0"/> <meta http-equiv="content-Type" content="text/html; charset=utf-8"/> <meta http-equiv="author" content="hoojo"/> <meta http-equiv="email" content="hoojo_@126.com"/> <meta http-equiv="ext-lib" content="version 2.2"/> <meta http-equiv="blog" content="http://blog.csdn.net/IBM_hoojo"/> <link rel="stylesheet" type="text/css" href="../ext2/resources/css/ext-all.css"/> <style type="text/css"> .checked{ background-image: url("../ext2/resources/images/default/menu/checked.gif"); } .unchecked{ background-image: url("../ext2/resources/images/default/menu/unchecked.gif"); } .x-combo-list-item { line-height: 18px; } </style> <script type="text/javascript" src="../ext2/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="../ext2/ext-all.js"></script> <script type="text/javascript" src="../ext2/build/locale/ext-lang-zh_CN-min.js"></script> <script type="text/javascript" src="Ext.form.MultiComboBox.js"></script> <script type="text/javascript" src="Ext.hoo.form.InterestMultiComboBox.js"></script> </head> <body> <center> <div id="show" style="padding-top: 200px;"></div> </center> </body> </html> ~~~   Ext.form.MultiComboBox.js ~~~ Ext.form.MultiComboBox = Ext.extend(Ext.form.TriggerField, { defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"}, listClass: '', selectedClass: 'x-combo-selected', triggerClass : 'x-form-arrow-trigger', shadow:'sides', listAlign: 'tl-bl?', maxHeight: 300, triggerAction: 'query', minChars : 4, typeAhead: false, queryDelay: 500, pageSize: 0, selectOnFocus: false, queryParam: 'query', loadingText: 'Loading...', resizable: false, handleHeight : 8, editable: true, allQuery: '', mode: 'remote', minListWidth : 70, forceSelection:false, typeAheadDelay : 250, displaySeparator:';', valueSeparator:',', lazyInit : true, initComponent : function(){ Ext.form.ComboBox.superclass.initComponent.call(this); this.addEvents( 'expand', 'collapse', 'beforeselect', 'select', 'beforequery' ); if(this.transform){ this.allowDomMove = false; var s = Ext.getDom(this.transform); if(!this.hiddenName){ this.hiddenName = s.name; } if(!this.store){ this.mode = 'local'; var d = [], opts = s.options; for(var i = 0, len = opts.length;i < len; i++){ var o = opts[i]; var value = (Ext.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text; if(o.selected) { this.value = value; } d.push([value, o.text]); } this.store = new Ext.data.SimpleStore({ 'id': 0, fields: ['value', 'text'], data : d }); this.valueField = 'value'; this.displayField = 'text'; } s.name = Ext.id(); // wipe out the name in case somewhere else they have a reference if(!this.lazyRender){ this.target = true; this.el = Ext.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate); Ext.removeNode(s); // remove it this.render(this.el.parentNode); }else{ Ext.removeNode(s); // remove it } } this.selectedIndex = -1; if(this.mode == 'local'){ if(this.initialConfig.queryDelay === undefined){ this.queryDelay = 10; } if(this.initialConfig.minChars === undefined){ this.minChars = 0; } } }, // private onRender : function(ct, position){ Ext.form.ComboBox.superclass.onRender.call(this, ct, position); var disValue=""; if(this.hiddenName){ this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)}, 'before', true); var hvalue=this.hiddenValue !== undefined ? this.hiddenValue : this.value !== undefined ? this.value : ''; var hvalueArray=hvalue.split(this.valueSeparator); for(var i=0;i<this.store.data.length;i++){ var r = this.store.getAt(i); var newValue = r.data[this.displayField]; var v=r.data[this.valueField]; for(var j=0;j<hvalueArray.length;j++){ if(hvalueArray[j]==v){ disValue+=newValue+this.displaySeparator; } } } this.hiddenField.value =this.hiddenValue !== undefined ? this.hiddenValue : this.value !== undefined ? this.value : ''; this.el.dom.removeAttribute('name'); } if(Ext.isGecko){ this.el.dom.setAttribute('autocomplete', 'off'); } if(!this.lazyInit){ this.initList(); }else{ this.on('focus', this.initList, this, {single: true}); } if(!this.editable){ this.editable = true; this.setEditable(false); } this.setValue(disValue); }, initList : function(){ if(!this.list){ var cls = 'x-combo-list'; this.list = new Ext.Layer({ shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false }); var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth); this.list.setWidth(lw); this.list.swallowEvent('mousewheel'); this.assetHeight = 0; if(this.title){ this.header = this.list.createChild({cls:cls+'-hd', html: this.title}); this.assetHeight += this.header.getHeight(); } this.innerList = this.list.createChild({cls:cls+'-inner'}); this.innerList.on('mouseover', this.onViewOver, this); this.innerList.on('mousemove', this.onViewMove, this); this.innerList.setWidth(lw - this.list.getFrameWidth('lr')) if(this.pageSize){ this.footer = this.list.createChild({cls:cls+'-ft'}); this.pageTb = new Ext.PagingToolbar({ store:this.store, pageSize: this.pageSize, renderTo:this.footer }); this.assetHeight += this.footer.getHeight(); } if(!this.tpl){ //alert(cls); //x-combo-list-item this.tpl = '<tpl for="."><div class="'+cls+'-item"><span class="unchecked" style="padding-bottom: 1px;" id="checkBox_{' + this.displayField + '}">    </span>{' + this.displayField + '}</div></tpl>'; } this.view = new Ext.DataView({ applyTo: this.innerList, tpl: this.tpl, singleSelect: true, selectedClass: this.selectedClass, itemSelector: this.itemSelector || '.' + cls + '-item' }); this.view.on('click', this.onViewClick, this); this.bindStore(this.store, true); if(this.resizable){ this.resizer = new Ext.Resizable(this.list, { pinned:true, handles:'se' }); this.resizer.on('resize', function(r, w, h){ this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight; this.listWidth = w; this.innerList.setWidth(w - this.list.getFrameWidth('lr')); this.restrictHeight(); }, this); this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px'); } } }, bindStore : function(store, initial){ if(this.store && !initial){ this.store.un('beforeload', this.onBeforeLoad, this); this.store.un('load', this.onLoad, this); this.store.un('loadexception', this.collapse, this); if(!store){ this.store = null; if(this.view){ this.view.setStore(null); } } } if(store){ this.store = Ext.StoreMgr.lookup(store); this.store.on('beforeload', this.onBeforeLoad, this); this.store.on('load', this.onLoad, this); this.store.on('loadexception', this.collapse, this); if(this.view){ this.view.setStore(store); } } }, // private initEvents : function(){ Ext.form.ComboBox.superclass.initEvents.call(this); this.keyNav = new Ext.KeyNav(this.el, { "up" : function(e){ this.inKeyMode = true; this.selectPrev(); }, "down" : function(e){ if(!this.isExpanded()){ this.onTriggerClick(); }else{ this.inKeyMode = true; this.selectNext(); } }, "enter" : function(e){ this.onViewClick(); //return true; }, "esc" : function(e){ this.collapse(); }, "tab" : function(e){ this.onViewClick(false); return true; }, scope : this, doRelay : function(foo, bar, hname){ if(hname == 'down' || this.scope.isExpanded()){ return Ext.KeyNav.prototype.doRelay.apply(this, arguments); } return true; }, forceKeyDown : true }); this.queryDelay = Math.max(this.queryDelay || 10, this.mode == 'local' ? 10 : 250); this.dqTask = new Ext.util.DelayedTask(this.initQuery, this); if(this.typeAhead){ this.taTask = new Ext.util.DelayedTask(this.onTypeAhead, this); } if(this.editable !== false){ this.el.on("keyup", this.onKeyUp, this); } if(this.forceSelection){ this.on('blur', this.doForce, this); } }, onDestroy : function(){ if(this.view){ this.view.el.removeAllListeners(); this.view.el.remove(); this.view.purgeListeners(); } if(this.list){ this.list.destroy(); } this.bindStore(null); Ext.form.ComboBox.superclass.onDestroy.call(this); }, // private fireKey : function(e){ if(e.isNavKeyPress() && !this.list.isVisible()){ this.fireEvent("specialkey", this, e); } }, // private onResize: function(w, h){ Ext.form.ComboBox.superclass.onResize.apply(this, arguments); if(this.list && this.listWidth === undefined){ var lw = Math.max(w, this.minListWidth); this.list.setWidth(lw); this.innerList.setWidth(lw - this.list.getFrameWidth('lr')); } }, // private onDisable: function(){ Ext.form.ComboBox.superclass.onDisable.apply(this, arguments); if(this.hiddenField){ this.hiddenField.disabled = this.disabled; } }, setEditable : function(value){ if(value == this.editable){ return; } this.editable = value; if(!value){ this.el.dom.setAttribute('readOnly', true); this.el.on('mousedown', this.onTriggerClick, this); this.el.addClass('x-combo-noedit'); }else{ this.el.dom.setAttribute('readOnly', false); this.el.un('mousedown', this.onTriggerClick, this); this.el.removeClass('x-combo-noedit'); } }, // private onBeforeLoad : function(){ if(!this.hasFocus){ return; } this.innerList.update(this.loadingText ? '<div class="loading-indicator">'+this.loadingText+'</div>' : ''); this.restrictHeight(); this.selectedIndex = -1; }, // private onLoad : function(){ if(!this.hasFocus){ return; } if(this.store.getCount() > 0){ this.expand(); this.restrictHeight(); if(this.lastQuery == this.allQuery){ if(this.editable){ this.el.dom.select(); } if(!this.selectByValue(this.value, true)){ this.select(0, true); } }else{ this.selectNext(); if(this.typeAhead && this.lastKey != Ext.EventObject.BACKSPACE && this.lastKey != Ext.EventObject.DELETE){ this.taTask.delay(this.typeAheadDelay); } } }else{ this.onEmptyResults(); } }, // private onTypeAhead : function(){ if(this.store.getCount() > 0){ var r = this.store.getAt(0); var newValue = r.data[this.displayField]; var len = newValue.length; var selStart = this.getRawValue().length; if(selStart != len){ this.setRawValue(newValue); this.selectText(selStart, newValue.length); } } }, // private onSelect : function(record, index){ if(this.fireEvent('beforeselect', this, record, index) !== false){ var r = this.store.getAt(index); var newValue = r.data[this.displayField]; var check=document.getElementById("checkBox_"+newValue); if(check.className=="checked"){ check.className="unchecked" }else{ check.className="checked" } var value=""; var hiddenValue=""; for(var i=0;i<this.store.data.length;i++){ var r = this.store.getAt(i); newValue = r.data[this.displayField]; check=document.getElementById("checkBox_"+newValue); if(check.className=="checked"){ value+= r.data[this.displayField]+this.displaySeparator; hiddenValue+= r.data[this.valueField]+this.valueSeparator; } } if(value.length>1){ value=value.substring(0,value.length-this.displaySeparator.length); } if(hiddenValue.length>1){ hiddenValue=hiddenValue.substring(0,value.length-this.valueSeparator.length); } this.setValue(value); this.hiddenField.value=hiddenValue; this.fireEvent('select', this, record, index); } }, getValue : function(){ if(this.valueField){ return typeof this.value != 'undefined' ? this.value : ''; }else{ return Ext.form.ComboBox.superclass.getValue.call(this); } }, /** * Clears any text/value currently set in the field */ clearValue : function(){ if(this.hiddenField){ this.hiddenField.value = ''; } this.setRawValue(''); this.lastSelectionText = ''; this.applyEmptyText(); }, setValue : function(v){ var text = v; if(this.valueField){ var r = this.findRecord(this.valueField, v); if(r){ text = r.data[this.displayField]; }else if(this.valueNotFoundText !== undefined){ text = this.valueNotFoundText; } } this.lastSelectionText = text; Ext.form.ComboBox.superclass.setValue.call(this, text); this.value = v; }, // private findRecord : function(prop, value){ var record; if(this.store.getCount() > 0){ this.store.each(function(r){ if(r.data[prop] == value){ record = r; return false; } }); } return record; }, // private onViewMove : function(e, t){ this.inKeyMode = false; }, // private onViewOver : function(e, t){ if(this.inKeyMode){ // prevent key nav and mouse over conflicts return; } var item = this.view.findItemFromChild(t); if(item){ var index = this.view.indexOf(item); this.select(index, false); } }, // private onViewClick : function(doFocus){ var index = this.view.getSelectedIndexes()[0]; var r = this.store.getAt(index); if(r){ this.onSelect(r, index); } if(doFocus !== false){ this.el.focus(); } }, // private restrictHeight : function(){ this.innerList.dom.style.height = ''; var inner = this.innerList.dom; var fw = this.list.getFrameWidth('tb'); var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight); this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight); this.list.beginUpdate(); this.list.setHeight(this.innerList.getHeight()+fw+(this.resizable?this.handleHeight:0)+this.assetHeight); this.list.alignTo(this.el, this.listAlign); this.list.endUpdate(); }, // private onEmptyResults : function(){ this.collapse(); }, /** * Returns true if the dropdown list is expanded, else false. */ isExpanded : function(){ return this.list && this.list.isVisible(); }, selectByValue : function(v, scrollIntoView){ if(v !== undefined && v !== null){ var r = this.findRecord(this.valueField || this.displayField, v); if(r){ this.select(this.store.indexOf(r), scrollIntoView); return true; } } return false; }, select : function(index, scrollIntoView){ this.selectedIndex = index; this.view.select(index); if(scrollIntoView !== false){ var el = this.view.getNode(index); if(el){ this.innerList.scrollChildIntoView(el, false); } } }, // private selectNext : function(){ var ct = this.store.getCount(); if(ct > 0){ if(this.selectedIndex == -1){ this.select(0); }else if(this.selectedIndex < ct-1){ this.select(this.selectedIndex+1); } } }, // private selectPrev : function(){ var ct = this.store.getCount(); if(ct > 0){ if(this.selectedIndex == -1){ this.select(0); }else if(this.selectedIndex != 0){ this.select(this.selectedIndex-1); } } }, // private onKeyUp : function(e){ if(this.editable !== false && !e.isSpecialKey()){ this.lastKey = e.getKey(); this.dqTask.delay(this.queryDelay); } }, // private validateBlur : function(){ return !this.list || !this.list.isVisible(); }, // private initQuery : function(){ this.doQuery(this.getRawValue()); }, // private doForce : function(){ if(this.el.dom.value.length > 0){ this.el.dom.value = this.lastSelectionText === undefined ? '' : this.lastSelectionText; this.applyEmptyText(); } }, doQuery : function(q, forceAll){ if(q === undefined || q === null){ q = ''; } var qe = { query: q, forceAll: forceAll, combo: this, cancel:false }; if(this.fireEvent('beforequery', qe)===false || qe.cancel){ return false; } q = qe.query; forceAll = qe.forceAll; if(forceAll === true || (q.length >= this.minChars)){ if(this.lastQuery !== q){ this.lastQuery = q; if(this.mode == 'local'){ this.selectedIndex = -1; if(forceAll){ this.store.clearFilter(); }else{ this.store.filter(this.displayField, q); } this.onLoad(); }else{ this.store.baseParams[this.queryParam] = q; this.store.load({ params: this.getParams(q) }); this.expand(); } }else{ this.selectedIndex = -1; this.onLoad(); } } }, // private getParams : function(q){ var p = {}; //p[this.queryParam] = q; if(this.pageSize){ p.start = 0; p.limit = this.pageSize; } return p; }, /** * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion. */ collapse : function(){ if(!this.isExpanded()){ return; } this.list.hide(); Ext.getDoc().un('mousewheel', this.collapseIf, this); Ext.getDoc().un('mousedown', this.collapseIf, this); this.fireEvent('collapse', this); }, // private collapseIf : function(e){ if(!e.within(this.wrap) && !e.within(this.list)){ this.collapse(); } }, /** * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion. */ expand : function(){ if(this.isExpanded() || !this.hasFocus){ return; } this.list.alignTo(this.wrap, this.listAlign); var hvalueArray=this.hiddenField.value.split(this.valueSeparator); for(var i=0;i<this.store.data.length;i++){ var r = this.store.getAt(i); var newValue = r.data[this.displayField]; var v=r.data[this.valueField]; for(var j=0;j<hvalueArray.length;j++){ if(hvalueArray[j]==v){ document.getElementById("checkBox_"+newValue).className="checked"; } } } this.list.show(); Ext.getDoc().on('mousewheel', this.collapseIf, this); Ext.getDoc().on('mousedown', this.collapseIf, this); this.fireEvent('expand', this); }, // private // Implements the default empty TriggerField.onTriggerClick function onTriggerClick : function(){ if(this.disabled){ return; } if(this.isExpanded()){ this.collapse(); this.el.focus(); }else { this.onFocus({}); if(this.triggerAction == 'all') { this.doQuery(this.allQuery, true); } else { this.doQuery(this.getRawValue()); } this.el.focus(); } } }); Ext.reg('multicombo', Ext.form.MultiComboBox); ~~~ Ext.hoo.form.InterestMultiComboBox.js ~~~ /** * @function 支持多选的combox * @auhor: hoojo * @createDate: Sep 10, 2010 10:32:00 PM * @blog: blog.csdn.net/IBM_hoojo * @email: hoojo_@126.com * @class Ext.hoo.form.InterestMultiComboBox * @extends Ext.form.MultiComboBox */ Ext.ns("Ext.hoo.form"); Ext.hoo.form.InterestMultiComboBox = Ext.extend(Ext.form.MultiComboBox, { constructor: function () { Ext.hoo.form.InterestMultiComboBox.superclass.constructor.call(this, { renderTo: "show", width: 250, mode: "local", fieldLabel: "多选下拉框", store: new Ext.data.SimpleStore({ fields: ["textAttr", "valueAttr"], data: [ ["打球", "batting"], ["看书", "lookbook"], ["睡觉", "sleeping"], ["购物", "shopping"], ["打游戏", "games"], ["唱歌", "sing"] ] }), editable: true, emptyText:"请选择", //allowBlank: false, valueField: "textAttr", displayField: "valueAttr", labelSeparator: ":", valueSeparator: "#", displaySeparator: ",", //value:"batting,sleeping", hiddenName: "interest", forceSelection: true, triggerAction: "all" }); this.on("select", function (field, record, index) { alert(field.getValue() + "%%%%" + field.getRawValue() + Ext.fly("interest").dom.value); }, this); } }); Ext.onReady(function () { Ext.BLANK_IMAGE_URL = "../ext2/resources/images/default/s.gif"; Ext.QuickTips.init(); Ext.form.Field.prototype.msgTarget = "qtip"; new Ext.hoo.form.InterestMultiComboBox(); }); ~~~ ### 五、动态Combobox Tree(下拉列表树)组件 可以多选、单选、checkbox ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe122acf8b3.gif) 需要的文件 动态下列列表树插件:Ext.ux.form.DynamicTreeCombox.js 示例文件:Ext.hoo.form.DynaTreeCombo.js 代码如下: ~~~ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>form Component -- MultiComboBox</title> <meta http-equiv="pragma" content="no-cache"/> <meta http-equiv="cache-control" content="no-cache"/> <meta http-equiv="expires" content="0"/> <meta http-equiv="content-Type" content="text/html; charset=utf-8"/> <meta http-equiv="author" content="hoojo"/> <meta http-equiv="email" content="hoojo_@126.com"/> <meta http-equiv="ext-lib" content="version 2.2"/> <meta http-equiv="blog" content="http://blog.csdn.net/IBM_hoojo"/> <link rel="stylesheet" type="text/css" href="../ext2/resources/css/ext-all.css"/> <script type="text/javascript" src="../ext2/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="../ext2/ext-all.js"></script> <script type="text/javascript" src="../ext2/build/locale/ext-lang-zh_CN-min.js"></script> <script type="text/javascript" src="Ext.ux.form.DynamicTreeCombox.js"></script> <script type="text/javascript" src="Ext.hoo.form.DynaTreeCombo.js"></script> </head> <body> <div id="show" style="float: left; margin: 100px 0 0 100px;"></div> <div id="showBasic" style="float: left; margin: 100px 0 0 100px;"></div> <div id="showConstom" style="float: left; margin: 100px 0 0 100px;"></div> </body> </html> ~~~ Ext.ux.form.DynamicTreeCombox.js ~~~ Ext.ns("Ext.ux.form"); Ext.ux.form.DynamicTreeCombox = Ext.extend(Ext.form.ComboBox, { initComponent:function(){ Ext.ux.form.DynamicTreeCombox.superclass.initComponent.call(this); this.addEvents('beforeClickNode','afterClickNode', 'select'); if(!this.tree){ var root = this.root || new Ext.tree.AsyncTreeNode(); this.tree = new Ext.tree.TreePanel({ containerScroll: true, rootVisible: false, border: false, root: root }); this.tree.loader = new Ext.tree.TreeLoader(this.loaderConfig||{dataUrl:"",baseAttrs:{}}); this.tree.loader.addListener("beforeload",this.beforeLoad,this); this.tree.loader.addListener("load",this.onLoad,this); this.tree.addListener('collapsenode',this.onNodeCollapse,this); this.tree.addListener('expandnode',this.onNodeExpand,this); } var fieldMp = { text: "text", qtip: "qtip", parentFuncId: "id", icon: "icon" }; if(!this.fieldMapping){ this.fieldMapping = fieldMp; }else{ Ext.applyIf(this.fieldMapping, fieldMp); } }, initList:function(){ if(!this.list){ var cls = 'x-combo-list'; this.list = new Ext.Layer({ shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false }); var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth); this.list.setWidth(lw); this.list.swallowEvent('mousewheel'); this.innerList = this.list.createChild({cls:cls+'-inner'}); this.innerList.setWidth(lw - this.list.getFrameWidth('lr')); this.innerList.setHeight("100%"); } }, doQuery:function(){ if(this.expandAll){ var loader=this.tree.loader; if(loader.baseAttrs){ Ext.apply(loader.baseAttrs,{expanded:this.expandAll}); }else{ loader.baseAttrs={expanded:this.expandAll}; } } if(!this.tree.rendered){ this.tree.render(this.innerList); this.tree.addListener("click",this.clickNode,this); } this.expand(); }, beforeLoad:function(loader,node){ if(node!=node.getOwnerTree().root){ loader.baseParams.parentFuncId=node.attributes[this.fieldMapping.parentFuncId]; } }, onLoad:function(loader,node,res){ var nodeArr=node.childNodes; for(var i=0,j=nodeArr.length;i<j;i++){ if(nodeArr[i].attributes[this.fieldMapping.icon]){ nodeArr[i].getUI().getIconEl().src=nodeArr[i].attributes[this.fieldMapping.icon]; } nodeArr[i].setText(nodeArr[i].attributes[this.fieldMapping.text]); nodeArr[i].ui.textNode.setAttribute("qtip", nodeArr[i].attributes[this.fieldMapping.qtip]); } }, clickNode:function(node){ if(this.fireEvent('beforeClickNode',this,node)){ this.setValue(node.attributes[this.displayField]); this.value = node.attributes[this.valField || "id"]; } this.fireEvent("afterClickNode",this,node); this.fireEvent("select",this,node); this.collapse(); }, onNodeCollapse:function(node){ this.list.setHeight(this.tree.getEl().getHeight()+2); }, getValue: function () { return this.value; }, onNodeExpand:function(node){ this.list.setHeight(this.tree.getEl().getHeight()+2); }, onDestroy:function(){ if(this.view){ this.view.el.removeAllListeners(); this.view.el.remove(); this.view.purgeListeners(); } if(this.tree.loader){ this.tree.loader.purgeListeners(); } if(this.tree){ this.tree.el.removeAllListerers(); this.tree.el.remove(); this.tree.purgeListeners(); } if(this.innerList){ this.innerList.destroy(); } if(this.list){ this.list.destroy(); } Ext.form.ComboBox.superclass.onDestroy.call(this); } }); Ext.reg("treecombox",Ext.ux.form.DynamicTreeCombox); ~~~ Ext.hoo.form.DynaTreeCombo.js ~~~ /** * @function 动态tree的combobox,本地数据 * @auhor: hoojo * @createDate: Sep 11, 2010 6:33:13 PM * @blog: blog.csdn.net/IBM_hoojo * @email: hoojo_@126.com * @class Ext.hoo.form.DynaTreeCombo * @extends Ext.ux.form.DynamicTreeCombox */ Ext.ns("Ext.hoo.form"); Ext.hoo.form.DynaTreeCombo = Ext.extend(Ext.ux.form.DynamicTreeCombox, { constructor: function () { Ext.hoo.form.DynaTreeCombo.superclass.constructor.call(this, { renderTo: "show", expandAll: false, readOnly: true, width: 200, displayField: "text", valField: "id", root: new Ext.tree.AsyncTreeNode({ text: "ExtJS", id: "0", children: [{ text: "adpter", qtip: "这是一个adpter的tip", children: [{ text: "ext", leaf: true, qtip: "这是一个ext的tip", checked: true },{ text: "yui", leaf: true },{ text: "jquery", leaf: true },{ text: "prototype", leaf: true, checked: false }] },{ text: "air", checked: false, children: [{ text: "air.jsb", leaf: true },{ text: "ext-air.js", leaf: true }] },{ text: "docs" }] }) }); this.on("select", function (field, node) { alert(field.getValue() + "###" + field.getRawValue() + "##" + node.text); }, this); this.on("check", function (field, node) { alert(field.getValue() + "###" + field.getRawValue() + "##" + node.text); }, this); } }); /** * @function DynamicTreeCombox 后台数据 * @auhor: hoojo * @createDate: Sep 11, 2010 10:30:27 PM * @blog: blog.csdn.net/IBM_hoojo * @email: hoojo_@126.com */ Ext.hoo.form.AsyncBasicDynaTreeCombo = Ext.extend(Ext.ux.form.DynamicTreeCombox, { constructor: function () { Ext.hoo.form.AsyncBasicDynaTreeCombo.superclass.constructor.call(this, { renderTo: "showBasic", expandAll: false, readOnly: true, width: 200, displayField: "text", valField: "id", loaderConfig:{ dataUrl: "../ServiceDataServlet?method=basic"//加载树的URL } }); this.on("select", function (field, node) { alert(field.getValue() + "###" + field.getRawValue() + "##" + node.text); }, this); } }); /** * @function 后台数据 * @auhor: hoojo * @createDate: Sep 11, 2010 10:30:59 PM * @blog: blog.csdn.net/IBM_hoojo * @email: hoojo_@126.com */ Ext.hoo.form.AsyncConstomDynaTreeCombo = Ext.extend(Ext.ux.form.DynamicTreeCombox, { constructor: function () { Ext.hoo.form.AsyncConstomDynaTreeCombo.superclass.constructor.call(this, { renderTo: "showConstom", expandAll: false, readOnly: true, width: 200, displayField: "text", valField: "id", loaderConfig:{ dataUrl: "../ServiceDataServlet?method=constom", baseParams:{parentFuncId:""}//如果传递parentFuncId就会查询当前id下的节点 }, //后台数据中没有text、qtip、icon、parentFuncId属性,需加上fieldMapping进行属性映射,如果后台返回数据中有此属性可省略 fieldMapping: { text: "text",//映射node的text字段 qtip: "tip",//映射node的qtip字段 parentFuncId: "text"//映射动态请求后台的text属性作为请求参数 //icon: "img" } }); this.on("select", function (field, node) { alert(field.getValue() + "###" + field.getRawValue() + "##" + node.text); }, this); } }); Ext.onReady(function () { Ext.BLANK_IMAGE_URL = "../ext2/resources/images/default/s.gif"; Ext.QuickTips.init(); Ext.form.Field.prototype.msgTarget = "qtip"; new Ext.hoo.form.DynaTreeCombo(); new Ext.hoo.form.AsyncBasicDynaTreeCombo(); new Ext.hoo.form.AsyncConstomDynaTreeCombo(); }); ~~~ ServiceDataServlet.java ~~~ package com.hoo.servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ServiceDataServlet extends HttpServlet { private static final long serialVersionUID = 1L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/json"); response.setCharacterEncoding("utf-8"); PrintWriter out = response.getWriter(); String method = request.getParameter("method"); StringBuilder sb = new StringBuilder(); if ("basic".equals(method)) { sb.append("[") .append("{").append("text:\"").append("adpter").append("\", qtip: \"").append("qtip提示:ADPTER").append("\","); sb.append("children: [") .append("{").append("text:\"").append("ext").append("\", qtip: \"").append("qtip提示:ext").append("\",") .append("leaf: true").append("},") .append("{").append("text:\"").append("jquery") .append("\",").append("leaf: true").append("}]").append("},") .append("{").append("text:\"").append("air").append("\","); sb.append("children: [") .append("{").append("text:\"").append("air.jsb") .append("\",").append("leaf: true").append("},") .append("{").append("text:\"").append("ext-air.js") .append("\",").append("leaf: true").append("}]").append("},") .append("{").append("text:\"").append("readMe.text").append("\",") .append("leaf: true}"); sb.append("]"); } if ("constom".equals(method)) { sb.append("[") .append("{").append("text:\"").append("adpter").append("\", tip: \"").append("ADPTER").append("\","); sb.append("children:[") .append("{").append("text:\"").append("ext").append("\", tip: \"").append("EXT").append("\",") .append("leaf: true").append("},") .append("{").append("text:\"").append("jquery").append("\", tip: \"").append("JQUERY").append("\",") .append("leaf: true").append("}]},") .append("{").append("text:\"").append("air").append("\", tip: \"").append("AIR").append("\","); sb.append("children:[") .append("{").append("text:\"").append("air.jsb").append("\", tip: \"").append("AIR.JSB").append("\",") .append("leaf: true").append("},") .append("{").append("text:\"").append("ext-air.js").append("\", tip: \"").append("EXT-AIR.JS").append("\",") .append("leaf: true").append("}]").append("},") .append("{").append("text:\"").append("readMe.text").append("\", tip: \"").append("README.TEXT").append("\",") .append("leaf: true}"); sb.append("]"); } out.print(sb.toString()); out.flush(); out.close(); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } } ~~~
';

Struts2、Spring3、MyBatis3整合ExtJS,完成CheckNodeColumnTree

最后更新于:2022-04-01 12:01:24

前面介绍了ColumnTree:[http://www.cnblogs.com/hoojo/archive/2011/05/11/2043426.html](http://www.cnblogs.com/hoojo/archive/2011/05/11/2043426.html "http://www.cnblogs.com/hoojo/archive/2011/05/11/2043426.html") [http://www.cnblogs.com/hoojo/archive/2011/05/11/2043453.html](http://www.cnblogs.com/hoojo/archive/2011/05/11/2043453.html "http://www.cnblogs.com/hoojo/archive/2011/05/11/2043453.html") 加入CheckNodeColumnTree A、首先在AccountAction中添加如下代码: ~~~ private String jsonText; public String getJsonText() { return jsonText; } public String checkNodeColumnTreeData() throws Exception { jsonText = JSONUtil.serialize(results); //ServletActionContext.getResponse().getWriter().print(jsonText); return "showData"; } ~~~ 我们不直接使用Servlet的response方法,这样会使Action耦合Servlet的API。我们直接在Action中提供一个jsonText的属性,给该属性提供getter方法。然后当执行checkNodeColumnTreeData这个方法的时候,为jsonText赋值。等待请求成功,跳转到showData的视图中直接去取jsonText的值即可。 B、看看struts.xml中的配置 ~~~ <action name="account" class="accountAction"> <result type="redirect">account!show.action</result> <result name="show">/show.jsp</result> <result name="showData">/showData.jsp</result> <result name="tree" type="json"> <param name="excludeProperties">acc</param> <param name="contentType">text/html</param> </result> </action> ~~~ 就配置一个result就可以了,跳转到showData页面 C、下面看看showData页面 ~~~ <%@ page language="java" import="java.util.*" pageEncoding="UTF-8" contentType="text/text; charset=UTF-8"%> ${jsonText} ~~~ 代码比较简单,设置下页面才contentType、然后用el表达式取出jsonText的值即可。 D、下面看看CheckNodeColumnTree代码 Ext.hoo.tree.UserCheckNodeColumnTree.js ~~~ /** * @function CheckNode ColumnTree 多列信息的tree * @auhor: hoojo * @createDate: Aug 29, 2010 10:39:02 PM * @blog: blog.csdn.net/IBM_hoojo * @email: hoojo_@126.com */ Ext.ns("Ext.hoo.tree"); Ext.hoo.tree.UserCheckNodeColumnTree = Ext.extend(Ext.tree.ColumnTree, { constructor: function () { Ext.hoo.tree.UserCheckNodeColumnTree.superclass.constructor.call(this, { renderTo: "show", title: "远程数据", width: 600, hieght: 400, autoScroll: true, rootVisible: true, checkModel: 'cascade',//级联多选,如果不需要checkbox,该属性去掉 onlyLeafCheckable: false,//所有结点可选,如果不需要checkbox,该属性去掉 columns: [{ header: "编号", width: 100, dataIndex: "accountId" }, { header: "用户名称", width: 100, dataIndex: "username" }, { header: "密码", width: 100, dataIndex: "password" }, { header: "创建时间", width: 150, dataIndex: "createTime" }], loader: new Ext.tree.TreeLoader({ dataUrl: Ext.hoo.tree.UserCheckNodeColumnTree.TREE_DATA_URL, clearOnLoad: false, baseAttrs: { uiProvider: Ext.ux.ColumnTreeCheckNodeUI, leaf: true } }), root: new Ext.tree.AsyncTreeNode({ text: "用户基本信息", id: "root" }) }); } }); Ext.hoo.tree.UserCheckNodeColumnTree.TREE_DATA_URL = "account!checkNodeColumnTreeData.action"; Ext.onReady(function () { Ext.BLANK_IMAGE_URL = "ext2/resources/images/default/s.gif"; new Ext.hoo.tree.UserCheckNodeColumnTree(); }); ~~~ 在webroot目录的jslib中加入这个文件Ext.hoo.tree.UserCheckNodeColumnTree.js,然后导入到html中即可。 E、上面的代码用到了Ext.ux.ColumnTreeCheckNodeUI这个UI,我们需要加入这个UI文件Ext.tree.ColumnTreeCheckNodeUI.js,代码如下: ~~~ /* * Ext JS Library 2.0 * Copyright(c) 2006-2007, Ext JS, LLC. * licensing@extjs.com * * http://extjs.com/license */ Ext.tree.ColumnTree = Ext.extend(Ext.tree.TreePanel, { //lines:false, borderWidth: Ext.isBorderBox ? 0 : 2, // the combined left/right border for each cell cls:'x-column-tree', scrollOffset : 18, onRender : function(){ Ext.tree.ColumnTree.superclass.onRender.apply(this, arguments); this.headers = this.body.createChild( {cls:'x-tree-headers '},this.body.dom); var cols = this.columns, c; var totalWidth = 0; for(var i = 0, len = cols.length; i < len; i++){ c = cols[i]; totalWidth += c.width; this.headers.createChild({ cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''), cn: { cls:'x-tree-hd-text', html: c.header }, style:'width:'+(c.width-this.borderWidth)+'px;' }); } this.headers.createChild({ cls:'x-tree-hd ', cn: { html: '' }, style:'width:'+this.scrollOffset+'px;' }); totalWidth += this.scrollOffset; this.headers.createChild({cls:'x-clear'}); // prevent floats from wrapping when clipped this.headers.setWidth(totalWidth); totalWidth -= this.scrollOffset; this.innerCt.setWidth(totalWidth); } }); Ext.tree.ColumnTreeNodeUI = Ext.extend(Ext.tree.TreeNodeUI, { focus: Ext.emptyFn, // prevent odd scrolling behavior renderElements : function(n, a, targetNode, bulkRender){ this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : ''; var t = n.getOwnerTree(); var cols = t.columns; var bw = t.borderWidth; var c = cols[0]; var cb = typeof a.checked == 'boolean'; if(typeof this.checkModel != 'undefined'){ cb = (!this.onlyLeafCheckable || n.isLeaf()); } var href = a.href ? a.href : Ext.isGecko ? "" : "#"; var buf = ['<li class="x-tree-node"><div ext:tree-node-id="',n.id,'" class="x-tree-node-el x-tree-node-leaf x-unselectable ', a.cls,'" unselectable="on">', '<div class="x-tree-col" style="width:',c.width-bw,'px;">', '<span class="x-tree-node-indent">',this.indentMarkup,"</span>", '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow">', '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on">', cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : '/>')) : '', '<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" tabIndex="1" ', a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '>', '<span unselectable="on">', n.text || (a[c.dataIndex]?(c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]):''),"&nbsp;</span></a>", "</div>"]; for(var i = 1, len = cols.length; i < len; i++){ c = cols[i]; buf.push('<div class="x-tree-col ',(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">', '<div class="x-tree-col-text">',(a[c.dataIndex]?(c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]):''),"&nbsp;</div>", "</div>"); } buf.push('<div class="x-clear"></div>', '</div>', '<ul class="x-tree-node-ct" style="display:none;"></ul>', "</li>"); if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){ this.wrap = Ext.DomHelper.insertHtml("beforeBegin",n.nextSibling.ui.getEl(), buf.join("")); }else{ this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf.join("")); } this.elNode = this.wrap.childNodes[0]; this.ctNode = this.wrap.childNodes[1]; var cs = this.elNode.firstChild.childNodes; this.indentNode = cs[0]; this.ecNode = cs[1]; this.iconNode = cs[2]; var index = 3; if(cb){ this.checkbox = cs[3]; index++; } this.anchor = cs[index]; this.textNode = cs[index].firstChild; } }); Ext.ux.ColumnTreeCheckNodeUI = function() { //多选: 'multiple'(默认) //单选: 'single' //级联多选: 'cascade'(同时选父和子);'parentCascade'(选父);'childCascade'(选子) this.checkModel = 'multiple'; //only leaf can checked this.onlyLeafCheckable = false; Ext.ux.ColumnTreeCheckNodeUI.superclass.constructor.apply(this, arguments); }; Ext.extend(Ext.ux.ColumnTreeCheckNodeUI, Ext.tree.ColumnTreeNodeUI, { renderElements : function(n, a, targetNode, bulkRender){ var t = n.getOwnerTree(); this.checkModel = t.checkModel || this.checkModel; this.onlyLeafCheckable = t.onlyLeafCheckable || false; Ext.ux.ColumnTreeCheckNodeUI.superclass.renderElements.apply(this, arguments); var cb = (!this.onlyLeafCheckable || n.isLeaf()); if(cb){ Ext.fly(this.checkbox).on('click', this.check.createDelegate(this,[null])); } }, // private check : function(checked){ var n = this.node; var tree = n.getOwnerTree(); this.checkModel = tree.checkModel || this.checkModel; if( checked === null ) { checked = this.checkbox.checked; } else { this.checkbox.checked = checked; } n.attributes.checked = checked; tree.fireEvent('check', n, checked); if(!this.onlyLeafCheckable){ if(this.checkModel == 'cascade' || this.checkModel == 'parentCascade'){ var parentNode = n.parentNode; if(parentNode !== null) { this.parentCheck(parentNode,checked); } } if(this.checkModel == 'cascade' || this.checkModel == 'childCascade'){ if( !n.expanded && !n.childrenRendered ) { n.expand(false,false,this.childCheck); }else { this.childCheck(n); } } } else if(this.checkModel == 'single'){ var checkedNodes = tree.getChecked(); for(var i=0;i<checkedNodes.length;i++){ var node = checkedNodes[i]; if(node.id != n.id){ node.getUI().checkbox.checked = false; node.attributes.checked = false; tree.fireEvent('check', node, false); } } } }, // private childCheck : function(node){ var a = node.attributes; if(!a.leaf) { var cs = node.childNodes; var csui; for(var i = 0; i < cs.length; i++) { csui = cs[i].getUI(); if(csui.checkbox.checked ^ a.checked) csui.check(a.checked); } } }, // private parentCheck : function(node ,checked){ var checkbox = node.getUI().checkbox; if(typeof checkbox == 'undefined')return ; if(!(checked ^ checkbox.checked))return; if(!checked && this.childHasChecked(node))return; checkbox.checked = checked; node.attributes.checked = checked; node.getOwnerTree().fireEvent('check', node, checked); var parentNode = node.parentNode; if( parentNode !== null){ this.parentCheck(parentNode,checked); } }, // private childHasChecked : function(node){ var childNodes = node.childNodes; if(childNodes || childNodes.length>0){ for(var i=0;i<childNodes.length;i++){ if(childNodes[i].getUI().checkbox.checked) return true; } } return false; }, toggleCheck : function(value){ var cb = this.checkbox; if(cb){ var checked = (value === undefined ? !cb.checked : value); this.check(checked); } } }); ~~~ F、在html页面中导入这些js库即可运行示例 ~~~ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>UserCheckNodeColumnTree 示例</title> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" /> <meta http-equiv="author" content="hoojo"/> <meta http-equiv="email" content="hoojo_@126.com"/> <meta http-equiv="ext-lib" content="verson 2.2"/> <meta http-equiv="blog" content="http://blog.csdn.net/IBM_hoojo"/> <meta http-equiv="blog" content="http://hoojo.cnblogs.com"/> <link rel="stylesheet" type="text/css" href="ext2/resources/css/ext-all.css" /> <link rel="stylesheet" type="text/css" href="jslib/column-tree.css" /> <script type="text/javascript" src="ext2/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="ext2/ext-all.js"></script> <script type="text/javascript" src="ext2/build/locale/ext-lang-zh_CN-min.js"></script> <script type="text/javascript" src="jslib/Ext.tree.ColumnTreeCheckNodeUI.js"></script> <script type="text/javascript" src="jslib/Ext.hoo.tree.UserCheckNodeColumnTree.js"></script> </head> <body> <div id="show" style="margin-left: 200px; margin-top: 50px;"></div> </body> </html> ~~~ 运行后,效果如下: [![clip_image002](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe1229ae1b0.gif "clip_image002")](http://hi.csdn.net/attachment/201105/12/0_1305176238y2Pk.gif) 如果有子节点的话,可以选择、展开子节点
';

Struts2、Spring3、MyBatis3整合ExtJS,完成ColumnTree 【二】

最后更新于:2022-04-01 12:01:21

###三、加入Struts2框架 1、 准备工作 添加jar文件如下: [![clip_image002](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe1228ec445.gif "clip_image002")](http://hi.csdn.net/attachment/201105/11/0_13051072424eVp.gif) org.springframework.web-3.0.5.RELEASE.jar org.springframework.aop-3.0.5.RELEASE.jar 这2个jar包是spring的context所依赖的jar包 struts2-spring-plugin-2.2.3.jar是struts整合spring的jar包 2、 在web.xml加入struts2的控制器 ~~~ <filter> <filter-name>struts2filter-name> <filter-class>org.apache.struts2.dispatcher.FilterDispatcherfilter-class> filter> <filter-mapping> <filter-name>struts2filter-name> <url-pattern>/*url-pattern> <filter-mapping> ~~~ 3、 在src目录添加struts.xml ~~~ <xml version="1.0" encoding="UTF-8"?> DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.i18n.encoding" value="UTF-8"/> <package name="ssMyBatis" extends="struts-default"> <package> <struts> ~~~ 启动后,可以看到首页index的页面就基本整合完成。 4、 首先看看Action代码,代码如下: ~~~ package com.hoo.action; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; import javax.inject.Named; import org.springframework.stereotype.Component; import com.hoo.biz.AccountBiz; import com.hoo.entity.Account; import com.opensymphony.xwork2.ActionSupport; /** * function: Account Action * @author hoojo * @createDate 2011-5-11 下午12:03:05 * @file AccountAction.java * @package com.hoo.action * @project S2SMyBatis * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ @Component public class AccountAction extends ActionSupport { /** * @author Hoojo */ private static final long serialVersionUID = -973535478139284399L; @Inject @Named("accountBiz") private AccountBiz<Account> biz; private Account acc; private List<Account> results = new ArrayList<Account>(); public List<Account> getResults() { return results; } public Account getAcc() { return acc; } public void setAcc(Account acc) { this.acc = acc; } public String add() throws Exception { if (!biz.addAccount(acc)) { this.addActionMessage("添加数据失败"); return ERROR; } return SUCCESS; } public String show() throws Exception { results = biz.getList(); return "show"; } public String remove() throws Exception { return SUCCESS; } public String edit() throws Exception { return SUCCESS; } public String treeData() throws Exception { results = biz.getList(); return "tree"; } } ~~~ 这个Action被注解成Component,那么在spring的applicationContext配置文件中就不需要进行标签的配置了。上面注入了AccountDao,完成相关操作。 5、 由于Struts2要和Spring进行整合,所以struts的配置会有点不同 ~~~ <action name="account" class="accountAction"> <result type="redirect">account!show.actionresult> <result name="show">/show.jspresult> <action> ~~~ 上面的class不再是AccountAction的classpath,而是spring容器中配置的bean。就是通过@Component注解过的AccountAction,被注解注释过后它的id默认是类名称首字母小写。所以上面的action的配置是accountAction。 6、 由于要整合ExtJS,所以这里用到struts2-json-plugin-2.2.3.jar这个插件,将其加入到lib库中,struts.xml更改成: ~~~ <xml version="1.0" encoding="UTF-8"?> <DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.i18n.encoding" value="UTF-8"/> <package name="ssMyBatis" extends="json-default"> <global-results> <result name="error">/error.jspresult> <global-results> <action name="account" class="accountAction"> <result type="redirect">account!show.actionresult> <result name="show">/show.jspresult> <result name="tree" type="json"> <param name="excludeProperties">accparam> <result> <action> <package> <struts> ~~~ AccountAction中的treeData方法返回的tree,在account这个action配置中找到tree的result,将result的type配置成json。表示该result的数据以json的方式展示。tree这个result还配置了一个param,名称为excludeProperties表示排除的属性。这个参数将排除当前Action中的acc属性。也就是说这个属性将不会得到json的转换。其他属性将会被转换成json。 7、 前台页面 index.jsp ~~~ <body> <a href="account!show.action">显示所有a> <br> <a href="add.jsp">添加数据a><br/> <a href="account!treeData.action">JSONa><br/> <body> ~~~ show.jsp ~~~ <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags" %> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>show all data</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body> <s:iterator value="results" status="s" var="data"> ${data }<===> <s:property value="#data.accountId"/># <s:property value="#data.username"/># <s:property value="#data.password"/># <s:property value="#data.createTime" /># <s:date name="#data.createTime" format="yyyy-MM-dd"/># <a href="account!remove.action">删除</a> | <a href="account!edit.action">修改</a> <br/> </s:iterator> </body> </html> ~~~ Struts标签和OGNL表达式显示数据 add.jsp ~~~ <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags" %> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>add</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body> <s:form action="account!add.action" method="post"> <s:textfield name="acc.username"/> <s:password name="acc.password"/> <s:submit value="提交"></s:submit> </s:form> </body> </html> ~~~ ###四、整合ExtJS 1、添加ext的库,版本是2.2.2 需要添加column-tree.css ~~~ /* * Ext JS Library 2.2.1 * Copyright(c) 2006-2009, Ext JS, LLC. * licensing@extjs.com * * http://extjs.com/license */ .x-column-tree .x-tree-node { zoom:1; } .x-column-tree .x-tree-node-el { /*border-bottom:1px solid #eee; borders? */ zoom:1; } .x-column-tree .x-tree-selected { background: #d9e8fb; } .x-column-tree .x-tree-node a { line-height:18px; vertical-align:middle; } .x-column-tree .x-tree-node a span{ } .x-column-tree .x-tree-node .x-tree-selected a span{ background:transparent; color:#000; } .x-tree-col { float:left; overflow:hidden; padding:0 1px; zoom:1; } .x-tree-col-text, .x-tree-hd-text { overflow:hidden; -o-text-overflow: ellipsis; text-overflow: ellipsis; padding:3px 3px 3px 5px; white-space: nowrap; font:normal 11px arial, tahoma, helvetica, sans-serif; } .x-tree-headers { background: #f9f9f9 url(../ext2/resources/images/default/grid/grid3-hrow.gif) repeat-x 0 bottom; cursor:default; zoom:1; } .x-tree-hd { float:left; overflow:hidden; border-left:1px solid #eee; border-right:1px solid #d0d0d0; } .task { background-image:url(../shared/icons/fam/cog.png) !important; } .task-folder { background-image:url(../shared/icons/fam/folder_go.png) !important; } Ext.tree.ColumnTree.js /* * Ext JS Library 2.2.1 * Copyright(c) 2006-2009, Ext JS, LLC. * licensing@extjs.com * * http://extjs.com/license */ Ext.tree.ColumnTree = Ext.extend(Ext.tree.TreePanel, { lines:false, borderWidth: Ext.isBorderBox ? 0 : 2, // the combined left/right border for each cell cls:'x-column-tree', onRender : function(){ Ext.tree.ColumnTree.superclass.onRender.apply(this, arguments); this.headers = this.body.createChild( {cls:'x-tree-headers'},this.innerCt.dom); var cols = this.columns, c; var totalWidth = 0; for(var i = 0, len = cols.length; i < len; i++){ c = cols[i]; totalWidth += c.width; this.headers.createChild({ cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''), cn: { cls:'x-tree-hd-text', html: c.header }, style:'width:'+(c.width-this.borderWidth)+'px;' }); } this.headers.createChild({cls:'x-clear'}); // prevent floats from wrapping when clipped this.headers.setWidth(totalWidth); this.innerCt.setWidth(totalWidth); } }); Ext.tree.ColumnNodeUI = Ext.extend(Ext.tree.TreeNodeUI, { focus: Ext.emptyFn, // prevent odd scrolling behavior renderElements : function(n, a, targetNode, bulkRender){ this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : ''; var t = n.getOwnerTree(); var cols = t.columns; var bw = t.borderWidth; var c = cols[0]; var buf = [ '<li class="x-tree-node"><div ext:tree-node-id="',n.id,'" class="x-tree-node-el x-tree-node-leaf ', a.cls,'">', '<div class="x-tree-col" style="width:',c.width-bw,'px;">', '<span class="x-tree-node-indent">',this.indentMarkup,"</span>", '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow">', '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on">', '<a hidefocus="on" class="x-tree-node-anchor" href="',a.href ? a.href : "#",'" tabIndex="1" ', a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '>', '<span unselectable="on">', n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]),"</span></a>", "</div>"]; for(var i = 1, len = cols.length; i < len; i++){ c = cols[i]; buf.push('<div class="x-tree-col ',(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">', '<div class="x-tree-col-text">',(c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]),"</div>", "</div>"); } buf.push( '<div class="x-clear"></div></div>', '<ul class="x-tree-node-ct" style="display:none;"></ul>', "</li>"); if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){ this.wrap = Ext.DomHelper.insertHtml("beforeBegin", n.nextSibling.ui.getEl(), buf.join("")); }else{ this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf.join("")); } this.elNode = this.wrap.childNodes[0]; this.ctNode = this.wrap.childNodes[1]; var cs = this.elNode.firstChild.childNodes; this.indentNode = cs[0]; this.ecNode = cs[1]; this.iconNode = cs[2]; this.anchor = cs[3]; this.textNode = cs[3].firstChild; } }); ~~~ 2、 编写静态ColumnTree ~~~ /** * @function column tree column tree 多列信息的tree * @auhor: hoojo * @createDate: Aug 29, 2010 10:39:02 PM * @blog: blog.csdn.net/IBM_hoojo * @email: hoojo_@126.com */ Ext.ns("Ext.hoo.tree"); Ext.hoo.tree.UserColumnTree = Ext.extend(Ext.tree.ColumnTree, { constructor: function () { Ext.hoo.tree.UserColumnTree.superclass.constructor.call(this, { renderTo: "show", title: "用户信息column tree", width: 450, hieght: 400, autoScroll: true, rootVisible: true, columns: [{ header: "名称", width: 100, dataIndex: "name" }, { header: "性别", width: 100, dataIndex: "sex" }, { header: "年龄", width: 100, dataIndex: "age" }, { header: "班级", width: 100, dataIndex: "classes" }], loader: new Ext.tree.TreeLoader({ baseAttrs: { uiProvider: Ext.tree.ColumnNodeUI } }), root: new Ext.tree.AsyncTreeNode({ text: "用户基本信息", children: [{ name: "大二一班", classes: "二(1)班", children: [{ name: "微微", sex: "女", age: 20, classes: "二(1)班", leaf: true },{ name: "筱筱", sex: "女", age: 22, classes: "二(1)班", leaf: true },{ name: "珠珠", sex: "女", age: 19, classes: "二(1)班", leaf: true },{ name: "拉拉", sex: "女", age: 19, classes: "二(1)班", leaf: true }] },{ name: "二二班", classes: "二(2)班", children: [{ name: "放放", sex: "男", age: 22, classes: "二(2)班", leaf: true },{ name: "枫枫", sex: "男", age: 22, classes: "二(2)班", leaf: true }] },{ name: "未成立", sex: "", age: 0, classes: "二(3)班", leaf: true }] }) }); this.on("click", this.onNodeClick, this); }, onNodeClick: function (node, e) { alert(Ext.encode(node.attributes) + "###" + node.leaf + "###" + Ext.encode(e.getPoint()) + "##" + e.getXY()); } }); Ext.onReady(function () { Ext.BLANK_IMAGE_URL = "ext2/resources/images/default/s.gif"; new Ext.hoo.tree.UserColumnTree(); }); ~~~ 上面的就是一个静态的ColumnTree,在Ext的onReady函数中运行它。 3、 、需要在页面中导入ext库、css、即我们编写的js ~~~ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>TreePanel 示例</title> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" /> <meta http-equiv="author" content="hoojo"/> <meta http-equiv="email" content="hoojo_@126.com"/> <meta http-equiv="ext-lib" content="version 2.2"/> <meta http-equiv="blog" content="http://blog.csdn.net/IBM_hoojo"/> <meta http-equiv="blog" content="http://hoojo.cnblogs.com"/> <link rel="stylesheet" type="text/css" href="ext2/resources/css/ext-all.css" /> <link rel="stylesheet" type="text/css" href="jslib/column-tree.css" /> <script type="text/javascript" src="ext2/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="ext2/ext-all.js"></script> <script type="text/javascript" src="ext2/build/locale/ext-lang-zh_CN-min.js"></script> <script type="text/javascript" src="jslib/Ext.tree.ColumnTree.js"></script> <script type="text/javascript" src="jslib/Ext.hoo.tree.UserColumnTree.js"></script> </head> <body> <div id="show" style="margin-left: 200px;"></div> <div id="showBasic" style="margin-left: 200px; margin-top: 50px;"></div> </body> </html> ~~~ 在浏览器中请求[http://localhost:8080/S2SMyBatis/columnTree.htm](http://localhost:8080/S2SMyBatis/columnTree.htm) 结果如下 [![clip_image004](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe1229379b5.gif "clip_image004")](http://hi.csdn.net/attachment/201105/11/0_1305107248dAkD.gif) 4、 下面编写远程数据的ColumnTree,代码如下: ~~~ Ext.ns("Ext.hoo.tree"); Ext.hoo.tree.UserBasicColumnTree = Ext.extend(Ext.tree.ColumnTree, { constructor: function () { Ext.hoo.tree.UserBasicColumnTree.superclass.constructor.call(this, { renderTo: "showBasic", title: "远程数据", width: 550, hieght: 400, autoScroll: true, rootVisible: true, columns: [{ header: "编号", width: 100, dataIndex: "accountId" }, { header: "用户名称", width: 100, dataIndex: "username" }, { header: "密码", width: 100, dataIndex: "password" }, { header: "创建时间", width: 150, dataIndex: "createTime" }], loader: new Ext.tree.TreeLoader({ baseAttrs: { uiProvider: Ext.tree.ColumnNodeUI } }), root: new Ext.tree.AsyncTreeNode({ text: "用户基本信息", children: [] }), listeners: { expandnode: { fn: this.onExpandNode, scope: this } } }); }, onExpandNode: function (node) { //只对未加载过的添加子结点,加载后不在重复加载;避免增加请求,浪费资源 if (!node.attributes.isLoad) { Ext.Ajax.request({ url: Ext.hoo.tree.UserBasicColumnTree.TREE_DATA_URL, success: function (response, options) { node.attributes.isLoad = true;//设置加载标识 var nodes = Ext.decode(response.responseText); //将json的text转换成js对象 node.appendChild(nodes.results); }, failure: function (response) { Ext.Msg.alert("程序异常", response.responseText); } }); } } }); Ext.hoo.tree.UserBasicColumnTree.TREE_DATA_URL = "account!treeData.action"; ~~~ 由于服务器端返回来的数据是一个对象,而不是一个Array。所以客户端要将数据稍作处理,然后再添加到columnTree的children中。 5、 在上面的onReady中创建这个对象就可以了运行 ~~~ Ext.onReady(function () { Ext.BLANK_IMAGE_URL = "ext2/resources/images/default/s.gif"; new Ext.hoo.tree.UserColumnTree(); new Ext.hoo.tree.UserBasicColumnTree(); }); ~~~ 同样在浏览器中请求[http://localhost:8080/S2SMyBatis/columnTree.htm](http://localhost:8080/S2SMyBatis/columnTree.htm) 可以看到 [![clip_image006](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe122971731.gif "clip_image006")](http://hi.csdn.net/attachment/201105/11/0_1305107253F9qx.gif) 由于Account对象的数据形式不是一个完整的tree形态。所以展示效果就是上面的样子。正确的数据的格式的话,Account中至少包含以下属性: Boolean leaf; List children; 这样就知道当前节点是否是叶子节点,并且知道其子元素。
';

Struts2、Spring3、MyBatis3整合ExtJS,完成ColumnTree 【一】

最后更新于:2022-04-01 12:01:19

开发环境: System:Windows WebBrowser:IE6+、Firefox3+ JavaEE Server:tomcat5.0.2.8、tomcat6 IDE:eclipse、MyEclipse 8 Database:MySQL 开发依赖库: JavaEE5、Spring 3.0.5、Mybatis 3.0.4、myBatis-spring-1.0、Struts2.2.3、junit4.8.2、ext2.2.2 Email:hoojo_@126.com Blog:[http://blog.csdn.net/IBM_hoojo](http://blog.csdn.net/IBM_hoojo) [http://hoojo.cnblogs.com/](http://hoojo.cnblogs.com/) 上次介绍过Spring3、SpringMVC、MyBatis3整合,在线博文:[http://www.cnblogs.com/hoojo/archive/2011/04/15/2016324.html](http://www.cnblogs.com/hoojo/archive/2011/04/15/2016324.html) ###一、准备工作 1、 下载jar包 Struts2 jar下载: [http://labs.renren.com/apache-mirror//struts/library/struts-2.2.3-lib.zip](http://labs.renren.com/apache-mirror/struts/library/struts-2.2.3-lib.zip) Spring3 jar下载: [http://ebr.springsource.com/repository/app/library/version/detail?name=org.springframework.spring&version=3.0.5.RELEASE](http://ebr.springsource.com/repository/app/library/version/detail?name=org.springframework.spring&version=3.0.5.RELEASE) MyBatis3 jar 下载:[http://www.mybatis.org/java.html](http://www.mybatis.org/java.html) 2、 添加的jar包如下: [![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe1228701fa.gif "image")](http://hi.csdn.net/attachment/201105/11/0_13051060949h9C.gif) #### 二、Spring、MyBatis整合 1、 需要的jar文件如下: [![clip_image002](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe12288ecc8.gif "clip_image002")](http://hi.csdn.net/attachment/201105/11/0_13051061019KQL.gif) 2、 在src目录中加入mybatis.xml,内容如下: ~~~ <xml version="1.0" encoding="UTF-8" ?> DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <-- 别名 --> <typeAliases> <typeAlias type="com.hoo.entity.Account" alias="account"/> <typeAliases> <configuration> ~~~ 上面的配置文件中,可以加入一些公共、常用的MyBatis方面的全局配置。如handler、objectFactory、plugin、以及mappers的映射路径(由于在applicationContext-common中的SqlSessionFactoryBean有配置mapper的location,这里就不需要配置)等。这个类的文件名称和下面的applicationContext-common.xml中的configLocation中的值对应,不是随便写的。 3、 在src目录中添加applicationContext-common.xml中加入内容如下: ~~~ <xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd "> <-- 配置DataSource数据源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://10.0.0.131:3306/ash2"/> <property name="username" value="dev"/> <property name="password" value="dev"/> bean> <-- 配置SqlSessionFactoryBean --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="configLocation" value="classpath:mybatis.xml"/> <-- mapper和resultmap配置路径 --> <property name="mapperLocations"> <list> <-- 表示在com.hoo.resultmap包或以下所有目录中,以-resultmap.xml结尾所有文件 --> <value>classpath:com/hoo/resultmap/**/*-resultmap.xmlvalue> <value>classpath:com/hoo/entity/*-resultmap.xmlvalue> <value>classpath:com/hoo/mapper/**/*-mapper.xmlvalue> list> property> bean> <-- 配置事务管理器,注意这里的dataSource和SqlSessionFactoryBean的dataSource要一致,不然事务就没有作用了 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> bean> <-- 配置事务的传播特性 --> <bean id="baseTransactionProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true"> <property name="transactionManager" ref="transactionManager" /> <property name="transactionAttributes"> <props> <prop key="add*">PROPAGATION_REQUIREDprop> <prop key="edit*">PROPAGATION_REQUIREDprop> <prop key="remove*">PROPAGATION_REQUIREDprop> <prop key="insert*">PROPAGATION_REQUIREDprop> <prop key="update*">PROPAGATION_REQUIREDprop> <prop key="del*">PROPAGATION_REQUIREDprop> <prop key="*">readOnlyprop> props> property> bean> <-- 通过扫描的模式,扫描目录在com/hoo/mapper目录下,所有的mapper都继承SqlMapper接口的接口, 这样一个bean就可以了 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.hoo.mapper"/> <property name="markerInterface" value="com.hoo.mapper.SqlMapper"/> <bean> <beans> ~~~ DataSource,这里的是采用jdbc的dataSource; SqlSessionFactory,是MyBatis团队提供整合Spring的SqlSession工厂Bean用它可以完成Spring和MyBatis的整合。SqlSessionFactoryBean需要注入DataSource,配置myBatis配置文件的location,以及配置mapper.xml和resultMap.xml文件的路径,可以用通配符模式配置。其实mapper里面是可以存放resultMap的内容的。由于resultMap文件的内容是和JavaBean及数据库表对象进行映射的。一般一张表、一个JavaBean(Model)对应一个resultMap文件。将resultMap独立出来提供可读性、维护性。 TransactionManager,事务管理器是采用jdbc的管理器。需要注入DataSource数据源,这里注入的数据源和SqlSessionFactory是同一个数据源。如果不同的数据源,事务将无法起到作用。 baseTransactionProxy,事务的传播特性才有spring提供的TransactionProxyFactoryBean这个事务代理工厂的拦截器类来完成。其他的暂时还没有可以支持事务配置的传播特性和隔离级别的方法,关于这里你可以参考:[http://www.cnblogs.com/hoojo/archive/2011/04/15/2017447.html](http://www.cnblogs.com/hoojo/archive/2011/04/15/2017447.html) MapperScannerConfigurer是MyBatis的mapper接口的扫描器,通过这个配置可以完成对指定basePackage报下的类进行扫描,如果这些类有继承SqlMapper这个类的,将会是MyBatis的接口。不需要单独配置mapper,而完成注入。 4、 在src目录添加applicationContext-beans.xml这个文件,文件内容如下: ~~~ <xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <-- 注解探测器 --> <context:component-scan base-package="com.hoo"/> <beans> ~~~ 这里面可以完成一下Action、Bean的注入配置 5、 JavaBean(Model、Entity)相关类、及resultMap.xml文件内容 Bean ~~~ package com.hoo.entity; import java.io.Serializable; import java.util.Date; public class Account implements Serializable { private static final long serialVersionUID = -7970848646314840509L; private Integer accountId; private String username; private String password; private Date createTime; public Account() { super(); } //getter、setter @Override public String toString() { return this.accountId + "#" + this.username + "#" + this.password + "#" + this.createTime; } } account-resultMap.xml xml version="1.0" encoding="UTF-8"?> DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="accountMap"> <resultMap type="com.hoo.entity.Account" id="accountResultMap"> <id property="accountId" column="account_id"/> <result property="username" column="username"/> <result property="password" column="password"/> <result property="createTime" column="create_time"/> <resultMap> <mapper> ~~~ resultMap的type属性和对应的classpath对应,id会在mapper.xml文件中用到。下面的属性property是JavaBean的属性,column和数据库的字段对应。 6、 上面的applicationContext-common.xml中配置了SqlMapper,下面是SqlMapper代码,就一个空接口 ~~~ package com.hoo.mapper; /** * function:所有的Mapper继承这个接口 * @author hoojo * @createDate 2011-4-12 下午04:00:31 * @file SqlMapper.java * @package com.hoo.mapper * @project MyBatisForSpring * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ public interface SqlMapper { } ~~~ 7、 定制我们自己的增删改查(CRUD)组件,接口如下 ~~~ package com.hoo.mapper; import java.util.List; import org.springframework.dao.DataAccessException; /** * function: BaseSqlMapper继承SqlMapper,对Mapper进行接口封装,提供常用的增删改查组件; * 也可以对该接口进行扩展和封装 * @author hoojo * @createDate 2011-4-14 上午11:36:41 * @file BaseSqlMapper.java * @package com.hoo.mapper * @project MyBatisForSpring * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ public interface BaseSqlMapper<T> extends SqlMapper { public void add(T entity) throws DataAccessException; public void edit(T entity) throws DataAccessException; public void remvoe(T entity) throws DataAccessException; public T get(T entity) throws DataAccessException; public List<T> getList(T entity) throws DataAccessException; } ~~~ 当然实际开发中,增删改查组件一定不止这几个方法。这里只是随便挪列几个方法做一个示例。实际中可以根据需求进行添加方法,这里添加的方法可以用一个mapper.xml进行实现,然后注入这个mapper即可完成操作。也可以定义其他的mapper,如AccountMapper继承这个接口。然后为AccountMapper提供实现也是可以的。 8、 下面看看AccountMapper接口 ~~~ package com.hoo.mapper; import java.util.List; import com.hoo.entity.Account; /** * function:继承SqlMapper,MyBatis数据操作接口;此接口无需实现类 * @author hoojo * @createDate 2010-12-21 下午05:21:20 * @file AccountMapper.java * @package com.hoo.mapper * @project MyBatis * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ public interface AccountMapper<T extends Account> extends BaseSqlMapper<T> { public List<T> getAllAccount(); } ~~~ 上面的AccountMapper继承了BaseSqlMapper,并且提供了自己所需要的方法。下面实现这个Mapper中和父接口的方法。 account-mapper.xml ~~~ <xml version="1.0" encoding="UTF-8"?> <DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <-- namespace和定义的Mapper接口对应,并实现其中的方法 --> <mapper namespace="com.hoo.mapper.AccountMapper"> <select id="getList" parameterType="com.hoo.entity.Account" resultType="list" resultMap="accountResultMap"> select * from account where username like '%' #{username} '%' select> <select id="getAllAccount" resultType="list" resultMap="accountResultMap"> select * from account select> <-- accountResultMap是account-resultmap.xml中定义的resultmap --> <select id="get" parameterType="account" resultType="com.hoo.entity.Account" resultMap="accountResultMap"> [CDATA[ select * from account where account_id = #{accountId} ]]> select> <-- 自动生成id策略 --> <insert id="add" useGeneratedKeys="true" keyProperty="account_id" parameterType="account"> insert into account(account_id, username, password) values(#{accountId}, #{username}, #{password}) insert> <update id="edit" parameterType="account"> update account set username = #{username}, password = #{password} where account_id = #{accountId} update> <delete id="remove" parameterType="account"> delete from account where account_id = #{accountId} <delete> <mapper> ~~~ mapper的namespace和接口的classpath对应,里面的sql语句的id和方法名称对应。这样就完成了AccountMapper的实现。 9、 下面来测试下AccountMapper的功能,代码如下: ~~~ package com.hoo.mapper; import java.util.List; import javax.inject.Inject; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit38.AbstractJUnit38SpringContextTests; import com.hoo.entity.Account; /** * function: AccountMapper JUnit测试类 * @author hoojo * @createDate 2011-4-12 下午04:21:50 * @file AccountMapperTest.java * @package com.hoo.mapper * @project MyBatisForSpring * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ @ContextConfiguration("classpath:applicationContext-*.xml") public class AccountMapperTest extends AbstractJUnit38SpringContextTests { @Inject private AccountMapper<Account> mapper; public void testGetAccount() { Account acc = new Account(); acc.setAccountId(28); System.out.println(mapper.get(acc)); } public void testAdd() { Account account = new Account(); account.setUsername("lisi@155.com"); account.setPassword("abc"); mapper.add(account); } public void testEditAccount() { Account acc = new Account(); acc.setAccountId(28); acc = mapper.get(acc); System.out.println(acc); acc.setUsername("Zhangsan22"); acc.setPassword("123123"); mapper.edit(acc); System.out.println(mapper.get(acc)); } public void testRemoveAccount() { Account acc = new Account(); acc.setAccountId(28); mapper.remvoe(acc); System.out.println(mapper.get(acc)); } public void testAccountList() { List<Account> acc = mapper.getAllAccount(); System.out.println(acc.size()); System.out.println(acc); } public void testList() { Account acc = new Account(); acc.setUsername("@qq.com"); List<Account> list = mapper.getList(acc); System.out.println(list.size()); System.out.println(list); } } ~~~ 运行上面的方法,没有错误基本上就算完成了Mapper的功能了。 10、 下面来写一个数据库操作的基类Dao,代码如下: ~~~ package com.hoo.dao; import java.util.List; import com.hoo.mapper.BaseSqlMapper; /** * function: * @author hoojo * @createDate 2011-4-14 上午11:30:09 * @file BaseMapperDao.java * @package com.hoo.dao * @project MyBatisForSpring * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ public interface BaseMapperDao<T> { @SuppressWarnings("unchecked") public void setMapperClass(Class<? extends BaseSqlMapper> mapperClass); public BaseSqlMapper<T> getMapper(); public boolean add(T entity) throws Exception; public boolean edit(T entity) throws Exception; public boolean remove(T entity) throws Exception; public T get(T entity) throws Exception; public List<T> getAll(T entity) throws Exception; } ~~~ 这个Dao定义了BaseSqlMapper中的方法,还提供了一个setMapperClass的方法。目的是在使用这个dao的时候,需要设置一个Mapper的class。且这个Mapper必须要是BaseSqlMapper或它的子类。所有的dao都可以用BaseMapperDao来完成CRUD操作即可,当BaseMapperDao的方法不够用的情况下,可以在接口中提供SqlSession、SqlSessionTemplate方法,让底层人员自己扩展所需要的方法。这里的dao和mapper好像有点冗余,因为dao和mapper完成的功能都是类似或是相同的。但是你可以在dao中,同时调用不同的mapper,来完成当前模块的dao所需要的功能。 11、 看看BaseMapperDao的实现代码 ~~~ package com.hoo.dao.impl; import java.util.List; import javax.inject.Inject; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionTemplate; import org.springframework.stereotype.Repository; import com.hoo.dao.BaseMapperDao; import com.hoo.mapper.BaseSqlMapper; /** * function:运用SqlSessionTemplate封装Dao常用增删改方法,可以进行扩展 * @author hoojo * @createDate 2011-4-14 下午12:22:07 * @file BaseMapperDaoImpl.java * @package com.hoo.dao.impl * @project MyBatisForSpring * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ @SuppressWarnings("unchecked") @Repository public class BaseMapperDaoImpl<T> extends SqlSessionTemplate implements BaseMapperDao<T> { @Inject public BaseMapperDaoImpl(SqlSessionFactory sqlSessionFactory) { super(sqlSessionFactory); } private Class<? extends BaseSqlMapper> mapperClass; public void setMapperClass(Class<? extends BaseSqlMapper> mapperClass) { this.mapperClass = mapperClass; } public BaseSqlMapper<T> getMapper() { return this.getMapper(mapperClass); } public boolean add(T entity) throws Exception { boolean flag = false; try { this.getMapper().add(entity); flag = true; } catch (Exception e) { flag = false; throw e; } return flag; } public boolean edit(T entity) throws Exception { boolean flag = false; try { this.getMapper().edit(entity); flag = true; } catch (Exception e) { flag = false; throw e; } return flag; } public T get(T entity) throws Exception { return this.getMapper().get(entity); } public List<T> getAll() throws Exception { return this.getMapper().getList(null); } public boolean remove(T entity) throws Exception { boolean flag = false; try { this.getMapper().remvoe(entity); flag = true; } catch (Exception e) { flag = false; throw e; } return flag; } } ~~~ 这个实现类继承了SqlSessionTemplate,并且要注入SqlSessionFactory。提供的setMapperClass方法需要设置其BaseSqlMapper或它的子类。 好了,至此基本的CRUD的mapper和dao就算完成了。现在我们可以定义我们自己的业务模块,调用公共组件来完成增删改查操作。 12、 下面测试下这个BaseMapperDao功能 ~~~ package com.hoo.dao; import javax.inject.Inject; import org.junit.Before; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit38.AbstractJUnit38SpringContextTests; import com.hoo.entity.Account; import com.hoo.mapper.AccountMapper; /** * function: * @author hoojo * @createDate 2011-4-14 下午01:08:49 * @file BaseMapperDaoImplTest.java * @package com.hoo.dao * @project MyBatisForSpring * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ @ContextConfiguration("classpath:applicationContext-*.xml") public class BaseMapperDaoImplTest extends AbstractJUnit38SpringContextTests { @Inject private BaseMapperDao<Account> dao; @Before public void init() { System.out.println(dao); dao.setMapperClass(AccountMapper.class); } public void testGet() throws Exception { init(); Account acc = new Account(); acc.setAccountId(28); System.out.println(dao.get(acc)); } public void testAdd() throws Exception { init(); Account account = new Account(); account.setUsername("temp@156.com"); account.setPassword("abc"); System.out.println(dao.add(account)); } } ~~~ 13、 下面定义AccountDao接口 ~~~ package com.hoo.dao; import java.util.List; import org.springframework.dao.DataAccessException; /** * function: Account数据库操作dao接口 * @author hoojo * @createDate 2011-4-13 上午10:21:38 * @file AccountDao.java * @package com.hoo.dao * @project MyBatisForSpring * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ public interface AccountDao<T> { /** * function: 添加Account对象信息 * @author hoojo * @createDate 2011-4-13 上午11:50:05 * @param entity Account * @return boolean 是否成功 * @throws DataAccessException */ public boolean addAccount(T entity) throws DataAccessException; /** * function: 根据id对到Account信息 * @author hoojo * @createDate 2011-4-13 上午11:50:45 * @param id 编号id * @return Account * @throws DataAccessException */ public T getAccount(Integer id) throws DataAccessException; /** * function: 查询所有Account信息 * @author hoojo * @createDate 2011-4-13 上午11:51:45 * @param id 编号id * @return Account * @throws DataAccessException */ public List<T> getList() throws DataAccessException; } ~~~ 14、 AccountDao实现类 ~~~ package com.hoo.dao.impl; import java.util.List; import javax.inject.Inject; import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Repository; import com.hoo.dao.AccountDao; import com.hoo.dao.BaseMapperDao; import com.hoo.entity.Account; import com.hoo.mapper.AccountMapper; /** * function: Account数据库操作dao * @author hoojo * @createDate 2011-4-13 上午10:25:02 * @file AccountDaoImpl.java * @package com.hoo.dao.impl * @project MyBatisForSpring * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ @SuppressWarnings("unchecked") @Repository public class AccountDaoImpl<T extends Account> implements AccountDao<T> { @Inject private BaseMapperDao<Account> dao; public boolean addAccount(T entity) throws DataAccessException { dao.setMapperClass(AccountMapper.class); boolean flag = false; try { dao.add(entity); flag = true; } catch (DataAccessException e) { flag = false; throw e; } catch (Exception e) { throw new RuntimeException(e); } return flag; } public T getAccount(Integer id) throws DataAccessException { dao.setMapperClass(AccountMapper.class); Account acc = new Account(); T entity = null; try { acc.setAccountId(id); entity = (T) dao.get(acc); } catch (DataAccessException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } return entity; } public List<T> getList() throws DataAccessException { dao.setMapperClass(AccountMapper.class); List<T> list = null; try { list = (List<T>) ((AccountMapper)dao.getMapper()).getAllAcount(); } catch (DataAccessException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } return list; } } ~~~ 注意,上面的方法都设置了MapperClass,表示当前dao的Mapper是AccountMapper对象。所有的mapper方法都是调用AccountMapper这个对象中的方法。 15、 下面定义服务器层接口 ~~~ package com.hoo.biz; import java.util.List; import org.springframework.dao.DataAccessException; /** * function: biz层Account接口 * @author hoojo * @createDate 2011-4-13 上午11:33:04 * @file AccountBiz.java * @package com.hoo.biz * @project MyBatisForSpring * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ public interface AccountBiz<T> { /** * function: 添加Account对象信息 * @author hoojo * @createDate 2011-4-13 上午11:50:05 * @param entity Account * @return boolean 是否成功 * @throws DataAccessException */ public boolean addAccount(T entity) throws DataAccessException; public boolean execute(T entity) throws DataAccessException; /** * function: 根据id对到Account信息 * @author hoojo * @createDate 2011-4-13 上午11:50:45 * @param id 编号id * @return Account * @throws DataAccessException */ public T getAccount(Integer id) throws DataAccessException; /** * function: 查询所有Account信息 * @author hoojo * @createDate 2011-4-13 上午11:51:45 * @param id 编号id * @return Account * @throws DataAccessException */ public List<T> getList() throws DataAccessException; } ~~~ 16、 AccountBiz的实现代码 ~~~ package com.hoo.biz.impl; import java.util.List; import javax.inject.Inject; import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Component; import com.hoo.biz.AccountBiz; import com.hoo.dao.AccountDao; import com.hoo.entity.Account; import com.hoo.exception.BizException; /** * function: Account Biz接口实现 * @author hoojo * @createDate 2011-4-13 上午11:34:39 * @file AccountBizImpl.java * @package com.hoo.biz.impl * @project MyBatisForSpring * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ //@Component @Service public class AccountBizImpl<T extends Account> implements AccountBiz<T> { @Inject private AccountDao<T> dao; public boolean addAccount(T entity) throws DataAccessException { if (entity == null) { throw new BizException(Account.class.getName() + "对象参数信息为Empty!"); } return dao.addAccount(entity); } public T getAccount(Integer id) throws DataAccessException { return dao.getAccount(id); } public List<T> getList() throws DataAccessException { return dao.getList(); } public boolean execute(T entity) throws DataAccessException { if (entity == null) { throw new BizException(Account.class.getName() + "对象参数信息为Empty!"); } return dao.addAccount(entity); } } ~~~ 直接注入AccountDao完成相关操作即可 17、 如果你需要在业务层设置事务传播特性,需要在applicationContext-bean.xml中加入配置如下: ~~~ <-- 为AccountBiz接口配置事务拦截器,baseTransactionProxy是事务拦截器,在Action中获取这个对象 --> <bean id="accountBiz" parent="baseTransactionProxy"> <-- 设置target,也就是AccountBiz的实现类 --> <property name="target" ref="accountBizImpl"/> <bean> ~~~ 这样在Action中注入accountBiz这个对象后,那么当这个对象出现DataAccessException异常的时候,就会自动回滚事务。 至此,Spring和MyBatis的整合就完成了。
';

ExtJS 文件浏览器,可以选择文件和文件夹

最后更新于:2022-04-01 12:01:17

话说long long ago,在本人开发项目时,需要导入一个文件夹(目录)下的文件,通过解析其中的数据并入库。 选择一个文件目录,好像没有这个控件。开始想到了不选目录,选文件。但是要选多个文件哦,而且数目不固定。 用file文件浏览不好,想到了用swfUpload可以选择多个文件。可以做到,但是还是选择文件不是选择目录。 不过我想要的,想呀想的…… 诶~可以用ExtJS,自己扩展一个还是可以的。于是就有了今天这篇文章和这个文件浏览器。 extFileBrowser.html ~~~ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Ext 文件浏览选择器</title> <meta http-equiv="author" content="hoojo"> <meta http-equiv="email" content="hoojo_@126.com"> <meta http-equiv="blog" content="http://blog.csdn.net/IBM_hoojo"> <meta http-equiv="ext-lib" content="v2.2.1"> <meta http-equiv="version" content="v1.0"> <meta http-equiv="content-type" content="text/html; charset=gbk"> <link rel="stylesheet" type="text/css" href="ext-2.2/resources/css/ext-all.css" /> <script type="text/javascript" src="ext-2.2/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="ext-2.2/ext-all.js"></script> <script type="text/javascript" src="Ext.hoo.component.FileBrowserComponent.js"></script> <script type="text/javascript"> Ext.onReady(function(){ Ext.BLANK_IMAGE_URL = "ext-2.2/resources/images/default/s.gif"; var fileBrowser = new Ext.hoo.component.FileBrowserWindow(); //var fileBrowser = new Ext.hoo.component.FileBrowserPanel(); fileBrowser.show(); fileBrowser.tree.getSelectionModel().on("beforeselect", function (sm, node) { //只能选择文件夹,如果要选择文件修改这里即可 var flag = ((!node || (!!node && !!node.leaf)) || !(node.attributes.path.indexOf(":") != -1)) ? true : false; fileBrowser.buttons[0].setDisabled(flag); fileBrowser.buttons[1].setDisabled(flag); }, fileBrowser.tree); }); </script> </head> <body> </body> </html> ~~~ Ext.hoo.component.FileBrowserComponent.js ~~~ /** * Ext.hoo.component.FileBrowserWindow 系统文件浏览选择组件,可以选定电脑上的文件或文件夹 * @author: hoojo * @createDate 2010-10-17 * @email: hoojo_@126.com * @blog: http://blog.csdn.net/IBM_hoojo * @ext_lib: v2.2 * @version 1.0 */ Ext.ns("Ext.hoo.component"); Ext.hoo.component.FileBrowserWindow = Ext.extend(Ext.Window, { constructor: function (config) { config = config || {}; Ext.apply(this, config); this.tree = new Ext.hoo.tree.FileSystemTree(); Ext.hoo.component.FileBrowserWindow.superclass.constructor.call(this, { renderTo: Ext.getBody(), width: 300, height: 300, frame: true, layout: "fit", border: false, title: "请选择", items: this.tree, buttons: [{ text: "新建", disabled: true, handler: this.onNewHandler, scope: this }, { text: "确定", disabled: true, handler: this.onOkHandler, scope: this }, { text: "取消", handler: function () { this.hide(Ext.getBody()); }, scope: this }] }); }, onNewHandler: function () { this.setPath(); this.setFile(); Ext.Msg.prompt("新建文件", "请输入文件夹名称", this.onCreateDir, this); }, onOkHandler: function () { this.setPath(); this.setFile(); Ext.Msg.alert("路径", this.getPath()); }, onCreateDir: function (btn, text) { if (btn == "ok") { var path = this.getPath(); var node = this.getFile(); var dirName = text; if (!!path && !!dirName) { //本地添加模式 /*var newNode = new Ext.tree.AsyncTreeNode({ text: dirName, path: node.attributes.path + "/" + dirName }); node.expand(true, true); node.appendChild(newNode);*/ //远程加载模式 Ext.Ajax.request({ url: Ext.hoo.tree.FileSystemTree.TREE_CREATE_DIR_URL, params: {path: encodeURIComponent(path), dirName: encodeURIComponent(dirName)},//处理中文文件名,乱码问题 success: function (response, options) { var returnNnode = Ext.decode(response.responseText); node.appendChild(returnNnode); node.expand(true); }, failure: function (response) { Ext.Msg.alert("程序异常", response.responseText); } }); } } }, setPath: function () { this.path = this.tree.getSelectedNode().attributes.path || ""; }, setFile: function () { this.nodeFile = this.tree.getSelectedNode() || {}; }, getPath: function () { return this.path; }, getFile: function () { return this.nodeFile; } }); /** * Ext.hoo.component.FileBrowserPanel 系统文件浏览选择组件,可以选定电脑上的文件或文件夹 * 不同于上面的是,这里是一个panel。有时候弹出window,并不能达到预想的效果。特别是window弹出在 * iframe中的Object对象上面,如:在播放器上面弹出此组件,拖动windwo的效果不理想。 * 这时就需要用模态,模态嵌入FileBrowserPanel组件即可 * @author: hoojo * @createDate 2010-10-17 * @email: hoojo_@126.com * @blog: http://blog.csdn.net/IBM_hoojo * @ext_lib: v2.2 * @version 1.0 */ Ext.hoo.component.FileBrowserPanel = Ext.extend(Ext.Panel, { constructor: function (config) { config = config || {}; Ext.apply(this, config); this.tree = new Ext.hoo.tree.FileSystemTree(); Ext.hoo.component.FileBrowserPanel.superclass.constructor.call(this, { renderTo: Ext.getBody(), border: false, width: 300, height: 400, layout: "fit", title: "请选择", items: this.tree, buttons: [{ text: "新建", disabled: true, handler: this.onNewHandler, scope: this }, { text: "确定", disabled: true, handler: function () { this.path = this.tree.getSelectedNode().attributes.path || ""; this.nodeFile = this.tree.getSelectedNode() || {}; //window.returnValue = this.path; //window.close(); Ext.Msg.alert("路径", this.path); }, scope: this }, { text: "取消", handler: function () { this.hide(Ext.getBody()); //window.close(); }, scope: this }] }); }, onNewHandler: function () { this.setPath(); this.setFile(); Ext.Msg.prompt("新建文件", "请输入文件夹名称", this.onCreateDir, this); }, onCreateDir: function (btn, text) { if (btn == "ok") { var path = this.getPath(); var node = this.getFile(); var dirName = text; if (!!path && !!dirName) { //本地添加模式 /*var newNode = new Ext.tree.AsyncTreeNode({ text: dirName, path: node.attributes.path + "/" + dirName }); node.expand(true, true); node.appendChild(newNode);*/ //远程加载模式 Ext.Ajax.request({ url: Ext.hoo.tree.FileSystemTree.TREE_CREATE_DIR_URL, params: {path: encodeURIComponent(path), dirName: encodeURIComponent(dirName)},//处理中文文件名,乱码问题 success: function (response, options) { var returnNnode = Ext.decode(response.responseText); node.appendChild(returnNnode); node.expand(true, true); }, failure: function (response) { Ext.Msg.alert("程序异常", response.responseText); } }); } } }, setPath: function () { this.path = this.tree.getSelectedNode().attributes.path || ""; }, setFile: function () { this.nodeFile = this.tree.getSelectedNode() || {}; }, getPath: function () { return this.path; }, getFile: function () { return this.nodeFile; } }); /** * Ext.hoo.tree.FileSystemTree 系统文件树,显示所有的文件 * @author: hoojo * @createDate 2010-10-17 * @email: hoojo_@126.com * @blog: http://blog.csdn.net/IBM_hoojo * @ext_lib: v2.2 * @version 1.0 */ Ext.ns("Ext.hoo.tree"); Ext.hoo.tree.FileSystemTree = Ext.extend(Ext.tree.TreePanel, { constructor: function () { Ext.hoo.tree.FileSystemTree.superclass.constructor.call(this, { //rootVisible: false, autoScroll: true, root: new Ext.tree.AsyncTreeNode({ text: "My System Files", id: "0", path: "root", children:[] }), listeners: { expandnode: { fn: this.onExpandNode, scope: this } } }); }, onExpandNode: function (node) { //只对未加载过的添加子结点,加载后不在重复加载;避免增加请求,浪费资源 if (!node.attributes.isLoad) { Ext.Ajax.request({ url: Ext.hoo.tree.FileSystemTree.TREE_DATA_URL, params: {path: encodeURIComponent(node.attributes.path)},//处理中文文件名,乱码问题 success: function (response, options) { node.attributes.isLoad = true;//设置加载标示 var nodes = Ext.decode(response.responseText); node.appendChild(nodes); }, failure: function (response) { Ext.Msg.alert("程序异常", response.responseText); } }); } }, getSelectedNode: function () { return this.getSelectionModel().getSelectedNode(); } }); Ext.hoo.tree.FileSystemTree.TREE_CREATE_DIR_URL = "http://localhost:8080/Test/FileBrowser?method=mkDir"; Ext.hoo.tree.FileSystemTree.TREE_DATA_URL = "http://localhost:8080/Test/FileBrowser?method=getData"; ~~~ 服务器端java code: FileBrowser Servlet: ~~~ package com.hoo.servlet; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.net.URLDecoder; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.swing.filechooser.FileSystemView; import net.sf.json.JSONArray; import com.hoo.entity.FileInfo; import com.hoo.util.FileUtils; /** * <b>function:</b> 查询本地硬盘文件数据、创建目录 * @project Test * @package com.hoo.servlet * @fileName FileBrowser.java * @author hoojo */ public class FileBrowser extends HttpServlet { private static final long serialVersionUID = 1599390137455995515L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); response.setCharacterEncoding("UTF-8"); PrintWriter out = response.getWriter(); String path = request.getParameter("path"); path = path == null ? "" : URLDecoder.decode(path, "UTF-8"); String method = request.getParameter("method"); FileInfo info = new FileInfo(); if ("getData".equals(method)) { if ("root".equals(path)) { FileSystemView fsv = FileSystemView.getFileSystemView(); File[] roots = fsv.getRoots(); //File.listRoots(); //桌面 for (File f : roots) { info.getChildren().add(FileUtils.getFileInfo(f)); } for (File f : roots[0].listFiles()) { if (f.getName().contains("My Documents")) { info.getChildren().add(FileUtils.getFileInfo(f)); } } FileInfo fileInfo = new FileInfo(); fileInfo.setName("我的电脑"); fileInfo.setPath("My Computer"); for (File fi : roots[0].listFiles()[0].listFiles()) { fileInfo.getChildren().add(FileUtils.getFileInfo(fi)); } info.getChildren().add(fileInfo); fileInfo = new FileInfo(); fileInfo.setName("网上邻居"); fileInfo.setPath("Network Place"); for (File fi : roots[0].listFiles()[1].listFiles()) { fileInfo.getChildren().add(FileUtils.getFileInfo(fi)); } info.getChildren().add(fileInfo); out.print(JSONArray.fromObject(info.getChildren()).toString()); } else if (path != null && !"".equals(path)) { FileUtils.getFileInfo(info, new File(path), new String[] {"*"}); out.print(JSONArray.fromObject(info.getChildren()).toString()); } } if ("mkDir".equals(method)) { String dirName = request.getParameter("dirName"); dirName = dirName == null ? "" : URLDecoder.decode(dirName, "UTF-8"); boolean success = false; try { success = FileUtils.mkDir(path, dirName); FileInfo node = FileUtils.getFileInfo(new File(FileUtils.getDoPath(path) + dirName)); out.print(JSONArray.fromObject(node)); } catch (Exception e) { e.printStackTrace(); success = false; } System.out.println(success); } out.flush(); out.close(); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } } ~~~ 这个类用到了json-lib.jar工具包,此包可以帮我们把java对象,包括list、map、array序列化成json的字符串。 至少用到以下依赖包: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe1228114e7.gif) FileInfo 封装文件信息的java Bean: ~~~ package com.hoo.entity; import java.util.ArrayList; import java.util.List; /** * <b>function:</b>文件信息 * @author hoojo * @createDate Oct 10, 2010 9:53:51 PM * @file FileInfo.java * @package com.hoo.entity * @project MultiUpload * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ public class FileInfo { //文件id private String id; //文件名称 private String name; private String text; //文件路径 private String path; //是否有目录,有无子节点 private boolean leaf; //修改日期 private String editDate; //后缀 private String suffix; //长度 private long length; // 子目录中所有文件 private List<FileInfo> children = new ArrayList<FileInfo>(); //setter、getter public String toString() { return "name:" + name + ", size:" + children.size(); } } ~~~ FileUtils 文件操作工具类 ~~~ package com.hoo.util; import java.io.File; import java.text.SimpleDateFormat; import java.util.Date; import java.util.UUID; import com.hoo.entity.FileInfo; /** * <b>function:</b> 磁盘文件操作工具类 * @project Test * @package com.hoo.util * @fileName FileUtils.java * @createDate 2010-10-4 下午03:32:42 * @author hoojo */ @SuppressWarnings("unused") public abstract class FileUtils { /** * <b>function:</b>传递一个File,返回该文件的FileInfo实体类 * @author hoojo * @createDate Oct 10, 2010 10:10:19 PM * @param file File * @return FileInfo */ public static FileInfo getFileInfo(File file) { FileInfo info = new FileInfo(); if (file != null) { info.setId(UUID.randomUUID().toString()); if (file.getName() == null || "".equals(file.getName()) || "::".equals(file.getName())) { info.setName(file.getAbsolutePath()); } else { info.setName(file.getName()); } //info.setLeaf(file.isFile()); info.setLeaf(!file.isDirectory()); info.setLength(file.length()); info.setPath(getDoPath(file.getAbsolutePath())); info.setSuffix(getType(file.getName())); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date date = new Date(); date.setTime(file.lastModified()); info.setEditDate(sdf.format(date)); } return info; } public static void setFileInfo(File file, FileInfo info) { if (file != null && info != null) { info.setId(UUID.randomUUID().toString()); if (file.getName() == null || "".equals(file.getName()) || "::".equals(file.getName())) { info.setName(file.getAbsolutePath()); } else { info.setName(file.getName()); } //info.setLeaf(file.isFile()); info.setLeaf(!file.isDirectory()); info.setLength(file.length()); info.setPath(getDoPath(file.getAbsolutePath())); info.setSuffix(getType(file.getName())); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date date = new Date(); date.setTime(file.lastModified()); info.setEditDate(sdf.format(date)); } } /** * <b>function:</b>处理后的系统文件路径 * @author hoojo * @createDate Oct 10, 2010 12:49:31 AM * @param path 文件路径 * @return 返回处理后的路径 */ public static String getDoPath(String path) { path = path.replace("//", "/"); String lastChar = path.substring(path.length() - 1); if (!"/".equals(lastChar)) { path += "/"; } return path; } /** * <b>function:</b>和文件后缀一样,不同的是没有“.” * @author hoojo * @createDate Oct 10, 2010 2:42:43 PM * @param fileName 文件名称 * @return */ public static String getType(String fileName) { int index = fileName.lastIndexOf("."); if (index != -1) { String suffix = fileName.substring(index + 1);//后缀 return suffix; } else { return null; } } /** * <b>function:</b> 得到指定目录下所有的文件集合 * @createDate 2010-10-20 下午02:20:06 * @author hoojo * @param info 将数据设置在该变量中 * @param file 文件目录 */ public static void getAllFileInfo(FileInfo info, File file) { if (file.isDirectory()) { long size = 0; File[] allFiles = file.listFiles(); for (File f : allFiles) { size += f.length(); FileInfo fi = getFileInfo(f); info.getChildren().add(fi); getAllFileInfo(fi, f); } info.setLength(size); } } /** * <b>function:</b> 得到当前目录所有文件 * @createDate 2010-10-20 下午02:21:06 * @author hoojo * @param info 文件对象 * @param file 目录 */ public static void getFileInfo(FileInfo info, File file, String[] allowTypes) { if (file.isDirectory()) { long size = 0; File[] allFiles = file.listFiles(); for (File f : allFiles) { size += f.length(); FileInfo fi = getFileInfo(f); if (f.isDirectory()) { info.getChildren().add(fi); } else { if (validTypeByName(f.getName(), allowTypes, true)) { info.getChildren().add(fi); } } } info.setLength(size); } } /** * <b>function:</b> 根据文件名和类型数组验证文件类型是否合法,flag是否忽略大小写 * @author hoojo * @createDate Oct 10, 2010 11:54:54 AM * @param fileName 文件名 * @param allowTypes 类型数组 * @param flag 是否获得大小写 * @return 是否验证通过 */ public static boolean validTypeByName(String fileName, String[] allowTypes, boolean flag) { String suffix = getType(fileName); boolean valid = false; if (allowTypes.length > 0 && "*".equals(allowTypes[0])) { valid = true; } else { for (String type : allowTypes) { if (flag) {//不区分大小写后缀 if (suffix != null && suffix.equalsIgnoreCase(type)) { valid = true; break; } } else {//严格区分大小写 if (suffix != null && suffix.equals(type)) { valid = true; break; } } } } return valid; } /** * <b>function:</b> 在path目录下创建目录 * @createDate 2010-11-3 下午04:03:34 * @author hoojo * @param path * @param dirName * @return */ public static boolean mkDir(String path, String dirName) { boolean success = false; File file = new File(getDoPath(path) + dirName); if (!file.exists()) { success = file.mkdirs(); } return success; } } ~~~ ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe1228362b7.gif) 点击新建可以创建新目录,确定可以获取选择的路径。
';

Ext 中,为Ext.form.HtmlEditor添加键盘事件

最后更新于:2022-04-01 12:01:15

这里扩展了Ext.form.HtmlEditor组件,为其添加了keyup,keydown,keypress事件监听。重写了Ext.form.HtmlEditor的方法: initEditor、initComponent; 重写后的Ext.form.HtmlEditor示例: ~~~ /*** * 重写Ext.form.HtmlEditor,为其添加键盘事件 * author: hoojo * email: hoojo_@126.com * blog: http://blog.csdn.net/IBM_hoojo * create by: 2010-8-14 * ext-lib: 3.2.1 * version: 1.0 */ Ext.override(Ext.form.HtmlEditor, { initEditor : function(){ var dbody = this.getEditorBody(); var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat'); ss['background-attachment'] = 'fixed'; // w3c ss['background-color'] = 'white'; dbody.bgProperties = 'fixed'; // ie Ext.DomHelper.applyStyles(dbody, ss); if(this.doc){ try{ Ext.EventManager.removeAll(this.doc); }catch(e){} } this.doc = this.getDoc(); Ext.EventManager.on(this.doc, { 'mousedown': this.onEditorEvent, 'dblclick': this.onEditorEvent, 'click': this.onEditorEvent, 'keyup': this.onEditorKeyUpEvent, 'keydown': this.onEditorKeyDownEvent, 'keypress': this.onEditorKeyPressEvent, buffer:100, scope: this }); if(Ext.isGecko){ Ext.EventManager.on(this.doc, 'keypress', this.applyCommand, this); } if(Ext.isIE || Ext.isSafari || Ext.isOpera){ Ext.EventManager.on(this.doc, 'keydown', this.fixKeys, this); } this.initialized = true; this.fireEvent('initialize', this); this.doc.editorInitialized = true; this.pushValue(); }, initComponent: function () { this.addEvents( 'initialize', 'activate', 'beforesync', 'beforepush', 'sync', 'push', 'editmodechange', 'keydown', 'keyup', 'keypress' ); }, onEditorKeyPressEvent : function(e){ this.updateToolbar(); this.fireEvent("keypress", this, e); }, onEditorKeyUpEvent : function(e){ this.updateToolbar(); this.fireEvent("keyup", this, e); }, onEditorKeyDownEvent : function(e){ this.updateToolbar(); this.fireEvent("keydown", this, e); } }); /** * 重写后的Ext.form.HtmlEditor有了键盘的keyPress事件了 */ Ext.ns("Ext.hoo.editor"); Ext.hoo.editor.HTMLEditor = Ext.extend(Ext.form.HtmlEditor, { constructor: function () { Ext.hoo.editor.HTMLEditor.superclass.constructor.call(this, { renderTo: Ext.getBody(), fieldLabel:'Biography', height:200, listeners: { "keydown": function (editor, e) { alert("keydown:" + editor.getValue()); }, "keyup": function (editor, e) { alert("keyup:" + editor.getValue()); }, "keypress": function (editor, e) { alert("keypress:" + editor.getValue()); } } }); } }); ~~~ 注意:要添加键盘事件请添加Ext.override里的那段代码。这段是扩展代码,目的是为HtmlEditor添加键盘事件的。 html页面 ~~~ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Ext 示例</title> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="this is my page"> <meta http-equiv="content-type" content="text/html; charset=gbk"> <link rel="stylesheet" type="text/css" href="ext-3.2.1/resources/css/ext-all.css" /> <script type="text/javascript" src="ext-3.2.1/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="ext-3.2.1/ext-all.js"></script> <script type="text/javascript" src="HtmlEditor.js"></script> <script type="text/javascript"> Ext.onReady(function(){ new Ext.hoo.editor.HTMLEditor(); }); </script> </head> <body> </body> </html> ~~~
';

ExtJS EditorGridPanel 示例之JSON格式Store前后台增删改查

最后更新于:2022-04-01 12:01:12

json格式传递数据示例,入口html页面: ~~~ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>用户数据编辑 用JsonReader 实现分页</title> <meta http-equiv="Content-Type" content="text/html; charset=gbk" /> <meta http-equiv="author" content="hoojo"> <meta http-equiv="email" content="hoojo_@126.com"> <meta http-equiv="blog" content="http://blog.csdn.net/IBM_hoojo"> <link rel="stylesheet" type="text/css" href="../ext2/resources/css/ext-all.css"></link> <script type="text/javascript" src="../ext2/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="../ext2/ext-all.js"></script> <script type="text/javascript" src="../ext2/build/locale/ext-lang-zh_CN-min.js"></script> <script type="text/javascript" src="JsonPagingEditorGridPanel.js"></script> <script type="text/javascript"> Ext.onReady(function (){ Ext.QuickTips.init(); //Ext.form.Field.prototype.msgTarget = "side"; Ext.BLANK_IMAGE_URL = "../ext2/resources/images/default/s.gif"; new JsonPagingEditorGridPanel(); }); </script> </head> <body> </body> </html>`` ~~~ JsonPagingEditorGridPanel.js: ~~~ /** * 用JSON格式的数据形式:Ext.data.Store,Ext.data.JsonReader解析器,实现editorGrid的增删改查 * author: hoojo * email: hoojo_@126.com * blog: http://blog.csdn.net/IBM_hoojo * ext-lib: v2.2 */ /****************************************************************/ /*******JsonPagingEditorGridPanel*******/ /****************************************************************/ JsonPagingEditorGridPanel = Ext.extend(Ext.grid.EditorGridPanel, { sexCombo: null, inserted: [], constructor: function () { this.sexCombo = new Ext.form.ComboBox({ mode: "local", value: "全部", readOnly: true, triggerAction: "all", displayField: "sex", //listAlign : "bl-tl", //下拉列表的显示方式 bl-tl是在上方显示,相反tl-bl是从下方显示 store: new Ext.data.SimpleStore({ data: ["男", "女", "全部"], expandData: true, fields: ["sex"] }), listeners: { "select": { fn: this.filterSex, scope: this } } }); this["store"] = new Ext.data.Store({ url: JsonPagingEditorGridPanel.USER_STORE_URL, reader: new Ext.data.JsonReader({ id: "id", //维护当前数据的唯一性(和数据库主键类似,避免数据重复--过滤重复数据) root: "users", totalProperty: "totals" }, Ext.data.Record.create(["id","name", {name: "age", type: "int"}, "sex", {name: "birthday", type: "date", dateFormat: "Y-m-d"} ]) ), sortInfo:{field:"id", direction:"ASC"} //排序 }); JsonPagingEditorGridPanel.superclass.constructor.call(this, { renderTo: Ext.getBody(), width: 480, height: 300, frame: true, //loadMask:true, //显示加载旋转条 stripeRows: true, //隔行变色,区分表格行 clicksToEdit: 2, //表示点击多少次数才可以编辑表格 collapsible: true, //是否在右上角显示收缩按钮 animCollapse: true, //表示收缩(闭合)面板时,显示动画效果 trackMouseOver: true, //鼠标在行上移动时显示高亮 enableColumnMove: false,//禁止用户拖动表头 //disableSelection:true, autoExpandColumn: "name", //这里指定的name的id ,此属性可以根据当前列 填充空白区域 title: "用户数据编辑器", tbar: [ "->","查看方式:", this.sexCombo, { text: "添加数据", handler: this.onAddClick, scope: this },"-",{ text: "删除数据", handler: this.onRemoveClick, scope: this },"-",{ text: "保存数据", handler: this.onCommitStore, scope: this } ], bbar: new Ext.PagingToolbar({ //width: 480, //如果pagingToolbar不在bbar[]括号中就不需要width,否则就要指定宽度才能显示displayInfo pageSize: 5, store: this.store, displayInfo: true, displayMsg: "显示第{0}-{1}条,共有{2}条记录", emptyMsg: "没有记录" }), columns:[{ id: "name", header: "姓名", align: "center", dataIndex: "name", editor: new Ext.form.TextField({ allowBlank: false, blankText: "姓名不能为空,必须输入" }) },{ header: "年龄", align: "center", dataIndex: "age", editor: new Ext.form.NumberField({ allowBlank: false, allowNegative: false, //只能为正数 //maxValue: 1000000000, grow: true, //前半部分显示正在改的数据,后半部分显示以前的老数据 selectOnFocus: true, //当获得焦点时,选中所有的文本内容 minValue: 1 }) },{ header: "性别", align: "center", dataIndex: "sex", editor: new Ext.form.ComboBox({ mode: "local", value: "男", readOnly: true, displayField: "sex", triggerAction: "all", store: new Ext.data.SimpleStore({ data: ["男", "女"], expandData: true, fields: ["sex"] }) }) },{ header: "生日", align: "center", sortable: true, dataIndex: "birthday", renderer: Ext.util.Format.dateRenderer('Y-m-d'), editor: new Ext.form.DateField({ format: "Y-m-d", minValue: "1950-01-01", disabledDays: [0, 7],//datefield的第0列:周日和第7列:周六不能编辑 disabledDaysText: "周末不能选择" }) }], sm: new Ext.grid.RowSelectionModel({ singleSelect: true }) }); this.store.load({params: {start: 0, limit: 5}}); Ext.Ajax.on("requestcomplete", function (conn, response, options) { //alert(response.responseText); Ext.example.msg('Click','You clicked on "Action 1".'); }); }, filterSex: function (cob) { if (cob.getValue() == "全部"){ this.store.clearFilter(); } else this.store.filter("sex", cob.value); }, onAddClick: function () { var rs = new Ext.data.Record({id: "",name: "", age: 1, sex: "", birthday: 0000-00-00}); this.getStore().add(rs); rs.set("name", "ext"); rs.set("age", 22); rs.set("sex", "男"); rs.set("birthday", new Date()); this.inserted.push(rs); this.startEditing(this.store.getCount() - 1, 0); }, saveInsertData: function (conn, response) { var xml = response.responseXML; var root = xml.documentElement; for (var i = 0; i < root.childNodes.length; i++) { this.inserted[i].set("id", root.childNodes[i].text); } this.getStore().commitChanges(); this.inserted = []; }, onCommitStore: function () { var mf = this.getStore().modified; var temp = []; for (var i = 0; i < mf.length; i ++) { if (mf[i].get("id") == ""){ continue; } var data = {}; for (var j in mf[i].modified) { data[j] = mf[i].get(j); } temp.push(Ext.apply(data, {id: mf[i].get("id")})); } for (var i = 0; i < this.inserted.length; i ++) { temp.push(this.inserted[i].data); } Ext.Ajax.on("requestcomplete", function (conn, response, options) { alert(response.responseText); }); //Ext.Ajax.on("requestcomplete", this.saveInsertData, this); //想服务器发送请求,传递修改的数据(只含修改的数据) Ext.Ajax.request({url: "../ServiceServlet?method=edit", params: {content:Ext.util.JSON.encode(temp)}}); this.store.commitChanges(); this.filterSex(this.sexCombo); }, onRemoveClick: function () { var rs = this.getSelectionModel(); try{ if (rs.getCount() == 0) { Ext.Msg.alert("系统提示", "没有选定数据,请选择数据行!"); }else { Ext.Msg.confirm("系统提示", "您确定要删除当前数据吗?", this.removeUserInfo, this); } }catch(er) { Ext.Msg.alert("系统提示", er.discription); } }, removeUserInfo: function (btnText) { if (btnText == "yes"){ var rs = this.getSelectionModel().getSelected(); this.getStore().remove(rs); if (rs.get("id") != "") { Ext.Ajax.on("requestcomplete", function (conn, response, options) { if (response.responseText == "success") { alert("success"); }else { alert("failure"); } }); Ext.Ajax.request({url: "../ServiceServlet?method=remove", params: {id:rs.get("id")}}); }else { this.inserted.remove(rs); //this.getStore().modified.romove(rs); } } } }); JsonPagingEditorGridPanel.USER_STORE_URL = "http://localhost:8080/Demo3/ServiceServlet?method=json"; ~~~ 后台java code: ~~~ import java.io.PrintWriter; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.hoo.dao.IUser; import com.hoo.dao.impl.UserDao; import com.hoo.entity.UserInfo; @SuppressWarnings({"unchecked", "serial"}) public class ServiceServlet extends HttpServlet { private IUser dao = new UserDao(); public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setCharacterEncoding("GBK"); PrintWriter out = response.getWriter(); StringBuilder builder = new StringBuilder(); String method = request.getParameter("method"); if ("json".equals(method)) { int start = Integer.parseInt(request.getParameter("start")); int limit = Integer.parseInt(request.getParameter("limit")); Object[] obj = dao.loadUserInfo(start, limit); List<UserInfo> list = (List<UserInfo>) obj[1]; builder.append("{totals:").append(obj[0]).append(",users:["); for (int i = 0; i < list.size(); i++) { UserInfo u = (UserInfo) list.get(i); builder.append("{id:/"").append(u.getId()) .append("/",name:/"").append(u.getName()) .append("/",age:").append(u.getAge()) .append(",sex:/"").append(u.getSex()) .append("/",birthday:/"").append(u.getBirthday()) .append("/"}"); if (i < list.size()-1) { builder.append(","); } } builder.append("]}"); out.write(builder.toString()); } if ("edit".equals(method)) { String content = request.getParameter("content"); out.print(content); } if ("remove".equals(method)) { Integer id = Integer.parseInt(request.getParameter("id")); if (dao.removeUserInfo(id)) { out.print("success"); }else { out.print("failure"); } } out.flush(); out.close(); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } } ~~~ ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe1227c1d14.gif) 至此,ExtJS中常用的3中交互方式的示例全部在此。其中json格式交互方式最为常用。 原因是xml格式体积过于庞大,里面有标签过多。而array数组格式的文件,虽然提交很小。 传输速度快。但在数据量很大的情况下,数据的可读性几乎全无。后来出现了json, 它简化了xml文件提交庞大的问题,可读性也非常好、在网络中的传输速度也很快,且易于使用、学习。
';

ExtJS EditorGridPanel 示例之Array格式Store前后台增删改查(不支持分页)

最后更新于:2022-04-01 12:01:10

示例入口页面html: ~~~ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>用户数据编辑 User EditorGridPanel</title> <meta http-equiv="Content-Type" content="text/html; charset=gbk" /> <meta http-equiv="author" content="hoojo"> <meta http-equiv="email" content="hoojo_@126.com"> <meta http-equiv="blog" content="http://blog.csdn.net/IBM_hoojo"> <link rel="stylesheet" type="text/css" href="../ext2/resources/css/ext-all.css"></link> <script type="text/javascript" src="../ext2/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="../ext2/ext-all.js"></script> <script type="text/javascript" src="../ext2/build/locale/ext-lang-zh_CN-min.js"></script> <script type="text/javascript" src="UserEditorGridPanel.js"></script> <script type="text/javascript"> Ext.onReady(function (){ Ext.QuickTips.init(); //Ext.form.Field.prototype.msgTarget = "side"; Ext.BLANK_IMAGE_URL = "../ext2/resources/images/default/s.gif"; new UserEditorGridPanel(); }); </script> </head> <body> </body> </html> ~~~ UserEditorGridPanel.js: ~~~ /** * 用Array格式的数据形式:Ext.data.SimpleStore,默认数组解析器,实现editorGrid的增删改查 * 上一示例扩展了ArrayReader组件就支持分页,这里用默认的就不支持分页了; * author: hoojo * email: hoojo_@126.com * blog: http://blog.csdn.net/IBM_hoojo * ext-lib: v2.2 */ /****************************************************************/ /*******UserEditorGridPanel*******/ /****************************************************************/ UserEditorGridPanel = Ext.extend(Ext.grid.EditorGridPanel, { sexCombo: null, inserted: [], constructor: function () { this.sexCombo = new Ext.form.ComboBox({ mode: "local", value: "全部", readOnly: true, triggerAction: "all", displayField: "sex", //listAlign : "bl-tl", //下拉列表的显示方式 bl-tl是在上方显示,相反tl-bl是从下方显示 store: new Ext.data.SimpleStore({ data: ["男", "女", "全部"], expandData: true, fields: ["sex"] }), listeners: { "select": { fn: this.filterSex, scope: this } } }); this["store"] = new Ext.data.SimpleStore({ autoLoad: true, url: UserEditorGridPanel.USER_STORE_URL, fields: ["id","name", "age", "sex", {name: "birthday", type: "date", dateFormat: "Y-m-d"} ], sortInfo:{field:"name", direction:"ASC"} //排序 }); UserEditorGridPanel.superclass.constructor.call(this, { renderTo: Ext.getBody(), width: 480, height: 300, frame: true, stripeRows: true, clicksToEdit: 2, //表示点击多少次数才可以编辑表格 collapsible: true, //是否在右上角显示收缩按钮 animCollapse: true, //表示收缩(闭合)面板时,显示动画效果 trackMouseOver: true, //鼠标在行上移动时显示高亮 enableColumnMove: false,//禁止用户拖动表头 autoExpandColumn: "name", //这里指定的name的id ,此属性可以根据当前列 填充空白区域 title: "用户数据编辑器", tbar: [ "查看方式:", this.sexCombo, "-", { text: "保存数据", handler: this.onCommitStore, scope: this } ], bbar: [{ text: "添加数据", handler: this.onAddClick, scope: this },"-",{ text: "删除数据", handler: this.onRemoveClick, scope: this }], columns:[{ id: "name", header: "姓名", align: "center", dataIndex: "name", editor: new Ext.form.TextField({ allowBlank: false, blankText: "姓名不能为空,必须输入" }) },{ header: "年龄", align: "center", dataIndex: "age", editor: new Ext.form.NumberField({ allowBlank: false, allowNegative: false, //只能为正数 //maxValue: 1000000000, grow: true, //前半部分显示正在改的数据,后半部分显示以前的老数据 selectOnFocus: true, //当获得焦点时,选中所有的文本内容 minValue: 1 }) },{ header: "性别", align: "center", dataIndex: "sex", editor: new Ext.form.ComboBox({ mode: "local", value: "男", readOnly: true, displayField: "sex", triggerAction: "all", store: new Ext.data.SimpleStore({ data: ["男", "女"], expandData: true, fields: ["sex"] }) }) },{ header: "生日", align: "center", sortable: true, dataIndex: "birthday", renderer: Ext.util.Format.dateRenderer('Y-m-d'), editor: new Ext.form.DateField({ format: "Y-m-d", minValue: "1950-01-01", disabledDays: [0, 7],//datefield的第0列:周日和第7列:周六不能编辑 disabledDaysText: "周末不能选择" }) }], sm: new Ext.grid.RowSelectionModel({ singleSelect: true }) }); /*Ext.Ajax.on("requestcomplete", function (conn, response, options) { alert(response.responseText); });*/ }, filterSex: function (cob) { if (cob.getValue() == "全部"){ this.store.clearFilter(); } else this.store.filter("sex", cob.value); }, onAddClick: function () { var rs = new Ext.data.Record({id: "",name: "", age: 1, sex: "", birthday: 0000-00-00}); this.getStore().add(rs); rs.set("name", "ext"); rs.set("age", 22); rs.set("sex", "男"); rs.set("birthday", new Date()); this.inserted.push(rs); this.startEditing(this.store.getCount() - 1, 0); }, saveInsertData: function (conn, response) { var xml = response.responseXML; var root = xml.documentElement; for (var i = 0; i < root.childNodes.length; i++) { this.inserted[i].set("id", root.childNodes[i].text); } this.getStore().commitChanges(); this.inserted = []; }, onCommitStore: function () { var mf = this.getStore().modified; var temp = []; for (var i = 0; i < mf.length; i ++) { if (mf[i].get("id") == ""){ continue; } var data = {}; for (var j in mf[i].modified) { data[j] = mf[i].get(j); } temp.push(Ext.apply(data, {id: mf[i].get("id")})); } for (var i = 0; i < this.inserted.length; i ++) { temp.push(this.inserted[i].data); } Ext.Ajax.on("requestcomplete", function (conn, response, options) { alert(response.responseText); }); //Ext.Ajax.on("requestcomplete", this.saveInsertData, this); //想服务器发送请求,传递修改的数据(只含修改的数据) Ext.Ajax.request({url: "../ServiceServlet?method=edit", params: {content:Ext.util.JSON.encode(temp)}}); this.store.commitChanges(); this.filterSex(this.sexCombo); }, onRemoveClick: function () { var rs = this.getSelectionModel(); try{ if (rs.getCount() == 0) { Ext.Msg.alert("系统提示", "没有选定数据,请选择数据行!"); }else { Ext.Msg.confirm("系统提示", "您确定要删除当前数据吗?", this.removeUserInfo, this); } }catch(er) { Ext.Msg.alert("系统提示", er.discription); } }, removeUserInfo: function (btnText) { if (btnText == "yes"){ var rs = this.getSelectionModel().getSelected(); this.getStore().remove(rs); if (rs.get("id") != "") { Ext.Ajax.on("requestcomplete", function (conn, response, options) { if (response.responseText == "success") { alert("success"); }else { alert("failure"); } }); Ext.Ajax.request({url: "../ServiceServlet?method=remove", params: {id:rs.get("id")}}); }else { this.inserted.remove(rs); //this.getStore().modified.romove(rs); } } } }); UserEditorGridPanel.USER_STORE_URL = "http://localhost:8080/Demo3/ServiceServlet?method=all"; ~~~ 后台增删改查方法Servlet 代码: ~~~ import java.io.PrintWriter; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.hoo.dao.IUser; import com.hoo.dao.impl.UserDao; import com.hoo.entity.UserInfo; @SuppressWarnings({"unchecked", "serial"}) public class ServiceServlet extends HttpServlet { private IUser dao = new UserDao(); public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setCharacterEncoding("GBK"); PrintWriter out = response.getWriter(); StringBuilder builder = new StringBuilder(); String method = request.getParameter("method"); if ("all".equals(method)) { List<UserInfo> list = dao.loadUserInfo(); builder.append("["); for (int i = 0; i < list.size(); i++) { UserInfo u = (UserInfo) list.get(i); builder.append("[/"").append(u.getId()) .append("/",/"").append(u.getName()) .append("/",").append(u.getAge()) .append(",/"").append(u.getSex()) .append("/",/"").append(u.getBirthday()) .append("/"]"); if (i < list.size()-1) { builder.append(","); } } builder.append("]"); out.write(builder.toString()); } if ("edit".equals(method)) { String content = request.getParameter("content"); out.print(content); } if ("remove".equals(method)) { Integer id = Integer.parseInt(request.getParameter("id")); if (dao.removeUserInfo(id)) { out.print("success"); }else { out.print("failure"); } } out.flush(); out.close(); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } } ~~~ ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe122772c70.gif) 插入图片以示真相,确实用默认的ArrayReader是不能分页。就算加上Ext.PagingToolbar分页工具条也是枉然,只是个摆设不能分页的,但可以结合静态数据分页的store还是可以的;见:[http://blog.csdn.net/IBM_hoojo/archive/2010/08/19/5823820.aspx](http://blog.csdn.net/IBM_hoojo/archive/2010/08/19/5823820.aspx)
';

ExtJS EditorGridPanel 示例之Array格式(自定义Array解析器)Store前后台增删改查

最后更新于:2022-04-01 12:01:08

本示例入口html页面: ~~~ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>用户数据编辑 用ArrayReader 实现分页</title> <meta http-equiv="Content-Type" content="text/html; charset=gbk" /> <meta http-equiv="author" content="hoojo"> <meta http-equiv="email" content="hoojo_@126.com"> <meta http-equiv="blog" content="http://blog.csdn.net/IBM_hoojo"> <link rel="stylesheet" type="text/css" href="../ext2/resources/css/ext-all.css"></link> <script type="text/javascript" src="../ext2/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="../ext2/ext-all.js"></script> <script type="text/javascript" src="../ext2/build/locale/ext-lang-zh_CN-min.js"></script> <!-- 用ArrayReader 实现分页,需要改进ArrayReader 的代码 --> <script type="text/javascript" src="ArrayReader.js"></script> <script type="text/javascript" src="ArrayPagingEditorGridPanel.js"></script> <script type="text/javascript"> Ext.onReady(function (){ Ext.QuickTips.init(); //Ext.form.Field.prototype.msgTarget = "side"; Ext.BLANK_IMAGE_URL = "../ext2/resources/images/default/s.gif"; new ArrayPagingEditorGridPanel(); }); </script> </head> <body> </body> </html> ~~~ ArrayPagingEditorGridPanel.js文件代码: ~~~ /** * 用Array格式的数据形式:Ext.data.Store,自定义Ext.data.ArrayReader数组解析器,实现editorGrid的增删改查 * author: hoojo * email: hoojo_@126.com * blog: http://blog.csdn.net/IBM_hoojo * ext-lib: v2.2 */ /****************************************************************/ /*******ArrayPagingEditorGridPanel*******/ /****************************************************************/ ArrayPagingEditorGridPanel = Ext.extend(Ext.grid.EditorGridPanel, { sexCombo: null, inserted: [], constructor: function () { this.sexCombo = new Ext.form.ComboBox({ mode: "local", value: "全部", readOnly: true, triggerAction: "all", displayField: "sex", //listAlign : "bl-tl", //下拉列表的显示方式 bl-tl是在上方显示,相反tl-bl是从下方显示 store: new Ext.data.SimpleStore({ data: ["男", "女", "全部"], expandData: true, fields: ["sex"] }), listeners: { "select": { fn: this.filterSex, scope: this } } }); this["store"] = new Ext.data.Store({ url: ArrayPagingEditorGridPanel.USER_STORE_URL, reader: new Ext.data.ArrayReader({ id: "id", //维护当前数据的唯一性(和数据库主键类似,避免数据重复--过滤重复数据) root: "users", totalProperty: "totals" }, Ext.data.Record.create(["id","name", {name: "age", type: "int"}, "sex", {name: "birthday", type: "date", dateFormat: "Y-m-d"} ]) ), sortInfo:{field:"name", direction:"ASC"} //排序 }); ArrayPagingEditorGridPanel.superclass.constructor.call(this, { renderTo: Ext.getBody(), width: 480, height: 300, frame: true, stripeRows: true, clicksToEdit: 2, //表示点击多少次数才可以编辑表格 collapsible: true, //是否在右上角显示收缩按钮 animCollapse: true, //表示收缩(闭合)面板时,显示动画效果 trackMouseOver: true, //鼠标在行上移动时显示高亮 enableColumnMove: false,//禁止用户拖动表头 autoExpandColumn: "name", //这里指定的name的id ,此属性可以根据当前列 填充空白区域 title: "用户数据编辑器", tbar: [ "查看方式:", this.sexCombo, "-", { text: "保存数据", handler: this.onCommitStore, scope: this } ], bbar: [{ text: "添加数据", handler: this.onAddClick, scope: this },"-",{ text: "删除数据", handler: this.onRemoveClick, scope: this },new Ext.PagingToolbar({ pageSize: 5, store: this.store })], columns:[{ id: "name", header: "姓名", align: "center", dataIndex: "name", editor: new Ext.form.TextField({ allowBlank: false, blankText: "姓名不能为空,必须输入" }) },{ header: "年龄", align: "center", dataIndex: "age", editor: new Ext.form.NumberField({ allowBlank: false, allowNegative: false, //只能为正数 //maxValue: 1000000000, grow: true, //前半部分显示正在改的数据,后半部分显示以前的老数据 selectOnFocus: true, //当获得焦点时,选中所有的文本内容 minValue: 1 }) },{ header: "性别", align: "center", dataIndex: "sex", editor: new Ext.form.ComboBox({ mode: "local", value: "男", readOnly: true, displayField: "sex", triggerAction: "all", store: new Ext.data.SimpleStore({ data: ["男", "女"], expandData: true, fields: ["sex"] }) }) },{ header: "生日", align: "center", sortable: true, dataIndex: "birthday", renderer: Ext.util.Format.dateRenderer('Y-m-d'), editor: new Ext.form.DateField({ format: "Y-m-d", minValue: "1950-01-01", disabledDays: [0, 7],//datefield的第0列:周日和第7列:周六不能编辑 disabledDaysText: "周末不能选择" }) }], sm: new Ext.grid.RowSelectionModel({ singleSelect: true }) }); this.store.load({params: {start: 0, limit: 5}}); /*Ext.Ajax.on("requestcomplete", function (conn, response, options) { alert(response.responseText); });*/ }, filterSex: function (cob) { if (cob.getValue() == "全部"){ this.store.clearFilter(); } else this.store.filter("sex", cob.value); }, onAddClick: function () { var rs = new Ext.data.Record({id: "",name: "", age: 1, sex: "", birthday: 0000-00-00}); this.getStore().add(rs); rs.set("name", "ext"); rs.set("age", 22); rs.set("sex", "男"); rs.set("birthday", new Date()); this.inserted.push(rs); this.startEditing(this.store.getCount() - 1, 0); }, saveInsertData: function (conn, response) { var xml = response.responseXML; var root = xml.documentElement; for (var i = 0; i < root.childNodes.length; i++) { this.inserted[i].set("id", root.childNodes[i].text); } this.getStore().commitChanges(); this.inserted = []; }, onCommitStore: function () { var mf = this.getStore().modified; var temp = []; for (var i = 0; i < mf.length; i ++) { if (mf[i].get("id") == ""){ continue; } var data = {}; for (var j in mf[i].modified) { data[j] = mf[i].get(j); } temp.push(Ext.apply(data, {id: mf[i].get("id")})); } for (var i = 0; i < this.inserted.length; i ++) { temp.push(this.inserted[i].data); } Ext.Ajax.on("requestcomplete", function (conn, response, options) { alert(response.responseText); }); //Ext.Ajax.on("requestcomplete", this.saveInsertData, this); //想服务器发送请求,传递修改的数据(只含修改的数据) Ext.Ajax.request({url: "../ServiceServlet?method=edit", params: {content:Ext.util.JSON.encode(temp)}}); this.store.commitChanges(); this.filterSex(this.sexCombo); }, onRemoveClick: function () { var rs = this.getSelectionModel(); try{ if (rs.getCount() == 0) { Ext.Msg.alert("系统提示", "没有选定数据,请选择数据行!"); }else { Ext.Msg.confirm("系统提示", "您确定要删除当前数据吗?", this.removeUserInfo, this); } }catch(er) { Ext.Msg.alert("系统提示", er.discription); } }, removeUserInfo: function (btnText) { if (btnText == "yes"){ var rs = this.getSelectionModel().getSelected(); this.getStore().remove(rs); if (rs.get("id") != "") { Ext.Ajax.on("requestcomplete", function (conn, response, options) { if (response.responseText == "success") { alert("success"); }else { alert("failure"); } }); Ext.Ajax.request({url: "../ServiceServlet?method=remove", params: {id:rs.get("id")}}); }else { this.inserted.remove(rs); //this.getStore().modified.romove(rs); } } } }); ArrayPagingEditorGridPanel.USER_STORE_URL = "http://localhost:8080/Demo3/ServiceServlet?method=arry"; ~~~ 后台java servlet交互代码: ~~~ import java.io.PrintWriter; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.hoo.dao.IUser; import com.hoo.dao.impl.UserDao; import com.hoo.entity.UserInfo; @SuppressWarnings({"unchecked", "serial"}) public class ServiceServlet extends HttpServlet { private IUser dao = new UserDao(); public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setCharacterEncoding("GBK"); PrintWriter out = response.getWriter(); StringBuilder builder = new StringBuilder(); String method = request.getParameter("method"); if ("arry".equals(method)) { int start = Integer.parseInt(request.getParameter("start")); int limit = Integer.parseInt(request.getParameter("limit")); Object[] obj = dao.loadUserInfo(start, limit); List<UserInfo> list = (List<UserInfo>) obj[1]; builder.append("{totals:").append(obj[0]).append(",users:["); for (int i = 0; i < list.size(); i++) { UserInfo u = (UserInfo) list.get(i); builder.append("[/"").append(u.getId()) .append("/",/"").append(u.getName()) .append("/",").append(u.getAge()) .append(",/"").append(u.getSex()) .append("/",/"").append(u.getBirthday()) .append("/"]"); if (i < list.size()-1) { builder.append(","); } } builder.append("]}"); out.write(builder.toString()); } if ("edit".equals(method)) { String content = request.getParameter("content"); out.print(content); } if ("remove".equals(method)) { Integer id = Integer.parseInt(request.getParameter("id")); if (dao.removeUserInfo(id)) { out.print("success"); }else { out.print("failure"); } } out.flush(); out.close(); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } } ~~~ 本示例用ArrayReader解析数组数据分页,需要扩展原有的ArrayReader.js文件,下面是扩展文件: ~~~ Ext.data.ArrayReader = Ext.extend(Ext.data.JsonReader, { readRecords : function(o){ var sid = this.meta ? this.meta.id : null; var recordType = this.recordType, fields = recordType.prototype.fields; var records = []; var root = this.meta ? this.meta["root"] ? o[this.meta["root"]] : o : o; for(var i = 0; i < root.length; i++){ var n = root[i]; var values = {}; var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null); for(var j = 0, jlen = fields.length; j < jlen; j++){ var f = fields.items[j]; var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j; var v = n[k] !== undefined ? n[k] : f.defaultValue; v = f.convert(v, n); values[f.name] = v; } var record = new recordType(values, id); record.json = n; records[records.length] = record; } return { records : records, totalRecords : this.meta ? this.meta["totalProperty"] ? o[this.meta["totalProperty"]] : records.length : records.length }; } }); ~~~ ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe122719c44.gif) 本示例最大的不同就是采用数据传递形式不同,用到的是Array格式。但ext v2.2版本中的ArrayReader结合Ext.data.Store 不支持分页,所以本示例进行扩展ArrayReader文件。
';

ExtJS EditorGridPanel 示例之xml格式Store前后台增删改查

最后更新于:2022-04-01 12:01:06

程序入口html页面: ~~~ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>用户数据编辑 XmlReader 实现分页</title> <meta http-equiv="Content-Type" content="text/html; charset=gbk" /> <meta http-equiv="author" content="hoojo"> <meta http-equiv="email" content="hoojo_@126.com"> <meta http-equiv="blog" content="http://blog.csdn.net/IBM_hoojo"> <link rel="stylesheet" type="text/css" href="../ext2/resources/css/ext-all.css"></link> <script type="text/javascript" src="../ext2/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="../ext2/ext-all.js"></script> <script type="text/javascript" src="../ext2/build/locale/ext-lang-zh_CN-min.js"></script> <script type="text/javascript" src="UserPagingEditorGridPanel.js"></script> <script type="text/javascript"> Ext.onReady(function (){ Ext.QuickTips.init(); //Ext.form.Field.prototype.msgTarget = "side"; Ext.BLANK_IMAGE_URL = "../ext2/resources/images/default/s.gif"; new UserPagingEditorGridPanel(); }); </script> </head> <body> </body> </html> ~~~ UserPagingEditorGridPanel.js: ~~~ /** * 用xml格式的数据形式:Ext.data.Store、Ext.data.XmlReader解析器,实现editorGrid的增删改查 * author: hoojo * email: hoojo_@126.com * blog: http://blog.csdn.net/IBM_hoojo * ext-lib: v2.2 */ /****************************************************************/ /*******UserEditorGridPanel*******/ /****************************************************************/ UserPagingEditorGridPanel = Ext.extend(Ext.grid.EditorGridPanel, { sexCombo: null, inserted: [], constructor: function () { this.sexCombo = new Ext.form.ComboBox({ mode: "local", value: "全部", readOnly: true, triggerAction: "all", displayField: "sex", //listAlign : "bl-tl", //下拉列表的显示方式 bl-tl是在上方显示,相反tl-bl是从下方显示 store: new Ext.data.SimpleStore({ data: ["男", "女", "全部"], expandData: true, fields: ["sex"] }), listeners: { "select": { fn: this.filterSex, scope: this } } }); this["store"] = new Ext.data.Store({ url: UserPagingEditorGridPanel.USER_STORE_URL, reader: new Ext.data.XmlReader({ id: "id", record: "user", totalRecords: "totals" //如果是属性(非节点)要用@totals——<users totals="2"> }, Ext.data.Record.create(["id","name", {name: "age", type: "int"}, "sex", {name: "birthday", type: "date", dateFormat: "Y-m-d"} ]) ), sortInfo:{field:'name', direction:'ASC'} //排序 }); UserPagingEditorGridPanel.superclass.constructor.call(this, { renderTo: Ext.getBody(), width: 480, height: 300, frame: true, stripeRows: true, clicksToEdit: 1, //表示点击多少次数才可以编辑表格 collapsible: true, //是否在右上角显示收缩按钮 animCollapse: true, //表示收缩(闭合)面板时,显示动画效果 trackMouseOver: true, //鼠标在行上移动时显示高亮 enableColumnMove: false,//禁止用户拖动表头 autoExpandColumn: "name", //这里指定的name的id ,此属性可以根据当前列 填充空白区域 title: "用户数据编辑器", tbar: [ "查看方式:", this.sexCombo, "-", { text: "保存数据", handler: this.onCommitStore, scope: this },"-",{ text: "添加数据", handler: this.onAddClick, scope: this },"-",{ text: "删除数据", handler: this.onRemoveClick, scope: this } ], bbar: new Ext.PagingToolbar({ pageSize: 5, store: this.store }), columns:[{ id: "name", header: "姓名", align: "center", dataIndex: "name", editor: new Ext.form.TextField({ allowBlank: false, blankText: "姓名不能为空,必须输入" }) },{ header: "年龄", align: "center", dataIndex: "age", editor: new Ext.form.NumberField({ allowBlank: false, allowNegative: false, //只能为正数 maxValue: 200, grow: true, //前半部分显示正在改的数据,后半部分显示以前的老数据 selectOnFocus: true, //当获得焦点时,选中所有的文本内容 minValue: 1 }) },{ header: "性别", align: "center", dataIndex: "sex", editor: new Ext.form.ComboBox({ mode: "local", value: "男", readOnly: true, displayField: "sex", triggerAction: "all", store: new Ext.data.SimpleStore({ data: ["男", "女"], expandData: true, fields: ["sex"] }) }) },{ header: "生日", align: "center", sortable: true, dataIndex: "birthday", renderer: Ext.util.Format.dateRenderer('Y-m-d'), editor: new Ext.form.DateField({ format: "Y-m-d", minValue: "1950-01-01", disabledDays: [0, 7],//datefield的第0列:周日和第7列:周六不能编辑 disabledDaysText: "周末不能选择" }) }], sm: new Ext.grid.RowSelectionModel({ singleSelect: true }) }); this.store.load({params: {start: 0, limit: 5}}); //Ext.Ajax.on("requestcomplete", this.savaInsertData, this); }, filterSex: function (cob) { if (cob.getValue() == "全部"){ this.store.clearFilter(); } else this.store.filter("sex", cob.value); }, onAddClick: function () { var rs = new Ext.data.Record({id: "",name: "", age: 1, sex: "", birthday: 0000-00-00}); this.getStore().add(rs); rs.set("name", "ext"); rs.set("age", 22); rs.set("sex", "男"); rs.set("birthday", new Date()); this.inserted.push(rs); this.startEditing(this.store.getCount() - 1, 0); }, saveInsertData: function (conn, response) { var xml = response.responseXML; var root = xml.documentElement; for (var i = 0; i < root.childNodes.length; i++) { this.inserted[i].set("id", root.childNodes[i].text); } this.getStore().commitChanges(); this.inserted = []; }, onCommitStore: function () { var mf = this.getStore().modified; var temp = []; for (var i = 0; i < mf.length; i ++) { if (mf[i].get("id") == ""){ continue; } var data = {}; for (var j in mf[i].modified) { data[j] = mf[i].get(j); } temp.push(Ext.apply(data, {id: mf[i].get("id")})); } for (var i = 0; i < this.inserted.length; i ++) { temp.push(this.inserted[i].data); } /*Ext.Ajax.on("requestcomplete", function (conn, response, options) { alert(response.responseText); });*/ //Ext.Ajax.on("requestcomplete", this.saveInsertData, this); //想服务器发送请求,传递修改的数据(只含修改的数据) Ext.Ajax.request({url: "../ServiceServlet?method=edit", params: {content:Ext.util.JSON.encode(temp)}}); this.store.commitChanges(); this.filterSex(this.sexCombo); }, onRemoveClick: function () { var rs = this.getSelectionModel(); try{ if (rs.getCount() == 0) { Ext.Msg.alert("系统提示", "没有选定数据,请选择数据行!"); }else { Ext.Msg.confirm("系统提示", "您确定要删除当前数据吗?", this.removeUserInfo, this); } }catch(er) { Ext.Msg.alert("系统提示", er.discription); } }, removeUserInfo: function (btnText) { if (btnText == "yes"){ var rs = this.getSelectionModel().getSelected(); this.getStore().remove(rs); if (rs.get("id") != "") { Ext.Ajax.on("requestcomplete", function (conn, response, options) { if (response.responseText == "success") { alert("success"); }else { alert("failure"); } }); Ext.Ajax.request({url: "../ServiceServlet?method=remove", params: {id:rs.get("id")}}); }else { this.inserted.remove(rs); this.getStore().modified.romove(rs); } } } }); UserPagingEditorGridPanel.USER_STORE_URL = "http://localhost:8080/Demo3/ServiceServlet?method=xml"; ~~~ 后台Servlet代码,ServiceServlet?method=xml方法代码 ~~~ import java.io.PrintWriter; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.hoo.dao.IUser; import com.hoo.dao.impl.UserDao; import com.hoo.entity.UserInfo; @SuppressWarnings({"unchecked", "serial"}) public class ServiceServlet extends HttpServlet { private IUser dao = new UserDao(); public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setCharacterEncoding("GBK"); response.setContentType("text/xml");//#text/xml# 一定要这样才能正常解析 PrintWriter out = response.getWriter(); StringBuilder builder = new StringBuilder(); String method = request.getParameter("method"); if ("xml".equals(method)) { int start = Integer.parseInt(request.getParameter("start")); int limit = Integer.parseInt(request.getParameter("limit")); Object[] obj = dao.loadUserInfo(start, limit); List<UserInfo> list = (List<UserInfo>) obj[1]; builder.append("<?xml version=/"1.0/" encoding=/"gbk/"?>") //.append("<users totals=/"").append(obj[0]).append("/">"); .append("<users><totals>").append(obj[0]).append("</totals>"); for (int i = 0; i < list.size(); i++) { UserInfo u = (UserInfo) list.get(i); builder.append("<user><id>").append(u.getId()) .append("</id><name>").append(u.getName()) .append("</name><age>").append(u.getAge()) .append("</age><sex>").append(u.getSex()) .append("</sex><birthday>").append(u.getBirthday()) .append("</birthday></user>"); } builder.append("</users>"); out.write(builder.toString()); } if ("edit".equals(method)) { String content = request.getParameter("content"); out.print(content); } if ("remove".equals(method)) { Integer id = Integer.parseInt(request.getParameter("id")); if (dao.removeUserInfo(id)) { out.print("success"); }else { out.print("failure"); } } out.flush(); out.close(); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } } ~~~ ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe122719c44.gif) 本示例用到的是Ext.data.Store/Ext.data.XmlReader解析器,解析后台传递的xml格式数据,当然也可以将UserPagingEditorGridPanel.USER_STORE_URL = "your.xml";这样也是可以的,但注意的是文件中定义的xml文档的格式要和store中的Ext.data.Record.create解析数据标签名称对应,本示例还用到了数据的简单过滤“性别”以及对脏数据的显示及其提交、修改等,下次将会提供更复杂的过滤查询。
';

ExtJS中grid按照使用Expand插件、分组显示、中文拼音首字母排序、改变行背景、列背景、静态数据分页综合案例

最后更新于:2022-04-01 12:01:03

~~~ /*** * grid基础综合案例 * 添加分页Ext.data.PagingMemoryProxy、重写Ext.data.Store支持中文汉字拼音字母首字母排序、 * Ext.data.GroupingStore分组(GroupingStore继承Store的)、 * 改变列背景色、行背景色、字体颜色getRowClass方法和renderer函数onRenderAgeCol的使用技巧 * 添加分组、行收缩展开插件Ext.grid.RowExpander使用及重写Ext.grid.RowExpander组件的 * 添加Ext.grid.GroupingView的groupTextTpl方法的使用 * author: hoojo * create by: 2010-8-14 */ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>可分组显示、expand插件、中文排序的Grid/改变行背景/单元格背景/字体颜色/分页</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="content-Type" content="text/html; charset=utf-8"> <meta http-equiv="author" content="hoojo"> <meta http-equiv="blog" content="http://blog.csdn.net/IBM_hoojo"> <link rel="stylesheet" type="text/css" href="ext2/resources/css/ext-all.css"> <style type="text/css"> .rowOdd { background-color: #EFF7FF; color: white; } .rowEven { background-color: #CAE3FF; color: white; } .cellBG { background-color: #FFDD99; font-weight: bold; } </style> <script type="text/javascript" src="ext2/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="ext2/ext-all.js"></script> <script type="text/javascript" src="ext2/build/locale/ext-lang-zh_CN-min.js"></script> <script type="text/javascript" src="jslib/PagingMemoryProxy.js"></script> <!-- PagingMemoryProxy.js在ext-2.2/examples/locale可以找到 --> <script type="text/javascript" src="jslib/RowExpander.js"></script> <!-- RowExpander.jsext-2.2/examples/grid下可以找到--> <script type="text/javascript" src="jslib/Ext.hoo.grid.ConformityGrid.js"></script> </head> <body> </body> </html> ~~~ ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe1226aa801.gif) look:按照国家分组的,其中还按照中文排序及Expand插件、分组修改单元格、行背景色、字体颜色 Ext.hoo.grid.ConformityGrid.js ~~~ /*** * grid基础综合案例 * 添加分页Ext.data.PagingMemoryProxy、重写Ext.data.Store支持中文汉字拼音字母首字母排序、 * Ext.data.GroupingStore分组(GroupingStore继承Store的)、 * 改变列背景色、行背景色、字体颜色getRowClass方法和renderer函数onRenderAgeCol的使用技巧 * 添加分组、行收缩展开插件Ext.grid.RowExpander使用及重写Ext.grid.RowExpander组件的 * 添加Ext.grid.GroupingView的groupTextTpl方法的使用 * author: hoojo * create by: 2010-8-14 */ Ext.ns("Ext.hoo.grid"); Ext.hoo.grid.ConformityGrid = Ext.extend(Ext.grid.GridPanel, { //expander: null, constructor: function () { this.data = [ [1, "奥巴马", 48, "美国", "好像是美国总统"], [3, "布朗", 20, "美国", "很常见的名字"], [5, "次郎", 22, "日本", "据说是武大郎赐给的名称"], [2, "多尔衮", 159, "中国", "差点历史就被改写"], [4, "厄洛斯", 34, "英国", "不知哪里来的"], [6, "弗莱德", 25, "美国", "和弗兰德很像"], [8, "哥萨克", 24, "英国", "是萨克斯的弟弟么"], [23, "汉德森", 48, "美国", "好像是部电影的主角"], [13, "杰克", 20, "美国", "电影中常出现的配角名称"], [15, "卡尔", 22, "美国", "是小时候看动画片的那个瘦猫,胖子是BC"], [12, "露丝", 159, "英国", "很常见的名称"], [14, "玛丽卡", 34, "美国", "这个名称好像不错哦"], [16, "妮娜", 25, "美国", "在中国有叫‘妮’、‘娜’的,‘妮娜’不多见"], [18, "欧德桑", 24, "英国", "Good Success!"] ], this.store = new Ext.data.GroupingStore({ proxy: new Ext.data.PagingMemoryProxy(this.data),//后台分页只须将proxy改为url: "your url", reader: new Ext.data.ArrayReader({}, [ {name: "id", type: "int", mapping: 0}, "name", "age", "country", "origin" ]), sortInfo: {field: "name", direction: "asc"}, groupField: "country" }), this.expander = new Ext.grid.RowExpander({ tpl : new Ext.Template( '<p><b>你是:</b> {name}</p><br>', '<p><b>评价:</b> {origin}</p>' ) }), Ext.hoo.grid.ConformityGrid.superclass.constructor.call(this, { renderTo: Ext.getBody(), title: "可按中文排序的Grid/改变行背景/单元格背景/字体颜色", height: 450, width: 600, frame: true, autoScroll: true, plugins: this.expander, autoExpandColumn: "age", columns: [this.expander, { header: "编号", dataIndex: "id", sortable: true }, { header: "名称", dataIndex: "name", sortable: true }, { header: "年龄", dataIndex: "age", sortable: true, renderer: this.onRenderAgeCol }, { header: "国家", dataIndex: "country", sortable: true }], sm: new Ext.grid.RowSelectionModel({singleSelect: true}), bbar: new Ext.PagingToolbar({ store: this.store, pageSize: 8, displayInfo: true, displayMsg: "显示{0}条到{1}条记录,总共{2}条记录", emptyMsg: "没有数据记录" }), view: new Ext.grid.GroupingView({ forceFit:true, autoFill: true, groupTextTpl: '{text}({[values.rs.length]} {[values.rs.length > 1 ? "人" : "人"]})-记录[{startRow}]'/*,startRow是行索引 //如果不用Ext.grid.RowExpande插件,这里才有效果,在用插件的情况下改变行背景颜色需要覆盖插件中的getRowClass方法 getRowClass: function(record, index, rowParams, store) { if (index % 2 == 0) { return 'rowEven'; } else { return 'rowOdd'; } }*/ }) }); this.store.load({params: {start: 0, limit: 8}}); }, /** * 列渲染器方法,在grid渲染的时候执行 * @param value 当前列的值 * @param metaData 当前列的css样式 * @param record 当前列的record记录 * @param rowIndex 行索引 * @param colIndex 当前列索引 * @param store 当前grid的store */ onRenderAgeCol: function (value, metaData, record, rowIndex, colIndex, store) { if (rowIndex == 2) {//改变第四行的当前单元格背景颜色 metaData.attr = 'style="color: white; background-color: #A9C9E2;"';//添加style样式 } else if (value > 40) { metaData.attr = 'style="color: red;"'; metaData.css = "cellBG";//添加class样式 } if (rowIndex > 2) { return "<a href='http://blog.csdn.net/IMB_hoojo' target='_blank'>【" + value + "】-【" + record.get("name") + "】-【" + store.getAt(0).data.name + "】</a>"; } else if (rowIndex == 1) { metaData.cellAttr = "style='background-color: white; color: green;'";//不兼容firefox return value; } else { return value; } } }); /** * 重写(覆盖)applySort方法 * 按照拼音字母进行排序 */ Ext.override(Ext.data.Store, { applySort: function () { if (this.sortInfo && !this.remoteSort) { var s = this.sortInfo; var f = s.field; var st = this.fields.get(f).sortType; var fn = function (r1, r2) { var v1 = st(r1.data[f]); var v2 = st(r2.data[f]); if (typeof(v1) == "string") { return v1.localeCompare(v2); } return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0); }; this.data.sort(s.direction, fn); if (this.snapshot && this.snapshot != this.data) { this.snapshot.sort(s.direction, fn); } } } }); /* 也可以下面这样写; Ext.data.Store.prototype.applySort = function () { };*/ Ext.override(Ext.grid.RowExpander, { getRowClass: function(record, rowIndex, rowParams, store) { rowParams.cols = rowParams.cols-1; var content = this.bodyContent[record.id]; if(!content && !this.lazyRender){ content = this.getBodyContent(record, rowIndex); } if(content){ rowParams.body = content; } if (rowIndex % 2 == 0) { return this.state[record.id] ? 'x-grid3-row-expanded rowEven' : 'x-grid3-row-collapsed rowEven';//添加样式rowEven } else { return this.state[record.id] ? 'x-grid3-row-expanded rowOdd' : 'x-grid3-row-collapsed rowOdd';//添加样式rowOdd } } }); Ext.onReady(function () { Ext.BLANK_IMAGE_URL = "ext2/resources/images/default/s.gif"; var grid = new Ext.hoo.grid.ConformityGrid(); }); ~~~
';

ExtJS中grid按照中文拼音首字母排序、改变行背景、列背景、静态数据分页不再困难

最后更新于:2022-04-01 12:01:01

本示例主要使用到了静态数据分页Ext.data.PagingMemoryProxy组件、Ext.PagingToolbar分页条、viewConfig的getRowClass方法、列column的renderer的方法使用、重写Ext.data.Store对中文排序的支持、以及Ext.Template结合grid的使用方法。 ~~~ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>可按中文排序的Grid/改变行背景/单元格背景/字体颜色/静态数据分页</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="content-Type" content="text/html; charset=utf-8"> <meta http-equiv="author" content="hoojo"> <meta http-equiv="blog" content="http://blog.csdn.net/IBM_hoojo"> <link rel="stylesheet" type="text/css" href="ext2/resources/css/ext-all.css"> <style type="text/css"> .rowOdd { background-color: #EFF7FF; color: white; } .rowEven { background-color: #CAE3FF; color: white; } .cellBG { background-color: #FFDD99; font-weight: bold; } </style> <script type="text/javascript" src="ext2/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="ext2/ext-all.js"></script> <script type="text/javascript" src="ext2/build/locale/ext-lang-zh_CN-min.js"></script> <script type="text/javascript" src="jslib/PagingMemoryProxy.js"></script> <!-- PagingMemoryProxy.js在ext-2.2/examples/locale可以找到 --> <script type="text/javascript" src="jslib/Ext.hoo.grid.SortChineseGridPanel.js"></script> </head> <body> <div id="showGrid"></div> <div id="showPanel"></div> </body> </html> ~~~ ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-01_56fe121baec56.gif) 看图:改变行列背景、字体颜色、name按照中文拼音首字母排序:依次是a、b、c、c、e;及数据分页 Ext.hoo.grid.SortChineseGridPanel.js ~~~ /** * 本示例主要使用到了静态数据分页Ext.data.PagingMemoryProxy组件、Ext.PagingToolbar分页条、 * viewConfig的getRowClass方法、列column的renderer的方法使用、重写Ext.data.Store对中文排序的支持、 * 以及Ext.Template结合grid的使用方法。 * author: hoojo * createDate: 2010-8-14 **/ Ext.ns("Ext.hoo.grid"); Ext.hoo.grid.SortChineseGridPanel = Ext.extend(Ext.grid.GridPanel, { constructor: function () { this.data = [ [1, "奥巴马", 48], [3, "布朗", 20], [5, "次郎", 22], [2, "多尔衮", 159], [4, "厄洛斯", 34], [6, "弗莱德", 25], [8, "哥萨克", 24], [23, "汉德森", 48], [13, "杰克", 20], [15, "卡尔", 22], [12, "露丝", 159], [14, "玛丽卡", 34], [16, "妮娜", 25], [18, "欧德桑", 24] ]; this.store = new Ext.data.Store({ proxy: new Ext.data.PagingMemoryProxy(this.data) , reader: new Ext.data.ArrayReader({}, [ {name: "id", type: "int", mapping: 0}, "name", "age" ]), sortInfo: {field: "name", direction: "asc"} }); Ext.hoo.grid.SortChineseGridPanel.superclass.constructor.call(this, { renderTo: "showGrid", title: "可按中文排序的Grid/改变行背景/单元格背景/字体颜色", height: 300, width: 600, frame: true, autoScroll: true, autoExpandColumn: "age", columns: [{ header: "编号", dataIndex: "id", sortable: true }, { header: "名称", dataIndex: "name", sortable: true }, { header: "年龄", dataIndex: "age", sortable: true, renderer: this.onRenderAgeCol }], sm: new Ext.grid.RowSelectionModel({ singleSelect: true, listeners: { rowselect: { fn: this.onRowSelected, scope: this } } }), bbar: new Ext.PagingToolbar({ store: this.store, pageSize: 5, displayInfo: true, displayMsg: "显示{0}条到{1}条记录,总共{2}条记录", emptyMsg: "没有数据记录" }), viewConfig: { forceFit: true, autoFill: true, getRowClass: function(record, index, rowParams, store) { if (index % 2 == 0) { return 'rowEven'; } else { return 'rowOdd'; } } } }); this.store.load({params: {start: 0, limit: 5}}); }, /** * 列渲染器方法,在grid渲染的时候执行 * @param value 当前列的值 * @param metaData 当前列的css样式 * @param record 当前列的record记录 * @param rowIndex 行索引 * @param colIndex 当前列索引 * @param store 当前grid的store */ onRenderAgeCol: function (value, metaData, record, rowIndex, colIndex, store) { if (rowIndex == 2) {//改变第四行的当前单元格背景颜色 metaData.attr = 'style="color: white; background-color: #A9C9E2;"';//添加style样式 } else if (value > 40) { metaData.attr = 'style="color: red;"'; metaData.css = "cellBG";//添加class样式 } if (rowIndex > 2) { return "<a href='http://blog.csdn.net/IMB_hoojo' target='_blank'>【" + value + "】-【" + record.get("name") + "】-【" + store.getAt(0).data.name + "】</a>"; } else if (rowIndex == 1) { metaData.cellAttr = "style='background-color: white; color: green;'";//不兼容firefox return value; } else { return value; } }, onRowSelected: function (sm, rowIndex, record) { var data = Ext.applyIf(record.data, {cls: this.getStyle()}) this.getViewTpl().overwrite(this.getViewPanel().body, data); }, getStyle: function () { return this.getViewPanel().getStyle(); }, setViewPanel: function (p) { this.viewPanel = p || {}; }, getViewPanel: function () { return this.viewPanel || {}; }, getViewTpl: function () { return this.getViewPanel().getViewTpl(); } }); /** * 重写(覆盖)applySort方法 * 按照拼音字母进行排序 */ Ext.override(Ext.data.Store, { applySort: function () { if (this.sortInfo && !this.remoteSort) { var s = this.sortInfo; var f = s.field; var st = this.fields.get(f).sortType; var fn = function (r1, r2) { var v1 = st(r1.data[f]); var v2 = st(r2.data[f]); if (typeof(v1) == "string") { return v1.localeCompare(v2); } return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0); }; this.data.sort(s.direction, fn); if (this.snapshot && this.snapshot != this.data) { this.snapshot.sort(s.direction, fn); } } } }); /* 也可以下面这样写; Ext.data.Store.prototype.applySort = function () { };*/ Ext.ns("Ext.hoo.panel"); Ext.hoo.panel.ViewPanel = Ext.extend(Ext.Panel, { constructor: function () { this.viewTplMarkup = [ "编号:<span style='{cls}'>{id}</span><br/>", "名称:<span style='{cls}'>{name}</span><br/>", "年龄:<span style='{cls}'>{age}</span><br/>" ]; this.viewTpl = new Ext.Template(this.viewTplMarkup); Ext.hoo.panel.ViewPanel.superclass.constructor.call(this, { //title: "详细信息", renderTo: "showPanel", height: 100, width: 600 }); }, getViewTpl: function () { return this.viewTpl; }, setStyle: function (cls) { this.sty = cls || "color: red;"; }, getStyle: function () { return this.sty || {}; } }); Ext.onReady(function () { Ext.BLANK_IMAGE_URL = "ext2/resources/images/default/s.gif"; var grid = new Ext.hoo.grid.SortChineseGridPanel(); var vp = new Ext.hoo.panel.ViewPanel(); vp.setStyle("color: red;") grid.setViewPanel(vp); }); ~~~
';

ExtJS中,在FireFox浏览器中字体很小,解决方法

最后更新于:2022-04-01 12:00:59

ff-firefox中ext的中文文字会小一些,看上去很模糊。这个是由字体大小决定的。font-size: 11px;英文显示没有问题,在IE中也是可以的。但是中文就不能正常显示。所以将font-size: 12px;即可,但是要针对样式;下面是修改后的字体样式。导入即可! ~~~ @CHARSET "UTF-8"; .x-window-footer { position: relative; top: 0; right: 0; } .x-tab-strip span.x-tab-strip-text { font-size: 12px; } .x-panel-header { font-size: 12px; } .x-tree-node { font-size: 12px; } .x-grid3-hd-row td { font-size: 12px; } .x-grid3-row td { font-size: 12px; line-height: 16px; } .x-tip .x-tip-bd { font-size: 12px; } .x-tip h3 { font-size: 12px; } .x-tip .x-tip-bd-inner { font-size: 12px; } .x-panel-tl .x-panel-header { font: normal 12px tahoma, arial, verdana, sans-serif; } .x-form-field { font: 12px tahoma, arial, helvetica, sans-serif; } .x-small-editor .x-form-field { font: 12px tahoma, arial, helvetica, sans-serif; } .x-combo-list-item { font: 12px tahoma, arial, helvetica, sans-serif; } .x-menu-list-item { font: 12px tahoma, arial, sans-serif; } .x-window-tl .x-window-header { font: bold 12px tahoma, arial, verdana, sans-serif; } .ext-ie .x-form-text { margin-top: 1px; } .x-form-item { font: 12px tahoma, arial, helvetica, sans-serif; } .x-grid-group-hd div { font: bold 12px tahoma, arial, helvetica, sans-serif; } .x-btn-text-icon .x-btn-center .x-btn-text { background-position: 0pt 2px; background-repeat: no-repeat; padding: 3px 0pt 2px 18px; } .ext-gecko .x-btn button { padding-left:0pt; padding-right: 0pt; } .x-btn button { font-size: 12px; } .x-toolbar td,.x-toolbar span,.x-toolbar input,.x-toolbar div,.x-toolbar select,.x-toolbar label { font-size: 12px; } .x-tip .x-tip-body { font-size: 12px; } ~~~
';

eclipse MyEclipse中安装 spket插件 js文件内容字体变小解决方案

最后更新于:2022-04-01 12:00:57

在eclipse、MyEclipse中用Spket插件打开js文件后,发现字体非常小。(在每次重启eclipse时会出现)这个时候你只需要找到Window->General->Appearance->Colors and Fonts->Basic-Text Font,点击右边的change,随便修改字体大小调节下,然后再调回原来的大小,点击apply就可以了对应properties文件插件修改也是同样的方法
';

eclipse MyEclipse中安装 ext插件 spket提示

最后更新于:2022-04-01 12:00:54

### 安装插件 首先在网上找到spket的插件包,下载后解压可以看到有这样2个文件夹。分别是features、plugins,然后找到eclipse、MyEclipse的安装目录(C:/Program Files/MyEclipse),在这个文件夹中找到eclipse或是myeclipse文件夹,将features、plugins直接放入文件夹中即可。提示否覆盖选择是; ### 配置插件 首先找到window->Spket->JavaScript Profiles; 在右边的JavaScript Profiles中点击New,随便写个名称ext; 然后选择你刚才创建的ext,点击Add Library选择ExtJS即可; 展开菜单可以看到你选择的ExtJS,选中这项。 点击Add File,找到你的ExtJS库文件中的source目录(如果是v3的就是src)下面的ext.jsb文件。 注意:这里的src、source目录中一定要有extJS库的源码文件,不能删除。而且ext.jsb要在这个文件夹下才可以。如果src、source没有源码文件或ext.jsb,而是让ext.jsb单独放在其他文件夹下是不能提示的。 最后展开可以看到你选择的jsb文件下有好多组件,选择你要使用的组件。只有选择的组件才有提示,不选择的就没有提示了。 完后还需要选择你新建的ext,点击右边的Default,设置为默认; 最后重启你的eclipse或MyEclipse即可; 插件虽好,不要过于依赖。在开发中可以使用提高开发效率,平时学习建议用NotePad、EditPlus等无提示软件、IDE即可。当然还有Spket的可视化拖拉的IDE,感觉不怎么成熟,不建议使用;  
';

ExtJS 中用js 操作cookie的方法

最后更新于:2022-04-01 12:00:52

cookie.js文件 ~~~ 偶尔发现ExtJS中有操作cookie的js方法,拿来给大家分享下。 var Cookies = {}; Cookies.set = function(name, value){ var argv = arguments; var argc = arguments.length; var expires = (argc > 2) ? argv[2] : null; var path = (argc > 3) ? argv[3] : '/'; var domain = (argc > 4) ? argv[4] : null; var secure = (argc > 5) ? argv[5] : false; document.cookie = name + "=" + escape (value) + ((expires == null) ? "" : ("; expires=" + expires.toGMTString())) + ((path == null) ? "" : ("; path=" + path)) + ((domain == null) ? "" : ("; domain=" + domain)) + ((secure == true) ? "; secure" : ""); }; Cookies.get = function(name){ var arg = name + "="; var alen = arg.length; var clen = document.cookie.length; var i = 0; var j = 0; while(i < clen){ j = i + alen; if (document.cookie.substring(i, j) == arg) return Cookies.getCookieVal(j); i = document.cookie.indexOf(" ", i) + 1; if(i == 0) break; } return null; }; Cookies.clear = function(name) { if(Cookies.get(name)){ document.cookie = name + "=" + "; expires=Thu, 01-Jan-70 00:00:01 GMT"; } }; Cookies.getCookieVal = function(offset){ var endstr = document.cookie.indexOf(";", offset); if(endstr == -1){ endstr = document.cookie.length; } return unescape(document.cookie.substring(offset, endstr)); }; ~~~ 示例: ~~~ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>OperateCookie.html</title> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="this is my page"> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <script type="text/javascript" src="cookie.js"></script> <script type="text/javascript"> function set() { Cookies.set("name", "tom"); Cookies.set("sex", "男"); } function get() { alert(Cookies.get("name")); alert(Cookies.get("sex")); } function clear(s) { Cookies.clear(s); } </script> </head> <body> <input type="button" value=" set " onclick="set()"/> <input type="button" value=" get " onclick="get()"/> <input type="button" value=" clear sex " onclick="clear('sex')"/> <input type="button" value=" clear name " onclick="clear('name')"/> </body> </html> ~~~ 可以参考:[http://blog.csdn.net/IBM_hoojo/archive/2010/07/02/5709282.aspx](http://blog.csdn.net/IBM_hoojo/archive/2010/07/02/5709282.aspx)
';

前言

最后更新于:2022-04-01 12:00:50

> 原文出处:[ExtJS 专栏](http://blog.csdn.net/column/details/extjs-by-hoojo.html) 作者:[ibm_hoojo](http://blog.csdn.net/ibm_hoojo) **本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!** # ExtJS 专栏 > 介绍ExtJS相关知识、技术文章
';