ExtJS6.0扩展日期选择控件为也可以选择时间

PS:ExtJS自带的日期选择控件只能够选择日期,但是现在的需求需要精确到秒,所以在网上搜索了一些例子(大部分是4.0的)作为参考,然后改出了6.0可用的一个日期时间选择控件。

1、找到extjs6.0源代码中Picker文件路径下的Date.js脚本(路径:ext-6.0.0-gplext-6.0.0classicclassicsrcpicker),拷贝一份出来命名为DateTimePicker.js

2、修改命名空间(把白色底的改成黑色底的命名空间以及别名,你也可以修改为自己存放该文件的路径,名称也可以自己命名)

  

3、定位到showToday模块

   把原来的:'<div id="{id}-footerEl" data-ref="footerEl" role="presentation" class="{baseCls}-footer">{%this.renderTodayBtn(values, out)%}</div>',

   替换成:'<div id="{id}-footerEl" role="presentation" style="background-color:#D9E5F3;border-top:0px solid #99BCE8;">{%this.renderHour(values, out)%}{%this.renderMinute(values, out)%}{%this.renderSecond(values, out)%}<center>{%this.renderOkQueDingBtn(values, out)%}&nbsp;&nbsp;{%this.renderTodayBtn(values, out)%}</center></div>',

   也就是增加 时、分、秒、OK控件

   

1  '<tpl if="showToday">',
2                 //'<div id="{id}-footerEl" data-ref="footerEl" role="presentation" class="{baseCls}-footer">{%this.renderTodayBtn(values, out)%}</div>',
3               '<div id="{id}-footerEl" role="presentation" style="background-color:#D9E5F3;border-top:0px solid #99BCE8;">{%this.renderHour(values, out)%}{%this.renderMinute(values, out)%}{%this.renderSecond(values, out)%}<center>{%this.renderOkQueDingBtn(values, out)%}&nbsp;&nbsp;{%this.renderTodayBtn(values, out)%}</center></div>',
4             '</tpl>',

4、添加时、分、秒、OK控件的渲染事件

 1 longDay: function(value){
 2                 return Ext.Date.format(value, this.longDayFormat);
 3             },
 4             renderHour: function(values, out) {
 5                 out.push('<font  style="float : left;">&nbsp&nbsp</font>');
 6                 Ext.DomHelper.generateMarkup(values.$comp.hour.getRenderTree(), out);
 7             },
 8             renderMinute: function(values, out) {
 9                 out.push('<font  style="float : left;font-weight:bold;">&nbsp:&nbsp&nbsp</font>');
10                 Ext.DomHelper.generateMarkup(values.$comp.minute.getRenderTree(), out);
11             },
12             renderSecond: function(values, out) {
13                 out.push('<font style="float : left;font-weight:bold;">&nbsp:&nbsp&nbsp</font>');
14                 Ext.DomHelper.generateMarkup(values.$comp.second.getRenderTree(), out);
15             },
16             renderOkQueDingBtn: function(values, out) {  
17                 Ext.DomHelper.generateMarkup(values.$comp.okQueDingBtn.getRenderTree(), out);  
18             },

5、在beforeRender事件中,添加时、分、秒、OK控件的事件

me.monthBtn = new Ext.button.Split({
            ownerCt: me,
            ownerLayout: me.getComponentLayout(),
            text: '',
            tooltip: me.monthYearText,
            tabIndex: -1,
            ariaRole: 'presentation',
            listeners: {
                click: me.doShowMonthPicker,
                arrowclick: me.doShowMonthPicker,
                scope: me
            }
        });

        me.hour = Ext.create('Ext.form.field.Number', {
            scope: me,
            ownerCt: me,
            editable : true,
            ownerLayout: me.getComponentLayout(),
            minValue: 0,
            maxValue: 23,
             70,
            style : {float:"left"},
            enableKeyEvents: true,
            listeners: {
                 keyup: function(field, e){
                     if (field.getValue() > 23){
                         e.stopEvent();
                         field.setValue(23);
                     }
                 }
             }
        });
        
        me.minute = Ext.create('Ext.form.field.Number', {
            scope: me,
            ownerCt: me,
            style : {float:"left"},
            ownerLayout: me.getComponentLayout(),
            minValue: 0,
            maxValue: 59,
            editable : true,
             70,
            enableKeyEvents: true,
            listeners: {
                keyup: function(field, e){
                    if (field.getValue() > 59){
                        e.stopEvent();
                        field.setValue(59);
                    }
                }
            }
        });
        
      me.second = Ext.create('Ext.form.field.Number', {
            scope: me,
            ownerCt: me,
            editable : true,
            style : {float:"left"},
            ownerLayout: me.getComponentLayout(),
            minValue: 0,
            maxValue: 59,
             70,
            enableKeyEvents: true,
            listeners: {
                keyup: function(field, e){
                    if (field.getValue() > 59){
                        e.stopEvent();
                        field.setValue(59);
                    }
                }
            }
        });
       me.okQueDingBtn = new Ext.button.Button({  
            ownerCt: me,  
            ownerLayout: me.getComponentLayout(),  
            text: me.okText,  
            tooltip: me.okTip,  
            tooltipType:'title',  
            handler:me.okQueDingHandler,//确认按钮的事件委托  
            scope: me  
        });  

6、添加OK按钮处理事件

 1 /** 
 2      * 确认 按钮触发的调用 
 3      */  
 4     okQueDingHandler : function(){  
 5         var me = this,  
 6             btn = me.okQueDingBtn;  
 7   
 8         if(btn && !btn.disabled){  
 9             me.setValue(this.getValue());  
10             me.fireEvent('select', me, me.value);  
11             me.onSelect();  
12         }  
13         return me;  
14     },  

7、在update事件中添加更新时间的事件

1         date.setHours(me.hour.getValue());
2         date.setMinutes(me.minute.getValue());
3         date.setSeconds(me.second.getValue());

8、在beforeDestroy释放事件中添加释放OK按钮的事件

 1 beforeDestroy: function() {
 2         var me = this;
 3 
 4         if (me.rendered) {
 5             Ext.destroy(
 6                 me.keyNav,
 7                 me.monthPicker,
 8                 me.monthBtn,
 9                 me.nextRepeater,
10                 me.prevRepeater,
11                 me.todayBtn,
12                 me.okQueDingBtn,
13                 me.todayElSpan
14             );
15             delete me.textNodes;
16             delete me.cells.elements;
17         }
18         me.callParent();
19     },

9、添加其他释放事件

 1  finishRenderChildren: function () {
 2             var me = this;
 3 
 4             me.callParent();
 5             me.monthBtn.finishRender();
 6             me.okQueDingBtn.finishRender();  
 7             if (me.showToday) {
 8                 me.todayBtn.finishRender();
 9             }
10             //添加时间相关
11             this.hour.finishRender();
12             this.minute.finishRender();
13             this.second.finishRender();
14         },
15         

