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 和其他组件。这些组件是普通的安装程序可执行程序和软件包,可以给用来自动部署…...

C++模板(详解)
非类型模板参数 模板参数可分为类型形参和非类型形参。类型形参: 出现在模板参数列表中,跟在class或typename关键字之后的参数类型名称。非类型形参: 用一个常量作为类(函数)模板的一个参数,在类ÿ…...

WuThreat身份安全云-TVD每日漏洞情报-2023-05-25
漏洞名称:Mitsubishi Electric MELSEC iQ-F 数据包缓冲区溢出 漏洞级别:严重 漏洞编号:CVE-2023-1424 相关涉及:Mitsubishi Electric Corporation MELSEC iQ-F Series CPU 漏洞状态:未定义 参考链接:https://tvd.wuthreat.com/#/listDetail?TVD_IDTVD-2023-12805 漏洞名称:Ap…...

android 12.0去掉recovery模式UI页面的选项
1.概述 在12.0进行定制化开发,会根据需要去掉recovery模式的一些选项 就是在device.cpp去掉一些选项就可以了 2.去掉recovery模式UI页面的选项核心代码 bootable/recovery/recovery_ui/device.cpp bootable/recovery/recovery_main.cpp 3.去掉recovery模式UI页面的选项的核…...

C++ vector类成员函数介绍
目录 🤔vector模板介绍: 🤔特点: 🤔vector的成员函数: 🔍vector构造函数: 🔍vector赋值函数 🔍vector容器的判断函数 resize函数的重点内容: …...

【C++】二叉搜索树Binary Search Tree
Binary Search Tree 二叉搜索树的概念二叉搜索树的操作二叉搜索树的实现查找插入删除 二叉搜索树的应用二叉搜索树的性能分析 二叉搜索树的概念 二叉搜索树又被称为二叉排序树,顾名思义,当我们使用中序遍历时,会得到一个有序的序列。二叉搜索…...

Hover.css动画库的使用
目录 1、 Hover.css是什么? 2、引入 2.1、整个文件引入 2.2、复制所需要的代码 案例: 1. 卷边效果 2. 调整大小的卷边 类别: 1、 Hover.css是什么? Hover.css是一个CSS3鼠标悬停的动画方案,里面包含了许多纯c…...

Baumer工业相机堡盟工业相机如何通过文件保存和导入的方式保存和载入相机的各类参数(C#)
Baumer工业相机堡盟工业相机如何通过文件保存和导入的方式使保存和载入相机的各类参数(C#) Baumer工业相机Baumer工业相机BGAPISDK中UserSet的技术背景相机配置文件代码案例分享第一步:保存相机当前参数设置doUserSetStore为文件第二步&#…...

封装设计!抽象BasePage,提升WEB自动化测试用例质量和效率
目录 前言: 一、什么是抽象BasePage 二、BasePage中的属性和方法 三、BasePage中的代码实现 四、抽象Page对象 五、测试用例 六、总结 前言: 对于测试工程师来说,WEB自动化测试是非常重要的一部分。然而,WEB自动化测试的开…...

c primer plus学习笔记(一)
1.int的大小恒定就是32位么? 不是的,int大小是跟着系统走的,不是在各个系统里固定不变的。 32位系统int就是32位。64位系统,int就是64位。short 和long的长度则跟着int走,一般来说int是32位,short就是16位…...

C语言2:说心里话
描述 分两次从控制台接收用户的两个输入:第一个内容为“人名”,第一个内容为“心里 话”。 然后将这两个输入内容组成如下句型并输出出来: 1.(人名),I want to say,(心里话 2. 输入输出示例: 输入ÿ…...