前言:本文主要用于我的个人学习与记录,内容如有谬误欢迎各位大佬斧正。同时,如果您想要快速获取问题的解决方案,请移步至文章底部“解决方案”栏,希望我的经验能对大家有所帮助。
问题描述
出于项目需求考虑,需要在输入框中输入完整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);
}
});
}
更多推荐