目录

一、业务场景

二、分析问题

三、bootstrap实现就地编辑

1、使用x-editable

简介

使用

2、使用bootstrap-table-editor

简介

使用

四、用一个数组保存就地编辑的结果

1、定义一个数组存保存结果

2、触发bootstrap-table-editor中的onEditorSave方法将更改数据添加进数组中

五、效果与说明

页面效果

 说明

1、回调事件的位置

 2、onEditorSave事件


一、业务场景

目前做的的项目中,要求所有列表页的每个单元格可以就地编辑,并且能完成整页的修改。

二、分析问题

该功能有两个难点,第一个是实现单元格的就地编辑,第二个是保存该页修改的值并提交。

三、bootstrap实现就地编辑

1、使用x-editable

简介

x-editable是一个通用的编辑能力组件,可以给任何元素都加上编辑能力,功能强大,可以编辑文本,数字,选项,时间等等各种类型的数据。但是x-editable有一个比较不好的地方,x-editable的编辑模式是弹框的形式,如下图所示:

使用

  • 引包

首先下载基于bootstrap的源码到本地。引用相关文件。需要引入的文件有:bootstrap-editable.css、bootstrap-editable.js、bootstrap-table-editable.js

<link href="/plugin/bootstrap/css/bootstrap.min.css" rel="stylesheet" />
<link href="/plugin/bootstrap3-editable/css/bootstrap-editable.css" rel="stylesheet" />
​
<script src="plugin/Scripts/jquery-1.9.1.min.js"></script>
<script src="/plugin/bootstrap/js/bootstrap.min.js"></script>
<script src="/plugin/bootstrap3-editable/js/bootstrap-editable.js"></script>
  • 编写页面元素
<a href="#" id="username" data-type="text" data-title="用户名">用户名</a>
  • js初始化
$(function () {
        $('#username').editable();
    });

2、使用bootstrap-table-editor

简介

这是网上一个大佬写的行内编辑组件,可直接更改单元格而不出现弹框,js代码如下:

/**
 * author: netcy
 */
