handsontable在線編輯excel擴展功能-踩坑篇


簡述

先說一下背景,之所以封裝handsontable插件,是因為公司要實現在線編輯導入excel文件的功能,然后我就找到了這個功能強大的插件handsontable。

具體功能

除了handsontable的功能外,還包括:

1、每一行數據統計錯誤數,重復數

2、每一列標記重復項,錯誤項

3、定位功能,當數據過多出現滾動條時,點擊上一條/下一條按鈕,定位到當前標記項。

4、表頭標注每一列數據的校驗規則。

5、當數據被編輯后,立即重新校驗,並標記重復項、錯誤項

6、配置isValidate,true則本地校驗,false則不校驗
(css樣式有待改進,后面會更新)

2018/3/30日,修復所有bug,

1、根絕插件局部渲染的特性,將每行的第一列的標注完成局部渲染,以及錯誤點定位,也根據局部渲染的特性,先滾動到指定行,再進行標記。

2、另外,修復時間控件在最前/后一列/行的情況下,會被遮擋的問題,修改了源碼,根據當前單元格的位置來計算時間控件展示的位置。

3、修改源碼,將時間控件的英文改為中文。(后面會附上源碼修改部分)

2018/4/3日,修改保存錯誤定位的數據結構,並標記每一個td的row,col位置,保證做到,定位錯誤點100%准確

 

/*
 * author Happy Guo 
 * date 2018-03-15
 */
