Extjs4.2 Grid搜索Ext.ux.grid.feature.Searching的使用

背景

Extjs4.2 默认提供的Search搜索,功能还是非常强大的,只是对于国内的用户来说,还是不习惯在每列里面单击好几下再筛选,于是相当当初2.2里面的搜索,更加的实用点,于是在4.2里面实现。

国际惯例,先上图

参考文献

https://gist.github.com/aghuddleston/3297619/

http://gridsearch.extjs.eu/

国外的大牛已经帮我们实现了在4.0中的应用,但是到4.2还需要做少许变更才可以使用。

修改后的源代码如下[复制如下代码,放到ux/grid/features/Searching.js]:

// JavaScript Document// vim: ts=4:sw=4:nu:fdc=4:nospell
/**
 * Search plugin for Ext.grid.GridPanel, Ext.grid.EditorGrid ver. 2.x or subclasses of them
 *
 * @author    Ing. Jozef Sakalos
 * @copyright (c) 2008, by Ing. Jozef Sakalos
 * @date      17. January 2008
 * @version   $Id: Ext.ux.grid.Search.js 220 2008-04-29 21:46:51Z jozo $
 *
 * @license Ext.ux.grid.Search is licensed under the terms of
 * the Open Source LGPL 3.0 license.  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.
 * 
 * License details: http://www.gnu.org/licenses/lgpl.html
 */

/*
    Revised for Ext 4
    by Nathan LeBlanc
    on July 8, 2011
*/