!function ($) {
    'use strict';

    const getItemField = $.fn.bootstrapTable.utils.getItemField;
    const calculateObjectValue  = $.fn.bootstrapTable.utils.calculateObjectValue;

    function createTextEditor(){
        const editor = $('<input class = "form-control"></input>');
        editor.css("width",`100%`);
        return editor;
    }

    function createNumberEditor(){
        var editor = $('<input class = "form-control"></input>');
        editor.attr('type', 'number');
        editor.attr('step', '0.1');
        editor.css("width",`100%`);
        return editor;
    }

    function createSelectEditor(){
        var select = $('<select class="form-control"></select>');
        return select;
    }

    const textEditor = createTextEditor();
    const numberEditor = createNumberEditor();

    const editorCache = {
        text:textEditor,
        number:numberEditor,
        select:createSelectEditor(),
    };

    $.extend($.fn.bootstrapTable.defaults, {
        editable: false,
        reInitOnEdit:true,
        onEditorInit: function () {
            return false;
        },
        onEditorSave: function (field, row, oldValue, $el) {
            return false;
        },
        onEditorShown: function (field, row, $el, editable) {
            return false;
        },
    });

    $.extend($.fn.bootstrapTable.Constructor.EVENTS, {
        'editor-init.bs.table': 'onEditorInit',
        'editor-save.bs.table': 'onEditorSave',
        'editor-shown.bs.table': 'onEditorShown',
    });

    var BootstrapTable = $.fn.bootstrapTable.Constructor,
        _initTable = BootstrapTable.prototype.initTable,
        _initBody = BootstrapTable.prototype.initBody;

    BootstrapTable.prototype.getEditor = function(key){
        return editorCache[key] || textEditor;
    }

    BootstrapTable.prototype.initTable = function () {
        var that = this;
        _initTable.apply(this, Array.prototype.slice.apply(arguments));

        if (!this.options.editable) {
            return;
        }

        $.each(this.columns, function (i, column) {
            if (!column.editable) {
                return;
            }
            that._initColumnEditor(column);
        });

        this.trigger('editor-init');
    };

    BootstrapTable.prototype._initColumnEditor = function(column){
        var editable = column.editable;
        var editorType = editable.type;
        var editor = this.getEditor(editorType);
        column._editor = editor;
    };

    BootstrapTable.prototype._setColumnEditorOptions = function(column){
        var editable = column.editable;
        var editor = column._editor;
        if(!editor) {
            return;
        }

        var options = editable.options || {},
            attributes = options.attributes,
            styles = options.styles;
        if(editor._options == options) { //避免重复
            return;
        }
        if(attributes){
            Object.keys(attributes).forEach(function(key){
                editor.attr(key,attributes[key]);
            });
        }
        if(styles){
            Object.keys(styles).forEach(function(key){
                editor.css(key,styles[key]);
            });
        }
        if(editable.type == "select" && options.items){
            editor.empty();
            $.each(options.items, function (index, val) {
                let label = val.label || val.value || val;
                let value = val.value || val;
                var opt = $('<option value="' + value + '">' + label + '</option>');
                editor.append(opt);
            });
        }
        editor._options = options;
    }

    BootstrapTable.prototype._startEdit = function($td){
        var that = this;
        var $tr = $td.parent(),
            item = that.data[$tr.data('index')],
            index = $td[0].cellIndex,
            fields = that.getVisibleFields(),
            field = fields[that.options.detailView && !that.options.cardView ? index - 1 : index],
            column = that.columns[that.fieldsColumnsIndex[field]],
            value = getItemField(item, field, that.options.escape),
            dataIndex = $td.parent().data('index');
        var editor = column._editor;
        if(!editor){
            return;
        }
        if(editor._row != item || editor._index != index){
            that._setColumnEditorOptions(column);
            editor._row = item;
            editor._index = index;

            editor.keydown(function(e) {
                if (e.keyCode == 13) {
                    that._saveEditData($td,editor,padding);
                }
            });
            editor.blur(function(e){
                that._saveEditData($td,editor,padding);
            });
            editor.change(function(e){
                that._saveEditData($td,editor,padding);
            });
            var padding = $td.css("padding");
            $td.html("");
            $td.css("padding","0px");
            $td.append(editor);
            editor.focus();
            editor.val(value);
            that.trigger('editor-shown', column.field, item, $td);
        }
    }

    BootstrapTable.prototype._saveEditData = function($td,editor,padding){
        let that = this;
        var $tr = $td.parent(),
            item = that.data[$tr.data('index')],
            index = $td[0].cellIndex,
            fields = that.getVisibleFields(),
            field = fields[that.options.detailView && !that.options.cardView ? index - 1 : index],
            column = that.columns[that.fieldsColumnsIndex[field]],
            value = getItemField(item, field, that.options.escape),
            dataIndex = $td.parent().data('index');

        let tdValue = editor.val();
        if(tdValue != value){
            this.updateCell({
                index: dataIndex,
                field: field,
                value: tdValue,
                reinit:this.options.reInitOnEdit,
            });
            if(!this.options.reInitOnEdit) {
                let args = [tdValue,item,index,field];
                let formatText = calculateObjectValue(column,column.formatter,args,tdValue);
                $td.html(formatText);
                $td.css("padding",padding);
            }
            that.trigger('editor-save', column.field, item, value, $td);
        } else {
            let args = [value,item,index,field];
            let formatText = calculateObjectValue(column,column.formatter,args,value);
            $td.html(formatText);
            $td.css("padding",padding);
        }
        editor._row = undefined;
        editor._index = undefined;
    };

    BootstrapTable.prototype.initBody = function () {
        var that = this;
        _initBody.apply(this, Array.prototype.slice.apply(arguments));

        if (!this.options.editable) {
            return;
        }

        this.$body.find('> tr[data-index] > td').on('click', function (e) {
            var $td = $(this);
            that._startEdit($td);
        });
    };

}(jQuery);

使用

  • 引包
<script th:src="@{/ruoyi/js/bootstrap-table-editor.js?v=1.1.1}"></script>
  • 指定表格的属性中editable为true
 let tableOptions = {
          columns:columns,
          editable:true, //editable需要设置为 true
        }
  • 在需要编辑的列上面指定editable属性

该属性上面可以指定编辑器的类型,目前支持文本,数字和下拉框。其中editable为true的时候,默认是文本编辑器,指定编辑器类型未select时候,需要指定下拉框的items。

  let columns = [{
            title: "编号",
            field: "id",
            sortable: true,
            width:200,
            editable:false,
        },{
            title: "月份",
            field: "month",
            sortable: true,
            width:200,
            formatter:function(v){
              return v + "月"
            },
            editable:{
              type:"select",
              options:{
                items:[{
                  value:1,
                  label:"1月",
                },{
                  value:2,
                  label:"2月",
                },{
                  value:3,
                  label:"3月",
                },{
                  value:4,
                  label:"4月",
                },{
                  value:5,
                  label:"5月",
                }]
              }
            }
        },{
            title: "部门",
            field: "department",
            sortable: true,
            width:200,
            editable:{
              type:"select",
              options:{
                items:[
                  "技术部","生产部","管理中心"
                ]
              }
            }
        },{
            title: "管理费用",
            field: "fee",
            sortable: true,
            width:200,
            editable:{
              type:"number"
            }
        },{
            title: "备注",
            field: "comment",
            sortable: true,
            width:200,
            editable:true,
            // editable:{
            //   type:"text"
            // }
        },];

四、用一个数组保存就地编辑的结果

1、定义一个数组存保存结果

