这次新增的功能有:
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)
}
// 读取tokenexport 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里面的tokendeleteToken()// 清楚当前用户状态vuexcommit("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里面的tokendeleteToken()// 清楚当前用户状态vuexcommit("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、前置守卫,每次访问接口都通过这个判断有没有权限没权限就拒绝:
导航守卫 | Vue Router
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 : "/"})}// 如果用户登录了,自动获取用户信息存储到vuexif (token){// 异步操作,添加awaitxawait 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 = trueconsole.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>