SpringBoot+VUE前后端分离项目学习笔记 - 【21 权限菜单 中】

news2024/11/18 22:52:15

1 新建了sys_dict表以及相应Dict类保存菜单menu的icon数据
2 新建了sys_role_menu表以及相应RoleMenu类保存前端Role页面传来的角色菜单ID的绑定关系
3 在MenuController里增加获取Dict里icon的方法 提供前端菜单页面显示
4 在RoleController增加Post接口,获取前台传来的角色菜单绑定关系存到数据库;同时编写Get接口请求最新的角色菜单关系,提供前台角色页面显示

数据库

新建sys_dict表,储存菜单menu的icon数据

CREATE TABLE `sys_dict` (
  `name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '名称',
  `value` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '内容',
  `type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '类型'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

在这里插入图片描述
新建sys_role_menu表,保存前端Role页面菜单分配得到的角色ID与菜单ID的关系

CREATE TABLE `sys_role_menu` (
  `role_id` int NOT NULL COMMENT '角色id',
  `menu_id` int NOT NULL COMMENT '菜单id',
  PRIMARY KEY (`role_id`,`menu_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='角色菜单关系表';

后台代码

整体结构

主要完成以下任务:
1 新建了Dict类保存菜单menu的icon数据
2 新建了RoleMenu类保存前端Role页面传来的角色菜单ID的绑定关系
3 在MenuController里增加获取Dict里icon的方法
4 在RoleController增加更新接口,将前台得到的角色与菜单的绑定关系传到后台,然后存到数据库的表里【采用先删后增的方式】

entity/Dict.java

新建Dict实体类以及DictMapper

package com.zj.demo.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

@TableName("sys_dict")
@Data
public class Dict {
    private String name;
    private String value;
    private String type;
}

DictMapper.java

package com.zj.demo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zj.demo.entity.Dict;

public interface DictMapper extends BaseMapper<Dict> {
}

common/Constants

在常量里增加icon类型,避免出错

package com.zj.demo.common;

public interface Constants {
    String CODE_200 = "200"; //成功
    String CODE_401 = "401";  // 权限不足
    String CODE_400 = "400";  // 参数错误
    String CODE_500 = "500"; // 系统错误
    String CODE_600 = "600"; // 其他业务异常

    String DICT_TYPE_ICON= "icon";
}

MenuController.java

增加获取icon的方法

package com.zj.demo.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zj.demo.common.Constants;
import com.zj.demo.common.Result;
import com.zj.demo.entity.Dict;
import com.zj.demo.entity.Menu;
import com.zj.demo.mapper.DictMapper;
import com.zj.demo.service.IMenuService;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author Joyce
 * @since 2023-01-10
 */
@RestController
@RequestMapping("/menu")
public class MenuController {

    @Resource
    private IMenuService menuService;

    @Resource
    private DictMapper dictMapper;

    // 新增或者更新
    @PostMapping
    public Result save(@RequestBody Menu menu) {
            return Result.success(menuService.saveOrUpdate(menu));
            }

    @DeleteMapping("/{id}")
    public Result delete(@PathVariable Integer id) {
            return Result.success(menuService.removeById(id));
            }

    @PostMapping("/del/batch")
    public Result deleteBatch(@RequestBody List<Integer> ids) {
            return Result.success(menuService.removeByIds(ids));
            }

    @GetMapping
    public Result findAll(@RequestParam(defaultValue ="") String name) {
        QueryWrapper<Menu> queryWrapper = new QueryWrapper<>();
        if (!"".equals(name)) {
            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);
    }

    @GetMapping("/{id}")
    public Result findOne(@PathVariable Integer id) {
            return Result.success(menuService.getById(id));
            }

    @GetMapping("/page")
    public Result findPage(@RequestParam(defaultValue ="") String name,
                           @RequestParam Integer pageNum,
                           @RequestParam Integer pageSize) {
            QueryWrapper<Menu> queryWrapper = new QueryWrapper<>();
            if (!"".equals(name)) {
                queryWrapper.like("name", name);
            }
            queryWrapper.orderByDesc("id");
            return Result.success(menuService.page(new Page<>(pageNum, pageSize), queryWrapper));
            }

    @GetMapping("/icons")
    public Result getIcons() {
        QueryWrapper<Dict> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("type", Constants.DICT_TYPE_ICON);
        return Result.success(dictMapper.selectList(queryWrapper));
    }

}


entity/RoleMenu.java

新建角色菜单类

package com.zj.demo.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

@TableName("sys_role_menu")
@Data
public class RoleMenu {
    private Integer roleId;
    private Integer menuId;

}


RoleMenuMapper.java

package com.zj.demo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zj.demo.entity.RoleMenu;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface RoleMenuMapper extends BaseMapper<RoleMenu> {

    @Delete("delete from sys_role_menu where role_id = #{roleId}")
    int deleteByRoleId(@Param("roleId") Integer roleId);

    @Select("select menu_id from sys_role_menu where role_id = #{roleId}")
    List<Integer> selectByRoleId(Integer roleId);
}

RoleController.java

写一个更新接口,将前台得到的角色与菜单的绑定关系传到后台,然后存到数据库的表里【采用先删后增的方式】,具体功能在Service的setRoleMenu方法里完成【具体代码在RoleServiceImpl.java】

package com.zj.demo.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zj.demo.common.Result;
import com.zj.demo.entity.Role;
import com.zj.demo.service.IRoleService;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author Joyce
 * @since 2023-01-10
 */
@RestController
@RequestMapping("/role")
public class RoleController {

    @Resource
    private IRoleService roleService;

    // 新增或者更新
    @PostMapping
    public Result save(@RequestBody Role role) {
            return Result.success(roleService.saveOrUpdate(role));
            }

    @DeleteMapping("/{id}")
    public Result delete(@PathVariable Integer id) {
            return Result.success(roleService.removeById(id));
            }

    @PostMapping("/del/batch")
    public Result deleteBatch(@RequestBody List<Integer> ids) {
            return Result.success(roleService.removeByIds(ids));
            }

    @GetMapping
    public Result findAll() {
            return Result.success(roleService.list());
            }

    @GetMapping("/{id}")
    public Result findOne(@PathVariable Integer id) {
            return Result.success(roleService.getById(id));
            }

    @GetMapping("/page")
    public Result findPage(
            @RequestParam(defaultValue ="") String name,
            @RequestParam Integer pageNum,
            @RequestParam Integer pageSize) {
            QueryWrapper<Role> queryWrapper = new QueryWrapper<>();
            if (!"".equals(name)) {
                queryWrapper.like("name", name);
            }
            queryWrapper.orderByDesc("id");
            return Result.success(roleService.page(new Page<>(pageNum, pageSize), queryWrapper));
            }

    /**
     * 绑定角色与菜单的关系
      * @param roleId 角色id
     * @param menuIds 菜单id集合
     * @return
     */

    @PostMapping("roleMenu/{roleId}")
    public Result roleMenu(@PathVariable Integer roleId,@RequestBody List<Integer> menuIds){
        roleService.setRoleMenu(roleId,menuIds);
        return Result.success();
    }
    /**
     * 获取角色与菜单的关系
     * @param roleId 角色id
     * @param menuIds 菜单id集合
     * @return
     */

    @GetMapping("roleMenu/{roleId}")
    public Result getroleMenu(@PathVariable Integer roleId){
        return Result.success(roleService.getRoleMenu(roleId));
    }
}


在这里插入图片描述
进入IRoleService
在这里插入图片描述
进入RoleServiceImpl
在这里插入图片描述
在这里插入图片描述

RoleServiceImpl.java

setRoleMenu方法编写, 有两种方法,
第一种使用QueryWrapper
第二种自己在Mapper里写delete方法【RoleMenuMapper.java——deleteByRoleId】

package com.zj.demo.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zj.demo.entity.Role;
import com.zj.demo.entity.RoleMenu;
import com.zj.demo.mapper.RoleMapper;
import com.zj.demo.mapper.RoleMenuMapper;
import com.zj.demo.service.IRoleService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author Joyce
 * @since 2023-01-10
 */
@Service
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IRoleService {

    @Resource
    private RoleMenuMapper roleMenuMapper;
    @Transactional
    @Override
    public void setRoleMenu(Integer roleId, List<Integer> menuIds) {
        //先删除当前角色id的所有绑定关系

//        QueryWrapper<RoleMenu> queryWrapper = new QueryWrapper<>();
//        queryWrapper.eq("role_id", roleId);
//        roleMenuMapper.delete(queryWrapper);
        roleMenuMapper.deleteByRoleId(roleId);

        //再把前端传过来的菜单id和角色id进行绑定

//        if (menuIds != null && menuIds.size() > 0)
//        {
//            menuIds.forEach(menuId -> {
//                RoleMenu roleMenu = new RoleMenu();
//                roleMenu.setMenuId(menuId);
//                roleMenu.setRoleId(roleId);
//                roleMenuMapper.insert(roleMenu);
//            });
//        }
        for (Integer menuId : menuIds)
        {
            RoleMenu roleMenu = new RoleMenu();
            roleMenu.setRoleId(roleId);
            roleMenu.setMenuId(menuId);
            roleMenuMapper.insert(roleMenu);
        }
    }
    @Override
    public List<Integer> getRoleMenu(Integer roleId) {
        return roleMenuMapper.selectByRoleId(roleId);
    }
}

前端代码

主要就是菜单页面的图标信息请求,以及角色页面菜单绑定数据的请求与上传

Menu.vue

请求icon数据,使得Menu前端的编辑里面的图标成为可选项

<template>
  <div>
    <div style="margin: 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="handleAdd1">新增 <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-class-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"></el-table-column>
      <el-table-column prop="name" label="名称"></el-table-column>
      <el-table-column prop="path" label="路径"></el-table-column>
      <el-table-column label="图标" class-name="fontSize16" align="center" label-class-name="fontSize12">
        <template slot-scope="scope">
          <span :class="scope.row.icon" />
        </template>
      </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="handleAdd(scope.row.id)" v-if="!scope.row.pid && !scope.row.path">新增子菜单 <i class="el-icon-plus"></i></el-button>
          <el-button type="success" @click="handleEdit(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="del(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-select clearable v-model="form.icon" placeholder="请选择" style="width: 100%">
            <el-option v-for="item in options" :key="item.name" :label="item.name" :value="item.value">
              <i :class="item.value"/> {{ item.name }}
            </el-option>
          </el-select>
        </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: "Menu",
  data() {
    return {
      tableData: [],
      total: 0,
      pageNum: 1,
      pageSize: 10,
      name: "",
      path: "",
      icon: "",
      description: "",
      form: {},
      dialogFormVisible: false,
      multipleSelection: [],
      options:[]
    }
  },
  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.code === '200') {
          this.$message.success("保存成功")
          this.dialogFormVisible = false
          this.load()
        } else {
          this.$message.error("保存失败")
        }
      })
    },
    del(id) {
      this.request.delete("/menu/" + id).then(res => {
        if (res.code === '200') {
          this.$message.success("删除成功")
          this.load()
        } else {
          this.$message.error("删除失败")
        }
      })
    },
    delBatch() {
      let ids = this.multipleSelection.map(v => v.id)  // [{}, {}, {}] => [1,2,3]
      this.request.post("/menu/del/batch", ids).then(res => {
        if (res.code === '200') {
          this.$message.success("批量删除成功")
          this.load()
        } else {
          this.$message.error("批量删除失败")
        }
      })
    },
    handleAdd1() {
      this.dialogFormVisible = true
      this.form = {}
    },
    handleAdd(pid) {
      this.dialogFormVisible = true
      this.form = {}
      if(pid)
      {
        this.form.pid = pid
      }

    },
    handleEdit(row) {
      this.form = row
      this.dialogFormVisible = true
      // 请求图标的数据
      this.request.get("/menu/icons").then(res => {
        this.options = res.data
      })
    },
    handleSelectionChange(val) {
      console.log(val)
      this.multipleSelection = val
    },
    reset() {
      this.name = ""
      this.load()
    },
    handleSizeChange(pageSize) {
      console.log(pageSize)
      this.pageSize = pageSize
      this.load()
    },
    handleCurrentChange(pageNum) {
      console.log(pageNum)
      this.pageNum = pageNum
      this.load()
    },
  }
}
</script>

<style>
.headerBg {
  background: #eee!important;
}
.fontSize16{
  font-size: 16px;
}
.fontSize12{
  font-size: 12px;
}
</style>


Role.vue

1、菜单分配加个图标并弄成展开形式
2、编写菜单分配的确定按钮所绑定的saveRoleMenu()方法,
3、selectMenu()方法除了原有的请求菜单数据,还要请求已有的角色菜单数据

<template>
  <div>
    <div style="margin: 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>
<!--      <el-upload action="http://localhost:9090/role/import" :show-file-list="false" accept="xlsx" :on-success="handleExcelImportSuccess" style="display: inline-block">-->
<!--        <el-button type="primary" class="ml-5">导入 <i class="el-icon-bottom"></i></el-button>-->
<!--      </el-upload>-->
<!--      <el-button type="primary" @click="exp" class="ml-5">导出 <i class="el-icon-top"></i></el-button>-->
    </div>

    <el-table :data="tableData" border stripe :header-cell-class-name="'headerBg'" @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55"></el-table-column>
      <el-table-column prop="id" label="ID"></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="handleEdit(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="del(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, 5, 10, 20]"
          :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="menuDialogVis" width="30%">
      <el-tree
          :props="props"
          :data="menuData"
          show-checkbox
          node-key="id"
          ref="tree"
          :default-expanded-keys="expends"
          :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="menuDialogVis = false">取 消</el-button>
        <el-button type="primary" @click="saveRoleMenu">确 定</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
export default {
  name: "Role",
  data() {
    return {
      tableData: [],
      total: 0,
      pageNum: 1,
      pageSize: 10,
      name: "",
      description: "",
      form: {},
      dialogFormVisible: false,
      menuDialogVis: false,
      multipleSelection: [],
      menuData: [],
      props:{
        label:'name',
      },
      expends: [],
      checks: [],
      roleId: 0,
      roleFlag: '',
      ids: []
    }
  },
  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.code === '200') {
          this.$message.success("保存成功")
          this.dialogFormVisible = false
          this.load()
        } else {
          this.$message.error("保存失败")
        }
      })
    },
    del(id) {
      this.request.delete("/role/" + id).then(res => {
        if (res.code === '200') {
          this.$message.success("删除成功")
          this.load()
        } else {
          this.$message.error("删除失败")
        }
      })
    },
    delBatch() {
      let ids = this.multipleSelection.map(v => v.id)  // [{}, {}, {}] => [1,2,3]
      this.request.post("/role/del/batch", ids).then(res => {
        if (res.code === '200') {
          this.$message.success("批量删除成功")
          this.load()
        } else {
          this.$message.error("批量删除失败")
        }
      })
    },
    handleAdd() {
      this.dialogFormVisible = true
      this.form = {}
    },
    handleEdit(row) {
      this.form = row
      this.dialogFormVisible = true
    },
    handleSelectionChange(val) {
      console.log(val)
      this.multipleSelection = val
    },
    reset() {
      this.name = ""
      this.load()
    },
    handleSizeChange(pageSize) {
      console.log(pageSize)
      this.pageSize = pageSize
      this.load()
    },
    handleCurrentChange(pageNum) {
      console.log(pageNum)
      this.pageNum = pageNum
      this.load()
    },

    saveRoleMenu() {
      this.request.post("/role/roleMenu/" + this.roleId, this.$refs.tree.getCheckedKeys()).then(res => {
        if (res.code === '200') {
          this.$message.success("绑定成功")
          this.menuDialogVis = false
        } else {
          this.$message.error(res.msg)
        }
      })
    },

    selectMenu(roleId) {
      this.menuDialogVis = true
      this.roleId = roleId
      //请求菜单数据
      this.request.get("/menu").then(res =>
      {
        this.menuData = res.data
        // 把后台返回的菜单数据处理成 id数组
        this.expends = this.menuData.map(v => v.id)
      })

      //请求角色菜单数据
      this.request.get("/role/roleMenu/" + this.roleId).then(res => {
        this.checks = res.data
      })
    },
  }
}
</script>


<style>
.headerBg {
  background: #eee!important;
}
</style>

前端页面展示

菜单编辑图标显示

在这里插入图片描述

用户页面菜单分配设置为展开形式的图标

在这里插入图片描述

角色与菜单关系绑定,并且传输数据到后台,存入数据库

在这里插入图片描述
数据库里存入相应关系数据
在这里插入图片描述

Role页面加入角色菜单关系请求后,显示数据库内的关系数据

以4用户为例,分配菜单勾选主页、用户和文件后,点击确定,绑定成功
在这里插入图片描述
再次点开分配菜单,显示已有的菜单绑定数据在这里插入图片描述

其他补充

@Resource和@Autowire的区别

@Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按byName自动注入罢了。@Resource有两个属性是比较重要的,分是name和type,Spring将@Resource注解的name属性解析为bean的名字。

二者具体区别:
1、@Autowired与@Resource都可以用来装配bean.都可以写在字段上,或写在setter方法上。
2、@Autowired默认按类型装配(这个注解是属业spring的),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false),如果我们想使用名称装配可以结合@Qualifier注解进行使用。
3、@Resource(这个注解属于J2EE的),默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

IntelliJ Idea 常用快捷键列表

快捷键一览

Alt+Enter 导入包,自动修正【我们这里新建被调用方法用的就是这个】
Ctrl+N 查找类
Ctrl+Shift+N 查找文件
Ctrl+Alt+L 格式化代码
Ctrl+Alt+O 优化导入的类和包
Alt+Insert 生成代码(如get,set方法,构造函数等)
Ctrl+E或者Alt+Shift+C 最近更改的代码
Ctrl+R 替换文本
Ctrl+F 查找文本
Ctrl+Shift+Space 自动补全代码
Ctrl+空格 代码提示
Ctrl+Alt+Space 类名或接口名提示
Ctrl+P 方法参数提示
Ctrl+Shift+Alt+N 查找类中的方法或变量
Alt+Shift+C 对比最近修改的代码

Shift+F6 重构-重命名
Ctrl+Shift+先上键
Ctrl+B可以查看类或方法定义,但是不如ctrl+鼠标左键方便。
Ctrl+X 删除行
Ctrl+D 复制行
Ctrl+/ 或 Ctrl+Shift+/ 注释(// 或者// )
Ctrl+J 自动代码
Ctrl+E 最近打开的文件
Ctrl+H 显示类结构图
Ctrl+Q 显示注释文档
Alt+F1 查找代码所在位置
Alt+1 快速打开或隐藏工程面板
Ctrl+Alt+ left/right 返回至上次浏览的位置
Alt+ left/right 切换代码视图
Alt+ Up/Down 在方法间快速移动定位
Ctrl+Shift+Up/Down 代码向上/下移动。
F2 或Shift+F2 高亮错误或警告快速定位

代码标签输入完成后,按Tab,生成代码。
选中文本,按Ctrl+Shift+F7 ,高亮显示所有该文本,按Esc高亮消失。
Ctrl+W 选中代码,连续按会有其他效果
选中文本,按Alt+F3 ,逐个往下查找相同文本,并高亮显示。
Ctrl+Up/Down 光标跳转到第一行或最后一行下
Ctrl+B 快速打开光标处的类或方法

最常用快捷键

1.Ctrl+E,可以显示最近编辑的文件列表
2.Shift+Click可以关闭文件
3.Ctrl+[或]可以跳到大括号的开头结尾
4.Ctrl+Shift+Backspace可以跳转到上次编辑的地方
5.Ctrl+F12,可以显示当前文件的结构
6.Ctrl+F7可以查询当前元素在当前文件中的引用,然后按F3可以选择
7.Ctrl+N,可以快速打开类
8.Ctrl+Shift+N,可以快速打开文件
9.Alt+Q可以看到当前方法的声明
10.Ctrl+W可以选择单词继而语句继而行继而函数
11.Alt+F1可以将正在编辑的元素在各个面板中定位
12.Ctrl+P,可以显示参数信息
13.Ctrl+Shift+Insert可以选择剪贴板内容并插入
14.Alt+Insert可以生成构造器/Getter/Setter等
15.Ctrl+Alt+V 可以引入变量。例如把括号内的SQL赋成一个变量
16.Ctrl+Alt+T可以把代码包在一块内,例如try/catch
17.Alt+Up and Alt+Down可在方法间快速移动
下面的不是很有用
18.在一些地方按Alt+Enter可以得到一些Intention Action,例如将”==”改为”equals()”
19.Ctrl+Shift+Alt+N可以快速打开符号
20.Ctrl+Shift+Space在很多时候都能够给出Smart提示
21.Alt+F3可以快速寻找
22.Ctrl+/和Ctrl+Shift+/可以注释代码
23.Ctrl+Alt+B可以跳转到抽象方法的实现
24.Ctrl+O可以选择父类的方法进行重写
25.Ctrl+Q可以看JavaDoc
26.Ctrl+Alt+Space是类名自动完成
27.快速打开类/文件/符号时,可以使用通配符,也可以使用缩写
28.Live Templates! Ctrl+J
29.Ctrl+Shift+F7可以高亮当前元素在当前文件中的使用
30.Ctrl+Alt+Up /Ctrl+Alt+Down可以快速跳转搜索结果
31.Ctrl+Shift+J可以整合两行
32.Alt+F8是计算变量值

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/159875.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

66页3万字医疗行业大数据治理解决方案

【版权声明】本资料来源网络&#xff0c;知识分享&#xff0c;仅供个人学习&#xff0c;请勿商用。【侵删致歉】如有侵权请联系小编&#xff0c;将在收到信息后第一时间删除&#xff01;完整资料领取见文末&#xff0c;部分资料内容&#xff1a; 目 录 1. 1、医疗行业大数据管…

分享116个PHP源码,总有一款适合您

PHP源码 分享116个PHP源码&#xff0c;总有一款适合您 116个PHP源码链接&#xff1a;https://pan.baidu.com/s/1dsupZiZbKqvHPmlpIAgWqA?pwdg52q 提取码&#xff1a;g52q import os import shutil import time from time import sleepimport requests from bs4 import Bea…

C++11静态断言static_assert

C11静态断言static_assert一、运行时断言二、静态断言的需求三、静态断言四、单参数版本的静态断言一、运行时断言 断言&#xff08;assertion&#xff09;是一种编程中常用的手段。在通常情况下&#xff0c;断言就是将一个返回值总是需要为真的判别式放在语句中&#xff0c;用…

Oracle No-Fee Terms and Conditions (NFTC)到底有啥条款?

1995年Sun微系统公司推出Java至今已有28年的历史&#xff0c;由于厂商持续升级优化&#xff0c;使用场景广阔&#xff0c;生态完善&#xff0c;Java目前仍然保持着非常旺盛的生命力。 付费许可 2019年java更新了许可政策 https://www.oracle.com/java/technologies/javase/ja…

【一文速通】机器学习样本不均衡/数据分布不同怎么办?

样本不均衡是什么意思样本&#xff08;类别&#xff09;样本不平衡&#xff08;class-imbalance&#xff09;指的是分类任务中不同类别的训练样例数目差别很大的情况&#xff0c;一般地&#xff0c;样本类别比例&#xff08;Imbalance Ratio&#xff09;&#xff08;多数类vs少…

antd中Tree组件使用方法个人笔记

一、前言 最近在自己自学前端&#xff0c;不清楚学习路线&#xff0c;只能盯着公司的前端项目硬看。 公司的前端项目是react框架&#xff0c;Ant Design Pro。 之前刚把router.config.js的逻辑理顺&#xff0c;目前准备开发个简单的前端页面。 在此总结下antd中<Tree>…

【算法刷题 DAY04】剑指offer树3和队列与栈总结

JZ36 二叉搜索树与双向链表 描述 输入一棵二叉搜索树&#xff0c;将该二叉搜索树转换成一个排序的双向链表。如下图所示 注意: 1.要求不能创建任何新的结点&#xff0c;只能调整树中结点指针的指向。当转化完成以后&#xff0c;树中节点的左指针需要指向前驱&#xff0c;树中…

虹科新闻 | 虹科与weeve正式建立合作伙伴关系

近日&#xff0c;虹科与weeve正式建立合作伙伴关系&#xff0c;双方就工业应用自动化领域进行深入的交流与合作&#xff0c;未来将共同致力于为中国市场提供完整的物联网边缘服务解决方案&#xff0c;解决中国客户的物联网挑战。 虹科与weeve都表示十分期待这次的合作。“虹科…

day36【代码随想录】贪心算法之根据身高重建队列、用最少数量的箭引爆气球、无重叠区间

文章目录前言一、根据身高重建队列&#xff08;力扣406&#xff09;二、用最少数量的箭引爆气球&#xff08;力扣452&#xff09;三、无重叠区间&#xff08;力扣435&#xff09;前言 1、根据身高重建队列 2、用最少数量的箭引爆气球 3、无重叠区间 一、根据身高重建队列&…

魔改插线板,让电视控制周边设备开关机

一.我的需求 本人是一个极简主义风格的人&#xff0c;自从用了N1盒子刷了coreELEC 系统后&#xff0c;就不断的进行折腾&#xff0c;跟大家说下我的心路历程。 1.我家很少看电视&#xff0c;不想因为偶尔开一次电视就每个月交24块钱&#xff0c;所以把广电的机顶盒停掉了。 2.电…

TextView

1.简介 向用户显示文本的用户界面元素。 2.常见使用 2.1 设置文本内容 //xml 硬编码 <TextView android:text"文本"/> //xml 推荐放在string.xml,为了国际化考虑 <TextView android:text"string/app_name"/> //kotlin tv.text getStr…

零基础学员的shell脚本的写作思路详解

前言 这两天一直再批改学员的脚本作业&#xff0c;大多数学员写的很好&#xff0c;有的学员写的不太好。 还有一些还没有入门到学员不知道脚本该咋写。 不知道脚本怎么写的学员&#xff0c;绝大多数犯了一个错误&#xff1a;一上来就把脚本想的太复杂了。 我们今天单独聊聊这…

以研究用途搭建OpenStreetMap Virtualbox服务器

又到了新年伊始&#xff0c;下载OpenStreetMap全球数据的时候了。结果惊奇的发现&#xff0c;主站已经无法打开。仔细了解了原委&#xff0c;表示理解。好在PBF数据依旧可以获取&#xff0c;只是瓦片服务已经关停。 1.OpenStreetMap的主要问题 OpenStreetMap之所以被Blocked&…

力扣刷题记录——459.重复的字符串、461. 汉明距离、476. 数字的补数

本专栏主要记录力扣的刷题记录&#xff0c;备战蓝桥杯&#xff0c;供复盘和优化算法使用&#xff0c;也希望给大家带来帮助&#xff0c;博主是算法小白&#xff0c;希望各位大佬不要见笑&#xff0c;今天要分享的是——《459.重复的字符串、461. 汉明距离、476. 数字的补数》。…

Mysql数据库中的表

创建表 和之前的创建库差不多&#xff0c;需要多指定一个engine&#xff08;不写默认INNODB&#xff09;这个后面说 字符集还有校对规则不指定的话和库的保持一致 案例 图形化创建就不说了&#xff0c;主要就指令 # 演示关于创建表的操作 # 在db02创建表USER包含id-整形 name…

(9)go-micro微服务Redis配置

文章目录一 go-redis介绍二 go-redis安装三 redis初始化连接四 存储mail邮件五 存储token六 最后一 go-redis介绍 Redis&#xff08;Remote Dictionary Server )&#xff0c;即远程字典服务&#xff0c;是一个开源的、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库…

【验证码逆向专栏】某验二代滑块验证码逆向分析

声明 本文章中所有内容仅供学习交流&#xff0c;抓包内容、敏感网址、数据接口均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff0c;若有侵权&#xff0c;请联系我立即删除&#xff01; 本文章未经许可禁止转载…

svn安装与配置并集成

svn安装 客户端安装与下载 下载地址&#xff1a;https://tortoisesvn.net/downloads.zh.html 根据自身设备情况下载 1、更换安装路径&#xff0c;点击next&#xff08;如果安装路径选择不了那么先点击next在点击back就可以选择安装路径了&#xff09; 2、如果需要集成idea在下…

详解“陌生的“位段

目录 一、什么是位段&#xff1f; 二、位段的内存分配 三、位段的应用 一、什么是位段&#xff1f; C 语言允许一个结构体中以位为单位来指定其成员所占内存长度&#xff0c;这种以位为单位的成员称为"位段"或"位域"&#xff08;bit field&#xff09;…

Golang——异常和error

Go 提供了两种处理错误 方式&#xff0c; 一 种是借助 panic和 recover 的抛出捕获机制&#xff0c; 另一种使用error 错误类型 一、异常 1、go没有try/catch&#xff0c;而是使用panic/recover。 panic包出异常&#xff0c;后续代码不再执行 recover再defer中捕获异常&#x…