前言:本文主要用于我的个人学习与记录,内容如有谬误欢迎各位大佬斧正。同时,如果您想要快速获取问题的解决方案,请移步至文章底部“解决方案”栏,希望我的经验能对大家有所帮助。


问题描述 

出于项目需求考虑,需要在输入框中输入完整SQL语句,如果语句类型为查询,则基于Layui数据表格在前台展示查询结果(项目为供公司内部运维使用的管理系统,故不考虑SQL注入风险)。因此在需求侧,可以得到具体流程如下:

1.在父页面输入SQL语句,点击操作后将语句传入子页面

2.在子页面选择操作类型,点击提交后将参数打包fetch(添加到常用为另外一个需求点,不赘述)

3.拿到查询结果后将数据回传到父页面,用于加载父页面表格(查询结果在后端格式化封装):


处理思路

先理一下整个逻辑:

父页面弹窗时调用子页面接参方法(参数:SQL语句)

-> 子页面将接到的SQL语句和选择的操作类型打包fetch

-> 后台按类型判断并执行

-> 得到返回结果并封装,在封装时夹带私货加一个"type":"xxx"供前端识别

-> 前端将结果转换成JSON并做判断:如果type为查询,则提示成功并调用returnData方法向父页面传递返回的res;否则提示成功后调用exitWindow方法关闭窗口

-> 在父页面接受res,并想办法从这个res里得到每一列的列名,再整合为cols重载表格

这套逻辑整体来说比较简洁,做起来也不难。只有一个小小的问题:传回父页面的res是一个JSON字符串,我们最多能做到的是用res.data拿到数据,但由于数据来源不定,数据的列名无从获取。而如果没有列名,就无法填充layui表格的cols,也就无法正确显示数据内容。

做完整套逻辑之后,就只差最后这一环了,于是赶紧去网上查了很多资料,但没有找到完全动态加载Layui表格的方法。然后只好自己想办法:我又顺着整个流程思考了一遍,发现:只有在成功查询到数据的时候,我们才需要按列名显示,如果结果为空,也就没有显示的必要了。换言之,需要显示列名时,一定查到了至少一组数据——那就好办多了,只需要:

在父页面写一个方法取出 res.data[0] ,用 for(var i in res.data[0]){} 遍历一遍,每次执行取出语句中的i(这里的i就是JSON里的数据名,数据内容表示为data[0][i])并向cols数组中添加一行field和title都等于i的col,最后将这个cols数组赋值给layui表格的cols

就大功告成了。


解决方案

按照上述思路,我写了一个getColsName()方法,用于从res.data中获取列名,代码如下:

    function getColsName(data) {
        let col = []
        let cols = []
        col.push({checkbox: true, fixed: true})
        for (var i in data) {
            col.push({field: i, title: i})
        }
        cols.push(col)
        return cols
    }

然后就只需要在重载表格时调用一下就ok啦:

    window.getTable = function (res) {
        layui.use(['jquery', 'layer', 'table'], function () {
            table.render({
                elem: '#table_Arbitrary'
                , id: 'table_Arbitrary'
                , toolbar: '#toolbarDemo'
                , cols: getColsName(res.data[0])
                , data: res.data
            });
        });
    }

贴上成功执行的截图,以及前后端部分涉及代码:

1.用测试SQL语句1查询:

2.用测试SQL语句2查询:

