日曆就是日曆,但它不是合適的時間選擇器。 html
大部分的表單中凡是涉及時間選擇的,都會採用日曆選擇器。大部分UI框架也會默認提供。在許多場景中使用這種控件並不合適,特別是一些二流的公司甚至沒有和日曆同步時間。 app
好比: 框架
看起來挺炫,其實毫無心義。這個控件不會和字段內的時間同步,照理說控件顯示的時候應該反應字段內的日期和時間。若是我倖幸苦苦選擇到指定日期,不當心點錯了一格,我必須從新來一遍。 post
再有一個狀況是,若是是輸入生日,那麼必須在年份上下按鈕點100次(若是100歲的話)。 ui
採用html內置的選擇器是更加合理的時間選擇器。 this
如下是我寫的控件: spa
{fieldName: "publishdAt",inputLabel: '發表日期',years:"-10,+10",ftype:'datetime'/*date*/,required:true,validators:{},gridSize:"1-2"},
配置很好理解,years以當前時間(不必定後面再說)爲參考,-10,+10,固然是顯示先後10年(根據須要),若是是編輯,字段自己已經有了值,那麼這個先後是根據這個值的年份先後10年。 設計
這樣設計還有問題,若是年份超越了這個範圍,不是沒法輸入了嗎?當你點擊日期select的時候,最下方有一個「其它」選項,當你選擇這個值時,select會變成text字段,容許你輸入任何年份,當你輸入年份,text失去焦點以後,select字段又會回來。 code
還有一個隱藏的功能,就是當你改變年月或者月份的時候,這個控件的view會重畫,何須呢?由於月份不一樣,日期的範圍會變化。 orm
外觀:
最後是源代碼:
var Lang = Y.Lang, Ob = Y.Object, fieldsViewNs = Y.namespace("M3958.CusFieldView"); fieldsViewNs.CusDateTimeField = Y.Base.create('cusDateTimeField', Y.View, [], { events: { 'input': {blur: 'onInputBlur'} }, /** * 控制input的寬度能夠經過設置width style. */ selectTpl: Y.Template.Micro.compile( '<select class="<%= this.dmodel.type %>" style="display:inline;margin-left:2px;">' + '<% for (var i=this.dmodel.range[0]; i<=this.dmodel.range[1];i++) { %>' + '<option value=<%= i %><%= (i == data.dmodel.cur) ? " selected" : "" %>><%= i + data.dmodel.postfix %></option>' + '<% } %>' + '</select>' ), template: Y.Template.Micro.compile( '<label for="<%= this.guid %>"><%= this.fieldDescription.inputLabel %></label>' + '<input type="text" placeholder="年份" style="width:50px;display:none;">'+ '<select id="<%= this.guid %>" class="<%= this.dmodel.type %>" style="display:inline;">' + '<% for (var i=this.dmodel.range[0]; i<=this.dmodel.range[1];i++) { %>' + '<option value=<%= i %><%= (i == data.dmodel.cur) ? " selected" : "" %>><%= i + data.dmodel.postfix %></option>' + '<% } %>' + '<option value="otheryear">其它</option>' + '</select>' ), initializer: function (){ var container = this.get('container'), self = this, model = this.get('model'), fieldDescription = this.get('fieldDescription'); model.after('clientValidateErrorChange',function(e){ var validateResult = e.newVal; if (!validateResult) { return; } if (validateResult.isForField(fieldDescription.fieldName)) { if (validateResult.msg) { container.addClass("validate-error"); } else { container.removeClass("validate-error"); } } },this); this.on('destroy',function(e){ container.all('select').detachAll(); }); }, render: function () { var container = this.get('container'), fieldDescription = this.get('fieldDescription'), formStyle = this.get('formStyle'), label = fieldDescription.inputLabel, tnum = fieldDescription.ftype === 'datetime' ? 6 : 3, helpInline = fieldDescription.helpInline, self = this, html, yselect, mselect, dselect, docfragment, guid = Y.guid(), dmodel, idx = 1, inputNode; if (container.all('select')) { container.all('select').detachAll(); } if(Lang.isFunction(helpInline)){ helpInline = helpInline.call(this); } if(Lang.isFunction(label)){ label = label.call(this); } if(fieldDescription.required && (Lang.isBoolean(fieldDescription.required) || Lang.isString(fieldDescription.required))){ label = '<strong>' + label + '</strong>'; } dmodel = this.createDmodel(); html = this.template({ guid: guid, dmodel: dmodel[0], fieldDescription: fieldDescription } ); container.setHTML(html); docfragment = Y.one(Y.config.doc.createDocumentFragment()); for (;idx < tnum;idx++) { docfragment.append(this.selectTpl({ dmodel: dmodel[idx] } )); } container.append(docfragment); if (formStyle === 'aligned') { container.addClass('pure-control-group'); } else { if (fieldDescription.gridSize) { container.addClass('pure-u-' + fieldDescription.gridSize); } else { container.addClass('pure-u-1'); } } yselect = container.one('select.year'); mselect = container.one('select.month'); dselect = container.one('select.date'); yselect.after('change',function(){ self.afterSelectChange('y'); }); mselect.after('change',function(){ self.afterSelectChange('m'); }); dselect.after('change',function(){ self.afterSelectChange('d'); }); return this; }, createDmodel: function(){ var container = this.get('container'), fieldDescription = this.get('fieldDescription'), years = fieldDescription.years, model = this.get('model'), value = model.get(fieldDescription.fieldName), now = value ? new Date(value) : new Date(), self = this, yearstart, yearend, dmodel = [], yearr = years.split(","); if ((yearr[0].indexOf('-') === -1) && (yearr[0].indexOf('+') === -1)) { //沒有加減號,值如今 yearstart = now.getFullYear(); } else { yearstart = now.getFullYear() + parseInt(yearr[0]); } if ((yearr[1].indexOf('-') === -1) && (yearr[1].indexOf('+') === -1)) { //沒有加減號,值如今 yearend = now.getFullYear(); } else { yearend = now.getFullYear() + parseInt(yearr[1]); } dmodel.push({range:[yearstart,yearend],cur:now.getFullYear(),type:'year',postfix:'年'}); dmodel.push({range:[1,12],cur:now.getMonth() + 1,type:'month',postfix:'月'}); dmodel.push({range:[1,Y.M3958.Util.DateTime.monthEnd(now.getFullYear(),now.getMonth())],cur:now.getDate(),type:'date',postfix:'日'}); dmodel.push({range:[0,23],cur:now.getHours(),type:'hour',postfix:'時'}); dmodel.push({range:[0,59],cur:now.getMinutes(),type:'minute',postfix:'分'}); dmodel.push({range:[0,59],cur:now.getSeconds(),type:'second',postfix:'秒'}); return dmodel; }, getInputValue : function(){ var container = this.get('container'), input = container.one('select'); return input.get('value'); }, getDateTimeLong : function(){ return Y.M3958.UtilsClass.DateUtils.tolong(this.getInputValue()); }, _getValue : function(){ var container = this.get('container'), inputs = container.all('select'), value = []; inputs.each(function(nd){ value.push(parseInt(nd.get('value'),10)); }); return value; }, saveValue : function(){ var fieldDescription = this.get('fieldDescription'), model = this.get('model'), v = this._getValue(), dtv = new Date(); Y.Array.each(v,function(one,idx){ if (idx === 0) { dtv.setFullYear(one); } else if (idx === 1) { dtv.setMonth(one - 1); } else if (idx === 2) { dtv.setDate(one); } else if (idx === 3) { dtv.setHours(one); } else if (idx === 4) { dtv.setMinutes(one); } else if (idx === 5) { dtv.setSeconds(one); } }); if (model) { model.set(fieldDescription.fieldName,dtv.getTime()); } }, afterSelectChange: function(t){ var fieldDescription = this.get('fieldDescription'), container = this.get('container'), yselect = container.one('select.year'), dinput = container.one('input'), model = this.get('model'); if (t === 'y' && yselect.get('value') === 'otheryear') { dinput.setStyle('display','inline'); yselect.setStyle('display','none'); dinput.focus(); return; } this.saveValue(); if (model && model.validateOneField) { model.validateOneField(fieldDescription); } if (t === 'm' || t === 'y') { this.render(); } }, onInputBlur: function() { var fieldDescription = this.get('fieldDescription'), container = this.get('container'), yselect = container.one('select.year'), dvalue = container.one('input').get('value'), model = this.get('model'), value = model.get(fieldDescription.fieldName), d = new Date(value); /** * 只是改變year的值,其它的不變。 */ if (dvalue) { d.setFullYear(parseInt(dvalue,10)); model.set(fieldDescription.fieldName,d.getTime()); this.render(); } } },{ ATTRS: { container: { valueFn: function () { return Y.Node.create('<div></div>'); } }, fieldDescription : { value : null }, model: { value: null } } });這個select change事件不會冒泡(ie<10),因此必須拙劣的在每一個select上面訂閱事件,看官若是有辦法,不要吝嗇告訴我一聲。