今天来实现vue3-admin商品管理后台项目的登录页功能
首先在pages文件夹下面添加一个login.vue文件,里面先写入简单的template

<template><div>登录</div>
</template>

然后在router文件夹下面的Index.js里面编辑,仍然是引入页面配置路由,about页暂时没啥用,只是测试用的,所以就把它删了。

import { createRouter, createWebHashHistory } from 'vue-router'
import Index from '~/pages/index.vue'
import Login from '~/pages/login.vue'
import NotFound from '~/pages/404.vue'const routes = [{// 根路由path:"/",component:Index
},{// 登录路由path:"/login",component:Login
},{//404路由 将匹配所有内容并将其放在 `$route.params.pathMatch` 下path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound
}]const router = createRouter({history:createWebHashHistory(),routes
})
export default router

运行项目域名输入http://127.0.0.1:5173/#/login,可以看见成功了

然后开始正式编写登录页代码,因为要做响应式布局,所以优先使用element plus的layout布局。然后我直接给出login.vue做完布局后的代码:结合了windi css进行布局,注释写的很详细

<template><!-- el-row会将页面分为24份,用span进行值的设定 这里面的class是windi css里面的颜色快捷方式,以及设置min-height使其占满整个屏幕,--><el-row class="min-h-screen bg-indigo-500"><!-- 左边布局 class里面实现的主要是垂直水平居中 flex-col就是垂直方向--><el-col :span="16" class="flex items-center justify-center"><div><!-- class里面的内容分别是:加粗,字体增大,字体加亮,与下面外间距1rem --><div class="font-bold text-5xl text-light-50 mb-4">欢迎光临</div><div class="text-gray-200 text-sm">此站是vue-admin的登录页面,作者是mzldustu</div></div></el-col><!-- 右边布局 -->  <el-col:span="8"class="bg-light-50 flex items-center justify-center flex-col"><!-- 加粗,字体3xl,颜色深灰色 --><h2 class="font-bold text-3xl text-gray-800">欢迎回来</h2><!-- flex布局,水平垂直居中,上下边距, 浅灰色,水平方向元素的间距 --><div class="flex items-center justify-center my-5 text-gray-300 space-x-2"><!-- 高度,宽度,浅灰色 --><span class="h-[1px] w-16 bg-gray-200"></span><span>账号密码登录</span><span class="h-[1px] w-16 bg-gray-200"></span></div><!-- 这里使用的是element plus里面的典型表单 --><!-- 表单宽度 --><el-form :model="form" class="w-[250px]"><!-- 用户名输入框 --><el-form-item><el-input v-model="form.username" placeholder="请输入用户名"/></el-form-item><!-- 密码输入框 --><el-form-item><el-input v-model="form.password" placeholder="请输入密码"/></el-form-item><!-- 按钮 --><el-form-item><!-- 圆角,宽度250px,颜色 --><el-button round color="#626aef" class="w-[250px]" type="primary" @click="onSubmit">登录</el-button></el-form-item></el-form></el-col></el-row>
</template><script setup>
import { reactive } from 'vue'// do not use same name with ref
const form = reactive({username:"",password:""
})const onSubmit = () => {console.log('submit!')
}
</script>

以上代码之后,login.vue的页面最终效果就是:

到此登录页的样式布局就写完了。

然后我们进行登录页的响应式处理以及输入框图标的引入:
直接上代码吧 ,注释写的很明白,图标用的是element plus里面的图标引入

<template><!-- el-row会将页面分为24份,用span进行值的设定 这里面的class是windi css里面的颜色快捷方式,以及设置min-height使其占满整个屏幕,--><el-row class="min-h-screen bg-indigo-500"><!-- 左边布局 class里面实现的主要是垂直水平居中 flex-col就是垂直方向--><!-- 因为要响应式布局,所以我们使用windi css里面类似媒体查询的一个功能 lg是>1200px的屏幕,md是>992px的屏幕--><el-col :lg="16" :md="12" class="flex items-center justify-center"><div><!-- class里面的内容分别是:加粗,字体增大,字体加亮,与下面外间距1rem --><div class="font-bold text-5xl text-light-50 mb-4">欢迎光临</div><div class="text-gray-200 text-sm">此站是vue-admin的登录页面,作者是mzldustu</div></div></el-col> <!-- 右边布局 -->  <el-col :lg="8" :md="12" class="bg-light-50 flex items-center justify-center flex-col"><!-- 加粗,字体3xl,颜色深灰色 --><h2 class="font-bold text-3xl text-gray-800">欢迎回来</h2><!-- flex布局,水平垂直居中,上下边距, 浅灰色,水平方向元素的间距 --><div class="flex items-center justify-center my-5 text-gray-300 space-x-2"><!-- 高度,宽度,浅灰色 --><span class="h-[1px] w-16 bg-gray-200"></span><span>账号密码登录</span><span class="h-[1px] w-16 bg-gray-200"></span></div><!-- 这里使用的是element plus里面的典型表单 --><!-- 表单宽度 --><el-form :model="form" class="w-[250px]"><!-- 用户名输入框 --><el-form-item><el-input v-model="form.username" placeholder="请输入用户名"><!-- 插槽引入user图标 --><template #prefix><el-icon><User /></el-icon></template></el-input></el-form-item><!-- 密码输入框 --><el-form-item><el-input v-model="form.password" placeholder="请输入密码"><!-- 插槽引入lock图标 --><template #prefix> <el-icon><Lock /></el-icon></template></el-input></el-form-item><!-- 按钮 --><el-form-item><!-- 圆角,宽度250px,颜色 --><el-button round color="#626aef" class="w-[250px]" type="primary" @click="onSubmit">登录</el-button></el-form-item></el-form></el-col></el-row>
</template><script setup>
import { reactive } from 'vue'
// 引入图标
import { User,Lock } from '@element-plus/icons-vue'// do not use same name with ref
const form = reactive({username:"",password:""
})const onSubmit = () => {console.log('submit!')
}
</script>

用windi css 的@apply抽离样式代码
很简单其实,直接看代码就行

<template><el-row class="login-container"><el-col :lg="16" :md="12" class="left"><div><div>欢迎光临</div><div>此站是vue-admin的登录页面,作者是mzldustu</div></div></el-col> <!-- 右边布局 -->  <el-col :lg="8" :md="12" class="right"><h2 class="title">欢迎回来</h2><div><span class="line"></span><span>账号密码登录</span><span class="line"></span></div><!-- 这里使用的是element plus里面的典型表单 --><!-- 表单宽度 --><el-form :model="form" class="w-[250px]"><!-- 用户名输入框 --><el-form-item><el-input v-model="form.username" placeholder="请输入用户名"><!-- 插槽引入user图标 --><template #prefix><el-icon><User /></el-icon></template></el-input></el-form-item><!-- 密码输入框 --><el-form-item><el-input v-model="form.password" placeholder="请输入密码"><!-- 插槽引入lock图标 --><template #prefix> <el-icon><Lock /></el-icon></template></el-input></el-form-item><!-- 按钮 --><el-form-item><!-- 圆角,宽度250px,颜色 --><el-button round color="#626aef" class="w-[250px]" type="primary" @click="onSubmit">登录</el-button></el-form-item></el-form></el-col></el-row>
</template><script setup>
import { reactive } from 'vue'
// 引入图标
import { User,Lock } from '@element-plus/icons-vue'// do not use same name with ref
const form = reactive({username:"",password:""
})const onSubmit = () => {console.log('submit!')
}
</script><style scoped>
.login-container{/*el-row会将页面分为24份,用span进行值的设定 这里面的class是windi css里面的颜色快捷方式,以及设置min-height使其占满整个屏幕 */@apply min-h-screen bg-indigo-500;
}/* <!-- 左边布局 class里面实现的主要是垂直水平居中 flex-col就是垂直方向--><!-- 因为要响应式布局,所以我们使用windi css里面类似媒体查询的一个功能 lg是>1200px的屏幕,md是>992px的屏幕--> */
.login-container .left, .login-container .right{@apply flex items-center justify-center;
}.login-container .right{@apply bg-light-50 flex-col;
}/* class里面的内容分别是:加粗,字体增大,字体加亮,与下面外间距1rem */
.left>div>div:first-child{@apply font-bold text-5xl text-light-50 mb-4;
}.left>div>div:last-child{@apply text-gray-200 text-sm;
}
/* 加粗,字体3xl,颜色深灰色 */
.right .title{@apply font-bold text-3xl text-gray-800;
}/* flex布局,水平垂直居中,上下边距,浅灰色,水平方向元素的间距 */
.right>div{@apply flex items-center justify-center my-5 text-gray-300 space-x-2;
}/* 高度,宽度,浅灰色 */
.right .line{@apply h-[1px] w-16 bg-gray-200;
}
</style>

