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

时间:2020-12-08 15:15:05

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

适用于2.4.1版本

uxPickerTime

使用方法参考:datepicker控件

 Ext.define('ux.picker.Time', {
extend: 'Ext.picker.Picker',
xtype: 'uxPickerTime',
alternateClassName: 'uxPickerTime',
requires: ['Ext.DateExtras', 'Ext.util.InputBlocker'], /**
* @event change
* Fired when the value of this picker has changed and the done button is pressed.
* @param {Ext.picker.Date} this This Picker
* @param {Date} value The date value
*/ config: {
/**
* @cfg {Number} yearFrom
* 开始年份,如果他比yearTo大,则选择顺序颠倒
* @accessor
*/
yearFrom: 2014, /**
* @cfg {Number} [yearTo=new Date().getFullYear()]
* 结束年份
* @accessor
*/
yearTo: 2000, /**
* @cfg {String} monthText
* 月显示值
* @accessor
*/
monthText: '月', /**
* @cfg {String} dayText
* 日显示值
* @accessor
*/
dayText: '日', /**
* @cfg {String} yearText
* 年显示值
* @accessor
*/
yearText: '年', /**
* @cfg {String} hourText
* 小时显示值
*/
hourText: '时', /**
* @cfg {String} minuteText
* 分显示值
*/
minuteText: '分', /**
* @cfg {String} ampmText
* 上午/下午显示值
*/
ampmText: '上午/下午',
/**
* @cfg {Array} slotOrder
* 默认的选项列表
* @accessor
*/
slotOrder: ['year', 'month', 'day', 'hour', 'minute'], // /**
* @cfg {Int}
*分钟间隔
* @accessor
*/
minuteInterval: 1, /**
* @cfg {Boolean} ampm
*是否使用12小时制
* @accessor
*/
ampm: false,
useTitles: true
}, platformConfig: [{
theme: ['Windows'],
doneButton: {
iconCls: 'check2',
ui: 'round',
text: ''
}
}], initialize: function () {
this.callParent(); this.on({
scope: this,
delegate: '> slot',
slotpick: this.onSlotPick
}); this.on({
scope: this,
show: this.onSlotPick
});
}, setValue: function (value, animated) {
if (Ext.isDate(value)) {
var ampm = 'AM',
currentHours = hour = value.getHours();
if (this.getAmpm()) {
if (currentHours > 12) {
ampm = "PM";
hour -= 12;
} else if (currentHours == 12) {
ampm = "PM";
} else if (currentHours == 0) {
hour = 12;
}
}
value = {
day: value.getDate(),
month: value.getMonth() + 1,
year: value.getFullYear(),
hour: hour,
minute: value.getMinutes(),
ampm: ampm
};
} this.callParent([value, animated]);
this.onSlotPick();
},
//获取值
getValue: function (useDom) {
var values = {},
items = this.getItems().items,
ln = items.length,
daysInMonth, day, month, year, hour, minute, item, i; for (i = 0; i < ln; i++) {
item = items[i];
if (item instanceof Ext.picker.Slot) {
values[item.getName()] = item.getValue(useDom);
}
} //if all the slots return null, we should not return a date
if (values.year === null && values.month === null && values.day === null && values.hour === null && values.minute === null) {
return null;
} year = Ext.isNumber(values.year) ? values.year : 1;
month = Ext.isNumber(values.month) ? values.month : 1;
day = Ext.isNumber(values.day) ? values.day : 1;
hour = Ext.isNumber(values.hour) ? values.hour : 1;
minute = Ext.isNumber(values.minute) ? values.minute : 1; if (month && year && month && day) {
daysInMonth = this.getDaysInMonth(month, year);
}
day = (daysInMonth) ? Math.min(day, daysInMonth) : day;
if (values.ampm && values.ampm == "PM" && hour < 12) {
hour = hour + 12;
}
if (values.ampm && values.ampm == "AM" && hour == 12) {
hour = 0;
}
return new Date(year, month - 1, day, hour, minute);
}, /**
* Updates the yearFrom configuration
*/
updateYearFrom: function () {
if (this.initialized) {
this.createSlots();
}
}, /**
* Updates the yearTo configuration
*/
updateYearTo: function () {
if (this.initialized) {
this.createSlots();
}
}, /**
* Updates the monthText configuration
*/
updateMonthText: function (newMonthText, oldMonthText) {
var innerItems = this.getInnerItems,
ln = innerItems.length,
item, i; //loop through each of the current items and set the title on the correct slice
if (this.initialized) {
for (i = 0; i < ln; i++) {
item = innerItems[i]; if ((typeof item.title == "string" && item.title == oldMonthText) || (item.title.html == oldMonthText)) {
item.setTitle(newMonthText);
}
}
}
}, /**
* Updates the {@link #dayText} configuration.
*/
updateDayText: function (newDayText, oldDayText) {
var innerItems = this.getInnerItems,
ln = innerItems.length,
item, i; //loop through each of the current items and set the title on the correct slice
if (this.initialized) {
for (i = 0; i < ln; i++) {
item = innerItems[i]; if ((typeof item.title == "string" && item.title == oldDayText) || (item.title.html == oldDayText)) {
item.setTitle(newDayText);
}
}
}
}, /**
* Updates the yearText configuration
*/
updateYearText: function (yearText) {
var innerItems = this.getInnerItems,
ln = innerItems.length,
item, i; //loop through each of the current items and set the title on the correct slice
if (this.initialized) {
for (i = 0; i < ln; i++) {
item = innerItems[i]; if (item.title == this.yearText) {
item.setTitle(yearText);
}
}
}
}, // @private
constructor: function () {
this.callParent(arguments);
this.createSlots();
}, /**
* Generates all slots for all years specified by this component, and then sets them on the component
* @private
*/
createSlots: function () {
var me = this,
slotOrder = me.getSlotOrder(),
yearsFrom = me.getYearFrom(),
yearsTo = me.getYearTo(),
years = [],
days = [],
months = [],
hours = [],
minutes = [],
ampm = [],
reverse = yearsFrom > yearsTo,
ln, i, daysInMonth; if (!this.getAmpm()) {
var index = slotOrder.indexOf('ampm')
if (index >= 0) {
slotOrder.splice(index);
}
}
//填充年列表
while (yearsFrom) {
years.push({
text: yearsFrom,
value: yearsFrom
}); if (yearsFrom === yearsTo) {
break;
} if (reverse) {
yearsFrom--;
} else {
yearsFrom++;
}
}
//填充天列表
daysInMonth = me.getDaysInMonth(1, new Date().getFullYear()); for (i = 0; i < daysInMonth; i++) {
days.push({
text: i + 1,
value: i + 1
});
}
//填充月列表
for (i = 0, ln = Ext.Date.monthNames.length; i < ln; i++) {
months.push({
text: Ext.Date.monthNames[i],
value: i + 1
});
}
//填充小时列表
var hourLimit = (this.getAmpm()) ? 12 : 23
var hourStart = (this.getAmpm()) ? 1 : 0
for (i = hourStart; i <= hourLimit; i++) {
hours.push({
text: this.pad2(i),
value: i
});
}
//填充分钟列表
for (i = 0; i < 60; i += this.getMinuteInterval()) {
minutes.push({
text: this.pad2(i),
value: i
});
}
//填充上午/下午
ampm.push({
text: '上午',
value: 'AM'
}, {
text: '下午',
value: 'PM'
}); var slots = []; slotOrder.forEach(function (item) {
slots.push(me.createSlot(item, days, months, years, hours, minutes, ampm));
}); me.setSlots(slots);
}, /**
* Returns a slot config for a specified date.
* @private
*/
createSlot: function (name, days, months, years, hours, minutes, ampm) {
switch (name) {
case 'year':
return {
name: 'year',
align: 'center',
data: years,
title: this.getYearText(),
flex: 3
};
case 'month':
return {
name: name,
align: 'center',
data: months,
title: this.getMonthText(),
flex: 3
};
case 'day':
return {
name: 'day',
align: 'center',
data: days,
width: '1px',
title: this.getDayText(),
flex: 2
};
case 'hour':
return {
name: 'hour',
align: 'center',
data: hours,
title: this.getHourText(),
flex: 2
};
case 'minute':
return {
name: 'minute',
align: 'center',
data: minutes,
title: this.getMinuteText(),
flex: 2
};
case 'ampm':
return {
name: 'ampm',
align: 'center',
data: ampm,
title: this.getAmpmText(),
flex: 2
};
}
}, onSlotPick: function () {
var value = this.getValue(true),
slot = this.getDaySlot(),
year = value.getFullYear(),
month = value.getMonth(),
days = [],
daysInMonth, i;
if (!value || !Ext.isDate(value) || !slot) {
return;
} this.callParent(arguments); //get the new days of the month for this new date
daysInMonth = this.getDaysInMonth(month + 1, year);
for (i = 0; i < daysInMonth; i++) {
days.push({
text: i + 1,
value: i + 1
});
}
// We don't need to update the slot days unless it has changed
if (slot.getStore().getCount() == days.length) {
return;
} slot.getStore().setData(days); // Now we have the correct amount of days for the day slot, lets update it
var store = slot.getStore(),
viewItems = slot.getViewItems(),
valueField = slot.getValueField(),
index, item; index = store.find(valueField, value.getDate());
if (index == -1) {
return;
} item = Ext.get(viewItems[index]); slot.selectedIndex = index;
slot.scrollToItem(item);
slot.setValue(slot.getValue(true)); }, getDaySlot: function () {
var innerItems = this.getInnerItems(),
ln = innerItems.length,
i, slot; if (this.daySlot) {
return this.daySlot;
} for (i = 0; i < ln; i++) {
slot = innerItems[i];
if (slot.isSlot && slot.getName() == "day") {
this.daySlot = slot;
return slot;
}
} return null;
}, // @private
getDaysInMonth: function (month, year) {
var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
return month == 2 && this.isLeapYear(year) ? 29 : daysInMonth[month - 1];
}, // @private
isLeapYear: function (year) {
return !!((year & 3) === 0 && (year % 100 || (year % 400 === 0 && year)));
}, onDoneButtonTap: function () {
var oldValue = this._value,
newValue = this.getValue(true),
testValue = newValue; if (Ext.isDate(newValue)) {
testValue = newValue.toDateString();
}
if (Ext.isDate(oldValue)) {
oldValue = oldValue.toDateString();
}
if (testValue != oldValue) {
this.fireEvent('change', this, newValue);
}
this.hide();
Ext.util.InputBlocker.unblockInputs();
},
pad2: function (number) {
return (number < 10 ? '0' : '') + number;
}
});

