最近被安排到一个新的项目里,首先被分配了一个成果管理的模块,虽然是简单的增删改查,但是也费了不少功夫。
其中耽误最长的时间就是form中嵌套了两个可编辑列表的子表。废话不说上干货 = =
参考资料 1. http://bootstrap-table.wenzhixin.net.cn/documentation/ api
2. http://blog.csdn.net/lzxadsl/article/details/49181127
//2017.10.17补充相关版本的bootstrap及edit.js
链接: https://pan.baidu.com/s/1gflGSW7 密码: ef5a
20180913 更新链接
链接: https://pan.baidu.com/s/1vPdFzr3XL7QKkGGP5bk_aQ 密码: kq97
界面图:
jsp代码:
简单的一个按钮和一个普通的table
<div class="my_table_bar"> <div class="col-xs-10 bar_title"> <p align="center"> <strong>成果的知识产权情况</strong> </p> </div> <div class="col-xs-2 bar_option"> <a class="insert" href="javascript:addRow(2);" title="新增行"> <i class="glyphicon glyphicon-plus" id="editTable_add_kjcg"></i> 新增 </a> </div> </div> <table id="reportTable2" class="table table-bordered table-hover"></table>
js代码:
/* * @param 主表ID * 加载可编辑列表公共方法*/ function loadEditTable(pid){ //重置table内数据 $(\'#reportTable1\').bootstrapTable(\'destroy\');//这里必须要添加这个销毁,否则新增、修改、查看的切换可编辑列表中的数据可能加载出现问题。 $(\'#reportTable2\').bootstrapTable(\'destroy\'); $(\'#reportTable1\').bootstrapTable({ method: \'get\', url:contextPath + \'/cggl/getHJQKTable.do?code=\'+pid, editable:true,//开启编辑模式 clickToSelect: true, cache : false, columns: [ {field:"JX_NAME",title:"所获奖项明称",align:"center",edit:{required:true,type:\'text\' }}, {field:"JX_ORG",title:"奖励单位",align:"center",edit:{type:\'text\' }}, {field:"JX_TIME",title:"奖励年度",align:"center",edit:{type:\'text\' }}, {field:"JX_RANK",title:"授奖等级",align:"center",edit:{type:\'text\' }}, {field:"operate",title:"操作",align:"center",formatter : operateFormatter1,edit:false} ], onEditableHidden: function(field, row, $el, reason) { // 当编辑状态被隐藏时触发 if(reason === \'save\') { var $td = $el.closest(\'tr\').children(); $td.eq(-1).html((row.price*row.number).toFixed(2)); $el.closest(\'tr\').next().find(\'.editable\').editable(\'show\'); //编辑状态向下一行移动 } else if(reason === \'nochange\') { $el.closest(\'tr\').next().find(\'.editable\').editable(\'show\'); } }, onEditableSave: function (field, row, oldValue, $el) { $table = $(\'#reportTable1\').bootstrapTable({}); $table.bootstrapTable(\'updateRow\', {index: row.rowId, row: row}); } }); $(\'#reportTable2\').bootstrapTable({ method: \'get\', url:contextPath + \'/cggl/getZscqTable.do?code=\'+pid, editable:true,//开启编辑模式 clickToSelect: true, cache : false, columns: [ {field:"ZSCQ_XS",title:"知识产权形式",align:"center",edit:{required:true ,type:\'text\'}}, {field:"ZSCQ_NAME",title:"所获知识产权项目名称",align:"center",edit:{type:\'text\'}}, {field:"ZSCQ_CODE",title:"知识产权号",align:"center",edit:{type:\'text\'}}, {field:"ZSCQ_TIME",title:"授权时间",align:"center",edit:{type:\'text\'}}, {field:"operate",title:"操作",align:"center",formatter : operateFormatter2,edit:false} ], onEditableSave: function (field, row, oldValue, $el) { $table = $(\'#reportTable1\').bootstrapTable({}); $table.bootstrapTable(\'updateRow\', {index: row.rowId, row: row}); } }); }
PS:上面提到的destroy方法,如果不加的话,很多时候可编辑列表都是直接调用的缓存中的数据。举个例子,就是我先打开新增,可编辑列表中是没有数据的,再打开修改的话,可编辑列表同样会没有数据,很奇怪,但是具体的原因我也没有找到,ajax 中 cache也已经设置成了false。头大
java代码:
其实我感觉后台是没必要贴的,但是想想我在查阅资料的时候,网上找的资料大多数不全,所以换位思考下,我就全贴出来了。
这里有一个点就是我先把主表保存了,返回了插入数据的ID,然后用这个ID作为外键,将子表数据新增。
其他的话,修改我是先删除ID关联的子表所有数据,然后再新增。(这里如果有更好的方法记得@我,谢谢大神)
/** * 新增 * 包括对成果获奖情况和知识产权情况的保存 */ @SuppressWarnings("rawtypes") @RequestMapping("/add.do") public void addToCggl() { HttpServletRequest request = getRequest(); HttpServletResponse response = getResponse(); try { // 全路径对象或者表名 String className = request.getParameter(OBJECTID_PARAMETER).trim(); //获取对应的子表数据 String hjqk = request.getParameter("hjqk").trim(); String zscq = request.getParameter("zscq").trim(); // JDBC处理方式 Map entityValue = assembleParameterMap(request.getParameterMap()); entityValue.remove("objectId"); //将子表数据移除 entityValue.remove("hjqk"); entityValue.remove("zscq"); jdbcService.saveCommonObject(className, entityValue); String parentID = entityValue.get("ID").toString(); //调用子表的保存方法 saveEditTable(parentID,hjqk,zscq); String msg = MsgUtil.getI18NTipMessage(CoreTipDefine.ADDT_0001); commonResult(response, true, msg); } catch (Exception e) { e.printStackTrace(); String msg = MsgUtil.getI18NExpMessage(e); commonResult(response, false, msg); } }
子表传过来的值是json字符串。下面就是在java中对json字符串的转换,因为字段不多,所以也没用Gson。
List<Map<String, String>> list = new ArrayList<Map<String, String>>(); List<Map<String, String>> list1 = new ArrayList<Map<String, String>>(); // 首先把字符串转成 JSONArray 对象 JSONArray json = JSONArray.fromObject(hjqk); JSONArray json1 = JSONArray.fromObject(zscq); if (json.size() > 0) { for (int i = 0; i < json.size(); i++) { JSONObject job = json.getJSONObject(i); // 遍历 jsonarray // 数组,把每一个对象转成 json 对象 Map<String, String> map = new HashMap<String, String>(); // 得到 每个对象中的属性值 map.put("CG_ID", code); map.put("JX_NAME", job.get("JX_NAME").toString()); map.put("JX_ORG", job.get("JX_ORG").toString()); map.put("JX_TIME", job.get("JX_TIME").toString()); map.put("JX_RANK", job.get("JX_RANK").toString()); map.put("id", UUIDGenerator.generateUUID()); list.add(map); } }
最后是bootstrap-table-edit.js
/** * 可编辑表格插件 * 如果编辑表格中有用到,下拉框和日期,必须先引入 * bootstrap-select 和 bootstrap-datetimepicker两个控件 * @author lizx <851697971@qq.com> * @version 1.0 * @date 2015-10-13 */ (function($){ \'use strict\'; $.extend($.fn.bootstrapTable.defaults, { editable: false }); var BootstrapTable = $.fn.bootstrapTable.Constructor, _init = BootstrapTable.prototype.init, _initBody = BootstrapTable.prototype.initBody, _onSort = BootstrapTable.prototype.onSort, _append = BootstrapTable.prototype.append, _initHeader = BootstrapTable.prototype.initHeader ; //添加编辑表格默认属性,如何edit设置为false时,表示该列不可编辑 $.extend(true,BootstrapTable.COLUMN_DEFAULTS,{ edit:{ type:\'text\'//目前只支持 文本:text 下拉:select 日期:date } }); BootstrapTable.prototype.init = function () { _init.apply(this, Array.prototype.slice.apply(arguments)); var that = this; that.prevEditRow = null;//上一次编辑的行 that.columns = {};//列配置信息 that.insertRowVal = {};//新插入行的默认值 that.enableAppend = true;//允许添加新行 if (that.options.editable) { var columnObj = this[\'getColumns\'](); $.each(columnObj,function(i,obj){ $.each(obj,function(z,col){ if(!isNaN(col.fieldIndex) && col.fieldIndex >= 0){ if(col.checkbox)col.edit = false; that.columns[\'column\'+col.fieldIndex] = col; that.insertRowVal[col.field] = \'\'; } }); }); //this.initEdit(); } }; /*BootstrapTable.prototype.initHeader = function(){ _initHeader.apply(this, Array.prototype.slice.apply(arguments)); this.$container.find(\'.fixed-table-header\').addClass(\'success\'); };*/ BootstrapTable.prototype.initBody = function () { var that = this; _initBody.apply(this, Array.prototype.slice.apply(arguments)); if (!that.options.editable) return; this.initEdit(); //如果列是下拉框,则转换值为对应的文本 $.each(that.columns,function(indx,col){ if(col.edit && col.edit.type == \'select\'){ col.edit = $.extend({},$.fn.bootstrapSelect.defaults,col.edit); if(col.edit.data.length > 0){ that.$body.find(\'>tr\').each(function(){ if(that.getData().length < 1)return true; var rowData = that.data[$(this).data(\'index\')];//当前点击td所在行的数据 var $td = $(this).find(\'td\').eq(col.fieldIndex); $.each(col.edit.data,function(i,data){ if(data[col.edit.valueField] == rowData[col.field]){ $td.html(data[col.edit.textField]); } }); }); } else if(col.edit.url){ $.ajax({ url:col.edit.url, type:\'post\', data:col.edit.paramsType == \'json\' ? JSON.stringify(col.edit.params) : col.edit.params, dataType:\'json\', success: function(jsonLst) { col.edit.onLoadSuccess.call(this,jsonLst); that.$body.find(\'>tr\').each(function(){ if(that.getData().length < 1)return true; var rowData = that.data[$(this).data(\'index\')];//当前点击td所在行的数据 var $td = $(this).find(\'td\').eq(col.fieldIndex); $.each(jsonLst,function(i,data){ if(data[col.edit.valueField] == rowData[col.field]){ $td.html(data[col.edit.textField]); } }); }); col.edit.data = jsonLst; col.edit.url = null; }, error: function(xhr, textStatus, errorThrown){ col.edit.onLoadError.call(this); col.edit.data = []; col.edit.url = null; throw col.field+\' 列下拉框数据加载失败\'; } }); } } }); }; //根据行号删除指定行 BootstrapTable.prototype.removeRow = function (rowNum) { var that = this; var len = that.options.data.length; if (isNaN(rowNum)){ return; } if(that.$body.find(\'.editable-select\').data(\'index\') != rowNum){ recover(that); } //删除数据 that.options.data.splice(rowNum,1); if (len === that.options.data.length){ return; } var oldClass = {};//保存被标记修改的样式 that.$body.find(\'>tr\').each(function(indx){ if($(this).hasClass(\'editable-modify\')){ if(indx > rowNum){ oldClass[indx-1] = \'editable-modify\'; } else{ oldClass[indx] = \'editable-modify\'; } } }); //this.prevEditRow = null; //this.$body.find(\'>tr\').removeClass(\'editable-select\'); that.initBody(); //将标记改变过行的样式从新设置回去 for(var key in oldClass){ that.$body.find(\'>tr\').eq(key).addClass(oldClass[key]); } //this.initEdit(); //没有数据时给提示加上样式 if(that.getData().length < 1){ that.$body.find(\'>tr\').addClass(\'no-records-found\'); } }; BootstrapTable.prototype.append = function (){ var that = this; //if(!that.enableAppend)return; var oldClass = {};//保存被标记修改的样式 that.$body.find(\'>tr\').each(function(indx){ if($(this).hasClass(\'editable-modify\') || $(this).hasClass(\'editable-insert\')){ oldClass[indx] = \'editable-modify\'; } }); arguments[0] = $.extend({},that.insertRowVal,arguments[0]); _append.apply(this,Array.prototype.slice.apply(arguments)); if (that.options.editable){ //that.initEdit(); setTimeout(function (){ //将标记改变过行的样式从新设置回去 for(var key in oldClass){ that.$body.find(\'>tr\').eq(key).addClass(oldClass[key]); } that.$body.find(\'>tr:last\').addClass(\'editable-modify\'); that.$body.find(\'>tr:last\').addClass(\'editable-insert\');//双重保险,防止在快速点击添加时,为给新增行设置editable-modify属性 that.$body.find(\'>tr:last\').click(); },60); } }; BootstrapTable.prototype.onSort = function () { _onSort.apply(this, Array.prototype.slice.apply(arguments)); var that = this; if (that.options.editable) { this.initEdit(); } }; BootstrapTable.prototype.getData = function () { return (this.searchText || this.searchCallback) ? this.data : this.options.data; }; //获取全部的数据 BootstrapTable.prototype.getAllData = function () { return this.options.data; }; BootstrapTable.prototype.getColumns = function () { return this.options.columns; }; /** * 获取有被修改过行的值 */ BootstrapTable.prototype.getModiDatas = function (){ var that = this; var datas = []; that.$body.find(\'.editable-modify\').each(function(){ if(that.data[$(this).data(\'index\')]){ datas.push(that.data[$(this).data(\'index\')]); } }); return datas; }; /** * 获取指定列的和,参数为列下标 */ BootstrapTable.prototype.getColTotal = function (num){ var retVal = 0; this.$body.find(\'>tr\').each(function(){ var colNum = 0; if($(this).hasClass(\'editable-select\')){ colNum = $(this).find(\'td\').eq(num).find(\'input\').val(); } else{ colNum = $(this).find(\'td\').eq(num).html(); } if(!isNaN(colNum)){//是数字才做想加 retVal += Number(colNum); } }); return retVal; }; /** * 创建可编辑表格 */ BootstrapTable.prototype.initEdit = function(){ var that = this, data = this.getData(); //this.$body.find(\'> tr\').unbind(\'click\').on(\'click\' //this.$body.delegate(\'>tr\',\'click\' this.$body.find(\'> tr\').unbind(\'click\').on(\'click\',function(){ var $tr = $(this); if($tr.hasClass(\'editable-select\') || data.length < 1 || $tr.hasClass(\'no-records-found\')){ return; } $tr.removeClass(\'no-records-found\'); recover(that); that.prevEditRow = $tr; $tr.addClass(\'editable-select\');//给当前编辑行添加样式,目前样式为空只做标识使用 that.$body.find(\'> tr\').not(this).removeClass(\'editable-select\'); $tr.find(\'td\').closest(\'td\').siblings().html(function(i,html){ initTrClick(that,this); }); }); //鼠标点击事件 $(document).bind(\'mousedown\',function(event){ var $target = $(event.target); if(!($target.parents().andSelf().is(that.$body)) && !($target.parents().andSelf().is($(\'.datetimepicker\')))){ setTimeout(function () { recover(that); //that.prevEditRow = null; //that.$body.find(\'> tr\').removeClass(\'editable-select\'); },10); }; }); }; $.fn.bootstrapTable.methods.push(\'getColumns\', \'getModiDatas\',\'removeRow\',\'getColTotal\',\'getAllData\'); /** * 给tr添加点击事件 */ function initTrClick(that,_this){ that.enableAppend = true; var $td = $(_this); var $tr = $td.parent(); var rowData = that.data[$tr.data(\'index\')];//当前点击td所在行的数据 var tdIndex = $tr.children().index($td);//当前点击的td下标 var tdOpt = that.columns[\'column\'+tdIndex]; if(rowData.IS_TYPING!="0"&&rowData.IS_TYPING!="2"){//判断接口取数方式 if(!tdOpt.edit || typeof tdOpt.edit != \'object\'){ return ; } $td.data(\'field\',tdOpt.field); if(!$td.data(\'oldVal\')){ $td.data(\'oldVal\',$.trim(rowData[tdOpt.field])); } var height = $td.innerHeight() - 3; var width = $td.innerWidth() - 2; $td.data(\'style\',$td.attr(\'style\'));//保存原来的样式 $td.attr(\'style\',\'margin:0px;padding:1px!important;\'); var placeholder = \'\'; if(tdOpt.edit.required == true){ placeholder = \'必填项\'; } var value = rowData[tdOpt.field] == null || rowData[tdOpt.field] == \'\'?\'\':rowData[tdOpt.field]; $td.html(\'<div style="margin:0;padding:0;overflow:hidden;border:solid 0px red;height:\'+(height)+\'px;width:\'+(width)+\'px;">\' +\'<input type="text" placeholder="\'+placeholder+\'" value="\'+value+\'" style="margin-left: 0px; margin-right: 0px; padding-top: 1px; padding-bottom: 1px; width:100%;height:100%">\' +\'</div>\'); $td.width(width); var $input = $td.find(\'input\'); if(!tdOpt.edit.type || tdOpt.edit.type == \'text\'){ if(tdOpt.edit[\'click\'] && typeof tdOpt.edit[\'click\'] === \'function\'){ $input.unbind(\'click\').bind(\'click\',function(event){ tdOpt.edit[\'click\'].call(this,event); }); } if(tdOpt.edit[\'focus\'] && typeof tdOpt.edit[\'focus\'] === \'function\'){ $input.unbind(\'focus\').bind(\'focus\',function(event){ tdOpt.edit[\'focus\'].call(this,event); }); } $input.unbind(\'blur\').on(\'blur\',function(event){ if(tdOpt.edit[\'blur\'] && typeof tdOpt.edit[\'blur\'] === \'function\'){ tdOpt.edit[\'blur\'].call(this,event); } }); /*$input.unbind(\'keyup\').on(\'keyup\',function(event){ debugger; var reg=new RegExp("/[^\d{1,}\.\-\d{1,}|\d{1,}]/g"); if(!reg.test($input.val())){ $input.val($input.val().replace(/[^\d{1,}\.\-\d{1,}|\d{1,}]/g, "")); } });*/ } else if(tdOpt.edit.type == \'select\'){ $input.bootstrapSelect(tdOpt.edit); } else if(tdOpt.edit.type == \'date\'){ $td.html(\'<div style="margin:0;padding:0;overflow:hidden;border:solid 0px red;height:\'+(height)+\'px;width:\'+(width)+\'px;" class="input-group date form_datetime" data-link-field="dtp_editable_input">\' +\'<input class="form-control" type="text" value="\'+value+\'">\' +\'<span class="input-group-addon"><span class="glyphicon glyphicon-th"></span></span>\' +\'</div>\' +\'<input type="hidden" id="dtp_editable_input" value="\'+value+\'"/>\' ); that.$body.find(\'.form_datetime\').datetimepicker({ weekStart: 1, todayBtn: 1, autoclose: 1, todayHighlight: 1, startView: 2, forceParse: 0, language:\'zh-CN\', format: \'yyyy-mm-dd hh:ii:ss\', pickerPosition: \'bottom-left\', showMeridian: 1 }); } }else{ return; } } /** * 恢复tr,使之处于不可编辑状态 */ function recover(that){ var isModi = false;//判断行值是否变动过 if(that.prevEditRow != null){ that.prevEditRow.find(\'td\').closest(\'td\').siblings().html(function(i,html){ $(this).attr(\'style\',$(this).data(\'style\')); var textVal = $(this).find(\'input[type="text"]\').val(); var hiddenVal = $(this).find(\'input[type="hidden"]\').val(); // if(typeof $(this).find(\'input[type="text"]\').bootstrapSelect(\'getText\') != \'object\'){ if(typeof $(this).find(\'input[type="text"]\') != \'object\'){ $(this).find(\'input[type="text"]\').bootstrapSelect(\'destroy\'); } if(textVal != undefined){ if($(this).data(\'oldVal\') != (hiddenVal?hiddenVal:$.trim(textVal)) && $(this).data(\'field\')) { that.data[that.prevEditRow.data(\'index\')][$(this).data(\'field\')] = hiddenVal?hiddenVal:$.trim(textVal); isModi = true; } if(that.columns[\'column\'+i].edit.required == true){ if(textVal == null || textVal == \'\'){ that.enableAppend = false; return \'<span style="color:red;">必填项不能为空</span>\'; } } return $.trim(textVal); } }); //新值跟旧值不匹配证明被改过 if(isModi || that.prevEditRow.hasClass(\'editable-insert\')){ that.prevEditRow.addClass(\'editable-modify\'); } else{ that.prevEditRow.removeClass(\'editable-modify\'); } that.prevEditRow = null; that.$body.find(\'> tr\').removeClass(\'editable-select\'); } } })(jQuery);
2017/08/03
对新增,删除调用方法的补充
function operateFormatter1(value, row, index) { return [ "<a class=\"remove\" href=\'javascript:removeRow("+index+",1)\' title=\"删除改行\">", "<i class=\'glyphicon glyphicon-remove\'></i>", "</a> " ].join(\'\'); } function operateFormatter2(value, row, index) { return [ "<a class=\"remove\" href=\'javascript:removeRow("+index+",2)\' title=\"删除改行\">", "<i class=\'glyphicon glyphicon-remove\'></i>", "</a> " ].join(\'\'); } $(\'#reportTable1\',\'#reportTable2\').on( \'click\', \'td:has(.editable)\', function (e) { //e.preventDefault(); e.stopPropagation(); // 阻止事件的冒泡行为 $(this).find(\'.editable\').editable(\'show\'); // 打开被点击单元格的编辑状态 } ); //可编辑列表新增一行 function addRow(mark){ var rows = []; //通过mark来判断为哪个可编辑框创建新一行 if(mark == 1){ $(\'#reportTable1\').bootstrapTable(\'append\',rows); }else if(mark == 2){ $(\'#reportTable2\').bootstrapTable(\'append\',rows); } } //删除指定行 function removeRow(deleteIndex,mark){ if(mark == "1"){ $(\'#reportTable1\').bootstrapTable(\'removeRow\', deleteIndex); }else if(mark == "2"){ $(\'#reportTable2\').bootstrapTable(\'removeRow\', deleteIndex); } }
好了,基本就是这些。