若依框架构造树形结构数据

若依框架构造树形结构数据

一、数据库表结构

以分组为例,仅创建分组所需字段,其余业务需要字段后续再添加即可

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());
    }
}

posted @ 2024-06-06 11:51  戒爱学Java  阅读(174)  评论(0编辑  收藏  举报