这次新增的功能有:
1、使用cookie存储token
参考网站:https://vueuse.org/
安装包:
npm i @vueuse/integrations
npm i universal-cookie@^7
2、cookie的设置读取和删除,代码:composables/auth.js
import { useCookies } from '@vueuse/integrations/useCookies'
const cookie = useCookies()
const tokenKey = "admin-token"
export function setToken(token){
return cookie.set(tokenKey, token)
}
// 读取token
export function getToken(){
return cookie.get(tokenKey)
}
// 删除token
export function deleteToken(){
return cookie.remove(tokenKey)
}
3、存储token
统一封装到permission.js
4、添加拦截器,axios中文文档|axios中文网 | axios,代码axios.js
import { createStore } from 'vuex'
import { setToken } from '~/composables/auth';
import { getInfo } from '~/api/manager'
import { login } from '~/api/manager'
import { deleteToken } from '../composables/auth';
//
// 创建一个新的 store 实例
const store = createStore({
state() {
return {
// 用户信息
user: {}
}
},
mutations: {
// 记录用户信息
SET_USERINFO(state, user) {
state.user = user
}
},
actions: {
// 登录
login({ commit }, { username, password }) {
return new Promise((resolve, reject) => {
login(username, password).then(res => {
setToken(res.token)
resolve(res)
}).catch(err => reject(err))
})
},
// 获取当前登录用户信息
getInfo({ commit }) {
return new Promise((resolve, reject) => {
getInfo().then(res => {
commit("SET_USERINFO", res)
resolve(res)
}).catch(err => reject(err))
})
},
// 退出登录
logout({ commit }) {
// 移除cookie里面的token
deleteToken()
// 清楚当前用户状态vuex
commit("SET_USERINFO", {})
}
},
})
export default store
5、抽出统一的方法,在manager.js里:
import axios from "~/axios"
export function login(username, password){
return axios.post("/admin/login",{
username,
password
})
}
export function getInfo(){
return axios.post("/admin/getinfo")
}
export function logout(){
return axios.post("/admin/logout")
}
6、写一个Util,封装成功或失败的提示方法
import { ElNotification } from 'element-plus'
import { fa } from 'element-plus/es/locales.mjs'
import { ElMessage, ElMessageBox } from 'element-plus'
// 成功提示
export function toast(message, type = "success", dangerouslyUseHTMLString=false){
ElNotification({
message,
type,
dangerouslyUseHTMLString,
duration: 3000
})
}
export function showModel(content = "提示内容", type = "warning",title=""){
return ElMessageBox.confirm(
content,
title,
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: type,
}
)
}
7、Vuex的状态管理,参考网站:https://vuex.vuejs.org/zh/
可以将我们登录完之后拿到的用户信息共享给其它页面和组件
安装:npm install vuex@next --save
a、新建:store/index.js
import { createStore } from 'vuex'
import { setToken } from '~/composables/auth';
import { getInfo } from '~/api/manager'
import { login } from '~/api/manager'
import { deleteToken } from '../composables/auth';
//
// 创建一个新的 store 实例
const store = createStore({
state() {
return {
// 用户信息
user: {}
}
},
mutations: {
// 记录用户信息
SET_USERINFO(state, user) {
state.user = user
}
},
actions: {
// 登录
login({ commit }, { username, password }) {
return new Promise((resolve, reject) => {
login(username, password).then(res => {
setToken(res.token)
resolve(res)
}).catch(err => reject(err))
})
},
// 获取当前登录用户信息
getInfo({ commit }) {
return new Promise((resolve, reject) => {
getInfo().then(res => {
commit("SET_USERINFO", res)
resolve(res)
}).catch(err => reject(err))
})
},
// 退出登录
logout({ commit }) {
// 移除cookie里面的token
deleteToken()
// 清楚当前用户状态vuex
commit("SET_USERINFO", {})
}
},
})
export default store
b、在main.js里面引入
import { createApp } from 'vue'
// import './style.css'
import App from './App.vue'
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import router from './router'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import store from './store'
import "./permission"
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.use(ElementPlus)
app.use(router)
app.use(store)
import 'virtual:windi.css'
app.mount('#app')
8、前置守卫,每次访问接口都通过这个判断有没有权限没权限就拒绝:
a、permission.js
import router from "~/router"
import {getToken} from "~/composables/auth"
import {toast} from "~/components/util"
import store from "./store"
// 前置守卫
router.beforeEach(async (to, from, next) => {
console.log("前置守卫")
const token = getToken()
console.log(token)
console.log(to.path)
// 没有登录,强制跳转为登录页面
if (!token && to.path != "/login"){
toast("请先登录", "error")
return next({path:"/login"})
}
// 防止重复登录
console.log(to.path)
if (token && to.path == "/login"){
toast("不允许重复登录", "error")
return next({path:from.path ? from.path : "/"})
}
// 如果用户登录了,自动获取用户信息存储到vuex
if (token){
// 异步操作,添加awaitx
await store.dispatch("getInfo")
}
next()
})
b、需要在main.js里面引入
import "./permission"
9、添加键盘事件
a、输入密码即可回车登录,F12,查看打印的信息
b、具体代码见:login.vue
10、退出登录
以上所有功能完整的login.vue代码:
<template>
<el-row class="login-container">
<el-col :lg="16" :md="12" class="left">
<div>
<div class="font-bold text-5xl text-light-50 mb-4">欢迎光临</div>
<div class="text-gray-200 text-sm">此站点是学习演示</div>
</div>
</el-col>
<el-col :lg="8" :md="12" class="right">
<h2 class="title">欢迎回来</h2>
<div class="">
<span class="line"></span>
<span>账号密码登录</span>
<span class="line"></span>
</div>
<el-form ref="formRef" :model="form" class="w-[250px]" :rules="rules">
<el-form-item prop="username">
<el-input v-model="form.username" placeholder="请输入用户名">
<template #prefix>
<el-icon>
<User />
</el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input v-model="form.password" placeholder="请输入密码" type="password" show-password>
<template #prefix>
<el-icon>
<Lock />
</el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item>
<el-button class="w-[250px]" type="primary" round @click="onSubmit" color="#626aef" :loading="loadding">登录</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
</template>
<script setup>
import { reactive, ref, onMounted, onBeforeUnmount } from 'vue';
// import { User, Lock} from '@element-plus/icons-vue'
import { login } from '~/api/manager'
import { toast } from '~/components/util';
// import { getInfo } from '~/api/manager'
import { ElNotification } from 'element-plus'
import { useRouter } from 'vue-router';
import { Loading } from '@element-plus/icons-vue';
import { fa, tr } from 'element-plus/es/locales.mjs';
import { setToken } from '~/composables/auth';
import { useStore } from 'vuex';
const router = useRouter()
const store = useStore()
const loadding = ref(false)
const form = reactive({
username: '',
password: ''
})
const rules = {
username: [
{
required: true,
message: '请输入用户名',
trigger: 'blur',
},
// {
// min: 6,
// max: 6,
// message: '用户名长度必须是6个字符',
// trigger: 'blur'
// }
],
password: [
{
required: true,
message: '请输入密码',
trigger: 'blur',
}
]
}
// 对表单输入的值做校验
const formRef = ref(null)
const onSubmit = () => {
formRef.value.validate((valid) => {
console.log('valid')
if (!valid) {
return false
}
loadding.value = true
console.log("开始请求接口")
store.dispatch("login", form).then(res=>{
toast("登录成功")
router.push("/")
})
.finally(()=>{
loadding.value = false
})
// login(form.username, form.password).then(res => {
// console.log(res)
// toast("登录成功")
// 提示成功
// ElNotification({
// message: "登录成功",
// type: 'success',
// duration: 3000
// })
// 存储用户相关信息
// const cookie = useCookies()
// setToken(res.token)
// cookie.set("admin-token", res.token)
// 获取用户相关信息
// getInfo().then(res2 =>{
// store.commit('SET_USERINFO', res2)
// console.log(res2)
// })
// 跳转后台首页
// router.push("/")
// }).finally(()=>{
// loadding.value = false
// })
})
}
// 监听回车事件
function onKeyUp(e){
console.log(e)
if (e.key == "Enter") onSubmit()
}
// 添加键盘监听
onMounted(()=>{
document.addEventListener("keyup", onKeyUp)
})
// 移除键盘监听
onBeforeUnmount(()=>{
document.removeEventListener("keyup", onKeyUp)
})
</script>
<style scoped>
.login-container {
@apply min-h-screen bg-indigo-500
}
.login-container .left, .login-container .right {
@apply flex items-center justify-center
}
.login-container .right {
@apply bg-light-50 flex-col
}
.login-container .right .title {
@apply font-bold text-3xl text-gray-900
}
.login-container .right>div {
@apply flex items-center justify-center my-5 space-x-2 text-gray-300
}
.login-container .right>div .line {
@apply h-[1px] w-16 bg-gray-200
}
</style>