前后端耦合下实现多文件上传和下拉树选择

环境

  • 后端框架:SpringBoot
  • 模板引擎:FreeMarker
  • 前端框架:JQuery
  • 前端 UI 组件:Layui、xm-select

前端代码

表单部分

  • 使用 Layui 表单样式。
  • 编写一个下拉树的分隔区块。
  • 编写一个上传按钮和上传成功后显示文件名和操作的表格。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<form class="layui-form" lay-filter="add" style="height:100%; padding-top:16px;">
<div class="layui-form-item">
<label class="layui-form-label">下拉树:<span style="color:red">*</span></label>
<div class="layui-input-block" id="selectTree"></div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">附件:</label>
<input type="hidden" name="attachment" id="attachment" value="${demo.attachment!}"/>
<div class="layui-input-block layui-upload">
<button type="button" class="layui-btn layui-btn-normal" id="testList">上传文件</button>
<div class="layui-upload-list">
<table class="layui-table">
<thead>
<tr>
<th>文件名</th>
<th>操作</th>
</tr>
</thead>
<tbody id="demoList"></tbody>
</table>
</div>
</div>
</div>
<div class="layui-form-item">
<button type="button" class="btn layer-cancel">取消</button>
<button lay-submit class="btn btn-primary" lay-filter="add" id="submitAddForm">完成</button>
<button type="reset" class="btn btn-warm" id="resetAddForm">重置</button>
</div>
</form>

JS 部分

  • 使用 layui.upload 上传组件,上传成功后 js 动态拼接表格元素显示文件名和监听删除操作。
  • 使用 xm-select 多选组件,初始化使用 ajax 请求接口返回树形数据,然后动态赋值给下拉框。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
$(function () {
var uploadListIns = layui.upload.render({
elem: '#testList'
,elemList: $('#demoList')
,url: '${request.contextPath}/file/upload'
,accept: 'file'
,multiple: true
,number: 10
,auto: true
,done: function(res, index, upload){ //成功的回调
var attachment = $("#attachment").val();
var attachmentArray = attachment ? attachment.split(",") : [];
attachmentArray.push(res.uuid)
$("#attachment").val(attachmentArray.join(","))
appendTr(res.uuid, res.fileName, res.fileId);
return;
}
,error: function(){
}
});

<#list files as file>
appendTr('${file.uuid}', '${file.fileName}', '${file.fileId}')
</#list>

function appendTr(uuid, fileName, fileId) {
let downloadUrl = '${request.contextPath}/file/download?fileId=' + fileId
var tr = $(['<tr id="'+ uuid +'">'
,'<td>'+ '<a href="' + downloadUrl + '" style="color:blue; text-decoration:underline;">' + fileName + '</a>' +'</td>'
,'<td>'
,'<button class="layui-btn layui-btn-xs layui-btn-danger demo-delete">删除</button>'
,'</td>'
,'</tr>'].join(''));

tr.find('.demo-delete').on('click', function(){
// Ajax 模拟 Form 表单提交
var formData = new FormData();
formData.append("uuid", tr.attr('id'));
$.ajax({
type: "post",
async: false,
url: '${request.contextPath}/file/remove',
dataType: "json",
data: formData,
contentType: false,//这里
processData: false,//这两个一定设置为false
success: function (res) {
Array.prototype.removeByValue = function (val) {
for (var i = 0; i < this.length; i++) {
if (this[i] === val) {
this.splice(i, 1);
i--;
}
}
return this;
}
var attachment = $("#attachment").val();
var attachmentArray = attachment ? attachment.split(",") : [];
attachmentArray.removeByValue(tr.attr('id'));
$("#attachment").val(attachmentArray.join(","))
},
error: function (err) {
console.log(err)
}
});
tr.remove();
});

$('#demoList').append(tr);
}

var selectTreeXmSelect = layui.xmSelect.render({
el: '#selectTree',
autoRow: true,
name: "selectTree",
//单选模式
radio: true,
height: 'auto',
data: []
})

$.ajax({
type: "get",
async: false,
url: Ams.ctxPath + "/demo/demo/selectTree",
dataType: "json",
data: {},
contentType: "application/json;charset=UTF-8",
success: function(res) {
selectTreeXmSelect.update({
data: res,
tree: {
show: true,
//非严格模式
strict: false,
//展开所有节点
expandedKeys: true,
},
})
selectTreeXmSelect.setValue(['${demo.selectTree!}'])
},
error: function(err) {
console.log(err)
}
})
});