到这里就抽离的差不多了。

然后实现登录表单验证处理:
login.vue的代码:

<template><el-row class="login-container"><el-col :lg="16" :md="12" class="left"><div><div>欢迎光临</div><div>此站是vue-admin的登录页面,作者是mzldustu</div></div></el-col> <!-- 右边布局 -->  <el-col :lg="8" :md="12" class="right"><h2 class="title">欢迎回来</h2><div><span class="line"></span><span>账号密码登录</span><span class="line"></span></div><!-- 这里使用的是element plus里面的典型表单 --><!-- 表单宽度 --><!-- 引入element里的rules ,定义一个ref--><el-form ref="formRef" :rules="rules" :model="form" class="w-[250px]"><!-- 用户名输入框 --><el-form-item prop="username"><el-input v-model="form.username" placeholder="请输入用户名"><!-- 插槽引入user图标 --><template #prefix><el-icon><User /></el-icon></template></el-input></el-form-item><!-- 密码输入框 --><el-form-item prop="password"><!-- type设为password就是非明文存储,show-password就是后面的小眼睛,可以点击显示不显示密码 --><el-input type="password" v-model="form.password" placeholder="请输入密码" show-password><!-- 插槽引入lock图标 --><template #prefix> <el-icon><Lock /></el-icon></template></el-input></el-form-item><!-- 按钮 --><el-form-item><!-- 圆角,宽度250px,颜色 --><el-button round color="#626aef" class="w-[250px]" type="primary" @click="onSubmit">登录</el-button></el-form-item></el-form></el-col></el-row>
</template><script setup>
import { reactive, ref } from 'vue'
// 引入图标
import { User,Lock } from '@element-plus/icons-vue'// do not use same name with ref
const form = reactive({username:"",password:""
})// 表单验证rules,要在前面指定prop
const rules = {username:[// 书写验证规则{required: true,message: '用户名不能为空',// 失去焦点的时候触发trigger: 'blur'},{min:4,max:8,message: '用户名长度必须是4-8个字符',trigger: 'blur'},],password:[{required: true,message: '密码不能为空',// 失去焦点的时候触发trigger: 'blur'},]
}// setup里拿到el-form节点
const formRef = ref(null)const onSubmit = () => {formRef.value.validate((valid)=>{// 会输出true或者false// console.log(valid)if(!valid){return false}console.log(验证通过)})
}
</script><style scoped>
.login-container{/*el-row会将页面分为24份,用span进行值的设定 这里面的class是windi css里面的颜色快捷方式,以及设置min-height使其占满整个屏幕 */@apply min-h-screen bg-indigo-500;
}/* <!-- 左边布局 class里面实现的主要是垂直水平居中 flex-col就是垂直方向--><!-- 因为要响应式布局,所以我们使用windi css里面类似媒体查询的一个功能 lg是>1200px的屏幕,md是>992px的屏幕--> */
.login-container .left, .login-container .right{@apply flex items-center justify-center;
}.login-container .right{@apply bg-light-50 flex-col;
}/* class里面的内容分别是:加粗,字体增大,字体加亮,与下面外间距1rem */
.left>div>div:first-child{@apply font-bold text-5xl text-light-50 mb-4;
}.left>div>div:last-child{@apply text-gray-200 text-sm;
}
/* 加粗,字体3xl,颜色深灰色 */
.right .title{@apply font-bold text-3xl text-gray-800;
}/* flex布局,水平垂直居中,上下边距,浅灰色,水平方向元素的间距 */
.right>div{@apply flex items-center justify-center my-5 text-gray-300 space-x-2;
}/* 高度,宽度,浅灰色 */
.right .line{@apply h-[1px] w-16 bg-gray-200;
}
</style>

目前达成的效果:

接着我们引入axios请求库和登录接口交互:
项目api接口文档
首先在src目录下创建一个axios.js,然后写上以下内容

import axios from  "axios"const service = axios.create({baseURL:"/api"
})export default service

编辑vite.config.js,处理一下跨域问题

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import WindiCSS from 'vite-plugin-windicss'import path from "path"// https://vitejs.dev/config/
export default defineConfig({resolve:{alias:{"~":path.resolve(__dirname,"src")}},server:{proxy:{//api代替了这个接口域名'/api':{target: 'http://ceshi13.dishait.cn',changeOrigin: true,rewrite: (path) => path.replace(/^\/api/,'')},}},plugins: [vue(),WindiCSS()]
})

在src目录下创建一个api目录,api目录里面创建manager.js,写入login的接口

import axios from '~/axios'
// 登录
export function Login(username,password) {return axios.post("/admin/login",{username,password})
}

然后在login.vue里面处理一下登录提示信息:

<template><el-row class="login-container"><el-col :lg="16" :md="12" class="left"><div><div>欢迎光临</div><div>此站是vue-admin的登录页面,作者是mzldustu</div></div></el-col> <!-- 右边布局 -->  <el-col :lg="8" :md="12" class="right"><h2 class="title">欢迎回来</h2><div><span class="line"></span><span>账号密码登录</span><span class="line"></span></div><!-- 这里使用的是element plus里面的典型表单 --><!-- 表单宽度 --><!-- 引入element里的rules ,定义一个ref--><el-form ref="formRef" :rules="rules" :model="form" class="w-[250px]"><!-- 用户名输入框 --><el-form-item prop="username"><el-input v-model="form.username" placeholder="请输入用户名"><!-- 插槽引入user图标 --><template #prefix><el-icon><User /></el-icon></template></el-input></el-form-item><!-- 密码输入框 --><el-form-item prop="password"><!-- type设为password就是非明文存储,show-password就是后面的小眼睛,可以点击显示不显示密码 --><el-input type="password" v-model="form.password" placeholder="请输入密码" show-password><!-- 插槽引入lock图标 --><template #prefix> <el-icon><Lock /></el-icon></template></el-input></el-form-item><!-- 按钮 --><el-form-item><!-- 圆角,宽度250px,颜色 --><el-button round color="#626aef" class="w-[250px]" type="primary" @click="onSubmit">登录</el-button></el-form-item></el-form></el-col></el-row>
</template><script setup>
import { reactive, ref } from 'vue'
// 引入图标
import { User,Lock } from '@element-plus/icons-vue'
// 引入登录方法
import { Login } from '~/api/manager'
// 引入通知
import { ElNotification } from 'element-plus'
// 引入useRouter方便跳转
import { useRouter } from 'vue-router'// 拿到useRouter,才方便后面调用它的push方法
const router = useRouter()// do not use same name with ref
const form = reactive({username:"",password:""
})// 表单验证rules,要在前面指定prop
const rules = {username:[// 书写验证规则{required: true,message: '用户名不能为空',// 失去焦点的时候触发trigger: 'blur'},{min:4,max:8,message: '用户名长度必须是4-8个字符',trigger: 'blur'},],password:[{required: true,message: '密码不能为空',// 失去焦点的时候触发trigger: 'blur'},]
}// setup里拿到el-form节点
const formRef = ref(null)const onSubmit = () => {formRef.value.validate((valid)=>{// 会输出true或者false// console.log(valid)if(!valid){return false}// console.log(验证通过)// 参数验证通过后调用login方法// 通过前面的form拿到username和passwordLogin(form.username,form.password).then(res=>{// 拿到响应成功结果console.log(res.data.data);// 提示成功,存储用户token和用户相关信息,然后跳转到后台首页ElNotification({message: "登录成功",type: 'success',// 三秒后关闭duration:3000})// 跳转到后台首页router.push("/")}).catch(err=>{// 拿到报错具体信息ElNotification({message: err.response.data.msg || "请求失败",type: 'error',// 三秒后关闭duration:3000})})})
}
</script><style scoped>
.login-container{/*el-row会将页面分为24份,用span进行值的设定 这里面的class是windi css里面的颜色快捷方式,以及设置min-height使其占满整个屏幕 */@apply min-h-screen bg-indigo-500;
}/* <!-- 左边布局 class里面实现的主要是垂直水平居中 flex-col就是垂直方向--><!-- 因为要响应式布局,所以我们使用windi css里面类似媒体查询的一个功能 lg是>1200px的屏幕,md是>992px的屏幕--> */
.login-container .left, .login-container .right{@apply flex items-center justify-center;
}.login-container .right{@apply bg-light-50 flex-col;
}/* class里面的内容分别是:加粗,字体增大,字体加亮,与下面外间距1rem */
.left>div>div:first-child{@apply font-bold text-5xl text-light-50 mb-4;
}.left>div>div:last-child{@apply text-gray-200 text-sm;
}
/* 加粗,字体3xl,颜色深灰色 */
.right .title{@apply font-bold text-3xl text-gray-800;
}/* flex布局,水平垂直居中,上下边距,浅灰色,水平方向元素的间距 */
.right>div{@apply flex items-center justify-center my-5 text-gray-300 space-x-2;
}/* 高度,宽度,浅灰色 */
.right .line{@apply h-[1px] w-16 bg-gray-200;
}
</style>

其中注释写的很详细,就不细说了,看注释就能明白
用户名或者密码错误时,都会弹框提示

登录成功时会提示成功并且跳转

快12点了,今天先到这,明天接着肝!

第二天了,继续肝xdm
引入cookie存储用户的token
只有用户有token才能被认定为登陆状态,我们这里使用cookie存储token
我们先来了解一个vueuse工具库,把一些不支持响应式的api转换成响应式,大大提高开发效率
vueuse工具库文档地址
我们使用其中的useCookies
先安装

npm i universal-cookie
npm i @vueuse/integrations

然后在login.vue的登录成功后面加上了cookie

<template><el-row class="login-container"><el-col :lg="16" :md="12" class="left"><div><div>欢迎光临</div><div>此站是vue-admin的登录页面,作者是mzldustu</div></div></el-col> <!-- 右边布局 -->  <el-col :lg="8" :md="12" class="right"><h2 class="title">欢迎回来</h2><div><span class="line"></span><span>账号密码登录</span><span class="line"></span></div><!-- 这里使用的是element plus里面的典型表单 --><!-- 表单宽度 --><!-- 引入element里的rules ,定义一个ref--><el-form ref="formRef" :rules="rules" :model="form" class="w-[250px]"><!-- 用户名输入框 --><el-form-item prop="username"><el-input v-model="form.username" placeholder="请输入用户名"><!-- 插槽引入user图标 --><template #prefix><el-icon><User /></el-icon></template></el-input></el-form-item><!-- 密码输入框 --><el-form-item prop="password"><!-- type设为password就是非明文存储,show-password就是后面的小眼睛,可以点击显示不显示密码 --><el-input type="password" v-model="form.password" placeholder="请输入密码" show-password><!-- 插槽引入lock图标 --><template #prefix> <el-icon><Lock /></el-icon></template></el-input></el-form-item><!-- 按钮 --><el-form-item><!-- 圆角,宽度250px,颜色 --><el-button round color="#626aef" class="w-[250px]" type="primary" @click="onSubmit">登录</el-button></el-form-item></el-form></el-col></el-row>
</template><script setup>
import { reactive, ref } from 'vue'
// 引入图标
import { User,Lock } from '@element-plus/icons-vue'
// 引入登录方法
import { Login } from '~/api/manager'
// 引入通知
import { ElNotification } from 'element-plus'
// 引入useRouter方便跳转
import { useRouter } from 'vue-router'
// 引入usevue里面的useCookie方法
import { useCookies } from '@vueuse/integrations/useCookies'// 拿到useRouter,才方便后面调用它的push方法
const router = useRouter()// do not use same name with ref
const form = reactive({username:"",password:""
})// 表单验证rules,要在前面指定prop
const rules = {username:[// 书写验证规则{required: true,message: '用户名不能为空',// 失去焦点的时候触发trigger: 'blur'},{min:4,max:8,message: '用户名长度必须是4-8个字符',trigger: 'blur'},],password:[{required: true,message: '密码不能为空',// 失去焦点的时候触发trigger: 'blur'},]
}// setup里拿到el-form节点
const formRef = ref(null)const onSubmit = () => {formRef.value.validate((valid)=>{// 会输出true或者false// console.log(valid)if(!valid){return false}// console.log(验证通过)// 参数验证通过后调用login方法// 通过前面的form拿到username和passwordLogin(form.username,form.password).then(res=>{// 拿到响应成功结果console.log(res.data.data);// 提示成功,存储用户token和用户相关信息,然后跳转到后台首页ElNotification({message: "登录成功",type: 'success',// 三秒后关闭duration:3000})// 登录成功存储用户cookie,用cookie变量接收const cookie = useCookies()cookie.set("admin-token", res.data.data.token)// 跳转到后台首页router.push("/")}).catch(err=>{// 拿到报错具体信息ElNotification({message: err.response.data.msg || "请求失败",type: 'error',// 三秒后关闭duration:3000})})})
}
</script><style scoped>
.login-container{/*el-row会将页面分为24份,用span进行值的设定 这里面的class是windi css里面的颜色快捷方式,以及设置min-height使其占满整个屏幕 */@apply min-h-screen bg-indigo-500;
}/* <!-- 左边布局 class里面实现的主要是垂直水平居中 flex-col就是垂直方向--><!-- 因为要响应式布局,所以我们使用windi css里面类似媒体查询的一个功能 lg是>1200px的屏幕,md是>992px的屏幕--> */
.login-container .left, .login-container .right{@apply flex items-center justify-center;
}.login-container .right{@apply bg-light-50 flex-col;
}/* class里面的内容分别是:加粗,字体增大,字体加亮,与下面外间距1rem */
.left>div>div:first-child{@apply font-bold text-5xl text-light-50 mb-4;
}.left>div>div:last-child{@apply text-gray-200 text-sm;
}
/* 加粗,字体3xl,颜色深灰色 */
.right .title{@apply font-bold text-3xl text-gray-800;
}/* flex布局,水平垂直居中,上下边距,浅灰色,水平方向元素的间距 */
.right>div{@apply flex items-center justify-center my-5 text-gray-300 space-x-2;
}/* 高度,宽度,浅灰色 */
.right .line{@apply h-[1px] w-16 bg-gray-200;
}
</style>

打开控制台的application选项,查看,现在是没有cookie的,如果你有,就delete一下,然后

然后点击登录之后,跳转到首页,然后你再看的时候,就会发现已经有一个cookie了,这时候就代表cookie设置成功了

我们来继续写请求拦截器和响应拦截器:
在axios官网找到拦截器
axios拦截器
响应拦截器,这里使用响应拦截器处理响应成功和响应失败
改了这里:

然后这些内容都在axios.js里统一处理了,目前axios.js文件内容:

import axios from  "axios"
// 引入通知组件
import { ElNotification } from 'element-plus'const service = axios.create({baseURL:"/api"
})// 添加请求拦截器
service.interceptors.request.use(function (config) {// 在发送请求之前做些什么return config;}, function (error) {// 对请求错误做些什么return Promise.reject(error);});// 添加响应拦截器
service.interceptors.response.use(function (response) {// 2xx 范围内的状态码都会触发该函数。// 对响应数据做点什么// 这里之后login.vue里面就不需要res.data.data了,直接res就行return response.data.data;}, function (error) {// 超出 2xx 范围的状态码都会触发该函数。// 对响应错误做点什么// 错误处理ElNotification({message: error.response.data.msg || "请求失败",type: 'error',// 三秒后关闭duration:3000})return Promise.reject(error);});export default service

然后我们发现功能仍然能正常使用。
响应拦截器应用就结束了。
继续来写请求拦截器:

这里是直接在axios.js里面写了请求拦截器,目的是在header添加token

此时axios.js的文件:

import axios from  "axios"
// 引入通知组件
import { ElNotification } from 'element-plus'
// 引入usevue里面的useCookie方法
import { useCookies } from '@vueuse/integrations/useCookies'const service = axios.create({baseURL:"/api"
})// 添加请求拦截器
service.interceptors.request.use(function (config) {// 在发送请求之前做些什么// 往header头自动添加tokenconst cookie = useCookies()const token = cookie.get("admin-token")if(token){// 往请求头传tokenconfig.headers["token"] = token}return config;}, function (error) {// 对请求错误做些什么return Promise.reject(error);});// 添加响应拦截器
service.interceptors.response.use(function (response) {// 2xx 范围内的状态码都会触发该函数。// 对响应数据做点什么// 这里之后login.vue里面就不需要res.data.data了,直接res就行return response.data.data;}, function (error) {// 超出 2xx 范围的状态码都会触发该函数。// 对响应错误做点什么// 错误处理ElNotification({message: error.response.data.msg || "请求失败",type: 'error',// 三秒后关闭duration:3000})return Promise.reject(error);});export default service

这样他就会自动在header里面添加token

然后我们来写获取登录用户信息的功能:
先在manager.js里面写这个接口方法:

import axios from '~/axios'
// 登录
export function login(username,password) {return axios.post("/admin/login",{username,password})
}// 获取登录用户信息
// 因为前面的请求拦截器已经自动添加了token,所以这里不用刻意传值了
export function getinfo(){return axios.post("/admin/getinfo")
}

然后在login.vue里面引入getinfo方法并使用


目前的login.vue代码:

<template><el-row class="login-container"><el-col :lg="16" :md="12" class="left"><div><div>欢迎光临</div><div>此站是vue-admin的登录页面,作者是mzldustu</div></div></el-col> <!-- 右边布局 -->  <el-col :lg="8" :md="12" class="right"><h2 class="title">欢迎回来</h2><div><span class="line"></span><span>账号密码登录</span><span class="line"></span></div><!-- 这里使用的是element plus里面的典型表单 --><!-- 表单宽度 --><!-- 引入element里的rules ,定义一个ref--><el-form ref="formRef" :rules="rules" :model="form" class="w-[250px]"><!-- 用户名输入框 --><el-form-item prop="username"><el-input v-model="form.username" placeholder="请输入用户名"><!-- 插槽引入user图标 --><template #prefix><el-icon><User /></el-icon></template></el-input></el-form-item><!-- 密码输入框 --><el-form-item prop="password"><!-- type设为password就是非明文存储,show-password就是后面的小眼睛,可以点击显示不显示密码 --><el-input type="password" v-model="form.password" placeholder="请输入密码" show-password><!-- 插槽引入lock图标 --><template #prefix> <el-icon><Lock /></el-icon></template></el-input></el-form-item><!-- 按钮 --><el-form-item><!-- 圆角,宽度250px,颜色 --><el-button round color="#626aef" class="w-[250px]" type="primary" @click="onSubmit">登录</el-button></el-form-item></el-form></el-col></el-row>
</template><script setup>
import { reactive, ref } from 'vue'
// 引入图标
import { User,Lock } from '@element-plus/icons-vue'
// 引入登录方法
import { login, getinfo } from '~/api/manager'
// 引入通知组件
import { ElNotification } from 'element-plus'
// 引入useRouter方便跳转
import { useRouter } from 'vue-router'
// 引入usevue里面的useCookie方法
import { useCookies } from '@vueuse/integrations/useCookies'// 拿到useRouter,才方便后面调用它的push方法
const router = useRouter()// do not use same name with ref
const form = reactive({username:"",password:""
})// 表单验证rules,要在前面指定prop
const rules = {username:[// 书写验证规则{required: true,message: '用户名不能为空',// 失去焦点的时候触发trigger: 'blur'},{min:4,max:8,message: '用户名长度必须是4-8个字符',trigger: 'blur'},],password:[{required: true,message: '密码不能为空',// 失去焦点的时候触发trigger: 'blur'},]
}// setup里拿到el-form节点
const formRef = ref(null)const onSubmit = () => {formRef.value.validate((valid)=>{// 会输出true或者false// console.log(valid)if(!valid){return false}// console.log(验证通过)// 参数验证通过后调用login方法// 通过前面的form拿到username和passwordlogin(form.username,form.password).then(res=>{// 拿到响应成功结果// console.log(res.data.data);// 因为前面axios.js里面使用了响应拦截器,所以不需要写这么长了,直接res即可console.log(res)// 提示成功,存储用户token和用户相关信息,然后跳转到后台首页ElNotification({message: "登录成功",type: 'success',// 三秒后关闭duration:3000})// 登录成功存储用户cookie,用cookie变量接收const cookie = useCookies()// 原:cookie.set("admin-token", res.data.data.token)// 因为前面的响应拦截器设置,这里也直接res.token就行cookie.set("admin-token", res.token)// 存储完成后获取用户信息getinfo().then(res2=>{console.log(res2)})// 跳转到后台首页router.push("/")})// 错误处理移动到了axios.js// .catch(err=>{// 拿到报错具体信息// ElNotification({//   message: err.response.data.msg || "请求失败",//   type: 'error',//   // 三秒后关闭//   duration:3000// })// })})
}
</script><style scoped>
.login-container{/*el-row会将页面分为24份,用span进行值的设定 这里面的class是windi css里面的颜色快捷方式,以及设置min-height使其占满整个屏幕 */@apply min-h-screen bg-indigo-500;
}/* <!-- 左边布局 class里面实现的主要是垂直水平居中 flex-col就是垂直方向--><!-- 因为要响应式布局,所以我们使用windi css里面类似媒体查询的一个功能 lg是>1200px的屏幕,md是>992px的屏幕--> */
.login-container .left, .login-container .right{@apply flex items-center justify-center;
}.login-container .right{@apply bg-light-50 flex-col;
}/* class里面的内容分别是:加粗,字体增大,字体加亮,与下面外间距1rem */
.left>div>div:first-child{@apply font-bold text-5xl text-light-50 mb-4;
}.left>div>div:last-child{@apply text-gray-200 text-sm;
}
/* 加粗,字体3xl,颜色深灰色 */
.right .title{@apply font-bold text-3xl text-gray-800;
}/* flex布局,水平垂直居中,上下边距,浅灰色,水平方向元素的间距 */
.right>div{@apply flex items-center justify-center my-5 text-gray-300 space-x-2;
}/* 高度,宽度,浅灰色 */
.right .line{@apply h-[1px] w-16 bg-gray-200;
}
</style>

然后我们再来做一个小功能,用户点击登录之后如果响应时间较长,那么在登录按钮加上一个点击之后loading的功能,防止用户重复点击
login.vue里面:




目前实现的效果:点击登录之后就有了一个加载按钮,此时用户也不能点击登录按钮了

至此,这个小功能也做完了。
此时的login.vue文件:

<template><el-row class="login-container"><el-col :lg="16" :md="12" class="left"><div><div>欢迎光临</div><div>此站是vue-admin的登录页面,作者是mzldustu</div></div></el-col> <!-- 右边布局 -->  <el-col :lg="8" :md="12" class="right"><h2 class="title">欢迎回来</h2><div><span class="line"></span><span>账号密码登录</span><span class="line"></span></div><!-- 这里使用的是element plus里面的典型表单 --><!-- 表单宽度 --><!-- 引入element里的rules ,定义一个ref--><el-form ref="formRef" :rules="rules" :model="form" class="w-[250px]"><!-- 用户名输入框 --><el-form-item prop="username"><el-input v-model="form.username" placeholder="请输入用户名"><!-- 插槽引入user图标 --><template #prefix><el-icon><User /></el-icon></template></el-input></el-form-item><!-- 密码输入框 --><el-form-item prop="password"><!-- type设为password就是非明文存储,show-password就是后面的小眼睛,可以点击显示不显示密码 --><el-input type="password" v-model="form.password" placeholder="请输入密码" show-password><!-- 插槽引入lock图标 --><template #prefix> <el-icon><Lock /></el-icon></template></el-input></el-form-item><!-- 按钮 --><el-form-item><!-- 圆角,宽度250px,颜色 --><!-- 加一个loading状态,正常状态下是false,改变之后就是true --><el-button round color="#626aef" class="w-[250px]" type="primary" @click="onSubmit" :loading="loading">登录</el-button></el-form-item></el-form></el-col></el-row>
</template><script setup>
import { reactive, ref } from 'vue'
// 引入图标
import { User,Lock } from '@element-plus/icons-vue'
// 引入登录方法
import { login, getinfo } from '~/api/manager'
// 引入通知组件
import { ElNotification } from 'element-plus'
// 引入useRouter方便跳转
import { useRouter } from 'vue-router'
// 引入usevue里面的useCookie方法
import { useCookies } from '@vueuse/integrations/useCookies'// 拿到useRouter,才方便后面调用它的push方法
const router = useRouter()// do not use same name with ref
const form = reactive({username:"",password:""
})// 表单验证rules,要在前面指定prop
const rules = {username:[// 书写验证规则{required: true,message: '用户名不能为空',// 失去焦点的时候触发trigger: 'blur'},{min:4,max:8,message: '用户名长度必须是4-8个字符',trigger: 'blur'},],password:[{required: true,message: '密码不能为空',// 失去焦点的时候触发trigger: 'blur'},]
}// setup里拿到el-form节点
const formRef = ref(null)
// 定义一个loading,默认让它为false,不显示
const loading = ref(false)const onSubmit = () => {formRef.value.validate((valid)=>{// 会输出true或者false// console.log(valid)if(!valid){return false}// 在请求之前将loading状态设为trueloading.value = true// console.log(验证通过)// 参数验证通过后调用login方法// 通过前面的form拿到username和passwordlogin(form.username,form.password).then(res=>{// 拿到响应成功结果// console.log(res.data.data);// 因为前面axios.js里面使用了响应拦截器,所以不需要写这么长了,直接res即可console.log(res)// 提示成功,存储用户token和用户相关信息,然后跳转到后台首页ElNotification({message: "登录成功",type: 'success',// 三秒后关闭duration:3000})// 登录成功存储用户cookie,用cookie变量接收const cookie = useCookies()// 原:cookie.set("admin-token", res.data.data.token)// 因为前面的响应拦截器设置,这里也直接res.token就行cookie.set("admin-token", res.token)// 存储完成后获取用户信息getinfo().then(res2=>{console.log(res2)})// 跳转到后台首页router.push("/")}).finally(()=>{// 登录成功之后loading设置为falseloading.value = false})// 错误处理移动到了axios.js// .catch(err=>{// 拿到报错具体信息// ElNotification({//   message: err.response.data.msg || "请求失败",//   type: 'error',//   // 三秒后关闭//   duration:3000// })// })})
}
</script><style scoped>
.login-container{/*el-row会将页面分为24份,用span进行值的设定 这里面的class是windi css里面的颜色快捷方式,以及设置min-height使其占满整个屏幕 */@apply min-h-screen bg-indigo-500;
}/* <!-- 左边布局 class里面实现的主要是垂直水平居中 flex-col就是垂直方向--><!-- 因为要响应式布局,所以我们使用windi css里面类似媒体查询的一个功能 lg是>1200px的屏幕,md是>992px的屏幕--> */
.login-container .left, .login-container .right{@apply flex items-center justify-center;
}.login-container .right{@apply bg-light-50 flex-col;
}/* class里面的内容分别是:加粗,字体增大,字体加亮,与下面外间距1rem */
.left>div>div:first-child{@apply font-bold text-5xl text-light-50 mb-4;
}.left>div>div:last-child{@apply text-gray-200 text-sm;
}
/* 加粗,字体3xl,颜色深灰色 */
.right .title{@apply font-bold text-3xl text-gray-800;
}/* flex布局,水平垂直居中,上下边距,浅灰色,水平方向元素的间距 */
.right>div{@apply flex items-center justify-center my-5 text-gray-300 space-x-2;
}/* 高度,宽度,浅灰色 */
.right .line{@apply h-[1px] w-16 bg-gray-200;
}
</style>

接着我们来实现常用工具类的封装(简化代码):
1.封装cookie
在src目录下面新建一个composables的目录,新建一个auth.js

// 引入usevue里面的useCookie方法
import { useCookies } from '@vueuse/integrations/useCookies'// 定义token统一的值
const Tokenkey = "admin-token"
const cookie = useCookies()// 获取token
export function getToken(){return cookie.get(Tokenkey)
}// 设置token
export function setToken(token){return cookie.set(Tokenkey,token)
}// 清除token
export function removeToken(){return cookie.remove(Tokenkey)
}

然后就去login.vue里面和axios.js里面用到了setToken和getToken的地方进行优化
目前login.vue代码:

<template><el-row class="login-container"><el-col :lg="16" :md="12" class="left"><div><div>欢迎光临</div><div>此站是vue-admin的登录页面,作者是mzldustu</div></div></el-col> <!-- 右边布局 -->  <el-col :lg="8" :md="12" class="right"><h2 class="title">欢迎回来</h2><div><span class="line"></span><span>账号密码登录</span><span class="line"></span></div><!-- 这里使用的是element plus里面的典型表单 --><!-- 表单宽度 --><!-- 引入element里的rules ,定义一个ref--><el-form ref="formRef" :rules="rules" :model="form" class="w-[250px]"><!-- 用户名输入框 --><el-form-item prop="username"><el-input v-model="form.username" placeholder="请输入用户名"><!-- 插槽引入user图标 --><template #prefix><el-icon><User /></el-icon></template></el-input></el-form-item><!-- 密码输入框 --><el-form-item prop="password"><!-- type设为password就是非明文存储,show-password就是后面的小眼睛,可以点击显示不显示密码 --><el-input type="password" v-model="form.password" placeholder="请输入密码" show-password><!-- 插槽引入lock图标 --><template #prefix> <el-icon><Lock /></el-icon></template></el-input></el-form-item><!-- 按钮 --><el-form-item><!-- 圆角,宽度250px,颜色 --><!-- 加一个loading状态,正常状态下是false,改变之后就是true --><el-button round color="#626aef" class="w-[250px]" type="primary" @click="onSubmit" :loading="loading">登录</el-button></el-form-item></el-form></el-col></el-row>
</template><script setup>
import { reactive, ref } from 'vue'
// 引入图标
import { User,Lock } from '@element-plus/icons-vue'
// 引入登录方法
import { login, getinfo } from '~/api/manager'
// 引入通知组件
import { ElNotification } from 'element-plus'
// 引入useRouter方便跳转
import { useRouter } from 'vue-router'
// 引入usevue里面的useCookie方法
// import { useCookies } from '@vueuse/integrations/useCookies'
// 引入auth.js里面的各种cookie方法,上面的那条引入就不需要了
import {setToken
} from '~/composables/auth'// 拿到useRouter,才方便后面调用它的push方法
const router = useRouter()// do not use same name with ref
const form = reactive({username:"",password:""
})// 表单验证rules,要在前面指定prop
const rules = {username:[// 书写验证规则{required: true,message: '用户名不能为空',// 失去焦点的时候触发trigger: 'blur'},{min:4,max:8,message: '用户名长度必须是4-8个字符',trigger: 'blur'},],password:[{required: true,message: '密码不能为空',// 失去焦点的时候触发trigger: 'blur'},]
}// setup里拿到el-form节点
const formRef = ref(null)
// 定义一个loading,默认让它为false,不显示
const loading = ref(false)const onSubmit = () => {formRef.value.validate((valid)=>{// 会输出true或者false// console.log(valid)if(!valid){return false}// 在请求之前将loading状态设为trueloading.value = true// console.log(验证通过)// 参数验证通过后调用login方法// 通过前面的form拿到username和passwordlogin(form.username,form.password).then(res=>{// 拿到响应成功结果// console.log(res.data.data);// 因为前面axios.js里面使用了响应拦截器,所以不需要写这么长了,直接res即可console.log(res)// 提示成功,存储用户token和用户相关信息,然后跳转到后台首页ElNotification({message: "登录成功",type: 'success',// 三秒后关闭duration:3000})// 登录成功存储用户cookie,用cookie变量接收// const cookie = useCookies()// 原:cookie.set("admin-token", res.data.data.token)// 因为前面的响应拦截器设置,这里也直接res.token就行// cookie.set("admin-token", res.token)// 这里经过auth.js里面的方法进行再次简化setToken(res.token)// 存储完成后获取用户信息getinfo().then(res2=>{console.log(res2)})// 跳转到后台首页router.push("/")}).finally(()=>{// 登录成功之后loading设置为falseloading.value = false})// 错误处理移动到了axios.js// .catch(err=>{// 拿到报错具体信息// ElNotification({//   message: err.response.data.msg || "请求失败",//   type: 'error',//   // 三秒后关闭//   duration:3000// })// })})
}
</script><style scoped>
.login-container{/*el-row会将页面分为24份,用span进行值的设定 这里面的class是windi css里面的颜色快捷方式,以及设置min-height使其占满整个屏幕 */@apply min-h-screen bg-indigo-500;
}/* <!-- 左边布局 class里面实现的主要是垂直水平居中 flex-col就是垂直方向--><!-- 因为要响应式布局,所以我们使用windi css里面类似媒体查询的一个功能 lg是>1200px的屏幕,md是>992px的屏幕--> */
.login-container .left, .login-container .right{@apply flex items-center justify-center;
}.login-container .right{@apply bg-light-50 flex-col;
}/* class里面的内容分别是:加粗,字体增大,字体加亮,与下面外间距1rem */
.left>div>div:first-child{@apply font-bold text-5xl text-light-50 mb-4;
}.left>div>div:last-child{@apply text-gray-200 text-sm;
}
/* 加粗,字体3xl,颜色深灰色 */
.right .title{@apply font-bold text-3xl text-gray-800;
}/* flex布局,水平垂直居中,上下边距,浅灰色,水平方向元素的间距 */
.right>div{@apply flex items-center justify-center my-5 text-gray-300 space-x-2;
}/* 高度,宽度,浅灰色 */
.right .line{@apply h-[1px] w-16 bg-gray-200;
}
</style>

axios.js现在的代码:

import axios from  "axios"
// 引入通知组件
import { ElNotification } from 'element-plus'
// 引入usevue里面的useCookie方法
// import { useCookies } from '@vueuse/integrations/useCookies'
// 这里引入auth里面的getToken方法,所以上面那条引入也不需要了
import { getToken } from '~/composables/auth'const service = axios.create({baseURL:"/api"
})// 添加请求拦截器
service.interceptors.request.use(function (config) {// 在发送请求之前做些什么// 往header头自动添加token// 因为上面import了getToken方法,所以下面两行不需要了,直接getToken()即可// const cookie = useCookies()// const token = cookie.get("admin-token")const token = getToken()if(token){// 往请求头传tokenconfig.headers["token"] = token}return config;}, function (error) {// 对请求错误做些什么return Promise.reject(error);});// 添加响应拦截器
service.interceptors.response.use(function (response) {// 2xx 范围内的状态码都会触发该函数。// 对响应数据做点什么// 这里之后login.vue里面就不需要res.data.data了,直接res就行return response.data.data;}, function (error) {// 超出 2xx 范围的状态码都会触发该函数。// 对响应错误做点什么// 错误处理ElNotification({message: error.response.data.msg || "请求失败",type: 'error',// 三秒后关闭duration:3000})return Promise.reject(error);});export default service

2.然后我们来封装提示:
在上面的composables文件夹再创建一个util.js

// 引入通知组件
import { ElNotification } from 'element-plus'// 消息提示统一方法
// dangerouslyUseHTMLString表示是否将 message 属性作为 HTML 片段渲染出来
export function toast(message, type = "success", dangerouslyUseHTMLString = false){ElNotification({message,type,// 三秒后关闭duration:3000,dangerouslyUseHTMLString})
}

axios.js里面


login.vue里面


至此我们的cookie封装和消息功能封装就完成了。
此时axios.js的代码:

import axios from  "axios"
// 引入通知组件
// import { ElNotification } from 'element-plus'
// 因为util.js里面已经写了toast方法,所以就不用上面的引入了
import { toast } from '~/composables/util'
// 引入usevue里面的useCookie方法
// import { useCookies } from '@vueuse/integrations/useCookies'
// 这里引入auth里面的getToken方法,所以上面那条引入也不需要了
import { getToken } from '~/composables/auth'const service = axios.create({baseURL:"/api"
})// 添加请求拦截器
service.interceptors.request.use(function (config) {// 在发送请求之前做些什么// 往header头自动添加token// 因为上面import了getToken方法,所以下面两行不需要了,直接getToken()即可// const cookie = useCookies()// const token = cookie.get("admin-token")const token = getToken()if(token){// 往请求头传tokenconfig.headers["token"] = token}return config;}, function (error) {// 对请求错误做些什么return Promise.reject(error);});// 添加响应拦截器
service.interceptors.response.use(function (response) {// 2xx 范围内的状态码都会触发该函数。// 对响应数据做点什么// 这里之后login.vue里面就不需要res.data.data了,直接res就行return response.data.data;}, function (error) {// 超出 2xx 范围的状态码都会触发该函数。// 对响应错误做点什么// 错误处理// 因为util的toast实现了消息的封装,所以直接像下面这样写就行toast(error.response.data.msg || "请求失败", "error")// ElNotification({//     message: error.response.data.msg || "请求失败",//     type: 'error',//     // 三秒后关闭//     duration:3000// })return Promise.reject(error);});export default service

login.vue的代码:

<template><el-row class="login-container"><el-col :lg="16" :md="12" class="left"><div><div>欢迎光临</div><div>此站是vue-admin的登录页面,作者是mzldustu</div></div></el-col> <!-- 右边布局 -->  <el-col :lg="8" :md="12" class="right"><h2 class="title">欢迎回来</h2><div><span class="line"></span><span>账号密码登录</span><span class="line"></span></div><!-- 这里使用的是element plus里面的典型表单 --><!-- 表单宽度 --><!-- 引入element里的rules ,定义一个ref--><el-form ref="formRef" :rules="rules" :model="form" class="w-[250px]"><!-- 用户名输入框 --><el-form-item prop="username"><el-input v-model="form.username" placeholder="请输入用户名"><!-- 插槽引入user图标 --><template #prefix><el-icon><User /></el-icon></template></el-input></el-form-item><!-- 密码输入框 --><el-form-item prop="password"><!-- type设为password就是非明文存储,show-password就是后面的小眼睛,可以点击显示不显示密码 --><el-input type="password" v-model="form.password" placeholder="请输入密码" show-password><!-- 插槽引入lock图标 --><template #prefix> <el-icon><Lock /></el-icon></template></el-input></el-form-item><!-- 按钮 --><el-form-item><!-- 圆角,宽度250px,颜色 --><!-- 加一个loading状态,正常状态下是false,改变之后就是true --><el-button round color="#626aef" class="w-[250px]" type="primary" @click="onSubmit" :loading="loading">登录</el-button></el-form-item></el-form></el-col></el-row>
</template><script setup>
import { reactive, ref } from 'vue'
// 引入图标
import { User,Lock } from '@element-plus/icons-vue'
// 引入登录方法
import { login, getinfo } from '~/api/manager'
// 引入通知组件
// import { ElNotification } from 'element-plus'
// 因为util里面写了toast方法,所以就不用上面的引用了
import { toast } from '~/composables/util'
// 引入useRouter方便跳转
import { useRouter } from 'vue-router'
// 引入usevue里面的useCookie方法
// import { useCookies } from '@vueuse/integrations/useCookies'
// 引入auth.js里面的各种cookie方法,上面的那条引入就不需要了
import {setToken
} from '~/composables/auth'// 拿到useRouter,才方便后面调用它的push方法
const router = useRouter()// do not use same name with ref
const form = reactive({username:"",password:""
})// 表单验证rules,要在前面指定prop
const rules = {username:[// 书写验证规则{required: true,message: '用户名不能为空',// 失去焦点的时候触发trigger: 'blur'},{min:4,max:8,message: '用户名长度必须是4-8个字符',trigger: 'blur'},],password:[{required: true,message: '密码不能为空',// 失去焦点的时候触发trigger: 'blur'},]
}// setup里拿到el-form节点
const formRef = ref(null)
// 定义一个loading,默认让它为false,不显示
const loading = ref(false)const onSubmit = () => {formRef.value.validate((valid)=>{// 会输出true或者false// console.log(valid)if(!valid){return false}// 在请求之前将loading状态设为trueloading.value = true// console.log(验证通过)// 参数验证通过后调用login方法// 通过前面的form拿到username和passwordlogin(form.username,form.password).then(res=>{// 拿到响应成功结果// console.log(res.data.data);// 因为前面axios.js里面使用了响应拦截器,所以不需要写这么长了,直接res即可console.log(res)// 提示成功,存储用户token和用户相关信息,然后跳转到后台首页// ElNotification({//   message: "登录成功",//   type: 'success',//   // 三秒后关闭//   duration:3000// })// 因为util里面实现了toast方法,所以就不用再写相关代码了,直接登陆成功即可toast("登录成功")// 登录成功存储用户cookie,用cookie变量接收// const cookie = useCookies()// 原:cookie.set("admin-token", res.data.data.token)// 因为前面的响应拦截器设置,这里也直接res.token就行// cookie.set("admin-token", res.token)// 这里经过auth.js里面的方法进行再次简化setToken(res.token)// 存储完成后获取用户信息getinfo().then(res2=>{console.log(res2)})// 跳转到后台首页router.push("/")}).finally(()=>{// 登录成功之后loading设置为falseloading.value = false})// 错误处理移动到了axios.js// .catch(err=>{// 拿到报错具体信息// ElNotification({//   message: err.response.data.msg || "请求失败",//   type: 'error',//   // 三秒后关闭//   duration:3000// })// })})
}
</script><style scoped>
.login-container{/*el-row会将页面分为24份,用span进行值的设定 这里面的class是windi css里面的颜色快捷方式,以及设置min-height使其占满整个屏幕 */@apply min-h-screen bg-indigo-500;
}/* <!-- 左边布局 class里面实现的主要是垂直水平居中 flex-col就是垂直方向--><!-- 因为要响应式布局,所以我们使用windi css里面类似媒体查询的一个功能 lg是>1200px的屏幕,md是>992px的屏幕--> */
.login-container .left, .login-container .right{@apply flex items-center justify-center;
}.login-container .right{@apply bg-light-50 flex-col;
}/* class里面的内容分别是:加粗,字体增大,字体加亮,与下面外间距1rem */
.left>div>div:first-child{@apply font-bold text-5xl text-light-50 mb-4;
}.left>div>div:last-child{@apply text-gray-200 text-sm;
}
/* 加粗,字体3xl,颜色深灰色 */
.right .title{@apply font-bold text-3xl text-gray-800;
}/* flex布局,水平垂直居中,上下边距,浅灰色,水平方向元素的间距 */
.right>div{@apply flex items-center justify-center my-5 text-gray-300 space-x-2;
}/* 高度,宽度,浅灰色 */
.right .line{@apply h-[1px] w-16 bg-gray-200;
}
</style>

而运行项目我们发现我们的功能仍然是没问题的,至此,封装就写完了。

然后我们来引入vuex状态管理用户信息
我们使用的是vuex4
先安装:

npm install vuex@next --save

然后在src文件夹下面创建store文件夹,创建index.js文件编辑代码:

import { createStore } from 'vuex'// 创建一个新的 store 实例
// 管理用户相关信息
const store = createStore({state () {return {// 用户信息存储在stateuser:{}}},// 修改用户信息mutations: {// 记录用户信息SET_USERINFO(state,user){// 通过state.user拿到上面的空对象state.user = user}}})export default store

然后在main.js里面引入并app.use()

import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import router from './router'
// 引入store
import store from './store'
//引入注册所有图标
import * as ElementPlusIconsVue from '@element-plus/icons-vue'const app = createApp(App)app.use(store)app.use(router)app.use(ElementPlus)
// 引入图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {app.component(key, component)
}import 'virtual:windi.css'app.mount('#app')

然后在login.vue里面引入并获取

然后将登录成功信息res2传给state

然后为了验证,我们打开index.vue

加上一行{{ $store.state.user }}
然后运行项目,首先首页,你发现大括号里什么都没有

然后再去登录,跳转之后你会发现数据全部拿到了,说明我们vuex存储获取用户数据成功了。

至此,vuex状态管理用户信息就完成了。

接着我们来实现全局路由守卫实现拦截实现登陆验证:
路由守卫实现的功能就是如果你没有登录,就无法进入某个页面,比如这里没有登录,就不能进入主页index.vue

我们在src下面创建permission.js处理权限验证相关的东西
main.js里面导入一下import "./permission"

permission.js:

import router from "~/router"
import { getToken } from "~/composables/auth"
import { toast } from "~/composables/util"// 全局前置守卫
// to是即将到达的页面路径,from表示从哪个路径来
router.beforeEach((to, from, next)=>{// console.log("全局前置守卫");// 拿一下tokenconst token = getToken()// 如果没有token而且不是在登录页,那么强制跳转回登录页if(!token && to.path != "/login" ){toast("请先登录","error")return next({ path:"/login" })}// 防止重复登录的判断if(token && to.path == "/login"){toast("请勿重复登录","error")return next({ path:from.path ? from.path : "/" })}next()
})

在这之后,如果没有登录会强制你到登录页登录,如果已经登录,那么你重复登陆它会提醒你不要重复登陆。


接着我们继续完善登录功能:
1.解决首页获取到用户信息之后刷新信息消失的问题
在store里面写action,

login.vue里面这一块就迁移了

在permisson.js里面:

import router from "~/router"
import { getToken } from "~/composables/auth"
import { toast } from "~/composables/util"
import store from "./store"// 全局前置守卫
// to是即将到达的页面路径,from表示从哪个路径来
router.beforeEach(async (to, from, next)=>{// console.log("全局前置守卫");// 拿一下tokenconst token = getToken()// 如果没有token而且不是在登录页,那么强制跳转回登录页if(!token && to.path != "/login" ){toast("请先登录","error")return next({ path:"/login" })}// 防止重复登录的判断if(token && to.path == "/login"){toast("请勿重复登录","error")return next({ path:from.path ? from.path : "/" })}// 如果用户登录了就自动获取用户登录信息,并存储在vuex里,这样它就会自动异步帮我们获取用户信息,刷新也没事if(token){await store.dispatch("getinfo")}next()
})

搞了一个async await的异步操作,然后它会自动异步帮你获取用户的信息。
然后刷新主页用户信息就不会被清空了,这里就解决了刷新用户信息会丢失的问题。

2.对登录相关方法进行抽离
action里面加一个login进行抽离

然后login.vue里面

这样就完成了抽离

3.接着我们实现按回车就登录的功能
在login.vue里面引入两个生命周期

然后直接写

这时候就实现了回车登录事件。
至此,登录功能完善完成。

然后我们来实现退出登录功能:
使用的是element里面的message box消息弹出框里面的确认消息,因为会在多处使用,所以我们将它封装成一个公共的方法。
首先manager.js编辑写一个logout方法:

import axios from '~/axios'
// 登录
export function login(username,password) {return axios.post("/admin/login",{username,password})
}// 获取登录用户信息
// 因为前面的请求拦截器已经自动添加了token,所以这里不用刻意传值了
export function getinfo(){return axios.post("/admin/getinfo")
}// 退出登录
// 请求栏已经有了,所以不用传任何参数
export function logout(){return axios.post("/admin/logout")
}

然后在index.vue里面写一个handleLogout方法

<template><div>后台首页{{ $store.state.user.username }}<el-button @click="handleLogout">退出登录</el-button></div>
</template>
<!-- 响应式api ,ref,一个变量响应式,普通类型,script里面count.value,template里面直接{{count}}-->
<!-- 响应式api , reactive,用于对象 script里面form.count++,template里面直接{{form.count}}--><script setup>
// 退出接口
import { logout } from "~/api/manager"
// 提示框
import { showModal, toast } from "~/composables/util"
// 方便页面跳转
import { useRouter } from "vue-router"
// 引入useStore
import { useStore } from 'vuex'const router = useRouter()const store = useStore()function handleLogout(){showModal("是否要退出登录?").then(res=>{console.log("退出登录");logout().finally(()=>{// 移出cookie里的token// 清除当前用户状态 vuex里的user// 上面的两步已经在store里面实现了store.dispatch("logout")// 跳转回登录页router.push("/login")// 提示退出登录成功toast("退出登录成功")})})}
</script>

在store里面实现user的清空,即清除token,以及清除state里面的user

目前store的index.js代码:

import { createStore } from 'vuex'
// 引入方法
import { login, getinfo } from '~/api/manager'
// 引入auth.js里面的各种cookie方法,上面的那条引入就不需要了
import { setToken, removeToken } from '~/composables/auth'
import { logout } from '../api/manager'// 创建一个新的 store 实例
// 管理用户相关信息
const store = createStore({state () {return {// 用户信息存储在stateuser:{}}},// 修改用户信息mutations: {// 记录用户信息SET_USERINFO(state,user){// 通过state.user拿到上面的空对象state.user = user}},actions: {// 抽离登录login({ commit }, { username,password }){return new Promise((resolve,reject)=>{login(username,password).then(res=>{setToken(res.token)// 如果成功,直接resolveresolve(res)}).catch(err=>reject(err))})},// 获取当前用户登录信息// 等同于store.commitgetinfo({ commit }){return new Promise((resolve,reject)=>{getinfo().then(res=>{commit("SET_USERINFO",res)// 成功调用resolveresolve(res)}).catch(err=>reject(err))})},// 退出登录// 解构user里的commitlogout({ commit }){// 移出cookie里的tokenremoveToken()// 清除当前用户状态 vuex,即state里的user清空commit("SET_USERINFO", {})}}})export default store

在这里我们再看看功能:

点击确认之后

到这里就完成了退出登录

然后我们进行全局loading进度条的实现:
我们这里要用到一个第三方库,nprogress
地址
安装

npm i nprogress

先在util.js里面写上关闭和开启nprogress方法

// 引入通知组件
import { ElNotification, ElMessageBox } from 'element-plus'
// 引入loading插件
import nProgress from 'nprogress'// 消息提示统一方法
// dangerouslyUseHTMLString表示是否将 message 属性作为 HTML 片段渲染出来
export function toast(message, type = "success", dangerouslyUseHTMLString = false){ElNotification({message,type,// 三秒后关闭duration:3000,dangerouslyUseHTMLString})
}// 消息提示框
export function showModal(content = "提示内容", type = "warning", title = ""){return ElMessageBox.confirm(content,title,{confirmButtonText: '确定',cancelButtonText: '取消',type,})
}// 显示全屏loading
export function showFullLoading(){nProgress.start()
}// 隐藏全屏loading
export function hideFullLoading(){nProgress.done()
}

默认进度条是浅蓝色,但是有点和界面颜色相同,所以在App.vue里面写样式,改成浅灰色

关闭就在permission.js里面,前置路由守卫引用,后置路由守卫退出。

import router from "~/router"
import { getToken } from "~/composables/auth"
import { toast, showFullLoading, hideFullLoading } from "~/composables/util"
import store from "./store"// 全局前置守卫,路由变化就会触发守卫
// to是即将到达的页面路径,from表示从哪个路径来
router.beforeEach(async (to, from, next)=>{// 显示loadingshowFullLoading()// console.log("全局前置守卫");// 拿一下tokenconst token = getToken()// 如果没有token而且不是在登录页,那么强制跳转回登录页if(!token && to.path != "/login" ){toast("请先登录","error")return next({ path:"/login" })}// 防止重复登录的判断if(token && to.path == "/login"){toast("请勿重复登录","error")return next({ path:from.path ? from.path : "/" })}// 如果用户登录了就自动获取用户登录信息,并存储在vuex里,这样它就会自动异步帮我们获取用户信息,刷新也没事if(token){await store.dispatch("getinfo")}next()
})// 全局后置守卫
// 调用关闭进度条方法
router.afterEach((to, from) => hideFullLoading())

这里没办法截图,因为太快了。自己试试吧。

接着写动态页面标题实现:
切换路由的时候会改变页面的title标题:
首先在router文件夹下的index.js的每个路由加上meta:{ title: }

然后在permisson.js里面的全局路由前置守卫进行处理

至此,我们的登录功能就做完了,还是挺全的。涉及到了表单验证,cookie存储,axios交互,请求拦截器和响应拦截器,工具库的封装,vuex的状态管理,全局路由守卫,退出登录,全局loading,动态页面标题。门道很多的,所以网上那些5分钟写完登陆注册功能什么成分不用说了吧,那都是小打小闹。

vue3-admin商品管理后台项目(登录页开发和功能实现)相关推荐

  1. Vue3管理后台项目使用高德地图选点

    前言 最近在做的管理后台项目中有一个业务场景是添加门店的地址和经纬度,地址可以输入,但经纬度这样处理却不合适,最好是让用户在地图上搜索或者直接点击获取点的经纬度等详细信息.因为我们的app中使用了高德 ...

  2. 【Vue 实战项目】后台管理系统登录页详解附源码

    提示:前端查漏补缺,仅代表个人观点. 文章目录 一.先看效果图 二.实战步骤 1. 创建项目 2. 引入库 3. 登录页关键代码 三.页面源代码 总结 提示:项目源代码除了登录页面还有动态路由 一.先 ...

  3. Django管理后台之登录

    Django提供了一套身份验证和授权的权限系统,允许验证用户凭证,并定义每个用户允许执行的操作. 权限系统框架包括了用户和分组的内置模型,用于登录用户的权限,指定用户是否可以执行任务.表单.视图,以及 ...

  4. 管理后台项目-04-SPU列表-增删改SPU-获取SKU【续】

    目录 1-删除spu 2-添加sku 2.1-获取skuForm页面组件的数据 2.2-收集form表单数据 2.3-保存提交数据 3-查看SKU信息和loading效果 上一篇文章管理后台项目-03 ...

  5. git 拉取项目CMS管理后台项目

    git 拉取项目CMS管理后台项目 github 项目地址:luwei.web.study-ant-design-pro 企业内部项目地址:study.ant-design-pro 注:github ...

  6. vue项目登录页-实现字体动画案例

    vue项目登录页-实现字体动画案例 实现思路: 1,让每个字都包含在span标签中,span标签的display:inner-block 2,页面刚生成时(动画之前)设置margin宽度为80px,o ...

  7. Vite + Vue3 + Antd + Typescript 管理后台前端简易框架

    Vite + Vue3 + Antd + Typescript 管理后台前端简易框架 这里是antd版本,如果你更倾向于使用element-plus,请点击这里. ✨ 最新版本 v1.0.5 新增环境 ...

  8. 最新ChatGPT网站源码V4.7.8+支持Ai绘画+ChatGPT商业运营版+管理后台+支持用户套餐+好友邀请功能+一键在线更新+永久使用!

    最新ChatGPT网站源码V4.7.8+支持Ai绘画+ChatGPT商业运营版+管理后台+支持用户套餐+好友邀请功能+一键在线更新+永久使用! 如果后续程序有新版,直接在后台一键更新即可 程序完美运行 ...

  9. 后台管理系统项目-登录页-实现步骤

    1.登录页面-样式调整 登录页整体思路如下: 表单验证(login/index.vue) utils/validate.js ---> validMobile 收集用户的参数,调用actions ...

最新文章

  1. 前端 重构时需要注意的事项_驾驶式扫地车的功能特点和使用时需要注意事项...
  2. 27岁的张一鸣教给我们工作上的那些事
  3. SharpDevelop源码分析笔记(一)
  4. php拍视频上传,php视频拍照上传头像功能实现代码分享
  5. 【Verilog语法】读文件
  6. java 获取键盘事件,java获取键盘事件
  7. PHP简单操作Excel
  8. C# 关于MVC框架的简单实例(计算器)
  9. -矩阵-创建矩阵-meshgrid函数
  10. 容器精华问答 | Docker是否比虚拟技术要好?
  11. 服务器进不去系统system,system是什么进程 system进程可以关闭吗
  12. matlab geodetic2ecef,卫星轨道问题
  13. 小米盒子3显示无网络连接服务器,小米盒子不显示无线网络连不上 - 卡饭网
  14. 小白入门之HTML--第四章 CSS样式深入
  15. 自己开发了一款视频播放器app
  16. 中国 省 市 自治区 资料
  17. JSON学习(一)——了解JSON
  18. 2018最牛java初级笔试面试题,offer拿到手软
  19. Easy EDA #学习笔记02# |Arduino UNO 单片机最小系统PCB原理图 (ATMEGA328P单片机 AMS1117芯片5V转3.3V )
  20. stata中计算公式命令_Stata数据处理:各种求和方式一览

热门文章

  1. 2021年江西省安全员C证考试题及江西省安全员C证免费试题
  2. python 做网站的工具_python能做网站
  3. 计算机一级摸拟题练习,2017计算机一级MSOffice模拟练习题附答案
  4. 友声电子秤设置软件_友声电子秤操作方法盘点
  5. activiti 7 生成流程图图片
  6. 二叉树前中后序遍历+刷题【中】【数据结构/初阶/C语言实现】
  7. 无盘服务器要开ahci,开启硬盘的ahci模式提升磁盘性能教程
  8. 高频电子线路 高频功率放大器
  9. 基于SSM的仓库管理系统(含完整源码+论文)
  10. TFS30063 您没有权限