'use strict';
define(["jquery","Handsontable"], function ($,Handsontable) {
 var HandsontableExtend = function(opt) {
   this.opt = $.extend(true, {}, opt);
   this.element = document.querySelector(this.opt.el);
   this.header = Object.keys(this.opt.dataObj.headMap);
   this.allErrorNum = [];//記錄每一行的錯誤數
   this.table = null;
   this.errorRow = 0;//統計有幾行是有錯誤的
   this.errorPosition = {
     index:0,
     row: 0,
     col: 0
   };//記錄當前被標注的錯誤位置
   this.firstError = {
    isExist :false,
    row:0,
    col:0
   }
   this.isPosition = false;
   this.position = null;
   //如果沒有數據,默認給出一行空行
   if(this.opt.dataObj.dataMap.length===0){
    var obj = {};
     this.header.map(function(item,index){
       obj[item] = {
         value:'',
         errorType:''
       }
     });
    this.opt.dataObj.dataMap.push(obj)
  }
 }
  HandsontableExtend.prototype = {
    constructor: HandsontableExtend,
   // extraType: ["CODE_TYPE","Date"], //不需要校驗的類型
    typeMap:{'STRING':'文本','INTEGER':'整型','DOUBLE':'小數','DATE':'日期','CODE_TYPE':'枚舉類型','BOOLEAN':'布爾','TIME':'小時分鍾'},
    dateFormat:['yyyy-MM-dd','yyyy/MM/dd','yyyy.MM.dd'],
    timePattern:/^([0-1]{1}\d|2[0-3]):([0-5]\d)$/,
    /*@method 整合table的列的配置項
     */
    setColumn: function() {
      var list = [];
      for (var i = 0; i < this.header.length; i++) {
        var obj = {
          data: this.header[i] + ".value",
          width: 150,
          height: 60
        };
        var typeData = this.opt.dataObj.headMap[this.header[i]];
        if (typeData.type === "CODE_TYPE") {
          obj.type = "dropdown";
          obj.source = this.getCodeValueList(typeData.codeValueList);
        }
        if (typeData.type === "BOOLEAN") {
          obj.type = "dropdown";
          obj.source = ['是','否'];
        }
        if(typeData.type === 'TIME'){
          obj.type = "time";
          obj.dateFormat = "h:mm";
        }
        if (typeData.type === "DATE") {
          obj.type = "date";
          obj.dateFormat = "YYYY-MM-DD";
          obj.datePickerConfig={
            firstDay: 1,
            yearRange:100,
            showWeekNumber: true,
            minDate: new Date('1900-01-01')
          }
        }
        list.push(obj);
      }
      return list;
      this.opt.set.columns = list;
    },
    setCell:function(){
      var list = [];
      var that = this;
      this.opt.dataObj.dataMap.map(function(item,index){
        Object.keys(item).forEach(function(key){
          if(item[key].originalValue){
            list.push({
              row:index,
              col:that.header.indexOf(key),
              comment:{
                value:item[key].originalValue
              }
            })
          }
        })
      });
      return list;
    },
    getCodeValueList:function(list){
      var newList = [];
      if(list instanceof Array){
        list.map(function(item,index){
          newList.push(item.displayName);
        });
        return newList;
      }else{
        return [];
      }
    },
    /*@method 初始化table的配置,以及事件監聽
     */
    init: function() {
      var that = this;
      this.opt.set = $.extend(true,
        {
          data: that.opt.dataObj.dataMap,
          comments:true,
          columns: this.setColumn(),
          cells: function(row, col, prop) {
            //單元格渲染
            this.renderer = function(instance,td,row,col,prop,value,cellProperties) {
              Handsontable.renderers.TextRenderer.apply(this, arguments);
              var obj = that.opt.dataObj.dataMap[row][that.header[col]];
              $(td).attr({row:row,col:col});
              if (typeof obj["errorType"] !== "undefined") {
                if (obj["errorType"] === "repeat") {
                  $(td).attr({ repeat: true });
                } else if (obj["errorType"] === "error") {
                  $(td).attr({ error: true });
                }
              } else {
                $(td).css({
                  border: "1px solid #ccc",
                  color: "#999",
                  "white-space": "normal",
                  "word-break": "break-all"
                });
              }
              if(obj.originalValue){
                $(td).css({
                  'background-color':'#F1F9FF'
                })
              }
            };
          },
          cell:that.setCell(),
          stretchH: "all",
          width: "100%",
          autoWrapRow: true,
          autoRowSize: true,
          autoColumnSize: true,
          height: "600",
          maxRows: 1000,
          manualRowResize: false,
          manualColumnResize: false,
          // beforeKeyDown : function(e) {
          //     // 禁止選中列后delete鍵和回退鍵清空整列數據
          //     if (e.keyCode === 8 || e.keyCode == 46) {
          //         Handsontable.Dom.stopImmediatePropagation(e);
          //     }
          // },
          manualRowMove: true,
          manualColumnMove: true,
          contextMenu: true,
          filters: true,
          dropdownMenu: true
        },
        this.opt.set
      );
      this.opt.set.rowHeaders = function(index) {
        var repeatNum = 0;
        var errorNum =  0;
        if(that.allErrorNum[index] instanceof Array){
          for(var j=0;j<that.allErrorNum[index].length;j++){
            if(that.allErrorNum[index][j].type==='error'){
              errorNum+=1;
            }else{
              repeatNum+=1;
            }
          }
        }
        var html = "<span class='error-th' style='display:"+((that.allErrorNum[index]&&that.allErrorNum[index].length>0)?"block":"none")+"'></span>";
        html +=
          " <span class='error-content'>重復:" + repeatNum +
          ",錯誤:" + errorNum + "</span>";
        html += "<span id='column_name' style='padding-right:6px;'>" + (index + 1) + "</span>";
        return html;
      };
      this.opt.set.colHeaders = function(index) {
        var desc = that.header[index]+"規則:";
        var map = that.opt.dataObj.headMap[that.header[index]];
        if(map.minLength&&map.maxLength){
          desc=desc+'長度:'+map.minLength+'-'+map.maxLength;
        }else if(map.length){
          desc=desc+'長度:'+map.length
        }
        if(map.type){
          desc=desc+',類型:'+that.typeMap[map.type]
        }
        if(map.required){
          desc=desc+',必填'
        }
        if(map.unique){
          desc=desc+',不能重復'
        }
        var html = "<span class='remark-th'></span>";
        html += " <span class='remark-content'>"+desc+"</span>";
        if(map.required){
          html += "<span id='column_name' style='color:#ED5565'>" + that.header[index] + "</span>";
        }else{
          html += "<span id='column_name'>" + that.header[index] + "</span>";
        }
        return html;
      };
      this.table = new Handsontable(this.element, this.opt.set);
      this.table.updateSettings({
        contextMenu: {
          callback: function(key, options) {
            if (key === "about") {
              setTimeout(function() {
                // timeout is used to make sure the menu collapsed before alert is shown
                alert(
                  "This is a context menu with default and custom options mixed"
                );
              }, 100);
            }
          },
          items: {
            row_above: {
              name: "向上插入一行",
              disabled: function() {
                return that.table.getSelected()[0] === 0;
              }
            },
            remove_row: {
              name: "刪除選中行",
              disabled: function() {
                // if first row, disable this option
                return that.table.getSelected()[0] === 0;
              }
            }
          }
        }
      });
      window.onresize = this.opt.isValidate
        ? function() {
            that.render.call(that);
          }
        : null;
      this.table.addHook("afterChange", function() {
        that.opt.isValidate && that.render.call(that);
      });
      this.table.addHook("afterRemoveRow", function() {
        that.opt.isValidate && that.render.call(that);
      });
      var topValue = 0,leftValue = 0;
      var interval = null;
      $(this.opt.el + " .wtHolder")[0].onscroll = function() {
        if (interval == null) {
          interval = setInterval(isFinishScroll, 1000);
        }
      };
      function isFinishScroll() {
        // 判斷此刻到頂部的距離是否和1秒前的距離相等
        if ($(that.opt.el + " .wtHolder")[0].scrollLeft === leftValue && $(that.opt.el + " .wtHolder")[0].scrollTop === topValue) {
          clearInterval(interval);
          interval = null;
          that.validate();
          that.renderError(); 
          that.remarkShow();//渲染表頭、列頭標注
          if(that.position){
            $("td[row='"+that.errorPosition.row+"'][col='"+that.errorPosition.col+"']").attr('current');
          }
        } else {
          topValue = $(that.opt.el + " .wtHolder")[0].scrollTop;
          leftValue = $(that.opt.el + " .wtHolder")[0].scrollLeft;
        }
      }
      this.render();
      return this;
    },
    /*@method 渲染整個table數據
     */
    render: function() {
      var table = this.table;
      this.validate.call(this); //初始化,先驗證,並標記重復項
      table.render(); //並渲染在行首部
      this.renderError();
      this.remarkShow();//渲染表頭、列頭標注
      $(".htInvalid").removeClass('htInvalid');
      $("td[current]").removeAttr('current');
    },
    /*@method 渲染出表頭和列頭的標注信息
     */
    remarkShow:function(){
      $(".remark-th").hover(function(){
        var top = $(this).closest('th').offset().top;
        var left = $(this).closest('th').offset().left;
        $(this).next().css({'top':top,"left":left})
      });
      $(".error-th").hover(function(){
        var top = $(this).closest('th').offset().top;
        var left = $(this).closest('th').offset().left+10;
        $(this).next().css({'top':top,"left":left})
      })
    },
    /*@method 渲染出重復、錯誤的單元格
     */
    renderError: function() {
      var that = this;
       var rowHeaderTr = $(".ht_clone_left .htCore").eq(0).find("tbody").find("tr");
       var tr = $(this.opt.el + " .htCore").eq(0).find("tbody").find("tr");
      //渲染錯誤項(只渲染當前可視區域的),
      //此處天坑,行頭是單獨的table,之前滾動后渲染位置錯誤。
      for (var i = 0; i < tr.length; i++) {
        var rowNum =  $(tr[i]).find("th #column_name").text();
        rowNum = parseInt(rowNum) - 1;
        //統計每一行錯誤項、重復項,如果列數過多,則列會渲染不完全,所以不能用選擇器查出准確數據,只能使用統計出的數據
        var repeatNum = 0;
        var errorNum =  0;
        if(that.allErrorNum[rowNum] instanceof Array){
          for(var j=0;j<this.allErrorNum[rowNum].length;j++){
            if(this.allErrorNum[rowNum][j].type==='error'){
              errorNum+=1;
            }else{
              repeatNum+=1;
            }
          }
        }
        var errorTH = $(rowHeaderTr[i]).find("th .error-th").eq(0);
        if(repeatNum+errorNum>0){
          errorTH.css({'display':'block'});
          var top = errorTH.offset().top;
          var left = errorTH.offset().left+10;
          errorTH.next().css({'top':top,"left":left})
          errorTH.next().text('重復:'+repeatNum+',錯誤:'+errorNum);
        }else{
          errorTH.css({'display':'none'});
        }
        $(tr[i]).find("th .error-th,th .error-content").remove();//將另一處被渲染的行標注刪除,防止誤導
      }
      this.remarkShow();
    },
    /*@method 驗證
     */
    validate: function() {
      var table = this.table;
      var that = this;
      this.allErrorNum = [];//初始化統計錯誤array
      this.firstError = {
        isExist :false,
        row:0,
        col:0
       };//初始化,第一個錯誤位置改為不存在
      var cellLength = table.getDataAtRow(0).length;
      var errorRows = [];
      for (var i = 0; i < cellLength; i++) {
        var cellData = table.getDataAtCol(i);
       // var newArr = [];
        cellData.map(function(item, index, arr) {
          var NewCell;
          var validateRule = that.opt.dataObj.headMap[that.header[i]];
          var dataMap = that.opt.dataObj.dataMap[index][that.header[i]];
          //if (newArr.indexOf(index) === -1) {
            //如果被重復項最后一個索引已有渲染標志,則不刪除渲染標志。
            NewCell = table.getCell(index, i);
            $(NewCell).removeAttr("error");
            $(NewCell).removeAttr("repeat");
            dataMap["errorType"] = "";
          //}
          if (validateRule.required && !item) {
            dataMap["errorType"] = "error";
            $(NewCell).attr("error", true);
            that.setAllErrorNum.call(that,index,i,"error");
            errorRows.indexOf(index)===-1 && errorRows.push(index);
            return;
          }
          if(item){
            if (validateRule.type &&
              validateRule.type === "CODE_TYPE") {
              var list = that.getCodeValueList(validateRule.codeValueList);
              if(list.indexOf(item)===-1){
                dataMap["errorType"] = "error";
                $(NewCell).attr("error", true);
                that.setAllErrorNum.call(that,index,i,"error");
                errorRows.indexOf(index)===-1 && errorRows.push(index);
                return;
              }
            }
            if(validateRule.type && validateRule.type === "BOOLEAN"){
              if(['是','否'].indexOf(item)===-1){
                dataMap["errorType"] = "error";
                $(NewCell).attr("error", true);
                that.setAllErrorNum.call(that,index,i,"error");
                errorRows.indexOf(index)===-1 && errorRows.push(index);
                return;
              }
            }
            if (
              validateRule.type &&
              validateRule.type === "DATE"
            ) {
              var result = false;
              that.dateFormat.map(function(format,index){
                if(new Date(item).format(format)===item){
                  result = true;
                }
              })
              if(!result){
                dataMap["errorType"] = "error";
                $(NewCell).attr("error", true);
                that.setAllErrorNum.call(that,index,i,"error");
                errorRows.indexOf(index)===-1 && errorRows.push(index);
                return;
              }
            }
            if(validateRule.type &&
              validateRule.type === "TIME" && !that.timePattern.test(item)){
              dataMap["errorType"] = "error";
              $(NewCell).attr("error", true);
              that.setAllErrorNum.call(that,index,i,"error");
              errorRows.indexOf(index)===-1 && errorRows.push(index);
              return;
            }
            if(validateRule.type &&
              validateRule.type === "INTEGER" && parseInt(item)!=item){
                dataMap["errorType"] = "error";
                $(NewCell).attr("error", true);
                that.setAllErrorNum.call(that,index,i,"error");
                errorRows.indexOf(index)===-1 && errorRows.push(index);
                return;
            }
            if(validateRule.type &&
              validateRule.type === "DOUBLE" && parseFloat(item)!=item){
              dataMap["errorType"] = "error";
              $(NewCell).attr("error", true);
              that.setAllErrorNum.call(that,index,i,"error");
              errorRows.indexOf(index)===-1 && errorRows.push(index);
              return;
            }
            if (parseInt(validateRule.minLength) && item.length < parseInt(validateRule.minLength)) {
              dataMap["errorType"] = "error";
              $(NewCell).attr("error", true);
              that.setAllErrorNum.call(that,index,i,"error");
              errorRows.indexOf(index)===-1 && errorRows.push(index);
              return;
            }
            if (parseInt(validateRule.maxLength) && item.length > parseInt(validateRule.maxLength)) {
              dataMap["errorType"] = "error";
              $(NewCell).attr("error", true);
              that.setAllErrorNum.call(that,index,i,"error");
              errorRows.indexOf(index)===-1 && errorRows.push(index);
              return;
            }
            if ((!validateRule.maxLength && !validateRule.minLength&& validateRule.length) && item.length !== validateRule.length) {
              dataMap["errorType"] = "error";
              $(NewCell).attr("error", true);
              that.setAllErrorNum.call(that,index,i,"error");
              errorRows.indexOf(index)===-1 && errorRows.push(index);
              return;
            }
            if (validateRule.regexp) {
              var pattern = new RegExp(validateRule.regexp);
              if(!pattern.test(item)){
                dataMap["errorType"] = "error";
                $(NewCell).attr("error", true);
                that.setAllErrorNum.call(that,index,i,"error");
                errorRows.indexOf(index)===-1 && errorRows.push(index);
                return;
              }
            }
            if (validateRule.unique && that.getRepeatNum(arr,item,index)) {
              $(NewCell).attr("repeat", true);
              dataMap["errorType"] = "repeat";
              that.setAllErrorNum.call(that,index,i,"repeat");
              errorRows.indexOf(index)===-1 && errorRows.push(index);
            }
          }
        });
      }
      $(".htInvalid").removeClass('htInvalid');
      this.errorRow = errorRows.length;
    },
    /*@method 輔助方法,獲取table中第一個錯誤點的位置
     */
    setAllErrorNum:function(x,y,type){
      if(!this.firstError.isExist||this.firstError.row>x){
        this.firstError = {
          isExist:true,
          row:x,
          col:y
        };
      }
      if(!this.allErrorNum[x]){
       this.allErrorNum[x] = [];
      } 
      this.allErrorNum[x].push({
        type:type,
        row:x,
        col:y
      });
      //如果在已經定位的情況下,又修改了其他單元格到處出現新的數據,那么此處的錯誤定位要重新定位
      if(x === this.errorPosition.row && y === this.errorPosition.col){
        this.errorPosition.index = this.allErrorNum[x].length-1;
      }
    },
    /*@method 輔助方法,判斷數組中某一個值是否有重復項
     */
    getRepeatNum:function(arr,val,i){
      var result = false;
      arr.map(function(item,index,array){
        if(item===val && index!==i){
          result = true;
        }
      })
      return result;
    },
    /*@method 下一個錯誤調用方法
     */
    nextError: function() {
      if (this.position) {
        $("td[current]").removeAttr("current");
        this.position = "next";
        this.nextErrorPostion();
        this.isPosition = true;
        this.scrollToError();
      }
    },
    /*@method 上一個錯誤調用方法
     */
    prevError: function() {
      if (this.position) {
        $("td[current]").removeAttr("current");
        this.position = "prev";
        this.prevErrorPosition();
        this.isPosition = true;
        this.scrollToError();
      }
    },
    /*@method 計算出下一個錯誤單元格的位置
     */
    nextErrorPostion: function() {
      var length = this.allErrorNum[this.errorPosition.row].length;
      if (this.errorPosition.index < length - 1) {
        this.errorPosition.index += 1;
        this.errorPosition.col = this.allErrorNum[this.errorPosition.row][this.errorPosition.index].col;
      } else {
        var maxLength = this.allErrorNum.length;
        for (var i = this.errorPosition.row + 1; i < maxLength; i++) {
          if (this.allErrorNum[i] instanceof Array &&this.allErrorNum[i].length>0) {
            this.errorPosition = {
              index:0,
              row: i,
              col: this.allErrorNum[i][0].col
            };
            return;
          }
        }
        if (i === maxLength && (this.errorPosition.index === this.allErrorNum[i-1].length-1)) {
          this.errorPosition = {
            index:0,
            row: this.firstError.row,
            col: this.firstError.col
          };
        }
      }
    },
    /*@method 計算出上一個錯誤單元格的位置
     */
    prevErrorPosition: function() {
      var length = this.allErrorNum[this.errorPosition.row].length;
      if (this.errorPosition.index > 0) {
        this.errorPosition.index -= 1;
        this.errorPosition.col = this.allErrorNum[this.errorPosition.row][this.errorPosition.index].col;
      } else {
        for (var i = this.errorPosition.row - 1; i >= 0; i--) {
          if (this.allErrorNum[i] instanceof Array &&this.allErrorNum[i].length>0) {
            var len = this.allErrorNum[i].length-1;
            this.errorPosition = {
              index:this.allErrorNum[i].length-1,
              row: i,
              col: this.allErrorNum[i][len].col
            };
            return;
          }
        }
      }
    },
    /*@method 滾動到errorPosition記錄的位置
     */
    scrollToError: function() {
      var that = this;
      var tr = $(that.opt.el + " .htCore tbody").find("th #column_name:contains('"+(that.errorPosition.row+1)+"')").closest('tr');
      var lastTd = (tr.length>0)?tr.find("td[col='"+this.errorPosition.col+"']"):'';
      //滾動到對應行的位置,並且插件會自動渲染出新的可視區域,此時再找出定位的單元格,並標記
      $(this.opt.el + " .wtHolder").animate({ scrollTop: (this.errorPosition.row-5)*28 }, 300);
      lastTd = tr.find("td[col='"+that.errorPosition.col+"']");
      $(this.opt.el + " .wtHolder").animate({ scrollLeft: (this.errorPosition.col-1)*150 }, 300);
      setTimeout(function(){
      //此處因為是定時器,所以要注意tr和ladtTd會在定時器回掉函數開始執行時消失,所以要在定時器中重新定義,或者使用閉包
        var tr = $(that.opt.el + " .htCore tbody").find("th #column_name:contains('"+(that.errorPosition.row+1)+"')").closest('tr');
        lastTd = tr.find("td[col='"+that.errorPosition.col+"']");
        $(lastTd).attr("current",true);
        that.isPosition = false;
      },600)
    },
    /*@method 標記當前錯誤點
     */
    currentError: function(){
      if(!this.position){
        this.errorPosition.row = this.firstError.row;
        this.errorPosition.col = this.firstError.col;
      }
      this.isPosition = true;
      this.position = "current";
      this.scrollToError();
    },
    /*@method 獲取有多少條記錄是有錯誤的、正確的 
     *@return array [總記錄數,錯誤數,正確數]
     */
    getErrorNum:function(){
      var total = this.opt.dataObj.dataMap.length;
      return [total,this.errorRow,total-this.errorRow];
    },
    /*@method 獲取到編輯后的table數據
     */
    getData: function() {
      return this.opt.dataObj;
    }
  };
  $.fn.HandsontableExtend = function (opts) {
   var hand = new HandsontableExtend(opts);
   return hand.init(window);
  };
})

 

