Vue + Element UI 实现权限管理系统 前端篇(十四):菜单功能实现菜

Vue + Element UI 实现权限管理系统 前端篇(十四):菜单功能实现 

菜单功能实现

菜单接口封装

菜单管理是一个对菜单树结构的增删改查操作。

提供一个菜单查询接口,查询整颗菜单树形结构。

http/modules/menu.js 添加 findMenuTree 接口。

复制代码

import axios from '../axios'/* * 菜单管理模块*/// 保存
export const save = (data) => {return axios({url: '/menu/save',method: 'post',data})
}
// 删除
export const batchDelete = (data) => {return axios({url: '/menu/delete',method: 'post',data})
}
// 查找导航菜单树
export const findNavTree = (params) => {return axios({url: '/menu/findNavTree',method: 'get',params})
}
// 查找导航菜单树
export const findMenuTree = () => {return axios({url: '/menu/findMenuTree',method: 'get'})
}

复制代码

菜单管理界面

菜单管理界面是使用封装的表格树组件显示菜单结构,并提供增删改查的功能。

Menu.vue

复制代码

<template><div class="container" style="width:99%;margin-top:-25px;"><!--工具栏--><div class="toolbar" style="float:left;padding-top:10px;padding-left:15px;"><el-form :inline="true" :model="filters" :size="size"><el-form-item><el-input v-model="filters.name" placeholder="名称"></el-input></el-form-item><el-form-item><kt-button label="查询" perms="sys:menu:view" type="primary" @click="findTreeData(null)"/></el-form-item><el-form-item><kt-button label="新增" perms="sys:menu:add" type="primary" @click="handleAdd"/></el-form-item></el-form></div><!--表格树内容栏--><el-table :data="tableTreeDdata" stripe size="mini" style="width: 100%;"v-loading="loading" element-loading-text="拼命加载中"><el-table-columnprop="id" header-align="center" align="center" width="80" label="ID"></el-table-column><table-tree-column prop="name" header-align="center" treeKey="id" width="150" label="名称"></table-tree-column><el-table-column header-align="center" align="center" label="图标"><template slot-scope="scope"><i :class="scope.row.icon || ''"></i></template></el-table-column><el-table-column prop="type" header-align="center" align="center" label="类型"><template slot-scope="scope"><el-tag v-if="scope.row.type === 0" size="small">目录</el-tag><el-tag v-else-if="scope.row.type === 1" size="small" type="success">菜单</el-tag><el-tag v-else-if="scope.row.type === 2" size="small" type="info">按钮</el-tag></template></el-table-column><el-table-column prop="parentName" header-align="center" align="center" width="120" label="上级菜单"></el-table-column><el-table-columnprop="url" header-align="center" align="center" width="150" :show-overflow-tooltip="true" label="菜单URL"></el-table-column><el-table-columnprop="perms" header-align="center" align="center" width="150" :show-overflow-tooltip="true" label="授权标识"></el-table-column><el-table-columnprop="orderNum" header-align="center" align="center" label="排序"></el-table-column><el-table-columnfixed="right" header-align="center" align="center" width="150" label="操作"><template slot-scope="scope"><kt-button label="修改" perms="sys:menu:edit" @click="handleEdit(scope.row)"/><kt-button label="删除" perms="sys:menu:delete" type="danger" @click="handleDelete(scope.row)"/></template></el-table-column></el-table><!-- 新增修改界面 --><el-dialog :title="!dataForm.id ? '新增' : '修改'" width="40%" :visible.sync="dialogVisible" :close-on-click-modal="false"><el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="submitForm()" label-width="80px" :size="size" style="text-align:left;"><el-form-item label="菜单类型" prop="type"><el-radio-group v-model="dataForm.type"><el-radio v-for="(type, index) in menuTypeList" :label="index" :key="index">{{ type }}</el-radio></el-radio-group></el-form-item><el-form-item :label="menuTypeList[dataForm.type] + '名称'" prop="name"><el-input v-model="dataForm.name" :placeholder="menuTypeList[dataForm.type] + '名称'"></el-input></el-form-item><el-form-item label="上级菜单" prop="parentName"><popup-tree-input :data="popupTreeData" :props="popupTreeProps" :prop="dataForm.parentName==null?'根节点':dataForm.parentName" :nodeKey="''+dataForm.parentId" :currentChangeHandle="handleTreeSelectChange"></popup-tree-input></el-form-item><el-form-item v-if="dataForm.type === 1" label="菜单路由" prop="url"><el-input v-model="dataForm.url" placeholder="菜单路由"></el-input></el-form-item><el-form-item v-if="dataForm.type !== 0" label="授权标识" prop="perms"><el-input v-model="dataForm.perms" placeholder="如: sys:user:add, sys:user:edit, sys:user:delete"></el-input></el-form-item><el-form-item v-if="dataForm.type !== 2" label="排序编号" prop="orderNum"><el-input-number v-model="dataForm.orderNum" controls-position="right" :min="0" label="排序编号"></el-input-number></el-form-item><el-form-item v-if="dataForm.type !== 2" label="菜单图标" prop="icon"><el-row><el-col :span="22"><!-- <el-popoverref="iconListPopover"placement="bottom-start"trigger="click"popper-class="mod-menu__icon-popover"><div class="mod-menu__icon-list"><el-buttonv-for="(item, index) in dataForm.iconList":key="index"@click="iconActiveHandle(item)":class="{ 'is-active': item === dataForm.icon }"><icon-svg :name="item"></icon-svg></el-button></div></el-popover> --><el-input v-model="dataForm.icon" v-popover:iconListPopover :readonly="true" placeholder="菜单图标名称(如:fa fa-home fa-lg)" class="icon-list__input"></el-input></el-col><el-col :span="2" class="icon-list__tips"><fa-icon-tooltip /></el-col></el-row></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button :size="size"  @click="dialogVisible = false">取消</el-button><el-button :size="size"  type="primary" @click="submitForm()">确定</el-button></span></el-dialog></div>
</template><script>
import KtButton from "@/views/Core/KtButton"
import TableTreeColumn from '@/views/Core/TableTreeColumn'
import PopupTreeInput from "@/components/PopupTreeInput"
import FaIconTooltip from "@/components/FaIconTooltip"
export default {components:{PopupTreeInput,KtButton,TableTreeColumn,FaIconTooltip},data() {return {size: 'small',loading: false,filters: {name: ''},tableTreeDdata: [],dialogVisible: false,menuTypeList: ['目录', '菜单', '按钮'],dataForm: {id: 0,type: 1,name: '',parentId: 0,parentName: '',url: '',perms: '',orderNum: 0,icon: '',iconList: []},dataRule: {name: [{ required: true, message: '菜单名称不能为空', trigger: 'blur' }],parentName: [{ required: true, message: '上级菜单不能为空', trigger: 'change' }]},popupTreeData: [],popupTreeProps: {label: 'name',children: 'children'}}},methods: {// 获取数据findTreeData: function () {this.loading = truethis.$api.menu.findMenuTree().then((res) => {this.tableTreeDdata = res.datathis.popupTreeData = this.getParentMenuTree(res.data)this.loading = false})},// 获取上级菜单树getParentMenuTree: function (tableTreeDdata) {let parent = {parentId: -1,name: '根节点',children: tableTreeDdata}return [parent]},// 显示新增界面handleAdd: function () {this.dialogVisible = truethis.dataForm = {id: 0,type: 1,typeList: ['目录', '菜单', '按钮'],name: '',parentId: 0,parentName: '',url: '',perms: '',orderNum: 0,icon: '',iconList: []}},// 显示编辑界面handleEdit: function (row) {this.dialogVisible = trueObject.assign(this.dataForm, row);},// 删除handleDelete (row) {this.$confirm('确认删除选中记录吗?', '提示', {type: 'warning'}).then(() => {let params = this.getDeleteIds([], row)this.$api.menu.batchDelete(params).then( res => {this.findTreeData()this.$message({message: '删除成功', type: 'success'})})})},// 获取删除的包含子菜单的id列表getDeleteIds (ids, row) {ids.push({id:row.id})if(row.children != null) {for(let i=0, len=row.children.length; i<len; i++) {this.getDeleteIds(ids, row.children[i])}}return ids},// 菜单树选中handleTreeSelectChange (data, node) {this.dataForm.parentId = data.idthis.dataForm.parentName = data.name},// 图标选中iconActiveHandle (iconName) {this.dataForm.icon = iconName},// 表单提交submitForm () {this.$refs['dataForm'].validate((valid) => {if (valid) {this.$confirm('确认提交吗?', '提示', {}).then(() => {this.editLoading = truelet params = Object.assign({}, this.dataForm)this.$api.menu.save(params).then((res) => {if(res.code == 200) {this.$message({ message: '操作成功', type: 'success' })} else {this.$message({message: '操作失败, ' + res.msg, type: 'error'})}this.editLoading = falsethis.$refs['dataForm'].resetFields()this.dialogVisible = falsethis.findTreeData()})})}})}},mounted() {this.findTreeData()}
}
</script><style scoped></style>

复制代码

其中对表格树组件进行了简单的封装。

views/Core/TableTreeColumn.vue

复制代码

<template><el-table-column :prop="prop" v-bind="$attrs"><template slot-scope="scope"><span @click.prevent="toggleHandle(scope.$index, scope.row)" :style="childStyles(scope.row)"><i :class="iconClasses(scope.row)" :style="iconStyles(scope.row)"></i>{{ scope.row[prop] }}</span></template></el-table-column>
</template><script>import isArray from 'lodash/isArray'export default {name: 'table-tree-column',props: {prop: {type: String},treeKey: {type: String,default: 'id'},parentKey: {type: String,default: 'parentId'},levelKey: {type: String,default: 'level'},childKey: {type: String,default: 'children'}},methods: {childStyles (row) {return { 'padding-left': (row[this.levelKey] * 25) + 'px' }},iconClasses (row) {return [ !row._expanded ? 'el-icon-caret-right' : 'el-icon-caret-bottom' ]},iconStyles (row) {return { 'visibility': this.hasChild(row) ? 'visible' : 'hidden' }},hasChild (row) {return (isArray(row[this.childKey]) && row[this.childKey].length >= 1) || false},// 切换处理toggleHandle (index, row) {if (this.hasChild(row)) {var data = this.$parent.store.states.data.slice(0)data[index]._expanded = !data[index]._expandedif (data[index]._expanded) {data = data.splice(0, index + 1).concat(row[this.childKey]).concat(data)} else {data = this.removeChildNode(data, row[this.treeKey])}this.$parent.store.commit('setData', data)this.$nextTick(() => {this.$parent.doLayout()})}},// 移除子节点removeChildNode (data, parentId) {var parentIds = isArray(parentId) ? parentId : [parentId]if (parentId.length <= 0) {return data}var ids = []for (var i = 0; i < data.length; i++) {if (parentIds.indexOf(data[i][this.parentKey]) !== -1 && parentIds.indexOf(data[i][this.treeKey]) === -1) {ids.push(data.splice(i, 1)[0][this.treeKey])i--}}return this.removeChildNode(data, ids)}}}
</script>

复制代码

测试效果

最终测试效果下图所示。

源码下载

后端:kitty: 基于Spring Boot、Spring Cloud、Vue.js 、Element UI实现,采用前后端分离架构的权限管理系统,JAVA快速开发平台。

前端:kitty-ui: Kitty 前端,基于 Vue + Element 实现的权限管理系统。

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

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

相关文章

uniapp 解决跨域的问题

uniapp 解决跨域的问题 我真的是个 沙雕 找对了解决办法 写错了地方 "h5" : {"devServer" : {"disableHostCheck" : true,"https": false,"proxy" : {"/app" : {"target" : "https://192.16…

MRI多任务技术及应用

目录 一、定量心血管磁共振成像&#xff08;CMR&#xff09;的改进方法二、磁共振多任务三、磁共振多任务的成像框架四、磁共振多任务的图像模型和采样和重建策略五、利用MR多任务进行快速三维稳态CEST(ss-CEST)成像5.1 利用MR多任务进行快速三维稳态CEST(ss-CEST)成像介绍5.2 …

Java调用Web Service接口

方法1. 用IDEA生成相关代码调用方法。 在IDEA插件商店下载插件 然后新建一个Java项目 创建一个包来存放生成的代码&#xff0c;(点击一下)选中这个包&#xff0c;点击Tools 填入接口url&#xff0c;记住后面拼接“?wsdl”&#xff0c;选择生成方法&#xff0c;然后OK即可生…

超详解| Yolov8模型手把手调参 | 配置 | 模型训练 | 验证 | 推理

YOLOv8是一款前沿、最先进&#xff08;SOTA&#xff09;的模型&#xff0c;基于先前YOLO版本的成功&#xff0c;引入了新功能和改进&#xff0c;进一步提升性能和灵活性。 然而&#xff0c;要充分发挥Yolov8的潜力&#xff0c;合理的参数配置是至关重要的。本文将带您深入了解…

stable diffusion实践操作-VAE

系列文章总目录 stable diffusion实践操作 文章目录 系列文章总目录一、 前言1 定义功能全局介绍2 模型全局介绍2.1 后缀以及存放位置2.2 查看大模型是否有VAE 二、正文1 原理1.1 基础原理 2 使用2.1 增加饱和度2.2 增加细节 3 下载3.1 自动下载3.2 手动下载 三 、总结 一、 前…

掌握逻辑漏洞复现技术,保护您的数字环境

环境准备 这篇文章旨在用于网络安全学习&#xff0c;请勿进行任何非法行为&#xff0c;否则后果自负。 1、支付逻辑漏洞 攻击相关介绍 介绍&#xff1a; 支付逻辑漏洞是指攻击者利用支付系统的漏洞&#xff0c;突破系统的限制&#xff0c;完成非法的支付操作。攻击者可以采…

从0到1构建界面设计系统思维

用户界面&#xff08;UI&#xff09;是人与机器之间发生交互的载体&#xff0c;也是用户体验&#xff08;UX&#xff09;的一个组成部分。用户界面由两个主要部分组成&#xff1a;视觉设计&#xff08;即传达产品的外观和感觉&#xff09;和交互设计&#xff08;即元素的功能和…

实现SSE的textevent-stream是什么?和applicationoctet-stream有什么区别?

WEB通讯技术。前端实现SSE长连接&#xff0c;nodejsexpress搭建简单服务器&#xff0c;进行接口调试&#xff0c;通过curl请求数据 点击上面的地址是可以了解轮询和长轮询以及websocket等通信模式&#xff0c;一些基础概念和速成技能&#xff0c;这篇来接着详细聊聊text/event…

Qt---对话框 事件处理 如何发布自己写的软件

目录 一、对话框 1.1 消息对话框&#xff08;QMessageBox&#xff09; 1> 消息对话框提供了一个模态的对话框&#xff0c;用来提示用户信息&#xff0c;或者询问用户问题并得到回答 2> 基于属性版本的API 3> 基于静态成员函数版本 4> 对话框案例 1、ui界面 …

2023年8月30日-[SWPUCTF 2021 新生赛]jicao

<?php highlight_file(index.php); include("flag.php"); $id$_POST[id]; $jsonjson_decode($_GET[json],true); if ($id"wllmNB"&&$json[x]"wllm") {echo $flag;} ?> 包含了flag.php文件&#xff0c;设定了一个POST请求的id和…

【AIGC专题】Stable Diffusion 从入门到企业级实战0401

一、概述 本章是《Stable Diffusion 从入门到企业级实战》系列的第四部分能力进阶篇《Stable Diffusion ControlNet v1.1 图像精准控制》第01节&#xff0c; 利用Stable Diffusion ControlNet Inpaint模型精准控制图像生成。本部分内容&#xff0c;位于整个Stable Diffusion生…

Ubuntu之apt-get系列--apt-get安装软件的方法/教程

原文网址&#xff1a;Ubuntu之apt-get系列--apt-get安装软件的方法/教程_IT利刃出鞘的博客-CSDN博客 简介 本文介绍Ubuntu使用apt-get安装软件的方法。 安装软件 先更新列表 sudo apt-get update 安装软件 sudo apt-get install <package name>[<version>]…

Android后退堆栈

修改代码 现在的ItemClick使得用户单击其中一个项目时就会跳转&#xff0c;现在要修改其使得在一个小屏幕设备上才会这样做&#xff0c;在一个大屏幕设备上运行用户选择一个训练项目时在右边的片段显示响应的信息。 希望片段处理后退的方式&#xff1a;假设用户在手机上运行这…

【ES6】js中的__proto__和prototype

在JavaScript中&#xff0c;__proto__和prototype都是用于实现对象继承的关键概念。 1、proto __proto__是一个非标准的属性&#xff0c;用于设置或获取一个对象的原型。这个属性提供了直接访问对象内部原型对象的途径。对于浏览器中的宿主对象和大多数对象来说&#xff0c;可…

加强版python连接飞书通知——本地电脑PC端通过网页链接打开本地已安装软件(调用注册表形式,以漏洞扫描工具AppScan为例)

前言 如果你想要通过超链接来打开本地应用,那么你首先你需要将你的应用添入windows注册表中(这样网页就可以通过指定代号来调用程序),由于安全性的原因所以网页无法直接通过输入绝对路径来调用本地文件。 一、通过创建reg文件自动配置注册表 创建文本文档,使用记事本打开…

STM32使用FAT文件系统-常见概念、代码走读

fat文件系统的所有外部接口都在ff.h中 格式化 挂载 使用文件系统的第一步&#xff0c;就是挂载。 函数原型&#xff1a;FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */ static FATFS fs; FRESULT fres f_mo…

【爬虫】7.4. 字体反爬案例分析与爬取实战

字体反爬案例分析与爬取实战 文章目录 字体反爬案例分析与爬取实战1. 案例介绍2. 案例分析3. 爬取 本节来分析一个反爬案例&#xff0c;该案例将真实的数据隐藏到字体文件里&#xff0c;即使我们获取了页面源代码&#xff0c;也无法直接提取数据的真实值。 1. 案例介绍 案例网…

存储过程报Illegal mix of collations错误的解决方法

CREATE PROCEDURE maxAgeStudent(IN _gender CHAR) BEGINDECLARE maxage INT DEFAULT 0;SELECT max(age) INTO maxage FROM student where gender _gender;SELECT * from student WHERE age maxage and gender _gender; END; 在调用的时候 call maxAgeStudent(1) 产生了报…

SpringCloud入门实战(十五)分布式事务框架Seata简介

&#x1f4dd; 学技术、更要掌握学习的方法&#xff0c;一起学习&#xff0c;让进步发生 &#x1f469;&#x1f3fb; 作者&#xff1a;一只IT攻城狮 &#xff0c;关注我&#xff0c;不迷路 。 &#x1f490;学习建议&#xff1a;1、养成习惯&#xff0c;学习java的任何一个技术…

qemu/kvm学习笔记

qemu/kvm架构 cpu虚拟化的示例 Reference: kvmtest.c [LWN.net] 主要步骤&#xff1a; QEMU通过/dev/kvm设备文件发起KVM_CREATE_VM ioctl&#xff0c;请求KVM创建一个虚拟机。KVM创建虚拟机相应的结构体&#xff0c;并为QEMU返回一个虚拟机文件描述符QEMU通过虚拟机文件描述…