
TS项目实战四:ts+vue3+elementplus实现登录注册功能
element
A Vue.js 2.0 UI Toolkit for Web
项目地址:https://gitcode.com/gh_mirrors/eleme/element

·
使用vue3+elementplus+vite+pinia实现用户登录、注册相关界面及对应业务流程的开发,对接express后端服务,调用对应接口,实现完整的用户登录注册功能。
源码下载:点击下载
讲解视频:
TS实战项目三十:Vue3项目创建
B站视频:
<iframe id="DYOPnKmW-1709868857546" frameborder="0" src="https://player.bilibili.com/player.html?aid=1951025133" allowfullscreen="true" data-mediaembed="bilibili"></iframe>
TS实战项目三十:Vue3项目创建
西瓜视频:
https://www.ixigua.com/7340090758781174306
一、界面预览
二、相关知识点
- tsc编译
- tsconfig.json配置项
- 模块定义及导入导出
- 类定义
- 参数属性
- 存取器
- 继承
- 抽象类
- 抽象方法
- 接口
- 枚举
- 静态变量
- async/await
- vite
- vue3
- elementplus
- pinia
- router
- axios
三、功能规划
使用vue3组合式开发,使用elementplus框架搭建界面,使用pinia实现数据状态管理,使用axios实现用户请求,搭建完整的用户登录、用户注册等相关界面,并实现相应的业务逻辑处理。
四、项目创建
- 安装npm create vue@latest:
- 安装elementplus:
- 安装axios:
-
安装pinia-plugin-persistedstate:
-
项目目录结构:
五、代码实现
- package.json
{
"name": "demo5",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"test:unit": "vitest",
"build-only": "vite build",
"type-check": "vue-tsc --build --force",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"format": "prettier --write src/"
},
"dependencies": {
"@types/axios": "^0.14.0",
"axios": "^1.6.7",
"element-plus": "^2.5.6",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"vue": "^3.4.15",
"vue-router": "^4.2.5"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.3.3",
"@tsconfig/node20": "^20.1.2",
"@types/jsdom": "^21.1.6",
"@types/node": "^20.11.10",
"@vitejs/plugin-vue": "^5.0.3",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"@vue/eslint-config-prettier": "^8.0.0",
"@vue/eslint-config-typescript": "^12.0.0",
"@vue/test-utils": "^2.4.4",
"@vue/tsconfig": "^0.5.1",
"eslint": "^8.49.0",
"eslint-plugin-vue": "^9.17.0",
"jsdom": "^24.0.0",
"npm-run-all2": "^6.1.1",
"prettier": "^3.0.3",
"typescript": "~5.3.0",
"vite": "^5.0.11",
"vitest": "^1.2.2",
"vue-tsc": "^1.8.27"
}
}
- vite.config.ts
import {
fileURLToPath,
URL,
} from 'node:url';
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
vueJsx(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
server: {
proxy: {
// with options: http://localhost:5173/api/bar-> http://jsonplaceholder.typicode.com/bar
'/api': {
target: 'http://localhost:3000/',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
}
},
}
})
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>君君军管理系统</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
- /src/main.ts
import './assets/main.css';
import 'element-plus/dist/index.css';
import { createApp } from 'vue';
import ElementPlus from 'element-plus';
import * as ElementPlusIconsVue from '@element-plus/icons-vue';
import App from './App.vue';
import router from './router';
import pinia from './stores';
const app = createApp(App)
//注册elementplus
app.use(ElementPlus)
//注册elementplus的图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
//注册pinia
app.use(pinia);
app.use(router)
app.mount('#app')
- /src/App.vue
<script setup lang="ts">
import {
RouterLink,
RouterView,
} from 'vue-router';
</script>
<template>
<RouterView />
</template>
<style scoped></style>
- /src/views/index.vue
<script setup lang="ts">
import { reactive } from 'vue';
import {
ElLoading,
ElMessage,
} from 'element-plus';
import api from '@/utils/api';
//加载到的用户数据
const userData = reactive({});
/**
* 加载用户信息
*/
const loadUserInfo = () => {
//显示加载动画
const loading = ElLoading.service({
lock: true,
text: '正常处理',
background: 'rgba(0, 0, 0, 0.7)',
});
api.get('/api/get?id=1').then((response: AxiosResponse<any>) => {
if (response.status != 200 || !response.data || response.data.code != 200) {
if (response.data) {
ElMessage.error('加载失败:' + response.data.msg)
} else {
ElMessage.error('加载失败!')
}
return;
}
let data = response.data.data;
Object.assign(userData, data);
}).catch((error: AxiosError<any>) => {
ElMessage.error('操作异常:' + error.message)
}).finally(() => {
loading.close();
});
}
</script>
<template>
<div style="padding:20px">
<el-result icon="success" title="登陆成功" sub-title="已登录,这是系统注册">
<template #extra>
<el-button type="primary" @click="loadUserInfo">获取用户</el-button>
</template>
</el-result>
<div>
用户信息:{{ userData }}
</div>
</div>
</template>
<style scoped></style>
- /src/views/register/register.vue
<script setup lang="ts">
import {
reactive,
ref,
} from 'vue';
//自组件的引入
import account from './components/account.vue';
import info from './components/info.vue';
import success from './components/success.vue';
//步骤条的进度
const step = ref(1);
//账号信息
const accountInfo = reactive({
account: '',
smscode: '',
captcha: ''
});
//转到信息完善界面
const toInfo = (data) => {
Object.assign(accountInfo, data);
step.value = 2;
}
</script>
<template>
<div class="registerPage">
<el-card class="registerPanel">
<!-- 步骤条 -->
<el-steps class="registerStep" :active="step" align-center>
<el-step title="账号验证" description="" />
<el-step title="信息完善" description="" />
<el-step title="注册完成" description="" />
</el-steps>
<!-- 每一步的内容 -->
<div class="registerContent">
<!-- 账号校验 -->
<account v-if="step === 1" @next="toInfo"></account>
<!-- 信息完善 -->
<info v-else-if="step === 2" @pre="step = 1" @next="step = 3" :accountInfo="accountInfo"></info>
<!-- 注册完成 -->
<success v-else></success>
</div>
</el-card>
<div class="footer">@Copyright 君君军通用管理系统 备案信息:陕432432432</div>
</div>
</template>
<style scoped>
.registerPage {
display: flex;
justify-content: center;
justify-items: center;
align-items: center;
height: 100%;
}
.registerPage .registerPanel {
width: 80%;
height: 60%;
max-width: 1024px;
max-height: 800px;
margin: 0 auto;
}
.registerPage .registerPanel .registerStep {
width: 80%;
margin: 0 auto;
margin-top: 40px;
}
.registerPage .registerPanel .registerContent {
margin-top: 40px;
}
.footer {
position: fixed;
bottom: 0px;
line-height: 40px;
text-align: center;
font-size: 14px;
color: #999;
}
</style>
- /src/views/register/components/account.vue
<script setup lang="ts">
/**
* 账号验证流程:
*
* 1.获取到输入的电话号码及验证码
*
* 2.调用获取短信验证码接口
*
* 3.获取短信验证码之后,获取按钮需要暂时禁用
*
* 4.获取到验证码之后,下一步的时候校验验证码是否合法,如果合法则进入下一步
*
*/
import {
defineEmits,
onUnmounted,
reactive,
ref,
} from 'vue';
import type {
AxiosError,
AxiosResponse,
} from 'axios';
import type {
FormInstance,
FormRules,
} from 'element-plus';
import {
ElLoading,
ElMessage,
} from 'element-plus';
import { useRouter } from 'vue-router';
import api from '@/utils/api';
// 路由控制七
const router = useRouter();
//自定义事件
const emit = defineEmits(['next']);
// 表单数据
const account = reactive({
account: '',
captcha: '',
smscode: ''
});
//表单的实例
const formRef = ref();
//数据校验
const rules = reactive<FormRules<typeof account>>({
account: [{ required: true, message: '请输入账号', trigger: 'blur' }],
captcha: [{ required: true, message: '请输入图像验证码', trigger: 'blur' }],
smscode: [{ required: true, message: '请输入短信验证码', trigger: 'blur' }],
})
//验证码
const captchaSrc = ref('/api/register/captcha');
//验证码的刷新
const refreshCaptcha = () => {
captchaSrc.value = '/api/register/captcha?t_=' + new Date().getTime()
}
//定义定时器
let timer: number | undefined;
//验证码倒计时
let smscodeTime = ref(0);
//获取短信验证码
const getSmscode = () => {
if (!account.account || !account.captcha) {
ElMessage.error('请输入电话号码及验证码');
return;
}
//显示加载动画
const loading = ElLoading.service({
lock: true,
text: '正在处理',
background: 'rgba(0, 0, 0, 0.7)',
});
api.post('/api/register/getSmscode', {
phone: account.account,
captcha: account.captcha
}).then((response: AxiosResponse<any>) => {
if (response.status != 200 || !response.data || response.data.code != 200) {
if (response.data) {
ElMessage.error('获取失败:' + response.data.msg)
} else {
ElMessage.error('获取失败!')
}
return;
}
//提示短信验证码已发送
ElMessage({
message: '短信验证码已发送,3分钟之内有效!',
type: 'success',
})
//限制获取验证码按钮一分钟之内不能重复点击
smscodeTime.value = 60;
timer = setInterval(() => {
smscodeTime.value--;
if (smscodeTime.value <= 0) {
smscodeTime.value = 0;
clearInterval(timer);
timer = null;
}
}, 1000);
}).catch((error: AxiosError<any>) => {
if (error.response && error.response.data) {
ElMessage.error(error.response.data.msg)
return;
}
ElMessage.error('操作异常:' + error.message)
}).finally(() => {
loading.close();
});
}
//提交表单
const submitForm = (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate((isValid: boolean) => {
if (!isValid) {
return;
}
//显示加载动画
const loading = ElLoading.service({
lock: true,
text: '正在处理',
background: 'rgba(0, 0, 0, 0.7)',
});
//请求接口,进行登陆
api.post('/api/register/validateSmscode', {
smscode: account.smscode
}).then((response: AxiosResponse<any>) => {
if (response.status != 200 || !response.data || response.data.code != 200) {
if (response.data) {
ElMessage.error('处理失败:' + response.data.msg)
} else {
ElMessage.error('处理失败!')
}
return;
}
emit('next', {
account: account.account,
smscode: account.smscode,
captcha: account.captcha
});
}).catch((error: AxiosError<any>) => {
if (error.response && error.response.data) {
ElMessage.error(error.response.data.msg)
return;
}
ElMessage.error('操作异常:' + error.message)
}).finally(() => {
loading.close();
});
})
}
//取消
const cancel = () => {
router.push('/login');
}
onUnmounted(() => {
//清除定时器
timer && clearInterval(timer);
})
</script>
<template>
<!-- 电话号码校验 -->
<el-form ref="formRef" :model="account" :rules="rules" size="large" label-width="120" class="accountForm"
status-icon>
<el-form-item label="电话号码:" prop="account">
<el-input v-model="account.account" placeholder="请输入电话号码" suffix-icon="UserFilled" />
</el-form-item>
<el-form-item label="图像验证码:" prop="captcha">
<div class="flex">
<div class="flexItem">
<el-input v-model="account.captcha" placeholder="请输入图形验证码" />
</div>
<div class="captchaPanel">
<img :src="captchaSrc" @click="refreshCaptcha">
</div>
</div>
</el-form-item>
<el-form-item label="短信验证码:" prop="smscode">
<div class="flex">
<div class="flexItem">
<el-input v-model="account.smscode" placeholder="请输入短信验证码" suffix-icon="Message" />
</div>
<div class="smscodePanel">
<el-button @click="getSmscode" :disabled="smscodeTime > 0">{{ smscodeTime > 0 ? smscodeTime
+
'秒之后再试' : '获取验证码' }}</el-button>
</div>
</div>
</el-form-item>
<el-form-item label="">
<div class="formBtns">
<el-button type="danger" @click="cancel">取消</el-button>
<el-button type="primary" @click="submitForm(formRef)">
下一步
</el-button>
</div>
</el-form-item>
</el-form>
</template>
<style scoped>
.accountForm {
width: 50%;
margin: 0 auto;
}
.accountForm .formBtns {
margin: 0 auto;
}
.accountForm .formBtns .el-button {
width: 150px;
}
.flex {
display: flex;
width: 100%;
}
.flex .flexItem {
flex: 1
}
.captchaPanel {
width: 110px;
padding-left: 10px;
}
.captchaPanel img {
width: 100px;
cursor: pointer;
}
.smscodePanel {
width: 110px;
padding-left: 10px;
}
.smscodePanel .el-button {
width: 100px;
}
</style>
- /src/views/register/components/info.vue
<script setup lang="ts">
import {
reactive,
ref,
} from 'vue';
import type {
AxiosError,
AxiosResponse,
} from 'axios';
import type {
FormInstance,
FormRules,
} from 'element-plus';
import {
ElLoading,
ElMessage,
} from 'element-plus';
import api from '@/utils/api';
//外部的参数
const prop = defineProps({
accountInfo: {
type: Object
}
});
//自定义事件
const emit = defineEmits(['pre', 'next']);
//表单数据
const info = reactive({
account: '',
sex: '1',
password: '',
name: ''
})
//表单的实例
const formRef = ref();
//数据校验
const rules = reactive<FormRules<typeof info>>({
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
account: [{ required: true, message: '请输入账号', trigger: 'blur' }],
sex: [{ required: true, message: '请选择性别', trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
})
//提交表单
const submitForm = (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate((isValid: boolean) => {
if (!isValid) {
return;
}
//显示加载动画
const loading = ElLoading.service({
lock: true,
text: '正在处理',
background: 'rgba(0, 0, 0, 0.7)',
});
//请求接口,进行登陆
api.post('/api/register', {
account: info.account,
sex: info.sex,
password: info.password,
phone: prop.accountInfo.account,
captcha: prop.accountInfo.captcha,
name: info.name
}).then((response: AxiosResponse<any>) => {
if (response.status != 200 || !response.data || response.data.code != 200) {
if (response.data) {
ElMessage.error('处理失败:' + response.data.msg)
} else {
ElMessage.error('处理失败!')
}
return;
}
emit('next');
}).catch((error: AxiosError<any>) => {
if (error.response && error.response.data) {
ElMessage.error(error.response.data.msg)
return;
}
ElMessage.error('操作异常:' + error.message)
}).finally(() => {
loading.close();
});
})
return null;
}
</script>
<template>
<el-form ref="formRef" :model="info" :rules="rules" size="large" label-width="120" class="infoForm" status-icon>
<el-form-item label="账号:" prop="account">
<el-input v-model="info.account" placeholder="请输入账号" suffix-icon="UserFilled" />
</el-form-item>
<el-form-item label="姓名:" prop="name">
<el-input v-model="info.name" placeholder="请输入姓名" suffix-icon="UserFilled" />
</el-form-item>
<el-form-item label="密码:" prop="password">
<el-input v-model="info.password" type="password" placeholder="请输入密码" suffix-icon="Lock" />
</el-form-item>
<el-form-item label="性别:" prop="sex">
<el-radio-group v-model="info.sex">
<el-radio label="1" value="1" size="large">男</el-radio>
<el-radio label="2" value="2" size="large">女</el-radio>
<el-radio label="3" value="3" size="large">保密</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="">
<div class="formBtns">
<el-button type="danger" @click="emit('pre')">上一步</el-button>
<el-button type="primary" @click="submitForm(formRef)">
注册
</el-button>
</div>
</el-form-item>
</el-form>
</template>
<style scoped>
.infoForm {
width: 50%;
margin: 0 auto;
}
.infoForm .formBtns {
margin: 0 auto;
}
.infoForm .formBtns .el-button {
width: 150px;
}
.flex {
display: flex;
width: 100%;
}
.flex .flexItem {
flex: 1
}
</style>
- /src/views/register/components/success.vue
<script setup lang="ts">
import { useRouter } from 'vue-router';
//路由状态
const router = useRouter();
</script>
<template>
<el-result icon="success" title="注册成功" sub-title="恭喜,注册成功">
<template #extra>
<el-button type="primary" @click="router.push('/login')">立即登陆</el-button>
</template>
</el-result>
</template>
<style scoped></style>
- /src/views/login/login.vue
<script setup lang="ts">
import {
reactive,
ref,
} from 'vue';
import type {
AxiosError,
AxiosResponse,
} from 'axios';
import type {
FormInstance,
FormRules,
} from 'element-plus';
import {
ElLoading,
ElMessage,
} from 'element-plus';
import { useRouter } from 'vue-router';
import { useLoginStore } from '@/stores/login';
import api from '@/utils/api';
//登陆状态存储
const loginStore = useLoginStore();
//路由状态
const router = useRouter();
/**
* 1.写登陆的界面组件实现
*
* 2.界面逻辑实现
*
* 3.登陆状态检测
*
*/
//登陆表单数据
const loginForm = reactive({
account: '',
password: '',
captcha: ''
})
//校验规则
const rules = reactive<FormRules<typeof loginForm>>({
account: [{ required: true, message: '请输入账号', trigger: 'blur' }],
password: [{
validator: (rule: any, value: any, callback: any) => {
if (value === '') {
callback(new Error('密码不能为空'))
} else if (loginForm.password.length < 6) {
callback(new Error("密码不能小鱼6位3字符"))
} else {
callback()
}
}, trigger: 'blur'
}],
captcha: [{ required: true, message: '请输入验证码', trigger: 'blur' }],
})
//form表单的实例
const formRef = ref<FormInstance>()
/**
* 提交
*/
const submitForm = (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate((isValid: boolean) => {
if (!isValid) {
return;
}
//显示加载动画
const loading = ElLoading.service({
lock: true,
text: '正在登陆',
background: 'rgba(0, 0, 0, 0.7)',
});
//请求接口,进行登陆
api.post('/api/login', loginForm).then((response: AxiosResponse<any>) => {
if (response.status != 200 || !response.data || response.data.code != 200) {
if (response.data) {
ElMessage.error('登陆失败:' + response.data.msg)
} else {
ElMessage.error('登陆失败!')
}
return;
}
let token = response.headers.authorization;
//存储token,后续使用
loginStore.authorization = token;
router.push('/index');
}).catch((error: AxiosError<any>) => {
if (error.response && error.response.data) {
ElMessage.error(error.response.data.msg)
return;
}
ElMessage.error('操作异常:' + error.message)
}).finally(() => {
loading.close();
});
})
}
// 验证码路径
const captchaSrc = ref('/api/login/captcha');
/**
* 刷新验证码
*/
const changecaptchaSrc = () => {
captchaSrc.value = '/api/login/captcha?t_=' + new Date().getTime();
}
</script>
<template>
<div class="loginPage">
<el-card class="loginPanel">
<div class="loginPanelInner">
<div class="logo">
<img src="../../assets/images/logo.png">
</div>
<el-divider direction="vertical" border-style="dashed" class="split" />
<div class="loginForm">
<div class="systemName"> 用户登陆 </div>
<el-form ref="formRef" size="large" :model="loginForm" status-icon :rules="rules"
label-width="120px" class="form">
<el-form-item label="账号:" prop="account">
<el-input v-model="loginForm.account" placeholder="请输入账号" autocomplete="off"
suffix-icon="UserFilled" />
</el-form-item>
<el-form-item label="密码:" prop="password">
<el-input v-model="loginForm.password" placeholder="请输入密码" type="password"
autocomplete="off" suffix-icon="Lock" />
</el-form-item>
<el-form-item label="验证码:" prop="captcha">
<div style="display: flex;width: 100%;">
<div style="flex:1">
<el-input v-model.number="loginForm.captcha" placeholder="请输入验证码" />
</div>
<div class="captchaSrc">
<img :src="captchaSrc" @click="changecaptchaSrc">
</div>
</div>
</el-form-item>
<div class="registerBtn">
<el-link type="primary" href="/register" :underline="false">
去账户?点击注册 </el-link>
</div>
<el-form-item>
<el-button type="primary" @click="submitForm(formRef)" class="loginBtn">
登陆 </el-button>
</el-form-item>
</el-form>
</div>
</div>
</el-card>
<div class="footer">
@Copyright 君君军通用管理系统 备案信息:陕432432432
</div>
</div>
</template>
<style scoped>
.loginPage {
width: 100%;
height: 100%;
display: flex;
justify-items: center;
justify-content: center;
align-items: center;
background: linear-gradient(133deg, #1994bb, #19acbb, #19b4bb, #2a89db);
}
.loginPage .loginPanel {
width: 60%;
height: 60%;
min-width: 600px;
max-width: 1000px;
min-height: 400px;
max-height: 500px;
margin: 0 auto;
}
.loginPage .loginPanel>>>.el-card__body {
width: 100%;
height: 100%;
}
.loginPage .loginPanel .loginPanelInner {
width: 100%;
height: 100%;
display: flex;
}
.loginPage .loginPanel .loginPanelInner .logo {
width: 40%;
text-align: center;
display: flex;
justify-items: center;
justify-content: center;
align-items: center;
}
.loginPage .loginPanel .loginPanelInner .logo img {
width: 50%;
}
.loginPage .loginPanel .loginPanelInner .split {
height: calc(100% - 40px);
}
.loginPage .loginPanel .loginPanelInner .loginForm {
flex: 1;
}
.loginPage .loginPanel .loginPanelInner .loginForm .systemName {
text-align: center;
font-size: 30px;
line-height: 60px;
letter-spacing: 3px;
margin-bottom: 20px;
}
.loginPage .loginPanel .loginPanelInner .loginForm .form {
width: 80%
}
.loginPage .loginPanel .loginPanelInner .loginForm .form .loginBtn {
width: 100%;
}
.loginPage .loginPanel .loginPanelInner .loginForm .form .captchaSrc {
width: 100px;
height: 100%;
padding-left: 10px;
}
.loginPage .loginPanel .loginPanelInner .loginForm .form .captchaSrc img {
width: 100px;
height: 100%;
cursor: pointer;
}
.loginPage .loginPanel .loginPanelInner .loginForm .form .registerBtn {
text-align: right;
line-height: 40px;
margin-bottom: 5px;
}
.footer {
position: fixed;
bottom: 0px;
line-height: 40px;
text-align: center;
font-size: 14px;
color: #fff;
}
</style>
- /src/utils/api.ts
import axios, { type AxiosResponse } from 'axios';
import { ElMessage } from 'element-plus';
import { useLoginStore } from '@/stores/login';
import router from '../router/index';
// 使用由库提供的配置的默认值来创建实例
// 此时超时配置的默认值是 `0`
const instance = axios.create({
baseURL: import.meta.env.BASE_URL
});
// 覆写库的超时默认值
// 现在,在超时前,所有请求都会等待 2.5 秒
instance.defaults.timeout = 3000;
//请求时需附加上token
// 添加请求拦截器
instance.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
const loginStore = useLoginStore();
config.headers.authorization = "Bearer " + loginStore.authorization;
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
//token过期拦截处理
instance.interceptors.response.use(function (response: AxiosResponse<any>) {
return response;
}, function (error) {
if (error.response.status == 401) {
//提示登陆状态已失效,需要重新登陆
ElMessage({
message: '登陆状态已失效,请重新登陆',
type: 'warning',
});
//自动转到登陆界面
router.push('/login');
}
return Promise.reject(error);
});
export default instance;
- /src/stores/index.ts
import { createPinia } from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
export default pinia;
- /src/stores/login.ts
import { defineStore } from 'pinia';
/**
* 存储状态的处理
*/
export const useLoginStore = defineStore('loginStore', {
state: () => {
return {
authorization: ''
}
},
actions: {
/**
* 更新登陆凭证
*/
setToken(value: string) {
this.authorization = value
},
/**
* 检测当前是否已经登陆
*/
isLogined(): boolean {
return this.authorization ? true : false;
}
},
persist: true,
})
- /src/router/index.ts
import {
createRouter,
createWebHistory,
} from 'vue-router';
import { useLoginStore } from '@/stores/login';
import index from '@/views/index.vue';
import login from '@/views/login/login.vue';
import register from '@/views/register/register.vue';
//声明元数据
declare module 'vue-router' {
interface RouteMeta {
// 每个路由都必须声明
requiresAuth: boolean
}
}
//路由信息
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: '系统主页',
redirect: '/index',
// 只有经过身份验证的用户才能创建帖子
meta: { requiresAuth: true }
},
{
path: '/login',
name: '用户登陆',
component: login,
// 只有经过身份验证的用户才能创建帖子
meta: { requiresAuth: false }
},
{
path: '/register',
name: '用户注册',
component: register,
// 只有经过身份验证的用户才能创建帖子
meta: { requiresAuth: false }
},
{
path: '/index',
name: '系统主页',
component: index,
// 只有经过身份验证的用户才能创建帖子
meta: { requiresAuth: true }
}
]
})
//添加路由的前置守卫,做登陆状态的检测
router.beforeEach(async (to, from) => {
if (to.meta.requiresAuth) {//判断当前界面是否需要鉴权
//登陆状态的存储器
const loginStore = useLoginStore();
if (!loginStore.isLogined()) {
// 将用户重定向到登录页面
return { path: '/login' }
}
}
})
export default router




A Vue.js 2.0 UI Toolkit for Web
最近提交(Master分支:8 个月前 )
c345bb45
1 年前
a07f3a59
* Update transition.md
* Update table.md
* Update transition.md
* Update table.md
* Update transition.md
* Update table.md
* Update table.md
* Update transition.md
* Update popover.md 1 年前
更多推荐
所有评论(0)