登录和退出
- 需求:
- 1:点击登录的时候,跳转到首页
- 2:假如用户点击的是 用户列表 那么返回到用户列表 => 则需要使用全局路由守卫 记录这个path
- 3:点击退出的时候,返回到登录界面,清空token!
登录
- 需求:
- 点击登录的时候,先验证表单
- 把当前的用户名,存储在本地存储之中 (用户名 密码 验证码 token)
- 并且把头部的登录隐藏 显示退出按钮!
router/index.js路由的配置
-
登录组件 和 home组件 同级!
- 在组件内最外围添加一个meta:{ } 里面设置 证书授权 requiresAuth: true title:“标题”
- 全局前置路由守卫判断 是否含有 证书授权 => 有的进行里面判断 没有的话,就直接next()
if(to.matched.some(item => item.meta.requiresAuth){}else{ next() }
- 先从本地存储拿到存储的用户登录信息!(userInfo) => 字符串的形式 (还需要一个 || {})
- 需要把字符串转化为对象形式,方便拿出token值 ,使用try{ 里面转化为对象} catch(err){ userInfo = {}}
- 然后从userInfo对象中判断是否有这个token值(authorization)
- token值存在的话,那么就是进行判断,否则直接next(到登录,还记录当前用户点击的path)
if(userInfo.authorization){ //进行判断 }else{ next({ path:"/login", query:{ redirectTo:to.fullpath //去往的那个path完整路径 } }) }
- token值存在时的判断!
- 发起请求,这就需要先引入发起请求的文件了!
import $request from "request"
- 先给next(),但是发起请求,判断token值是否失效了!,如果请求回来的token值还是成功的状态,那么就直接next(),否则的话,还是依旧 path=> 去往login,并且记录当前用户所想要去的完整path
- 发起请求,这就需要先引入发起请求的文件了!
$request.get("/jwtverify",{ params:{ authorization:userInfo.authorization } }) .then(data => { if(data.data.code === 1){ next() }else{ next({ path:"/login", query:{ redirectTo:to.fullPath } }) } }) next()
-
完整代码!
import Vue from "vue"
import VueRouter from "vue-router"
import Login0 from "../pages/Login0.vue"
import Reg from "../pages/Reg.vue"
import NotFound from "../pages/NotFound.vue"
import $request from "../netword/request"
Vue.use(VueRouter)
const router = new VueRouter({
routes: [{
path: "/",
redirect: "/login",
},{
path: '/404',
component: NotFound
}, // 404页面效果
{
path: '*',
redirect: '/404'
}, {
path: "/login",
name: "Login",
component: () => import("../pages/Login.vue")
}, {
path: "/home",
component: () => import("../pages/home/Home.vue"),
children: [{
path: "/user",
meta: {
title: "用户",
requiresAuth: true
},
component: () => import("../pages/user/Default.vue"),
children: [{
path: "/",
redirect: "list"
}, {
path: "list",
name: "UserList",
meta: {
title: "用户列表"
},
component: () => import("../pages/user/List.vue")
},
{
path: "add",
name: "UserDdd",
// component: () => import("../pages/user/Add.vue")
meta: {
title: "用户添加"
},
component: () => import("../pages/user/Edit.vue")
}, {
name: "UserEdit",
// path: "edit",
path: "edit/:id",
meta: {
title: "用户编辑"
},
component: () => import("../pages/user/Edit.vue")
}
]
}, {
name: "Order",
path: "/order",
meta: {
title: "订单管理",
requiresAuth: true
},
component: () => import("../pages/order/OrderHome.vue"),
children: [{
path: "/",
redirect: "list"
}, {
path: "list",
name: "OrderList",
meta: {
title: "订单分类"
},
component: () => import("../pages/order/OdrderList.vue")
}, {
name: "Updata",
path: "updata",
meta: {
title: "订单更新",
requiresAuth: true
},
component: () => import("../pages/order/OrderUpdata.vue")
}]
}
]
}
]
})
router.beforeEach(function (to, from, next) {
//判断目标路由是否需要登录 才可访问!
//当匹配的路由 项之中 有这个授权请求 那么就先从本地拿一下 本地的存储用户信息!
if (to.matched.some(item => item.meta.requiresAuth)) {
let userInfo = localStorage.getItem('userInfo') || {};
try {
userInfo = JSON.parse(userInfo)
} catch (err) {
userInfo = {}
}
// 判断当前用户信息是否包含token
if (userInfo.authorization) {
//发起请求 验证token是否过期
$request.get("/jwtverify", {
params: {
authorization: userInfo.authorization
}
})
.then(res => {
// console.log("我是data啊", res.data.code)
if (res.data.code === 1) {
next()
} else {
next({
path: "/login",
query: {
redirectTo: to.fullPath
}
})
}
})
next();
} else {
next({
path: "/login",
query: {
// 跳转到登录页面,并传递目标页面路径
redirectTo: to.fullPath
}
})
}
} else {
next();
}
})
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
return originalPush.call(this, location).catch(err => err)
}
export default router;
Login.vue组件
-
功能:
- 先发起请求,渲染拿到验证码
- 用户名,密码,验证码失去焦点验证
- 把请求后的用户,存储在本地之中, 需要先定义一个 userInfo 变量 存储!
- 避免userInfo 报错
const userInfo = localStorage.getItem("userInfo") || {}
- 避免userInfo 报错
- 判断 登录成功时候,不仅需要存储到本地,还有判断 用户之前操作的 path路径 这从全局前置路由守卫那边拿取!
//路由守卫 设置了 最开始 点击那里 就返回那个的path const { redirectTo = "/home" } = this.$route.query; this.$router.replace(redirectTo);
-
数据的解析:
- :rules=“rules” => 是检验的规则
- ref=“ruleForm” => 这是为了拿到当前组件的内容
- :model=“ruleForm” => 渲染的数据,以一个数组的形式!
- prop => 接受父组件传递的数据(比如:ruleForm 里面的 username等等!)
- v-model=“ruleForm.username” => 数据与表达绑定!
- 验证码是返回一个html标签,因此需要使用 v-html去绑定 需要定义一个变量!
<div v-html="vcodeSvg" class="vcode" @click="getVcode"></div>
- getVcode => 获取验证码
- handleLogin =>登录
<div class="login-wrap">
<el-form
:rules="rules"
ref="ruleForm"
:label-position="labelPosition"
label-width="80px"
class="login-form"
:model="ruleForm"
>
<h2>用户登录</h2>
<el-form-item label="用户名" prop="username">
<el-input v-model="ruleForm.username"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="ruleForm.password"></el-input>
</el-form-item>
<el-form-item label="验证码" prop="vcode">
<el-input v-model="ruleForm.vcode">
<template v-slot:append>
<div v-html="vcodeSvg" class="vcode" @click="getVcode"></div>
</template>
</el-input>
</el-form-item>
<el-form-item>
<el-checkbox label="下次免登陆" v-model="ruleForm.mdl"></el-checkbox>
</el-form-item>
<el-button type="primary" class="login-btn" @click.prevent="handleLogin">登录</el-button>
</el-form>
</div>
data() {
var checkUsername = async (rule, value, callback) => {
if (value.trim() == "") {
return callback(new Error("用户名不能为空"));
} else {
callback();
}
};
var checkUserpass = (rule, value, callback) => {
if (value.trim() == "") {
return callback(new Error("密码不能为空"));
} else {
callback();
}
};
var checkUservcode = (rule, value, callback) => {
if (value.trim() == "") {
return callback(new Error("请输入验证码"));
} else {
callback();
}
};
return {
labelPosition: "left",
vcodeSvg: "", //用于存放 请求回来的svg验证码这个html标签
token: "",
ruleForm: {
username: "",
password: "",
vcode: "",
mdl: true,
},
rules: {
username: [{ validator: checkUsername, trigger: "blur" }],
password: [{ validator: checkUserpass, trigger: "blur" }],
vcode: [{ validator: checkUservcode, trigger: "blur" }],
},
};
},
created() {
//一开始 发起请求 拿到验证码!
this.getVcode();
},
methods: {
async handleLogin() {
// console.log(1, this.ruleForm);
// 一进入登录界面的时候,就发起请求 请求验证码!
const res = await this.$request.get("/mlogin", {
params: {
...this.ruleForm,
},
});
// console.log("我是点击登录", res.data);
if (res.data.code === 1) {
this.$message.success("登录成功");
localStorage.setItem("userInfo", JSON.stringify(res.data.data));
//路由守卫 设置了 最开始 点击那里 就返回那个的path
const { redirectTo = "/home" } = this.$route.query;
this.$router.replace(redirectTo);
}
},
async getVcode() {
const res = await this.$request.get("/vcode");
this.vcodeSvg = res.data.data;
},
},
Home.vue组件
-
在头部之中,有着登录按钮,点击去往登录,登录成功后,显示用户名 和 退出按钮!
- 这时候就需要使用 template这个标签来作为载体,把v-if条件扔在这边 根据是否有token值(userInfo)
假如有token值就是显示退出,否则就是登录 - 那么这就需要监听 url 的变化 才做出决策了
watch:{ '$route.path'(to,from){} }
- 假如是从 from.path === '/login" 那么就显示登录成功了 就显示那个退出 => 调用相应的方法 操作
- 假如是从 to.path === ‘/login’ 那么就是需要去登录的状态,那么就显示登录!=> 调用相应的方法 操作
watch: { "$route.path"(to, from) { // console.log("我是路由", to, from); if (from === "/login") { this.getUserList(); } if (to === "login") { this.signOut(); } }, },
- 这时候就需要使用 template这个标签来作为载体,把v-if条件扔在这边 根据是否有token值(userInfo)
-
数据分析
- userInfo.authorization => 当前用户的token值
- signOut => 退出操作
- gotoLogin => 去往登录界面操作
<div>
<!-- 头部 -->
<el-row :gutter="20" class="header-con">
<el-col :span="12">
<div class="grid-content bg-purple">
<i class="el-icon-help"></i>
小渣亮后台管理系统
</div>
</el-col>
<el-col :span="12" style="text-align:right">
<div class="login-box">
<template v-if="userInfo.authorization">
<span>{{ userInfo.username }}</span>
<el-link type="primary" @click="signOut" style="margin-left:10px">退出</el-link>
</template>
<template v-else>
<el-link type="primary" @click="gotoLogin" style="margin-right:10px">登录</el-link>
</template>
</div>
</el-col>
</el-row>
<!-- 主体 -->
<el-container style="height: 500px; border: 1px solid #eee">
<!-- 侧边 -->
<el-aside width="200px" style="background-color: rgb(238, 241, 246)">
<el-menu
:default-active="activeIndex"
:unique-opened="true"
class="el-menu-demo"
mode="vertical"
background-color="#545c64"
text-color="#fff"
active-text-color="#ff0"
@select="change"
:default-openeds="openMenu"
router
>
<!-- active-text-color 高亮显示 -->
<template v-for="item in menu">
<!-- :index="绑定的是唯一的url路径" 注意点:就是 :key="需要写在渲染的组件上,不是元素模板!"-->
<!-- v-if 存在这个子菜单,就渲染这个子菜单项! -->
<el-menu-item :index="item.path" :key="item.path" v-if="!item.submenu">
<i :class="item.icon"></i>
{{item.text}}
</el-menu-item>
<!-- 带有子菜单的 -->
<el-submenu :index="item.path" :key="item.path" v-else>
<!-- 外层的父 -->
<template v-slot:title>
<i :class="item.icon"></i>
{{item.text}}
</template>
<!-- 里面的子 -->
<el-menu-item
:index="item.path+subitem.path"
v-for="subitem in item.submenu"
:key="subitem.path"
>{{subitem.text}}</el-menu-item>
</el-submenu>
</template>
</el-menu>
</el-aside>
<!-- 左侧内容区 -->
<el-main>
<div style="padding:15px 0;">
<router-view />
</div>
</el-main>
</el-container>
</div>
name: "App",
data() {
return {
menu: [
{
text: "首页",
path: "/home",
icon: "el-icon-s-home",
},
{
text: "权限管理",
path: "/power",
icon: "el-icon-user-solid",
submenu: [
{
text: "角色列表",
path: "/poweruserlist",
},
{
text: "权限列表",
path: "/powerList",
},
],
},
{
text: "用户管理",
path: "/user",
icon: "el-icon-user-solid",
submenu: [
{
text: "添加用户",
path: "/add",
},
{
text: "用户列表",
path: "/list",
},
],
},
{
text: "商品管理",
path: "/goods",
icon: "el-icon-grape",
submenu: [
{
text: "商品列表",
path: "/list",
},
{
text: "商品添加",
path: "/add",
},
{
text: "商品分类",
path: "/type",
},
],
},
{
text: "订单管理",
path: "/order",
icon: "el-icon-s-order",
submenu: [
{
text: "订单列表",
path: "/list",
},
{
text: "订单更新",
path: "/updata",
},
],
},
],
currentIndex: 0,
activeIndex: "/home",
openMenu: ["/user"],
userInfo: {},
};
},
created() {
this.getUserList();
},
watch: {
"$route.path"(to, from) {
// console.log("我是路由", to, from);
if (from === "/login") {
this.getUserList();
}
if (to === "login") {
this.signOut();
}
},
},
methods: {
getUserList() {
// 拿到本地的token值 ,防止出错 需要 在 ||一个空对象! 由于拿到的是字符串 需要转化为对象! 使用try的话 就不用判断了!
let userInfo = localStorage.getItem("userInfo") || {};
try {
userInfo = JSON.parse(userInfo);
} catch (err) {
this.userInfo = {};
}
this.userInfo = userInfo;
// console.log("我是token", userInfo, userInfo.authorization);
},
gotoLogin() {
this.$router.push({
name: "Login",
});
},
change(path, idx) {
this.activeIndex = path;
this.currentIndex = idx; //这是点击事件修改的idx值!
this.$router.push(path);
},
signOut() {
//点击退出 ,也就是清除token
localStorage.removeItem("userInfo");
this.$message.success("退出成功");
this.userInfo = {};
this.$router.push("/login");
},
},