2017年12月8日 星期五

Kendo UI Date Picker 民國年格式 | Kendo UI Date Picker Taiwan calander format



背景

一個Grid,在編輯時用popup,內容是template,
有個日期欄位,
使用者想要輸入1060101或者10601011735這種日期或日期時間格式
不想要選取也不想要打「-」、空白或「:」

研究了一下也提問在官方討論區了,官方是說不支援民國年這種格式,
實際上你輸入1900年之前的日期,也會無法set,
所以也不能用民國106年直接套上DatePicker/DateTimePicker,而且萬年曆也會不對,

這裡就用textbox自幹一個字串轉日期的方法
(如果想要讓後端收到的值仍然有「-」、空白或「:」,是可以搭配mask)

首先在原本的template的時間輸入框前面加入一個input textbox

<input class="k-textbox " data-bind="value:myDateTW" id="myDateTW" name="myDateTW" placeholder="民國年yyyMMdd" style="width: 200px;" tabindex="110">
<span class="k-invalid-msg" data-for="myDateTW"></span>
<input data-bind="value:myDate" id="myDate" name="myDate" style="display: none; width: 250px;" tabindex="110">
<span class="k-invalid-msg" data-for="myDate"></span>


然後準備好字串轉日期、日期轉字串的處理,
這裡列的方法是我很直觀的去寫,沒有做什麼效能分析

//傳入值與格式,分析與轉換成JS日期物件
function parseTWDate(val, format) {
  var result = { result: true, msg: "", DateObj: null };

  if (typeof val === "undefined") {
    result.result = false;
    return result;
  }

  //只接受數字與字串型態
  if (typeof val !== "number" && typeof val !== "string") {
    result.result = false;
    return result;
  }
  //檢查格式字串
  if (
    format !== "yyyMMdd" &&
    format !== "yyyMMddHHmm" &&
    format !== "yyyMMddHHmmss"
  ) {
    result.result = false;
    result.msg = "不支援的日期格式設定";
    return result;
  }
  //只接受數字不接受符號
  if (/^\d+$/.test(val) === false) {
    result.result = false;
    return result;
  }
  var str = "";

  //數字型態轉文字
  if (typeof val === "number") {
    str = val.toString();
  } else {
    str = val;
  }

  //從後往前兩位兩位取出字串,轉數字後做驗證
  var sec = 0;
  var min = 0;
  var hr = 0;
  var day = 0;
  var mon = 0;
  var year = 0;
  var movingCount = 0;
  if (format === "yyyMMddHHmmss") {
    if (str.length < 11) {
      result.result = false;
      return result;
    }
    sec = parseInt(
      str.substring(str.length - movingCount - 2, str.length - movingCount),
      10
    );
    if (sec === NaN || sec > 59 || sec < 0) {
      result.result = false;
      return result;
    }
    movingCount += 2;
  }
  if (format === "yyyMMddHHmm" || format === "yyyMMddHHmmss") {
    if (str.length < 9) {
      result.result = false;
      return result;
    }
    min = parseInt(
      str.substring(str.length - movingCount - 2, str.length - movingCount),
      10
    );
    if (min === NaN || min > 59 || min < 0) {
      result.result = false;
      return result;
    }
    movingCount += 2;

    hr = parseInt(
      str.substring(str.length - movingCount - 2, str.length - movingCount),
      10
    );
    if (hr === NaN || hr > 23 || hr < 0) {
      result.result = false;
      return result;
    }
    movingCount += 2;
  }
  if (str.length < 5) {
    result.result = false;
    return result;
  }
  day = parseInt(
    str.substring(str.length - movingCount - 2, str.length - movingCount),
    10
  );
  if (day === NaN || day > 31 || day < 1) {
    result.result = false;
    return result;
  }
  movingCount += 2;

  mon = parseInt(
    str.substring(str.length - movingCount - 2, str.length - movingCount),
    10
  );
  if (mon === NaN || mon > 12 || mon < 1) {
    result.result = false;
    return result;
  }
  movingCount += 2;

  year = parseInt(str.substring(0, str.length - movingCount), 10);
  if (year === NaN || year < 1) {
    result.result = false;
    return result;
  }
  //JS的日期的月份是0 base
  result.DateObj = new Date(year + 1911, mon - 1, day, hr, min, sec);

  return result;
}
//將日期選擇轉成民國年
function getTWDate(val, format) {
  var rawYear = val.getFullYear();
  var twYear = rawYear - 1911;
  var rawMonth = val.getMonth() + 1;
  var twMonth = rawMonth.toString().padStart(2, "0");
  var rawDay = val.getDate();
  var twDay = rawDay.toString().padStart(2, "0");
  var strTWDate = twYear + twMonth + twDay;
  if (format === "yyyMMddHHmm" || format === "yyyMMddHHmmss") {
    var hr = val
      .getHours()
      .toString()
      .padStart(2, "0");
    var min = val
      .getMinutes()
      .toString()
      .padStart(2, "0");
    strTWDate = strTWDate + hr + min;
    if (format === "yyyMMddHHmmss") {
      var sec = val
        .getSeconds()
        .toString()
        .padStart(2, "0");
      strTWDate = strTWDate + sec;
    }
  }
  return strTWDate;
}


