最近被安排到一個新的項目裏,首先被分配了一個成果管理的模塊,雖然是簡單的增刪改查,可是也費了很多功夫。javascript
其中耽誤最長的時間就是form中嵌套了兩個可編輯列表的子表。廢話不說上乾貨 = =html
參考資料 1. http://bootstrap-table.wenzhixin.net.cn/documentation/ apijava
2. http://blog.csdn.net/lzxadsl/article/details/49181127ajax
//2017.10.17補充相關版本的bootstrap及edit.jsjson
連接: https://pan.baidu.com/s/1gflGSW7 密碼: ef5abootstrap
20180913 更新連接 api
連接: https://pan.baidu.com/s/1vPdFzr3XL7QKkGGP5bk_aQ 密碼: kq97數組
界面圖:緩存
jsp代碼:app
簡單的一個按鈕和一個普通的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); } }
好了,基本就是這些。