ext中的date控件,只能支持年月日的选择,而其自带的time控件不是很符合我们要求,所以来扩展其date控件,让其支持小时分钟的选择.

JS

  1DatetimePicker = function(config){
  2    DatetimePicker.superclass.constructor.call(this, config);
  3}
;
  4
  5Ext.extend(DatetimePicker, Ext.DatePicker, {
  6
  7    selectToday : function(){
  8        this.setValue(new Date().clearTime());
  9        var val1 = this.value;
 10        val1.setHours(this.theHours);
 11        val1.setMinutes(this.theMinutes);
 12        this.fireEvent("select"this, val1);
 13    }
,
 14    handleDateClick : function(e, t){
 15        e.stopEvent();
 16        if(t.dateValue && !Ext.fly(t.parentNode).hasClass("x-date-disabled")){
 17            this.setValue(new Date(t.dateValue));
 18            var val1 = this.value;
 19            val1.setHours(this.theHours);
 20            val1.setMinutes(this.theMinutes);
 21            this.fireEvent("select"this, val1);
 22        }

 23    }
,
 24     onRender : function(container, position){
 25        var m = [
 26             '<table cellspacing="0">',
 27                '<tr><td colspan="3"><table cellspacing="0" width="100%"><tr><td class="x-date-left"><a href="#" title="'this.prevText ,'">*</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="'this.nextText ,'">*</a></td></tr></table></td></tr>',
 28                '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
 29        var dn = this.dayNames;
 30        for(var i = 0; i < 7; i++){
 31            var d = this.startDay+i;
 32            if(d > 6){
 33                d = d-7;
 34            }

 35            m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
 36        }

 37        m[m.length] = "</tr></thead><tbody><tr>";
 38        for(i = 0; i < 42; i++{
 39            if(i % 7 === 0 && i !== 0){
 40                m[m.length] = "</tr><tr>";
 41            }

 42            m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
 43        }

 44
 45        m[m.length] = '</tr></tbody></table></td></tr><tr><td class="minutecss"><table cellspacing="0" ><tr>';
 46        m[m.length] = '<td class="y-hour-left"><a href="#" title="down"> </a></td><td class="y-hour-middle" align="center"></td><td class="y-hour-right"><a href="#" title="up"> </a></td>';
 47        m[m.length] = '<td class="y-minute-left"><a href="#" title="down"> </a></td><td class="y-minute-middle" align="center"></td><td class="y-minute-right"><a href="#" title="up"> </a></td>';
 48        m[m.length] = '</tr></table></td><td  colspan="2" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
 49
 50        var el = document.createElement("div");
 51        el.className = "x-date-picker";
 52        el.innerHTML = m.join("");
 53
 54        container.dom.insertBefore(el, position);
 55
 56        this.el = Ext.get(el);
 57        this.eventEl = Ext.get(el.firstChild);
 58
 59        new Ext.util.ClickRepeater(this.el.child("td.x-date-left a"), {
 60            handler: this.showPrevMonth,
 61            scope: this,
 62            preventDefault:true,
 63            stopDefault:true
 64        }
);
 65
 66        new Ext.util.ClickRepeater(this.el.child("td.x-date-right a"), {
 67            handler: this.showNextMonth,
 68            scope: this,
 69            preventDefault:true,
 70            stopDefault:true
 71        }
);
 72        new Ext.util.ClickRepeater(this.el.child("td.y-hour-left a"), {
 73                                                                            handler: function(){
 74                                                                                if(this.theHours>0){
 75                                                                                    this.theHours--;
 76                                                                                    this.theHours = this.theHours %24;
 77                                                                                    var txt = '';
 78                                                                                    if(this.theHours<10){
 79                                                                                        txt='0'+this.theHours;
 80                                                                                    }

 81                                                                                    else{
 82                                                                                        txt= this.theHours;
 83                                                                                    }

 84                                                                                    this.hourLabel.update(txt+'');
 85                                                                                    
 86                                                                                }

 87                                                                            }
.createDelegate(this), 
 88                                                                            scope: this
 89                                                                        }
);
 90        new Ext.util.ClickRepeater(this.el.child("td.y-hour-right a"), {
 91                                                                            handler: function(){
 92                                                                                this.theHours++;
 93                                                                                this.theHours = this.theHours % 24;
 94                                                                                var txt = '';
 95                                                                                if(this.theHours<10){
 96                                                                                    txt='0'+this.theHours;
 97                                                                                }

 98                                                                                else{
 99                                                                                    txt= this.theHours;
100                                                                                }

101                                                                                this.hourLabel.update(txt+'');
102                                                                            }
.createDelegate(this), 
103                                                                            scope: this
104                                                                        }
);
105        new Ext.util.ClickRepeater(this.el.child("td.y-minute-left a"), {
106                                                                            handler: function(){
107                                                                                if(this.theMinutes>0){
108                                                                                    this.theMinutes--;
109                                                                                    this.theMinutes = this.theMinutes % 60;
110                                                                                    var txt = '';
111                                                                                    if(this.theMinutes<10){
112                                                                                        txt='0'+this.theMinutes;
113                                                                                    }

114                                                                                    else{
115                                                                                        txt= this.theMinutes;
116                                                                                    }

117                                                                                    this.minuteLabel.update(txt+'');
118                                                                                    
119                                                                                }

120                                                                            }
.createDelegate(this), 
121                                                                            scope: this
122                                                                        }
);
123        new Ext.util.ClickRepeater(this.el.child("td.y-minute-right a"), {
124                                                                            handler: function(){
125                                                                                this.theMinutes++;
126                                                                                this.theMinutes = this.theMinutes % 60;
127                                                                                var txt = '';
128                                                                                if(this.theMinutes<10){
129                                                                                    txt='0'+this.theMinutes;
130                                                                                }

131                                                                                else{
132                                                                                    txt= this.theMinutes;
133                                                                                }
    
134                                                                                this.minuteLabel.update(txt+'');
135                                                                            }
.createDelegate(this), 
136                                                                            scope: this
137                                                                        }
);
138
139        this.eventEl.on("mousewheel"this.handleMouseWheel,  this);
140
141        this.monthPicker = this.el.down('div.x-date-mp');
142        this.monthPicker.enableDisplayMode('block');
143        
144        var kn = new Ext.KeyNav(this.eventEl, {
145            "left" : function(e){
146                e.ctrlKey ?
147                    this.showPrevMonth() :
148                    this.update(this.activeDate.add("d"-1));
149            }
,
150
151            "right" : function(e){
152                e.ctrlKey ?
153                    this.showNextMonth() :
154                    this.update(this.activeDate.add("d"1));
155            }
,
156
157            "up" : function(e){
158                e.ctrlKey ?
159                    this.showNextYear() :
160                    this.update(this.activeDate.add("d"-7));
161            }
,
162
163            "down" : function(e){
164                e.ctrlKey ?
165                    this.showPrevYear() :
166                    this.update(this.activeDate.add("d"7));
167            }
,
168
169            "pageUp" : function(e){
170                this.showNextMonth();
171            }
,
172
173            "pageDown" : function(e){
174                this.showPrevMonth();
175            }
,
176
177            "enter" : function(e){
178                e.stopPropagation();
179                return true;
180            }
,
181
182            scope : this
183        }
);
184
185        this.eventEl.on("click"this.handleDateClick,  this{delegate: "a.x-date-date"});
186
187        this.eventEl.addKeyListener(Ext.EventObject.SPACE, this.selectToday,  this);
188
189        this.el.unselectable();
190        
191        this.cells = this.el.select("table.x-date-inner tbody td");
192        this.textNodes = this.el.query("table.x-date-inner tbody span");
193
194        this.mbtn = new Ext.Button({
195            text: "*",
196            tooltip: this.monthYearText,
197            renderTo: this.el.child("td.x-date-middle"true)
198        }
);
199
200        this.mbtn.on('click'this.showMonthPicker, this);
201        this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
202
203        var dt1 = new Date();
204        var txt = '';
205        this.hourLabel = this.el.child("td.y-hour-middle");
206        this.theHours = dt1.getHours();
207        if(this.theHours<10){
208            txt='0'+this.theHours;
209        }

210        else{
211            txt= this.theHours;
212        }
    
213        this.hourLabel.update(txt+'');
214
215        this.minuteLabel = this.el.child("td.y-minute-middle");
216        this.theMinutes = dt1.getMinutes();
217        if(this.theMinutes<10){
218            txt='0'+this.theMinutes;
219        }

220        else{
221            txt= this.theMinutes;
222        }
    
223        this.minuteLabel.update(txt+'');
224
225        var today = (new Date()).dateFormat(this.format);
226        var todayBtn = new Ext.Button({
227            renderTo: this.el.child("td.x-date-bottom"true),
228            text: String.format(this.todayText, today),
229            tooltip: String.format(this.todayTip, today),
230            handler: this.selectToday,
231            scope: this
232        }
);
233        
234        if(Ext.isIE){
235            this.el.repaint();
236        }

237        this.update(this.value);
238    }
,
239
240    /**
241    * Method Name: update
242    */

243    update : function(date){
244        var vd = this.activeDate;
245        this.activeDate = date;
246        if(vd && this.el){
247            var t = date.getTime();
248            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
249                this.cells.removeClass("x-date-selected");
250                this.cells.each(function(c){
251                   if(c.dom.firstChild.dateValue == t){
252                       c.addClass("x-date-selected");
253                       setTimeout(function(){
254                            try{c.dom.firstChild.focus();}catch(e){}
255                       }
50);
256                       return false;
257                   }

258                }
);
259                return;
260            }

261        }

262        var days = date.getDaysInMonth();
263        var firstOfMonth = date.getFirstDateOfMonth();
264        var startingPos = firstOfMonth.getDay()-this.startDay;
265
266        if(startingPos <= this.startDay){
267            startingPos += 7;
268        }

269
270        var pm = date.add("mo"-1);
271        var prevStart = pm.getDaysInMonth()-startingPos;
272
273        var cells = this.cells.elements;
274        var textEls = this.textNodes;
275        days += startingPos;
276
277        // convert everything to numbers so it's fast
278        var day = 86400000;
279        var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
280        var today = new Date().clearTime().getTime();
281        var sel = date.clearTime().getTime();
282        var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
283        var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
284        var ddMatch = this.disabledDatesRE;
285        var ddText = this.disabledDatesText;
286        var ddays = this.disabledDays ? this.disabledDays.join("") : false;
287        var ddaysText = this.disabledDaysText;
288        var format = this.format;
289
290        var setCellClass = function(cal, cell){
291            cell.title = "";
292            var t = d.getTime();
293            cell.firstChild.dateValue = t;
294            if(t == today){
295                cell.className += " x-date-today";
296                cell.title = cal.todayText;
297            }

298            if(t == sel){
299                cell.className += " x-date-selected";
300                setTimeout(function(){
301                    try{cell.firstChild.focus();}catch(e){}
302                }
50);
303            }

304            // disabling
305            if(t < min) {
306                cell.className = " x-date-disabled";
307                cell.title = cal.minText;
308                return;
309            }

310            if(t > max) {
311                cell.className = " x-date-disabled";
312                cell.title = cal.maxText;
313                return;
314            }

315            if(ddays){
316                if(ddays.indexOf(d.getDay()) != -1){
317                    cell.title = ddaysText;
318                    cell.className = " x-date-disabled";
319                }

320            }

321            if(ddMatch && format){
322                var fvalue = d.dateFormat(format);
323                if(ddMatch.test(fvalue)){
324                    cell.title = ddText.replace("%0", fvalue);
325                    cell.className = " x-date-disabled";
326                }

327            }

328        }
;
329
330        var i = 0;
331        for(; i < startingPos; i++{
332            textEls[i].innerHTML = (++prevStart);
333            d.setDate(d.getDate()+1);
334            cells[i].className = "x-date-prevday";
335            setCellClass(this, cells[i]);
336        }

337        for(; i < days; i++){
338            intDay = i - startingPos + 1;
339            textEls[i].innerHTML = (intDay);
340            d.setDate(d.getDate()+1);
341            cells[i].className = "x-date-active";
342            setCellClass(this, cells[i]);
343        }

344        var extraDays = 0;
345        for(; i < 42; i++{
346             textEls[i].innerHTML = (++extraDays);
347             d.setDate(d.getDate()+1);
348             cells[i].className = "x-date-nextday";
349             setCellClass(this, cells[i]);
350        }

351
352        this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
353
354        if(this.theHours<10){
355            txt='0'+this.theHours;
356        }

357        else{
358            txt= this.theHours;
359        }

360        this.hourLabel.update(txt+'');
361
362        if(this.theMinutes<10){
363            txt='0'+this.theMinutes;
364        }

365        else{
366            txt= this.theMinutes;
367        }
    
368        this.minuteLabel.update(txt+'');
369
370        if(!this.internalRender){
371            var main = this.el.dom.firstChild;
372            var w = main.offsetWidth;
373            this.el.setWidth(w + this.el.getBorderWidth("lr"));
374            Ext.fly(main).setWidth(w);
375            this.internalRender = true;
376            // opera does not respect the auto grow header center column
377            // then, after it gets a width opera refuses to recalculate
378            // without a second pass
379            if(Ext.isOpera && !this.secondPass){
380                main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
381                this.secondPass = true;
382                this.update.defer(10this, [date]);
383            }

384        }

385    }
,
386    
387    /***** Public Instance Variables *****/
388    
389    /**
390    * Variable Name: nextYearText, prevYearText
391    * Description: Hover text for the previous year and next year arrow changers
392    * Default: as shown
393    * Type: string
394    */

395    nextYearText: 'Next Year (Control+Up)',
396    prevYearText: 'Previous Year (Control+Down)'
397}
);
398
399
400/** Class Name: DatetimeItem
401 * Inherits From: Ext.menu.Adapter
402 */

403DatetimeItem = function(config){
404    DatetimeItem.superclass.constructor.call(thisnew DatetimePicker(config), config);
405    this.picker = this.component;
406    this.addEvents({select: true});
407
408    this.picker.on("render", function(picker){
409        picker.getEl().swallowEvent("click");
410        picker.container.addClass("x-menu-date-item");
411    }
);
412
413    this.picker.on("select"this.onSelect, this);
414}
;
415
416Ext.extend(DatetimeItem, Ext.menu.Adapter, {
417    onSelect : function(picker, date){
418        this.fireEvent("select"this, date, picker);
419        DatetimeItem.superclass.handleClick.call(this);
420    }

421}
);
422
423
424/** Class Name: DatetimeMenu
425 * Inherits From: Ext.menu.Menu
426 */

427DatetimeMenu = function(config){
428    DatetimeMenu.superclass.constructor.call(this, config);
429    this.plain = true;
430    var di = new DatetimeItem(config);
431    this.add(di);
432    this.picker = di.picker;
433    this.relayEvents(di, ["select"]);
434}
;
435Ext.extend(DatetimeMenu, Ext.menu.Menu);

用法:
1new Ext.form.DateField({
2fieldLabel: 'datetime',
3name: 'begintime',
4format:'Y-m-d H:i',
5menu:new DatetimeMenu()
6}
)

效果:


说明:
1.如果选择小时,分钟的左右按钮不能正常显示,请检查css中图片路径是否正确.
2.如果只想选择年月日的话,去掉menu:new DatetimeMenu().