Ext.define('Ext.ux.grid.feature.Searching', {
    extend: 'Ext.grid.feature.Feature',
    alias: 'feature.searching',

    /**
     * cfg {Boolean} autoFocus true to try to focus the input field on each store load (defaults to undefined)
     */

    /**
     * @cfg {String} searchText Text to display on menu button
     */
    searchText:'查询',

    /**
     * @cfg {String} searchTipText Text to display as input tooltip. Set to '' for no tooltip
     */ 
    searchTipText:'输入关键字回车查询',

    /**
     * @cfg {String} selectAllText Text to display on menu item that selects all fields
     */
    selectAllText:'所有列',

    /**
     * @cfg {String} position Where to display the search controls. Valid values are top and bottom (defaults to bottom)
     * Corresponding toolbar has to exist at least with mimimum configuration tbar:[] for position:top or bbar:[]
     * for position bottom. Plugin does NOT create any toolbar.
     */
    position:'top',

    /**
     * @cfg {String} iconCls Icon class for menu button (defaults to icon-magnifier)
     */
    iconCls: 'Zoom',

    /**
     * @cfg {String/Array} checkIndexes Which indexes to check by default. Can be either 'all' for all indexes
     * or array of dataIndex names, e.g. ['persFirstName', 'persLastName']
     */
    checkIndexes:'all',

    /**
     * @cfg {Array} disableIndexes Array of index names to disable (not show in the menu), e.g. ['persTitle', 'persTitle2']
     */
    disableIndexes:[],

    /**
     * @cfg {String} dateFormat how to format date values. If undefined (the default) 
     * date is formatted as configured in colummn model
     */
    dateFormat:undefined,

    /**
     * @cfg {Boolean} showSelectAll Select All item is shown in menu if true (defaults to true)
     */
    showSelectAll:true,

    /**
     * @cfg {String} menuStyle Valid values are 'checkbox' and 'radio'. If menuStyle is radio
     * then only one field can be searched at a time and selectAll is automatically switched off.
     */
    menuStyle:'checkbox',

    /**
     * @cfg {Number} minChars minimum characters to type before the request is made. If undefined (the default)
     * the trigger field shows magnifier icon and you need to click it or press enter for search to start. If it
     * is defined and greater than 0 then maginfier is not shown and search starts after minChars are typed.
     */
    minChars: 2,
    /**
     * @cfg {String} minCharsTipText Tooltip to display if minChars is > 0
     */
    minCharsTipText:'至少输入{0}个字符',

    /**
     * @cfg {String} mode Use 'remote' for remote stores or 'local' for local stores. If mode is local
     * no data requests are sent to server the grid's store is filtered instead (defaults to 'remote')
     */
    mode:'remote',

    /**
     * @cfg {Array} readonlyIndexes Array of index names to disable (show in menu disabled), e.g. ['persTitle', 'persTitle2']
     */

    /**
     * @cfg {Number} width Width of input field in pixels (defaults to 100)
     */
    width:100,

    /**
     * @cfg {Object} paramNames Params name map (defaults to {fields:'fields', query:'query'}
     */
    paramNames: {
         fields:'fields'
        ,query:'query'
    },

    /**
     * @cfg {String} shortcutKey Key to fucus the input field (defaults to r = Sea_r_ch). Empty string disables shortcut
     */
    shortcutKey:'r',

    /**
     * @cfg {String} shortcutModifier Modifier for shortcutKey. Valid values: alt, ctrl, shift (defaults to alt)
     */
    shortcutModifier:'alt',

    /**
     * @cfg {String} align 'left' or 'right' (defaults to 'left')
     */
    align:'left',
    /**
     * @cfg {Number} minLength force user to type this many character before he can make a search
     */
    minLength: 2,
    /**
     * @cfg {Ext.Panel/String} toolbarContainer Panel (or id of the panel) which contains toolbar we want to render
     * search controls to (defaults to this.grid, the grid this plugin is plugged-in into)
     */
            
    //attachEvents: function() {
    //    this.grid = this.view.up('gridpanel');
    //    if(this.grid.rendered)
    //        this.onRender();
    //    else
    //        this.grid.on('render', this.onRender, this);
    //},
    
    init: function (grid) {
        this.grid = grid;
        if (this.grid.rendered)
            this.onRender();
        else
            this.grid.on('render', this.onRender, this);
    },

    onRender:function() {
        
        var panel = this.toolbarContainer || this.grid;
        var tb = 'bottom' === this.position ? panel.getDockedItems('toolbar[dock="bottom"]') : panel.getDockedItems('toolbar[dock="top"]');
        if(tb.length > 0)
            tb = tb[0]
        else {
            tb = Ext.create('Ext.toolbar.Toolbar', {dock: this.position});
            panel.addDocked(tb);
        }

        // add menu
        this.menu = Ext.create('Ext.menu.Menu');

        // handle position
        if('right' === this.align) {
            tb.add('->');
        }
        else {
            if(0 < tb.items.getCount()) {
                tb.add('-');
            }
        }

        // add menu button
        tb.add({
             text:this.searchText
            ,menu:this.menu
            ,iconCls:this.iconCls
        });

        // add input field (TwinTriggerField in fact)
        this.field = Ext.create('Ext.form.TwinTriggerField', {
            width:this.width,
            qtip: 'ddd',
            selectOnFocus:undefined === this.selectOnFocus ? true : this.selectOnFocus,
            triggerCls: 'x-form-clear-trigger',
            //triggerCls: this.minChars ? 'x-hidden' : 'x-form-search-trigger',
            onTrigger1Click: Ext.bind(this.onTriggerClear, this),
            //onTrigger2Click: this.minChars ? Ext.emptyFn : Ext.bind(this.onTriggerSearch, this),
            //onTrigger1Click: this.minChars ? Ext.emptyFn : Ext.bind(this.onTriggerSearch, this),
            minLength:this.minLength
        });
        
        // install event handlers on input field
        this.field.on('render', function() {
            
            var qtip = this.minChars ? Ext.String.format(this.minCharsTipText, this.minChars) : this.searchTipText;
            Ext.QuickTips.register({
                target: this.field.inputEl,
                text: qtip
            });
            
            if(this.minChars) {
                this.field.el.on({scope:this, buffer:300, keyup:this.onKeyUp});
            }

            // install key map
            var map = new Ext.KeyMap(this.field.el, [{
                 key:Ext.EventObject.ENTER
                ,scope:this
                ,fn:this.onTriggerSearch
            },{
                 key:Ext.EventObject.ESC
                ,scope:this
                ,fn:this.onTriggerClear
            }]);
            map.stopEvent = true;
        }, this, {single:true});

        tb.add(this.field);

        // reconfigure
        this.reconfigure();

        // keyMap
        if(this.shortcutKey && this.shortcutModifier) {
            var shortcutEl = this.grid.getEl();
            var shortcutCfg = [{
                 key:this.shortcutKey
                ,scope:this
                ,stopEvent:true
                ,fn:function() {
                    this.field.focus();
                }
            }];
            shortcutCfg[0][this.shortcutModifier] = true;
            this.keymap = new Ext.KeyMap(shortcutEl, shortcutCfg);
        }

        if(true === this.autoFocus) {
            this.grid.store.on({scope:this, load:function(){this.field.focus();}});
        }
    } // eo function onRender
    // }}}
    // {{{
    /**
     * field el keypup event handler. Triggers the search
     * @private
     */
    ,onKeyUp:function() {
        var length = this.field.getValue().toString().length;
        if(0 === length || this.minChars <= length) {
            this.onTriggerSearch();
        }
    } // eo function onKeyUp
    // }}}
    // {{{
    /**
     * private Clear Trigger click handler
     */
    ,onTriggerClear:function() {
        if (this.field.getValue()) {
            //if (this.field.getValue().length < this.minChars) {
            //    this.field.setValue('');
            //    return;
            //}
            this.field.setValue('');
            this.field.focus();            
            this.onTriggerSearch();
        }
    } // eo function onTriggerClear
    // }}}
    // {{{
    /**
     * private Search Trigger click handler (executes the search, local or remote)
     */
    ,onTriggerSearch:function() {
        if(!this.field.isValid()) {
            return;
        }
        var val = this.field.getValue(),
            store = this.grid.store,
            proxy = store.getProxy();

        // grid's store filter
        if('local' === this.mode) {
            store.clearFilter();
            if(val) {
                store.filterBy(function(r) {
                    var retval = false;
                    this.menu.items.each(function(item) {
                        if(!item.checked || retval) {
                            return;
                        }
                        var rv = r.get(item.dataIndex);
                        rv = rv instanceof Date ? Ext.Date.format(rv, this.dateFormat || r.fields.get(item.dataIndex).dateFormat) : rv;
                        var re = new RegExp(val, 'gi');
                        retval = re.test(rv);
                    }, this);
                    if(retval) {
                        return true;
                    }
                    return retval;
                }, this);
            }
            else {
            }
        }
        // ask server to filter records
        // your proxy must be a Server proxy
        else if(proxy instanceof Ext.data.proxy.Server) {
            // clear start (necessary if we have paging)
            if(store.lastOptions && store.lastOptions.params) {
                store.lastOptions.params[store.paramNames.start] = 0;
            }

            // get fields to search array
            var fields = [];
            this.menu.items.each(function(item) {
                if(item.checked && item.dataIndex) {
                    fields.push(item.dataIndex);
                }
            });

            // add fields and query to baseParams of store
            delete(proxy.extraParams[this.paramNames.fields]);
            delete(proxy.extraParams[this.paramNames.query]);
            if (store.lastOptions && store.lastOptions.params) {
                delete(proxy.lastOptions.params[this.paramNames.fields]);
                delete(proxy.lastOptions.params[this.paramNames.query]);
            }
            if(fields.length) {
                proxy.extraParams[this.paramNames.fields] = (fields);
                proxy.extraParams[this.paramNames.query] = (val);
            }

            // reload store
            store.load();
        }

    } // eo function onTriggerSearch
    // }}}
    // {{{
    /**
     * @param {Boolean} true to disable search (TwinTriggerField), false to enable
     */
    ,setDisabled:function() {
        this.field.setDisabled.apply(this.field, arguments);
    } // eo function setDisabled
    // }}}
    // {{{
    /**
     * Enable search (TwinTriggerField)
     */
    ,enable:function() {
        this.setDisabled(false);
    } // eo function enable
    // }}}
    // {{{
    /**
     * Enable search (TwinTriggerField)
     */
    ,disable:function() {
        this.setDisabled(true);
    } // eo function disable
    // }}}
    // {{{
    /**
     * private (re)configures the plugin, creates menu items from column model
     */
    ,reconfigure:function() {
        // {{{
        // remove old items
        var menu = this.menu;
        menu.removeAll();

        // add Select All item plus separator
        if(this.showSelectAll && 'radio' !== this.menuStyle) {
            menu.add({
                xtype: 'menucheckitem',
                text:this.selectAllText,
                checked:!(this.checkIndexes instanceof Array),
                hideOnClick:false,
                handler:function(item) {
                    var checked = item.checked;
                    item.parentMenu.items.each(function(i) {
                        if(item !== i && i.setChecked && !i.disabled) {
                            i.setChecked(checked);
                        }
                    });
                }
            },'-');
        }

        // }}}
        // {{{
        // add new items
        var columns = this.grid.headerCt.items.items;
        var group = undefined;
        if('radio' === this.menuStyle) {
            group = 'g' + (new Date).getTime();    
        }
        
        Ext.each(columns, function(column) {
            var disable = false;
            if(column.text && column.dataIndex && column.dataIndex != '') {
                Ext.each(this.disableIndexes, function(item) {
                    disable = disable ? disable : item === column.dataIndex;
                });
                if(!disable) {
                    menu.add({
                        xtype: 'menucheckitem',
                        text: column.text,
                        hideOnClick: false,
                        group:group,
                        checked: 'all' === this.checkIndexes,
                        dataIndex: column.dataIndex,
                    });
                }
            }
        }, this);
        // }}}
        // {{{
        // check items
        if(this.checkIndexes instanceof Array) {
            Ext.each(this.checkIndexes, function(di) {
                var item = menu.items.findBy(function(itm) {
                    return itm.dataIndex === di;
                });
                if(item) {
                    item.setChecked(true, true);
                }
            }, this);
        }
        // }}}
        // {{{
        // disable items
        if(this.readonlyIndexes instanceof Array) {
            Ext.each(this.readonlyIndexes, function(di) {
                var item = menu.items.findBy(function(itm) {
                    return itm.dataIndex === di;
                });
                if(item) {
                    item.disable();
                }
            }, this);
        }
        // }}}

    } // eo function reconfigure
    // }}}

}); // eo extend

// eof
View Code
使用方法

非常简单,代码如下:

Ext.define('WMS.view.SystemGrid', {
    extend: 'Ext.grid.Panel',
    alias: 'widget.SystemGrid',
.................
features: [
        {
            ftype: 'searching',
            minChars: 2,
            width: 150,
            mode: 'remote',//远程还是本地store
            position: 'top/bottom',//状态栏还是工具栏
            iconCls: 'Zoom',//图标
            menuStyle: 'checkbox/radiobox',//单选还是多选
            showSelectAll: true,   //是否显示全选按钮
            checkIndexes: ["SysName"],       //默认是否选择所有的列
            disableIndexes: ["CreateUser", "CreateTime", "ModifyUser", "ModifyTime", "Disable"]          //禁止那些列参与查询
        }
    ],        

//其他参数参照searching的源代码
查看产生的URL

ok,收工。

下一篇文章将会介绍,MVC如果获取参数,以及如何将Extjs查询的参数动态转换为EntityFramework的Expression<Func<T, bool>>,构造查询SQL(http://www.cnblogs.com/qidian10/p/3209458.html)

posted @ 2013-07-23 21:29  jackchain  阅读(2276)  评论(0编辑  收藏  举报