目录
1、权限菜单后端接口
2、查询权限菜单列表
2.1 设计效果图
3、 新增权限菜单
3.1 新增权限菜单窗口代码
3.2 选择所属菜单代码
3.3 封装图标选择器
3.4 新增、编辑和删除权限菜单
1、权限菜单后端接口
package com.cizhu.service;
import com.cizhu.entity.Permission;
import com.baomidou.mybatisplus.extension.service.IService;
import com.cizhu.vo.query.PermissionQueryVo;
import java.util.List;
/**
* <p>
* 服务类
* </p>
*
* @author cizhu
* @since 2023-12-14
*/
public interface IPermissionService extends IService<Permission> {
/**
* 根据用户ID查询菜单列表
* @param userId
* @return
*/
List<Permission> findPermissionListByUserId(Long userId);
List<Permission> findPermissionList(PermissionQueryVo permissionQueryVo);
boolean hasChildrenOfPermission(Long id);
List<Permission> findParentPermissionList();
}
package com.cizhu.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cizhu.entity.Permission;
import com.cizhu.mapper.PermissionMapper;
import com.cizhu.service.IPermissionService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cizhu.utils.MenuTree;
import com.cizhu.vo.query.PermissionQueryVo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
/**
* <p>
* 服务实现类
* </p>
*
* @author cizhu
* @since 2023-12-14
*/
@Service
@Transactional
public class PermissionServiceImpl extends ServiceImpl<PermissionMapper, Permission> implements IPermissionService {
@Override
public List<Permission> findPermissionListByUserId(Long userId) {
return baseMapper.findPermissionListByUserId(userId);
}
@Override
public List<Permission> findPermissionList(PermissionQueryVo permissionQueryVo) {
//创建条件构造器对象
QueryWrapper<Permission> queryWrapper = new QueryWrapper<Permission>();
//排序
queryWrapper.orderByAsc("order_num");
//调用查询菜单列表的方法
List<Permission> permissionList = baseMapper.selectList(queryWrapper);
//生成菜单树
List<Permission> menuTree = MenuTree.makeMenuTree(permissionList, 0L);
//返回数据
return menuTree;
}
@Override
public boolean hasChildrenOfPermission(Long id) {
return false;
}
@Override
public List<Permission> findParentPermissionList() {
QueryWrapper<Permission> queryWrapper = new QueryWrapper<Permission>();
//只查询type为目录和菜单的数据(type=0或type=1)
queryWrapper.in("type", Arrays.asList(0,1));
//排序
queryWrapper.orderByAsc("order_num");
//查询菜单数据
List<Permission> permissionList = baseMapper.selectList(queryWrapper);
//构造顶级菜单信息,如果数据库中的菜单表没有数据,选择上级菜单时则显示顶级菜单
Permission permission = new Permission();
permission.setId(0L);
permission.setParentId(-1L);
permission.setLabel("顶级菜单");
permissionList.add(permission);//将顶级菜单添加到集合
//生成菜单数据
List<Permission> menuTree = MenuTree.makeMenuTree(permissionList, -1L);
//返回数据
return menuTree;
}
}
package com.cizhu.controller;
import com.cizhu.entity.Permission;
import com.cizhu.service.IPermissionService;
import com.cizhu.utils.Result;
import com.cizhu.vo.query.PermissionQueryVo;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* <p>
* 前端控制器
* </p>
*
* @author cizhu
* @since 2023-12-14
*/
@RestController
@RequestMapping("/api/permission")
public class PermissionController {
@Resource
private IPermissionService permissionService;
/**
* 查询菜单列表
* @param permissionQueryVo
* @return
*/
@GetMapping("/getPermissionList")
@PreAuthorize("hasAuthority('sys:menu:select')")
public Result getPermissionList(PermissionQueryVo permissionQueryVo){
List<Permission> resultPermissionList = permissionService.findPermissionList(permissionQueryVo);
return Result.ok(resultPermissionList);
}
/**
* 查询上级菜单列表
* @return
*/
@GetMapping("/parent/list")
public Result getParentPermissionList(){
List<Permission> parentPermissionList = permissionService.findParentPermissionList();
return Result.ok(parentPermissionList);
}
/**
* 根据id查询菜单信息
* @param id
* @return
*/
@GetMapping("/{id}")
public Result getMenuById(@PathVariable Long id){
return Result.ok(permissionService.getById(id));
}
/**
* 添加菜单
* @param permission
* @return
*/
@PostMapping("/addPermission")
@PreAuthorize("hasAuthority('sys:menu:add')")
public Result addPermission(@RequestBody Permission permission){
if (permissionService.save(permission)) return Result.ok().message("菜单新增成功!");
return Result.error().message("菜单新增失败!");
}
/**
* 修改菜单
* @param permission
* @return
*/
@PutMapping("/editPermission")
@PreAuthorize("hasAuthority('sys:menu:edit')")
public Result editPermission(@RequestBody Permission permission){
if(permissionService.updateById(permission)) return Result.ok().message("修改菜单成功!");
return Result.error().message("修改菜单失败!");
}
/**
* 根据id删除菜单
* @param id
* @return
*/
@DeleteMapping("/delete/{id}")
@PreAuthorize("hasAuthority('sys:menu:delete')")
public Result deletePermissionById(@PathVariable Long id){
if (permissionService.removeById(id)) return Result.ok().message("删除菜单成功!");
return Result.ok().message("删除菜单失败!");
}
/**
* 检查菜单是否有子菜单
* @param id
* @return
*/
@GetMapping("/check/{id}")
public Result hasChildrenOfPermission(@PathVariable Long id){
if (permissionService.hasChildrenOfPermission(id)) {
return Result.exist().message("该菜单下有子菜单,不能删除!");
}
return Result.ok();
}
}
package com.cizhu.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* <p>
*
* </p>
*
* @author cizhu
* @since 2023-12-14
*/
@Getter
@Setter
@TableName("sys_role_permission")
@ApiModel(value = "RolePermission对象", description = "")
public class RolePermission implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("角色ID")
private Long roleId;
@ApiModelProperty("权限ID")
private Long permissionId;
}
package com.cizhu.vo.query;
import com.cizhu.entity.Permission;
import lombok.Data;
@Data
public class PermissionQueryVo extends Permission {
}
package com.cizhu.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* <p>
*
* </p>
*
* @author cizhu
* @since 2023-12-14
*/
@Getter
@Setter
@TableName("sys_permission")
@ApiModel(value = "Permission对象", description = "")
public class Permission implements Serializable {
/**
* 子菜单列表
*/
@JsonInclude(JsonInclude.Include.NON_NULL) //属性值为null不进行序列化操作
@TableField(exist = false)
private List<Permission> children = new ArrayList<Permission>();
/**
* 用于前端判断是菜单、目录或按钮
*/
@TableField(exist = false)
private String value;
/**
* 是否展开
*/
@TableField(exist = false)
private Boolean open;
private static final long serialVersionUID = 1L;
@ApiModelProperty("权限编号")
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@ApiModelProperty("权限名称")
private String label;
@ApiModelProperty("父权限ID")
private Long parentId;
@ApiModelProperty("父权限名称")
private String parentName;
@ApiModelProperty("授权标识符")
private String code;
@ApiModelProperty("路由地址")
private String path;
@ApiModelProperty("路由名称")
private String name;
@ApiModelProperty("授权路径")
private String url;
@ApiModelProperty("权限类型(0-目录 1-菜单 2-按钮)")
private Integer type;
@ApiModelProperty("图标")
private String icon;
@ApiModelProperty("创建时间")
private Date createTime;
@ApiModelProperty("修改时间")
private Date updateTime;
@ApiModelProperty("备注")
private String remark;
@ApiModelProperty("排序")
private Integer orderNum;
@ApiModelProperty("是否删除(0-未删除,1-已删除)")
private Integer isDelete;
}
2、查询权限菜单列表
2.1 设计效果图
2.2 menuList.vue
列表页面原型代码,编写前端api脚本和页面组件代码
<template>
<el-main>
<!-- 新增按钮 -->
<el-button type="success" size="small" @click="openAddWindow()" icon="el-icon-plus">新增</el-button>
<!-- 数据表格 -->
<el-table style="margin-top: 10px" :height="tableHeight" :data="menuList" row-key="id"
default-expand-all :tree-props="{ children: 'children' }" :border="true" stripe>
<el-table-column prop="label" label="菜单名称"></el-table-column>
<el-table-column prop="type" label="菜单类型" align="center">
<template slot-scope="scope">
<el-tag v-if="scope.row.type == '0'" size="normal">目录</el-tag>
<el-tag type="success" v-else-if="scope.row.type == '1'" size="normal">菜单</el-tag>
<el-tag type="warning" v-else-if="scope.row.type == '2'" size="normal">按钮</el-tag>
</template>
</el-table-column>
<el-table-column prop="icon" label="图标" align="center">
<template slot-scope="scope">
<i :class="scope.row.icon" v-if="scope.row.icon.includes('el-icon')" ></i>
<svg-icon v-else :icon-class="scope.row.icon"></svg-icon>
</template>
</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="url" label="组件路径"></el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button type="primary" icon="el-icon-edit" size="small" @click="editMenu(scope.row)">编辑</el-button>
<el-button type="danger" size="small" icon="el-icon-delete" @click="deleteMenu(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-main>
</template>
<style lang="scss" scoped>
</style>
menu.js
import http from '@/utils/request'
export default {
/**
* 查询权限菜单列表
* @param params
*/
async getMenuList(params){
return await http.get("/api/permission/list",params);
},
/**
* 获取上级菜单
* @returns
*/
async getParentMenuList(params) {
return await http.get("/api/permission/parent/list", params)
},
/**
* 添加菜单
* @returns
*/
async addMenu(params){
return await http.post("/api/permission/add",params)
},
/**
* 修改菜单
* @returns
*/
async updateMenu(params) {
return await http.put("/api/permission/update", params);
},
/**
* 检查菜单下是否存在子菜单
*/
async checkPermission(param){
return await http.getRestApi("/api/permission/check",param);
},
/**
* 删除菜单
* @returns
*/
async deleteById(params) {
return await http.delete("/api/permission/delete", params);
}
}
1) 菜单类型,图标
2)滚动条样式
public/index.html
::-webkit-scrollbar{
width: 5px;
height: 5px;
background-color: #F5F5F5;
}
::-webkit-scrollbar-track {
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
border-radius: 8px;
background-color: #F5F5F5;
}
::-webkit-scrollbar-thumb{
border-radius: 8px;
box-shadow: inset 0 0 6px rgba(0, 0, 0, .1);
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .1);
background-color: #c8c8c8;
}
3、 新增权限菜单
3.1 新增权限菜单窗口代码
<!-- 新增或编辑弹框 -->
<system-dialog
:title="menuDialog.title"
:width="menuDialog.width"
:height="menuDialog.height"
:visible="menuDialog.visible"
@onClose="onClose"
@onConfirm="onConfirm">
<div slot="content">
<el-form :model="menu" ref="menuForm" :rules="rules" label-width="80px" :inline="true" size="small">
<el-col :span="24">
<el-form-item label="菜单类型" prop="type">
<el-radio-group v-model="menu.type">
<el-radio :label="0">目录</el-radio>
<el-radio :label="1">菜单</el-radio>
<el-radio :label="2">按钮</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-form-item label="所属菜单" size="small" prop="parentName">
<el-input v-model="menu.parentName" :readonly="true" @click.native="selectParentMenu()" />
</el-form-item>
<el-form-item label="菜单名称" size="small" prop="label">
<el-input v-model="menu.label"></el-input>
</el-form-item>
<el-form-item label="路由名称" size="small" prop="name" v-if="menu.type == 1">
<el-input v-model="menu.name"></el-input>
</el-form-item>
<el-form-item label="路由地址" size="small" prop="path" v-if="menu.type != 2">
<el-input v-model="menu.path"></el-input>
</el-form-item>
<el-form-item label="组件路径" size="small" prop="url" v-if="menu.type == 1">
<el-input v-model="menu.url"></el-input>
</el-form-item>
<el-form-item label="权限字段" size="small" prop="code">
<el-input v-model="menu.code"></el-input>
</el-form-item>
<el-form-item label="菜单图标" size="small" >
<my-icon @selecticon="setIcon" ref="child"></my-icon>
</el-form-item>
<el-form-item label="菜单序号" size="small">
<el-input v-model="menu.orderNum"></el-input>
</el-form-item>
</el-form>
</div>
</system-dialog>
3.2 选择所属菜单代码
<!-- 选择所属菜单弹框 -->
<system-dialog
:title="parentDialog.title"
:width="parentDialog.width"
:height="parentDialog.height"
:visible="parentDialog.visible"
@onClose="onParentClose"
@onConfirm="onParentConfirm">
<div slot="content">
<el-tree
style="font-size: 14px"
ref="parentTree"
:data="parentMenuList"
node-key="id"
:props="defaultProps"
empty-text="暂无数据"
:show-checkbox="false"
default-expand-all
:highlight-current="true"
:expand-on-click-node="false"
@node-click="handleNodeClick">
<div class="customer-tree-node" slot-scope="{ node, data }">
<!-- 长度为0说明没有下级 -->
<span v-if="data.children.length == 0">
<i class="el-icon-document" style="color: #8c8c8c; font-size: 18px"/>
</span>
<span v-else @click.stop="changeIcon(data)">
<svg-icon v-if="data.open" icon-class="add-s"/>
<svg-icon v-else icon-class="sub-s"/>
</span>
<span style="margin-left: 3px">{{ node.label }}</span>
</div>
</el-tree>
</div>
</system-dialog>
3.3 封装图标选择器
utils/icons.js
export const elementIcons = ["platform-eleme", "eleme", "delete-solid", "delete",
"s-tools", "setting", "user-solid", "user", "phone", "phone-outline", "more",
"more-outline", "star-on", "star-off", "s-goods", "goods", "warning", "warningoutline", "question", "info", "remove", "circle-plus", "success", "error", "zoomin", "zoom-out", "remove-outline", "circle-plus-outline", "circle-check", "circleclose", "s-help", "help", "minus", "plus", "check", "close", "picture", "pictureoutline", "picture-outline-round", "upload", "upload2", "download", "camerasolid", "camera", "video-camera-solid", "video-camera", "message-solid", "bell",
"s-cooperation", "s-order", "s-platform", "s-fold", "s-unfold", "s-operation", "spromotion", "s-home", "s-release", "s-ticket", "s-management", "s-open", "s-shop",
"s-marketing", "s-flag", "s-comment", "s-finance", "s-claim", "s-custom", "sopportunity", "s-data", "s-check", "s-grid", "menu", "share", "d-caret", "caretleft", "caret-right", "caret-bottom", "caret-top", "bottom-left", "bottom-right",
"back", "right", "bottom", "top", "top-left", "top-right", "arrow-left", "arrowright", "arrow-down", "arrow-up", "d-arrow-left", "d-arrow-right", "video-pause",
"video-play", "refresh", "refresh-right", "refresh-left", "finished", "sort",
"sort-up", "sort-down", "rank", "loading", "view", "c-scale-to-original", "date",
"edit", "edit-outline", "folder", "folder-opened", "folder-add", "folder-remove",
"folder-delete", "folder-checked", "tickets", "document-remove", "documentdelete", "document-copy", "document-checked", "document", "document-add",
"printer", "paperclip", "takeaway-box", "search", "monitor", "attract", "mobile",
"scissors", "umbrella", "headset", "brush", "mouse", "coordinate", "magic-stick",
"reading", "data-line", "data-board", "pie-chart", "data-analysis", "collectiontag", "film", "suitcase", "suitcase-1", "receiving", "collection", "files",
"notebook-1", "notebook-2", "toilet-paper", "office-building", "school", "tablelamp", "house", "no-smoking", "smoking", "shopping-cart-full", "shopping-cart-1",
"shopping-cart-2", "shopping-bag-1", "shopping-bag-2", "sold-out", "sell",
"present", "box", "bank-card", "money", "coin", "wallet", "discount", "price-tag",
"news", "guide", "male", "female", "thumb", "cpu", "link", "connection", "open",
"turn-off", "set-up", "chat-round", "chat-line-round", "chat-square", "chat-dotround", "chat-dot-square", "chat-line-square", "message", "postcard", "position",
"turn-off-microphone", "microphone", "close-notification", "bangzhu", "time",
"odometer", "crop", "aim", "switch-button", "full-screen", "copy-document", "mic",
"stopwatch", "medal-1", "medal", "trophy", "trophy-1", "first-aid-kit",
"discover", "place", "location", "location-outline", "location-information", "addlocation", "delete-location", "map-location", "alarm-clock", "timer", "watch-1",
"watch", "lock", "unlock", "key", "service", "mobile-phone", "bicycle", "truck",
"ship", "basketball", "football", "soccer", "baseball", "wind-power", "lightrain", "lightning", "heavy-rain", "sunrise", "sunrise-1", "sunset", "sunny",
"cloudy", "partly-cloudy", "cloudy-and-sunny", "moon", "moon-night", "dish",
"dish-1", "food", "chicken", "fork-spoon", "knife-fork", "burger", "tableware",
"sugar", "dessert", "ice-cream", "hot-water", "water-cup", "coffee-cup", "colddrink", "goblet", "goblet-full", "goblet-square", "goblet-square-full",
"refrigerator", "grape", "watermelon", "cherry", "apple", "pear", "orange",
"coffee", "ice-tea", "ice-drink", "milk-tea", "potato-strips", "lollipop", "icecream-square", "ice-cream-round"].map(s => "el-icon-" + s);
/**
* 切换图标
* @param data
*/
changeIcon(data) {
data.ope = !data.open
this.$refs.parentTree.store.nodesMap[data.id].expanded = !data.open
},
//给icon绑定的点击事件
setIcon(icon) {
this.menu.icon = icon;
},
<template>
<div class="chooseIcons">
<el-popover placement="bottom" width="450" trigger="click">
<span slot="reference" style="display: inline-block;width: 200px;height: 33px;line-height: 33px;">
<i :class="userChooseIcon"></i>
{{ userChooseIcon }}
</span>
<div class="iconList">
<i v-for="item in iconList" :key="item" :class="item" @click="setIcon(item)" style="font-size: 20px"></i>
</div>
</el-popover>
</div>
</template>
<script>
//导入自定义的icon图标库
import { elementIcons } from "@/utils/icons";
export default {
name: 'MyIcon',
data(){
return{
userChooseIcon:"",//用户选中的图标
iconList:[],//图标列表
}
},
created() {
//获取图标列表
this.getIconList();
},
methods:{
/**
* 获取图标列表
*/
getIconList(){
this.iconList = elementIcons;
},
//给icon绑定的点击事件
setIcon(icon) {
//将i的样式设为选中的样式el-icon-xxx
this.userChooseIcon = icon;
//将选中的图标传递给父组件
this.$emit("selecticon",icon)
},
}
}
</script>
<style scoped lang="scss">
.iconList {
width: 400px;
height: 300px;
overflow-y: scroll;
overflow-x: hidden;
display: flex;
justify-content: space-around;
flex-wrap: wrap;
i {
display: inline-block;
width: 60px;
height: 45px;
color: #000000;
font-size: 20px;
border: 1px solid #e6e6e6;
border-radius: 4px;
cursor: pointer;
text-align: center;
line-height: 45px;
margin: 5px;
&:hover {
color: orange;
border-color:orange;
}
}
}
.chooseIcons{
width: 175px;
background-color: #FFFFFF;
background-image: none;
border-radius: 4px;
border: 1px solid #DCDFE6;
box-sizing: border-box;
color: #606266;
display: inline-block;
font-size: inherit;
height: 33px;
line-height: 25px;
outline: none;
padding: 0 15px;
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
}
</style>
3.4 新增、编辑和删除权限菜单
<template>
<el-main>
<!-- 新增按钮 -->
<el-button type="success" size="small" @click="openAddWindow()" icon="el-icon-plus">新增</el-button>
<!-- 数据表格 -->
<el-table style="margin-top: 10px" :height="tableHeight" :data="menuList" row-key="id"
default-expand-all :tree-props="{ children: 'children' }" :border="true" stripe>
<el-table-column prop="label" label="菜单名称"></el-table-column>
<el-table-column prop="type" label="菜单类型" align="center">
<template slot-scope="scope">
<el-tag v-if="scope.row.type == '0'" size="normal">目录</el-tag>
<el-tag type="success" v-else-if="scope.row.type == '1'" size="normal">菜单</el-tag>
<el-tag type="warning" v-else-if="scope.row.type == '2'" size="normal">按钮</el-tag>
</template>
</el-table-column>
<el-table-column prop="icon" label="图标" align="center">
<template slot-scope="scope">
<i :class="scope.row.icon" v-if="scope.row.icon.includes('el-icon')" ></i>
<svg-icon v-else :icon-class="scope.row.icon"></svg-icon>
</template>
</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="url" label="组件路径"></el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button type="primary" icon="el-icon-edit" size="small" @click="editMenu(scope.row)">编辑</el-button>
<el-button type="danger" size="small" icon="el-icon-delete" @click="deleteMenu(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 新增或编辑弹框 -->
<system-dialog
:title="menuDialog.title"
:width="menuDialog.width"
:height="menuDialog.height"
:visible="menuDialog.visible"
@onClose="onClose"
@onConfirm="onConfirm">
<div slot="content">
<el-form :model="menu" ref="menuForm" :rules="rules" label-width="80px" :inline="true" size="small">
<el-col :span="24">
<el-form-item label="菜单类型" prop="type">
<el-radio-group v-model="menu.type">
<el-radio :label="0">目录</el-radio>
<el-radio :label="1">菜单</el-radio>
<el-radio :label="2">按钮</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-form-item label="所属菜单" size="small" prop="parentName">
<el-input v-model="menu.parentName" :readonly="true" @click.native="selectParentMenu()" />
</el-form-item>
<el-form-item label="菜单名称" size="small" prop="label">
<el-input v-model="menu.label"></el-input>
</el-form-item>
<el-form-item label="路由名称" size="small" prop="name" v-if="menu.type == 1">
<el-input v-model="menu.name"></el-input>
</el-form-item>
<el-form-item label="路由地址" size="small" prop="path" v-if="menu.type != 2">
<el-input v-model="menu.path"></el-input>
</el-form-item>
<el-form-item label="组件路径" size="small" prop="url" v-if="menu.type == 1">
<el-input v-model="menu.url"></el-input>
</el-form-item>
<el-form-item label="权限字段" size="small" prop="code">
<el-input v-model="menu.code"></el-input>
</el-form-item>
<el-form-item label="菜单图标" size="small" >
<my-icon @selecticon="setIcon" ref="child"></my-icon>
</el-form-item>
<el-form-item label="菜单序号" size="small">
<el-input v-model="menu.orderNum"></el-input>
</el-form-item>
</el-form>
</div>
</system-dialog>
<!-- 选择所属菜单弹框 -->
<system-dialog
:title="parentDialog.title"
:width="parentDialog.width"
:height="parentDialog.height"
:visible="parentDialog.visible"
@onClose="onParentClose"
@onConfirm="onParentConfirm">
<div slot="content">
<el-tree
style="font-size: 14px"
ref="parentTree"
:data="parentMenuList"
node-key="id"
:props="defaultProps"
empty-text="暂无数据"
:show-checkbox="false"
default-expand-all
:highlight-current="true"
:expand-on-click-node="false"
@node-click="handleNodeClick">
<div class="customer-tree-node" slot-scope="{ node, data }">
<!-- 长度为0说明没有下级 -->
<span v-if="data.children.length == 0">
<i class="el-icon-document" style="color: #8c8c8c; font-size: 18px"/>
</span>
<span v-else @click.stop="changeIcon(data)">
<svg-icon v-if="data.open" icon-class="add-s"/>
<svg-icon v-else icon-class="sub-s"/>
</span>
<span style="margin-left: 3px">{{ node.label }}</span>
</div>
</el-tree>
</div>
</system-dialog>
</el-main>
</template>
<script>
// 导入menu脚本文件
import menuApi from '@/api/menu'
//导入自定义图标组件
import MyIcon from '@/components/system/MyIcon.vue'
// 导入对话框组件
import SystemDialog from '@/components/system/SystemDialog.vue'
export default {
name: 'menuList',
//注册组件
components:{ SystemDialog, MyIcon },
data() {
return {
//新增或编辑弹框属性
menuDialog: {
title: "",
width: 630,
height: 270,
visible: false,
},
//菜单属性
menu: {
id: "",
type: "",
parentId: "",
parentName: "",
label: "",
icon: "",
name: "",
path: "",
url: "",
code: "",
orderNum: "",
},
rules: {
type: [{ required: true, trigger: "change", message: "请选择菜单类型" }],
parentName: [{ required: true, trigger: "change", message: "请选择所属菜单"}],
label: [{ required: true, trigger: "blur", message: "请输入菜单名称" }],
name: [{ required: true, trigger: "blur", message: "请输入路由名称" }],
path: [{ required: true, trigger: "blur", message: "请输入路由路径" }],
url: [{ required: true, trigger: "blur", message: "请输入组件路径" }],
code: [{ required: true, trigger: "blur", message: "请输入权限字段" }],
},
parentDialog: {
title: '选择所属菜单',
width: 280,
height: 450,
visible: false
},
//树属性定义
defaultProps: {
children: 'children',
label: 'label'
},
menuList: [], //菜单列表
tableHeight: 0, //表格高度
parentMenuList: [], //所属菜单列表
}
},
mounted() {
this.$nextTick(() => {
this.tableHeight = window.innerHeight - 180
})
},
//初始化时调用
created(){
//调用查询菜单列表的方法
this.search()
},
mounted() {
this.$nextTick(() => {
this.tableHeight = window.innerHeight - 180
})
},
methods:{
/**
* 选择所属菜单
*/
async selectParentMenu() {
//显示窗口
this.parentDialog.visible = true;
//发送查询请求
let res = await menuApi.getParentMenuList();
//判断是否成功
if (res.success) {
//赋值
this.parentMenuList = res.data;
}
},
/**
* 查询菜单列表
*/
async search() {
//发送查询请求
let res = await menuApi.getMenuList();
//判断是否成功
if(res.success){
//赋值
this.menuList = res.data;
}
},
/**
* 编辑菜单
* @param row
*/
editMenu(row){
//把当前要编辑的数据复制到数据域,给表单回显
this.$objCopy(row, this.menu);
//设置弹框属性
this.menuDialog.title = "编辑菜单";
this.menuDialog.visible = true;
this.$nextTick(() => {
this.$refs["child"].userChooseIcon = row.icon;//菜单图标回显
})
},
/**
* 删除菜单
* @param row
*/
async deleteMenu(row){
//判断是否存在子菜单
let result = await menuApi.checkPermission({ id: row.id });
//判断是否可以删除
if (!result.success) {
//提示不能删除
this.$message.warning(result.message);
} else {
//确认是否删除
let confirm =await this.$myconfirm("确定要删除该数据吗?");
if (confirm) {
//发送删除请求
let res = await menuApi.deleteById({ id: row.id });
//判断是否成功
if (res.success) {
//成功提示
this.$message.success(res.message);
//刷新
this.search();
} else {
//失败提示
this.$message.error(res.message);
}
}
}
},
/**
* 打开新增窗口
*/
openAddWindow(){
this.$resetForm("menuForm", this.menu) //清空表单数据
this.menuDialog.title = "新增菜单" //设置窗口标题
this.menuDialog.visible = true //显示窗口
this.$nextTick(() => {
this.$refs['child'].userChooseIcon = '' // 清空菜单图标
})
},
/**
* 选择所属菜单取消事件
*/
onParentClose() {
this.parentDialog.visible = false //关闭窗口
},
/**
* 选择所属菜单确认事件
*/
onParentConfirm() {
this.parentDialog.visible = false //关闭窗口
},
/**
* 添加和修改菜单窗口关闭事件
*/
onClose() {
this.menuDialog.visible = false; //关闭窗口
},
/**
* 添加和修改菜单窗口确认事件
*/
onConfirm() {
//表单验证
this.$refs.menuForm.validate(async (valid) => {
if (valid) {
let res = null;
//判断菜单ID是否为空
if (this.menu.id === "") {
//发送添加请求
res = await menuApi.addMenu(this.menu);
} else {
//发送修改请求
res = await menuApi.updateMenu(this.menu);
}
//判断是否成功
if (res.success) {
this.$message.success(res.message);
//刷新
//this.search();
window.location.reload();
//关闭窗口
this.menuDialog.visible = false;
} else {
this.$message.error(res.message);
}
this.menuDialog.visible = false; //关闭窗口
}
});
},
/**
* 所属菜单节点点击事件
*/
handleNodeClick(data) {
//所属父级菜单ID
this.menu.parentId = data.id;
//所属父级菜单名称
this.menu.parentName = data.label;
},
/**
* 切换图标
* @param data
*/
changeIcon(data) {
data.open = !data.open
this.$refs.parentTree.store.nodesMap[data.id].expanded = !data.open
},
//给icon绑定的点击事件
setIcon(icon) {
this.menu.icon = icon;
},
}
}
</script>
<style lang="scss" scoped>
</style>