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                     width: '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 });

效果图:

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

只需要时、分选项如下:

1             xtype: 'uxFieldTime',

2             picker: {

3                 slotOrder: ['hour', 'minute']

4             },

5             dateFormat: 'H:i',

6             value: new Date(),

7             label: '时间:',

8             placeHolder: '请输入时间'

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

一些额外的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*/

 

你可能感兴趣的:(Sencha Touch)