1、实现父级ID

  1. menu表中,新增字段pid

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PJ9JXzJL-1673186490002)(D:\桌面\Java学习\项目\管理系统–前后端分离\项目截图\42.jpg)]

实现id和pid之间的关联,例如pid=1的数据则是id=1的数据的子级

  1. 在Menu实体类中,添加pid字段,和children字段
@TableField(exist = false)  //在数据库中没有这个字段
    private List<Menu> children;

    private Integer pid;
  1. MenuController.java
//查询所有数据
    @GetMapping
    public Result findAll(@RequestParam(defaultValue = "") String name){
        QueryWrapper<Menu>queryWrapper=new QueryWrapper<>();
        queryWrapper.like("name",name);
        //查询所有数据
        List<Menu>list=menuService.list(queryWrapper);
        //找出pid为null的一级菜单
        List<Menu>parentNode = list.stream().filter(menu -> menu.getPid()==null).collect(Collectors.toList());
        //找出一级菜单的子菜单
        for (Menu menu:parentNode) {
            //筛选所有数据中pid=父级id的数据就是二级菜单
            menu.setChildren(list.stream().filter(m ->menu.getId().equals(m.getPid())).collect(Collectors.toList()));
        }
        return Result.success(parentNode);
    }
  • 改造了findAll接口,因为现在需要menu展现的树形数据,就是有一级菜单和二级菜单

2、前端实现树形数据

  1. 在Menu页面中

Menu.vue

<template>
    <div>
     <div style="padding:10px 0">
       <el-input style="width:200px" placeholder="请输入名称" suffix-icon="el-icon-search" v-model="name"></el-input>
       <el-button class="ml-5" type="primary" @click="load">搜索</el-button>
       <el-button  type="warning" @click="reset">重置</el-button>
      
     </div>

     <div style="margin:10px 0">
       <el-button type="primary" @click="handleAdd()">新增<i class="el-icon-circle-plus-outline"></i></el-button>
       <el-popconfirm
                class="ml-5"
                confirm-button-text='确定'
                cancel-button-text='我再想想'
                icon="el-icon-info"
                icon-color="red"
                title="您确定要删除这些内容吗?"
                @confirm="delBatch"
          >
          <el-button type="danger"  slot="reference">批量删除<i class="el-icon-remove-outline"></i></el-button>
          </el-popconfirm>
     </div>
     <el-table :data="tableData" border stripe :header-cell-calss-name="'headerBg'" 
                row-key="id"  default-expand-all   @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55"></el-table-column>
      <el-table-column prop="id" label="ID" width="80"></el-table-column>
       <el-table-column prop="name" label="名称" ></el-table-column>
       <el-table-column prop="path" label="路径" ></el-table-column>
       <el-table-column prop="icon" label="图标" ></el-table-column>
       <el-table-column prop="description" label="描述" ></el-table-column>
       <el-table-column label="操作" width="300" align="center">
         <template slot-scope="scope" >
          <!--没有路径(说明有子菜单)和没有父级(说明会有子菜单)才加新增子菜单的按钮 -->
          <el-button type="primary" @click="handleMenuAdd(scope.row.id)" v-if="!scope.row.pid && !scope.row.path">新增子菜单 <i class="el-icon-plus"></i></el-button>

           <el-button type="success" @click="handleUpdate(scope.row)">编辑 <i class="el-icon-edit"></i></el-button>
          <el-popconfirm
                class="ml-5"
                confirm-button-text='确定'
                cancel-button-text='我再想想'
                icon="el-icon-info"
                icon-color="red"
                title="您确定要删除吗?"
                @confirm="handleDelete(scope.row.id)"
          >
          <el-button type="danger" slot="reference">删除<i class="el-icon-remove-outline"></i></el-button>
          </el-popconfirm>
         </template>
       </el-table-column>
     </el-table>

      <el-dialog title="菜单信息" :visible.sync="dialogFormVisible" width="30%">
        <el-form label-width="80px" size="small">
          <el-form-item label="名称" >
            <el-input v-model="form.name" autocomplete="off"></el-input>
          </el-form-item>
          <el-form-item label="路径" >
            <el-input v-model="form.path" autocomplete="off"></el-input>
          </el-form-item>
          <el-form-item label="图标" >
            <el-input v-model="form.icon" autocomplete="off"></el-input>
          </el-form-item>
          <el-form-item label="描述" >
            <el-input v-model="form.description" autocomplete="off"></el-input>
          </el-form-item>
        </el-form>
        <div slot="footer" class="dialog-footer">
          <el-button @click="dialogFormVisible = false">取 消</el-button>
          <el-button type="primary" @click="save">确 定</el-button>
        </div>
      </el-dialog>
    </div>