然後在原本的datasource的schema中加入一個自訂的民國年欄位,類型是字串
這個欄位不會回傳值給API,只是用來data bind,
裡面做自定義驗證方法,在民國年字串輸入框跳開時,去跑日期解析,
成功就把日期物件帶回model,失敗就給予錯誤訊息並回傳false;
原本的欄位也給他一個驗證是如果跑到了驗證就檢查他的值是否可以解析成日期,
這裡弄了很久是不知如何避免偶爾會出現原欄位跳出必填的問題,
還有編輯時無法透過model.set讓他自己data bind去改變前端DOM的值,
set下去model的值竟然不會改,只能手動去塞值&改DOM,
改值的時候也要先改DOM在做SET,不然也會有怪現象

var dataSource = {
  schema: {
    model: {
      fields: {
        myDate: {
          type: "date",
          validation: {
            required: true,
            dateFormat: function(input, option) {
              if (input.is("[name='myDate']")) {
                if (Date.parse(input.val()) !== NaN) {
                  $("#myDateTW").removeAttr("data-TWdate-msg");
                  $("#myDate").removeAttr("data-required-msg");
                  return true;
                } else {
                  $("#myDateTW").attr(
                    "data-TWdate-msg",
                    "格式民國年yyyMMdd"
                  );
                  return false;
                }
              } else {
                return true;
              }
            }
          }
        },
        myDateTW: {
          type: "string",
          validation: {
            required: true,
            TWdate: function(input, option) {
              if (input.is("[name='myDateTW']")) {
                var val = input.val();
                var format = "yyyMMdd";
                var result = parseTWDate(val, format);
                if (result.result) {
                  input.removeAttr("data-TWdate-msg");
                  //帶回model
                  var source = $("#myDateTW")[0].kendoBindingTarget
                    .source;
                  $("#myDate").val(result.DateObj);
                  source.set("myDate", result.DateObj);
                  return true;
                } else {
                  input.attr("data-TWdate-msg", "格式民國年" + format);
                  //帶回model
                  var source = $("#myDateTW")[0].kendoBindingTarget
                    .source;
                  $("#myDate").val(val);
                  source.set("myDate", val);
                  return false;
                }
              } else {
                return true;
              }
            }
          },
        }
      }
    }
  },
};
};



再來是grid新增或編輯時需要把model的date欄位清空與轉換

edit: function(e) {
  var arg = e;
  if (e.model.isNew()) {
    //日期型態先清空
    e.model.set("myDate", null);
  } else {
    //民國年要先轉好
    var myDate = e.model.get("ㄗㄩDate");
    var strMyDateTW = getTWDate(myDate, "yyyMMdd");
    e.model.myDateTW = strMyDateTW;
    e.model.set("myDateTW", strMyDateTW);
    $("#myDateTW").val(strMyDateTW);
  }
}



這樣就可以輸入民國年日期轉成西元年日期,編輯時也可以把西元年日期轉回民國年日期了

沒有留言:

張貼留言