uxFieldTime

使用方法参考:datepickerfield控件

 Ext.define('ux.field.Time', {
extend: 'Ext.field.Text',
alternateClassName: 'uxFieldTime',
xtype: 'uxFieldTime',
requires: [
'ux.picker.Time',
'Ext.DateExtras'
], /**
* @event change
* Fires when a date is selected
* @param {Ext.field.DatePicker} this
* @param {Date} newDate The new date
* @param {Date} oldDate The old date
*/ config: {
ui: 'select', /**
* @cfg {Object/ux.picker.Time} picker
* An object that is used when creating the internal {@link ux.picker.Time} component or a direct instance of {@link ux.picker.Time}.
* @accessor
*/
picker: true, /**
* @cfg {Boolean}
* @hide
* @accessor
*/
clearIcon: false, /**
* @cfg {Object/Date} value
* Default value for the field and the internal {@link ux.picker.Time} component. Accepts an object of 'year',
* 'month' and 'day' values, all of which should be numbers, or a {@link Date}.
*
* Example: {year: 1989, day: 1, month: 5} = 1st May 1989 or new Date()
* @accessor
*/ /**
* @cfg {Boolean} destroyPickerOnHide
* 完成选择时隐藏或者销毁该控件,默认销毁
* @accessor
*/
destroyPickerOnHide: false, /**
* @cfg {String} dateFormat 默认时间格式
* 接受任何有效的时间格式. 请参考 {@link Ext.Date}.
*/
dateFormat: 'Y-m-d H:i', /**
* @cfg {Object}
* @hide
*/
component: {
useMask: true
}
}, initialize: function () {
var me = this,
component = me.getComponent(); me.callParent(); component.on({
scope: me,
masktap: 'onMaskTap'
}); component.doMaskTap = Ext.emptyFn; if (Ext.browser.is.AndroidStock2) {
component.input.dom.disabled = true;
}
}, syncEmptyCls: Ext.emptyFn, applyValue: function (value) {
if (!Ext.isDate(value) && !Ext.isObject(value)) {
return null;
} if (Ext.isObject(value)) {
return new Date(value.year, value.month - 1, value.day, value.hour, value.minute);
} return value;
}, updateValue: function (newValue, oldValue) {
var me = this,
picker = me._picker; if (picker && picker.isPicker) {
picker.setValue(newValue);
} // Ext.Date.format expects a Date
if (newValue !== null) {
me.getComponent().setValue(Ext.Date.format(newValue, me.getDateFormat() || Ext.util.Format.defaultDateFormat));
} else {
me.getComponent().setValue('');
} if (newValue !== oldValue) {
me.fireEvent('change', me, newValue, oldValue);
}
}, /**
* Updates the date format in the field.
* @private
*/
updateDateFormat: function (newDateFormat, oldDateFormat) {
var value = this.getValue();
if (newDateFormat != oldDateFormat && Ext.isDate(value)) {
this.getComponent().setValue(Ext.Date.format(value, newDateFormat || Ext.util.Format.defaultDateFormat));
}
}, /**
* Returns the {@link Date} value of this field.
* If you wanted a formated date
* @return {Date} The date selected
*/
getValue: function () {
if (this._picker && this._picker instanceof ux.picker.Time) {
return this._picker.getValue();
}
return this._value;
}, /**
* Returns the value of the field formatted using the specified format. If it is not specified, it will default to
* {@link #dateFormat} and then {@link Ext.util.Format#defaultDateFormat}.
* @param {String} format The format to be returned.
* @return {String} The formatted date.
*/
getFormattedValue: function (format) {
var value = this.getValue();
return (Ext.isDate(value)) ? Ext.Date.format(value, format || this.getDateFormat() || Ext.util.Format.defaultDateFormat) : value;
}, applyPicker: function (picker, pickerInstance) {
if (pickerInstance && pickerInstance.isPicker) {
picker = pickerInstance.setConfig(picker);
} return picker;
}, getPicker: function () {
var picker = this._picker,
value = this.getValue(); if (picker && !picker.isPicker) {
picker = Ext.factory(picker, ux.picker.Time);
if (value != null) {
picker.setValue(value);
}
} picker.on({
scope: this,
change: 'onPickerChange',
hide: 'onPickerHide'
}); this._picker = picker; return picker;
}, /**
* @private
* Listener to the tap event of the mask element. Shows the internal DatePicker component when the button has been tapped.
*/
onMaskTap: function () {
if (this.getDisabled()) {
return false;
} this.onFocus(); return false;
}, /**
* Called when the picker changes its value.
* @param {ux.picker.Time} picker The date picker.
* @param {Object} value The new value from the date picker.
* @private
*/
onPickerChange: function (picker, value) {
var me = this,
oldValue = me.getValue(); me.setValue(value);
me.fireEvent('select', me, value);
me.onChange(me, value, oldValue);
}, /**
* Override this or change event will be fired twice. change event is fired in updateValue
* for this field. TOUCH-2861
*/
onChange: Ext.emptyFn, /**
* Destroys the picker when it is hidden, if
* {@link Ext.field.DatePicker#destroyPickerOnHide destroyPickerOnHide} is set to `true`.
* @private
*/
onPickerHide: function () {
var me = this,
picker = me.getPicker(); if (me.getDestroyPickerOnHide() && picker) {
picker.destroy();
me._picker = me.getInitialConfig().picker || true;
}
}, reset: function () {
this.setValue(this.originalValue);
}, onFocus: function (e) {
var component = this.getComponent();
this.fireEvent('focus', this, e); if (Ext.os.is.Android4) {
component.input.dom.focus();
}
component.input.dom.blur(); if (this.getReadOnly()) {
return false;
} this.isFocused = true; this.getPicker().show();
}, // @private
destroy: function () {
var picker = this._picker; if (picker && picker.isPicker) {
picker.destroy();
} this.callParent(arguments);
}
//<deprecated product=touch since=2.0>
}, function () {
this.override({
getValue: function (format) {
if (format) {
//<debug warn>
Ext.Logger.deprecate("format argument of the getValue method is deprecated, please use getFormattedValue instead", this);
//</debug>
return this.getFormattedValue(format);
}
return this.callOverridden();
}
}); /**
* @method getDatePicker
* @inheritdoc Ext.field.DatePicker#getPicker
* @deprecated 2.0.0 Please use #getPicker instead
*/
Ext.deprecateMethod(this, 'getDatePicker', 'getPicker');
//</deprecated>
});

效果图:

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

只需要时、分选项如下:

             xtype: 'uxFieldTime',
picker: {
slotOrder: ['hour', 'minute']
},
dateFormat: 'H:i',
value: new Date(),
label: '时间:',
placeHolder: '请输入时间'

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

一些额外的css:

 /*#region pick */

 .x-picker-slot-title {
height:auto;
text-align:center;
padding:0.2em 0;
}
.x-picker-slot .x-dataview-item {
padding:0 8px !important;
}
.x-webkit .x-layout-box.x-horizontal > .x-layout-box-item.x-picker-slot {
width:auto !important;
}
/*#endregion*/