ux.form.field.Year 只能选年的时间扩展
效果如图,亲测6.2.1版本可用,用法同时间选择控件
1 //只选择年的控件 2 Ext.define('ux.picker.Year', { 3 extend: 'Ext.Component', 4 alias: 'widget.uxYearpicker', 5 alternateClassName: 'ux.uxYearpicker', 6 cls: 'uxYearpicker', 7 isYearPicker: true, 8 9 focusable: true, 10 11 childEls: [ 12 'bodyEl', 'prevEl', 'nextEl', 'yearEl' 13 ], 14 15 renderTpl: [ 16 '<div id="{id}-bodyEl" data-ref="bodyEl" class="{baseCls}-body">', 17 '<div id="{id}-yearEl" data-ref="yearEl" class="{baseCls}-years">', 18 '<div class="{baseCls}-yearnav">', 19 '<div class="{baseCls}-yearnav-button-ct">', 20 '<a id="{id}-prevEl" data-ref="prevEl" class="{baseCls}-yearnav-button {baseCls}-yearnav-prev" hidefocus="on" role="button"></a>', 21 '</div>', 22 '<div class="{baseCls}-yearnav-button-ct">', 23 '<a id="{id}-nextEl" data-ref="nextEl" class="{baseCls}-yearnav-button {baseCls}-yearnav-next" hidefocus="on" role="button"></a>', 24 '</div>', 25 '</div>', 26 '<tpl for="years">', 27 '<div class="{parent.baseCls}-item {parent.baseCls}-year">', 28 '<a hidefocus="on" class="{parent.baseCls}-item-inner" role="button">{.}</a>', 29 '</div>', 30 '</tpl>', 31 '</div>', 32 '<div class="' + Ext.baseCSSPrefix + 'clear"></div>', 33 '<tpl if="showButtons">', 34 '<div class="{baseCls}-buttons">{%', 35 'var me=values.$comp, okBtn=me.okBtn, cancelBtn=me.cancelBtn;', 36 'okBtn.ownerLayout = cancelBtn.ownerLayout = me.componentLayout;', 37 'okBtn.ownerCt = cancelBtn.ownerCt = me;', 38 'Ext.DomHelper.generateMarkup(okBtn.getRenderTree(), out);', 39 'Ext.DomHelper.generateMarkup(cancelBtn.getRenderTree(), out);', 40 '%}</div>', 41 '</tpl>', 42 '</div>' 43 ], 44 45 //<locale> 46 /** 47 * @cfg {String} okText The text to display on the ok button. 48 */ 49 okText: '确定', 50 //</locale> 51 52 //<locale> 53 /** 54 * @cfg {String} cancelText The text to display on the cancel button. 55 */ 56 cancelText: '取消', 57 //</locale> 58 59 /** 60 * @cfg {String} [baseCls='x-monthpicker'] 61 * The base CSS class to apply to the picker element. 62 */ 63 baseCls: Ext.baseCSSPrefix + 'monthpicker', 64 65 /** 66 * @cfg {Boolean} showButtons True to show ok and cancel buttons below the picker. 67 */ 68 showButtons: true, 69 70 /** 71 * @cfg {String} [selectedCls='x-monthpicker-selected'] The class to be added to selected items in the picker. 72 */ 73 74 /** 75 * @cfg {Date/Number[]} value The default value to set. See {@link #setValue} 76 */ 77 78 /** 79 * @cfg {String} 80 * The {@link Ext.button.Button#ui} to use for the month picker's footer buttons. 81 */ 82 footerButtonUI: 'default', 83 84 measureWidth: 35, 85 measureMaxHeight: 20, 86 87 // used when attached to date picker which isnt showing buttons 88 smallCls: Ext.baseCSSPrefix + 'monthpicker-small', 89 90 /** 91 * @private 92 */ 93 totalYears: 10, 94 yearOffset: 5, // 10 years in total, 2 per row 95 monthOffset: 6, // 12 months, 2 per row 96 alignOnScroll: false, 97 98 /** 99 * @event cancelclick 100 * Fires when the cancel button is pressed. 101 * @param {Ext.picker.Month} this 102 */ 103 104 /** 105 * @event monthclick 106 * Fires when a month is clicked. 107 * @param {Ext.picker.Month} this 108 * @param {Array} value The current value 109 */ 110 111 /** 112 * @event monthdblclick 113 * Fires when a month is clicked. 114 * @param {Ext.picker.Month} this 115 * @param {Array} value The current value 116 */ 117 118 /** 119 * @event okclick 120 * Fires when the ok button is pressed. 121 * @param {Ext.picker.Month} this 122 * @param {Array} value The current value 123 */ 124 125 /** 126 * @event select 127 * Fires when a month/year is selected. 128 * @param {Ext.picker.Month} this 129 * @param {Array} value The current value 130 */ 131 132 /** 133 * @event yearclick 134 * Fires when a year is clicked. 135 * @param {Ext.picker.Month} this 136 * @param {Array} value The current value 137 */ 138 139 /** 140 * @event yeardblclick 141 * Fires when a year is clicked. 142 * @param {Ext.picker.Month} this 143 * @param {Array} value The current value 144 */ 145 146 /** 147 * @inheritdoc 148 * @private 149 */ 150 initComponent: function () { 151 var me = this; 152 153 me.selectedCls = me.baseCls + '-selected'; 154 155 if (me.small) { 156 me.addCls(me.smallCls); 157 } 158 me.setValue(me.value); 159 me.activeYear = me.getYear(new Date().getFullYear() - 4, -4); 160 161 if (me.showButtons) { 162 me.okBtn = new Ext.button.Button({ 163 ui: me.footerButtonUI, 164 text: me.okText, 165 handler: me.onOkClick, 166 scope: me 167 }); 168 me.cancelBtn = new Ext.button.Button({ 169 ui: me.footerButtonUI, 170 text: me.cancelText, 171 handler: me.onCancelClick, 172 scope: me 173 }); 174 } 175 176 this.callParent(); 177 }, 178 179 /** 180 * @inheritdoc 181 * @private 182 */ 183 beforeRender: function () { 184 var me = this; 185 186 if (me.padding && !me.width) { 187 me.cacheWidth(); 188 } 189 190 me.callParent(); 191 192 Ext.apply(me.renderData, { 193 years: me.getYears(), 194 showButtons: me.showButtons 195 }); 196 }, 197 198 cacheWidth: function () { 199 var me = this, 200 padding = me.parseBox(me.padding), 201 widthEl = Ext.getBody().createChild({ 202 cls: me.baseCls + ' ' + me.borderBoxCls, 203 style: 'position:absolute;top:-1000px;left:-1000px;', 204 html: ' ' // required for opera 11.64 to measure a width 205 }); 206 207 me.self.prototype.width = widthEl.getWidth() + padding.left + padding.right; 208 widthEl.destroy(); 209 }, 210 211 /** 212 * @inheritdoc 213 * @private 214 */ 215 afterRender: function () { 216 var me = this, 217 body = me.bodyEl; 218 219 me.callParent(); 220 221 me.el.on('mousedown', me.onElClick, me, { 222 translate: false 223 }); 224 225 body.on({ 226 scope: me, 227 click: 'onBodyClick', 228 dblclick: 'onBodyClick' 229 }); 230 231 // keep a reference to the year/month elements since we'll be re-using them 232 me.years = body.select('.' + me.baseCls + '-year a'); 233 234 me.backRepeater = new Ext.util.ClickRepeater(me.prevEl, { 235 handler: Ext.Function.bind(me.adjustYear, me, [-me.totalYears]) 236 }); 237 238 me.prevEl.addClsOnOver(me.baseCls + '-yearnav-prev-over'); 239 me.nextRepeater = new Ext.util.ClickRepeater(me.nextEl, { 240 handler: Ext.Function.bind(me.adjustYear, me, [me.totalYears]) 241 }); 242 me.nextEl.addClsOnOver(me.baseCls + '-yearnav-next-over'); 243 me.updateBody(); 244 245 }, 246 247 /** 248 * Set the value for the picker. 249 * @param {Date/Number[]} value The value to set. It can be a Date object, where the month/year will be extracted, or 250 * it can be an array, with the month as the first index and the year as the second. 251 * @return {Ext.picker.Month} this 252 */ 253 setValue: function (value) { 254 var me = this, 255 active = me.activeYear, 256 year; 257 258 if (!value) { 259 me.value = [null, null]; 260 } else if (Ext.isDate(value)) { 261 me.value = [value.getMonth(), value.getFullYear()]; 262 } else { 263 me.value = [value[0], value[1]]; 264 } 265 266 if (me.rendered) { 267 year = me.value[1]; 268 if (year !== null) { 269 if ((year < active || year > active + me.yearOffset)) { 270 me.activeYear = year - me.yearOffset + 1; 271 } 272 } 273 me.updateBody(); 274 } 275 276 return me; 277 }, 278 279 /** 280 * Gets the selected value. It is returned as an array [month, year]. It may 281 * be a partial value, for example [null, 2010]. The month is returned as 282 * 0 based. 283 * @return {Number[]} The selected value 284 */ 285 getValue: function () { 286 return this.value; 287 }, 288 289 /** 290 * Checks whether the picker has a selection 291 * @return {Boolean} Returns true if both a month and year have been selected 292 */ 293 hasSelection: function () { 294 var value = this.value; 295 return value[0] !== null && value[1] !== null; 296 }, 297 298 /** 299 * Get an array of years to be pushed in the template. It is not in strict 300 * numerical order because we want to show them in columns. 301 * @private 302 * @return {Number[]} An array of years 303 */ 304 getYears: function () { 305 var me = this, 306 offset = me.yearOffset, 307 start = me.activeYear, // put the "active" year on the left 308 end = start + offset, 309 i = start, 310 years = []; 311 312 for (; i < end; ++i) { 313 years.push(i, i + offset); 314 } 315 316 return years; 317 }, 318 319 /** 320 * Update the years in the body based on any change 321 * @private 322 */ 323 updateBody: function () { 324 var me = this, 325 years = me.years, 326 yearNumbers = me.getYears(), 327 cls = me.selectedCls, 328 value = me.getYear(null), 329 year, 330 yearItems, y, yLen, el; 331 332 if (me.rendered) { 333 years.removeCls(cls); 334 335 yearItems = years.elements; 336 yLen = yearItems.length; 337 338 for (y = 0; y < yLen; y++) { 339 el = Ext.fly(yearItems[y]); 340 341 year = yearNumbers[y]; 342 el.dom.innerHTML = year; 343 if (year === value) { 344 el.addCls(cls); 345 } 346 } 347 } 348 }, 349 350 /** 351 * Gets the current year value, or the default. 352 * @private 353 * @param {Number} defaultValue The default value to use if the year is not defined. 354 * @param {Number} offset A number to offset the value by 355 * @return {Number} The year value 356 */ 357 getYear: function (defaultValue, offset) { 358 var year = this.value[1]; 359 offset = offset || 0; 360 return year === null ? defaultValue : year + offset; 361 }, 362 363 onElClick: function (e) { 364 e.stopEvent(); 365 }, 366 367 /** 368 * React to clicks on the body 369 * @private 370 */ 371 onBodyClick: function (e, t) { 372 var me = this, 373 isDouble = e.type === 'dblclick'; 374 375 if (e.getTarget('.' + me.baseCls + '-year')) { 376 e.stopEvent(); 377 me.onYearClick(t, isDouble); 378 } 379 }, 380 381 /** 382 * Modify the year display by passing an offset. 383 * @param {Number} [offset=10] The offset to move by. 384 */ 385 adjustYear: function (offset) { 386 if (typeof offset !== 'number') { 387 offset = this.totalYears; 388 } 389 this.activeYear += offset; 390 this.updateBody(); 391 }, 392 393 /** 394 * React to the ok button being pressed 395 * @private 396 */ 397 onOkClick: function () { 398 this.fireEvent('okclick', this, this.value); 399 }, 400 401 /** 402 * React to the cancel button being pressed 403 * @private 404 */ 405 onCancelClick: function () { 406 this.fireEvent('cancelclick', this); 407 }, 408 409 /** 410 * React to a year being clicked 411 * @private 412 * @param {HTMLElement} target The element that was clicked 413 * @param {Boolean} isDouble True if the event was a doubleclick 414 */ 415 onYearClick: function (target, isDouble) { 416 var me = this; 417 me.value[1] = me.activeYear + me.resolveOffset(me.years.indexOf(target), me.yearOffset); 418 me.updateBody(); 419 me.fireEvent('year' + (isDouble ? 'dbl' : '') + 'click', me, me.value); 420 me.fireEvent('select', me, me.value); 421 422 }, 423 424 /** 425 * Returns an offsetted number based on the position in the collection. Since our collections aren't 426 * numerically ordered, this function helps to normalize those differences. 427 * @private 428 * @param {Object} index 429 * @param {Object} offset 430 * @return {Number} The correctly offsetted number 431 */ 432 resolveOffset: function (index, offset) { 433 if (index % 2 === 0) { 434 return (index / 2); 435 } 436 return offset + Math.floor(index / 2); 437 }, 438 439 /** 440 * @inheritdoc 441 * @private 442 */ 443 beforeDestroy: function () { 444 var me = this; 445 me.years = me.months = null; 446 Ext.destroyMembers(me, 'backRepeater', 'nextRepeater', 'okBtn', 'cancelBtn'); 447 me.callParent(); 448 }, 449 450 onDestroy: function () { 451 Ext.destroyMembers(this, 'okBtn', 'cancelBtn'); 452 this.callParent(); 453 }, 454 455 privates: { 456 // Do the job of a container layout at this point even though we are not a Container. 457 // TODO: Refactor as a Container. 458 finishRenderChildren: function () { 459 var me = this; 460 461 this.callParent(arguments); 462 463 if (this.showButtons) { 464 me.okBtn.finishRender(); 465 me.cancelBtn.finishRender(); 466 } 467 } 468 } 469 });
1 //只选择年的控件 2 Ext.define('ux.form.field.Year', { 3 extend: 'Ext.form.field.Date', 4 alias: 'widget.uxYearfield', 5 xtype: 'uxYearfield', 6 requires: ['ux.picker.Year'], 7 format: 'Y', 8 selectYear: new Date(), 9 createPicker: function () { 10 var me = this; 11 return new ux.picker.Year({ 12 value: new Date(), 13 renderTo: document.body, 14 floating: true, 15 hidden: true, 16 focusOnShow: true, 17 listeners: { 18 scope: me, 19 select: me.onSelect, 20 cancelclick: me.onCancelClick, 21 okclick: me.onOKClick, 22 yeardblclick: me.onOKClick, 23 monthdblclick: me.onOKClick 24 } 25 }); 26 }, 27 onCancelClick: function () { 28 var me = this; 29 me.selectYear = null; 30 me.collapse(); 31 }, 32 onOKClick: function () { 33 var me = this; 34 if (me.selectYear) { 35 me.setValue(me.selectYear); 36 me.fireEvent('select', me, me.selectYear); 37 } 38 me.collapse(); 39 }, 40 onSelect: function (m, d) { 41 var me = this; 42 me.selectYear = new Date((d[0] + 1) + '/1/' + d[1]); 43 } 44 });