日历就是日历,但它不是合适的时间选择器。
大部分的表单中凡是涉及时间选择的,都会采用日历选择器。大部分UI框架也会默认提供。在许多场景中使用这种控件并不合适,特别是一些二流的公司甚至没有和日历同步时间。
比如:
看起来挺炫,其实毫无意义。这个控件不会和字段内的时间同步,照理说控件显示的时候应该反应字段内的日期和时间。如果我幸幸苦苦选择到指定日期,不小心点错了一格,我必须重新来一遍。
再有一个情况是,如果是输入生日,那么必须在年份上下按钮点100次(如果100岁的话)。
采用html内置的选择器是更加合理的时间选择器。
以下是我写的控件:
{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字段又会回来。
还有一个隐藏的功能,就是当你改变年月或者月份的时候,这个控件的view会重画,何必呢?因为月份不同,日期的范围会变化。
外观:
最后是源代码:
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上面订阅事件,看官如果有办法,不要吝啬告诉我一声。