//修改源碼部分:
//第36508行,function showDatepicker(event)的部分,228,258分別為時間控件的高和寬
      if(this.TD.offsetTop+228>holder.offsetHeight){
        this.datePickerStyle.top = window.pageYOffset + offset.top - 228 + 'px';
      }else{
        this.datePickerStyle.top = window.pageYOffset + offset.top + (0, _element.outerHeight)(this.TD)  + 'px';
      }
      if(this.TD.offsetLeft+258>holder.offsetWidth){
        this.datePickerStyle.left = window.pageXOffset + offset.left - (0, _element.outerWidth)(this.TD) + 'px';
      }else{
        this.datePickerStyle.left = window.pageXOffset + offset.left + 'px';
      }
      //this.datePickerStyle.top = window.pageYOffset + offset.top + (0, _element.outerHeight)(this.TD) + 'px';
      //this.datePickerStyle.left = window.pageXOffset + offset.left + 'px';
//i18n,json對象改為
i18n:{
            previousMonth : '上一月',
            nextMonth     : '下一月',
            months        : ['一月','二月','三月','四月','五月','六月','七月','八月','九月','十月','十一月','十二月'],
            weekdays      : ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'],
            weekdaysShort : ['周日','周一','周二','周三','周四','周五','周六']
        }

 

 調用方法:

 
         
