1 准备工作
软件:vscode
安装vue-cli脚手架,参考另一篇笔记:https://blog.csdn.net/qq_36932169/article/details/105379074
我的版本是:
vue-cli:@vue/cli 5.0.8
vue:vue@3.3.4
以下教程用的是vue3
安装完成之后启动一下:npm run serve
2 登录页面
2.1 路由配置
src/router/index.js
import {
createRouter,
createWebHistory
} from 'vue-router';
const routes = [{
path: '/login',
name: 'login',
component: () =>
import('@/views/Login/index.vue')
},
{
path: '/',
name: 'layout',
component: () =>
import('@/views/Layout'),
redirect: '/home',
children: [{
path: '/home',
name: 'home',
component: () =>
import('@/views/system/Home'),
},
{
path: 'system/journal',
name: 'journal',
meta: {
label: '期刊'
},
component: () =>
import('@/views/system/journal')
},
{
path: 'system/paper',
name: 'paper',
meta: {
label: '文章'
},
component: () =>
import('@/views/system/paper')
},
{
path: 'system/user',
name: 'user',
meta: {
label: '用户'
},
component: () =>
import('@/views/system/user')
},
{
path: 'system/record',
name: 'record',
meta: {
label: '记录'
},
component: () =>
import('@/views/system/record')
}
]
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
2.2 登录页面组件
src/views下创建Login文件夹并建立index.vue和login.vue
(1)src/views/Login/index.vue
<template>
<div>
<div class="login" :style="'background-image:url(' + Background + ');'">
<div class="login_position">
<login></login>
</div>
</div>
</div>
</template>
<script>
import Background from "@/assets/bg01.jpg"
//引入
import login from "@/views/Login/login";
export default {
//注册
components: {
login
},
data(){
return{
Background: Background
}
}
}
</script>
<style lang="less" scoped>
.login {
display: flex;
justify-content: center;
align-items: center;
height: 605px;
width: 100%;
position: relative;
background-size: cover;
}
.login_position {
border-radius: 6px;
position: absolute;
top: 80px;
right: 100px;
background-color: #fff;
}
</style>
(2)登陆组件:src/views/Login/login.vue,
这里需要注意搭配用的element-plus,也可以参考笔记https://blog.csdn.net/qq_36932169/article/details/105379074
<template>
<div class="LoginForm-Panel" v-loading="loading">
<div class="login">
<div class="login-title">
<span>账号登录</span>
<span>忘记密码</span>
</div>
<el-form :rules="rules" :model="loginForm" ref="loginForm">
<el-form-item prop="userName">
<el-input
v-model="loginForm.userName"
prefix-icon="User"
placeholder="请输入用户名"
></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
type="password"
show-password
v-model="loginForm.password"
prefix-icon="Lock"
placeholder="请输入密码"
></el-input>
</el-form-item>
<el-button style="width: 100%" type="primary" @click="login()"
>登录</el-button
>
</el-form>
</div>
</div>
</template>
<script>
export default {
created() {
console.log("created-------------");
},
data() {
return {
loading: false, //页面加载状态
loginForm: {
//表单项
userName: "",
password: "",
},
rules: {
//验证规则
userName: [{ required: true, message: "请输入用户名" }],
password: [{ required: true, message: "请输入密码" }],
},
};
},
methods: {
//设置页面加载状态
setLoading(bool) {
this.loading = bool;
},
login() {
}
}
</script>
<style lang="less">
.login {
padding: 20px;
.login-title {
font-size: 14px;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 40px;
height: 40px;
line-height: 1.5;
a {
outline: none;
}
}
}
.el-form-item__content {
display: flex;
}
.login-code {
margin-left: 5px;
width: 33%;
height: 100%;
img {
cursor: pointer;
vertical-align: middle;
height: 100%;
}
}
</style>
2.2.1 ui好看的登录页面(我用的是这个)
<template>
<div id="login">
<div id="login-form" @keyup.enter="inputInfo">
<h1 class="login-title">登陆界面</h1>
<el-form :rules="loginRules" :model="loginForm" ref="loginForm">
<el-form-item prop="userName">
<label for="userName"
><i class="el-icon-user-solid" style="color: #c1c1c1"></i
></label>
<input
type="text"
placeholder="用户名"
name="userName"
id="userName"
autocapitalize="off"
v-model.trim="loginForm.userName"
aria-autocomplete="off"
/>
</el-form-item>
<el-form-item prop="password">
<label for="password"
><i class="el-icon-right" style="color: #c1c1c1"></i
></label>
<input
type="password"
placeholder="密码"
name="password"
id="password"
autocapitalize="off"
v-model.trim="loginForm.password"
/>
</el-form-item>
<div>
<el-button type="primary" v-on:click="login">登录</el-button>
<el-button type="info" @click="register">注册</el-button>
</div>
</el-form>
</div>
</div>
</template>
<script>
export default {
data: function () {
return {
loginForm: {
//表单项
userName: "",
password: "",
},
loginRules: {
//验证规则
userName: [
{ required: true, trigger: "blur", message: "请输入用户名" },
],
password: [{ required: true, trigger: "blur", message: "请输入密码" }],
},
};
},
methods: {
login() {
this.$refs.loginForm.validate((val) => {
if (val) {
// 表单验证通过
this.$router.push("/");
}
});
},
},
};
</script>
<style lang="scss" scoped>
#login {
width: 100vw;
height: 100vh;
overflow: hidden;
position: relative;
background-image: url("@/assets/images/bg02.jpg");
background-repeat: no-repeat;
background-attachment: fixed;
background-position: center;
background-size: cover;
}
#login-form {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 50vw;
min-width: 300px;
max-width: 400px;
display: flex;
flex-direction: column;
background-color: rgba(0, 0, 0, 0.7);
border-radius: 15px;
box-shadow: 0 15px 25px rgba(0, 0, 0, 0.5);
h1 {
width: 60%;
margin: 50px auto 20px;
color: #c1c1c1;
text-align: center;
}
input {
width: 60%;
margin: 15px auto;
outline: none;
border: none;
padding: 10px;
border-bottom: 1px solid #fff;
background: transparent;
color: white;
}
label {
width: 60%;
margin: 0 auto;
position: relative;
top: 30px;
left: -15px;
}
div {
width: 60%;
margin: 10px auto;
display: flex;
justify-content: center;
align-content: center;
}
button {
// rgba
background-color: rgba(9, 108, 144, 0.5);
margin: 10px 25px 40px 25px;
}
p {
width: 60%;
margin: 8px auto;
position: relative;
left: -15px;
color: #ff0000;
font-size: 8px;
}
}
input {
-webkit-text-fill-color: #ffffff !important;
transition: background-color 5000s ease-in-out, width 1s ease-out !important;
}
</style>
2.3 处理登录请求
(1)封装使用接口请求,方法写在api里。
src/utils文件夹下创建request.js
import axios from "axios";
let request = axios.create({
baseURL: 'http://192.168.50.86:8081',
timeout: 5000
})
export default request
src/api/login.js
import request from "@/utils/request"
//登录
export function login(data) {
return request({
url: "/user/login",
method: "post",
data
})
}
views/Login/index.vue
<script>
import { login } from "@/api/login";
...
methods: {
login() {
this.$refs.loginForm.validate((val) => {
if (val) {
//表单验证通过,将登录表单信息传给后端
login(this.loginForm).then((res) => {
console.log(res.data);
this.$router.push("/");
});
}
});
},
},
(2)添加拦截器,提示请求失败信息
我们发现登陆失败的时候,页面一点反应都没有,是不是很不合常理?我们需要在获得后端响应之后对后端返回的信息进行处理并显示出来,于是就需要使用拦截器。
src/utils/request.js
导入axios,并创建实例,然后给实例添加响应拦截器即可
import axios from "axios";
let request = axios.create({
baseURL: 'http://192.168.50.86:8081',
timeout: 5000
})
//添加响应拦截器
request.interceptors.response.use(response => {
console.log(response);
console.log('响应拦截器');
return response.data;
},
error => {
ElMessage.error('请求失败了' + error);
return Promise.reject(error)
})
export default request
(3)token认证(jwt)
JWT本身就包含校验信息,是不需要写入数据库的。可以理解为:token是身份证号码,JWT是身份证,身份证上面是有过期时间的。
①保存用户名密码到cookies
②保存token到cookies
新建utils/auth.js,定义公共的获取token和设置token的方法
//npm install js-cookie
import Cookies from 'js-cookie'
import Config from '@/settings'
const TokenKey = Config.TokenKey
// const TokenKey = 'EL-ADMIN-TOEKN'
export function getToken() {
return Cookies.get(TokenKey)
}
export function setToken(token) {
Cookies.set(TokenKey, token)
}
export function removeToken() {
return Cookies.remove(TokenKey)
}
存放一些公用配置信息src/setting.js
module.exports = {
TokenKey: 'EL-ADMIN-TOEKN',
}
接着我们在发送登陆请求之后把token保存到Cookies里面
views/Login/index.vue
import { setToken } from "@/utils/auth";
...
login() {
this.$refs.loginForm.validate((val) => {
login(this.loginForm).then((res) => {
console.log(res.data);
setToken(res.data.token); //保存token
this.$router.push("/");
});
}
});
},
(4)路由导航守卫NavigationGuard
如何想在登录过后,直接跳过登录页面?或者没有登录就跳转到登录页面
router/index.js中添加
//假如用户A没有登录,访问了Login页面
router.beforeEach((to, from, next) => {
if (getToken() !== 'undefined' && getToken()) { //已登录
if (to.name === 'login') next({
name: 'layout'
}) // 如果访问的是登陆页面,直接跳转到主页
// 另外一种写法,直接写path,next('/layout')
else next() //想去哪里就去哪里
} else if (to.name !== 'login')
//没有登录
next({
name: 'login'
}) //如果不是登陆页面,重定向到登陆页面
else next() //如果是登陆页面,就直接放行
})
(5)请求拦截器,实现携带token发送请求
utils/requst.js
//添加请求拦截器
request.interceptors.request.use(config => {
if (getToken()) {
config.headers['Authorization'] = getToken()
}
return config
},
error => {
return Promise.reject(error)
})
(6)注销功能
utils/requst.js
//添加响应拦截器
request.interceptors.response.use(response => {
console.log(response);
console.log('响应拦截器');
return response.data;
},
error => {
let code = error.response.data.status
ElMessage.error(code + ' ' + error.response.data.message);
if (code === 401) { //如果认证失败,则实行注销操作
removeToken(Config.TokenKey)
router.replace('/').then(res => {})
}
return Promise.reject(error)
})