sencha touch datepicker/datepickerfield(时间选择控件)扩展

参考资料: https://market.sencha.com/extensions/datetimepicker

适用于2.4.1版本

uxPickerTime

使用方法参考:datepicker控件

  1 Ext.define('ux.picker.Time', {
  2     extend: 'Ext.picker.Picker',
  3     xtype: 'uxPickerTime',
  4     alternateClassName: 'uxPickerTime',
  5     requires: ['Ext.DateExtras', 'Ext.util.InputBlocker'],
  6 
  7     /**
  8     * @event change
  9     * Fired when the value of this picker has changed and the done button is pressed.
 10     * @param {Ext.picker.Date} this This Picker
 11     * @param {Date} value The date value
 12     */
 13 
 14     config: {
 15         /**
 16         * @cfg {Number} yearFrom
 17         * 开始年份,如果他比yearTo大,则选择顺序颠倒
 18         * @accessor
 19         */
 20         yearFrom: 2014,
 21 
 22         /**
 23         * @cfg {Number} [yearTo=new Date().getFullYear()]
 24         * 结束年份
 25         * @accessor
 26         */
 27         yearTo: 2000,
 28 
 29         /**
 30         * @cfg {String} monthText
 31         * 月显示值
 32         * @accessor
 33         */
 34         monthText: '月',
 35 
 36         /**
 37         * @cfg {String} dayText
 38         * 日显示值
 39         * @accessor
 40         */
 41         dayText: '日',
 42 
 43         /**
 44         * @cfg {String} yearText
 45         * 年显示值
 46         * @accessor
 47         */
 48         yearText: '年',
 49 
 50         /**
 51         * @cfg {String} hourText
 52         * 小时显示值
 53         */
 54         hourText: '时',
 55 
 56         /**
 57         * @cfg {String} minuteText
 58         * 分显示值
 59         */
 60         minuteText: '分',
 61 
 62         /**
 63         * @cfg {String} ampmText
 64         * 上午/下午显示值
 65         */
 66         ampmText: '上午/下午',
 67         /**
 68         * @cfg {Array} slotOrder
 69         * 默认的选项列表
 70         * @accessor
 71         */
 72         slotOrder: ['year', 'month', 'day', 'hour', 'minute'], //
 73 
 74         /**
 75         * @cfg {Int} 
 76         *分钟间隔
 77         * @accessor
 78         */
 79         minuteInterval: 1,
 80 
 81         /**
 82         * @cfg {Boolean} ampm
 83         *是否使用12小时制
 84         * @accessor
 85         */
 86         ampm: false,
 87         useTitles: true
 88     },
 89 
 90     platformConfig: [{
 91         theme: ['Windows'],
 92         doneButton: {
 93             iconCls: 'check2',
 94             ui: 'round',
 95             text: ''
 96         }
 97     }],
 98 
 99     initialize: function () {
100         this.callParent();
101 
102         this.on({
103             scope: this,
104             delegate: '> slot',
105             slotpick: this.onSlotPick
106         });
107 
108         this.on({
109             scope: this,
110             show: this.onSlotPick
111         });
112     },
113 
114     setValue: function (value, animated) {
115         if (Ext.isDate(value)) {
116             var ampm = 'AM',
117             currentHours = hour = value.getHours();
118             if (this.getAmpm()) {
119                 if (currentHours > 12) {
120                     ampm = "PM";
121                     hour -= 12;
122                 } else if (currentHours == 12) {
123                     ampm = "PM";
124                 } else if (currentHours == 0) {
125                     hour = 12;
126                 }
127             }
128             value = {
129                 day: value.getDate(),
130                 month: value.getMonth() + 1,
131                 year: value.getFullYear(),
132                 hour: hour,
133                 minute: value.getMinutes(),
134                 ampm: ampm
135             };
136         }
137 
138         this.callParent([value, animated]);
139         this.onSlotPick();
140     },
141     //获取值
142     getValue: function (useDom) {
143         var values = {},
144             items = this.getItems().items,
145             ln = items.length,
146             daysInMonth, day, month, year, hour, minute, item, i;
147 
148         for (i = 0; i < ln; i++) {
149             item = items[i];
150             if (item instanceof Ext.picker.Slot) {
151                 values[item.getName()] = item.getValue(useDom);
152             }
153         }
154 
155         //if all the slots return null, we should not return a date
156         if (values.year === null && values.month === null && values.day === null && values.hour === null && values.minute === null) {
157             return null;
158         }
159 
160         year = Ext.isNumber(values.year) ? values.year : 1;
161         month = Ext.isNumber(values.month) ? values.month : 1;
162         day = Ext.isNumber(values.day) ? values.day : 1;
163         hour = Ext.isNumber(values.hour) ? values.hour : 1;
164         minute = Ext.isNumber(values.minute) ? values.minute : 1;
165 
166         if (month && year && month && day) {
167             daysInMonth = this.getDaysInMonth(month, year);
168         }
169         day = (daysInMonth) ? Math.min(day, daysInMonth) : day;
170         if (values.ampm && values.ampm == "PM" && hour < 12) {
171             hour = hour + 12;
172         }
173         if (values.ampm && values.ampm == "AM" && hour == 12) {
174             hour = 0;
175         }
176         return new Date(year, month - 1, day, hour, minute);
177     },
178 
179     /**
180     * Updates the yearFrom configuration
181     */
182     updateYearFrom: function () {
183         if (this.initialized) {
184             this.createSlots();
185         }
186     },
187 
188     /**
189     * Updates the yearTo configuration
190     */
191     updateYearTo: function () {
192         if (this.initialized) {
193             this.createSlots();
194         }
195     },
196 
197     /**
198     * Updates the monthText configuration
199     */
200     updateMonthText: function (newMonthText, oldMonthText) {
201         var innerItems = this.getInnerItems,
202             ln = innerItems.length,
203             item, i;
204 
205         //loop through each of the current items and set the title on the correct slice
206         if (this.initialized) {
207             for (i = 0; i < ln; i++) {
208                 item = innerItems[i];
209 
210                 if ((typeof item.title == "string" && item.title == oldMonthText) || (item.title.html == oldMonthText)) {
211                     item.setTitle(newMonthText);
212                 }
213             }
214         }
215     },
216 
217     /**
218     * Updates the {@link #dayText} configuration.
219     */
220     updateDayText: function (newDayText, oldDayText) {
221         var innerItems = this.getInnerItems,
222             ln = innerItems.length,
223             item, i;
224 
225         //loop through each of the current items and set the title on the correct slice
226         if (this.initialized) {
227             for (i = 0; i < ln; i++) {
228                 item = innerItems[i];
229 
230                 if ((typeof item.title == "string" && item.title == oldDayText) || (item.title.html == oldDayText)) {
231                     item.setTitle(newDayText);
232                 }
233             }
234         }
235     },
236 
237     /**
238     * Updates the yearText configuration
239     */
240     updateYearText: function (yearText) {
241         var innerItems = this.getInnerItems,
242             ln = innerItems.length,
243             item, i;
244 
245         //loop through each of the current items and set the title on the correct slice
246         if (this.initialized) {
247             for (i = 0; i < ln; i++) {
248                 item = innerItems[i];
249 
250                 if (item.title == this.yearText) {
251                     item.setTitle(yearText);
252                 }
253             }
254         }
255     },
256 
257     // @private
258     constructor: function () {
259         this.callParent(arguments);
260         this.createSlots();
261     },
262 
263     /**
264     * Generates all slots for all years specified by this component, and then sets them on the component
265     * @private
266     */
267     createSlots: function () {
268         var me = this,
269             slotOrder = me.getSlotOrder(),
270             yearsFrom = me.getYearFrom(),
271             yearsTo = me.getYearTo(),
272             years = [],
273             days = [],
274             months = [],
275                hours = [],
276             minutes = [],
277             ampm = [],
278             reverse = yearsFrom > yearsTo,
279             ln, i, daysInMonth;
280 
281         if (!this.getAmpm()) {
282             var index = slotOrder.indexOf('ampm')
283             if (index >= 0) {
284                 slotOrder.splice(index);
285             }
286         }
287         //填充年列表
288         while (yearsFrom) {
289             years.push({
290                 text: yearsFrom,
291                 value: yearsFrom
292             });
293 
294             if (yearsFrom === yearsTo) {
295                 break;
296             }
297 
298             if (reverse) {
299                 yearsFrom--;
300             } else {
301                 yearsFrom++;
302             }
303         }
304         //填充天列表
305         daysInMonth = me.getDaysInMonth(1, new Date().getFullYear());
306 
307         for (i = 0; i < daysInMonth; i++) {
308             days.push({
309                 text: i + 1,
310                 value: i + 1
311             });
312         }
313         //填充月列表
314         for (i = 0, ln = Ext.Date.monthNames.length; i < ln; i++) {
315             months.push({
316                 text: Ext.Date.monthNames[i],
317                 value: i + 1
318             });
319         }
320         //填充小时列表
321         var hourLimit = (this.getAmpm()) ? 12 : 23
322         var hourStart = (this.getAmpm()) ? 1 : 0
323         for (i = hourStart; i <= hourLimit; i++) {
324             hours.push({
325                 text: this.pad2(i),
326                 value: i
327             });
328         }
329         //填充分钟列表
330         for (i = 0; i < 60; i += this.getMinuteInterval()) {
331             minutes.push({
332                 text: this.pad2(i),
333                 value: i
334             });
335         }
336         //填充上午/下午
337         ampm.push({
338             text: '上午',
339             value: 'AM'
340         }, {
341             text: '下午',
342             value: 'PM'
343         });
344 
345         var slots = [];
346 
347         slotOrder.forEach(function (item) {
348             slots.push(me.createSlot(item, days, months, years, hours, minutes, ampm));
349         });
350 
351         me.setSlots(slots);
352     },
353 
354     /**
355     * Returns a slot config for a specified date.
356     * @private
357     */
358     createSlot: function (name, days, months, years, hours, minutes, ampm) {
359         switch (name) {
360             case 'year':
361                 return {
362                     name: 'year',
363                     align: 'center',
364                     data: years,
365                     title: this.getYearText(),
366                     flex: 3
367                 };
368             case 'month':
369                 return {
370                     name: name,
371                     align: 'center',
372                     data: months,
373                     title: this.getMonthText(),
374                     flex: 3
375                 };
376             case 'day':
377                 return {
378                     name: 'day',
379                     align: 'center',
380                     data: days,
381                      '1px',
382                     title: this.getDayText(),
383                     flex: 2
384                 };
385             case 'hour':
386                 return {
387                     name: 'hour',
388                     align: 'center',
389                     data: hours,
390                     title: this.getHourText(),
391                     flex: 2
392                 };
393             case 'minute':
394                 return {
395                     name: 'minute',
396                     align: 'center',
397                     data: minutes,
398                     title: this.getMinuteText(),
399                     flex: 2
400                 };
401             case 'ampm':
402                 return {
403                     name: 'ampm',
404                     align: 'center',
405                     data: ampm,
406                     title: this.getAmpmText(),
407                     flex: 2
408                 };
409         }
410     },
411 
412     onSlotPick: function () {
413         var value = this.getValue(true),
414             slot = this.getDaySlot(),
415             year = value.getFullYear(),
416             month = value.getMonth(),
417             days = [],
418             daysInMonth, i;
419         if (!value || !Ext.isDate(value) || !slot) {
420             return;
421         }
422 
423         this.callParent(arguments);
424 
425         //get the new days of the month for this new date
426         daysInMonth = this.getDaysInMonth(month + 1, year);
427         for (i = 0; i < daysInMonth; i++) {
428             days.push({
429                 text: i + 1,
430                 value: i + 1
431             });
432         }
433         // We don't need to update the slot days unless it has changed
434         if (slot.getStore().getCount() == days.length) {
435             return;
436         }
437 
438         slot.getStore().setData(days);
439 
440         // Now we have the correct amount of days for the day slot, lets update it
441         var store = slot.getStore(),
442             viewItems = slot.getViewItems(),
443             valueField = slot.getValueField(),
444             index, item;
445 
446         index = store.find(valueField, value.getDate());
447         if (index == -1) {
448             return;
449         }
450 
451         item = Ext.get(viewItems[index]);
452 
453         slot.selectedIndex = index;
454         slot.scrollToItem(item);
455         slot.setValue(slot.getValue(true));
456 
457 
458     },
459 
460     getDaySlot: function () {
461         var innerItems = this.getInnerItems(),
462             ln = innerItems.length,
463             i, slot;
464 
465         if (this.daySlot) {
466             return this.daySlot;
467         }
468 
469         for (i = 0; i < ln; i++) {
470             slot = innerItems[i];
471             if (slot.isSlot && slot.getName() == "day") {
472                 this.daySlot = slot;
473                 return slot;
474             }
475         }
476 
477         return null;
478     },
479 
480     // @private
481     getDaysInMonth: function (month, year) {
482         var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
483         return month == 2 && this.isLeapYear(year) ? 29 : daysInMonth[month - 1];
484     },
485 
486     // @private
487     isLeapYear: function (year) {
488         return !!((year & 3) === 0 && (year % 100 || (year % 400 === 0 && year)));
489     },
490 
491     onDoneButtonTap: function () {
492         var oldValue = this._value,
493             newValue = this.getValue(true),
494             testValue = newValue;
495 
496         if (Ext.isDate(newValue)) {
497             testValue = newValue.toDateString();
498         }
499         if (Ext.isDate(oldValue)) {
500             oldValue = oldValue.toDateString();
501         }
502         if (testValue != oldValue) {
503             this.fireEvent('change', this, newValue);
504         }
505         this.hide();
506         Ext.util.InputBlocker.unblockInputs();
507     },
508     pad2: function (number) {
509         return (number < 10 ? '0' : '') + number;
510     }
511 });