var handTableExtend = $("#hot").HandsontableExtend({
el:'#hot',
dataObj:$scope.dataObject,
isValidate:true,
set:{
height: 500
}
})
//dataObject接受的數據結構
dataObject = {
                headMap:{
                    '姓名':{
                        isRequired:true,
                        type:'string',
                        minLength:2,
                        maxlength:20,
                        isUnique:false
                    },
                    '性別':{
                        isRequired:false,
                        type:'string',
                        isUnique:false
                    },
                    '身份證號':{
                        isRequired:true,
                        type:'string',
                        minLength:9,
                        maxlength:20,
                        isUnique:true,
                        regexp:/^[1-9][0-9]{5}(19|20)[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|31)|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}([0-9]|x|X)$/
                    },
                    '聯系電話':{
                        isRequired:true,
                        type:'string',
                        //length:11,
                        isUnique:true
                    },
                    '地址':{
                        isRequired:false,
                        type:'string',
                        maxLength:50,
                        isUnique:false
                    },
                    '職位':{
                        isRequired:false,
                        type:'string',
                        isUnique:false
                    },
                    '部門':{
                        isRequired:false,
                        type:'code_type',
                        enumData:['it部','hr部','后勤部','銷售部'],
                        isUnique:false
                    },
                    '入職日期':{
                        isRequired:false,
                        type:'date',
                        isUnique:false
                    }
                },
                dataMap:[
                    {
                        '姓名':{
                            value:'張三',
                            errorType:''
                        },
                        '性別':{
                            value:'男',
                            errorType:''
                        },
                        '身份證號':{
                            value:'',
                            errorType:''
                        },
                        '聯系電話':{
                            value:'18817802351',
                            errorType:'repeat'
                        },
                        '地址':{
                            value:'上海浦東',
                            errorType:''
                        },
                        '職位':{
                            value:'it',
                            errorType:''
                        },
                        '部門':{
                            value:'it部',
                            errorType:''
                        },
                        '入職日期':{
                            value:'it部',
                            errorType:''
                        }
                    },{
                        '姓名':{value:'李四1',
                        errorType:''},
                        '性別':{value:'女',
                        errorType:''},
                        '身份證號':{value:'111222',
                        errorType:''},
                        '聯系電話':{value:'18817802351',
                        errorType:''},
                        '地址':{value:'上海浦東',
                        errorType:''},
                        '職位':{value:'it',
                        errorType:''},
                        '部門':{value:'it部',
                        errorType:''},
                        '入職日期':{value:'it部',
                        errorType:''}
                    },{
                        '姓名':{value:'李四1',
                        errorType:''},
                        '性別':{value:'女',
                        errorType:''},
                        '身份證號':{value:'111222',
                        errorType:''},
                        '聯系電話':{value:'18817802351',
                        errorType:''},
                        '地址':{value:'上海浦東',
                        errorType:''},
                        '職位':{value:'it',
                        errorType:''},
                        '部門':{value:'it部',
                        errorType:''},
                        '入職日期':{value:'it部',
                        errorType:''}
                    }
                ]
            }; 

頁面html代碼

<div ng-controller="SurveyHandsontable">
    <button id="prev" ng-click="prevError()">></button>
    <button id="next" ng-click="nextError()"><</button>
    <div id="hot"></div>
    <button ng-click="getData()">點擊</button>
</div>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM