需求 当某一父节点下的子节点全选时保存此父节点 否则保存已选中的子节点
难点在于前端与后台互相传参会全量传参 数据单一很难筛选数据
业务数据 一级节点一定是部门 父id为0 一级节点下的节点可能是人员或部门
业务数据只有三层部门和三层人员 第三层部门下没有数据 更多层继续循环判断即可
d|0|10|15 d->部门 0->一级部门 10->下级节点数量 15->当前节点id
d|15|5|20 d->部门 15->父部门id 5->下级节点数量 20->当前节点id
r|20|0|105 r->人员 20->父部门id 0->人员无下级节点 20->当前节点id
下载authtree https://github.com/wangerzi/layui-authtree
使用的spring thymeleaf 'th:'前缀的标签可替换成其它标签
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="${file}==null?'文件新增':'文件替换'">title>
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<script th:src="@{/layui/layui.js}" charset="utf-8">script>
head>
<body>
<div class="layui-container">
<form class="layui-form layui-form-pane">
<input type="hidden" name="id" th:value="${file?.id}"/>
<div class="layui-row">
<div class="layui-col-xs10 layui-col-xs-offset1">
<div class="layui-form-item">
<label class="layui-form-label">上传文件label>
<div class="layui-input-block" th:if="${file!=null}">
<button type="button" class="layui-btn" id="file">点击选择button>
<span id="fileName" th:text="${file?.fileAddress}">span>
<input type="hidden" lay-verify="required" name="fileAddress" th:value="${file?.fileAddress}" autocomplete="off" class="layui-input"/>
div>
div>
div>
div>
<div class="layui-row">
<div class="layui-col-xs10 layui-col-xs-offset1">
<div class="layui-form-item">
<label class="layui-form-label">文件名称label>
<div class="layui-input-inline">
<input type="text" name="fileName" lay-verify="required" autocomplete="off" class="layui-input" th:value="${file?.fileName}"/>
div>
<a class="layui-btn layui-btn-radius" lay-filter="save" lay-submit>保存a>
div>
div>
div>
<label class="layui-form-label">选择权限label>
<div class="layui-row">
<div class="layui-form-item">
<div class="layui-input-block">
<div id="LAY-auth-tree-index">div>
<div class="layui-input-block">
<input type="checkbox" class="switch_checked" lay-filter="checkAll"
lay-skin="switch" name="checkAll" lay-text="全选|全选">
<input type="checkbox" class="switch_checked" lay-filter="showAll"
lay-skin="switch" name="showAll" lay-text="展开|展开">
div>
div>
div>
div>
form>
div>
<script type="text/html" id="isSelect">
<span class="layui-btn layui-btn-normal layui-btn-xs" lay-event="isSelect">点击选择</span>
script>
<script th:inline="javascript">
/*
layui.use(['layer', 'form', 'upload'], function () {
var layer = layui.layer,
form = layui.form,
upload = layui.upload,
$ = layui.$;
var uploadInsts = upload.render({ // 允许上传的文件后缀
elem: '#file'
,field:'file'
,url: '/file/upload'
,size: 2097512 // 限制文件大小 单位KB 现在为2G
,accept: 'file' // 普通文件
,exts: 'doc|docx|xls|xlsx|ppt|pptx|pdf' //只允许上传office文件
,before: function (obj) { // obj参数包含的信息 跟choose回调完全一致 可参见上文
layer.load(2); // 上传loading
}
,done: function (res) {
// 上传成功
$("input[name='fileName']").val(data.fileName); // 为input赋值
$("input[name='fileAddress']").val(data.fileAddress); // 为隐藏域赋值
return layer.msg('上传成功');
}
,error: function () { // ajax出现错误时
$('#file').html('上传失败 请重试');
$('#file').find('.file-reload').on('click', function () {
uploadInsts.upload();
});
}
});
// 后台传递的数据 此处使用静态模板渲染 可以ajax替代
var trees = [[${sysUserCates}]];
// localhost:8080/layui/authtree/authtree.js
layui.config({
base: '/layui'
}).extend({
authtree: 'authtree/authtree',
});
layui.use(['form','layer', 'authtree'], function () {
var form = layui.form,
authtree = layui.authtree,
layer = layui.layer;
// 渲染元素id 树形结构数据 checked选中状态 input表单的name(不知道什么意思 不影响使用)
authtree.render('#LAY-auth-tree-index', trees, {
inputname: 'ids[]'
,layfilter: 'lay-check-auth'
,autowidth: true
});
// 使用 authtree.on() 不会有冒泡延迟
authtree.on('change(lay-check-auth)', function(data) {
console.log('监听 authtree 触发事件数据', data);
// 获取所有节点
var all = authtree.getAll('#LAY-auth-tree-index');
// 获取所有已选中节点
var checked = authtree.getChecked('#LAY-auth-tree-index');
// 获取所有未选中节点
var notchecked = authtree.getNotChecked('#LAY-auth-tree-index');
// 获取选中的叶子节点
var leaf = authtree.getLeaf('#LAY-auth-tree-index');
// 获取最新选中
var lastChecked = authtree.getLastChecked('#LAY-auth-tree-index');
// 获取最新取消
var lastNotChecked = authtree.getLastNotChecked('#LAY-auth-tree-index');
console.log(
'all', all,"\n",
'checked', checked,"\n",
'notchecked', notchecked,"\n",
'leaf', leaf,"\n",
'lastChecked', lastChecked,"\n",
'lastNotChecked', lastNotChecked,"\n"
);
});
authtree.on('deptChange(lay-check-auth)', function(data) {
console.log('监听到显示层数改变',data);
});
authtree.on('dblclick(lay-check-auth)', function(data) {
console.log('监听到双击事件',data);
});
// 监听提交
form.on('submit(save)', function (data) {
var userCateIds = [];// 定义一个空数组
$("input[type='checkbox']:checked").each(function (i) { // 把所有被选中的复选框的值存入数组
userCateIds[i] = $(this).val();
});
if (!userCateIds.length > 0) {
layer.alert("请选择权限!")
return;
}
var resources = authtree.getChecked('#LAY-auth-tree-index');
resources = resources.join(",");
$.ajax({
type: "post",
async: false,
cache: false,
data: {
resources: resources,
id: $("input[name='id']").val(),
fileName: $("input[name='fileName']").val(),
fileAddress: $("input[name='fileAddress']").val()
},
url: '/form/save',
success: function (data) {
if (data) {
layer.msg('保存成功',{time:1000},function () {
window.location.href='/form/show').val(); // 为隐藏域赋值
});
} else {
layer.msg("保存失败");
}
}
});
return false;
});
});
});
// 全选
function checkAll(dst) {
layui.use(['jquery', 'layer', 'authtree'], function () {
var layer = layui.layer;
var authtree = layui.authtree;
authtree.checkAll(dst);
});
}
// 全不选
function uncheckAll(dst) {
layui.use(['jquery', 'layer', 'authtree'], function () {
var layer = layui.layer;
var authtree = layui.authtree;
authtree.uncheckAll(dst);
});
}
// 显示全部
function showAll(dst) {
layui.use(['jquery', 'layer', 'authtree'], function () {
var layer = layui.layer;
var authtree = layui.authtree;
authtree.showAll(dst);
});
}
// 隐藏全部
function closeAll(dst) {
layui.use(['jquery', 'layer', 'authtree'], function () {
var layer = layui.layer;
var authtree = layui.authtree;
authtree.closeAll(dst);
});
}
layui.use(['form','layer'], function () {
var $ = layui.$
,form = layui.form;
form.on('switch(checkAll)',function (data) {
// 开关是否开启 true或者false
var check = data.elem.checked;
if (check) {
layui.use(['jquery', 'layer', 'authtree'], function () {
var layer = layui.layer;
var authtree = layui.authtree;
authtree.checkAll('#LAY-auth-tree-index');
});
} else {
layui.use(['jquery', 'layer', 'authtree'], function () {
var layer = layui.layer;
var authtree = layui.authtree;
authtree.uncheckAll('#LAY-auth-tree-index');
});
}
});
form.on('switch(showAll)',function (data) {
// 开关是否开启 true或者false
var check = data.elem.checked;
if (check) {
layui.use(['jquery', 'layer', 'authtree'], function () {
var layer = layui.layer;
var authtree = layui.authtree;
authtree.showAll('#LAY-auth-tree-index');
});
} else {
layui.use(['jquery', 'layer', 'authtree'], function () {
var layer = layui.layer;
var authtree = layui.authtree;
authtree.closeAll('#LAY-auth-tree-index');
});
}
});
});
/*]]>*/
script>
body>
html>
html>
往前台发送的数据 如下为查询部门 查询人员的数据结合
部门数据 -> 一级部门parentId为0 二级部门parentId为一级部门的id
人员数据 -> 人员parentId为部门id
<select id="listUserCate" resultType="java.util.Map">
SELECT
suc.`cate_name` AS 'name',
suc.`id` AS 'value',
suc.`parent_id` AS 'parentId',
<if test="fileId == null">
FALSE AS 'checked'
</if>
<if test="fileId != null">
IF(fuc.`user_cate_id` IS NULL, FALSE, TRUE) AS 'checked'
</if>
, 'bumen' AS 'type'
FROM
sys_user_category suc
<if test="fileId != null">
LEFT JOIN file_user_cate fuc ON fuc.`user_cate_id` = suc.`id` AND fuc.`file_id` = #{fileId}
</if>
UNION ALL
SELECT
su.`user_name` AS 'name',
su.`id` AS 'value',
suc.`id` AS 'parentId',
<if test="fileId == null">
FALSE AS 'checked'
</if>
<if test="fileId != null">
IF(fu.`id` IS NULL, FALSE, TRUE) AS 'checked'
</if>
, 'ren' AS 'type'
FROM sys_user su
LEFT JOIN sys_user_category suc ON suc.`id` = su.`user_cate_id`
<if test="fileId != null">
LEFT JOIN file_user fu ON fu.`user_id` = su.`id` AND fu.`file_id` = #{fileId}
</if>
WHERE su.`del_flag` = 1 AND suc.`del_flag` = 1
</select>
/** 把数据转成三层数据 如果当前节点数据已选中则把子节点也选中 */
@Override
public List<Map<String, Object>> listUserCate(Integer fileId, Integer userId, Integer type) {
List<Map<String, Object>> resultList = sysUserCateMapper.listUserCate(fileId);
List<Map<String, Object>> oneList = new ArrayList<>();
// 根据用户分类size循环
for (int i = 0; i < resultList.size(); i++) {
// 获取单个用户分类
Map<String, Object> one = resultList.get(i);
// 第一层
if (one.get("parentId") != null && one.get("parentId").toString().equals("0")) {
List<Map<String, Object>> twoList = new ArrayList<>();
for (int j = 0; j < resultList.size(); j++) {
Map<String, Object> two = resultList.get(j);
// 第二层
if (two.get("parentId") != null && two.get("parentId").toString().equals(one.get("value").toString())) {
List<Map<String, Object>> threeList = new ArrayList<>();
for (int k = 0; k < resultList.size(); k++) {
Map<String, Object> three = resultList.get(k);
// 第三层
if (three.get("parentId") != null && three.get("parentId").toString().equals(two.get("value").toString())) {
// 如果父节点未选中和自身未选中 则不选中自身
if (three.get("checked").toString().equals("0")) {
three.put("checked", false); // 不启用
} else {
three.put("checked", true); // 启用
}
three.put("disabled", false); // 不开启禁用
if (three.get("type") != null && three.get("type").toString().equals("bumen")) {
three.put("value", "d|" + three.get("parentId") + "|" + 0 + "|" + three.get("value"));
} else {
// 如果当前人员id 等于 当前登录人员id 则让此人默认选中
if (three.get("value").toString().equals(userId.toString())) {
three.put("checked", true); // 启用
}
three.put("value", "r|" + three.get("parentId") + "|" + 0 + "|" + three.get("value"));
}
three.put("parentId", "d" + three.get("parentId"));
threeList.add(three);
}
}
// 如果父节点未选中和自身未选中 则不选中自身
if (two.get("checked").toString().equals("0")) {
two.put("checked", false); // 不启用
} else {
two.put("checked", true); // 启用
}
two.put("disabled", false); // 不开启禁用
if (two.get("type") != null && two.get("type").toString().equals("bumen")) {
two.put("value", "d|" + two.get("parentId") + "|" + threeList.size() + "|" + two.get("value"));
} else {
// 如果当前人员id 等于 当前登录人员id 则让此人默认选中
if (two.get("value").toString().equals(userId.toString())) {
two.put("checked", true); // 启用
}
two.put("value", "r|" + two.get("parentId") + "|" + threeList.size() + "|" + two.get("value"));
}
two.put("parentId", "d" + two.get("parentId"));
if (threeList.size() > 0) {
// 把threeList放在two
two.put("list", threeList);
}
twoList.add(two);
}
}
if (one.get("checked").toString().equals("0")) {
one.put("checked", false);
} else {
one.put("checked", true);
}
one.put("disabled", false); // 不开启禁用
if (twoList.size() > 0) {
one.put("list", twoList); // 把第一层的list添加到返回的第一层中
}
if (one.get("type") != null && one.get("type").toString().equals("bumen")) {
one.put("value", "d|" + one.get("parentId") + "|" + twoList.size() + "|" + one.get("value"));
} else {
// 如果当前人员id 等于 当前登录人员id 则让此人默认选中
if (one.get("value").toString().equals(userId.toString())) {
one.put("checked", true); // 启用
}
one.put("value", "r|" + one.get("parentId") + "|" + twoList.size() + "|" + one.get("value"));
}
oneList.add(one); // 把第一层添加到返回的list中
}
}
// ---把勾选父节点的数据的字节的数据也进行勾选
for (int i = 0; i < oneList.size(); i++) {
// 如果第一层的节点已被选中 则选中该节点下的节点
if (oneList.get(i).get("checked").toString().equals("true")) {
if (oneList.get(i).get("list") != null
&& ((List<Map<String, Object>>) oneList.get(i).get("list")).size() > 0) {
List<Map<String, Object>> twoList = (List) oneList.get(i).get("list");
for (int j = 0; j < twoList.size(); j++) {
twoList.get(j).put("checked", true);
// 判断有没有第三层
if (twoList.get(j).get("checked").toString().equals("true")
&& twoList.get(j).get("list") != null
&& ((List<Map<String, Object>>) twoList.get(j).get("list")).size() > 0) {
List<Map<String, Object>> threeList = (List) twoList.get(j).get("list");
for (int k = 0; k < threeList.size(); k++) {
threeList.get(k).put("checked", true);
}
}
}
}
}
// 如果第一层的节点未被选中 则进入该节点下的节点
if (oneList.get(i).get("checked").toString().equals("false")) {
if (oneList.get(i).get("list") != null
&& ((List<Map<String, Object>>) oneList.get(i).get("list")).size() > 0) {
List<Map<String, Object>> twoList = (List) oneList.get(i).get("list");
for (int j = 0; j < twoList.size(); j++) {
// 如果第二层的节点已被选中 则选中该节点下的节点 并把该节点的父节点勾选
if (twoList.get(j).get("checked").toString().equals("true")) {
oneList.get(i).put("checked", true);
if (twoList.get(j).get("list") != null
&& ((List<Map<String, Object>>) twoList.get(j).get("list")).size() > 0) {
List<Map<String, Object>> threeList = (List) twoList.get(j).get("list");
for (int k = 0; k < threeList.size(); k++) {
threeList.get(k).put("checked", true);
}
}
}
// ----------
// 如果第二层的节点未被选中 则进入该节点下的节点
if (twoList.get(j).get("checked").toString().equals("false")) {
if (twoList.get(j).get("list") != null
&& ((List<Map<String, Object>>) twoList.get(j).get("list")).size() > 0) {
List<Map<String, Object>> threeList = (List) twoList.get(j).get("list");
for (int k = 0; k < threeList.size(); k++) {
// 如果第三层的节点已被选中 则选中该节点下的节点 并把该节点的父节点勾选 没有第四层
if (threeList.get(k).get("checked").toString().equals("true")) {
twoList.get(j).put("checked", true);
oneList.get(i).put("checked", true);
// if (threeList.get(k).get("list") != null
// && ((List
// List
//
// for (int l = 0; l < fourList.size(); l++) {
// fourList.get(k).put("checked", true);
// }
// }
}
}
}
}
//-------------
}
}
}
}
return oneList;
}
前端往后台发送的数据
/** 新增或修改 */
@RequiresPermissions("word-edit")
@PostMapping("save")
@ResponseBody
public boolean save(File file, HttpServletRequest request) {
String resources = request.getParameter("resources");
List<String> dataList = Arrays.asList(resources.split(","));
List<String> oneDeptIdList = new ArrayList<>(); //部门
List<String> twoDeptIdList = new ArrayList<>(); // 子部门
List<String> memberIdList = new ArrayList<>(); // 人员
List<String> saveDeptList = new ArrayList<>();
List<String> saveMemberList = new ArrayList<>();
dataList.forEach(data -> {
//d|0|5|15
if (data.indexOf("d") != -1) {
if (data.indexOf("d|0") != -1) {
oneDeptIdList.add(data); // 一级部门
} else {
twoDeptIdList.add(data); // 二级部门
}
} else {
memberIdList.add(data); // 人员
}
});
// 循环一级部门
List<Map> resultList = new ArrayList<Map>(){{
oneDeptIdList.forEach(oneDeptId -> {
List<Map> twolist = getDeptAndMember(oneDeptId, twoDeptIdList, memberIdList);
add(new HashMap() {{
put("type", 1);
put("pid", oneDeptId.split("\\|")[1]);
put("id", oneDeptId.split("\\|")[3]);
put("num", oneDeptId.split("\\|")[2]);
if (twolist.size() != 0) {
put("child", twolist);
}
}});
});
}};
for (Map one : resultList) {
// 一级部门下没有节点就保存一级部门id
int oneNum = Integer.parseInt(one.get("num").toString());
if (oneNum == 0) {
saveDeptList.add(one.get("id").toString());
} else { // 获取一级部门下部门和人员节点
// 一级部门下的部门和人员节点数量等于正确数量 保存一级部门id
List<Map> twoList = (List) one.get("child");
for (Map two : twoList) {
// 判断部门
if (two.get("type").toString().equals("1")) {
// 二级部门下没有节点就保存二级部门id
int twoNum = Integer.parseInt(two.get("num").toString());
if (twoNum == 0) {
saveDeptList.add(two.get("id").toString());
} else { // 获取二级部门下部门和人员节点
// 二级部门下的部门和人员节点数量等于正确数量 保存二级部门id
List<Map> threeList = (List) two.get("child");
// 如果第三层的人员和部门数量不缺失 保存第二层部门id的flag 否则保存第三层的人员和部门
if (Integer.parseInt(two.get("num").toString()) == threeList.size()) {
two.put("saveDept", true); // 保存部门标识
} else {
threeList.forEach(item -> {
if (item.get("type").toString().equals("1")) {
saveDeptList.add(item.get("id").toString());
} else {
saveMemberList.add(item.get("id").toString());
}
});
two.put("saveDept", false); // 不保存部门标识
}
}
}
}
// 如果当前第一层下的所有第二层的部门id已保存 保存第一层部门id 否则保存第二层的人员
int twoDeptFlag = 0;
for (Map item : twoList) {
if (item.get("type").equals("1") && item.get("saveDept").toString().equals("true")) {
twoDeptFlag++;
}
}
// 如果第二层的人员和第二层的已保存部门数量不缺失 保存第一层部门id 否则保存第二层的人员和部门
int twoMemberNum = 0;
for (Map item : twoList) {
if (item.get("type").toString().equals("2")) {
twoMemberNum++;
}
}
if (twoDeptFlag + twoMemberNum == Integer.parseInt(one.get("num").toString())) { // 保存第一层的id
saveDeptList.add(one.get("id").toString());
} else { // 保存第一层下的人
twoList.forEach(item -> {
if (item.get("type").toString().equals("1")) {
// 保存第二层部门id
if (item.get("saveDept").toString().equals("true")) {
saveDeptList.add(item.get("id").toString());
}
} else {
saveMemberList.add(item.get("id").toString());
}
});
}
}
}
List<Integer> userCateIds = saveDeptList.stream().map(Integer::parseInt).collect(Collectors.toList());
List<Integer> userIds = saveMemberList.stream().map(Integer::parseInt).collect(Collectors.toList());
file.setUserIds(userIds);
file.setUserCateIds(userCateIds);
file.setPreviewFlag(true);
try {
fileService.saveFile(file);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/** 根据一级部门id获取此一级部门下的二级部门和用户 (递归暂时不可使用已经注掉) */
private List<Map> getDeptAndMember(String oneDeptId, List<String> twoDeptIdList, List<String> memberIdList) {
List<Map> twoList = new ArrayList<>(); // map中的type 1部门 2人员
twoDeptIdList.forEach(twoDeptId -> {
// // 递归调用 判断重新循环的id是否是当前id 否则StackOverflowError
// List
// // 判断当前id的下级是否有子节点 否则StackOverflowError
// if (!twoDeptId.equals(oneDeptId) && !twoDeptId.split("\\|")[2].equals("0")) {
// threeList = getDeptAndMember(twoDeptId, twoDeptIdList, memberIdList);
// }
List<Map> threeList = new ArrayList<>();
twoDeptIdList.forEach(threeDeptId -> {
// 二级部门id值等于三级部门的父id值
if (twoDeptId.split("\\|")[3].equals(threeDeptId.split("\\|")[1])) {
// 保存三级部门id 没有四级部门
threeList.add(new HashMap() {{
put("type", 1);
put("pid", threeDeptId.split("\\|")[1]);
put("id", threeDeptId.split("\\|")[3]);
put("num", threeDeptId.split("\\|")[2]);
}});
}
});
memberIdList.forEach(threeMemberId -> {
// 二级部门id值等于三级人员的父id值
if (twoDeptId.split("\\|")[3].equals(threeMemberId.split("\\|")[1])) {
// 保存三级人员id
threeList.add(new HashMap() {{
put("type", 2);
put("pid", threeMemberId.split("\\|")[1]);
put("id", threeMemberId.split("\\|")[3]);
put("num", threeMemberId.split("\\|")[2]);
}});
}
});
// 一级部门id值等于二级部门的父id值
if (oneDeptId.split("\\|")[3].equals(twoDeptId.split("\\|")[1])) {
// 保存二级部门
twoList.add(new HashMap() {{
put("type", 1);
put("pid", twoDeptId.split("\\|")[1]);
put("id", twoDeptId.split("\\|")[3]);
put("num", twoDeptId.split("\\|")[2]);
if (threeList.size() != 0) {
put("child", threeList); // 保存第三层数据
}
}});
// // 配合递归使用
// Map map = new HashMap();
// map.put("type", 1);
// map.put("pid", twoDeptId.split("\\|")[1]);
// map.put("id", twoDeptId.split("\\|")[3]);
// map.put("num", twoDeptId.split("\\|")[2]);
// if (threeList.size() != 0) {
// map.put("child", threeList); // 保存第三层数据
// }
// twoList.add(map);
}
});
memberIdList.forEach(twoMemberId -> {
// 一级部门id值等于二级人员的父id值
if (oneDeptId.split("\\|")[3].equals(twoMemberId.split("\\|")[1])) {
// 保存二级人员id
twoList.add(new HashMap() {{
put("type", 2);
put("pid", twoMemberId.split("\\|")[1]);
put("id", twoMemberId.split("\\|")[3]);
put("num", twoMemberId.split("\\|")[2]);
}});
}
});
return twoList;
}
service方法
@Transactional
@Override
public void saveFile(File file) {
Integer userId = (Integer) SecurityUtils.getSubject().getPrincipal();
if (file.getId() != null) { // 修改
file.setUpdateBy(userId); // 设置更新者id
fileMapper.updateFileById(file); // 修改
// 删除已有的文件用户分类id
fileMapper.deleteFileUserCateIdByFileId(file.getId());
// 删除已有的文件用户id
fileMapper.deleteFileUserIdByFileId(file.getId());
} else {
file.setCreateBy(userId); // 设置创建者id
file.setUpdateBy(userId); // 设置更新者id
fileMapper.insertFile(file); // 增加
}
// 增加文件分类id
for (Integer id : file.getUserCateIds()) {
if (id != null) {
fileMapper.insertFileUserCateId(file.getId(), id);
}
}
for (Integer userId_1 : file.getUserIds()) {
if (userId_1 != null) {
// 新增文件用户id
fileMapper.insertFileUserId(file.getId(), userId_1);
}
}
}