uxFieldTime

使用方法参考:datepickerfield控件

  1 Ext.define('ux.field.Time', {
  2     extend: 'Ext.field.Text',
  3     alternateClassName: 'uxFieldTime',
  4     xtype: 'uxFieldTime',
  5     requires: [
  6         'ux.picker.Time',
  7         'Ext.DateExtras'
  8     ],
  9 
 10     /**
 11     * @event change
 12     * Fires when a date is selected
 13     * @param {Ext.field.DatePicker} this
 14     * @param {Date} newDate The new date
 15     * @param {Date} oldDate The old date
 16     */
 17 
 18     config: {
 19         ui: 'select',
 20 
 21         /**
 22         * @cfg {Object/ux.picker.Time} picker
 23         * An object that is used when creating the internal {@link ux.picker.Time} component or a direct instance of {@link ux.picker.Time}.
 24         * @accessor
 25         */
 26         picker: true,
 27 
 28         /**
 29         * @cfg {Boolean}
 30         * @hide
 31         * @accessor
 32         */
 33         clearIcon: false,
 34 
 35         /**
 36         * @cfg {Object/Date} value
 37         * Default value for the field and the internal {@link ux.picker.Time} component. Accepts an object of 'year',
 38         * 'month' and 'day' values, all of which should be numbers, or a {@link Date}.
 39         *
 40         * Example: {year: 1989, day: 1, month: 5} = 1st May 1989 or new Date()
 41         * @accessor
 42         */
 43 
 44 
 45         /**
 46         * @cfg {Boolean} destroyPickerOnHide
 47         * 完成选择时隐藏或者销毁该控件,默认销毁
 48         * @accessor
 49         */
 50         destroyPickerOnHide: false,
 51 
 52         /**
 53         * @cfg {String} dateFormat 默认时间格式
 54         * 接受任何有效的时间格式. 请参考 {@link Ext.Date}.
 55         */
 56         dateFormat: 'Y-m-d H:i',
 57 
 58         /**
 59         * @cfg {Object}
 60         * @hide
 61         */
 62         component: {
 63             useMask: true
 64         }
 65     },
 66 
 67     initialize: function () {
 68         var me = this,
 69             component = me.getComponent();
 70 
 71         me.callParent();
 72 
 73         component.on({
 74             scope: me,
 75             masktap: 'onMaskTap'
 76         });
 77 
 78 
 79         component.doMaskTap = Ext.emptyFn;
 80 
 81         if (Ext.browser.is.AndroidStock2) {
 82             component.input.dom.disabled = true;
 83         }
 84     },
 85 
 86     syncEmptyCls: Ext.emptyFn,
 87 
 88     applyValue: function (value) {
 89         if (!Ext.isDate(value) && !Ext.isObject(value)) {
 90             return null;
 91         }
 92 
 93         if (Ext.isObject(value)) {
 94             return new Date(value.year, value.month - 1, value.day, value.hour, value.minute);
 95         }
 96 
 97         return value;
 98     },
 99 
100     updateValue: function (newValue, oldValue) {
101         var me = this,
102             picker = me._picker;
103 
104         if (picker && picker.isPicker) {
105             picker.setValue(newValue);
106         }
107 
108         // Ext.Date.format expects a Date
109         if (newValue !== null) {
110             me.getComponent().setValue(Ext.Date.format(newValue, me.getDateFormat() || Ext.util.Format.defaultDateFormat));
111         } else {
112             me.getComponent().setValue('');
113         }
114 
115         if (newValue !== oldValue) {
116             me.fireEvent('change', me, newValue, oldValue);
117         }
118     },
119 
120     /**
121     * Updates the date format in the field.
122     * @private
123     */
124     updateDateFormat: function (newDateFormat, oldDateFormat) {
125         var value = this.getValue();
126         if (newDateFormat != oldDateFormat && Ext.isDate(value)) {
127             this.getComponent().setValue(Ext.Date.format(value, newDateFormat || Ext.util.Format.defaultDateFormat));
128         }
129     },
130 
131     /**
132     * Returns the {@link Date} value of this field.
133     * If you wanted a formated date
134     * @return {Date} The date selected
135     */
136     getValue: function () {
137         if (this._picker && this._picker instanceof ux.picker.Time) {
138             return this._picker.getValue();
139         }
140         return this._value;
141     },
142 
143     /**
144     * Returns the value of the field formatted using the specified format. If it is not specified, it will default to
145     * {@link #dateFormat} and then {@link Ext.util.Format#defaultDateFormat}.
146     * @param {String} format The format to be returned.
147     * @return {String} The formatted date.
148     */
149     getFormattedValue: function (format) {
150         var value = this.getValue();
151         return (Ext.isDate(value)) ? Ext.Date.format(value, format || this.getDateFormat() || Ext.util.Format.defaultDateFormat) : value;
152     },
153 
154     applyPicker: function (picker, pickerInstance) {
155         if (pickerInstance && pickerInstance.isPicker) {
156             picker = pickerInstance.setConfig(picker);
157         }
158 
159         return picker;
160     },
161 
162     getPicker: function () {
163         var picker = this._picker,
164             value = this.getValue();
165 
166         if (picker && !picker.isPicker) {
167             picker = Ext.factory(picker, ux.picker.Time);
168             if (value != null) {
169                 picker.setValue(value);
170             }
171         }
172 
173         picker.on({
174             scope: this,
175             change: 'onPickerChange',
176             hide: 'onPickerHide'
177         });
178 
179         this._picker = picker;
180 
181         return picker;
182     },
183 
184     /**
185     * @private
186     * Listener to the tap event of the mask element. Shows the internal DatePicker component when the button has been tapped.
187     */
188     onMaskTap: function () {
189         if (this.getDisabled()) {
190             return false;
191         }
192 
193         this.onFocus();
194 
195         return false;
196     },
197 
198     /**
199     * Called when the picker changes its value.
200     * @param {ux.picker.Time} picker The date picker.
201     * @param {Object} value The new value from the date picker.
202     * @private
203     */
204     onPickerChange: function (picker, value) {
205         var me = this,
206             oldValue = me.getValue();
207 
208         me.setValue(value);
209         me.fireEvent('select', me, value);
210         me.onChange(me, value, oldValue);
211     },
212 
213     /**
214     * Override this or change event will be fired twice. change event is fired in updateValue
215     * for this field. TOUCH-2861
216     */
217     onChange: Ext.emptyFn,
218 
219     /**
220     * Destroys the picker when it is hidden, if
221     * {@link Ext.field.DatePicker#destroyPickerOnHide destroyPickerOnHide} is set to `true`.
222     * @private
223     */
224     onPickerHide: function () {
225         var me = this,
226             picker = me.getPicker();
227 
228         if (me.getDestroyPickerOnHide() && picker) {
229             picker.destroy();
230             me._picker = me.getInitialConfig().picker || true;
231         }
232     },
233 
234     reset: function () {
235         this.setValue(this.originalValue);
236     },
237 
238     onFocus: function (e) {
239         var component = this.getComponent();
240         this.fireEvent('focus', this, e);
241 
242         if (Ext.os.is.Android4) {
243             component.input.dom.focus();
244         }
245         component.input.dom.blur();
246 
247         if (this.getReadOnly()) {
248             return false;
249         }
250 
251         this.isFocused = true;
252 
253         this.getPicker().show();
254     },
255 
256     // @private
257     destroy: function () {
258         var picker = this._picker;
259 
260         if (picker && picker.isPicker) {
261             picker.destroy();
262         }
263 
264         this.callParent(arguments);
265     }
266     //<deprecated product=touch since=2.0>
267 }, function () {
268     this.override({
269         getValue: function (format) {
270             if (format) {
271                 //<debug warn>
272                 Ext.Logger.deprecate("format argument of the getValue method is deprecated, please use getFormattedValue instead", this);
273                 //</debug>
274                 return this.getFormattedValue(format);
275             }
276             return this.callOverridden();
277         }
278     });
279 
280     /**
281     * @method getDatePicker
282     * @inheritdoc Ext.field.DatePicker#getPicker
283     * @deprecated 2.0.0 Please use #getPicker instead
284     */
285     Ext.deprecateMethod(this, 'getDatePicker', 'getPicker');
286     //</deprecated>
287 });

效果图:

只需要时、分选项如下:

1             xtype: 'uxFieldTime',
2             picker: {
3                 slotOrder: ['hour', 'minute']
4             },
5             dateFormat: 'H:i',
6             value: new Date(),
7             label: '时间:',
8             placeHolder: '请输入时间'

一些额外的css:

 1 /*#region pick */
 2 
 3 .x-picker-slot-title {
 4     height:auto;
 5     text-align:center;
 6     padding:0.2em 0;
 7 }
 8 .x-picker-slot .x-dataview-item {
 9     padding:0 8px !important;
10 }
11 .x-webkit .x-layout-box.x-horizontal > .x-layout-box-item.x-picker-slot {
12     width:auto !important;
13 }
14 /*#endregion*/
原文地址:https://www.cnblogs.com/mlzs/p/4153586.html