Vue + ElementUI 后台管理项目实战
文章目录
- 项目演示
- 八、项目实战八
- Ⅰ、登录界面
- 1. 编写 login 页面
- 2. 登录权限 & 导航守卫
- 2. 登录接口逻辑
- 3. 菜单权限功能
- Ⅱ、权限管理问题 & 退出登录
- 1. 刷新白屏的解决方法
- 2. 权限管理
- 3. 退出功能
项目演示
vue + element-ui 项目演示
八、项目实战八
Ⅰ、登录界面
1. 编写 login 页面
- ./views/Login/login.vue,编写登录页面
<template>
<!--status-icon: 在输入框中显示校验结果反馈图标 -->
<el-form
:model="form"
status-icon
:rules="rules"
ref="form"
label-width="100px"
class="login-container"
>
<h3 class="login_title">系统登录</h3>
<!-- prop:定义在form中对应的字段 -->
<el-form-item
label="用户名"
label-width="80px"
prop="username"
class="username"
>
<!-- autocomplete:表单是否启用自动完成功能。自动完成允许浏览器对字段的输入,是基于之前输入过的值。 -->
<el-input
type="input"
v-model="form.username"
autocomplete="off"
placeholder="请输入账号"
>
</el-input>
</el-form-item>
<el-form-item
label="密码"
label-width="80px"
prop="password"
>
<el-input
type="password"
v-model="form.password"
autocomplete="off"
placeholder="请输入密码"
></el-input>
</el-form-item>
<el-form-item class="login_submit">
<el-button type="primary" @click="login" class="login_submit">登录</el-button>
</el-form-item>
</el-form>
</template>
<script>
// import Mock from 'mockjs'
import {getMenu} from '../../api/data'
export default {
name: "Login",
data() {
return {
form: {},
// 表单校验的定义
rules: {
username: [
// 用户名校验:必需、没有输入会有提示、失去焦点触发
{ required: true, message: "请输入用户名", trigger: "blur" },
{
min: 3,
message: "用户名长度不能小于3位",
trigger: "blur",
},
],
// 密码校验:必需、提示、失去焦点触发
password: [{ required: true, message: "请输入密码", trigger: "blur" }],
},
};
},
};
</script>
<style lang="less" scoped>
.login-container {
border-radius: 15px;
// 背景裁剪的内边距
background-clip: padding-box;
margin: 180px auto;
width: 350px;
padding: 35px 35px 15px 35px;
background-color: #fff;
border: 1px solid #eaeaea;
box-shadow: 0 0 25px #cac6c6;
}
.login_title {
margin: 0px auto 40px auto;
text-align: center;
color: #505458;
}
.login_submit {
margin: 10px auto 0 auto;
}
</style>
登录页面
2. 登录权限 & 导航守卫
- 安装缓存插件
npm i js-cookie
- 在 store 文件中,创建 user.js 文件,用于缓存输入的内容。
import Cookie from "js-cookie";
export default {
state: {
token: "",
},
mutations: {
// 设置cookie
setToken(state, val) {
(state.token = val), Cookie.set("token", val); //cookie的名称,传入的值
},
// 清除cookie
clearToken(state) {
(state.token = ""), Cookie.remove("token");
},
// 获取cookie
getToken(state) {
// 如果当前的缓存中有token,直接获取。如果没有,要从state中获取
state.token = Cookie.get("token") || state.token;
},
},
};
- 在 ./store/index.js 中导入
import Vue from 'vue'
import Vuex from 'vuex'
import tab from './tab'
import user from './user'
// 全局使用Vuex
Vue.use(Vuex)
export default new Vuex.Store({
// 模块化定义
modules:{
tab,
user
}
})
- main.js 中添加前置路由守卫
// 前置路由守卫
router.beforeEach((to, from, next) => {
store.commit('getToken') //防止页面刷新后vuex丢失token信息
const token = store.state.user.token
// 如果token不存在,并且当前页不是登录页
if(!token && to.name !== 'login') {
next({name: 'login'}) // 返回登录页
} else if(token && to.name === 'login'){
next({name: 'home'})
} else {
next()
}
})
2. 登录接口逻辑
- ./api/mockServerData/permission.js,用于定义接口相关的逻辑
// 接口的相关逻辑
import Mock from 'mockjs'
export default {
// 模拟菜单权限,接收传递进来的参数
getMenu: config => {
console.log(config);
const { username, password } = JSON.parse(config.body)
console.log(JSON.parse(config.body))
// 先判断用户是否存在
// 判断账号和密码是否对应
if (username === 'admin' && password === 'admin') {
return {
code: 20000,
data: {
menu: [
{
path: '/home',
name: 'home',
label: '首页',
icon: 's-home',
url: 'home/index'
},
{
path: '/mall',
name: 'mall',
label: '商品管理',
icon: 'video-play',
url: 'mall/index'
},
{
path: '/user',
name: 'user',
label: '用户管理',
icon: 'user',
url: 'User/index'
},
{
label: '其他',
icon: 'location',
children: [
{
path: '/page1',
name: 'page1',
label: '页面1',
icon: 'setting',
url: 'other/pageOne.vue'
},
{
path: '/page2',
name: 'page2',
label: '页面2',
icon: 'setting',
url: 'other/pageTwo.vue'
}
]
}
],
token: Mock.Random.guid(),
message: '获取成功'
}
}
} else if (username === 'xiaoxiao' && password === 'xiaoxiao') {
return {
code: 20000,
data: {
menu: [
{
path: '/',
name: 'home',
label: '首页',
icon: 's-home',
url: 'home/index'
},
{
path: '/mall',
name: 'mall',
label: '商品管理',
icon: 'video-play',
url: 'mall/index'
}
],
token: Mock.Random.guid(),
message: '获取成功'
}
}
} else {
return {
code: -999,
data: {
message: '密码错误'
}
}
}
}
}
- 在 mock.js 中进行接口拦截
import permissionApi from './mockServerData/permission'
Mock.mock(/permission\/getMenu/, 'post', permissionApi.getMenu)
3. 菜单权限功能
- 动态添加路由,在 tab.js 中定义 menu 空数组
menu: []
- 在 tab.js 的 mutations 中添加修改方法
setMenu(state, val) {
state.menu = val
Cookie.set('menu', JSON.stringify(val))
},
clearMenu(state) {
state.menu = []
Cookie.remove('menu')
},
addMenu(state, router) {
if(!Cookie.get('menu')) {
return
}
// 转成对象
const menu = JSON.parse(Cookie.get('menu'))
state.menu = menu
const menuArray = []
menu.forEach(item => {
// 有二级菜单的数据
if(item.children) {
item.children = item.children.map(item => {
item.component = () => import(`../views/${item.url}`)
return item
})
menuArray.push(...item.children)
// 一级菜单
}else{
item.component = () => import(`../views/${item.url}`)
menuArray.push(item)
}
})
// 路由的动态添加
menuArray.forEach(item => {
router.addRoute('Main', item)
})
}
- login.vue 中添加方法
login() {
getMenu(this.form).then((res) => {
console.log(res, "res");
// 接口调用成功
if (res.code === 20000) {
// 登录成功后,清除当前路由
this.$store.commit("clearMenu");
// 设置路由,传入数据
this.$store.commit("setMenu", res.data.menu);
// 设置token,传入接口的数据
this.$store.commit("setToken", res.data.token);
// 动态添加路由,传入router 实例
this.$store.commit("addMenu", this.$router);
// 页面跳转
this.$router.push({ name: "home" });
} else {
//失败的提示
this.$message.warning(res.data.message);
}
});
}
这样就可以把 CommonAside.vue 中写死的数据去掉,只留 menu: []。
.router/index.js 中里面的数据都删掉,只保留 children: []。
- 在 CommonAside.vue 中定义 asyncMenu(),用来获取 menu
computed: {
noChildren() {
// 过滤出来没有子项目的数据
return this.asyncMenu.filter((item) => !item.children);
},
hasChildren() {
// 过滤出有子项目的数据
return this.asyncMenu.filter((item) => item.children);
},
isCollapse() {
return this.$store.state.tab.isCollapse;
},
asyncMenu() {
// 获取menu
return this.$store.state.tab.menu
}
}
登录成功
Ⅱ、权限管理问题 & 退出登录
1. 刷新白屏的解决方法
- main.js 中,在 vue实例生成前, created 钩子中调用动态路由的方法。
created() {
store.commit('addMenu', router)
}
2. 权限管理
已经登录后,不应该还能访问登录页面,而是让它跳转到首页。
- 在 main.js 中修改路由守卫
router.beforeEach((to, from, next) => {
store.commit('getToken') //防止页面刷新后vuex丢失token信息
const token = store.state.user.token
// 如果token不存在,并且当前页不是登录页
if(!token && to.name !== 'login') {
next({name: 'login'}) // 返回登录页
} else if(token && to.name === 'login'){
next({name: 'home'})
} else {
next()
}
})
3. 退出功能
在 CommonHeader.vue 中添加退出功能
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人中心</el-dropdown-item>
<el-dropdown-item @click.native="logOut">退出</el-dropdown-item>
</el-dropdown-menu>
logOut() {
this.$store.commit("clearToken"); //清除token
this.$store.commit("clearMenu"); //清除menu
this.$router.push("/login"); //跳转到登录界面
}
点击退出,返回到登录界面