规定父节点为null的pid为root,否则为节点的id
@Data
public class Tree {
private String id;
private String name;
private Integer sortNumber;
private String pid;
private Integer type;
private List<Tree> children;
}
1. 展示树
获取所有数据,然后获取root节点,再根据root节点递归查找子节点
@Override
public List<Tree> getTree() {
List<Tree> resultList = new ArrayList<>();
//select * from tree order by sort_number
List<Tree> treeList = treeMapper.getTree();
//一级节点的父节点是root
for(Tree tree : treeList){
if (tree.getPid().equals("root")) {
resultList.add(tree);
}
}
//添加子节点
for(Tree tree :resultList){
tree.setChildren(getChild(tree.getId(), treeList));
}
//添加根节点
Tree rootNode = new Tree();
rootNode.setId("root");
rootNode.setName("根节点");
rootNode.setType(-1);
rootNode.setChildren(resultList);
List<Tree> rootTreeList = new ArrayList<>();
rootTreeList.add(rootNode);
return rootTreeList;
}
/**
* 添加子节点
* @param id
* @param treeList
* @return
*/
private List<Tree> getChild(String id, List<Tree> treeList) {
List<Tree> childList = new ArrayList<>();
for(Tree tree : treeList){
if(tree.getPid().equals(id)){
tree.setChildren(getChild(tree.getId(),treeList));
childList.add(tree);
}
}
return childList;
}
2. 添加子项
pid前端设值或者后端设值
// 设置树形表格 添加子项时的初始化
initTreeAddChild: function(rowItem) {
$scope.modalBean.isAdd = true;
$scope.modalBean.dataObj.parentName = rowItem.name;
$scope.modalBean.dataObj.pid = rowItem.id === null ? 'root' : rowItem.id;
}
后端获取当前pid下的最大排序号,如果为null,设值为1,否则+1
@Override
public Tree add(Tree tree) {
String id = UUID.randomUUID().toString().replaceAll("-","");
tree.setId(id);
// 获取当前父节点下的最大排序号
/*
select max(sort_number) from tree
<where>
<if test="pid != null and pid != ''">
and pid = #{pid}
</if>
</where>
*/
Integer maxSort = treeMapper.getMaxSnByPid(tree.getPid());
tree.setSortNumber(maxSort == null ? 1 : maxSort + 1);
treeMapper.add(tree);
return tree;
}
3. 删除
删除需要递归查询其子节点,再一并删除
@Override
public void delete(List<String> ids) {
// 需要删除子节点,获取需要删除的子节点集合
List<String> deleteIds = new ArrayList<>();
for (String id : ids) {
Tree tree = treeMapper.selectById(id);
if (tree != null) {
deleteIds.add(id);
this.setChildrenIds(id,deleteIds);
}
}
/*
delete from tree where id in
<foreach item="id" collection="ids" open="(" separator="," close=")">
#{id}
</foreach>
*/
treeMapper.deleteBatch(deleteIds);
}
/**
* 遍历节点下的子节点
*
* @param pid 需要遍历的父菜单 id
* @param deleteIds 需要删除的 id 集合
*/
private void setChildrenIds(String pid,List<String> deleteIds) {
// select id from tree where pid = #{pid}
List<String> childrenIds = treeMapper.getChildrenIdsByPid(pid);
if (!childrenIds.isEmpty()) {
for (String id : childrenIds) {
deleteIds.add(id);
this.setChildrenIds(id,deleteIds);
}
}
}
4. 上移,下移
上移,下移通过交换临近节点的排序号来实现
@Override
public void up(Tree tree) {
// select * from tree
// where pid = #{pid} and sort_number <![CDATA[<]]> #{sortNumber} order by sort_number desc limit 1
Tree prev = treeMapper.getPrevTree(tree);
if(prev != null){
exchangeSortNumber(tree,prev);
}
}
@Override
public void down(Tree tree) {
//select * from tree
// where pid = #{pid} and sort_number <![CDATA[>]]> #{sortNumber} order by sort_number asc limit 1
Tree down = treeMapper.getNextTree(tree);
if(down != null){
exchangeSortNumber(tree,down);
}
}
/**
* 交换排序号
*
* @param oldTree 旧节点
* @param newTree 新节点
*/
private void exchangeSortNumber(Tree oldTree ,Tree newTree) {
Integer tempSn = oldTree.getSortNumber();
oldTree.setSortNumber(newTree.getSortNumber());
newTree.setSortNumber(tempSn);
treeMapper.update(oldTree);
treeMapper.update(newTree);
}
5. 置顶
置顶是获取当前pid下的子节点中最小的排序号startSn,需要置顶的排序号endSn。
将[startSn,endSn)范围内的排序号全部+1,再将置顶排序号赋值给需要置顶的节点
@Override
public void top(Tree tree) {
// select min(sort_number) from tree where pid = #{pid}
Integer topSortNumber = treeMapper.getTopSnByPid(tree.getPid());
// 下移该节点以上的排序号
// update tree set sort_number = sort_number + 1
// where pid = #{pid} and sort_number <![CDATA[>=]]> #{startSn} and sort_number <![CDATA[<]]> #{endSn}
treeMapper.goNext(tree.getPid(),topSortNumber,tree.getSortNumber());
tree.setSortNumber(topSortNumber);
treeMapper.update(tree);
}