hy 6 лет назад
Родитель
Сommit
ac80af5ada

+ 1 - 0
.gitignore

@@ -3,4 +3,5 @@ bootstrap.*
 classic.json*
 modern.json*
 .sencha
+*.log
 

+ 193 - 0
app/data/DataListData.json

@@ -0,0 +1,193 @@
+{
+    "total": "27",
+    "data": [{
+        "id": "1",
+        "price": "71.72",
+        "company": "3m Co",
+        "date": "2007-09-01",
+        "size": "large",
+        "visible": "1"
+    }, {
+        "id": "2",
+        "price": "31.61",
+        "company": "AT&T Inc.",
+        "date": "2008-02-01",
+        "size": "extra large",
+        "visible": "0"
+    }, {
+        "id": "3",
+        "price": "29.01",
+        "company": "Aloca Inc",
+        "date": "2007-08-01",
+        "size": "medium",
+        "visible": "0"
+    }, {
+        "id": "4",
+        "price": "83.81",
+        "company": "Altria Group Inc",
+        "date": "2007-08-03",
+        "size": "large",
+        "visible": "0"
+    }, {
+        "id": "5",
+        "price": "52.55",
+        "company": "American Express Company",
+        "date": "2008-01-04",
+        "size": "extra large",
+        "visible": "1"
+    }, {
+        "id": "6",
+        "price": "64.13",
+        "company": "American International Group Inc.",
+        "date": "2008-03-04",
+        "size": "small",
+        "visible": "1"
+    }, {
+        "id": "7",
+        "price": "75.43",
+        "company": "Boeing Co.",
+        "date": "2008-01-01",
+        "size": "large",
+        "visible": "1"
+    }, {
+        "id": "8",
+        "price": "67.27",
+        "company": "Caterpillar Inc.",
+        "date": "2007-12-03",
+        "size": "medium",
+        "visible": "1"
+    }, {
+        "id": "9",
+        "price": "49.37",
+        "company": "Citigroup, Inc.",
+        "date": "2007-11-24",
+        "size": "large",
+        "visible": "1"
+    }, {
+        "id": "10",
+        "price": "40.48",
+        "company": "E.I. du Pont de Nemours and Company",
+        "date": "2007-05-09",
+        "size": "extra large",
+        "visible": "0"
+    }, {
+        "id": "11",
+        "price": "68.1",
+        "company": "Exxon Mobile Corp",
+        "date": "2007-12-12",
+        "size": "large",
+        "visible": "1"
+    }, {
+        "id": "12",
+        "price": "34.14",
+        "company": "General Electric Company",
+        "date": "2008-06-16",
+        "size": "extra large",
+        "visible": "1"
+    }, {
+        "id": "13",
+        "price": "30.27",
+        "company": "General Motors Corporation",
+        "date": "2006-12-07",
+        "size": "medium",
+        "visible": "1"
+    }, {
+        "id": "14",
+        "price": "36.53",
+        "company": "Hewlett-Packard Co.",
+        "date": "2007-05-13",
+        "size": "large",
+        "visible": "1"
+    }, {
+        "id": "15",
+        "price": "38.77",
+        "company": "Honweywell Intl Inc",
+        "date": "2006-11-07",
+        "size": "medium",
+        "visible": "0"
+    }, {
+        "id": "16",
+        "price": "19.88",
+        "company": "Intel Corporation",
+        "date": "2007-01-09",
+        "size": "small",
+        "visible": "1"
+    }, {
+        "id": "17",
+        "price": "81.41",
+        "company": "International Business Machines",
+        "date": "2005-01-21",
+        "size": "extra large",
+        "visible": "1"
+    }, {
+        "id": "18",
+        "price": "64.72",
+        "company": "Johnson & Johnson",
+        "date": "2008-01-10",
+        "size": "extra large",
+        "visible": "1"
+    }, {
+        "id": "19",
+        "price": "45.73",
+        "company": "JP Morgan & Chase & Co",
+        "date": "2008-02-20",
+        "size": "large",
+        "visible": "0"
+    }, {
+        "id": "20",
+        "price": "36.76",
+        "company": "McDonald's Corporation",
+        "date": "2007-06-12",
+        "size": "large",
+        "visible": "1"
+    }, {
+        "id": "21",
+        "price": "27.96",
+        "company": "Pfizer Inc",
+        "date": "2007-12-30",
+        "size": "small",
+        "visible": "0"
+    }, {
+        "id": "22",
+        "price": "45.07",
+        "company": "The Coca-Cola Company",
+        "date": "2007-01-30",
+        "size": "medium",
+        "visible": "0"
+    }, {
+        "id": "23",
+        "price": "34.64",
+        "company": "The Home Depot, Inc",
+        "date": "2006-12-31",
+        "size": "small",
+        "visible": "1"
+    }, {
+        "id": "24",
+        "price": "61.91",
+        "company": "The Procter & Gamble Company",
+        "date": "2007-04-08",
+        "size": "extra large",
+        "visible": "1"
+    }, {
+        "id": "25",
+        "price": "63.26",
+        "company": "United Technologies Corporation",
+        "date": "2006-06-04",
+        "size": "medium",
+        "visible": "1"
+    }, {
+        "id": "26",
+        "price": "35.57",
+        "company": "Verizon Communications",
+        "date": "2005-07-09",
+        "size": "small",
+        "visible": "0"
+    }, {
+        "id": "27",
+        "price": "45.45",
+        "company": "Wal-Mart Stores, Inc",
+        "date": "2006-09-09",
+        "size": "large",
+        "visible": "1"
+    }]
+}

+ 27 - 0
app/model/DataListGridModel.js

@@ -0,0 +1,27 @@
+/*
+ * @Description: 数据列表Model
+ * @Author: hy
+ * @Date: 2019-08-12 18:35:44
+ * @LastEditTime: 2019-08-12 18:45:29
+ */
+Ext.define('uas.model.DataListGridModel', {
+    extend: 'Ext.data.Model',
+    fields: [{
+        name: 'id',
+        type: 'int'
+    }, {
+        name: 'company'
+    }, {
+        name: 'price',
+        type: 'float'
+    }, {
+        name: 'date',
+        type: 'date',
+        dateFormat: 'Y-m-d'
+    }, {
+        name: 'visible',
+        type: 'boolean'
+    }, {
+        name: 'size'
+    }]
+});

+ 45 - 0
app/overrides/util/ClickRepeater.js

@@ -0,0 +1,45 @@
+/*
+ * @Description: numberField 清除按钮点击重复调用的bug
+ * @Author: hy
+ * @Date: 2019-08-12 18:31:11
+ * @LastEditTime: 2019-08-12 18:40:58
+ */
+Ext.define('uas.overrides.util.ClickRepeater', {
+    override: 'Ext.util.ClickRepeater',
+
+    privates: {
+        handleMouseDown: function(e) {
+            var me = this,
+                el = me.getEl();
+            Ext.undefer(me.timer);
+            if (me.pressedCls) {
+                el.addCls(me.pressedCls);
+            }
+            me.mousedownTime = Ext.now();
+            if (e.pointerType === 'mouse') {
+                el.on("mouseout", me.handleMouseOut, me);
+            }
+            Ext.getDoc().on("mouseup", me.handleMouseUp, me);
+            me.fireEvent("mousedown", me, e);
+            me.fireClick(e);
+            // Do not honor delay or interval if acceleration wanted.
+            if (me.accelerate) {
+                me.delay = 400;
+            }
+            //numberField 清除按钮点击重复调用的bug
+            if(me.getEl().dom.id.indexOf('menuNumberField')<0){
+                me.timer = Ext.defer(me.click, me.delay || me.interval, me, [
+                    e
+                ]);
+            }
+            if (me.mousedownPreventDefault) {
+                e.preventDefault();
+            }
+            if (me.mousedownStopEvent) {
+                e.stopEvent();
+            }
+        }
+    }
+});
+
+    

+ 22 - 0
app/store/DataListGridStore.js

@@ -0,0 +1,22 @@
+/*
+ * @Description: 列表store
+ * @Author: hy
+ * @Date: 2019-08-12 18:34:16
+ * @LastEditTime: 2019-08-12 18:45:17
+ */
+Ext.define('uas.store.DataListGridStore', {
+    extend: 'Ext.data.Store',
+    alias: 'store.dataListGridStore',
+    model: 'uas.model.DataListGridModel',
+    proxy: {
+        type: 'ajax',
+        url: 'data/DataListData.json',
+        reader: {
+            type: 'json',
+            rootProperty: 'data',
+            idProperty: 'id',
+            totalProperty: 'total'
+        }
+    },
+    remoteSort: false
+});

