Shelton

jsTree结合Java使用例子[create、rename、delete]

jsTree结合Java使用例子[create、rename、delete]

起因

基于JQuery树形Tree插件网上随便google下一大堆,但结合实际情况,只开发树形文件夹管理、树形菜单、树形权限管理模块等等,我最后还是选择了jsTree,功能够用即可。

需求

在此以SSH[Spring Spring MVC Hibernate]开发作为DEMO,为了节省时间使用了JeeSite框架,DB用的是MySQL

准备

建立文件夹table,以下SQL语句:

1
2
3
4
5
6
7
CREATE TABLE `cms_dir` (
`id` varchar(36) NOT NULL DEFAULT '',
`name` varchar(255) DEFAULT NULL,
`parent_id` varchar(36) DEFAULT NULL,
`status` varchar(1) DEFAULT NULL,
PRIMARY KEY (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

开始

###1.前端页面代码:

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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jstree basic demos</title>
<style>
html { margin:0; padding:0; font-size:62.5%; }
body { max-width:800px; min-width:300px; margin:0 auto; padding:20px 10px; font-size:14px; font-size:1.4em; }
h1 { font-size:1.8em; }
</style>
<link rel="stylesheet" href="./../../dist/themes/default/style.min.css" />
</head>
<body>
<h1>HTML demo</h1>
<div id="fileTree"></div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="./../../dist/jstree.min.js"></script>
<script>
// html demo
$(function () {
/**
* 调用后台SpringMVC Action
*/
function _remoteCall(url, params, callback, isJson, isAsync) {
$.ajax({
type: "POST",
url: url,
data: params ? params : {},
async: isAsync ? false : true,
dataType: isJson ? "json" : "text",
success: function(data) {
if(typeof(callback) == "function") callback(data);
}
});
}
// 自定义右击菜单
var item_setting = {
"create" : {
"separator_before" : false,
"separator_after" : false,
"_disabled" : false, //(this.check("create_node", data.reference, {}, "last")),
"label" : "Create",
"action" : function (data) {
var inst = $.jstree.reference(data.reference),
obj = inst.get_node(data.reference);
//debugger;
inst.create_node(obj, {"text" : "新建目录"}, "last", function (new_node) {
try {
inst.edit(new_node);
} catch (ex) {
setTimeout(function () { inst.edit(new_node); },0);
}
});
}
},
"rename" : {
"separator_before" : false,
"separator_after" : false,
"_disabled" : false, //(this.check("rename_node", data.reference, this.get_parent(data.reference), "")),
"label" : "Rename",
/*!
"shortcut" : 113,
"shortcut_label" : 'F2',
"icon" : "glyphicon glyphicon-leaf",
*/
"action" : function (data) {
var inst = $.jstree.reference(data.reference),
obj = inst.get_node(data.reference);
inst.edit(obj);
//console.log("当前对象ID: "+obj.id);
}
},
"remove" : {
"separator_before" : false,
"icon" : false,
"separator_after" : false,
"_disabled" : false, //(this.check("delete_node", data.reference, this.get_parent(data.reference), "")),
"label" : "Delete",
"action" : function (data) {
var inst = $.jstree.reference(data.reference),
obj = inst.get_node(data.reference);
if (obj.parent === "#") {
alert("parent node cannot delete!");
} else {
// debugger;
if (obj.children.length > 0) {
if (confirm("此目录下还有子目录,确定删除?")) {
if(inst.is_selected(obj)) {
inst.delete_node(inst.get_selected());
}
else {
inst.delete_node(obj);
}
_remoteCall("/cms/file/remove", {fileId: obj.id, isDir: "Y" }, function() {
console.log("delete success!");
},false, true);
} else {
//TODO
}
} else {
if(inst.is_selected(obj)) {
inst.delete_node(inst.get_selected());
}
else {
inst.delete_node(obj);
}
_remoteCall("/cms/file/remove", {fileId: obj.id, isDir: "Y" }, function() {
console.log("delete success!");
},false, true);
}
}
}
}
};
// 初始化jsTree
$("#fileTree").jstree({
"core" : {
// so that create works
"check_callback": true, //开启异步刷新必须加
'data' : {
"url" : "/cms/file/ajaxDir",
"dataType" : "json" // needed only if you do not supply JSON headers
}
},
"plugins" : [ "changed", "contextmenu" ],
"contextmenu" : {items : item},
}).on("changed.jstree", function (e, data) { //触发事件
//console.log(data.node.parent);
}).on('rename_node.jstree', function (e, data) { //重命名节点
if (data.text !== "新建文件夹") {
_remoteCall("/cms/file/rename", {fileId: data.node.id, newName: data.text, isDir: "Y" },
function() {
}, false, true);
}
}).on('create_node.jstree', function (e, data) { //创建新节点
_remoteCall("/cms/file/createDir", {parentId: data.node.parent, dirName : data.node.text}
, function(id) {
debugger;
data.instance.set_id(data.node, id); //设置节点ID
}, false, true);
});
});
</script>
</body>
</html>

###2.Controller层
ajax数据初始化Java方法

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
/**
* ajax返回目录
* @return
*/
@RequestMapping("/ajaxDir")
@ResponseBody
public String ajaxDir() {
// 查找全部文件夹
List<CmsDir> dirs = cmsDirService.getAllDirs();
// 遍历目录
for (CmsDir dir : dirs) {
// 拼装目录json数据
JSONObject state = new JSONObject();
JSONObject jsonObject = new JSONObject();
jsonObject.put("id", dir.getId());
if (dir.getId().equals(parentId) || "#".equals(dir.getParentId())) {
jsonObject.put("parent", "#");
} else {
jsonObject.put("parent", dir.getParentId());
}
jsonObject.put("text", dir.getName());
//jsonObject.put("icon", "fa fa-file");
jsonObject.put("isFile", false);
state.put("opened", false);
jsonObject.put("state", state);
jsonArray.add(jsonObject);
}
return jsonArray.toString();
}

创建文件夹Java方法:

1
2
3
4
5
6
7
8
9
10
/**
* 创建目录
* @return id
*/
@RequestMapping(value = "/createDir")
@ResponseBody
public String createDir(@RequestParam("parentId") String parentId,
@RequestParam("dirName") String dirName) {
return cmsDirService.createDir(parentId, dirName);
}

重命名文件夹Java方法

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 文件或目录重命名
* @param fileId
* @param newName
* @param isDir
*/
@RequestMapping(value = "rename")
@ResponseBody
public void rename(@RequestParam("fileId") String fileId,
@RequestParam("newName") String newName, @RequestParam("isDir") String isDir ) {
cmsService.rename(fileId, newName, FileConstants.VALID_YES.equals(isDir));
}

删除文件夹Java方法[以下代码包括删除文件夹以及文件,仅供参考]

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
/**
* 删除文件或目录
* @param fileId
* @param isDir
*/
@RequestMapping(value = "remove")
@ResponseBody
public void remove(@RequestParam("fileId") String fileId, @RequestParam("isDir") String isDir) {
if (FileConstants.VALID_YES.equals(isDir)) { // 目录类型
CmsDir directory = cmsDirService.getDirById(fileId);
delDirectory(directory); //删除目录以及目录下所有文件[包括子目录]
} else { //remove file only
CmsFile cmsFile = cmsFileService.getFileDetail(fileId);
cmsFileService.removeFile(cmsFile);
}
}
public void delDirectory(CmsDir directory) {
List<CmsFile> files = new ArrayList();
// 递归删除
if (directory.getChildList().size() > 0) {
for (CmsDir dir : directory.getChildList()) {
delDirectory(dir);
}
}
// 删除当前目录
files = cmsFileService.searchFiles(directory.getId(), null);
cmsService.removeDirAndFiles(directory , files);
}

###3.Service层
CmsDirService代码如下:

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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/**
* 目录Service
* @author shelton
* @version 2017-09-07
*/
@Service
@Transactional(readOnly = true)
public class CmsDirService extends BaseService {
@Autowired
private CmsDirDao cmsDirDao;
public CmsDir get(String id) {
return cmsDirDao.get(id);
}
@Transactional(readOnly = false)
public void save(CmsDir cmsDir) {
cmsDirDao.clear();
cmsDirDao.save(cmsDir);
}
@Transactional(readOnly = false)
public void delete(String id) {
cmsDirDao.deleteById(id);
}
/**
* 创建目录
* @param parentId
* @param dirName
* @return
*/
@Transactional(readOnly = false)
public String createDir(String parentId, String dirName) {
String id = IdGen.uuid();
String businessType = cmsDirDao.get(parentId).getBusinessType(); //获取当前文件夹下的业务类型
CmsDir cmsDir = new CmsDir();
cmsDir.setBusinessType(businessType);
cmsDir.setIsDepartDir(FileConstants.VALID_YES);
cmsDir.setParentId(parentId);
cmsDir.setName(dirName);
cmsDir.setId(id);
cmsDirDao.save(cmsDir);
return id;
}
/**
* 根据ID查找目录
* @param dirID
* @return
*/
@Transactional(readOnly = false)
public CmsDir getDirById(String dirID) {
if (StringUtils.isEmpty(dirID) || StringUtils.isBlank(dirID)) {
return new CmsDir();
/*CmsDir cmsDir = cmsDirDao.getByHql("from CmsDir where parentId = '#' and " +
"businessType = 'ALL'");
if (cmsDir == null) {
return new CmsDir();
} else {
return cmsDir;
}*/
}
return cmsDirDao.get(dirID);
}
/**
* 根据ID查找全部目录
* @param parentId
* @param isJSONType
* @return
*/
@Transactional(readOnly = false)
public List<CmsDir> searchDirs(String parentId, boolean isJSONType) {
List<CmsDir> dirs;
if (!isJSONType) {
if(StringUtils.isNotEmpty(parentId) && parentId != null) {
dirs = cmsDirDao.find("from CmsDir where parentId = :p1 order by createDate", new Parameter(parentId));
} else {
dirs = cmsDirDao.find("from CmsDir where parentId = '#' order by createDate");
}
} else {
if (StringUtils.isNotBlank(parentId) && parentId != null && !"".equals(parentId)) {
// 递归查询[使用SQL进行查询]
/*dirs = cmsDirDao.find("from CmsDir start with Id = :p1 connect by prior Id = parentId",
new Parameter(parentId.trim()));*/
StringBuffer sql = new StringBuffer();
sql.append("select * from Cms_Dir start with id = '");
sql.append(parentId + "' ");
sql.append("connect by prior id = parent_id order by create_date");
SQLQuery query = cmsDirDao.getSession().createSQLQuery(sql.toString());
query.addEntity(CmsDir.class);
dirs = query.list();
} else {
dirs = cmsDirDao.find("from CmsDir");
}
}
return dirs;
}
/**
* 根据业务类型目录获取目录ID
* @param businessType
* @return
*/
@Transactional(readOnly = false)
public String getIdByDirType(String businessType) {
CmsDir cmsDir = cmsDirDao.getByHql("from CmsDir where businessType = :p1 and parentId = '#' ",
new Parameter(businessType));
if (cmsDir != null) {
return cmsDir.getId();
} else {
return "";
}
}
}

###4.DAO层
CmsDirDao代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 目录DAO接口
* @author shelton
* @version 2017-09-07
*/
@Repository
public class CmsDirDao extends BaseDao<CmsDir>{
/**
* 获取全部业务类型目录
* @return
*/
public List getAllDirType() {
return this.find("select distinct businessType from CmsDir");
}
}

###5.Entity层
cms_file的Entity如下:

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
@Entity
@Table(name = "cms_dir")
@DynamicInsert
@DynamicUpdate
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@Indexed
@Analyzer(impl = IKAnalyzer.class)
public class CmsDir extends implements Serializable {
private static final long serialVersionUID = 1L;
private String name; //文件夹名
private String parentId; //上级ID
private List<CmsDir> childList = new ArrayList<CmsDir>(); //子目录Entity
//transparent
private int level;
private int pad;
private boolean hasChild;
@Length(min = 1, max = 255)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Length(min = 0, max = 36)
public String getParentId() {
return parentId;
}
public void setParentId(String parentId) {
this.parentId = parentId;
}
/**
* 获取子目录
* @return
*/
@JsonIgnore
@OneToMany(mappedBy = "parentId", fetch=FetchType.LAZY)
@NotFound(action = NotFoundAction.IGNORE)
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public List<CmsDir> getChildList() {
return childList;
}
public void setChildList(List<CmsDir> childList) {
this.childList = childList;
}
}

以上代码仅供参考用,运行是没有问题的