https://www.jmqz1000.com

表格行内编辑增删改查

  • 前言

    最近在做一个OA系统,需要将大量excel中的数据录入,并且希望以后新建数据时,也能像excel那样方便。并且这个后台系统有非常多这样的表单。因此需要做一个内敛的表单控件。

    上网找到一款编辑起来非常方便的控件handsontable,这个表单控件可以支持excel中的多种操作,比如多行复制粘贴,ctrl+z撤销,ctr+r重做等等。但是这个插件对数据的提交和数据的校验做的并不好。于是自己对这个插件进一步封装成为一个控件。

    写好后的控件演示地址

    目标

    控件希望至少做到的下几点

    当某一行有单元格发生变化时,自动校验这一行的数据,如果校验成功,将数据post到一个save url,post能绑定固定参数。 但有多行发生变化时,依次校验各行,依次提交校验通过的数据。 当最后一行数据发生变化时,不管是否已经提交,都立即在后面追加一行。 当保存的行是新建的时候,要将响应的记录ID更新到table上。 绑定table.render刷新事件,但刷新时间被触发时,重新渲染列表。 绑定add.table_id事件,事件将绑定一行新的数据(由触发的时候传递过来),响应事件时,将这行新的记录追加到表格中。 选择多行快速删除,get一个请求传递参数ids(逗号分隔id) 使用

    首先看看如何使用这个控件,只需要像下面这样

    <?php
    $data = array(
            'rows'   =>  array(
                    array('id'=>1,'username'=>'lvyahui','email'=>'devlyh@163.com','phone'=>'9999','group'=>'组1','group_id'=>1),
                    array('id'=>2,'username'=>'lvyahui','email'=>'devlyh@163.com','phone'=>'9999','group'=>'组2','group_id'=>2),
                    array('id'=>3,'username'=>'lvyahui','email'=>'devlyh@163.com','phone'=>'9999','group'=>'组1','group_id'=>1),
                    array('id'=>4,'username'=>'lvyahui','email'=>'devlyh@163.com','phone'=>'9999','group'=>'组3','group_id'=>3),
            ),
    );
    
    ?>
    
    <div id="dataTable">
    
    </div>
    {{HTML::script('js/handsontable.full.min.js')}}
    {{HTML::style('css/handsontable.full.min.css')}}
    {{HTML::script('js/editTable.js')}}
    <script>
        $('#dataTable').initEdit({
            rows    :  JSON.parse('<?=json_encode($data['rows'])?>'),
            colHeaders :   ['ID','用户名','邮箱','电话号码'],
            columns :   {
                id  :   {
                    readOnly    :   true
                },
                username : {
                    label   :   '用户名',
                    required    :   true,
                    validator  :    /^\w+$/
                },
                email :{
                    required    :   true,
                    //validator  :    /^\w+$/
                    editor: 'select',
                    selectOptions : ['lvyahui8@126.com','devlyh@163.com']
                },
                phone :{
                    validator  :    function(html' target='_blank'>value,callback){
                        //return value.length > 1;
                        callback(true);
                        return true;
                    },
                    allowVaild  :   true
                },
                group  :    {
                    required    :   true,
                    editor: 'select',
                    selectOptions : ['组1','组2','组3']
                }
            },
    
            bindData    :   {
                cus :   1,
                category_id :   2
            },
    
            beforeSave  :   function(data){
                var groups = [{name:'组1',id:1},{name:'组2',id:2},{name:'组3',id:3}];
                var has = groups.filter(function(item){
                   if(item.name == data.group){
                       return true;
                   }else{
                        return false;
                   }
                });
                if(has.length > 0){
                    data.group_id = has[0].id;
                }
                return data;
            },
    
            afterSave   :   function(resp){
    
            },
    
            url         :   {
                save    :   '<?=URL::to('test/table-row')?>',
                delete  :   '<?=URL::to('test/table-delete')?>'
            }
        });
        $('body').trigger('add.dataTable',{
            id  :   111,
            username    :   'devlyh',
            email   :   'lvyahui@126.com',
            phone   :   '100000',
            gourp_id    :   1
        });
    
    </script>

    上面的代码体现了设计思路,提交数据的时候,提交的是rows的某一行,显示的时候,只显示columns中有的属性。对于关系型数据,可以在提交数据之前将关系数据绑定提交。最下面触发的add.table_id(之所以事件跟一个table_id是为了保证一个页面有多个这个table的时候不冲突)事件,新增了一条数据。

    你就可以看到生成了这样一个表格。

    下面以一个实际的例子为例

     1 <div class="table" id="editTable"></div>
     2 {{HTML::script('js/handsontable.full.min.js')}}
     3 {{HTML::style('css/handsontable.full.min.css')}}
     4 {{HTML::script('js/editTable.js')}}
     5 
     6 <script>
     7     $('#editTable').initEdit({
     8         rows    :  [<?= implode(",",array_map(function($item){
     9             return "{id:$item->id,serial:'$item->serial',name:'$item->name',store:'$item->store',ship_time:'$item->ship_time',number:$item->number}";
    10         },$model->items->all()));?>],
    11         columns :   {
    12             id  :   {
    13                 label       :   'ID',
    14                 readOnly    :   true
    15             },
    16             store : {
    17                 label   :   'PO #',
    18                 required:   true
    19             },
    20             serial : {
    21                 label   :   '产品ID',
    22                 required    :   true
    23             },
    24 
    25             name :{
    26                 label       :   '产品名称',
    27                 required    :   true
    28             },
    29 
    30             description: {
    31                 label       : '产品描述',
    32                 required    : false
    33             },
    34 
    35             ship_time   :   {
    36                 label       :   '发货截止时间',
    37                 required    :   true
    38             },
    39             number  :    {
    40                 label       :   '数量',
    41                 required    :   true
    42             }
    43         },
    44         bindData    :   {
    45             customer_orders_id :   '<?=$model->id?>'
    46         },
    47         url         :   {
    48             save    :   '<?=URL::to('customerOrderItem/edit')?>',
    49             delete  :   '<?=URL::to('customerOrderItem/delete')?>'
    50         }
    51     });
    52 </script>
    效果

    生成的表单就像这样

    下面来批量新建,可以看到但出现两行符合要求是,向后台post了两个请求,请求响应了成功,将ID更新到单元格

    修改单元格

    触发add.table_id事件,向表格添加一行数据

     1 $("body").delegate(".select-item", "click", function (e) {
     2     var m = $("#add-product");
     3     $.get($(this).attr('href')+'&type='+$that.data('type'),function(resp){
     4         console.log(resp);
     5         resp.number = resp.number || 0;
     6         $('body').trigger('add.'+$that.data('table'),resp);
     7         m.modal('hide');
     8     },"json");
     9     return false;
    10 });

    触发刷新

    $('.order-item').one('shown.bs.collapse',function(){
        $('div.edit-table').trigger('table.render');
    });

    多行删除,这里因为后台还没做,所以会报错,但是数据时请求到了delete url上的

    代码

    下面是这个控件的核心代码。

      1 /*
      2 * editTable.js
      3 */
      4 ;(function($,global){
      5 
      6     var objToArray = function(obj){
      7         var arr = [];
      8         for(var x in obj){
      9             arr.push(obj[x]);
     10         }
     11         return arr;
     12     },
     13         requiredRender = function(hot, td, row, col, prop, value, cellProperties){
     14             Handsontable.renderers.TextRenderer.apply(this, arguments);
     15             td.className += 'required';
     16             //td.style.backgroundColor = 'yellow';
     17         };
     18 
     19     var ExcelTable = function(element,options){
     20         var that = this;
     21 
     22         this.element = element;
     23         this.hot = null;
     24         this.edit = null;
     25         this.defaults = {
     26             bindData    :   {},
     27             rows        :   [],
     28             url         :   {
     29                 save    :   '',
     30                 delete  :   ''
     31             },
     32             columns     :   {},
     33             tableClassName : '',
     34             afterSave   :   function(resp){},
     35             beforeSave  :   function(data){}
     36         };
     37 
     38         this.options = $.extend({},this.defaults,options);
     39         this.rows = this.options.rows;
     40         //this.cols = objToArray(that.options.columns);
     41         var colHeaders = [],
     42             columns = [];
     43         for(var x in this.options.columns){
     44             if(this.options.columns[x].hasOwnProperty('label')){
     45                 colHeaders.push(this.options.columns[x].label);
     46             }else{
     47                 colHeaders.push(x);
     48             }
     49             columns.push($.extend({data:x},this.options.columns[x]));
     50         }
     51         this.cols = columns;
     52         var hotOptions = {
     53             data        :   options.rows,
     54             colHeaders  :   colHeaders,
     55             afterChange :   function(changes,source){
     56                 if(source !== 'loadData'){
     57                     that.save(changes);
     58                 }
     59             },
     60             beforeRemoveRow:function(index,amount){
     61                 that.delete(index,amount);
     62             },
     63             columns     :  columns ,
     64             minSpareRows:   1,
     65             contextMenu: ['remove_row'],
     66             cells: function (row, col, prop) {
     67                 if(col < that.cols.length && that.cols[col].required){
     68                     this.renderer = requiredRender;
     69                 }
     70             },
     71             tableClassName  :   this.options.tableClassName,
     72             width   :   '100%',
     73             stretchH: "all",
     74             colWidths   :   this.options.colWidths
     75         };
     76 
     77         if(typeof Handsontable === "function"){
     78             this.hot = new Handsontable(this.element,hotOptions);
     79         }
     80 
     81         $('body').bind('add.'+$(element).attr('id'),function(e,row){
     82             that.rows.splice(that.rows.length-1,0,row);
     83             that.hot.render();
     84         });
     85         $(element).bind('table.render',function(){
     86             that.hot.render();
     87         });
     88     };
     89 
     90     ExcelTable.prototype = {
     91         constructor :   ExcelTable,
     92         post    :   function(data){
     93             var that = this;
     94             var ret = this.options.beforeSave(data);
     95             if(typeof ret === "object"){
     96                 data = ret;
     97             }
     98             if(data.id){
     99                 data.action = 'edit';
    100             }else{
    101                 data.id = '';
    102                 data.action = 'edit';
    103             }
    104             if(this.options.url.save){
    105                 $.post(this.options.url.save,data,function(resp){
    106                     if(!data.id && resp.id !== null){
    107                         // 新建了记录,重新渲染
    108                         that.options.rows[resp.index].id = resp.id;
    109                         that.hot.render();
    110                     }
    111                     that.options.afterSave(resp);
    112                 },'json');
    113             }
    114         },
    115         getDelete   :   function(ids){
    116             if(this.options.url.delete){
    117                 $.get(this.options.url.delete+'?id='+ids,function(resp){
    118 
    119                 });
    120             }
    121         },
    122         save    :   function(cells){
    123             var that = this,
    124                 rows = [];
    125             cells.forEach(function(cell){
    126                 if(cell[2] !== cell[3]){
    127                     rows[cell[0]] = cell[0];
    128                 }
    129             });
    130             rows.forEach(function(rowIndex){
    131                 var row = that.rows[rowIndex],
    132                     data = $.extend(row,that.options.bindData);
    133                 data.index = rowIndex;
    134                 if(that.validate(row)){
    135                     console.log(data);
    136                     that.post(data);
    137                 }
    138             });
    139         },
    140         delete  :   function(start,amount){
    141             var ids = [];
    142             for(var x = start;x < start + amount;x++){
    143                 ids.push(this.rows[x].id);
    144             }
    145             this.getDelete(ids.join(','));
    146         },
    147         validate   :   function(row){
    148             var that = this;
    149             return that.cols.filter(function(col,index){
    150                     if(row.hasOwnProperty(col.data)){
    151                         var valitator = that.hot.getCellValidator(0,index);
    152                         if(col.required){
    153                             if(!row[col.data]) return true;
    154                             if(valitator){
    155                                 return !that.execValidator(valitator,row[col.data]);
    156                             }
    157                             return false;
    158                         }else if(row[col.data] && valitator){
    159                             return !that.execValidator(valitator,row[col.data]);
    160                         }else{
    161                             return false;
    162                         }
    163                     }
    164                     else{
    165                         return false;
    166                     }
    167                 }).length == 0;
    168         },
    169         execValidator:function(validator,value){
    170             if(validator instanceof RegExp === true){
    171                 return validator.test(value);
    172             }else if(typeof  validator === "function"){
    173                 return validator(value,function(){});
    174             }else{
    175                 return false;
    176             }
    177         },
    178 
    179         isEmptyRow  :   function(rowIndex){
    180             var rowData = this.hot.getData()[rowIndex];
    181 
    182             for (var i = 0, ilen = rowData.length; i < ilen; i++) {
    183                 if (rowData[i] !== null) {
    184                     return false;
    185                 }
    186             }
    187             return true;
    188         }
    189     };
    190 
    191     $.fn.initEdit = function(options){
    192         return this.each(function(){
    193             var excelTable = new ExcelTable(this,options);
    194         });
    195     }
    196 
    197 })($ || jQuery,window);
    PHP编程

    郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。

  • 相关文章阅读