vue实现用户动态权限登录
一、使用vue+elementUI搭登录框架,主要就是1、2、3、4
配置:
①vue.config.js
'use strict'
const path = require('path')function resolve(dir) {return path.join(__dirname, dir)
}// All configuration item explanations can be find in https://cli.vuejs.org/config/
module.exports = {publicPath: '/',outputDir: 'dist',assetsDir: 'static',lintOnSave: false, // 是否校验语法productionSourceMap: false,devServer: {port: 8888,open: true,},configureWebpack: {resolve: {alias: {'@': resolve('src')}}}
}
②main.js
import Vue from "vue"
import App from "./App.vue"
import router from "./router"
import store from "./store"
import ElementUI from "element-ui"
import 'element-ui/lib/theme-chalk/index.css'
import "./router/router-config" // 路由守卫,做动态路由的地方Vue.config.productionTip = false
Vue.use(ElementUI)new Vue({router,store,render: (h) => h(App),
}).$mount("#app")
二、代码部分
- layout目录:
- layout/index.vue
<template><el-container><el-header><header-temp></header-temp></el-header><el-container><el-aside width="200px"><sidebar class="sidebar-container"></sidebar></el-aside><el-main><app-main></app-main></el-main></el-container></el-container>
</template>
<script>
import AppMain from './appMain' // 页面布局的右侧区域
import sidebar from './sideBar' // 页面布局的左侧菜单
import headerTemp from "./headerTemp" // 页面布局的header菜单
export default {name: 'layout',components: { sidebar, AppMain, headerTemp }
}
</script>
<style>
.el-header{padding: 0!important;margin-left: 180px;}
</style>
①appMain/index.vue
<template><section class="app-main"><transition name="fade" mode="out-in"><router-view></router-view></transition></section>
</template><script>
export default { name: 'AppMain' }
</script>
②headerTemp/index.vue
<template><div class="header-temp-container"><div class="userInfo"><el-image :src="userInfo.avatar" class="eImage"></el-image><el-dropdown @command="handleLogout"><div class="detail user-link"><span>{{ userInfo.name }}</span><span>{{ userInfo.desc }}</span><i class="el-icon--right"></i></div><template #dropdown><el-dropdown-menu><el-dropdown-item command="logout">退出</el-dropdown-item></el-dropdown-menu></template></el-dropdown></div></div>
</template>
<script>
import { Message } from "element-ui"
export default {name: "header-temp-container",data() {return {userInfo: JSON.parse(window.localStorage.getItem("userInfo"))}},methods: {// 退出登录handleLogout(key) {if(key == "logout") {window.localStorage.removeItem("userInfo")Message({ type: 'success', message: "退出登录", showClose: true, duration: 3000 })this.$router.replace({ path: "/login" })location.reload()}}}
}
</script>
<style scoped>
.header-temp-container{border-bottom: 1px solid #ddd; width: 100%;height: 60px;}
.userInfo{display: flex;flex-direction: row;align-items: center;justify-content: flex-end;height: 100%;margin-right: 20px;}
.eImage{width: 40px;height: 40px;border-radius: 50%;margin-right: 10px;}
.detail{display: flex;flex-direction: column;align-items: flex-start;justify-content: space-around;}
</style>
③sideBar/index.vue
<template><el-menumode="vertical"unique-opened:default-active="$route.path"background-color="#304156"text-color="#fff"active-text-color="#409EFF"><sidebar-item :routes="routes"></sidebar-item></el-menu>
</template><script>
import sidebarItem from "./sidebarItem";export default {components: { sidebarItem },computed: {routes() {return this.$router.options.routes;},},
};
</script>
<style scoped>
.sidebar-container {transition: width 0.28s;width: 180px !important;height: 100%;position: fixed;top: 0;bottom: 0;left: 0;z-index: 1001;
}
.sidebar-container a {display: inline-block;width: 100%;
}
.sidebar-container .svg-icon {margin-right: 16px;
}
.sidebar-container .el-menu {border: none;width: 100%;
}
</style>
<style>
a{text-decoration: none;}
</style>
④sideBar/sidebarItem.vue
<template><div class="menu-wrapper"><template v-for="item in routes" v-if="!item.hidden && item.children"><router-linkv-if="item.children.length === 1 &&!item.children[0].children &&!item.alwaysShow":to="item.children[0].path":key="item.children[0].name"><el-menu-item:index="item.children[0].path":class="{ 'submenu-title-noDropdown': !isNest }"><span v-if="item.children[0].meta && item.children[0].meta.title">{{item.children[0].meta.title}}</span></el-menu-item></router-link><el-submenu v-else :index="item.name || item.path" :key="item.name"><template slot="title"><span v-if="item.meta && item.meta.title">{{ item.meta.title }}</span></template><template v-for="child in item.children" v-if="!child.hidden"><sidebar-item:is-nest="true"class="nest-menu"v-if="child.children && child.children.length > 0":routes="[child]":key="child.path"></sidebar-item><router-link v-else :to="child.path" :key="child.name"><el-menu-item :index="child.path"><span v-if="child.meta && child.meta.title">{{child.meta.title}}</span></el-menu-item></router-link></template></el-submenu></template></div>
</template>
<script>
export default {name: "sidebarItem",props: {routes: { type: Array },isNest: {type: Boolean,default: false,},},
};
</script>
<style scoped>
.nest-menu .el-submenu > .el-submenu__title,
.el-submenu .el-menu-item {min-width: 180px !important;background-color: #1f2d3d !important;
}
.nest-menu .el-submenu > .el-submenu__title,
.el-submenu .el-menu-item :hover {background-color: #001528 !important;
}
.el-menu--collapse .el-menu .el-submenu {min-width: 180px !important;
}
</style>
- 路由配置
①router/index.js
import Vue from "vue"
import VueRouter from "vue-router"
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location, onResolve, onReject) {
if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject)return originalPush.call(this, location).catch(err => err)
}
Vue.use(VueRouter)
const routes = [{ name: "login", path: "/login", meta: { title: "login" },component: () => import("../views/login/index"), hidden: true }
]
const router = new VueRouter({ routes })
export default router
②router/router-config.js
import router from "./index"
import Layout from "../layout/index"
import NProgress from 'nprogress' // progress barNProgress.configure({ showSpinner: false }) // NProgress Configurationconst filterRoutes = ["/login"] // 需要过滤掉的路由
router.beforeEach((to, from, next) => {// start progress barNProgress.start()// 获取路由 meta 中的title,并设置给页面标题document.title = "动态路由(" + to.meta.title + ")"// 判断路由指向是否在需要过滤的路由地址数组里// 如果在,则直接跳进页面,无需判断if(filterRoutes.indexOf(to.path) !== -1) {next()return false}if(router.options.routes.length == 1) {// 获取token和原始路由数组const userInfo = JSON.parse(window.localStorage.getItem('userInfo')) ?? ""// 当token和原始路由都存在的时候if(userInfo.token && userInfo.routes) {onFilterRoutes(to, next, userInfo.routes) // 执行路由过滤和跳转}else {next({ path: "/login", replace: true })}} else next()
})router.afterEach(() => {// finish progress barNProgress.done()
})// 路由拼接
function loadView(view) {return () => import(`@/views/${ view }`)
}// 路由过滤和跳转
async function onFilterRoutes(to, next, e) {const routes = await filterASyncRoutes(e) // 路由过滤routes.sort((a, b) => a['id'] - b['id'])routes.forEach(item => {router.options.routes.push(item)router.addRoute(item)})next({ ...to, replace: true })
}// 路由过滤 遍历路由 转换为组件对象和路径
function filterASyncRoutes(data) {const routes = data.filter(item => {if(item["component"] === "Layout") item.component = Layoutelse item["component"] = loadView(item["component"])// 路由递归,转换组件对象和路径if(item["children"] && item["children"].length > 0) item["children"] = filterASyncRoutes(item.children)return true})return routes
}
- 登录(views/login/index.vue)
<template><div class="login-wrapper"><div class="modal"><el-form :model="user" status-icon :rules="rules" ref="userForm"><div class="title">动态路由</div><el-form-item prop="username"><el-input type="text" prefix-icon="el-icon-user" placeholder="请输入用户名" v-model="user.username" /></el-form-item><el-form-item prop="password"><el-input type="password" prefix-icon="el-icon-view" placeholder="请输入密码" v-model="user.password" /></el-form-item><el-form-item><el-button type="primary" class="btn-login" @click="login">登录</el-button></el-form-item><div class="toast"><span>管理员账号:admin </span><span>密码:654321</span></div><div class="toast"><span>普通人员账号:people</span><span>密码:123456</span></div></el-form></div></div>
</template>
<script>
import dynamicUser from "../../mock"
import { Message } from "element-ui"export default {name: 'login',data() {return {user: {username: "",password: ""},rules: {username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],password: [{ required: true, message: '请输入密码', trigger: 'blur' }]}}},methods: {login() {this.$refs.userForm.validate(( valid ) => {if(valid) {let flag = !1window.localStorage.removeItem("userInfo")dynamicUser.forEach(item => {if(item["username"] == this.user['username'] && item["password"] == this.user['password']) {flag = !0Message({ type: 'success', message: "登录成功", showClose: true, duration: 3000 })window.localStorage.setItem("userInfo", JSON.stringify(item))this.$router.replace({ path: "/" })}})if(!flag) Message({ type: 'warning', message: "账号密码错误,请重试!", showClose: true, duration: 3000 })} else return false})}}
}
</script>
<style scoped>
.login-wrapper {display: flex;flex-direction: row;align-items: center;justify-content: center;background-color: #fff;width: 100vw;height: 100vh;
}
.modal {width: 360px;height: 380px;box-shadow: 0 0 10px 5px #ddd;padding: 50px;border-radius: 5px;
}
.title {width: 100%;text-align: center;line-height: 1.5;font-size: 50px;margin-bottom: 30px;
}
.btn-login {width: 100%;
}
.toast{width: 100%;display: flex;flex-direction: row;align-items: center;justify-content: space-between;height: 50px;
}
</style>
- 动态返回路由
使用mock.js造了一条路由,后端返回格式类似于下列这种样式:
const dynamicUser = [{name: "管理员",avatar: "https://sf3-ttcdn-tos.pstatp.com/img/user-avatar/ccb565eca95535ab2caac9f6129b8b7a~300x300.image",desc: "管理员 - admin",username: "admin",password: "654321",token: "rtVrM4PhiFK8PNopqWuSjsc1n02oKc3f",routes: [{ id: 1, name: "/", path: "/", component: "Layout", redirect: "/index", hidden: false, children: [{ name: "index", path: "/index", meta: { title: "index" }, component: "index/index" },]},{ id: 2, name: "/form", path: "/form", component: "Layout", redirect: "/form/index", hidden: false, children: [{ name: "/form/index", path: "/form/index", meta: { title: "form" }, component: "form/index" }]},{ id: 3, name: "/example", path: "/example", component: "Layout", redirect: "/example/tree", meta: { title: "example" }, hidden: false, children: [{ name: "/tree", path: "/example/tree", meta: { title: "tree" }, component: "tree/index" },{ name: "/copy", path: "/example/copy", meta: { title: "copy" }, component: "tree/copy" }] },{ id: 4, name: "/table", path: "/table", component: "Layout", redirect: "/table/index", hidden: false, children: [{ name: "/table/index", path: "/table/index", meta: { title: "table" }, component: "table/index" }] },{ id: 5, name: "/admin", path: "/admin", component: "Layout", redirect: "/admin/index", hidden: false, children: [{ name: "/admin/index", path: "/admin/index", meta: { title: "admin" }, component: "admin/index" }] },{ id: 6, name: "/people", path: "/people", component: "Layout", redirect: "/people/index", hidden: false, children: [{ name: "/people/index", path: "/people/index", meta: { title: "people" }, component: "people/index" }] }]},{name: "普通用户",avatar: "https://img0.baidu.com/it/u=2097980764,1024880469&fm=253&fmt=auto&app=138&f=JPEG?w=300&h=300",desc: "普通用户 - people",username: "people",password: "123456",token: "4es8eyDwznXrCX3b3439EmTFnIkrBYWh",routes: [{ id: 1, name: "/", path: "/", component: "Layout", redirect: "/index", hidden: false, children: [{ name: "index", path: "/index", meta: { title: "index" }, component: "index/index" },]},{ id: 2, name: "/form", path: "/form", component: "Layout", redirect: "/form/index", hidden: false, children: [{ name: "/form/index", path: "/form/index", meta: { title: "form" }, component: "form/index" }]},{ id: 6, name: "/people", path: "/people", component: "Layout", redirect: "/people/index", hidden: false, children: [{ name: "/people/index", path: "/people/index", meta: { title: "people" }, component: "people/index" }] }]}
]export default dynamicUser
相关文章:

vue实现用户动态权限登录
一、使用vueelementUI搭登录框架,主要就是1、2、3、4 配置: ①vue.config.js use strict const path require(path)function resolve(dir) {return path.join(__dirname, dir) }// All configuration item explanations can be find in https://cli.v…...
ONNX模型修改为自定义节点
参考一 首先,需要将ONNX模型中的节点修改为自定义节点。要实现这一点,您需要了解自定义节点的定义和如何在ONNX中使用它们。ONNX定义了一个自定义运算符的接口,您可以使用该接口定义自己的运算符,并将其编译为ONNX模型可以识别的…...

内存对齐原则
struct (1)结构体第一个数据成员放在offset为0的地方,后面每个成员相对于结构体首地址的偏移量(offset)都是成员大小(该变量类型所占字节)的整数倍,如有需要编译器会在成员之间加上填…...

Java SPI 一 之SPI(Service Provider Interface)进阶 AutoService
一、SPI(Service Provider Interface) 1.1 介绍 SPI(Service Provider Interface),是JDK内置的一种 服务提供发现机制(为某个接口寻找服务实现的机制),可以用来启用框架扩展和替换组件,其…...

C++ list类成员函数介绍
目录 🤔list模板介绍: 🤔特点: 🤔list内存结构图解: 🤔 list的成员函数: 😊list构造函数: 🔍代码示例: 🔍运行结果&…...

【服务器】本地搭建PHP简单Imagewheel私人云图床
文章目录 1.前言2. Imagewheel网站搭建2.1. Imagewheel下载和安装2.2. Imagewheel网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar临时数据隧道3.2.Cpolar稳定隧道(云端设置)3.3.Cpolar稳定隧道(本地设置) 4.公网访问测…...
第四十二回:DateRangePickerDialog Widget
文章目录 概念介绍使用方法示例代码 我们在上一章回中介绍了DatePickerDialog Widget相关的内容,本章回中将介绍 DateRangePickerDialog Widget.闲话休提,让我们一起Talk Flutter吧。 概念介绍 我们在这里说的DateRangePickerDialog是一种弹出窗口,只不…...

【C++系列P3】‘类与对象‘-三部曲——[基础知识](1/3)
前言 大家好吖,欢迎来到 YY 滴 C系列 ,热烈欢迎! 【 类与对象-三部曲】的大纲主要内容如下: 如标题所示,本章是【 类与对象-三部曲】三章中的第一章节——基础知识章节,主要内容如下: 目录 一.…...
Android UEvent事件分析之Kernel上报电量
kernel-4.4\drivers\power\power_supply_core.c 当电量信息需要更新的时候,kernel会调用power_supply_changed_work这个工作队列,使用kobject_uevent函数往上发送uevent事件,action是KOBJ_CHANGE; static void power_supply_changed_work(struct work_struct *work) {uns…...
C++ vector模板和deque的简单应用
目录 🤔vector模板和deque的简单介绍: 🤔vector和deque的主要不同之处: 🤔今天我们用vector模板和deque模板实现以下简单的功能: 代码实现: 🤔讲解: 🤔vector模板和d…...

声明式事务控制
声明式事务控制 编程式事务控制相关对象 PlatformTransactionManager PlatformTransactionManager接口是spring的事务管理器,它里面提供了常用的操作事务的方法 方法说明TransactionStatus getTransaction(TransactionDefaultion defination)获取事务的状态信息…...

cisp pte模拟题
1.信息搜集 本题共三个key 端口 1433 27689 存活ip 192.168.85.137 2.访问网站27689进行信息搜集 一个登录框,sql注入失败,暴力破解失败 扫描目录 发现三个文件robots.txt ,web.config 除了robots.txt,其他都访问不了 访问robots.txt,发现一个file参数…...

Docker容器 和 Kubernetes容器集群管理系统
一、快速了解Docker 1. 什么是Docker的定义 Docker 是一个开源的应用容器引擎,基于Go语言并遵从 Apache2.0 协议开源。Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以…...

港联证券|资金疯狂涌入AI,这一板块涨幅超90%!万亿巨头继续狂飙
国内外资金继续加码AI概念股。 当前,国内政策层面对于通用人工智能和算力中心的创新发展扶持政策不断推出,资本市场对于AI(人工智能)热情不断升温。在AI下游应用中,游戏板块成为最被看好的投资标的,资金流入…...

短视频矩阵系统源码-开源开发php语言搭建
短视频矩阵系统源码---------- php源码是什么? PHP源码指的就是PHP源代码,源代码是用特定编程语言编写的人类可读文本,源代码的目标是为可以转换为机器语言的计算机设置准确的规则和规范。因此,源代码是程序和网站的基础。 PHP…...

PFEA112-20 3BSE050091R20 张力控制器
您的张力测量系统包括: 张力电子PFEA111或PFEA112–PFEA111是一款经济高效的紧凑型用户友好型张力电子产品,提供 来自两个称重传感器的精确可靠的快速模拟SUM信号,用于控制 和/或监测。显示器可以显示SUM、单个A&B和差异信号小尺寸和DIN导…...
Java springboot+vue生成报纸排版页面的新闻官网
实现方案,可以作为您开始开发的参考: 后端: 使用Spring Boot框架构建Java Web应用;使用MyBatis Plus进行ORM映射,管理MySQL数据库;按照需求定义新闻数据表、类别数据表和用户数据表,使用默认的…...

Terra-Luna归零一年后:信任重建、加密未来路在何方?
本月既是Terra-Luna归零的一周年,也是FTX崩溃的第六个月,而这两个事件分别代表着2022年加密市场连环爆的开始与高潮,引发了加密行业15年历史上最可怕的生存危机。 尽管今年市场行情有所回暖,比特币开年至今涨幅70%,以太…...
Android 12.0 手动安装Persistent app失败的解决方案
1.概述 在12.0的系统产品开发中,对于一些安装app的失败问题,需要看日志 和抛出异常来判断问题所在,在最近的一些app安装失败抛出了关于Presistent app安装失败的问题,就需要从PMS安装的过程中看异常抛出的原因解决问题所在 2.手动安装Persistent app失败的解决方案的核心类…...

Unity3D安装:从命令行安装 Unity
推荐:将 NSDT场景编辑器 加入你的3D工具链 3D工具集: NSDT简石数字孪生 从命令行安装 Unity 如果要在组织中自动部署 Unity,可以从命令行安装 Editor 和其他组件。这些组件是普通的安装程序可执行程序和软件包,可以给用来自动部署…...

CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...

大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...

.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...

HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...

MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...