chuanqiMa

  最近被安排到一个新的项目里,首先被分配了一个成果管理的模块,虽然是简单的增删改查,但是也费了不少功夫。

其中耽误最长的时间就是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);
    }
}

 

好了,基本就是这些。

分类:

技术点:

相关文章: