前言

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

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

写好后的控件演示地址

目标

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

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

使用

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

<?php
$data = array('rows'   =>  array(array('id'=>1,'username'=>'lvyahui','email'=>'lvyahui@163.com','phone'=>'9999','group'=>'组1','group_id'=>1),array('id'=>2,'username'=>'lvyahui','email'=>'lvyahui@163.com','phone'=>'9999','group'=>'组2','group_id'=>2),array('id'=>3,'username'=>'lvyahui','email'=>'lvyahui@163.com','phone'=>'9999','group'=>'组1','group_id'=>1),array('id'=>4,'username'=>'lvyahui','email'=>'lvyahui@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','lvyahui@163.com']},phone :{validator  :    function(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    :   'lvyahui',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);

转载于:https://www.cnblogs.com/lvyahui/p/4891483.html

表格行内编辑增删改查相关推荐

  1. bootstrap table 表格支持shirt 多选_bootstrap-table 表格行内编辑实现

    这篇文章向大家介绍一下如何使用bootstrap table插件实现表格的行内编辑功能. 我的web前端学习交流群点击进入1045267283,欢迎加入! 先放一张效果图: 应用场景 之前的项目也是采 ...

  2. python测试开发django-173.bootstrap实现table表格行内编辑

    前言 网上看了很多基于bootstrap的table表格行内编辑,需要基于bootstrap-table,bootstrap-table-edit,x-editable等插件,写的很复杂. 我想实现的 ...

  3. Datatables实现表格行内编辑功能

    表格行内编辑功能通过操作DOM来实现,最终实现效果如下代代码 html <table class="table table-striped table-bordered table-h ...

  4. datatables表格行内编辑的实现

    Datatables是一款jquery表格插件,它是一个高度灵活的工具,灵活就意味着很多功能需要自己去实现,比如说行内编辑功能. Datatables自己是没有行内编辑功能的,最简单的是通过modal ...

  5. oracle SQL 命令行(三.增删改查)

    SQL> select * from stu;SON SNAME SAGE -------- -------------------- ---------- 1001 张三 18 1003 李四 ...

  6. 使用ABSL(ABAP Script Language)完成SAP Cloud for Customer里Customer Quote以及行项目的增删改查

    The user roles are Studio Administrator, Developer, and Business User. PDI_ADMINISTRATION / Administ ...

  7. editable组件_表格行内编辑事件

    1.简介 x-editable组件是一个适用于bootstrap(目前只更新到bootstrap3),jquery,jquery UI三种风格样式的弹出框编辑插件.本文根据项目需求主要介绍它在boot ...

  8. Mysql —— C语言链接mysql数据库,命令行形式(getopt()函数),用户、用户组增删改查(用户组表内有用户控制的策略字段)

    函数说明--getopt(): 函数说明 getopt()用来分析命令行参数.参数argc和argv分别代表参数个数和内容,跟main()函数的命令行参数是一样的. optstring中的指定的内容的 ...

  9. vue+element实现树状表格的增删改查;使用el-table树形数据与懒加载实现树状表格增删改查

    以下代码可以直接复制使用 一.情景: 列表是一个树状表格,可以无限添加下级,以及对列表的某一行进行增删改查(目前查没有写). 原博链接 二.本篇是在原博主的代码基础上添加了部分功能. 功能1: 给树状 ...

最新文章

  1. C# typeof Gettype is as 拆箱 装箱
  2. torch拼接合并 cat优化记录
  3. C++代码片段(四)萃取模板类的模板参数类型
  4. httpclient封装获取响应实体_Httpclient 接口自动化
  5. mysql与citespace_CiteSpace与MySQL数据库的连接-科学网—博客.PDF
  6. 地方政府大数据发展的现实与理想
  7. 32位与64位、单精度(single-precision)与双精度(double-precision)
  8. linux内存——/proc/sys/vm/drop_caches
  9. python的集合类型_python集合类型
  10. tensorflow支持python3.7吗_前端开发行业真的会被AI取代吗?
  11. 您知道数据中心的最佳温度是多少吗?
  12. 手机输入法,谁能笑到最后?
  13. 微信小程序样式-id选择器的使用教程
  14. 笔迹心理学(2): 功能设计
  15. 北京程序员小哥哥的故事
  16. 热门好用的二维码生成器API
  17. 克隆虚拟机后无法连接网络的问题
  18. Mothur5进阶_Mothur扩增子基因序列分析_基于OTU或ASV的多样性指数分析
  19. token令牌的含义和操作
  20. 华为鸿蒙os对比安卓系统,华为手机鸿蒙OS 2.0、EMUI 11界面对比:安卓底层没了

热门文章

  1. c语言模拟java面向对象_面向对象设计模式C语言实现.PDF
  2. 三极管工作原理_4种集电极-基极负反馈式三极管偏置电路的工作原理分析
  3. spring boot-The temporary upload location [ ] is not valid
  4. 【thymeleaf】th:text、[[]]、th:utext、[()]输出变量
  5. maven 引入net.sf.json-lib依赖时报错(classifier)
  6. java join yield_java中join和yield有什么区别?
  7. 图片怎么等比缩放_mac图像缩放工具Teorex iResizer
  8. ios 持续获取定位 高德地图_概述-iOS 定位SDK | 高德地图API
  9. php代码实现对word文件的查找与替换,ThinkPHP5使用phpword实现文件模板字符替换
  10. linux如何判断光盘是否挂载,LInux下如何挂载光盘找rpm包的方法步骤