</template>

<script>
export default{
    name:"User",
    data() {
     return {
       tableData:[],
       total: 0 ,
       pageNum:1,
       pageSize:4,
       name:"",
       form:{},
       dialogFormVisible:false,
       multipleSelection:[]
     }
    },
    created(){
        this.load()

    },
    methods:{
        load(){
      this.request.get("/menu",{
        params:{
        name:this.name

      }
        }).then(res=>{
        this.tableData=res.data
      })
     },
     save(){
      this.request.post("/menu",this.form).then(res=>{
        if(res.data){
          this.$message.success("保存成功!")
          this.dialogFormVisible=false
          this.load()
        }else{
          this.$message.error("保存失败!")
        }
      })


     },
     handleAdd(){

      this.dialogFormVisible=true
      this.form={}
     },
     handleMenuAdd(pid){
      this.dialogFormVisible=true
      this.form={}
      if(pid){
        this.form.pid=pid}

     },
     handleUpdate(row){
      this.form={...row}
      this.dialogFormVisible=true
     
     },
     handleDelete(id){
      this.request.delete("/menu/" + id).then(res=>{
        if(res.data){
          this.$message.success("删除成功!")
          this.load()
        }else{
          this.$message.error("删除失败!")
        }
      })
     },
     delBatch(){
      let ids=this.multipleSelection.map(v => v.id)  //把对象数组转化为id数组【1,2,3】
      this.request.post("/menu/del/batch",ids).then(res=>{
        if(res.data){
          this.$message.success("批量删除成功!")
          this.load()
        }else{
          this.$message.error("批量删除失败!")
        }
      })

     },
     handleSelectionChange(val){
      this.multipleSelection=val



     },
     reset(){
      this.name=""
      this.load()

     },
    
     handleSizeChange(pageSize){
      this.pageSize=pageSize
      this.load()
     },
     handleCurrentChange(pageNum){
      this.pageNum=pageNum
      this.load()
     }

    }
}

</script>

<style>
.headerBg{
    background: #eee!important;
}
</style>
  • 新增了一个添加子菜单按钮,并且在“没有路径(说明有子菜单)和没有父级(说明会有子菜单)才加新增子菜单的按钮”,实现handleMenuAdd方法
  1. 去页面中,添加数据,根据自己的菜单添加如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ptO76gyE-1673186490003)(D:\桌面\Java学习\项目\管理系统–前后端分离\项目截图\43.jpg)]

3、实现角色表和菜单表互联

  1. 对Role表中MenuData改造,不用假数据,请求接口也改成请求menu

Role.vue

<template>
    <div>
     <div style="padding:10px 0">
       <el-input style="width:200px" placeholder="请输入名称" suffix-icon="el-icon-search" v-model="name"></el-input>
       <el-button class="ml-5" type="primary" @click="load">搜索</el-button>
       <el-button  type="warning" @click="reset">重置</el-button>
      
     </div>

     <div style="margin:10px 0">
       <el-button type="primary" @click="handleAdd">新增<i class="el-icon-circle-plus-outline"></i></el-button>
       <el-popconfirm
                class="ml-5"
                confirm-button-text='确定'
                cancel-button-text='我再想想'
                icon="el-icon-info"
                icon-color="red"
                title="您确定要删除这些内容吗?"
                @confirm="delBatch"
          >
          <el-button type="danger"  slot="reference">批量删除<i class="el-icon-remove-outline"></i></el-button>
          </el-popconfirm>
     </div>
     <el-table :data="tableData" border stripe :header-cell-calss-name="'headerBg'"    @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55"></el-table-column>
      <el-table-column prop="id" label="ID" width="80"></el-table-column>
       <el-table-column prop="name" label="名称" ></el-table-column>
       <el-table-column prop="description" label="描述" ></el-table-column>
       <el-table-column label="操作" width="280" align="center">
         <template slot-scope="scope" >
          <el-button type="info" @click="selectMenu(scope.row.id)">分配菜单<i class="el-icon-menu"></i></el-button>

           <el-button type="success" @click="handleUpdate(scope.row)">编辑 <i class="el-icon-edit"></i></el-button>
          <el-popconfirm
                class="ml-5"
                confirm-button-text='确定'
                cancel-button-text='我再想想'
                icon="el-icon-info"
                icon-color="red"
                title="您确定要删除吗?"
                @confirm="handleDelete(scope.row.id)"
          >
          <el-button type="danger" slot="reference">删除<i class="el-icon-remove-outline"></i></el-button>
          </el-popconfirm>
         </template>
       </el-table-column>
     </el-table>
     <div style="padding:10px 0">
       <el-pagination
       @size-change="handleSizeChange"
       @current-change="handleCurrentChange"
         :current-page="pageNum"
         :page-sizes="[2, 4, 6, 10]"
         :page-size="pageSize"
         layout="total, sizes, prev, pager, next, jumper"
         :total="total">
       </el-pagination>
     </div>

      <el-dialog title="角色信息" :visible.sync="dialogFormVisible" width="30%">
        <el-form label-width="80px" size="small">
          <el-form-item label="名称" >
            <el-input v-model="form.name" autocomplete="off"></el-input>
          </el-form-item>
          <el-form-item label="描述" >
            <el-input v-model="form.description" autocomplete="off"></el-input>
          </el-form-item>
        </el-form>
        <div slot="footer" class="dialog-footer">
          <el-button @click="dialogFormVisible = false">取 消</el-button>
          <el-button type="primary" @click="save">确 定</el-button>
        </div>
      </el-dialog>

      <el-dialog title="分配菜单" :visible.sync="menuDialogVisable" width="30%">
        <el-tree
            :props="props"
            :data="menuData"
            show-checkbox
            node-key="id"
            ref="tree"
            :default-expanded-keys="expands"
            :default-checked-keys="checks">
            <span class="custom-tree-node" slot-scope="{node,data}">
                <span><i :class="data.icon"></i> {{ data.name }}</span>
            </span>
        </el-tree>
        <div slot="footer" class="dialog-footer">
          <el-button @click="menuDialogVisable = false">取 消</el-button>
          <el-button type="primary" @click="saveRoleMenu">确 定</el-button>
        </div>
      </el-dialog>
    </div>