子页面请求方法代码如下:

    var popupWindow = new Vue({
        el: '#popupWindow_insert',
        data: {
            type: '',
            sql: '',
        },
        updated: function () {
            layui.use('form', function () {
                layui.form.render('select');
            })
        },
        methods: {
            sql_submit() {
                if (!this.sql) {
                    $.message({message: "sql语句不能为空!", type: 'warning'})
                    return
                } else if (!this.type) {
                    $.message({message: "类别不能为空!", type: 'warning'})
                    return
                }
                //发送请求给后台
                fetch("url", {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({sqlType: this.type, sql: this.sql})
                })
                    .then(res => res.json())
                    .then(res => {  //res就是后台返回的结果
                            console.log(res)
                            if (res.data != null) {  //判断请求是否成功
                                if (res.type === '查询') {
                                    $.message({message: "查询成功!", type: 'success'})
                                    setTimeout(() => {
                                        returnData(res)
                                    }, 1000)
                                }
                                if (res.type === '新增') {
                                    $.message({message: "新增成功!", type: 'success'})
                                    setTimeout(() => {
                                        exitWindow()
                                    }, 1000)
                                }
                                if (res.type === '修改') {
                                    $.message({message: "修改成功!", type: 'success'})
                                    setTimeout(() => {
                                        exitWindow()
                                    }, 1000)
                                }
                                if (res.type === '删除') {
                                    $.message({message: "删除成功!", type: 'success'})
                                    setTimeout(() => {
                                        exitWindow()
                                    }, 1000)
                                }
                            } else {
                                $.message({message: "操作失败!", type: 'error'})
                            }
                        }
                    )
            },
        }
    })

Controller层代码如下:

    @ResponseBody
    public LayuiTableData ArbitraryTableSQL(String sqlType, String sql) {
         if(Objects.equals(sqlType, "查询")) {
             List<HashMap<String, Object>> table = tableService.selectSQL(sql);
            return LayuiTableData.layTypeData(table.size(), table, sqlType);
        }
        else if(Objects.equals(sqlType, "新增")) {
            Integer table = tableService.insertSQL(sql);
            return LayuiTableData.layTypeData(Collections.singletonList(table).size(), Collections.singletonList(table), sqlType);
        }
        else if(Objects.equals(sqlType, "修改")) {
            Integer test1 = tableService.updateSQL(sql);
            return LayuiTableData.layTypeData(Collections.singletonList(test1).size(), Collections.singletonList(test1), sqlType);
        }
        else if(Objects.equals(sqlType, "删除")) {
            Integer test1 = tableService.deleteSQL(sql);
            return LayuiTableData.layTypeData(Collections.singletonList(test1).size(), Collections.singletonList(test1), sqlType);
        }
        return null;
    }

    @PostMapping ("/arbitrarySQL")
    public LayuiTableData arbitraryTableSQL(@RequestBody String arbitraryStr) {

        JSONObject arbitraryObj = JSONUtil.parseObj(arbitraryStr);

        String sqlType = arbitraryObj.getStr("sqlType");
        String sql = arbitraryObj.getStr("sql");

        return ArbitraryTableSQL(sqlType, sql);
    }

格式化封装方法如下:

public class LayuiTableData extends HashMap<String, Object> {
    public static LayuiTableData layData(int count, List<?> data) {
        LayuiTableData layui = new LayuiTableData();
        layui.put("code", 0);        // 这里的状态码必须设为0或者200
        layui.put("msg", "");        // 这里一般为空即可
        layui.put("count", count);    // 数据的总数(必须)
        layui.put("data", data);        // 数据(必须)
        return layui;
    }
    public static LayuiTableData layTypeData(int count, List<?> data, String type) {
        LayuiTableData layui = new LayuiTableData();
        layui.put("code", 0);        // 这里的状态码必须设为0或者200
        layui.put("msg", "");        // 这里一般为空即可
        layui.put("count", count);    // 数据的总数(必须)
        layui.put("data", data);        // 数据(必须)
        layui.put("type", type);        // 数据(必须)
        return layui;
    }
}

子页面向父页面传参方法如下:

    function returnData(res) {
        var index = parent.layer.getFrameIndex(window.name); //获取当前窗口的name
        window.parent.getTable(res);//子页面调用
        parent.layer.close(index);
    }

父页面向子页面传参方法如下:

    function submitCommon() {
        var layer = layui.layer;
        layer.open({
            title: '操作面板',
            type: 2,
            area: ['500px', '330px'],
            content: 'popupWindow_insert.html',
            scrollbar: false,
            success: function (layero, index) {
                // 获取子页面的iframe
                var iframe = window['layui-layer-iframe' + index];
                // 向子页面的全局函数child传参
                iframe.child(Common.sql);
            }
        });
    }

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