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