</template>

<script>
import { Tree } from 'element-ui'
export default{
    name:"User",
    data() {
     return {
       tableData:[],
       total: 0 ,
       pageNum:1,
       pageSize:4,
       name:"",
       form:{},
       dialogFormVisible:false,
       menuDialogVisable:false,
       multipleSelection:[],
       menuData: [],
       props:{
        label:'name'
       },
       expands:[],
       checks:[],
       roleId:0
     }
    },
    created(){
        this.load()
    },
    methods:{
        load(){
      this.request.get("/role/page",{
        params:{
        pageNum:this.pageNum,
        pageSize:this.pageSize,
        name:this.name

      }
        }).then(res=>{
        console.log(res)
        this.tableData=res.data.records
        this.total=res.data.total 
      })
     },
     save(){
      this.request.post("/role",this.form).then(res=>{
        if(res.data){
          this.$message.success("保存成功!")
          this.dialogFormVisible=false
          this.load()
        }else{
          this.$message.error("保存失败!")
        }
      })
           },
    saveRoleMenu(){
      this.request.post("/role/roleMenu/" + this.roleId,this.$refs.tree.getCheckedKeys()).then(res => {
       console.log(res)
      })

    },

    handleAdd(){

     this.dialogFormVisible=true
     this.form={}

    },
    handleUpdate(row){
     this.form={...row}
     this.dialogFormVisible=true
    
    },
    handleDelete(id){
     this.request.delete("/role/" + id).then(res=>{
       if(res.data){
         this.$message.success("删除成功!")
         this.load()
       }else{
         this.$message.error("删除失败!")
       }
     })
    },
    delBatch(){
     let ids=this.multipleSelection.map(v => v.id)  //把对象数组转化为id数组【1,2,3】
     this.request.post("/role/del/batch",ids).then(res=>{
       if(res.data){
         this.$message.success("批量删除成功!")
         this.load()
       }else{
         this.$message.error("批量删除失败!")
       }
     })

    },
    handleSelectionChange(val){
     this.multipleSelection=val
          },
    reset(){
     this.name=""
     this.load()

    },
   
    handleSizeChange(pageSize){
     this.pageSize=pageSize
     this.load()
    },
    handleCurrentChange(pageNum){
     this.pageNum=pageNum
     this.load()
    },
   selectMenu(roleId){
     this.menuDialogVisable=true;
     this.roleId=roleId
      //请求menu数据
      this.request.get("/menu").then(res=>{
         this.menuData=res.data

         //把后台返回的菜单数据处理成 id数组
         this.expands = this.menuData.map(v=>v.id)
     })

   }
   }
    }

   </script>

   <style>

   .headerBg{
  background: #eee!important;
        }

   </style>
  • 效果如图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d6Je4OPX-1673186490004)(D:\桌面\Java学习\项目\管理系统–前后端分离\项目截图\44.jpg)]

  • 默认展开和默认勾选都是可以设置的,通过 :default-expanded-keys=“[]” :default-checked-keys="[]"设置展开和默认勾选

页面显示效果如图,就完成了!