若依框架构造树形结构数据
若依框架构造树形结构数据
一、数据库表结构
以分组为例,仅创建分组所需字段,其余业务需要字段后续再添加即可
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for ggroup_data
-- ----------------------------
DROP TABLE IF EXISTS `group_data`;
CREATE TABLE `group_data` (
`group_id` bigint NOT NULL AUTO_INCREMENT COMMENT '分组id',
`parent_id` bigint NULL DEFAULT NULL COMMENT '父组id',
`ancestors` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '祖级列表',
`group_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '分组名称',
`order_num` int NULL DEFAULT NULL COMMENT '显示顺序',
`type` int NULL DEFAULT NULL COMMENT '节点类型,用于业务处理',
PRIMARY KEY (`group_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 58 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
SET FOREIGN_KEY_CHECKS = 1;
二、前端 - 表格
1、表格结构
<el-table v-if="refreshTable" v-loading="loading" :data="groupList" row-key="groupId" :default-expand-all="isExpandAll" :tree-props="{children:'children',hasChildren:'hasChildren'}">
<el-table-column label="分组名称" align="left" prop="groupName" />
<el-table-column label="排序" align="left" prop="orderNum" />
<el-table-column label="节点类型" align="center" prop="type" >
<template slot-scope="scope">
<span v-if="scope.row.type === 0">xxx</span>
<span v-else-if="scope.row.type === 1">xxx</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['device:group:edit']"> 修改</el-button>
<!-- 可根据不同的节点类型来实现不同的逻辑 -->
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['device:group:remove']">{{scope.row.type === 0 ? '删除' : '解绑/xxx' }}</el-button>
<el-button size="mini" type="text" icon="el-icon-plus" @click="handleAdd(scope.row)" v-hasPermi="['device:group:add']" v-if="scope.row.type===0">新增</el-button>
</template>
</el-table-column>
</el-table>
2、添加对话中上级节点
<!-- 添加或修改场景管理对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body @close="cancel">
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<!-- 上级节点 -->
<el-form-item label="上级分组" prop="parentId" v-if="form.parentId !== 0">
<treeselect v-model="form.parentId" :options="groupOptions" :normalizer="normalizer" placeholder="选择上级场景" />
</el-form-item>
<el-form-item label="类型" prop="type">
<el-radio-group v-model="form.type" size="small" @change="handleTypeChange($event)" :disabled="isUpdate">
<el-radio v-for="(item, index) in groupTypeList" :key="index" :label="item.value"
:disabled="item.disabled">{{item.label}}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</el-dialog>
3、需要引入依赖
import Treeselect from '@riophae/vue-treeselect'
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
4、data中数据
data() {
return {
// 遮罩层
loading: true,
// 分组管理表格数据
groupList: [],
// 是否展开,默认全部展开
isExpandAll: true,
refreshTable: true,
groupOptions:[],
//例子
groupTypeList: [
{"label": "文件夹","value": 0},
{"label": "SMCB", "value": 1},
{"label": "SMCT", "value": 2}
],
};
},
5、相关方法
/** 查询分组管理列表 */
getList() {
this.loading = true;
listGroup(this.queryParams).then(response => {
//TODO:使用handleTree构造为树形数据让表格显示为可折叠类型
this.groupList = this.handleTree(response.data,"groupId")
this.loading = false;
});
},
/** 新增按钮操作 */
handleAdd(row) {
//传统添加逻辑
//TODO:获取树形结构
listGroup().then(res => {
this.groupOptions = this.handleTree(res.data,"groupId");
})
},
/** 展开/折叠操作 */
toggleExpandAll() {
this.refreshTable = false;
this.isExpandAll = !this.isExpandAll;
this.$nextTick(() => {
this.refreshTable = true;
});
},
/** 修改按钮操作 */
handleUpdate(row) {
//传统修改逻辑
//TODO:查询除自身节点外的其他数据
listGroupExcludeChild(row.groupId).then(response => {
this.groupOptions = this.handleTree(response.data,"groupId");
if (this.groupOptions.length == 0){
const noResultOptions = {groupId:this.form.parentId,groupName:this.form.groupName,children:[]};
this.groupOptions.push(noResultOptions);
}
});
},
/** 转换分组数据结构 */
normalizer(node) {
if (node.children && !node.children.length) {
delete node.children;
}
return {
id: node.groupId,
label: node.groupName,
children: node.children
};
},
三、前端 - 侧边栏树形结构
1、侧边栏树形结构
<el-row :gutter="20">
<el-col :span="4" :xs="24">
<!-- 筛选分组节点 -->
<div class="head-container">
<el-input v-model="groupName" placeholder="请输入分组名称" clearable size="small" prefix-icon="el-icon-search" style="margin-bottom: 20px"/>
</div>
<!-- 分组树形结构 -->
<div class="head-container">
<el-tree :data="groupOptions" :props="defaultProps" :expand-on-click-node="false" :filter-node-method="filterNode" ref="tree" node-key="groupId" default-expand-all highlight-current @node-click="handleNodeClick"/>
</div>
</el-col>
<el-col :span="20" :xs="24">
</el-col>
</el-row>
2、data中数据
groupOptions: [],
groupName: "",
nodeInfo: {},
defaultProps: {
children: "children",
label: "label",
groupData: "groupData",
},
3、监听器方法
watch: {
//监听groupName,用于筛选分组数据
groupName(val) {
this.$refs.tree.filter(val);
},
},
4、相关方法
//节点过滤方法
filterNode(value, data) {
if (!value) return true;
return data.label.indexOf(value) !== -1;
},
//点击节点触发的方法
handleNodeClick(data) {
this.nodeInfo = data.groupData;
//TODO:后续的业务处理,例如建立连接/展示数据...
}
//获取分组树
getGroupTree() {
groupSelectTree().then(res => {
this.groupOptions = res.data;
})
},
四、后端
1、表格数据 - excludeChild
对应前端的listGroupExcludeChild
//controller
@GetMapping("/list/exclude/{groupId}")
public AjaxResult excludeChild(@PathVariable(value = "groupId",required = false)Long groupId)
{
//查询要分组列表
List<GroupData> list = groupDataService.selectGroupDataList(new GroupData());
//使用流的方式处理数据
list.removeIf(g -> g.getGroupId().intValue() == groupId || ArrayUtils.contains(StringUtils.split(g.getAncestors(),","),groupId+""));
return success(list);
}
2、树形数据
对应前端的groupSelectTree
//controller
@GetMapping("/groupSelectTree")
public AjaxResult selectGroupTreeList(GdtGroupData group){
logger.info("分组构建树为:{}",success(groupDataService.selectGroupTreeList(group)));
return success(groupDataService.selectGroupTreeList(group));
}
//---------------------------------------------------------------------------------------------------------------------------------------
//service
List<GroupTreeSelect> selectGroupTreeList(GroupData group);
//---------------------------------------------------------------------------------------------------------------------------------------
//impl
//1、查询树形结构数据
@Override
public List<GroupTreeSelect> selectGroupTreeList(GroupData group) {
List<GroupData> groupData = selectGroupDataList(group);
return buildGroupTreeSelect(groupData);
}
//2、构建前端下拉框结构
private List<GroupTreeSelect> buildGroupTreeSelect(List<GroupData> list) {
List<GroupData> groupDataList = buildGroupTree(list);
return groupDataList.stream().map(GroupTreeSelect::new).collect(Collectors.toList());
}
//3、构建前端所需树结构
public List<GroupData> buildGroupTree(List<GroupData> groups){
List<GroupData> resultList = new ArrayList<>();
List<Long> tempList = groups.stream().map(GroupData::getGroupId).collect(Collectors.toList());
for (GroupData group : groups) {
if (!tempList.contains(group.getParentId())){
recursionFn(groups,group);
resultList.add(group);
}
}
if (resultList.isEmpty()){
resultList = groups;
}
return resultList;
}
//4、递归列表
private void recursionFn(List<GroupData> list, GroupData g) {
List<GroupData> childList = getChildList(list,g);
g.setChildren(childList);
for (GroupData gChild : childList) {
if (hasChild(list,gChild)){
recursionFn(list,gChild);
}
}
}
//5、得到子节点
private List<GroupData> getChildList(List<GroupData> list, GroupData g) {
List<GroupData> tList = new ArrayList<>();
Iterator<GroupData> iterator = list.iterator();
while (iterator.hasNext()){
GroupData n = iterator.next();
if (StringUtils.isNotNull(n.getParentId()) && n.getParentId().longValue() == g.getGroupId().longValue()){
tList.add(n);
}
}
return tList;
}
//6、判断是否有子节点
private boolean hasChild(List<GroupData> list, GroupData gChild) {
return getChildList(list,gChild).size() > 0;
}
//---------------------------------------------------------------------------------------------------------------------------------------
//domain
/**
* GroupTreeselect树结构实体类
*
* @author Mr.Liu
*/
@Data
public class GroupTreeSelect implements Serializable
{
private static final long serialVersionUID = 1L;
/** 节点ID */
private Long id;
/** 节点名称 */
private String label;
/** 子节点 */
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private List<GroupTreeSelect> children;
private GroupData groupData;
public GroupTreeSelect() { }
public GroupTreeSelect(GroupData group)
{
this.id = group.getGroupId();
this.label = group.getGroupName();
this.groupData = group;
this.children = group.getChildren().stream().map(GroupTreeSelect::new).collect(Collectors.toList());
}
}