✨ add automatic login
This commit is contained in:
1
components.d.ts
vendored
1
components.d.ts
vendored
@@ -29,6 +29,7 @@ declare module 'vue' {
|
||||
BoxDog: typeof import('./src/components/BoxDog/BoxDog.vue')['default']
|
||||
DynamicTitle: typeof import('./src/components/DynamicTitle/DynamicTitle.vue')['default']
|
||||
ForgetPage: typeof import('./src/views/Forget/ForgetPage.vue')['default']
|
||||
LandingPage: typeof import('./src/views/Landing/LandingPage.vue')['default']
|
||||
LockOutlined: typeof import('@ant-design/icons-vue')['LockOutlined']
|
||||
LoginFooter: typeof import('./src/views/Login/LoginFooter.vue')['default']
|
||||
LoginPage: typeof import('./src/views/Login/LoginPage.vue')['default']
|
||||
|
@@ -5,11 +5,17 @@ import {PhoneLogin} from "@/types/user";
|
||||
* 获取用户信息
|
||||
*/
|
||||
export const getUserInfo = () => {
|
||||
return service.Get('/api/auth/user/List', {
|
||||
return service.Get('/api/auth/user/list', {
|
||||
meta: {
|
||||
ignoreToken: false
|
||||
},
|
||||
cacheFor: 1000 * 60
|
||||
cacheFor: {
|
||||
// 设置缓存模式为持久化模式
|
||||
mode: 'restore',
|
||||
// 缓存时间
|
||||
expire: 1000 * 10,
|
||||
tag: 'v1'
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
@@ -18,10 +24,9 @@ export const getUserInfo = () => {
|
||||
* @param refreshToken
|
||||
*/
|
||||
export const refreshToken = (refreshToken: string) => {
|
||||
return service.Get('/api/auth/token/refresh', {
|
||||
params: {
|
||||
refresh_token: refreshToken
|
||||
},
|
||||
return service.Post('/api/token/refresh', {
|
||||
refresh_token: refreshToken
|
||||
}, {
|
||||
meta: {
|
||||
authRole: 'refreshToken',
|
||||
ignoreToken: false
|
||||
|
@@ -38,6 +38,7 @@ export default {
|
||||
sendCaptchaError: "captcha sending failed, please try again later",
|
||||
loginSuccess: "login success!",
|
||||
loginError: "login failed!",
|
||||
|
||||
},
|
||||
error: {
|
||||
networkError: 'Network error, please try again later',
|
||||
@@ -55,5 +56,8 @@ export default {
|
||||
other: 'connect error',
|
||||
authTokenError: "auth token error, please try again later",
|
||||
authTokenExpired: "auth token expired, please login again",
|
||||
loginExpired: "login expired, please try again",
|
||||
pleaseLogin: "please login first",
|
||||
|
||||
}
|
||||
};
|
||||
|
@@ -57,6 +57,7 @@ export default {
|
||||
other: '连接出错',
|
||||
authTokenError: "认证失败,请重新登录!",
|
||||
authTokenExpired: "认证过期,请重新登录!",
|
||||
|
||||
loginExpired: "登录已过期!,请重新登录!",
|
||||
pleaseLogin: "请先登录!",
|
||||
}
|
||||
};
|
||||
|
11
src/router/modules/landing.ts
Normal file
11
src/router/modules/landing.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export default [
|
||||
{
|
||||
path: '/',
|
||||
name: 'index',
|
||||
component: () => import('@/views/Landing/LandingPage.vue'),
|
||||
meta: {
|
||||
requiresAuth: false,
|
||||
title: '首页'
|
||||
}
|
||||
}
|
||||
];
|
@@ -7,5 +7,23 @@ export default [
|
||||
requiresAuth: false,
|
||||
title: '登录页'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/qrlogin',
|
||||
name: 'qrlogin',
|
||||
component: () => import('@/views/QRLogin/QRLogin.vue'),
|
||||
meta: {
|
||||
requiresAuth: false,
|
||||
title: '扫码登录'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/resetpass',
|
||||
name: 'resetpass',
|
||||
component: () => import('@/views/Forget/ForgetPage.vue'),
|
||||
meta: {
|
||||
requiresAuth: false,
|
||||
title: '重置密码'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
@@ -1,11 +0,0 @@
|
||||
export default [
|
||||
{
|
||||
path: '/qrlogin',
|
||||
name: 'qrlogin',
|
||||
component: () => import('@/views/QRLogin/QRLogin.vue'),
|
||||
meta: {
|
||||
requiresAuth: false,
|
||||
title: '扫码登录'
|
||||
}
|
||||
}
|
||||
];
|
@@ -1,11 +0,0 @@
|
||||
export default [
|
||||
{
|
||||
path: '/resetpass',
|
||||
name: 'resetpass',
|
||||
component: () => import('@/views/Forget/ForgetPage.vue'),
|
||||
meta: {
|
||||
requiresAuth: false,
|
||||
title: '重置密码'
|
||||
}
|
||||
}
|
||||
];
|
@@ -4,7 +4,7 @@ export default [
|
||||
name: 'test',
|
||||
component: () => import('@/views/TestTheme.vue'),
|
||||
meta: {
|
||||
requiresAuth: false,
|
||||
requiresAuth: true,
|
||||
title: '测试'
|
||||
}
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@ export default [
|
||||
name: 'test2',
|
||||
component: () => import('@/views/TestI18n.vue'),
|
||||
meta: {
|
||||
requiresAuth: false,
|
||||
requiresAuth: true,
|
||||
title: '测试'
|
||||
}
|
||||
}
|
||||
|
@@ -6,17 +6,15 @@ import test from "@/router/modules/test.ts";
|
||||
import test2 from "@/router/modules/testI18n.ts";
|
||||
import useStore from "@/store";
|
||||
import {message} from "ant-design-vue";
|
||||
import {close, start} from '@/utils/nprogress/nprogress.ts';
|
||||
import qrlogin from "@/router/modules/qrlogin.ts";
|
||||
import resetpass from "@/router/modules/resetpass.ts";
|
||||
import {close, start} from '@/components/Nprogress/nprogress.ts';
|
||||
import notFound from "@/router/modules/notFound.ts";
|
||||
import landing from "@/router/modules/landing.ts";
|
||||
|
||||
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
...login,
|
||||
...qrlogin,
|
||||
...resetpass,
|
||||
...notFound,
|
||||
...landing,
|
||||
...test,
|
||||
...test2,
|
||||
{
|
||||
@@ -35,14 +33,14 @@ router.beforeEach((to, from, next) => {
|
||||
start();
|
||||
if (to.meta.requiresAuth) {
|
||||
const user = useStore().user;
|
||||
const token: string | undefined = user.getUser()?.token;
|
||||
const userId: string | undefined = user.getUser()?.userId;
|
||||
if (token !== undefined && userId !== undefined) {
|
||||
const token: string | undefined = user.user.refreshToken;
|
||||
const userId: string | undefined = user.user.userId;
|
||||
if (token !== "" && userId !== "") {
|
||||
next();
|
||||
} else {
|
||||
message.warn('请先登录').then();
|
||||
next({
|
||||
path: '/',
|
||||
path: '/login',
|
||||
query: {redirect: to.fullPath}
|
||||
});
|
||||
}
|
||||
|
@@ -1,10 +1,20 @@
|
||||
import {useAuthStore} from '@/store/modules/userStore.ts';
|
||||
import {useThemeStore} from "@/store/modules/themeStore.ts";
|
||||
import {langStore} from "@/store/modules/langStore.ts";
|
||||
import {useAuthSessionStore} from "@/store/modules/userSessionStore.ts";
|
||||
|
||||
export default function useStore() {
|
||||
// 是否自动登录 默认自动化登录
|
||||
function isAutoLogin() {
|
||||
const result: string | null = localStorage.getItem('auto_login');
|
||||
if (result) {
|
||||
return result === 'true';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return {
|
||||
user: useAuthStore(),
|
||||
user: isAutoLogin() ? useAuthStore() : useAuthSessionStore(), // 自动登录时使用 useAuthStore,否则使用 useAuthSessionStore
|
||||
theme: useThemeStore(),
|
||||
lang: langStore()
|
||||
};
|
||||
|
27
src/store/modules/userSessionStore.ts
Normal file
27
src/store/modules/userSessionStore.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import {defineStore} from 'pinia';
|
||||
import {reactive} from 'vue';
|
||||
|
||||
|
||||
export const useAuthSessionStore = defineStore(
|
||||
'user',
|
||||
() => {
|
||||
const user: any = reactive({
|
||||
accessToken: '',
|
||||
userId: '',
|
||||
refreshToken: '',
|
||||
expiresAt: 0,
|
||||
});
|
||||
|
||||
return {
|
||||
user,
|
||||
};
|
||||
},
|
||||
{
|
||||
// 开启数据持久化
|
||||
persist: {
|
||||
key: 'user',
|
||||
storage: sessionStorage,
|
||||
paths: ['user'],
|
||||
}
|
||||
}
|
||||
);
|
1
src/types/user.d.ts
vendored
1
src/types/user.d.ts
vendored
@@ -1,4 +1,3 @@
|
||||
|
||||
export interface AccountLogin {
|
||||
account?: string
|
||||
password?: string;
|
||||
|
@@ -22,25 +22,18 @@ const {onAuthRequired, onResponseRefreshToken} = createServerTokenAuthentication
|
||||
|
||||
// 当token过期时触发,在此函数中触发刷新token
|
||||
handler: async () => {
|
||||
try {
|
||||
const user = useStore().user;
|
||||
const res: any = await refreshToken(user.user?.refreshToken || '');
|
||||
if (res.code === 0 && res.data) {
|
||||
const {uid, access_token, refresh_token, expires_at} = res.data;
|
||||
user.user.userId = uid;
|
||||
user.user.accessToken = access_token;
|
||||
user.user.refreshToken = refresh_token;
|
||||
user.user.expiresAt = expires_at;
|
||||
}
|
||||
// else {
|
||||
// message.error(res.message);
|
||||
// await router.push('/login');
|
||||
// }
|
||||
} catch (error) {
|
||||
// token刷新失败,跳转回登录页
|
||||
message.error(i18n.global.t('error.authTokenError')).then();
|
||||
// 刷新token
|
||||
const user = useStore().user;
|
||||
const res: any = await refreshToken(user.user?.refreshToken || '');
|
||||
if (res.code === 0 && res.data) {
|
||||
const {access_token, refresh_token, uid} = res.data;
|
||||
user.user.accessToken = access_token;
|
||||
user.user.refreshToken = refresh_token;
|
||||
user.user.uid = uid;
|
||||
} else {
|
||||
message.error(i18n.global.t('error.loginExpired'));
|
||||
localStorage.removeItem('user');
|
||||
await router.push('/login');
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
17
src/views/Landing/LandingPage.vue
Normal file
17
src/views/Landing/LandingPage.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<script lang="ts">
|
||||
import {defineComponent} from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: "LandingPage"
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1>Welcome to our website!</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -54,7 +54,10 @@
|
||||
</AFormItem>
|
||||
<AFormItem>
|
||||
<AFlex :vertical="false" justify="space-between">
|
||||
<ACheckbox>{{ t("login.autoLogin") }}</ACheckbox>
|
||||
<ACheckbox @change="(e: any)=>{
|
||||
autoLoginChang(e.target.checked);
|
||||
}" v-model:checked="autoLoginChecked">{{ t("login.autoLogin") }}
|
||||
</ACheckbox>
|
||||
</AFlex>
|
||||
|
||||
</AFormItem>
|
||||
@@ -100,7 +103,10 @@
|
||||
</AFormItem>
|
||||
<AFormItem>
|
||||
<AFlex :vertical="false" justify="space-between">
|
||||
<ACheckbox>{{ t("login.autoLogin") }}</ACheckbox>
|
||||
<ACheckbox v-model:checked="autoLoginChecked" @change="(e: any)=>{
|
||||
autoLoginChang(e.target.checked);
|
||||
}">{{ t("login.autoLogin") }}
|
||||
</ACheckbox>
|
||||
<a @click="()=>{
|
||||
router.push('/resetpass')
|
||||
}">{{ t("login.forgotPassword") }}</a>
|
||||
@@ -162,6 +168,7 @@ const phoneLoginFormRef = ref<any>();
|
||||
const showRotateCaptcha = ref<boolean>(false);
|
||||
const captchaData = reactive({angle: 0, image: "", thumb: "", key: ""});
|
||||
const captchaErrorCount = ref<number>(0);
|
||||
const autoLoginChecked = ref<boolean>(localStorage.getItem('auto_login') === 'true');
|
||||
const rotateEvent = {
|
||||
confirm: (angle: number) => {
|
||||
checkCaptcha(angle);
|
||||
@@ -209,7 +216,7 @@ const rules: Record<string, Rule[]> = {
|
||||
captcha: [
|
||||
{
|
||||
required: true, message: t('login.captchaValidate'), trigger: 'change'
|
||||
}
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
@@ -379,6 +386,10 @@ async function sendMessageByPhone(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function autoLoginChang(checkedValue: boolean) {
|
||||
return localStorage.setItem('auto_login', String(checkedValue));
|
||||
}
|
||||
</script>
|
||||
<style src="./index.scss" scoped>
|
||||
@import "@/assets/styles/global.scss";
|
||||
|
Reference in New Issue
Block a user