//定义数组,用来存放对象
    var objItemList = new Array();

2、触发bootstrap-table-editor中的onEditorSave方法将更改数据添加进数组中

  • 在ry.js的bootstraTable中添加onEditorSave回调事件

  •  在你写的html文件的options中添加该回调函数

  •  编写onEditorSave的具体实现
//编辑保存单元格
    function onEditorSave (field, row, oldValue, $el) {
        //记录该列的数据类型
        var dataType = '';
        for (var i = 0; i < fieldList.length; i++) {
            if (fieldList[i].field == field) {
                dataType = fieldList[i].dataType;
                break;
            }
        }
        //检验数据是否符合要求(文本与数字)
        var ifDataType = CommonUtils.isNum(dataType, row[field]);
        if (ifDataType) {
            var objItem = {};          //存储修改的该条数据的信息(行id,模板id,列名|列值)
            var ids = new Array();     //存储已经修改过的行id
            var fields = new Array();  //存储已经修改过的列名
            objItem.id = parseInt(row.id);
            objItem.templateId = parseInt(row.templateId);
            objItem.data = field + "|" + row[field];
            //alert("字段名:" + field + ",当前值:" + row[field]  + ",旧值:" + oldValue);
            if (objItemList.length > 0) {
                //拿到已修改的数据的行号与属性列
                for (var i = 0; i < objItemList.length; i++) {
                    ids.push(objItemList[i].id);
                    fields.push(objItemList[i].data.split("|")[0]);
                }
                //判断该数据是否是第一次修改
                for (var j = 0; j < ids.length; j++) {
                    if (ids[j] == row.id && fields[j] == field) {
                        //说明不是第一次修改,找到该数据删除
                        for (var k = 0; k < objItemList.length; k++) {
                            if (objItemList[k].id == row.id) {
                                objItemList.pop(k);
                                break;
                            }
                        }
                    }
                }
                //添加修改的数据
                objItemList.push(objItem);
            }  else {
                objItemList.push(objItem);
            }
        } else {
            $.modal.alert("需要" + dataType + "类型的数据,请重新填写符合要求的数据!");
        }
    }
  • 编写保存方法
//就地编辑保存
    function inPlaceEditSave() {
        var request = {
            url: prefix + "/inPlaceEdit",
            type: "post",
            async: false,
            dataType: "json",
            data: {
                "datas":JSON.stringify(objItemList)    //数组转字符串,用于提交到后端
            },
            success: function (result) {
                $.modal.alert(result.content);
                //执行完清空objItemList
                objItemList = [];
            },
            error:function() {            //ajax提交失败
                $.modal.alert("error");
            }
        };
        $.ajax(request);
    }
  • 后台业务方法的具体实现
/**
     * 就地编辑
     *
     * @param datas 转成字节流的List<ObjItem>
     * @return
     * */
    @Override
    public int inPlaceEdit(String datas) {
        int num = 0;
        //存储ObjItem
        List<ObjItem> objItemList = new LinkedList<ObjItem>();
        //解析数据
        JSONArray dataArr = JSONArray.parseArray(datas);
        for (Object data : dataArr) {
            ObjItem objItem = new ObjItem();
            //模板id
            String templateId = data.toString().split(",")[2].split(":")[1].replace("}","");
            //数据id
            String rowId = data.toString().split(",")[1].split(":")[1];
            //查询原数据
            ObjItem oldObjItem = objItemService.getObjItem(Integer.valueOf(templateId), Integer.valueOf(rowId));
            //得到数据
            HashMap<Field, Object> oldData = oldObjItem.getData();
            //更改的字段名
            String fieldName = data.toString().split(",")[0].replace("{","").split(":")[1].split("\\|")[0].substring(1);
            //更改的字段值
            String fieldValue = data.toString().split(",")[0].replace("{","").split(":")[1].split("\\|")[1];
            //改变的字段
            Field changeField = fieldService.getField(Integer.valueOf(templateId), fieldName);
            //修改oldData的值
            oldData.put(changeField, fieldValue.substring(0, fieldValue.length() - 1));
            num += objItemService.updateById(oldObjItem);
        }
        return num;
    }

五、效果与说明

页面效果

  • 整个页面就地编辑之前

  •  就地编辑之后点击保存
  •  点击确认之后数据被修改

 说明

1、回调事件的位置

在ry.js中添加回调事件的时候,一定要写在表格的回调事件里,不要写在树的回调事件里,不然会没效果

表格:(加在这个里面)

​​​​​​​​​​​​​​

 表格树:(而不是这里)

 2、onEditorSave事件

bootstrap-table-editor里的onEditorSave方法是在单元格更改且失焦的时候触发,而x-editable中的onEditableSave方法是在点击提交按钮之后提交。


版权声明:本文为m0_52248951原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/m0_52248951/article/details/127340822