+ 108 - 0
app/view/grid/dataList/DataListPanel.js

@@ -0,0 +1,108 @@
+/*
+ * @Description: 数据列表
+ * @Author: hy
+ * @Date: 2019-08-12 18:33:04
+ * @LastEditTime: 2019-08-12 18:46:40
+ */
+Ext.define('uas.view.grid.dataList.DataListPanel', {
+    extend: 'Ext.grid.Panel',
+    xtype: 'dataListPanel',
+    requires: [
+        'uas.view.plugins.gridHeaderFilter.GridHeaderFilter',
+        'uas.store.DataListGridStore'
+    ],
+
+    title: '筛选头列表',
+    collapsible: true,
+    frame: true,
+    height: 600,
+    resizable: true,
+
+    plugins: {
+        gridHeaderFilter: true
+    },
+
+    emptyText: '无数据',
+    loadMask: true,
+    stateful: true,
+
+    // Set a stateId so that this grid's state is persisted.
+    stateId: 'stateful-filter-grid',
+
+    store: {
+        type: 'dataListGridStore',
+        url: 'data/grid/grid-filter.json',
+        autoLoad: true,
+        autoDestroy: true
+    },
+
+    // Dispatch named listener and handler methods to this instance
+    defaultListenerScope: true,
+
+    tbar: {
+        name:'filterToolbar',
+        height:46,
+        items:['->']
+    },
+
+    columns: [{
+        dataIndex: 'id',
+        text: 'Id',
+        width: 50
+
+        // Specify that this column has an associated Filter. This is
+        // processed by the gridfilters plugin. If this is a string,
+        // this is the type of filter to apply.
+        // filter: 'number'
+    }, {
+        dataIndex: 'company',
+        text: 'Company',
+        flex: 1,
+
+        // As an object, the type property indicates the type of filter to
+        // apply. All other properties configure that filter instance.
+        filter: {
+            type:'string'
+        }
+    }, {
+        dataIndex: 'price',
+        text: 'Price',
+        width: 90,
+        formatter: 'usMoney',
+        flex: 1,
+
+        filter: {
+            type:'number'
+        }
+    }, {
+        dataIndex: 'size',
+        text: 'Size',
+        width: 120,
+        flex: 1,
+
+        filter: {
+            type:'combo',
+            combo:[
+                ["small", "小"],
+                ["medium", "中"],
+                ["large", "大"],
+                ["extra large", "最大"]
+            ]
+        }
+    }, {
+        xtype: 'datecolumn',
+        dataIndex: 'date',
+        text: 'Date',
+        width: 120,
+        flex: 1,
+
+        filter: {
+            type:'date'
+        }
+    }, {
+        dataIndex: 'visible',
+        text: 'Visible',
+        width: 80,
+        flex: 1,
+    }]
+});

+ 705 - 0
app/view/plugins/gridHeaderFilter/GridHeaderFilter.js

