vue實(shí)現(xiàn)用戶動態(tài)權(quán)限登錄的代碼示例
一、使用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, // 是否校驗(yàn)語法 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" // 路由守衛(wèi),做動態(tài)路由的地方 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' // 頁面布局的右側(cè)區(qū)域 import sidebar from './sideBar' // 頁面布局的左側(cè)菜單 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-menu mode="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-link v-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 bar NProgress.configure({ showSpinner: false }) // NProgress Configuration const filterRoutes = ["/login"] // 需要過濾掉的路由 router.beforeEach((to, from, next) => { // start progress bar NProgress.start() // 獲取路由 meta 中的title,并設(shè)置給頁面標(biāo)題 document.title = "動態(tài)路由(" + to.meta.title + ")" // 判斷路由指向是否在需要過濾的路由地址數(shù)組里 // 如果在,則直接跳進(jìn)頁面,無需判斷 if(filterRoutes.indexOf(to.path) !== -1) { next() return false } if(router.options.routes.length == 1) { // 獲取token和原始路由數(shù)組 const userInfo = JSON.parse(window.localStorage.getItem('userInfo')) ?? "" // 當(dāng)token和原始路由都存在的時(shí)候 if(userInfo.token && userInfo.routes) { onFilterRoutes(to, next, userInfo.routes) // 執(zhí)行路由過濾和跳轉(zhuǎn) } else { next({ path: "/login", replace: true }) } } else next() }) router.afterEach(() => { // finish progress bar NProgress.done() }) // 路由拼接 function loadView(view) { return () => import(`@/views/${ view }`) } // 路由過濾和跳轉(zhuǎn) 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 }) } // 路由過濾 遍歷路由 轉(zhuǎn)換為組件對象和路徑 function filterASyncRoutes(data) { const routes = data.filter(item => { if(item["component"] === "Layout") item.component = Layout else item["component"] = loadView(item["component"]) // 路由遞歸,轉(zhuǎn)換組件對象和路徑 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">動態(tài)路由</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 = !1 window.localStorage.removeItem("userInfo") dynamicUser.forEach(item => { if(item["username"] == this.user['username'] && item["password"] == this.user['password']) { flag = !0 Message({ type: 'success', message: "登錄成功", showClose: true, duration: 3000 }) window.localStorage.setItem("userInfo", JSON.stringify(item)) this.$router.replace({ path: "/" }) } }) if(!flag) Message({ type: 'warning', message: "賬號密碼錯(cuò)誤,請重試!", 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>
- 動態(tài)返回路由
使用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實(shí)現(xiàn)用戶動態(tài)權(quán)限登錄的代碼示例的詳細(xì)內(nèi)容,更多關(guān)于vue 用戶動態(tài)權(quán)限登錄的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vuex state及mapState的基礎(chǔ)用法詳解
這篇文章主要介紹了vuex state及mapState的基礎(chǔ)用法詳解,本文通過實(shí)例代碼相結(jié)合的形式給大家介紹的非常詳細(xì),需要的朋友跟隨腳本之家小編一起學(xué)習(xí)吧2018-04-04使用vue開發(fā)移動端管理后臺的注意事項(xiàng)
這篇文章主要介紹了使用vue開發(fā)移動端管理后臺的注意事項(xiàng),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-03-03vue中的el-tree @node-click傳自定義參數(shù)
這篇文章主要介紹了vue中的el-tree @node-click傳自定義參數(shù)方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08Vue 中如何利用 new Date() 獲取當(dāng)前時(shí)間
在 Vue 開發(fā)中,利用 new Date() 方法可以方便地獲取當(dāng)前時(shí)間,并通過 Date 對象的方法進(jìn)行時(shí)間格式化和操作。通過本文的介紹,您應(yīng)該對在 Vue 中獲取當(dāng)前時(shí)間有了更深入的了解,并了解了一些常見的時(shí)間操作方法,需要的朋友可以參考下2023-07-07vue2.0.js的多級聯(lián)動選擇器實(shí)現(xiàn)方法
下面小編就為大家分享一篇vue2.0.js的多級聯(lián)動選擇器實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-02-02Vue手機(jī)號正則匹配姓名加密展示功能的實(shí)現(xiàn)
這篇文章主要介紹了Vue手機(jī)號正則匹配,姓名加密展示,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-08-08