本文基于vue3(JavaScript),使用vue3的setup语法糖书写方式
setup语法糖也是当前各大适用Vue的框架官网都在推崇的书写方式,此外各大主流框架的源码首选是TypeScript,而不是JavaScript。
以下登录页面模板,基于element-plus和scss,关于elment-plus和scss的环境准备本文不做赘述,包括element-plus图标库的引入。
效果图:
<template>
<div class="login-body">
<div class="login-panel">
<div class="login-title">用户登录</div>
<el-form :model="formData" :rules="rules" ref="formDataRef">
<el-form-item prop="username">
<el-input placeholder="请输入账号" v-model="formData.username" size="large" type="text">
<template #prefix>
<el-icon>
<User />
</el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input placeholder="请输入密码" v-model="formData.password" size="large" type="password"
@keyup.enter.native="login()">
<template #prefix>
<el-icon>
<Lock />
</el-icon>
</template>
</el-input>
</el-form-item>
<!-- <el-form-item label="">
<div class="check-code-panel">
<el-input placeholder="请输入验证码" v-model="formData.checkCode" class="input-panel" />
<img src="checkCodeUrl" class="check-code">
</div>
</el-form-item> -->
<!-- <el-form-item label="">
<el-checkbox label="记住密码" name="type" />
</el-form-item> -->
<el-form-item label="">
<el-button type="primary" style="width: 100%;" @click="login()" size="large">登录</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script setup>
import { ref, reactive, } from 'vue'
import { ElMessage } from 'element-plus';
import request from '@/utils/request'; //这里使用自行封装的axios,下文已给出,照搬后修改运行端口即可
import { useRouter } from 'vue-router';
// const checkCodeUrl = "api/checkCode?" + new Date().getTime();
//表单
const formDataRef = ref();
let formData = reactive({
username: "",
password: ""
});
const rules = {
username: [{
required: true,
message: "请输入用户名"
}],
password: [{
required: true,
message: "请输入密码"
}],
// checkCode: [{
// required: true,
// message: "请输入验证码"
// }],
}
const router = useRouter();
const login = () => {
var form_obj = JSON.parse(JSON.stringify(formData));
// console.log(form_obj);
// console.log(form_obj.username);
// console.log(form_obj.password);
// 后端代码自行准备
request.post("/user/login", form_obj).then(res => {
if (res) {
ElMessage({
message: '登录成功',
type: 'success',
})
let tokenObj = { token: " isLogin", startTime: new Date().getTime() };
window.localStorage.setItem("isLogin", JSON.stringify(tokenObj));
localStorage.setItem("username", JSON.parse(JSON.stringify(formData.username)));
router.push("/");
} else {
ElMessage.error('账号或密码错误!!!登录失败!!!')
}
});
};
</script>
<style lang="scss" scoped >
.login-body {
background: url("../assets/三门峡.png") no-repeat center center;
height: 100%;
width: 100%;
background-size: cover;
position: absolute;
left: 0;
top: 0;
.login-panel {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
padding: 25px;
width: 26%;
min-width: 460px;
height: 30%;
min-height: 300px;
background: rgba(255, 255, 255, 0.6);
border-radius: 5%;
box-shadow: 2px 2px 10px #ddd;
.login-title {
font-size: 22px;
text-align: center;
margin-bottom: 22px;
}
}
}
</style>
utils/request.js
import axios from 'axios'
//这里的端口是后端程序的运行端口
const request = axios.create({
baseURL: 'http://localhost:10029', // 注意!! 这里是全局统一加上了 '/api' 前缀,也就是说所有接口都会加上'/api'前缀在,页面里面写接口的时候就不要加 '/api'了,否则会出现2个'/api',类似 '/api/api/user'这样的报错,切记!!!
timeout: 5000
})
// request 拦截器
// 可以自请求发送前对请求做一些处理
// 比如统一加token,对请求参数统一加密
request.interceptors.request.use(config => {
config.headers['Content-Type'] = 'application/json;charset=utf-8';
// config.headers['token'] = user.token; // 设置请求头
return config
}, error => {
return Promise.reject(error)
});
// response 拦截器
// 可以在接口响应后统一处理结果
request.interceptors.response.use(
response => {
let res = response.data;
// 如果是返回的文件
if (response.config.responseType === 'blob') {
return res
}
// 兼容服务端返回的字符串数据
if (typeof res === 'string') {
res = res ? JSON.parse(res) : res
}
return res;
},
error => {
console.log('' + error) // for debug
return Promise.reject(error)
}
)
export default request
通过添加路由守卫的方式,监控登录状态,登录成功记录时间戳到本地缓存,若本地缓存的时间戳超过设定的时间则自动退出登录状态。
router/index.js 添加全局路由守卫
import { createRouter, createWebHistory } from 'vue-router'
import { ElMessage } from 'element-plus'
import { h } from 'vue';
const routes = [
//登录页路由
{
path: '/login',
name: '登录页',
component: () => import('../views/LoginView.vue')
},
// 页面找不到404 路由
{
path: '/404',
name: 'NoPage 404',
component: () => import('../views/404.vue'),
hidden: true
},
{
path: '/:pathMatch(.*)',
redirect: '/404',
hidden: true
},
//框架布局路由
{
path: '/',
name: "框架页",
component: () => import('../views/LayoutView.vue'),
redirect: '/home',
children: [
{
path: 'home',
name: '首页',
component: () => import('../components/Home.vue')
},
]
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
// 设置登录过期时间(一天)86400000
let expire = 21600000;
//路由守卫
//全局守卫,登录拦截
//进行路由拦截:当没有登陆标识,直接打回登陆页面,如何避免退回到 登陆页呢?
router.beforeEach((to, from, next) => {
// 从本地缓存中获取保存的 token 信息
const tokenObj = JSON.parse(window.localStorage.getItem('isLogin'))
if (to.path === "/login") {
next()
} else {
// 如果没有token,强制跳转到登录页面;如果有,则判断token时间是否过期
if (!tokenObj || !tokenObj.token) {
next('/login')
} else {
let date = new Date().getTime();
// 当前时间 - token中的登录时间 > 设置的过期时间,为过期;则清除token,并强制跳转至登录页
// 反之为有效期,则放行
if (date - tokenObj.startTime > expire) {
window.localStorage.removeItem('isLogin');
next('/login')
ElMessage({
message: h('p', null, [
h('span', null, '登录状态过期'),
h('i', { style: 'color: teal' }, '请重新登录!'),
]),
})
} else {
next();
}
}
}
});
export default router