10、到此Date.js脚本就改造完毕了下面是完整的DateTimePicker.js脚本

   1 /**
   2  * A date picker. This class is used by the Ext.form.field.Date field to allow browsing and selection of valid
   3  * dates in a popup next to the field, but may also be used with other components.
   4  *
   5  * Typically you will need to implement a handler function to be notified when the user chooses a date from the picker;
   6  * you can register the handler using the {@link #select} event, or by implementing the {@link #handler} method.
   7  *
   8  * By default the user will be allowed to pick any date; this can be changed by using the {@link #minDate},
   9  * {@link #maxDate}, {@link #disabledDays}, {@link #disabledDatesRE}, and/or {@link #disabledDates} configs.
  10  *
  11  * All the string values documented below may be overridden by including an Ext locale file in your page.
  12  *
  13  *     @example
  14  *     Ext.create('Ext.panel.Panel', {
  15  *         title: 'Choose a future date:',
  16  *          200,
  17  *         bodyPadding: 10,
  18  *         renderTo: Ext.getBody(),
  19  *         items: [{
  20  *             xtype: 'datepicker',
  21  *             minDate: new Date(),
  22  *             handler: function(picker, date) {
  23  *                 // do something with the selected date
  24  *             }
  25  *         }]
  26  *     });
  27  */
  28 Ext.define('Ext.ux.DateTimePicker', {
  29     extend: 'Ext.Component',
  30     alias: 'widget.datetimepicker',
  31     alternateClassName: 'Ext.DateTimePicker',
  32     requires: [
  33         'Ext.XTemplate',
  34         'Ext.button.Button',
  35         'Ext.button.Split',
  36         'Ext.util.ClickRepeater',
  37         'Ext.util.KeyNav',
  38         'Ext.fx.Manager',
  39         'Ext.picker.Month'
  40     ],
  41     
  42     //<locale>
  43     /**
  44      * @cfg {String} todayText
  45      * The text to display on the button that selects the current date
  46      */
  47     todayText: 'Today',
  48     //添加确定按钮
  49     okText:'Ok',
  50     okTip:'Ok',
  51     //</locale>
  52     
  53     //<locale>
  54     /**
  55      * @cfg {String} ariaTitle
  56      * The text to display for the aria title
  57      */
  58     ariaTitle: 'Date Picker: {0}',
  59     //</locale>
  60     
  61     //<locale>
  62     /**
  63      * @cfg {String} ariaTitleDateFormat
  64      * The date format to display for the current value in the {@link #ariaTitle}
  65      */
  66     ariaTitleDateFormat: 'F d',
  67     //</locale>
  68 
  69     /**
  70      * @cfg {Function} handler
  71      * Optional. A function that will handle the select event of this picker. The handler is passed the following
  72      * parameters:
  73      *
  74      *   - `picker` : Ext.picker.Date
  75      *
  76      * This Date picker.
  77      *
  78      *   - `date` : Date
  79      *
  80      * The selected date.
  81      */
  82 
  83     /**
  84      * @cfg {Object} scope
  85      * The scope (`this` reference) in which the `{@link #handler}` function will be called.
  86      *
  87      * Defaults to this DatePicker instance.
  88      */
  89 
  90     //<locale>
  91     /**
  92      * @cfg {String} todayTip
  93      * A string used to format the message for displaying in a tooltip over the button that selects the current date.
  94      * The `{0}` token in string is replaced by today's date.
  95      */
  96     todayTip: '{0} (Spacebar)',
  97     //</locale>
  98 
  99     //<locale>
 100     /**
 101      * @cfg {String} minText
 102      * The error text to display if the minDate validation fails.
 103      */
 104     minText: 'This date is before the minimum date',
 105     //</locale>
 106     
 107     //<locale>
 108     /**
 109      * @cfg {String} ariaMinText The text that will be announced by Assistive Technologies
 110      * such as screen readers when user is navigating to the cell which date is less than
 111      * {@link #minDate}.
 112      */
 113     ariaMinText: "This date is before the minimum date",
 114     //</locale>
 115 
 116     //<locale>
 117     /**
 118      * @cfg {String} maxText
 119      * The error text to display if the maxDate validation fails.
 120      */
 121     maxText: 'This date is after the maximum date',
 122     //</locale>
 123     
 124     //<locale>
 125     /**
 126      * @cfg {String} ariaMaxText The text that will be announced by Assistive Technologies
 127      * such as screen readers when user is navigating to the cell which date is later than
 128      * {@link #maxDate}.
 129      */
 130     ariaMaxText: "This date is after the maximum date",
 131     //</locale>
 132 
 133     /**
 134      * @cfg {String} format
 135      * The default date format string which can be overriden for localization support. The format must be valid
 136      * according to {@link Ext.Date#parse} (defaults to {@link Ext.Date#defaultFormat}).
 137      */
 138 
 139     //<locale>
 140     /**
 141      * @cfg {String} disabledDaysText
 142      * The tooltip to display when the date falls on a disabled day.
 143      */
 144     disabledDaysText: 'Disabled',
 145     //</locale>
 146     
 147     //<locale>
 148     /**
 149      * @cfg {String} ariaDisabledDaysText The text that Assistive Technologies such as screen readers
 150      * will announce when the date falls on a disabled day of week.
 151      */
 152     ariaDisabledDaysText: "This day of week is disabled",
 153     //</locale>
 154     
 155     //<locale>
 156     /**
 157      * @cfg {String} disabledDatesText
 158      * The tooltip text to display when the date falls on a disabled date.
 159      */
 160     disabledDatesText: 'Disabled',
 161     //</locale>
 162 
 163     //<locale>
 164     /**
 165      * @cfg {String} ariaDisabledDatesText The text that Assistive Technologies such as screen readers
 166      * will announce when the date falls on a disabled date.
 167      */
 168     ariaDisabledDatesText: "This date is disabled",
 169     
 170     //</locale>
 171     /**
 172      * @cfg {String[]} monthNames
 173      * An array of textual month names which can be overriden for localization support (defaults to Ext.Date.monthNames)
 174      * @deprecated This config is deprecated. In future the month names will be retrieved from {@link Ext.Date}
 175      */
 176 
 177     /**
 178      * @cfg {String[]} dayNames
 179      * An array of textual day names which can be overriden for localization support (defaults to Ext.Date.dayNames)
 180      * @deprecated This config is deprecated. In future the day names will be retrieved from {@link Ext.Date}
 181      */
 182 
 183     //<locale>
 184     /**
 185      * @cfg {String} nextText
 186      * The next month navigation button tooltip
 187      */
 188     nextText: 'Next Month (Control+Right)',
 189     //</locale>
 190 
 191     //<locale>
 192     /**
 193      * @cfg {String} prevText
 194      * The previous month navigation button tooltip
 195      */
 196     prevText: 'Previous Month (Control+Left)',
 197     //</locale>
 198 
 199     //<locale>
 200     /**
 201      * @cfg {String} monthYearText
 202      * The header month selector tooltip
 203      */
 204     monthYearText: 'Choose a month (Control+Up/Down to move years)',
 205     //</locale>
 206     
 207     //<locale>
 208     /**
 209      * @cfg {String} monthYearFormat
 210      * The date format for the header month
 211      */
 212     monthYearFormat: 'F Y',
 213     //</locale>
 214 
 215     //<locale>
 216     /**
 217      * @cfg {Number} [startDay=undefined]
 218      * Day index at which the week should begin, 0-based.
 219      *
 220      * Defaults to `0` (Sunday).
 221      */
 222     startDay: 0,
 223     //</locale>
 224 
 225     //<locale>
 226     /**
 227      * @cfg {Boolean} showToday
 228      * False to hide the footer area containing the Today button and disable the keyboard handler for spacebar that
 229      * selects the current date.
 230      */
 231     showToday: true,
 232     //</locale>
 233 
 234     /**
 235      * @cfg {Date} [minDate=null]
 236      * Minimum allowable date (JavaScript date object)
 237      */
 238 
 239     /**
 240      * @cfg {Date} [maxDate=null]
 241      * Maximum allowable date (JavaScript date object)
 242      */
 243 
 244     /**
 245      * @cfg {Number[]} [disabledDays=null]
 246      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday.
 247      */
 248 
 249     /**
 250      * @cfg {RegExp} [disabledDatesRE=null]
 251      * JavaScript regular expression used to disable a pattern of dates. The {@link #disabledDates}
 252      * config will generate this regex internally, but if you specify disabledDatesRE it will take precedence over the
 253      * disabledDates value.
 254      */
 255 
 256     /**
 257      * @cfg {String[]} disabledDates
 258      * An array of 'dates' to disable, as strings. These strings will be used to build a dynamic regular expression so
 259      * they are very powerful. Some examples:
 260      *
 261      *   - ['03/08/2003', '09/16/2003'] would disable those exact dates
 262      *   - ['03/08', '09/16'] would disable those days for every year
 263      *   - ['^03/08'] would only match the beginning (useful if you are using short years)
 264      *   - ['03/../2006'] would disable every day in March 2006
 265      *   - ['^03'] would disable every day in every March
 266      *
 267      * Note that the format of the dates included in the array should exactly match the {@link #format} config. In order
 268      * to support regular expressions, if you are using a date format that has '.' in it, you will have to escape the
 269      * dot when restricting dates. For example: ['03\.08\.03'].
 270      */
 271 
 272     /**
 273      * @cfg {Boolean} disableAnim
 274      * True to disable animations when showing the month picker.
 275      */
 276     disableAnim: false,
 277 
 278     /**
 279      * @cfg {String} [baseCls='x-datepicker']
 280      * The base CSS class to apply to this components element.
 281      */
 282     baseCls: Ext.baseCSSPrefix + 'datepicker',
 283 
 284     /**
 285      * @cfg {String} [selectedCls='x-datepicker-selected']
 286      * The class to apply to the selected cell.
 287      */
 288 
 289     /**
 290      * @cfg {String} [disabledCellCls='x-datepicker-disabled']
 291      * The class to apply to disabled cells.
 292      */
 293 
 294     //<locale>
 295     /**
 296      * @cfg {String} longDayFormat
 297      * The format for displaying a date in a longer format.
 298      */
 299     longDayFormat: 'F d, Y',
 300     //</locale>
 301 
 302     /**
 303      * @cfg {Object} keyNavConfig
 304      * Specifies optional custom key event handlers for the {@link Ext.util.KeyNav} attached to this date picker. Must
 305      * conform to the config format recognized by the {@link Ext.util.KeyNav} constructor. Handlers specified in this
 306      * object will replace default handlers of the same name.
 307      */
 308 
 309     /**
 310      * @cfg {String}
 311      * The {@link Ext.button.Button#ui} to use for the date picker's footer buttons.
 312      */
 313     footerButtonUI: 'default',
 314 
 315     isDatePicker: true,
 316     
 317     ariaRole: 'region',
 318     focusable: true,
 319 
 320     childEls: [
 321         'innerEl', 'eventEl', 'prevEl', 'nextEl', 'middleBtnEl', 'footerEl'
 322     ],
 323     
 324     border: true,
 325     
 326     /**
 327      * @cfg
 328      * @inheritdoc
 329      */
 330     renderTpl: [
 331         '<div id="{id}-innerEl" data-ref="innerEl" role="presentation">',
 332             '<div class="{baseCls}-header">',
 333                 '<div id="{id}-prevEl" data-ref="prevEl" class="{baseCls}-prev {baseCls}-arrow" role="presentation" title="{prevText}"></div>',
 334                 '<div id="{id}-middleBtnEl" data-ref="middleBtnEl" class="{baseCls}-month" role="heading">{%this.renderMonthBtn(values, out)%}</div>',
 335                 '<div id="{id}-nextEl" data-ref="nextEl" class="{baseCls}-next {baseCls}-arrow" role="presentation" title="{nextText}"></div>',
 336             '</div>',
 337             '<table role="grid" id="{id}-eventEl" data-ref="eventEl" class="{baseCls}-inner" cellspacing="0" tabindex="0">',
 338                 '<thead>',
 339                     '<tr role="row">',
 340                         '<tpl for="dayNames">',
 341                             '<th role="columnheader" class="{parent.baseCls}-column-header" aria-label="{.}">',
 342                                 '<div role="presentation" class="{parent.baseCls}-column-header-inner">{.:this.firstInitial}</div>',
 343                             '</th>',
 344                         '</tpl>',
 345                     '</tr>',
 346                 '</thead>',
 347                 '<tbody>',
 348                     '<tr role="row">',
 349                         '<tpl for="days">',
 350                             '{#:this.isEndOfWeek}',
 351                             '<td role="gridcell">',
 352                                 '<div hidefocus="on" class="{parent.baseCls}-date"></div>',
 353                             '</td>',
 354                         '</tpl>',
 355                     '</tr>',
 356                 '</tbody>',
 357             '</table>',
 358             '<tpl if="showToday">',
 359                 //'<div id="{id}-footerEl" data-ref="footerEl" role="presentation" class="{baseCls}-footer">{%this.renderTodayBtn(values, out)%}</div>',
 360               '<div id="{id}-footerEl" role="presentation" style="background-color:#D9E5F3;border-top:0px solid #99BCE8;">{%this.renderHour(values, out)%}{%this.renderMinute(values, out)%}{%this.renderSecond(values, out)%}<center>{%this.renderOkQueDingBtn(values, out)%}&nbsp;&nbsp;{%this.renderTodayBtn(values, out)%}</center></div>',
 361             '</tpl>',
 362             // These elements are used with Assistive Technologies such as screen readers
 363             '<div id="{id}-todayText" class="' + Ext.baseCSSPrefix + 'hidden-clip">{todayText}.</div>',
 364             '<div id="{id}-todayText" class="' + Ext.baseCSSPrefix + 'hidden-clip">{okText}.</div>',
 365             '<div id="{id}-ariaMinText" class="' + Ext.baseCSSPrefix + 'hidden-clip">{ariaMinText}.</div>',
 366             '<div id="{id}-ariaMaxText" class="' + Ext.baseCSSPrefix + 'hidden-clip">{ariaMaxText}.</div>',
 367             '<div id="{id}-ariaDisabledDaysText" class="' + Ext.baseCSSPrefix + 'hidden-clip">{ariaDisabledDaysText}.</div>',
 368             '<div id="{id}-ariaDisabledDatesText" class="' + Ext.baseCSSPrefix + 'hidden-clip">{ariaDisabledDatesText}.</div>',
 369         '</div>',
 370         {
 371             firstInitial: function(value) {
 372                 return Ext.picker.Date.prototype.getDayInitial(value);
 373             },
 374             isEndOfWeek: function(value) {
 375                 // convert from 1 based index to 0 based
 376                 // by decrementing value once.
 377                 value--;
 378                 var end = value % 7 === 0 && value !== 0;
 379                 return end ? '</tr><tr role="row">' : '';
 380             },
 381             longDay: function(value){
 382                 return Ext.Date.format(value, this.longDayFormat);
 383             },
 384             renderHour: function(values, out) {
 385                 out.push('<font  style="float : left;">&nbsp&nbsp</font>');
 386                 Ext.DomHelper.generateMarkup(values.$comp.hour.getRenderTree(), out);
 387             },
 388             renderMinute: function(values, out) {
 389                 out.push('<font  style="float : left;font-weight:bold;">&nbsp:&nbsp&nbsp</font>');
 390                 Ext.DomHelper.generateMarkup(values.$comp.minute.getRenderTree(), out);
 391             },
 392             renderSecond: function(values, out) {
 393                 out.push('<font style="float : left;font-weight:bold;">&nbsp:&nbsp&nbsp</font>');
 394                 Ext.DomHelper.generateMarkup(values.$comp.second.getRenderTree(), out);
 395             },
 396             renderOkQueDingBtn: function(values, out) {  
 397                 Ext.DomHelper.generateMarkup(values.$comp.okQueDingBtn.getRenderTree(), out);  
 398             },
 399             renderTodayBtn: function(values, out) {
 400                 Ext.DomHelper.generateMarkup(values.$comp.todayBtn.getRenderTree(), out);
 401             },
 402             renderMonthBtn: function(values, out) {
 403                 Ext.DomHelper.generateMarkup(values.$comp.monthBtn.getRenderTree(), out);
 404             }
 405         }
 406     ],
 407 
 408     // Default value used to initialise each date in the DatePicker.
 409     // __Note:__ 12 noon was chosen because it steers well clear of all DST timezone changes.
 410     initHour: 12, // 24-hour format
 411 
 412     numDays: 42,
 413 
 414     /**
 415      * @event select
 416      * Fires when a date is selected
 417      * @param {Ext.picker.Date} this DatePicker
 418      * @param {Date} date The selected date
 419      */
 420 
 421     /**
 422      * @private
 423      * @inheritdoc
 424      */
 425     initComponent: function() {
 426         var me = this,
 427             clearTime = Ext.Date.clearTime;
 428 
 429         me.selectedCls = me.baseCls + '-selected';
 430         me.disabledCellCls = me.baseCls + '-disabled';
 431         me.prevCls = me.baseCls + '-prevday';
 432         me.activeCls = me.baseCls + '-active';
 433         me.cellCls = me.baseCls + '-cell';
 434         me.nextCls = me.baseCls + '-prevday';
 435         me.todayCls = me.baseCls + '-today';
 436         
 437         
 438         if (!me.format) {
 439             me.format = Ext.Date.defaultFormat;
 440         }
 441         if (!me.dayNames) {
 442             me.dayNames = Ext.Date.dayNames;
 443         }
 444         me.dayNames = me.dayNames.slice(me.startDay).concat(me.dayNames.slice(0, me.startDay));
 445 
 446         me.callParent();
 447 
 448         me.value = me.value ? clearTime(me.value, true) : clearTime(new Date());
 449 
 450         me.initDisabledDays();
 451     },
 452 
 453     // Keep the tree structure correct for Ext.form.field.Picker input fields which poke a 'pickerField' reference down into their pop-up pickers.
 454     getRefOwner: function() {
 455         return this.pickerField || this.callParent();
 456     },
 457 
 458     getRefItems: function() {
 459         var results = [],
 460             monthBtn = this.monthBtn,
 461             todayBtn = this.todayBtn;
 462 
 463         if (monthBtn) {
 464             results.push(monthBtn);
 465         }
 466 
 467         if (todayBtn) {
 468             results.push(todayBtn);
 469         }
 470 
 471         return results;
 472     },
 473 
 474     beforeRender: function() {
 475         /*
 476          * days array for looping through 6 full weeks (6 weeks * 7 days)
 477          * Note that we explicitly force the size here so the template creates
 478          * all the appropriate cells.
 479          */
 480         var me = this,
 481             encode = Ext.String.htmlEncode,
 482             days = new Array(me.numDays),
 483             today = Ext.Date.format(new Date(), me.format);
 484 
 485         if (me.padding && !me.width) {
 486             me.cacheWidth();
 487         }
 488 
 489         me.monthBtn = new Ext.button.Split({
 490             ownerCt: me,
 491             ownerLayout: me.getComponentLayout(),
 492             text: '',
 493             tooltip: me.monthYearText,
 494             tabIndex: -1,
 495             ariaRole: 'presentation',
 496             listeners: {
 497                 click: me.doShowMonthPicker,
 498                 arrowclick: me.doShowMonthPicker,
 499                 scope: me
 500             }
 501         });
 502 
 503         me.hour = Ext.create('Ext.form.field.Number', {
 504             scope: me,
 505             ownerCt: me,
 506             editable : true,
 507             ownerLayout: me.getComponentLayout(),
 508             minValue: 0,
 509             maxValue: 23,
 510              70,
 511             style : {float:"left"},
 512             enableKeyEvents: true,
 513             listeners: {
 514                  keyup: function(field, e){
 515                      if (field.getValue() > 23){
 516                          e.stopEvent();
 517                          field.setValue(23);
 518                      }
 519                  }
 520              }
 521         });
 522         
 523         me.minute = Ext.create('Ext.form.field.Number', {
 524             scope: me,
 525             ownerCt: me,
 526             style : {float:"left"},
 527             ownerLayout: me.getComponentLayout(),
 528             minValue: 0,
 529             maxValue: 59,
 530             editable : true,
 531              70,
 532             enableKeyEvents: true,
 533             listeners: {
 534                 keyup: function(field, e){
 535                     if (field.getValue() > 59){
 536                         e.stopEvent();
 537                         field.setValue(59);
 538                     }
 539                 }
 540             }
 541         });
 542         
 543       me.second = Ext.create('Ext.form.field.Number', {
 544             scope: me,
 545             ownerCt: me,
 546             editable : true,
 547             style : {float:"left"},
 548             ownerLayout: me.getComponentLayout(),
 549             minValue: 0,
 550             maxValue: 59,
 551              70,
 552             enableKeyEvents: true,
 553             listeners: {
 554                 keyup: function(field, e){
 555                     if (field.getValue() > 59){
 556                         e.stopEvent();
 557                         field.setValue(59);
 558                     }
 559                 }
 560             }
 561         });
 562        me.okQueDingBtn = new Ext.button.Button({  
 563             ownerCt: me,  
 564             ownerLayout: me.getComponentLayout(),  
 565             text: me.okText,  
 566             tooltip: me.okTip,  
 567             tooltipType:'title',  
 568             handler:me.okQueDingHandler,//确认按钮的事件委托  
 569             scope: me  
 570         });  
 571         if (me.showToday) {
 572             me.todayBtn = new Ext.button.Button({
 573                 ui: me.footerButtonUI,
 574                 ownerCt: me,
 575                 ownerLayout: me.getComponentLayout(),
 576                 text: Ext.String.format(me.todayText, today),
 577                 tooltip: Ext.String.format(me.todayTip, today),
 578                 tooltipType: 'title',
 579                 tabIndex: -1,
 580                 ariaRole: 'presentation',
 581                 handler: me.selectToday,
 582                 scope: me
 583             });
 584         }
 585 
 586         me.callParent();
 587 
 588         Ext.applyIf(me, {
 589             renderData: {}
 590         });
 591 
 592         Ext.apply(me.renderData, {
 593             dayNames: me.dayNames,
 594             showToday: me.showToday,
 595             prevText: encode(me.prevText),
 596             nextText: encode(me.nextText),
 597             todayText: encode(me.todayText),
 598             ariaMinText: encode(me.ariaMinText),
 599             ariaMaxText: encode(me.ariaMaxText),
 600             ariaDisabledDaysText: encode(me.ariaDisabledDaysText),
 601             ariaDisabledDatesText: encode(me.ariaDisabledDatesText),
 602             days: days
 603         });
 604 
 605         me.protoEl.unselectable();
 606     },
 607 
 608     cacheWidth: function() {
 609         var me = this,
 610             padding = me.parseBox(me.padding),
 611             widthEl = Ext.getBody().createChild({
 612                 cls: me.baseCls + ' ' + me.borderBoxCls,
 613                 style: 'position:absolute;top:-1000px;left:-1000px;'
 614             });
 615 
 616         me.self.prototype.width = widthEl.getWidth() + padding.left + padding.right;
 617         widthEl.destroy();
 618     },
 619 
 620     /**
 621      * @inheritdoc
 622      * @private
 623      */
 624     onRender: function(container, position) {
 625         var me = this;
 626 
 627         me.callParent(arguments);
 628 
 629         me.cells = me.eventEl.select('tbody td');
 630         me.textNodes = me.eventEl.query('tbody td div');
 631         
 632         me.eventEl.set({ 'aria-labelledby': me.monthBtn.id });
 633 
 634         me.mon(me.eventEl, {
 635             scope: me,
 636             mousewheel: me.handleMouseWheel,
 637             click: {
 638                 fn: me.handleDateClick,
 639                 delegate: 'div.' + me.baseCls + '-date'
 640             }
 641         });
 642         
 643     },
 644 
 645     /**
 646      * @inheritdoc
 647      * @private
 648      */
 649     initEvents: function(){
 650         var me = this,
 651             pickerField = me.pickerField,
 652             eDate = Ext.Date,
 653             day = eDate.DAY;
 654 
 655         me.callParent();
 656 
 657         // If we're part of a date field, don't allow us to focus, the field will
 658         // handle that. If we are standalone, then allow the default behaviour
 659         // to occur to receive focus
 660         if (pickerField) {
 661             me.el.on('mousedown', me.onMouseDown, me);
 662         }
 663 
 664         me.prevRepeater = new Ext.util.ClickRepeater(me.prevEl, {
 665             handler: me.showPrevMonth,
 666             scope: me,
 667             preventDefault: true,
 668             stopDefault: true
 669         });
 670 
 671         me.nextRepeater = new Ext.util.ClickRepeater(me.nextEl, {
 672             handler: me.showNextMonth,
 673             scope: me,
 674             preventDefault: true,
 675             stopDefault: true
 676         });
 677 
 678         me.keyNav = new Ext.util.KeyNav(me.eventEl, Ext.apply({
 679             scope: me,
 680 
 681             left: function(e) {
 682                 if (e.ctrlKey) {
 683                     me.showPrevMonth();
 684                 } else {
 685                     me.update(eDate.add(me.activeDate, day, -1));
 686                 }
 687             },
 688 
 689             right: function(e){
 690                 if (e.ctrlKey) {
 691                     me.showNextMonth();
 692                 } else {
 693                     me.update(eDate.add(me.activeDate, day, 1));
 694                 }
 695             },
 696 
 697             up: function(e) {
 698                 if (e.ctrlKey) {
 699                     me.showNextYear();
 700                 } else {
 701                     me.update(eDate.add(me.activeDate, day, -7));
 702                 }
 703             },
 704 
 705             down: function(e) {
 706                 if (e.ctrlKey) {
 707                     me.showPrevYear();
 708                 } else {
 709                     me.update(eDate.add(me.activeDate, day, 7));
 710                 }
 711             },
 712 
 713             pageUp: function(e) {
 714                 if (e.ctrlKey) {
 715                     me.showPrevYear();
 716                 } else {
 717                     me.showPrevMonth();
 718                 }
 719             },
 720 
 721             pageDown: function(e) {
 722                 if (e.ctrlKey) {
 723                     me.showNextYear();
 724                 } else {
 725                     me.showNextMonth();
 726                 }
 727             },
 728 
 729             tab: function(e) {
 730                 // When the picker is floating and attached to an input field, its
 731                 // 'select' handler will focus the inputEl so when navigation happens
 732                 // it does so as if the input field was focused all the time.
 733                 // This is the desired behavior and we try not to interfere with it
 734                 // in the picker itself, see below.
 735                 me.handleTabClick(e);
 736                 
 737                 // Allow default behaviour of TAB - it MUST be allowed to navigate.
 738                 return true;
 739             },
 740 
 741             enter: function(e) {
 742                 me.handleDateClick(e, me.activeCell.firstChild);
 743             },
 744 
 745             space: function() {
 746                 me.setValue(new Date(me.activeCell.firstChild.dateValue));
 747                 var startValue = me.startValue,
 748                     value = me.value,
 749                     pickerValue;
 750 
 751                 if (pickerField) {
 752                     pickerValue = pickerField.getValue();
 753                     if (pickerValue && startValue && pickerValue.getTime() === value.getTime()) {
 754                         pickerField.setValue(startValue);
 755                     } else {
 756                         pickerField.setValue(value);
 757                     }
 758                 }
 759             },
 760 
 761             home: function(e) {
 762                 me.update(eDate.getFirstDateOfMonth(me.activeDate));
 763             },
 764 
 765             end: function(e) {
 766                 me.update(eDate.getLastDateOfMonth(me.activeDate));
 767             }
 768         }, me.keyNavConfig));
 769 
 770         if (me.disabled) {
 771             me.syncDisabled(true);
 772         }
 773         me.update(me.value);
 774     },
 775 
 776     onMouseDown: function(e) {
 777         e.preventDefault();
 778     },
 779 
 780     handleTabClick: function (e) {
 781         var me = this,
 782             t = me.getSelectedDate(me.activeDate),
 783             handler = me.handler;
 784 
 785         // The following code is like handleDateClick without the e.stopEvent()
 786         if (!me.disabled && t.dateValue && !Ext.fly(t.parentNode).hasCls(me.disabledCellCls)) {
 787             me.setValue(new Date(t.dateValue));
 788             me.fireEvent('select', me, me.value);
 789             if (handler) {
 790                 handler.call(me.scope || me, me, me.value);
 791             }
 792             me.onSelect();
 793         }
 794     },
 795 
 796     getSelectedDate: function (date) {
 797         var me = this,
 798             t = date.getTime(),
 799             cells = me.cells,
 800             cls = me.selectedCls,
 801             cellItems = cells.elements,
 802             cLen = cellItems.length,
 803             cell, c;
 804 
 805         cells.removeCls(cls);
 806 
 807         for (c = 0; c < cLen; c++) {
 808             cell = cellItems[c].firstChild;
 809             if (cell.dateValue === t) {
 810                 return cell;
 811             }
 812         }
 813         return null;
 814     },
 815 
 816     /**
 817      * Setup the disabled dates regex based on config options
 818      * @private
 819      */
 820     initDisabledDays: function() {
 821         var me = this,
 822             dd = me.disabledDates,
 823             re = '(?:',
 824             len,
 825             d, dLen, dI;
 826 
 827         if(!me.disabledDatesRE && dd){
 828                 len = dd.length - 1;
 829 
 830             dLen = dd.length;
 831 
 832             for (d = 0; d < dLen; d++) {
 833                 dI = dd[d];
 834 
 835                 re += Ext.isDate(dI) ? '^' + Ext.String.escapeRegex(Ext.Date.dateFormat(dI, me.format)) + '$' : dI;
 836                 if (d !== len) {
 837                     re += '|';
 838                 }
 839             }
 840 
 841             me.disabledDatesRE = new RegExp(re + ')');
 842         }
 843     },
 844 
 845     /**
 846      * Replaces any existing disabled dates with new values and refreshes the DatePicker.
 847      * @param {String[]/RegExp} disabledDates An array of date strings (see the {@link #disabledDates} config for
 848      * details on supported values), or a JavaScript regular expression used to disable a pattern of dates.
 849      * @return {Ext.picker.Date} this
 850      */
 851     setDisabledDates: function(dd) {
 852         var me = this;
 853 
 854         if (Ext.isArray(dd)) {
 855             me.disabledDates = dd;
 856             me.disabledDatesRE = null;
 857         } else {
 858             me.disabledDatesRE = dd;
 859         }
 860         me.initDisabledDays();
 861         me.update(me.value, true);
 862         return me;
 863     },
 864 
 865     /**
 866      * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker.
 867      * @param {Number[]} disabledDays An array of disabled day indexes. See the {@link #disabledDays} config for details
 868      * on supported values.
 869      * @return {Ext.picker.Date} this
 870      */
 871     setDisabledDays: function(dd) {
 872         this.disabledDays = dd;
 873         return this.update(this.value, true);
 874     },
 875 
 876     /**
 877      * Replaces any existing {@link #minDate} with the new value and refreshes the DatePicker.
 878      * @param {Date} value The minimum date that can be selected
 879      * @return {Ext.picker.Date} this
 880      */
 881     setMinDate: function(dt) {
 882         this.minDate = dt;
 883         return this.update(this.value, true);
 884     },
 885 
 886     /**
 887      * Replaces any existing {@link #maxDate} with the new value and refreshes the DatePicker.
 888      * @param {Date} value The maximum date that can be selected
 889      * @return {Ext.picker.Date} this
 890      */
 891     setMaxDate: function(dt) {
 892         this.maxDate = dt;
 893         return this.update(this.value, true);
 894     },
 895 
 896     /**
 897      * Sets the value of the date field
 898      * @param {Date} value The date to set
 899      * @return {Ext.picker.Date} this
 900      */
 901     setValue: function(value) {
 902         // If passed a null value just pass in a new date object.
 903         this.value = Ext.Date.clearTime(value || new Date(), true);
 904         return this.update(this.value);
 905     },
 906 
 907     /**
 908      * Gets the current selected value of the date field
 909      * @return {Date} The selected date
 910      */
 911     getValue: function() {
 912         return this.value;
 913     },
 914 
 915     //<locale type="function">
 916     /**
 917      * Gets a single character to represent the day of the week
 918      * @return {String} The character
 919      */
 920     getDayInitial: function(value) {
 921         return value.substr(0,1);
 922     },
 923     //</locale>
 924 
 925     /**
 926      * @inheritdoc
 927      * @private
 928      */
 929     onEnable: function() {
 930         var me = this;
 931 
 932         me.callParent();
 933         me.syncDisabled(false);
 934         me.update(me.activeDate);
 935 
 936     },
 937 
 938     /**
 939      * @inheritdoc
 940      * @private
 941      */
 942     onShow: function() {
 943         var me = this;
 944 
 945         me.callParent();
 946         me.syncDisabled(false);
 947         if (me.pickerField) {
 948             me.startValue = me.pickerField.getValue();
 949         }
 950     },
 951     
 952     /**
 953      * @inheritdoc
 954      * @private
 955      */
 956     onHide: function() {
 957         this.callParent();
 958         this.syncDisabled(true);
 959     },
 960 
 961     /**
 962      * @inheritdoc
 963      * @private
 964      */
 965     onDisable: function() {
 966         this.callParent();
 967         this.syncDisabled(true);
 968     },
 969 
 970     /**
 971      * Get the current active date.
 972      * @private
 973      * @return {Date} The active date
 974      */
 975     getActive: function(){
 976         return this.activeDate || this.value;
 977     },
 978 
 979     /**
 980      * Run any animation required to hide/show the month picker.
 981      * @private
 982      * @param {Boolean} isHide True if it's a hide operation
 983      */
 984     runAnimation: function(isHide){
 985         var picker = this.monthPicker,
 986             options = {
 987                 duration: 200,
 988                 callback: function() {
 989                     picker.setVisible(!isHide);
 990                 }
 991             };
 992 
 993         if (isHide) {
 994             picker.el.slideOut('t', options);
 995         } else {
 996             picker.el.slideIn('t', options);
 997         }
 998     },
 999 
1000     /**
1001      * Hides the month picker, if it's visible.
1002      * @param {Boolean} [animate] Indicates whether to animate this action. If the animate
1003      * parameter is not specified, the behavior will use {@link #disableAnim} to determine
1004      * whether to animate or not.
1005      * @return {Ext.picker.Date} this
1006      */
1007     hideMonthPicker: function(animate){
1008         var me = this,
1009             picker = me.monthPicker;
1010 
1011         if (picker && picker.isVisible()) {
1012             if (me.shouldAnimate(animate)) {
1013                 me.runAnimation(true);
1014             } else {
1015                 picker.hide();
1016             }
1017         }
1018         return me;
1019     },
1020     
1021     doShowMonthPicker: function(){
1022         // Wrap in an extra call so we can prevent the button
1023         // being passed as an animation parameter.
1024         this.showMonthPicker();
1025     },
1026     
1027     doHideMonthPicker: function() {
1028         // Wrap in an extra call so we can prevent this
1029         // being passed as an animation parameter
1030         this.hideMonthPicker();
1031     },
1032 
1033     /**
1034      * Show the month picker
1035      * @param {Boolean} [animate] Indicates whether to animate this action. If the animate
1036      * parameter is not specified, the behavior will use {@link #disableAnim} to determine
1037      * whether to animate or not.
1038      * @return {Ext.picker.Date} this
1039      */
1040     showMonthPicker: function(animate) {
1041         var me = this,
1042             el = me.el,
1043             picker;
1044         
1045         if (me.rendered && !me.disabled) {
1046             picker = me.createMonthPicker();            
1047             if (!picker.isVisible()) {
1048                 picker.setValue(me.getActive());
1049                 picker.setSize(el.getSize());
1050 
1051                 // Null out floatParent so that the [-1, -1] position is not made relative to this
1052                 picker.floatParent = null;
1053                 picker.setPosition(-el.getBorderWidth('l'), -el.getBorderWidth('t'));
1054                 if (me.shouldAnimate(animate)) {
1055                     me.runAnimation(false);
1056                 } else {
1057                     picker.show();
1058                 }
1059             }
1060         }
1061         return me;
1062     },
1063     
1064     /**
1065      * Checks whether a hide/show action should animate
1066      * @private
1067      * @param {Boolean} [animate] A possible animation value
1068      * @return {Boolean} Whether to animate the action
1069      */
1070     shouldAnimate: function(animate) {
1071         return Ext.isDefined(animate) ? animate : !this.disableAnim;
1072     },
1073 
1074     /**
1075      * Create the month picker instance
1076      * @private
1077      * @return {Ext.picker.Month} picker
1078      */
1079     createMonthPicker: function() {
1080         var me = this,
1081             picker = me.monthPicker;
1082 
1083         if (!picker) {
1084             me.monthPicker = picker = new Ext.picker.Month({
1085                 renderTo: me.el,
1086                 // We need to set the ownerCmp so that owns() can correctly
1087                 // match up the component hierarchy so that focus does not leave
1088                 // an owning picker field if/when this gets focus.
1089                 ownerCmp: me,
1090                 floating: true,
1091                 padding: me.padding,
1092                 shadow: false,
1093                 small: me.showToday === false,
1094                 footerButtonUI: me.footerButtonUI,
1095                 listeners: {
1096                     scope: me,
1097                     cancelclick: me.onCancelClick,
1098                     okclick: me.onOkClick,
1099                     yeardblclick: me.onOkClick,
1100                     monthdblclick: me.onOkClick
1101                 }
1102             });
1103             if (!me.disableAnim) {
1104                 // hide the element if we're animating to prevent an initial flicker
1105                 picker.el.setStyle('display', 'none');
1106             }
1107             picker.hide();
1108             me.on('beforehide', me.doHideMonthPicker, me);
1109         }
1110         return picker;
1111     },
1112 
1113     /**
1114      * Respond to an ok click on the month picker
1115      * @private
1116      */
1117     onOkClick: function(picker, value) {
1118         var me = this,
1119             month = value[0],
1120             year = value[1],
1121             date = new Date(year, month, me.getActive().getDate());
1122 
1123         if (date.getMonth() !== month) {
1124             // 'fix' the JS rolling date conversion if needed
1125             date = Ext.Date.getLastDateOfMonth(new Date(year, month, 1));
1126         }
1127         me.setValue(date);
1128         me.hideMonthPicker();
1129     },
1130 
1131     /**
1132      * Respond to a cancel click on the month picker
1133      * @private
1134      */
1135     onCancelClick: function() {
1136         this.selectedUpdate(this.activeDate);
1137         this.hideMonthPicker();
1138     },
1139 
1140     /**
1141      * Show the previous month.
1142      * @param {Object} e
1143      * @return {Ext.picker.Date} this
1144      */
1145     showPrevMonth: function(e) {
1146         return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.MONTH, -1));
1147     },
1148 
1149     /**
1150      * Show the next month.
1151      * @param {Object} e
1152      * @return {Ext.picker.Date} this
1153      */
1154     showNextMonth: function(e) {
1155         return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.MONTH, 1));
1156     },
1157 
1158     /**
1159      * Show the previous year.
1160      * @return {Ext.picker.Date} this
1161      */
1162     showPrevYear: function() {
1163         return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.YEAR, -1));
1164     },
1165 
1166     /**
1167      * Show the next year.
1168      * @return {Ext.picker.Date} this
1169      */
1170     showNextYear: function() {
1171         return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.YEAR, 1));
1172     },
1173 
1174     /**
1175      * Respond to the mouse wheel event
1176      * @private
1177      * @param {Ext.event.Event} e
1178      */
1179     handleMouseWheel: function(e) {
1180         e.stopEvent();
1181         if(!this.disabled){
1182             var delta = e.getWheelDelta();
1183             if(delta > 0){
1184                 this.showPrevMonth();
1185             } else if(delta < 0){
1186                 this.showNextMonth();
1187             }
1188         }
1189     },
1190 
1191     /**
1192      * Respond to a date being clicked in the picker
1193      * @private
1194      * @param {Ext.event.Event} e
1195      * @param {HTMLElement} t
1196      */
1197     handleDateClick: function(e, t) {
1198         var me = this,
1199             handler = me.handler;
1200 
1201         e.stopEvent();
1202         if(!me.disabled && t.dateValue && !Ext.fly(t.parentNode).hasCls(me.disabledCellCls)){
1203             me.setValue(new Date(t.dateValue));
1204             me.fireEvent('select', me, me.value);
1205             if (handler) {
1206                 handler.call(me.scope || me, me, me.value);
1207             }
1208             // event handling is turned off on hide
1209             // when we are using the picker in a field
1210             // therefore onSelect comes AFTER the select
1211             // event.
1212             me.onSelect();
1213         }
1214     },
1215 
1216     /**
1217      * Perform any post-select actions
1218      * @private
1219      */
1220     onSelect: function() {
1221         if (this.hideOnSelect) {
1222              this.hide();
1223          }
1224     },
1225 
1226     /**
1227      * Sets the current value to today.
1228      * @return {Ext.picker.Date} this
1229      */
1230     selectToday: function() {
1231         var me = this,
1232             btn = me.todayBtn,
1233             handler = me.handler;
1234 
1235         if (btn && !btn.disabled) {
1236             me.setValue(Ext.Date.clearTime(new Date()));
1237             me.fireEvent('select', me, me.value);
1238             if (handler) {
1239                 handler.call(me.scope || me, me, me.value);
1240             }
1241             me.onSelect();
1242         }
1243         return me;
1244     },
1245 
1246     /**
1247      * Update the selected cell
1248      * @private
1249      * @param {Date} date The new date
1250      */
1251     selectedUpdate: function(date) {
1252         var me        = this,
1253             t         = date.getTime(),
1254             cells     = me.cells,
1255             cls       = me.selectedCls,
1256             c,
1257             cLen      = cells.getCount(),
1258             cell;
1259         
1260         me.eventEl.dom.setAttribute('aria-busy', 'true');
1261         
1262         cell = me.activeCell;
1263         
1264         if (cell) {
1265             Ext.fly(cell).removeCls(cls);
1266             cell.setAttribute('aria-selected', false);
1267         }
1268 
1269         for (c = 0; c < cLen; c++) {
1270             cell = cells.item(c);
1271 
1272             if (me.textNodes[c].dateValue === t) {
1273                 me.activeCell = cell.dom;
1274                 me.eventEl.dom.setAttribute('aria-activedescendant', cell.dom.id);
1275                 cell.dom.setAttribute('aria-selected', true);
1276                 cell.addCls(cls);
1277                 me.fireEvent('highlightitem', me, cell);
1278                 break;
1279             }
1280         }
1281         
1282         me.eventEl.dom.removeAttribute('aria-busy');
1283     },
1284 
1285     /**
1286      * Update the contents of the picker for a new month
1287      * @private
1288      * @param {Date} date The new date
1289      */
1290     fullUpdate: function(date) {
1291         var me = this,
1292             cells = me.cells.elements,
1293             textNodes = me.textNodes,
1294             disabledCls = me.disabledCellCls,
1295             eDate = Ext.Date,
1296             i = 0,
1297             extraDays = 0,
1298             newDate = +eDate.clearTime(date, true),
1299             today = +eDate.clearTime(new Date()),
1300             min = me.minDate ? eDate.clearTime(me.minDate, true) : Number.NEGATIVE_INFINITY,
1301             max = me.maxDate ? eDate.clearTime(me.maxDate, true) : Number.POSITIVE_INFINITY,
1302             ddMatch = me.disabledDatesRE,
1303             ddText = me.disabledDatesText,
1304             ddays = me.disabledDays ? me.disabledDays.join('') : false,
1305             ddaysText = me.disabledDaysText,
1306             format = me.format,
1307             days = eDate.getDaysInMonth(date),
1308             firstOfMonth = eDate.getFirstDateOfMonth(date),
1309             startingPos = firstOfMonth.getDay() - me.startDay,
1310             previousMonth = eDate.add(date, eDate.MONTH, -1),
1311             ariaTitleDateFormat = me.ariaTitleDateFormat,
1312             prevStart, current, disableToday, tempDate, setCellClass, html, cls,
1313             formatValue, value;
1314 
1315         if (startingPos < 0) {
1316             startingPos += 7;
1317         }
1318 
1319         days += startingPos;
1320         prevStart = eDate.getDaysInMonth(previousMonth) - startingPos;
1321         current = new Date(previousMonth.getFullYear(), previousMonth.getMonth(), prevStart, me.initHour);
1322 
1323         if (me.showToday) {
1324             tempDate = eDate.clearTime(new Date());
1325             disableToday = (tempDate < min || tempDate > max ||
1326                 (ddMatch && format && ddMatch.test(eDate.dateFormat(tempDate, format))) ||
1327                 (ddays && ddays.indexOf(tempDate.getDay()) !== -1));
1328 
1329             if (!me.disabled) {
1330                 me.todayBtn.setDisabled(disableToday);
1331             }
1332         }
1333 
1334         setCellClass = function(cellIndex, cls){
1335             var cell = cells[cellIndex],
1336                 describedBy = [];
1337             
1338             // Cells are not rendered with ids
1339             if (!cell.hasAttribute('id')) {
1340                 cell.setAttribute('id', me.id + '-cell-' + cellIndex);
1341             }
1342             
1343             // store dateValue number as an expando
1344             value = +eDate.clearTime(current, true);
1345             cell.firstChild.dateValue = value;
1346             
1347             cell.setAttribute('aria-label', eDate.format(current, ariaTitleDateFormat));
1348             
1349             // Here and below we can't use title attribute instead of data-qtip
1350             // because JAWS will announce title value before cell content
1351             // which is not what we need. Also we are using aria-describedby attribute
1352             // and not placing the text in aria-label because some cells may have
1353             // compound descriptions (like Today and Disabled day).
1354             cell.removeAttribute('aria-describedby');
1355             cell.removeAttribute('data-qtip');
1356             
1357             if (value === today) {
1358                 cls += ' ' + me.todayCls;
1359                 describedBy.push(me.id + '-todayText');
1360             }
1361             
1362             if (value === newDate) {
1363                 me.activeCell = cell;
1364                 me.eventEl.dom.setAttribute('aria-activedescendant', cell.id);
1365                 cell.setAttribute('aria-selected', true);
1366                 cls += ' ' + me.selectedCls;
1367                 me.fireEvent('highlightitem', me, cell);
1368             }
1369             else {
1370                 cell.setAttribute('aria-selected', false);
1371             }
1372 
1373             if (value < min) {
1374                 cls += ' ' + disabledCls;
1375                 describedBy.push(me.id + '-ariaMinText');
1376                 cell.setAttribute('data-qtip', me.minText);
1377             }
1378             else if (value > max) {
1379                 cls += ' ' + disabledCls;
1380                 describedBy.push(me.id + '-ariaMaxText');
1381                 cell.setAttribute('data-qtip', me.maxText);
1382             }
1383             else if (ddays && ddays.indexOf(current.getDay()) !== -1){
1384                 cell.setAttribute('data-qtip', ddaysText);
1385                 describedBy.push(me.id + '-ariaDisabledDaysText');
1386                 cls += ' ' + disabledCls;
1387             }
1388             else if (ddMatch && format){
1389                 formatValue = eDate.dateFormat(current, format);
1390                 if(ddMatch.test(formatValue)){
1391                     cell.setAttribute('data-qtip', ddText.replace('%0', formatValue));
1392                     describedBy.push(me.id + '-ariaDisabledDatesText');
1393                     cls += ' ' + disabledCls;
1394                 }
1395             }
1396             
1397             if (describedBy.length) {
1398                 cell.setAttribute('aria-describedby', describedBy.join(' '));
1399             }
1400             
1401             cell.className = cls + ' ' + me.cellCls;
1402         };
1403         
1404         me.eventEl.dom.setAttribute('aria-busy', 'true');
1405 
1406         for (; i < me.numDays; ++i) {
1407             if (i < startingPos) {
1408                 html = (++prevStart);
1409                 cls = me.prevCls;
1410             } else if (i >= days) {
1411                 html = (++extraDays);
1412                 cls = me.nextCls;
1413             } else {
1414                 html = i - startingPos + 1;
1415                 cls = me.activeCls;
1416             }
1417             textNodes[i].innerHTML = html;
1418             current.setDate(current.getDate() + 1);
1419             setCellClass(i, cls);
1420         }
1421         
1422         me.eventEl.dom.removeAttribute('aria-busy');
1423 
1424         me.monthBtn.setText(Ext.Date.format(date, me.monthYearFormat));
1425     },
1426 
1427     /**
1428      * Update the contents of the picker
1429      * @private
1430      * @param {Date} date The new date
1431      * @param {Boolean} forceRefresh True to force a full refresh
1432      */
1433     update: function(date, forceRefresh) {
1434         var me = this,
1435             active = me.activeDate;
1436         //添加时间相关
1437         date.setHours(me.hour.getValue());
1438         date.setMinutes(me.minute.getValue());
1439         date.setSeconds(me.second.getValue());
1440         
1441         if (me.rendered) {
1442             me.activeDate = date;
1443             if (!forceRefresh && active && me.el &&
1444                     active.getMonth() === date.getMonth() &&
1445                     active.getFullYear() === date.getFullYear()) {
1446                 me.selectedUpdate(date, active);
1447             } else {
1448                 me.fullUpdate(date, active);
1449             }
1450         }
1451         return me;
1452     },
1453    /** 
1454      * 确认 按钮触发的调用 
1455      */  
1456     okQueDingHandler : function(){  
1457         var me = this,  
1458             btn = me.okQueDingBtn;  
1459   
1460         if(btn && !btn.disabled){  
1461             me.setValue(this.getValue());  
1462             me.fireEvent('select', me, me.value);  
1463             me.onSelect();  
1464         }  
1465         return me;  
1466     },  
1467     /**
1468      * @private
1469      * @inheritdoc
1470      */
1471     beforeDestroy: function() {
1472         var me = this;
1473 
1474         if (me.rendered) {
1475             Ext.destroy(
1476                 me.keyNav,
1477                 me.monthPicker,
1478                 me.monthBtn,
1479                 me.nextRepeater,
1480                 me.prevRepeater,
1481                 me.todayBtn,
1482                 me.okQueDingBtn,
1483                 me.todayElSpan
1484             );
1485             delete me.textNodes;
1486             delete me.cells.elements;
1487         }
1488         me.callParent();
1489     },
1490 
1491     privates: {
1492         // Do the job of a container layout at this point even though we are not a Container.
1493         // TODO: Refactor as a Container.
1494         finishRenderChildren: function () {
1495             var me = this;
1496 
1497             me.callParent();
1498             me.monthBtn.finishRender();
1499             me.okQueDingBtn.finishRender();  
1500             if (me.showToday) {
1501                 me.todayBtn.finishRender();
1502             }
1503             //添加时间相关
1504             this.hour.finishRender();
1505             this.minute.finishRender();
1506             this.second.finishRender();
1507         },
1508         
1509         getFocusEl: function() {
1510             return this.eventEl;
1511         },
1512 
1513         /**
1514          * Set the disabled state of various internal components
1515          * @param {Boolean} disabled
1516          * @private
1517          */
1518         syncDisabled: function (disabled) {
1519             var me = this,
1520                 keyNav = me.keyNav;
1521 
1522             // If we have one, we have all
1523             if (keyNav) {
1524                 keyNav.setDisabled(disabled);
1525                 me.prevRepeater.setDisabled(disabled);
1526                 me.nextRepeater.setDisabled(disabled);
1527                 if (me.todayBtn) {
1528                     me.todayBtn.setDisabled(disabled);
1529                 }
1530             }
1531         }
1532     }
1533 });

 11、下面则是扩展出来的一个DeteTimeField控件

 完整的DeteTimeField.js代码

  1 /**
  2  * @docauthor Jason Johnston <jason@sencha.com>
  3  *
  4  * Provides a date input field with a {@link Ext.picker.Date date picker} dropdown and automatic date
  5  * validation.
  6  *
  7  * This field recognizes and uses the JavaScript Date object as its main {@link #value} type. In addition,
  8  * it recognizes string values which are parsed according to the {@link #format} and/or {@link #altFormats}
  9  * configs. These may be reconfigured to use date formats appropriate for the user's locale.
 10  *
 11  * The field may be limited to a certain range of dates by using the {@link #minValue}, {@link #maxValue},
 12  * {@link #disabledDays}, and {@link #disabledDates} config parameters. These configurations will be used both
 13  * in the field's validation, and in the date picker dropdown by preventing invalid dates from being selected.
 14  *
 15  * # Example usage
 16  *
 17  *     @example
 18  *     Ext.create('Ext.form.Panel', {
 19  *         renderTo: Ext.getBody(),
 20  *          300,
 21  *         bodyPadding: 10,
 22  *         title: 'Dates',
 23  *         items: [{
 24  *             xtype: 'datefield',
 25  *             anchor: '100%',
 26  *             fieldLabel: 'From',
 27  *             name: 'from_date',
 28  *             maxValue: new Date()  // limited to the current date or prior
 29  *         }, {
 30  *             xtype: 'datefield',
 31  *             anchor: '100%',
 32  *             fieldLabel: 'To',
 33  *             name: 'to_date',
 34  *             value: new Date()  // defaults to today
 35  *         }]
 36  *     });
 37  *
 38  * # Date Formats Examples
 39  *
 40  * This example shows a couple of different date format parsing scenarios. Both use custom date format
 41  * configurations; the first one matches the configured `format` while the second matches the `altFormats`.
 42  *
 43  *     @example
 44  *     Ext.create('Ext.form.Panel', {
 45  *         renderTo: Ext.getBody(),
 46  *          300,
 47  *         bodyPadding: 10,
 48  *         title: 'Dates',
 49  *         items: [{
 50  *             xtype: 'datefield',
 51  *             anchor: '100%',
 52  *             fieldLabel: 'Date',
 53  *             name: 'date',
 54  *             // The value matches the format; will be parsed and displayed using that format.
 55  *             format: 'm d Y',
 56  *             value: '2 4 1978'
 57  *         }, {
 58  *             xtype: 'datefield',
 59  *             anchor: '100%',
 60  *             fieldLabel: 'Date',
 61  *             name: 'date',
 62  *             // The value does not match the format, but does match an altFormat; will be parsed
 63  *             // using the altFormat and displayed using the format.
 64  *             format: 'm d Y',
 65  *             altFormats: 'm,d,Y|m.d.Y',
 66  *             value: '2.4.1978'
 67  *         }]
 68  *     });
 69  */
 70 Ext.define('Ext.ux.DateTimeField', {
 71     extend:'Ext.form.field.Picker',
 72     alias: 'widget.datetimefield',
 73     requires: ['Ext.ux.DateTimePicker'],
 74 
 75     //<locale>
 76     /**
 77      * @cfg {String} format
 78      * The default date format string which can be overriden for localization support. The format must be valid
 79      * according to {@link Ext.Date#parse}.
 80      */
 81     format : "m/d/Y",
 82     //</locale>
 83     //<locale>
 84     /**
 85      * @cfg {String} altFormats
 86      * Multiple date formats separated by "|" to try when parsing a user input value and it does not match the defined
 87      * format.
 88      */
 89     altFormats : "m/d/Y|n/j/Y|n/j/y|m/j/y|n/d/y|m/j/Y|n/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d|Y-m-d|n-j|n/j",
 90     //</locale>
 91     //<locale>
 92     /**
 93      * @cfg {String} disabledDaysText
 94      * The tooltip to display when the date falls on a disabled day.
 95      */
 96     disabledDaysText : "Disabled",
 97     //</locale>
 98     //<locale>
 99     /**
100      * @cfg {String} disabledDatesText
101      * The tooltip text to display when the date falls on a disabled date.
102      */
103     disabledDatesText : "Disabled",
104     //</locale>
105     //<locale>
106     /**
107      * @cfg {String} minText
108      * The error text to display when the date in the cell is before {@link #minValue}.
109      */
110     minText : "The date in this field must be equal to or after {0}",
111     //</locale>
112     //<locale>
113     /**
114      * @cfg {String} maxText
115      * The error text to display when the date in the cell is after {@link #maxValue}.
116      */
117     maxText : "The date in this field must be equal to or before {0}",
118     //</locale>
119     //<locale>
120     /**
121      * @cfg {String} invalidText
122      * The error text to display when the date in the field is invalid.
123      */
124     invalidText : "{0} is not a valid date - it must be in the format {1}",
125     //</locale>
126     /**
127      * @cfg {String} [triggerCls='x-form-date-trigger']
128      * An additional CSS class used to style the trigger button. The trigger will always get the class 'x-form-trigger'
129      * and triggerCls will be **appended** if specified (default class displays a calendar icon).
130      */
131     triggerCls : Ext.baseCSSPrefix + 'form-date-trigger',
132     /**
133      * @cfg {Boolean} showToday
134      * false to hide the footer area of the Date picker containing the Today button and disable the keyboard handler for
135      * spacebar that selects the current date.
136      */
137     showToday : true,
138     /**
139      * @cfg {Date/String} minValue
140      * The minimum allowed date. Can be either a Javascript date object or a string date in a valid format.
141      */
142     /**
143      * @cfg {Date/String} maxValue
144      * The maximum allowed date. Can be either a Javascript date object or a string date in a valid format.
145      */
146     /**
147      * @cfg {Number[]} disabledDays
148      * An array of days to disable, 0 based. Some examples:
149      *
150      *     // disable Sunday and Saturday:
151      *     disabledDays:  [0, 6]
152      *     // disable weekdays:
153      *     disabledDays: [1,2,3,4,5]
154      */
155     /**
156      * @cfg {String[]} disabledDates
157      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular expression so
158      * they are very powerful. Some examples:
159      *
160      *     // disable these exact dates:
161      *     disabledDates: ["03/08/2003", "09/16/2003"]
162      *     // disable these days for every year:
163      *     disabledDates: ["03/08", "09/16"]
164      *     // only match the beginning (useful if you are using short years):
165      *     disabledDates: ["^03/08"]
166      *     // disable every day in March 2006:
167      *     disabledDates: ["03/../2006"]
168      *     // disable every day in every March:
169      *     disabledDates: ["^03"]
170      *
171      * Note that the format of the dates included in the array should exactly match the {@link #format} config. In order
172      * to support regular expressions, if you are using a {@link #format date format} that has "." in it, you will have
173      * to escape the dot when restricting dates. For example: `["03\.08\.03"]`.
174      */
175 
176     /**
177      * @cfg {String} submitFormat
178      * The date format string which will be submitted to the server. The format must be valid according to
179      * {@link Ext.Date#parse}.
180      *
181      * Defaults to {@link #format}.
182      */
183 
184     /**
185      * @cfg {Boolean} useStrict
186      * True to enforce strict date parsing to prevent the default Javascript "date rollover".
187      * Defaults to the useStrict parameter set on Ext.Date
188      * See {@link Ext.Date#parse}.
189      */
190     useStrict: undefined,
191 
192     // in the absence of a time value, a default value of 12 noon will be used
193     // (note: 12 noon was chosen because it steers well clear of all DST timezone changes)
194     initTime: '12', // 24 hour format
195 
196     initTimeFormat: 'H',
197 
198     matchFieldWidth: false,
199     //<locale>
200     /**
201      * @cfg {Number} [startDay=undefined]
202      * Day index at which the week should begin, 0-based.
203      *
204      * Defaults to `0` (Sunday).
205      */
206     startDay: 0,
207     //</locale>
208 
209     /**
210      * @inheritdoc
211      */
212     valuePublishEvent: ['select', 'blur'],
213 
214     initComponent : function(){
215         var me = this,
216             isString = Ext.isString,
217             min, max;
218 
219         min = me.minValue;
220         max = me.maxValue;
221         if(isString(min)){
222             me.minValue = me.parseDate(min);
223         }
224         if(isString(max)){
225             me.maxValue = me.parseDate(max);
226         }
227         me.disabledDatesRE = null;
228         me.initDisabledDays();
229 
230         me.callParent();
231     },
232 
233     initValue: function() {
234         var me = this,
235             value = me.value;
236 
237         // If a String value was supplied, try to convert it to a proper Date
238         if (Ext.isString(value)) {
239             me.value = me.rawToValue(value);
240         }
241 
242         me.callParent();
243     },
244 
245     // private
246     initDisabledDays : function(){
247         if(this.disabledDates){
248             var dd   = this.disabledDates,
249                 len  = dd.length - 1,
250                 re   = "(?:",
251                 d,
252                 dLen = dd.length,
253                 date;
254 
255             for (d = 0; d < dLen; d++) {
256                 date = dd[d];
257 
258                 re += Ext.isDate(date) ? '^' + Ext.String.escapeRegex(date.dateFormat(this.format)) + '$' : date;
259                 if (d !== len) {
260                     re += '|';
261                 }
262             }
263 
264             this.disabledDatesRE = new RegExp(re + ')');
265         }
266     },
267 
268     /**
269      * Replaces any existing disabled dates with new values and refreshes the Date picker.
270      * @param {String[]} disabledDates An array of date strings (see the {@link #disabledDates} config for details on
271      * supported values) used to disable a pattern of dates.
272      */
273     setDisabledDates : function(disabledDates){
274         var me = this,
275             picker = me.picker;
276 
277         me.disabledDates = disabledDates;
278         me.initDisabledDays();
279         if (picker) {
280             picker.setDisabledDates(me.disabledDatesRE);
281         }
282     },
283 
284     /**
285      * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the Date picker.
286      * @param {Number[]} disabledDays An array of disabled day indexes. See the {@link #disabledDays} config for details on
287      * supported values.
288      */
289     setDisabledDays : function(disabledDays){
290         var picker = this.picker;
291 
292         this.disabledDays = disabledDays;
293         if (picker) {
294             picker.setDisabledDays(disabledDays);
295         }
296     },
297 
298     /**
299      * Replaces any existing {@link #minValue} with the new value and refreshes the Date picker.
300      * @param {Date} value The minimum date that can be selected
301      */
302     setMinValue : function(value){
303         var me = this,
304             picker = me.picker,
305             minValue = (Ext.isString(value) ? me.parseDate(value) : value);
306 
307         me.minValue = minValue;
308         if (picker) {
309             picker.minText = Ext.String.format(me.minText, me.formatDate(me.minValue));
310             picker.setMinDate(minValue);
311         }
312     },
313 
314     /**
315      * Replaces any existing {@link #maxValue} with the new value and refreshes the Date picker.
316      * @param {Date} value The maximum date that can be selected
317      */
318     setMaxValue : function(value){
319         var me = this,
320             picker = me.picker,
321             maxValue = (Ext.isString(value) ? me.parseDate(value) : value);
322 
323         me.maxValue = maxValue;
324         if (picker) {
325             picker.maxText = Ext.String.format(me.maxText, me.formatDate(me.maxValue));
326             picker.setMaxDate(maxValue);
327         }
328     },
329 
330     /**
331      * Runs all of Date's validations and returns an array of any errors. Note that this first runs Text's validations,
332      * so the returned array is an amalgamation of all field errors. The additional validation checks are testing that
333      * the date format is valid, that the chosen date is within the min and max date constraints set, that the date
334      * chosen is not in the disabledDates regex and that the day chosed is not one of the disabledDays.
335      * @param {Object} [value] The value to get errors for (defaults to the current field value)
336      * @return {String[]} All validation errors for this field
337      */
338     getErrors: function(value) {
339         value = arguments.length > 0 ? value : this.formatDate(this.processRawValue(this.getRawValue()));
340 
341         var me = this,
342             format = Ext.String.format,
343             clearTime = Ext.Date.clearTime,
344             errors = me.callParent([value]),
345             disabledDays = me.disabledDays,
346             disabledDatesRE = me.disabledDatesRE,
347             minValue = me.minValue,
348             maxValue = me.maxValue,
349             len = disabledDays ? disabledDays.length : 0,
350             i = 0,
351             svalue,
352             fvalue,
353             day,
354             time;
355 
356         
357 
358         if (value === null || value.length < 1) { // if it's blank and textfield didn't flag it then it's valid
359              return errors;
360         }
361 
362         svalue = value;
363         value = me.parseDate(value);
364         if (!value) {
365             errors.push(format(me.invalidText, svalue, Ext.Date.unescapeFormat(me.format)));
366             return errors;
367         }
368 
369         time = value.getTime();
370         if (minValue && time < clearTime(minValue).getTime()) {
371             errors.push(format(me.minText, me.formatDate(minValue)));
372         }
373 
374         if (maxValue && time > clearTime(maxValue).getTime()) {
375             errors.push(format(me.maxText, me.formatDate(maxValue)));
376         }
377 
378         if (disabledDays) {
379             day = value.getDay();
380 
381             for(; i < len; i++) {
382                 if (day === disabledDays[i]) {
383                     errors.push(me.disabledDaysText);
384                     break;
385                 }
386             }
387         }
388 
389         fvalue = me.formatDate(value);
390         if (disabledDatesRE && disabledDatesRE.test(fvalue)) {
391             errors.push(format(me.disabledDatesText, fvalue));
392         }
393 
394         return errors;
395     },
396 
397     rawToValue: function(rawValue) {
398         return this.parseDate(rawValue) || rawValue || null;
399     },
400 
401     valueToRaw: function(value) {
402         return this.formatDate(this.parseDate(value));
403     },
404 
405     /**
406      * @method setValue
407      * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid date,
408      * using {@link #format} as the date format, according to the same rules as {@link Ext.Date#parse} (the default
409      * format used is "m/d/Y").
410      *
411      * Usage:
412      *
413      *     //All of these calls set the same date value (May 4, 2006)
414      *
415      *     //Pass a date object:
416      *     var dt = new Date('5/4/2006');
417      *     dateField.setValue(dt);
418      *
419      *     //Pass a date string (default format):
420      *     dateField.setValue('05/04/2006');
421      *
422      *     //Pass a date string (custom format):
423      *     dateField.format = 'Y-m-d';
424      *     dateField.setValue('2006-05-04');
425      *
426      * @param {String/Date} date The date or valid date string
427      * @return {Ext.form.field.Date} this
428      */
429 
430     /**
431      * Attempts to parse a given string value using a given {@link Ext.Date#parse date format}.
432      * @param {String} value The value to attempt to parse
433      * @param {String} format A valid date format (see {@link Ext.Date#parse})
434      * @return {Date} The parsed Date object, or null if the value could not be successfully parsed.
435      */
436     safeParse : function(value, format) {
437         var me = this,
438             utilDate = Ext.Date,
439             result = null,
440             strict = me.useStrict,
441             parsedDate;
442 
443         if (utilDate.formatContainsHourInfo(format)) {
444             // if parse format contains hour information, no DST adjustment is necessary
445             result = utilDate.parse(value, format, strict);
446         } else {
447             // set time to 12 noon, then clear the time
448             parsedDate = utilDate.parse(value + ' ' + me.initTime, format + ' ' + me.initTimeFormat, strict);
449             if (parsedDate) {
450                 result = utilDate.clearTime(parsedDate);
451             }
452         }
453         return result;
454     },
455 
456     // @private
457     getSubmitValue: function() {
458         var format = this.submitFormat || this.format,
459             value = this.getValue();
460 
461         return value ? Ext.Date.format(value, format) : '';
462     },
463 
464     /**
465      * @private
466      */
467     parseDate : function(value) {
468         if(!value || Ext.isDate(value)){
469             return value;
470         }
471 
472         var me = this,
473             val = me.safeParse(value, me.format),
474             altFormats = me.altFormats,
475             altFormatsArray = me.altFormatsArray,
476             i = 0,
477             len;
478 
479         if (!val && altFormats) {
480             altFormatsArray = altFormatsArray || altFormats.split('|');
481             len = altFormatsArray.length;
482             for (; i < len && !val; ++i) {
483                 val = me.safeParse(value, altFormatsArray[i]);
484             }
485         }
486         return val;
487     },
488 
489     // private
490     formatDate: function(date){
491         return Ext.isDate(date) ? Ext.Date.dateFormat(date, this.format) : date;
492     },
493 
494     createPicker: function() {
495         var me = this,
496             format = Ext.String.format;
497 
498         // Create floating Picker BoundList. It will acquire a floatParent by looking up
499         // its ancestor hierarchy (Pickers use their pickerField property as an upward link)
500         // for a floating component.
501         return new Ext.ux.DateTimePicker({
502             pickerField: me,
503             floating: true,
504             focusable: false, // Key events are listened from the input field which is never blurred
505             hidden: true,
506             minDate: me.minValue,
507             maxDate: me.maxValue,
508             disabledDatesRE: me.disabledDatesRE,
509             disabledDatesText: me.disabledDatesText,
510             disabledDays: me.disabledDays,
511             disabledDaysText: me.disabledDaysText,
512             format: me.format,
513             showToday: me.showToday,
514             startDay: me.startDay,
515             minText: format(me.minText, me.formatDate(me.minValue)),
516             maxText: format(me.maxText, me.formatDate(me.maxValue)),
517             listeners: {
518                 scope: me,
519                 select: me.onSelect
520             },
521             keyNavConfig: {
522                 esc: function() {
523                     me.collapse();
524                 }
525             }
526         });
527     },
528 
529     onSelect: function(m, d) {
530         var me = this;
531 
532         me.setValue(d);
533         me.fireEvent('select', me, d);
534         me.collapse();
535     },
536 
537     /**
538      * @private
539      * Sets the Date picker's value to match the current field value when expanding.
540      */
541     onExpand: function() {
542         var me = this,
543             value = me.getValue() instanceof Date ? me.getValue() : new Date();
544         me.picker.setValue(value);     
545         me.picker.hour.setValue(value.getHours());
546         me.picker.minute.setValue(value.getMinutes());
547         me.picker.second.setValue(value.getSeconds());
548     },
549 
550     // private
551     onBlur: function(e) {
552         var me = this,
553             v = me.rawToValue(me.getRawValue());
554 
555         if (Ext.isDate(v)) {
556             me.setValue(v);
557         }
558         me.callParent([e]);
559     }
560 
561     /**
562      * @cfg {Boolean} grow
563      * @private
564      */
565     /**
566      * @cfg {Number} growMin
567      * @private
568      */
569     /**
570      * @cfg {Number} growMax
571      * @private
572      */
573     /**
574      * @method autoSize
575      * @private
576      */
577 });

 PS:写这篇文章也是为了方便以后自己用,也方便一下使用EXTJS的同道,为了生命远离EXTJS...

参考文章:http://www.cnblogs.com/linxiong945/p/3977445.html

              http://toutiao.com/a4669125293/

              http://gogo1217.iteye.com/blog/1856265

              http://express.ruanko.com/ruanko-express_69/tech-overnight1.html

              http://blog.csdn.net/oscar999/article/details/9984679

原文地址:https://www.cnblogs.com/ganqiyin/p/5029633.html