后端代码

控制器

  • 页面跳转和获取下拉树数据的接口。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 跳转页面
*
*/
@RequestMapping(value = "/editDemo", method = RequestMethod.GET)
public ModelAndView editDemo(ModelAndView modelAndView, String id) {
Demo demo = new Demo();
if (StringUtils.isNotEmpty(id)) {
demo = demoService.getById(id);
}
List<Object> files = demoService.getFiles(demo);
modelAndView.addObject("files", files);
modelAndView.addObject("demo", demo);
modelAndView.setViewName("/demo/demoEdit");
return modelAndView;
}

/**
* 下拉树
*
*/
@RequestMapping(value = "/selectTree", method = RequestMethod.GET)
public List<Object> selectTree() {
return demoService.selectTree();
}

Service

  • 构造树形数据以及查询文件信息。
  • 文件上传、删除、下载相关操作的接口,单独抽出一个模块来实现,实现方式有很多种,例如可存储到本地,也可以存储到 MongoDB 等,具体实现以后单独写篇文章。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
@Override
public List<Object> selectTree() {
List<SelectTree> selectTree = selectTreeService.list();
return menuList(selectTree);
}

/**
* Menu list list.
*
* @param menu the menu
* @return the list
*/
public List<Object> menuList(List<SelectTree> menu) {
List<Object> list = new ArrayList<>();
for (SelectTree selectTree : menu) {
Map<String, Object> map = new LinkedHashMap<>();
if (selectTree.getParentId() == null) {
menuItem(menu, list, selectTree, map);
}
}
return list;
}

/**
* Menu child list.
*
* @param menu the menu
* @param id the id
* @return the list
*/
public List<Object> menuChild(List<SelectTree> menu, String id) {
List<Object> list = new ArrayList<>();
for (SelectTree selectTree : menu) {
Map<String, Object> map = new LinkedHashMap<>();
if (selectTree.getParentId() != null && selectTree.getParentId().equals(id)) {
menuItem(menu, list, selectTree, map);
}
}
return list;
}

private void menuItem(
List<SelectTree> menu,
List<Object> list,
SelectTree selectTree,
Map<String, Object> map
) {
map.put("value", selectTree.getUuid());
map.put("name", selectTree.getPointName());
map.put("pid", selectTree.getParentId());
List<Object> children = menuChild(menu, selectTree.getUuid());
map.put("children", children);
list.add(map);
}

@Override
public List<Object> getFiles(Demo demo) {
String attachment = demo.getAttachment();
if (StringUtils.isEmpty(attachment)) {
return new ArrayList<>();
}
String[] attachmentArray = attachment.split(",");
StringBuilder in = new StringBuilder();
for (String s : attachmentArray) {
if (in.length() == 0) {
in.append("'").append(s).append("'");
}
in.append(",'").append(s).append("'");
}
return demoDao.getNativeQueryList(filesSql(in.toString()));
}

public String filesSql(String in) {
return "SELECT\n" +
"\tuuid,\n" +
"\tfile_name,\n" +
"\tfile_id \n" +
"FROM\n" +
"\tFILE \n" +
"WHERE\n" +
"\tuuid IN (" + in + ")";
}

前后端耦合下实现多文件上传和下拉树选择
http://www.loquy.cn/posts/2df684da.html
作者
loquy
发布于
2022年7月23日
许可协议