@@ -0,0 +1,705 @@
+/*
+ * @Description: 列表筛选头
+ * @Author: hy
+ * @Date: 2019-07-29 15:22:51
+ * @LastEditTime: 2019-08-12 18:38:13
+ */
+Ext.define('uas.view.plugins.gridHeaderFilter.GridHeaderFilter', {
+    extend: 'Ext.plugin.Abstract',
+    alias: 'plugin.gridHeaderFilter',
+    mixins: [
+        'Ext.util.StoreHolder'
+    ],
+    requires: [
+        'Ext.grid.filters.filter.*',
+        'uas.view.plugins.gridHeaderFilter.field.*'
+    ],
+    id: 'gridHeaderFilter',
+    /**
+     * @property {Object} defaultFilterTypes
+     * This property maps {@link Ext.data.Model#cfg-fields field type} to the
+     * appropriate grid filter type.
+     * @private
+     */
+    defaultFilterTypes: {
+        'boolean': 'boolean',
+        'int': 'number',
+        date: 'date',
+        number: 'number'
+    },
+    /**
+     * @property {String} [filterCls="x-grid-filters-filtered-column"]
+     * The CSS applied to column headers with active filters.
+     */
+    filterCls: Ext.baseCSSPrefix + 'grid-filters-filtered-column',
+    /**
+     * @cfg {String} [menuFilterText]
+     * The text for the filters menu.
+     * @locale
+     */
+    menuFilterText: 'Filters',
+    /**
+     * @cfg {Boolean} showMenu
+     * Defaults to true, including a filter submenu in the default header menu.
+     */
+    showMenu: true,
+    /**
+     * @cfg {String} stateId
+     * Name of the value to be used to store state information.
+     */
+    stateId: undefined,
+    init: function (grid) {
+        var me = this,store;
+        Ext.Assert.falsey(me.grid);
+        me.grid = grid;
+        grid.filters = me;//绑定到grid
+        if (me.grid.normalGrid) {
+            me.isLocked = true;
+        }
+        grid.clearFilters = me.clearFilters.bind(me);//清除过滤绑定到grid
+        grid.getHeaderFilter = me.getHeaderFilter.bind(me);//清除过滤绑定到grid
+        store = grid.store;
+
+        me.gridListeners = grid.on({
+            destroyable: true,
+            scope: me,
+            reconfigure: me.onReconfigure
+        });
+        if (store.isEmptyStore) {
+            return;
+        }
+        me.initColumns();
+    },
+    /**
+     * Creates the Filter objects for the current configuration.
+     * Reconfigure and on add handlers.
+     * @private
+     */
+    initColumns: function () {
+        var grid = this.grid,
+            store = grid.getStore(),
+            columns = grid.columnManager.getColumns(),
+            len = columns.length,
+            i, column, filter, filterCollection;
+        // We start with filters defined on any columns.
+        for (i = 0; i < len; i++) {
+            column = columns[i];
+            filter = column.filter;
+            if (filter) {
+                if (!filterCollection) {
+                    filterCollection = store.getFilters();
+                    filterCollection.beginUpdate();
+                }
+                this.createColumnFilter(column);
+            }
+        }
+        if (filterCollection) {
+            filterCollection.endUpdate();
+        }
+    },
+    /**
+     * 根据类型生成不同的筛选头
+     */
+    createColumnFilter: function (column) {
+        const me = this;
+        const filter = column.filter;
+        if(!column.hasHeaderField && !column.hidden){
+            if(typeof filter.type === 'string'){
+                switch (filter.type) {
+                    case 'string':
+                        me.createStringFilter(column);
+                        column.hasHeaderField = true
+                        break;
+                    case 'number':
+                        me.createNumberFilter(column);
+                        column.hasHeaderField = true
+                        break;
+                    case 'combo':
+                        me.createComboFilter(column,filter.combo);
+                        column.hasHeaderField = true
+                        break;
+                    case 'date':
+                        me.createDateFilter(column);
+                        column.hasHeaderField = true
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+    },
+    /**
+     * 为toolbar添加标签
+     */
+    addTips:function(column,filterToolbar,text){
+        filterToolbar.add({
+            iconCls:'x-btn-field-clear',
+            xtype: 'button',
+            text: text,
+            iconAlign: 'right',
+            handler:function(btn){
+                if( window.event && window.event.target && window.event.target.getAttribute('id')
+                    && window.event.target.getAttribute('id').indexOf('-btnIconEl')>-1 ){
+                        if(column.filterContainer.input.xtype==="resetComboField"){
+                            column.filterContainer.input.setValue([]);
+                        }else{
+                            column.filterContainer.input.setValue(null);
+                        }
+                        filterToolbar.remove(btn);
+                }
+            }
+        });
+    },
+
+    /**
+     * @description: 将筛选条件转换成sql
+     * @param {} 
+     * @return: SQLString
+     */    
+    getHeaderFilter:function(){
+        const me = this;
+        const { grid } = me;
+        const columns = grid.getColumns();
+        const filterToolbar = grid.down('[name=filterToolbar]');
+        filterToolbar.removeAll();
+        let condition = [];
+        columns.map(column=>{
+            if(column.hasHeaderField && column.filterContainer ){
+                let con = column.filterContainer.getCondition();
+                if(con){
+                    condition.push(con);
+                    //更新toolbar
+                    let text = `${column.text}/${column.filterContainer.operator.operateText} : <span style="color:#ff0000">${con.value[0]}</span>`
+                    switch (con.type) {
+                        case 'string':
+                            if(con.operate==='isNull'){
+                                text = `${column.text} : <span style="color:#ff0000">${con.value[0]}</span>`
+                            }
+                            me.addTips(column,filterToolbar,text);
+                            break;
+                        case 'number':
+                            if(con.operate==='~'){
+                                text = `${column.text} : <span style="color:#ff0000">${con.value[0].split('~')[0]} ~ ${con.value[0].split('~')[1]}</span>`
+                            }else{
+                                text = `${column.text} <span style="color:#ff0000">${isNaN(Number(con.value[0]))?con.value[0]:(con.operate+con.value[0])}</span>`
+                            }
+                            me.addTips(column,filterToolbar,text);
+                            break;
+                        case 'combo':
+                            text = `${column.text}/${column.filterContainer.operator.operateText} : <span style="color:#ff0000">${con.value.join(',')}</span>`
+                            me.addTips(column,filterToolbar,text);
+                            break;
+                        case 'date':
+                    
+                            break;
+                        default:
+                            break;
+                    }
+                }
+            }
+        });
+        // condition = Ext.JSON.encodeValue(condition, '\n').replace(/^[ ]+/gm, function (s) {
+        //     for (var r = '', i = s.length; i--;) {
+        //         r += '&#160;';
+        //     }
+        //     return r;
+        // });
+        // condition = condition.replace(/\n/g, '<br>');
+        // Ext.toast({
+        //     html: condition,
+        //     closable: false,
+        //     align: 't',
+        //     slideDUration: 400,
+        //     maxWidth: 400
+        // });
+    },
+
+    /**
+     * 时间日期过滤头
+     */
+    createDateFilter:function(column){
+        const me = this;
+        let config = {
+            xtype:'resetDateField'
+        };
+        //下拉按钮
+        let operator = {
+            xtype:'operateButton',
+            operateText: '等于',
+            operate: "equal"
+        };
+        //筛选头容器
+        let DateFilter = {
+            xtype:'filterContainer',
+            inputType:config.xtype,
+            items:[ config,operator ],
+            getCondition:function(){
+                const me = this;
+                let items = [],value=[];
+                const { operator,input } = me;
+                const inputV = input.getValue();
+                if(inputV && Ext.isString(inputV)){
+                    items = inputV.split(',');
+                    items.map(item=>{
+                        input.datas.map(data=>{
+                            if(data[1]===item){
+                                value.push(data[0])
+                            }
+                        })
+                    });
+                    return {
+                        type: 'combo',
+                        operate: operator.operate,
+                        field: column.dataIndex,
+                        value: value
+                    };
+                }else{
+                    return '';
+                }
+            }
+        };
+        //加入到表头
+        let filterContainer = column.insert(0,DateFilter);
+        //绑定到列
+        column.filterContainer = filterContainer;
+        filterContainer.items.items.map((item)=>{
+            item.on({
+                scope: me,
+                keyup: Ext.emptyFn,
+                el: {
+                    click: function(e) {
+                        e.stopPropagation();
+                    }
+                }
+            });
+        });
+    },
+
+    /**
+     * 下拉框过滤头
+     */
+    createComboFilter:function(column,combo){
+        const me = this;
+        let config = {
+            xtype:'resetComboField',
+            datas:combo||[]
+        };
+        //下拉按钮
+        let operator = {
+            xtype:'operateButton',
+            operateText: '包括',
+            operate: "in"
+        };
+        //筛选头容器
+        let ComboFilter = {
+            xtype:'filterContainer',
+            inputType:config.xtype,
+            items:[ config,operator ],
+            getCondition:function(){
+                const me = this;
+                let items = [],value=[];
+                const { operator,input } = me;
+                const inputV = input.getValue();
+                if(inputV && Ext.isString(inputV)){
+                    items = inputV.split(',');
+                    items.map(item=>{
+                        input.datas.map(data=>{
+                            if(data[1]===item){
+                                value.push(data[0])
+                            }
+                        })
+                    });
+                    return {
+                        type: 'combo',
+                        operate: operator.operate,
+                        field: column.dataIndex,
+                        value: value
+                    };
+                }else{
+                    return '';
+                }
+            }
+        };
+        //加入到表头
+        let filterContainer = column.insert(0,ComboFilter);
+        //绑定到列
+        column.filterContainer = filterContainer;
+        filterContainer.items.items.map((item)=>{
+            item.on({
+                scope: me,
+                keyup: Ext.emptyFn,
+                el: {
+                    click: function(e) {
+                        e.stopPropagation();
+                    }
+                }
+            });
+        });
+    },
+
+    /**
+     * 数字过滤头
+     */
+    createNumberFilter:function(column){
+        const me = this;
+        //逻辑按钮
+        let operator = {
+            xtype:'operateButton',
+            operateText: '等于',
+            operate: "=",
+            menu: {
+                xtype:'filterMenu',
+                width:220,
+                maxWidth:220,
+                defaults:{
+                    xtype:'menuNumberField'
+                },
+                items:[{
+                    fieldLabel:'等于',
+                    operate: "="
+                },{
+                    fieldLabel: '不等于',
+                    operate: "!="
+                },{
+                    fieldLabel: '大于',
+                    operate: ">"
+                },{
+                    fieldLabel: '大于等于',
+                    operate: ">="
+                },{
+                    fieldLabel: '小于',
+                    operate: "<"
+                },{
+                    fieldLabel: '小于等于',
+                    operate: "<="
+                },{
+                    fieldLabel: '介于',
+                    xtype: 'container',
+                    layout: 'hbox',
+                    operate: "~",
+                    margin:'2 5 0 0',
+                    items:[{
+                        margin:'6 0 0 10',
+                        xtype:'displayfield',
+                        value:'介于:&nbsp;'
+                    },{
+                        width:77,
+                        hideLabel:true,
+                        xtype:'menuNumberField'
+                    },{
+                        margin:'6 0 0 0',
+                        xtype:'displayfield',
+                        value:'~&nbsp;'
+                    },{
+                        width:77,
+                        hideLabel:true,
+                        xtype:'menuNumberField'
+                    }],
+                    setValue:function(v){
+                        const me = this;
+                        //校验Value 符合条件则赋值
+                        let v1=null,v2=null;
+                        if(v&&v.indexOf('~')>-1){
+                            let arr = v.split('~');
+                            if(arr[0]){ v1 = arr[0]; }
+                            if(arr[1]){ v2 = arr[1]; }
+                        }
+                        me.items.items.map((item,index)=>{
+                            if(item.xtype==="menuNumberField"){
+                                if(index === 1){
+                                    item.setValue(v1);
+                                }else{
+                                    item.setValue(v2);
+                                }
+                            }
+                            if(index===0){
+                                if(v1 || v2){
+                                    item.inputEl.dom.classList.add('x-menu-label-select')
+                                }else{
+                                    item.inputEl.dom.classList.remove('x-menu-label-select')
+                                }
+                            }
+                        })
+                    }
+                }]
+            }
+        }
+        //带清除按钮的输入框1 选择逻辑类型为介于时显示
+        let config = {
+            xtype:'resetNumberField'
+        }
+        //筛选头容器
+        let NumberFilter = {
+            xtype:'filterContainer',
+            inputType:config.xtype,
+            items:[ config,operator ],
+            getCondition:function(){
+                const me = this;
+                const { operator,input } = me;
+                const inputV = input.getValue();
+                if(inputV){
+                    if(operator.fieldLabel==='介于'){
+                        return {
+                            type: 'number',
+                            operate: operator.operate,
+                            field: column.dataIndex,
+                            value: [inputV.split('~')[0],inputV.split('~')[1]]
+                        };
+                    }else{
+                        return {
+                            type: 'number',
+                            operate: operator.operate,
+                            field: column.dataIndex,
+                            value: [inputV]
+                        };
+                    }
+                }else{
+                    return '';
+                }
+            }
+        }
+        //加入到表头
+        let filterContainer = column.insert(0,NumberFilter);
+        //绑定到列
+        column.filterContainer = filterContainer;
+        filterContainer.items.items.map((item)=>{
+            item.on({
+                scope: me,
+                keyup: Ext.emptyFn,
+                el: {
+                    click: function(e) {
+                        e.stopPropagation();
+                    }
+                }
+            });
+        });
+    },
+
+    /**
+     * 字符串过滤头
+     */
+    createStringFilter:function(column){
+        const me = this;
+        //逻辑按钮
+        let operator = {
+            xtype:'operateButton',
+            operate: 'like',
+            operateText: '包含',
+            menu: {
+                xtype:'filterMenu',
+                width:200,
+                maxWidth:200,
+                defaults:{
+                    xtype:'menuTextField'
+                },
+                items:[{
+                    fieldLabel:'包含',
+                    operate: "like"
+                },{
+                    fieldLabel: '不包含',
+                    operate: "notLike"
+                },{
+                    fieldLabel: '开头是',
+                    operate: "first"
+                },{
+                    fieldLabel: '结尾是',
+                    operate: "last"
+                },{
+                    fieldLabel: '等于',
+                    operate: "equal"
+                },{
+                    fieldLabel: '不等于',
+                    operate: "unEqual",
+                },{
+                    margin:'5 0 0 20',
+                    labelWidth:70,
+                    xtype: 'checkbox',
+                    fieldLabel: '空(未填写)',
+                    operate: "isNull",
+                    listeners:{
+                        change:function(me,newValue,oldValue){
+                            const menu = me.up();
+                            const container = menu.up().up();
+                            const { operator,input }= container;
+                            const grid = container.up().up().up();
+                            if(window.event.target.getAttribute('id').indexOf('checkboxfield')>-1){
+                                //清除其它输入框
+                                menu.items.items.map(item=>{
+                                    if(item.operate!==me.operate){
+                                        item.setValue(null);
+                                    }
+                                })
+                                if(input.value && input.value === me.fieldLabel){
+                                    input.setValue(null)
+                                    operator.operate = "like";
+                                    operator.operateText = '包含';
+                                }else{
+                                    input.setValue(me.fieldLabel);
+                                    operator.operate = me.operate;
+                                    operator.operateText = me.fieldLabel;
+                                }
+                                grid.getHeaderFilter();
+                                menu.hide();
+                            }
+                        }
+                    }
+                }]
+            }
+        }
+        //带清除按钮的输入框
+        let config = {
+            xtype:'resetTextField',
+        }
+        //筛选头容器
+        let StringFilter = {
+            xtype:'filterContainer',
+            inputType:config.xtype,
+            items:[ config, operator ],
+            getCondition:function(){
+                const column = this.up();
+                const logic = this.operator.operate;
+                const inputV = this.input.getValue();
+                if(inputV){
+                    return {
+                        type: 'string',
+                        operate: logic,
+                        field: column.dataIndex,
+                        value: [inputV]
+                    };
+                }else{
+                    return '';
+                }
+            }
+        }
+
+        //加入到表头
+        let filterContainer = column.insert(0,StringFilter);
+        //绑定到列
+        column.filterContainer = filterContainer;
+        //更改作用域
+        filterContainer.items.items.map((item)=>{
+            item.on({
+                scope: me,
+                keyup: Ext.emptyFn,
+                el: {
+                    click: function(e) {
+                        e.stopPropagation();
+                    }
+                }
+            });
+        })
+    },
+
+    destroy: function () {
+        var me = this,
+            filterMenuItem = me.filterMenuItem,
+            item;
+        Ext.destroy(me.headerCtListeners, me.gridListeners, me.headerMenuListeners);
+        me.sep = Ext.destroy(me.sep);
+        for (item in filterMenuItem) {
+            filterMenuItem[item].destroy();
+        }
+        this.callParent();
+    },
+    getHeaders: function () {
+        return this.grid.view.headerCt.columnManager.getColumns();
+    },
+    /**
+     * Checks the plugin's grid for statefulness.
+     * @return {Boolean}
+     */
+    isStateful: function () {
+        return this.grid.stateful;
+    },
+    /**
+     * Adds a filter to the collection and creates a store filter if has a `value` property.
+     * @param {Object/Object[]/Ext.util.Filter/Ext.util.Filter[]} filters A filter
+     * configuration or a filter object.
+     */
+    clearFilters: function () {
+        var grid = this.grid,
+            columns = grid.columnManager.getColumns(),
+            store = grid.store,
+            column, filter, i, len, filterCollection;
+        // We start with filters defined on any columns.
+        for (i = 0, len = columns.length; i < len; i++) {
+            column = columns[i];
+            filter = column.filter;
+            if (filter && filter.isGridFilter) {
+                if (!filterCollection) {
+                    filterCollection = store.getFilters();
+                    filterCollection.beginUpdate();
+                }
+                filter.setActive(false);
+            }
+        }
+        if (filterCollection) {
+            filterCollection.endUpdate();
+        }
+    },
+    onReconfigure: function (grid, store, columns, oldStore) {
+        var me = this,
+            filterMenuItem = me.filterMenuItem,
+            changed = oldStore !== store,
+            key;
+        // The Filters item's menu should have already been destroyed by the time we get here but
+        // we still need to null out the menu reference.
+        if (columns) {
+            for (key in filterMenuItem) {
+                //filterMenuItem[key].setMenu(null);
+            }
+        }
+        if (store) {
+            if (oldStore && !oldStore.destroyed && changed) {
+                me.resetFilters(oldStore);
+            }
+            if (changed) {
+                me.applyFilters(store);
+            }
+        }
+        me.initColumns();
+    },
+    privates: {
+        applyFilters: function (store) {
+            var columns = this.grid.columnManager.getColumns(),
+                len = columns.length,
+                i, column, filter, filterCollection;
+            // We start with filters defined on any columns.
+            for (i = 0; i < len; i++) {
+                column = columns[i];
+                filter = column.filter;
+                if (filter && filter.isGridFilter) {
+                    if (!filterCollection) {
+                        filterCollection = store.getFilters();
+                        filterCollection.beginUpdate();
+                    }
+                    if (filter.active) {
+                        filter.activate();
+                    }
+                }
+            }
+            if (filterCollection) {
+                filterCollection.endUpdate();
+            }
+        },
+        resetFilters: function (store) {
+            var filters = store.getFilters(),
+                i, updating, filter;
+            if (filters) {
+                for (i = filters.getCount() - 1; i >= 0; --i) {
+                    filter = filters.getAt(i);
+                    if (filter.isGridFilter) {
+                        if (!updating) {
+                            filters.beginUpdate();
+                        }
+                        filters.remove(filter);
+                        updating = true;
+                    }
+                }
+                if (updating) {
+                    filters.endUpdate();
+                }
+            }
+        }
+    }
+});

+ 71 - 0
app/view/plugins/gridHeaderFilter/GridHeaderFilter.scss

@@ -0,0 +1,71 @@
+.x-grid-header-filter{
+    padding: 3px;
+    width: 100% !important;
+    .x-box-inner{
+        height:38px !important;
+        width: 100% !important;
+    }
+    .x-box-target{
+        height: 100% !important;
+        flex-direction: row;
+        display: flex;
+        width: 100% !important;
+    }
+    .x-grid-header-filter-operator{
+        border-width: 0px;
+        padding: 0;
+        height: 32px;
+        overflow: unset !important;
+        position: initial !important;
+        .x-btn-wrap-default-small.x-btn-arrow-right:after {
+            margin-right: 8px;
+        }
+    }
+    .x-grid-header-filter-input{
+        position: initial !important;
+        width: 100%;
+        .x-form-item-label-default{
+            width:0px !important;
+        }
+        .x-form-trigger-default:before {
+            content: '';
+        }
+        .x-form-text-default {
+            font: 400 13px/21px 'Microsoft YaHei';
+            padding: 4px 0 4px 4px;
+        }
+    }
+}
+.x-grid-header-filter-operator-menu{
+    .x-grid-header-filter-menu-field{
+        .x-form-trigger-default:before {
+            content: '';
+        }
+    }
+    .x-menu-label-select{
+        color:#5fa2dd;
+    }
+}
+.x-btn-field-clear{
+    width: 26px;
+    background-position: center !important;
+    background-repeat: no-repeat;
+    background:url(get-resource-path('images/clear.png')) no-repeat;
+}
+.x-datepicker-buttons{
+    flex-direction: row;
+    display: flex;
+    width: 100%;
+    height: 30px;
+    .x-btn-default-small{
+        flex: 1;
+        padding:2px;
+    }
+    .x-btn-default-toolbar-small{
+        height:26px !important;
+        padding: 0 2px;
+    }
+    .x-btn-inner-default-small {
+        padding: 0 2px;
+    }
+}

+ 24 - 0
app/view/plugins/gridHeaderFilter/field/FilterContainer.js

@@ -0,0 +1,24 @@
+/*
+ * @Description: 筛选头容器
+ * @Author: hy
+ * @Date: 2019-08-05 17:29:53
+ * @LastEditTime: 2019-08-12 18:39:07
+ */
+Ext.define('uas.view.plugins.gridHeaderFilter.field.FilterContainer', {
+    extend: 'Ext.container.Container',
+    alias: 'widget.filterContainer',
+    xtype: 'filterContainer',
+    height:38,
+    cls:'x-grid-header-filter',
+    layout: 'hbox', 
+    listeners:{
+        afterrender:function(p){
+            //绑定 逻辑按钮 和 输入框
+            const operator = p.down('[name=operator]');
+            const input = p.down(p.inputType);
+            //input.getTriggers().picker.hide();
+            p.operator = operator;
+            p.input = input;
+        }
+    }
+});

+ 35 - 0
app/view/plugins/gridHeaderFilter/field/FilterMenu.js

@@ -0,0 +1,35 @@
+/*
+ * @Description: 筛选头下拉菜单
+ * @Author: hy
+ * @Date: 2019-08-07 16:47:33
+ * @LastEditTime: 2019-08-12 18:39:01
+ */
+Ext.define('uas.view.plugins.gridHeaderFilter.field.FilterMenu', {
+    extend: 'Ext.menu.Menu',
+    alias: 'widget.filterMenu',
+    xtype: 'filterMenu',
+    cls:'x-grid-header-filter-operator-menu',
+    onMouseOver: function(e) {
+        var me = this,
+            fromEl = e.getRelatedTarget(),
+            mouseEnter = !me.el.contains(fromEl),
+            item = me.getItemFromEvent(e),
+            parentMenu = me.parentMenu,
+            parentItem = me.parentItem;
+
+        if (mouseEnter && parentMenu) {
+            parentMenu.setActiveItem(parentItem);
+            parentItem.cancelDeferHide();
+            parentMenu.mouseMonitor.mouseenter();
+        }
+
+        if (me.disabled) {
+            return;
+        }
+
+        if (mouseEnter) {
+            me.fireEvent('mouseenter', me, e);
+        }
+        me.fireEvent('mouseover', me, item, e);
+    }
+});

+ 49 - 0
app/view/plugins/gridHeaderFilter/field/OperateButton.js

@@ -0,0 +1,49 @@
+/*
+ * @Description: 筛选头下拉按钮
+ * @Author: hy
+ * @Date: 2019-08-01 17:48:33
+ * @LastEditTime: 2019-08-12 18:38:56
+ */
+Ext.define('uas.view.plugins.gridHeaderFilter.field.OperateButton', {
+    extend: 'Ext.button.Button',
+    alias: 'widget.operateButton',
+    xtype: 'operateButton',
+    width:0,
+    cls:'x-grid-header-filter-operator',
+    name:'operator',
+    changeAnimate:function(width){
+        this.animate({
+            duration: 300,
+            to: {
+                width: width,
+            }
+        });
+    },
+    listeners:{
+        menuhide:function( btn ){
+            if( window.event && window.event.target && window.event.target.getAttribute('id') &&
+                (window.event.target.getAttribute('id').indexOf('resetTextField')<0 &&
+                window.event.target.getAttribute('id').indexOf('resetNumberField')<0 &&
+                 window.event.target.getAttribute('id').indexOf('operateButton')<0) ){
+                btn.changeAnimate(0)
+                btn.onBlur();
+            } 
+        },
+        menushow:function( btn, menu, e ){
+            const v = btn.ownerCt.input.getValue();
+            menu.items.items.map(item=>{
+                if(item.operate === btn.operate && v){
+                    if( item.xtype==='checkbox' ) {
+                        item.setValue(v?true:false)
+                    }else{
+                        item.setValue(v)
+                    }
+                    item.labelEl&&item.labelEl.dom.classList.add('x-menu-label-select')
+                }else{
+                    item.setValue(null)
+                    item.labelEl&&item.labelEl.dom.classList.remove('x-menu-label-select')
+                }
+            })
+        }
+    }
+});

+ 197 - 0
app/view/plugins/gridHeaderFilter/field/combo/ResetComboField.js

@@ -0,0 +1,197 @@
+/*
+ * @Description: 
+ * @Author: hy
+ * @Date: 2019-08-12 18:37:09
+ * @LastEditTime: 2019-08-12 18:40:42
+ */
+Ext.define('uas.view.plugins.gridHeaderFilter.field.number.ResetComboField', {
+    extend: 'Ext.form.field.Text',
+    alias: 'widget.resetComboField',
+    xtype:'resetComboField',
+    editable : false,
+    matchFieldWidth: true,
+    value: [],
+    cachedConfig: {
+        menuAlign: 'tl-bl?',
+        destroyMenu: true
+    },
+    //清除按钮
+    config: {
+        triggers: {
+            picker: {
+                cls:'x-btn-field-clear',
+                scope: 'this',
+                handler:function(me,picker,e){
+                    const grid = me.up().up().up().up();
+                    me.setValue([]);
+                    grid.getHeaderFilter();
+                }
+            }
+        }
+    },
+    emptyText:'',
+    labelWidth: 0,
+    enableKeyEvents: true,
+    cls: 'x-grid-header-filter-input',
+    initComponent: function() {
+        var me = this;
+        me.callParent(arguments);
+    },
+    listeners:{
+        focus: function() {
+            const me = this;
+            me.showMenu();
+        }
+    },
+    getMenu: function() {
+        var me = this;
+
+        if (!me.menu) {
+
+            me.menu = Ext.create('Ext.menu.Menu',{
+                parent:me,
+                hideOnParentHide: false,
+                items: me.getMenuItems(),
+                listeners:{
+                    beforehide:function(menu,e){
+                        const event = window.event || event;
+                        if(event.target && event.target.getAttribute('id') === menu.parent.inputId){
+                            return false;
+                        }
+                        menu.parent.triggerWrap.el.dom.classList.remove('x-form-trigger-wrap-focus')
+                    }
+                }
+            });
+        }
+        return me.menu;
+    },
+    getMenuItems:function(){
+        var me = this,
+            i = 0,
+            item,
+            items = me.datas,
+            itemsLn = me.datas.length,
+            menuItems = [],
+            value = me.value==true? me.value:[];
+        for (; i < itemsLn; i++) {
+            item = items[i];
+            var checked = !!value.find(function(v) {
+                return v.value == iem[0];
+            });
+            menuItem = new Ext.menu.CheckItem({
+                text: item[1],
+                checked: checked,
+                checkValue:item[0],
+                hideOnClick: false,
+                checkHandler: this.onCheckChange,
+                scope: this
+            });
+            menuItems.push(menuItem);
+        }
+        return menuItems.length ? menuItems : null;
+    },
+    onCheckChange:function(checkItem, checked){
+        var items=this.menu.items.items,
+            checkedItems = [];
+        Ext.Array.each(items,function(item){
+            if(item.checked){
+                checkedItems.push({
+                    text: item.text,
+                    value: item.checkValue
+                });
+            }
+        });
+        this.setValue(checkedItems);
+    },
+
+    setValue: function(items) {
+        var me = this, rawV = '',
+        datas = me.datas,
+        menu = me.menu;
+        if(Ext.isString(items)) {
+            items = items.split(',');
+        }
+        items.map(function(item, index) {
+            if(Ext.isString(item)) {
+                // var data = datas.find(function(d) {
+                //     return d[0] == item;
+                // });
+                var data = Ext.Array.findBy(datas, function(d) {
+                    return d[0] == item || d[1] == item;
+                });
+                if(data) {
+                    items[index] = {
+                        text: data[1],
+                        value: data[0]
+                    }
+                }else {
+                    items[index] = {
+                        text: item,
+                        value: item
+                    }
+                }
+            }
+        })
+        rawV = items.map(function(item) {
+            return item.text
+        }).join(',');
+        this.setRawValue(rawV);
+        this.value = items;
+        if(menu) {
+            Ext.Array.each(menu.items.items, function(item) {
+                item.setChecked(!!items.find(function(i) {
+                    return i.value == item.checkValue;
+                }));
+            });
+        }
+        if(rawV){
+            me.getTriggers().picker.show();
+        }else{
+            me.getTriggers().picker.hide();
+        }
+        if(me.rendered){
+            const grid = me.up().up().up().up();
+            grid.getHeaderFilter();
+        }
+        this.publishState('value', items);
+    },
+
+    showMenu: function (e, menu) {
+        menu = menu || this.getMenu();
+        /**
+         * menu上边框样式待处理
+         * */
+        if (menu) {
+            if (menu.isVisible()) {
+                    menu.focus();
+            } else {
+                menu.autoFocus = true;
+                if(this.matchFieldWidth){
+                    menu.setWidth(this.bodyEl.getWidth());
+                }
+                if (menu.isMenu) {
+                    menu.showBy(this.inputEl, this.getMenuAlign(),[-1, 0]);
+                } else if (menu.isViewportMenu) {
+                    menu.setDisplayed(!menu.getDisplayed());
+                } else {
+                    menu.show();
+                }
+            }
+        }
+    },
+    hideMenu: function(b) {
+        if (this.hasVisibleMenu()) {
+            var target=b.parentEvent.relatedTarget;
+            if(target){
+                if(!((target.className && target.className.indexOf('x-menu')!=-1) || (target.name && target.name.indexOf(this.name)!=-1))){
+                    this.menu.hide();
+                }
+            }
+        }
+        return this;
+    },
+    hasVisibleMenu: function() {
+        var menu = this.menu;
+        return menu && menu.rendered && menu.isVisible();
+    }
+});

+ 213 - 0
app/view/plugins/gridHeaderFilter/field/date/DateTypePicker.js

@@ -0,0 +1,213 @@
+/*
+ * @Description: 日期筛选头-日期选择组件
+ * @Author: hy
+ * @Date: 2019-08-12 18:37:09
+ * @LastEditTime: 2019-08-12 18:39:58
+ */
+Ext.define('uas.view.plugins.gridHeaderFilter.field.date.DateTypePicker', {
+    extend: 'Ext.picker.Date',
+    alias: 'widget.dateTypePicker',
+    xtype: 'dateTypePicker',
+    renderTpl: [
+        '<div id="{id}-innerEl" data-ref="innerEl" role="presentation">',
+        '<div id="{id}-topButtons" data-ref="topButtons" class="{baseCls}-buttons" role="heading">',
+            '<tpl for="topButtons">',
+                '{%this.renderButtons(values, out)%}',
+            '</tpl>',
+        '</div>',
+        '<div class="{baseCls}-header">',
+        '<div id="{id}-prevEl" data-ref="prevEl" class="{baseCls}-prev {baseCls}-arrow" role="presentation" title="{prevText}"></div>',
+        '<div id="{id}-middleBtnEl" data-ref="middleBtnEl" class="{baseCls}-month" role="heading">{%this.renderMonthBtn(values, out)%}</div>',
+        '<div id="{id}-nextEl" data-ref="nextEl" class="{baseCls}-next {baseCls}-arrow" role="presentation" title="{nextText}"></div>',
+        '</div>',
+        '<table role="grid" id="{id}-eventEl" data-ref="eventEl" class="{baseCls}-inner" cellspacing="0" tabindex="0" aria-readonly="true">',
+        '<thead>',
+        '<tr role="row">',
+        '<tpl for="dayNames">',
+        '<th role="columnheader" class="{parent.baseCls}-column-header" aria-label="{.}">',
+        '<div role="presentation" class="{parent.baseCls}-column-header-inner">{.:this.firstInitial}</div>',
+        '</th>',
+        '</tpl>',
+        '</tr>',
+        '</thead>',
+        '<tbody>',
+        '<tr role="row">',
+        '<tpl for="days">',
+        '{#:this.isEndOfWeek}',
+        '<td role="gridcell">',
+        '<div hidefocus="on" class="{parent.baseCls}-date"></div>',
+        '</td>',
+        '</tpl>',
+        '</tr>',
+        '</tbody>',
+        '</table>',
+        '<div id="{id}-bottomButtons" data-ref="bottomButtons" class="{baseCls}-buttons" role="presentation">',
+            '<tpl for="bottomButtons">',
+                '{%this.renderButtons(values, out)%}',
+            '</tpl>',
+        '</div>',
+        '</div>',
+        {
+            firstInitial: function(value) {
+                return Ext.picker.Date.prototype.getDayInitial(value);
+            },
+            isEndOfWeek: function(value) {
+                // convert from 1 based index to 0 based
+                // by decrementing value once.
+                value--;
+                var end = value % 7 === 0 && value !== 0;
+                return end ? '</tr><tr role="row">' : '';
+            },
+            renderTodayBtn: function(values, out) {
+                Ext.DomHelper.generateMarkup(values.$comp.todayBtn.getRenderTree(), out);
+            },
+            renderMonthBtn: function(values, out) {
+                Ext.DomHelper.generateMarkup(values.$comp.monthBtn.getRenderTree(), out);
+            },
+            renderButtons: function(button, out) {
+                Ext.DomHelper.generateMarkup(button.getRenderTree(), out);
+            }
+        }
+    ],
+    beforeRender: function() {
+        /*
+         * days array for looping through 6 full weeks (6 weeks * 7 days)
+         * Note that we explicitly force the size here so the template creates
+         * all the appropriate cells.
+         */
+        var me = this,
+            encode = Ext.String.htmlEncode,
+            days = new Array(me.numDays),
+            today = Ext.Date.format(new Date(), me.format);
+        if (me.padding && !me.width) {
+            me.cacheWidth();
+        }
+        if (me.showTopButtons){
+            me.equal = new Ext.button.Button({
+                margin:'5 0 0 8',
+                ownerCt: me,
+                ownerLayout: me.getComponentLayout(),
+                text: '等于',
+                tabIndex: -1,
+                ariaRole: 'presentation',
+                handler: Ext.emptyFn,
+                scope: me
+            });
+            me.begin = new Ext.button.Button({
+                margin:'5 0 0 0',
+                ownerCt: me,
+                ownerLayout: me.getComponentLayout(),
+                text: '开始于',
+                tabIndex: -1,
+                ariaRole: 'presentation',
+                handler: Ext.emptyFn,
+                scope: me
+            });
+            me.end = new Ext.button.Button({
+                margin:'5 0 0 0',
+                ownerCt: me,
+                ownerLayout: me.getComponentLayout(),
+                text: '结束于',
+                tabIndex: -1,
+                ariaRole: 'presentation',
+                handler: Ext.emptyFn,
+                scope: me
+            });
+            me.between = new Ext.button.Button({
+                margin:'5 8 0 0',
+                ownerCt: me,
+                ownerLayout: me.getComponentLayout(),
+                text: '介于',
+                tabIndex: -1,
+                ariaRole: 'presentation',
+                handler: Ext.emptyFn,
+                scope: me
+            });
+        }
+        me.monthBtn = new Ext.button.Button({
+            ownerCt: me,
+            ownerLayout: me.getComponentLayout(),
+            text: '',
+            tooltip: me.monthYearText,
+            tabIndex: -1,
+            ariaRole: 'presentation',
+            showEmptyMenu: false,
+            menu: [],
+            listeners: {
+                click: me.doShowMonthPicker,
+                scope: me
+            }
+        });
+        if (me.showBottomButtons) {
+            me.nowDayBtn = new Ext.button.Button({
+                margin:'0 0 0 5',
+                ui: me.footerButtonUI,
+                ownerCt: me,
+                ownerLayout: me.getComponentLayout(),
+                text: '今天',
+                tabIndex: -1,
+                ariaRole: 'presentation',
+                handler: me.selectToday,
+                scope: me
+            });
+            me.nowMonthBtn = new Ext.button.Button({
+                ui: me.footerButtonUI,
+                ownerCt: me,
+                ownerLayout: me.getComponentLayout(),
+                text: '本月',
+                tabIndex: -1,
+                ariaRole: 'presentation',
+                handler: me.selectToday,
+                scope: me
+            });
+            me.preMonthBtn = new Ext.button.Button({
+                ui: me.footerButtonUI,
+                ownerCt: me,
+                ownerLayout: me.getComponentLayout(),
+                text: '上月',
+                tabIndex: -1,
+                ariaRole: 'presentation',
+                handler: me.selectToday,
+                scope: me
+            });
+            me.nowYearBtn = new Ext.button.Button({
+                ui: me.footerButtonUI,
+                ownerCt: me,
+                ownerLayout: me.getComponentLayout(),
+                text: '本年',
+                tabIndex: -1,
+                ariaRole: 'presentation',
+                handler: me.selectToday,
+                scope: me
+            });
+            me.preYearBtn = new Ext.button.Button({
+                ui: me.footerButtonUI,
+                ownerCt: me,
+                ownerLayout: me.getComponentLayout(),
+                text: '上年',
+                tabIndex: -1,
+                ariaRole: 'presentation',
+                handler: me.selectToday,
+                scope: me
+            });
+        }
+        me.callParent();
+        Ext.applyIf(me, {
+            renderData: {}
+        });
+        Ext.apply(me.renderData, {
+            dayNames: me.dayNames,
+            topButtons: me.showTopButtons?[me.equal,me.begin,me.end,me.between]:[],
+            bottomButtons: me.showBottomButtons?[me.nowDayBtn,me.nowMonthBtn,me.preMonthBtn,me.nowYearBtn,me.preYearBtn]:[],
+            prevText: encode(me.prevText),
+            nextText: encode(me.nextText),
+            todayText: encode(me.todayText),
+            ariaMinText: encode(me.ariaMinText),
+            ariaMaxText: encode(me.ariaMaxText),
+            ariaDisabledDaysText: encode(me.ariaDisabledDaysText),
+            ariaDisabledDatesText: encode(me.ariaDisabledDatesText),
+            days: days
+        });
+        me.protoEl.unselectable();
+    }
+});

+ 72 - 0
app/view/plugins/gridHeaderFilter/field/date/ResetDateField.js

@@ -0,0 +1,72 @@
+/*
+ * @Description: 日期筛选头输入框
+ * @Author: hy
+ * @Date: 2019-08-12 18:37:09
+ * @LastEditTime: 2019-08-12 18:40:30
+ */
+Ext.define('uas.view.plugins.gridHeaderFilter.field.date.ResetDateField', {
+    extend: 'Ext.form.field.Text',
+    alias: 'widget.resetDateField',
+    xtype:'resetDateField',
+    editable : false,
+    matchFieldWidth: true,
+    value: [],
+    cachedConfig: {
+        menuAlign: 'tl-bl?',
+        destroyMenu: true
+    },
+    //清除按钮
+    config: {
+        triggers: {
+            picker: {
+                hidden:true,
+                cls:'x-btn-field-clear',
+                scope: 'this',
+                handler:function(me,picker,e){
+                    const grid = me.up().up().up().up();
+                    me.setValue([]);
+                    grid.getHeaderFilter();
+                }
+            }
+        }
+    },
+    emptyText:'',
+    labelWidth: 0,
+    enableKeyEvents: true,
+    cls: 'x-grid-header-filter-input',
+    initComponent: function() {
+        var me = this;
+        me.callParent(arguments);
+    },
+    listeners:{
+        focus: function() {
+            const me = this;
+            me.showDatePicker()
+        },
+        blur:function(){
+            const me = this;
+            me.datePicker&&me.datePicker.hide();
+        }
+    },
+    showDatePicker: function () {
+        var me = this;
+        const XY = me.getAlignToXY(me.inputEl);
+        if(me.datePicker){
+            me.datePicker.showAt(...XY);
+        }else{
+            me.datePicker = me.createDatePicker().showAt(...XY);
+        }
+    },
+    createDatePicker:function(){
+        const me = this;
+        return new Ext.create("uas.view.plugins.gridHeaderFilter.field.date.DateTypePicker",{  
+            pickerField: me,
+            floating: true, 
+            startDay : 0,
+            dataIndex:this.dataIndex,
+            showToday : false,
+            showTopButtons: true,//顶部逻辑按钮栏
+            showBottomButtons: true,//底部快速日期栏
+        });
+    }
+});

+ 143 - 0
app/view/plugins/gridHeaderFilter/field/number/MenuNumberField.js

@@ -0,0 +1,143 @@
+/*
+ * @Description: 筛选头下拉菜单输入框
+ * @Author: hy
+ * @Date: 2019-08-06 17:02:27
+ * @LastEditTime: 2019-08-12 18:39:22
+ */
+Ext.define('uas.view.plugins.gridHeaderFilter.field.number.MenuNumberField', {
+    extend: 'Ext.form.field.Number',
+    alias: 'widget.MenuNumberField',
+    xtype: 'menuNumberField',
+    emptyText:'',
+    cls: 'x-grid-header-filter-menu-field',
+    labelAlign:'right',
+    labelWidth:60,
+    margin:'5 5 0 0',
+    hideTrigger:true,
+    //清除按钮
+    config: {
+        triggers: {
+            picker: {
+                cls:'x-btn-field-clear',
+                handler:function(btn){
+                    var me = this.ownerCt.fieldLabel==='介于'?this.ownerCt:this;
+                    const menu = me.ownerCt;
+                    const container = me.up().up().up();
+                    const { input,operator } = container;
+                    const grid = container.up().up().up();
+                    if(operator.operate===me.operate){
+                        if(me.operate==='~'){
+                            const minValue = me.items.items[1].getValue(),
+                                  maxValue = me.items.items[3].getValue();
+                            if(typeof minValue==='number' && typeof maxValue==='number'){
+                                input.setValue(null);
+                                operator.operate = '=';
+                                operator.operateText = '等于';
+                                menu.hide();
+                                grid.getHeaderFilter();
+                            }else{
+                                this.setValue(null);
+                            }
+                        }else{
+                            input.setValue(null);
+                            operator.operate = '=';
+                            operator.operateText = '等于';
+                            menu.hide();
+                            grid.getHeaderFilter();
+                        }
+                    }
+                    me.operate!=='~'?me.setValue(null):this.setValue(null);//介于时不直接清除
+                },
+                scope: 'this'
+            }
+        }
+    },
+
+    listeners:{
+        change:function(f,newValue,oldValue){
+            if(newValue!==oldValue){
+                if(typeof newValue==='number'){
+                    f.getTriggers().picker.show();
+                }else{
+                    f.getTriggers().picker.hide();
+                }
+            }
+        },
+        specialkey: function(me, e){
+            if (!me.isValid()) {
+                return false;
+            }
+            if (e.getKey() == e.ENTER) {
+                me = this.ownerCt.fieldLabel==='介于'?this.ownerCt:this;
+                const menu = me.ownerCt;
+                const container = me.up().up().up();
+                const { input,operator } = container;
+                const grid = container.up().up().up();
+                if(me.operate!=='~'){
+                    input.setValue(me.operate + me.value);
+                    menu.hide();
+                }else{
+                    const minValue = me.items.items[1].getValue(),
+                          maxValue = me.items.items[3].getValue();
+                    if(typeof minValue==='number' && typeof maxValue==='number'){
+                        if(minValue>maxValue){
+                            Ext.toast({html: '左边的数值不能大于右边的数值'});
+                        }else{
+                            input.setValue( minValue + me.operate + maxValue );
+                            menu.hide();
+                        }
+                    }
+                }
+                operator.operate = me.operate;
+                operator.operateText = me.fieldLabel;
+                grid.getHeaderFilter();
+            }
+        }
+    },
+    setValue: function(value) {
+        var me = this,
+            bind, valueBind;
+        //解析value
+        if(value&&me.judgeValue(value+'')){
+            if((value+'').indexOf(me.operate)>-1){
+                value = value.replace(me.operate,'')
+            }
+        }else{
+            value = value===0?0:null
+        }
+        if (me.hasFocus) {
+            bind = me.getBind();
+            valueBind = bind && bind.value;
+            if (valueBind && valueBind.syncing && value === me.value) {
+                return me;
+            }
+        }
+        return me.callParent([
+            value
+        ]);
+    },
+    judgeValue:function(value){
+        if(value.indexOf('>=')==0||value.indexOf('<=')==0||value.indexOf('>')==0||value.indexOf('<')==0||value.indexOf('!=')==0||value.indexOf('=')==0){
+            var reg = /^(>=|>|<=|<|=|!=)?(-)?([1-9][0-9]+|[0-9])([.][0-9]+)?$/;
+            if(value.match(reg)==null){
+                return false;
+            }
+        }else if(value.indexOf('~')>-1){
+            var arr = value.split('~');
+            var reg = /^(-)?([1-9][0-9]+|[0-9])([.][0-9]+)?~(-)?([1-9][0-9]+|[0-9])([.][0-9]+)?$/;
+            if(value.match(reg)==null){
+                return false;
+            }else if(Number(arr[0])>Number(arr[1])){
+                return false;
+            }
+        }else if(value.indexOf('>=')>-1||value.indexOf('<=')>-1||value.indexOf('>')>-1||value.indexOf('<')>-1||value.indexOf('!=')>-1||value.indexOf('=')>-1){
+            return false;
+        }else{
+            var reg = /^(-)?([1-9][0-9]+|[0-9])([.][0-9]+)?$/;
+            if(value.match(reg)==null){
+                return false;
+            }
+        }
+        return true;
+    }
+});

+ 96 - 0
app/view/plugins/gridHeaderFilter/field/number/ResetNumberField.js

@@ -0,0 +1,96 @@
+/*
+ * @Description: 筛选头数字输入框
+ * @Author: hy
+ * @Date: 2019-08-01 13:55:07
+ * @LastEditTime: 2019-08-12 18:39:27
+ */
+Ext.define('uas.view.plugins.gridHeaderFilter.field.number.ResetNumberField', {
+    extend: 'Ext.form.field.Text',
+    alias: 'widget.resetNumberField',
+    xtype: 'resetNumberField',
+    //清除按钮
+    config: {
+        triggers: {
+            picker: {
+                cls:'x-btn-field-clear',
+                scope: 'this',
+                handler:function(me,picker,e){
+                    const grid = me.up().up().up().up();
+                    me.setValue(null);
+                    grid.getHeaderFilter();
+                }
+            }
+        }
+    },
+    hideTrigger:true,
+    emptyText:'',
+    labelWidth: 0,
+    enableKeyEvents: true,
+    cls: 'x-grid-header-filter-input',
+
+    listeners:{
+        afterrender:function(me){
+            me.maskRe =new RegExp('[0123456789.]+|(\\s)'); 
+        },
+        focus:function(){
+            const me = this;
+            me.ownerCt.operator.changeAnimate(24);
+        },
+        blur:function(){
+            const me = this;
+            if(this.ownerCt.operator.el.dom.classList.toString().indexOf(' x-btn-pressed')<0){
+                me.ownerCt.operator.changeAnimate(0);
+            }
+        },
+        change:function(f,newValue,oldValue){
+            if(newValue!==oldValue){
+                if(newValue&&newValue!=''){
+                    f.getTriggers().picker.show();
+                }else{
+                    f.getTriggers().picker.hide();
+                }
+            }
+        },
+        specialkey: function(me, e){
+            const grid = me.up().up().up().up();
+            const v = me.getValue();
+            if(!v && v===''){
+                grid.getHeaderFilter();
+            }
+            if(v && me.judgeValue(v)){
+                if (e.getKey() == e.ENTER) {
+                    grid.getHeaderFilter();
+                }
+            }
+        }
+    },
+    judgeValue:function(value){
+        if(value.indexOf('>=')==0||value.indexOf('<=')==0||value.indexOf('>')==0||value.indexOf('<')==0||value.indexOf('!=')==0||value.indexOf('=')==0){
+            var reg = /^(>=|>|<=|<|=|!=)?(-)?([1-9][0-9]+|[0-9])([.][0-9]+)?$/;
+            if(value.match(reg)==null){
+                Ext.toast('输入筛选数据有误,请重新输入!');
+                return false;
+            }
+        }else if(value.indexOf('~')>-1){
+            var arr = value.split('~');
+            var reg = /^(-)?([1-9][0-9]+|[0-9])([.][0-9]+)?~(-)?([1-9][0-9]+|[0-9])([.][0-9]+)?$/;
+            if(value.match(reg)==null){
+                Ext.toast('输入筛选数据有误,请重新输入!');
+                return false;
+            }else if(Number(arr[0])>Number(arr[1])){
+                Ext.toast('前面的数字不能大于后面的数字!');
+                return false;
+            }
+        }else if(value.indexOf('>=')>-1||value.indexOf('<=')>-1||value.indexOf('>')>-1||value.indexOf('<')>-1||value.indexOf('!=')>-1||value.indexOf('=')>-1){
+            Ext.toast('输入筛选数据有误,请重新输入!');
+            return false;
+        }else{
+            var reg = /^(-)?([1-9][0-9]+|[0-9])([.][0-9]+)?$/;
+            if(value.match(reg)==null){
+                Ext.toast('输入筛选数据有误,请重新输入!');
+                return false;
+            }
+        }
+        return true;
+    }
+});

+ 69 - 0
app/view/plugins/gridHeaderFilter/field/string/MenuTextField.js

@@ -0,0 +1,69 @@
+/*
+ * @Description: 筛选头下拉菜单输入框
+ * @Author: hy
+ * @Date: 2019-07-31 17:10:30
+ * @LastEditTime: 2019-08-12 18:38:46
+ */
+Ext.define('uas.view.plugins.gridHeaderFilter.field.string.MenuTextField', {
+    extend: 'Ext.form.field.Text',
+    alias: 'widget.MenuTextField',
+    xtype: 'menuTextField',
+    emptyText:'',
+    cls: 'x-grid-header-filter-menu-field',
+    labelAlign:'right',
+    labelWidth:50,
+    margin:'5 5 0 0',
+    //清除按钮
+    config: {
+        triggers: {
+            picker: {
+                hidden:true,
+                cls:'x-btn-field-clear',
+                handler:function(btn){
+                    var me = this;
+                    const menu = me.ownerCt;
+                    const container = me.up().up().up();
+                    const { input,operator } = container;
+                    const grid = container.up().up().up();
+                    me.setValue(null);
+                    if(operator.operate===me.operate){
+                        input.setValue(null);
+                        operator.operateText = '包含';
+                        menu.hide();
+                        grid.getHeaderFilter();
+                    }
+                },
+                scope: 'this'
+            }
+        }
+    },
+
+    listeners:{
+        change:function(f,newValue,oldValue){
+            if(newValue!==oldValue){
+                if(newValue&&newValue!=''){
+                    f.getTriggers().picker.show();
+                }else{
+                    f.getTriggers().picker.hide();
+                }
+            }
+        },
+        specialkey: function(me, e){
+            if (!me.isValid()) {
+                return false;
+            }
+            if (e.getKey() == e.ENTER) {
+                const menu = me.ownerCt;
+                const container = me.up().up().up();
+                const { input } = container;
+                let { operator } = container;
+                const grid = container.up().up().up();
+                input.setValue(me.value);
+                operator.operate = me.operate;
+                operator.operateText = me.fieldLabel;
+                menu.hide();
+                grid.getHeaderFilter();
+            }
+        }
+    }
+});

+ 62 - 0
app/view/plugins/gridHeaderFilter/field/string/ResetTextField.js

@@ -0,0 +1,62 @@
+/*
+ * @Description: 筛选头字符串输入框
+ * @Author: hy
+ * @Date: 2019-07-31 17:10:30
+ * @LastEditTime: 2019-08-12 18:39:15
+ */
+Ext.define('uas.view.plugins.gridHeaderFilter.field.String.ResetTextField', {
+    extend: 'Ext.form.field.Text',
+    alias: 'widget.resetTextField',
+    xtype: 'resetTextField',
+    emptyText:'',
+    labelWidth: 0,
+    cls: 'x-grid-header-filter-input',
+    //清除按钮
+    config: {
+        triggers: {
+            picker: {
+                hidden:true,
+                cls:'x-btn-field-clear',
+                handler:function(btn){
+                    var me = this;
+                    me.setValue(null)
+                    const grid = me.up().up().up().up();
+                    grid.getHeaderFilter();
+                },
+                scope: 'this'
+            }
+        }
+    },
+
+    listeners:{
+        focus:function(){
+            const me = this;
+            me.ownerCt.operator.changeAnimate(24);
+        },
+        blur:function(){
+            const me = this;
+            if(this.ownerCt.operator.el.dom.classList.toString().indexOf(' x-btn-pressed')<0){
+                me.ownerCt.operator.changeAnimate(0);
+            }
+        },
+        change:function(f,newValue,oldValue){
+            if(newValue!==oldValue){
+                if(newValue&&newValue!=''){
+                    f.getTriggers().picker.show();
+                }else{
+                    f.getTriggers().picker.hide();
+                }
+            }
+        },
+        specialkey: function(field, e){
+            if (!field.isValid()) {
+                return false;
+            }
+            if (e.getKey() == e.ENTER) {
+                const grid = field.up().up().up().up();
+                field.blur();
+                grid.getHeaderFilter();
+            }
+        }
+    }
+});

+ 1 - 1
resources/json/navigation.json

@@ -12,7 +12,7 @@
         "children": [
             {
                 "text": "筛选头",
-                "target": "grid-header-filter",
+                "target": "dataListPanel",
                 "leaf": true,
                 "iconCls": "x-fa fa-smile-o"
             },