perf: improve the logic related to login expiration
This commit is contained in:
@@ -16,6 +16,7 @@ const defaultPreferences: Preferences = {
|
||||
isMobile: false,
|
||||
layout: 'sidebar-nav',
|
||||
locale: 'zh-CN',
|
||||
loginExpiredMode: 'page',
|
||||
name: 'Vben Admin Pro',
|
||||
},
|
||||
breadcrumb: {
|
||||
|
||||
@@ -7,6 +7,12 @@ import type {
|
||||
ThemeModeType,
|
||||
} from '@vben-core/typings';
|
||||
|
||||
/**
|
||||
* 登录过期模式
|
||||
* 'modal' 弹窗模式 | 'page' 页面模式
|
||||
*/
|
||||
type LoginExpiredModeType = 'modal' | 'page';
|
||||
|
||||
type BreadcrumbStyleType = 'background' | 'normal';
|
||||
|
||||
type AccessModeType = 'allow-all' | 'backend' | 'frontend';
|
||||
@@ -44,6 +50,8 @@ interface AppPreferences {
|
||||
layout: LayoutType;
|
||||
/** 支持的语言 */
|
||||
locale: SupportedLanguagesType;
|
||||
/** 登录过期模式 */
|
||||
loginExpiredMode: LoginExpiredModeType;
|
||||
/** 应用名 */
|
||||
name: string;
|
||||
}
|
||||
@@ -236,6 +244,7 @@ export type {
|
||||
HeaderPreferences,
|
||||
LayoutHeaderModeType,
|
||||
LayoutType,
|
||||
LoginExpiredModeType,
|
||||
LogoPreferences,
|
||||
NavigationPreferences,
|
||||
NavigationStyleType,
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@vben-core/locales": "workspace:*",
|
||||
"@vben-core/toolkit": "workspace:*",
|
||||
"axios": "^1.7.2",
|
||||
"vue-request": "^2.0.4"
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
export * from './request-client';
|
||||
export type * from './types';
|
||||
export * from './util';
|
||||
|
||||
@@ -17,16 +17,22 @@ class InterceptorManager {
|
||||
) => InternalAxiosRequestConfig | Promise<InternalAxiosRequestConfig>,
|
||||
rejected?: (error: any) => any,
|
||||
) {
|
||||
this.axiosInstance.interceptors.request.use(fulfilled, rejected);
|
||||
this.axiosInstance.interceptors.request.use(
|
||||
fulfilled,
|
||||
rejected || ((res) => res),
|
||||
);
|
||||
}
|
||||
|
||||
addResponseInterceptor(
|
||||
addResponseInterceptor<T = any>(
|
||||
fulfilled: (
|
||||
response: AxiosResponse,
|
||||
response: AxiosResponse<T>,
|
||||
) => AxiosResponse | Promise<AxiosResponse>,
|
||||
rejected?: (error: any) => any,
|
||||
) {
|
||||
this.axiosInstance.interceptors.response.use(fulfilled, rejected);
|
||||
this.axiosInstance.interceptors.response.use(
|
||||
fulfilled,
|
||||
rejected || ((res) => res),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import type {
|
||||
|
||||
import type { MakeAuthorizationFn, RequestClientOptions } from './types';
|
||||
|
||||
import { $t } from '@vben-core/locales';
|
||||
import { merge } from '@vben-core/toolkit';
|
||||
|
||||
import axios from 'axios';
|
||||
@@ -19,6 +20,7 @@ import { FileUploader } from './modules/uploader';
|
||||
class RequestClient {
|
||||
private instance: AxiosInstance;
|
||||
private makeAuthorization: MakeAuthorizationFn | undefined;
|
||||
private options: RequestClientOptions;
|
||||
public addRequestInterceptor: InterceptorManager['addRequestInterceptor'];
|
||||
public addResponseInterceptor: InterceptorManager['addResponseInterceptor'];
|
||||
public download: FileDownloader['download'];
|
||||
@@ -39,6 +41,7 @@ class RequestClient {
|
||||
timeout: 10_000,
|
||||
};
|
||||
const { makeAuthorization, ...axiosConfig } = options;
|
||||
this.options = options;
|
||||
const requestConfig = merge(axiosConfig, defaultConfig);
|
||||
|
||||
this.instance = axios.create(requestConfig);
|
||||
@@ -77,24 +80,86 @@ class RequestClient {
|
||||
});
|
||||
}
|
||||
|
||||
private errorHandler(error: any) {
|
||||
return Promise.reject(error);
|
||||
private setupAuthorizationInterceptor() {
|
||||
this.addRequestInterceptor(
|
||||
(config: InternalAxiosRequestConfig) => {
|
||||
const authorization = this.makeAuthorization?.(config);
|
||||
if (authorization) {
|
||||
const { token } = authorization.tokenHandler?.() ?? {};
|
||||
config.headers[authorization.key || 'Authorization'] = token;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error: any) => Promise.reject(error),
|
||||
);
|
||||
}
|
||||
|
||||
private setupAuthorizationInterceptor() {
|
||||
this.addRequestInterceptor((config: InternalAxiosRequestConfig) => {
|
||||
const authorization = this.makeAuthorization?.(config);
|
||||
if (authorization) {
|
||||
const { token } = authorization.handler?.() ?? {};
|
||||
config.headers[authorization.key || 'Authorization'] = token;
|
||||
}
|
||||
return config;
|
||||
}, this.errorHandler);
|
||||
private setupDefaultResponseInterceptor() {
|
||||
this.addResponseInterceptor(
|
||||
(response: AxiosResponse) => {
|
||||
return response;
|
||||
},
|
||||
(error: any) => {
|
||||
if (axios.isCancel(error)) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
const err: string = error?.toString?.() ?? '';
|
||||
let errMsg = '';
|
||||
if (err?.includes('Network Error')) {
|
||||
errMsg = $t('fallback.http.networkError');
|
||||
} else if (error?.message?.includes?.('timeout')) {
|
||||
errMsg = $t('fallback.http.requestTimeout');
|
||||
}
|
||||
const { makeAuthorization, makeErrorMessage } = this.options;
|
||||
if (errMsg) {
|
||||
makeErrorMessage?.(errMsg);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
let errorMessage = error?.response?.data?.error?.message ?? '';
|
||||
const status = error?.response?.status;
|
||||
|
||||
switch (status) {
|
||||
case 400: {
|
||||
errorMessage = $t('fallback.http.badRequest');
|
||||
break;
|
||||
}
|
||||
|
||||
case 401: {
|
||||
errorMessage = $t('fallback.http.unauthorized');
|
||||
makeAuthorization?.().unAuthorizedHandler?.();
|
||||
break;
|
||||
}
|
||||
case 403: {
|
||||
errorMessage = $t('fallback.http.forbidden');
|
||||
break;
|
||||
}
|
||||
// 404请求不存在
|
||||
case 404: {
|
||||
errorMessage = $t('fallback.http.notFound');
|
||||
break;
|
||||
}
|
||||
case 408: {
|
||||
errorMessage = $t('fallback.http.requestTimeout');
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
errorMessage = $t('fallback.http.internalServerError');
|
||||
}
|
||||
}
|
||||
|
||||
makeErrorMessage?.(errorMessage);
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
private setupInterceptors() {
|
||||
// 默认拦截器
|
||||
this.setupAuthorizationInterceptor();
|
||||
this.setupDefaultResponseInterceptor();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,18 +7,41 @@ type RequestContentType =
|
||||
| 'multipart/form-data;charset=utf-8';
|
||||
|
||||
interface MakeAuthorization {
|
||||
handler: () => { refreshToken: string; token: string } | null;
|
||||
key?: string;
|
||||
tokenHandler: () => { refreshToken: string; token: string } | null;
|
||||
unAuthorizedHandler?: () => Promise<void>;
|
||||
}
|
||||
|
||||
type MakeAuthorizationFn = (
|
||||
config?: InternalAxiosRequestConfig,
|
||||
) => MakeAuthorization;
|
||||
|
||||
type ErrorMessageFn = (message: string) => void;
|
||||
|
||||
interface RequestClientOptions extends CreateAxiosDefaults {
|
||||
/**
|
||||
* 用于生成Authorization
|
||||
*/
|
||||
makeAuthorization?: MakeAuthorizationFn;
|
||||
/**
|
||||
* 用于生成错误消息
|
||||
*/
|
||||
makeErrorMessage?: ErrorMessageFn;
|
||||
}
|
||||
export type { MakeAuthorizationFn, RequestClientOptions, RequestContentType };
|
||||
|
||||
interface HttpResponse<T = any> {
|
||||
/**
|
||||
* 0 表示成功 其他表示失败
|
||||
* 0 means success, others means fail
|
||||
*/
|
||||
code: number;
|
||||
data: T;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export type {
|
||||
HttpResponse,
|
||||
MakeAuthorizationFn,
|
||||
RequestClientOptions,
|
||||
RequestContentType,
|
||||
};
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import axios from 'axios';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { isCancelError } from './util';
|
||||
|
||||
describe('isCancelError', () => {
|
||||
const source = axios.CancelToken.source();
|
||||
source.cancel('Operation canceled by the user.');
|
||||
|
||||
it('should detect cancellation', () => {
|
||||
const error = new axios.Cancel('Operation canceled by the user.');
|
||||
|
||||
const result = isCancelError(error);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should not detect cancellation on regular errors', () => {
|
||||
const error = new Error('Regular error');
|
||||
|
||||
const result = isCancelError(error);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -1,7 +0,0 @@
|
||||
import axios from 'axios';
|
||||
|
||||
function isCancelError(error: any) {
|
||||
return axios.isCancel(error);
|
||||
}
|
||||
|
||||
export { isCancelError };
|
||||
@@ -39,7 +39,16 @@
|
||||
"offline": "Offline Page",
|
||||
"offlineError": "Oops! Network Error",
|
||||
"offlineErrorDesc": "Sorry, can't connect to the internet. Check your connection.",
|
||||
"coming-soon": "Coming Soon"
|
||||
"comingSoon": "Coming Soon",
|
||||
"http": {
|
||||
"requestTimeout": "The request timed out. Please try again later.",
|
||||
"networkError": "A network error occurred. Please check your internet connection and try again.",
|
||||
"badRequest": "Bad Request. Please check your input and try again.",
|
||||
"unauthorized": "Unauthorized. Please log in to continue.",
|
||||
"forbidden": "Forbidden. You do not have permission to access this resource.",
|
||||
"notFound": "Not Found. The requested resource could not be found.",
|
||||
"internalServerError": "Internal Server Error. Something went wrong on our end. Please try again later."
|
||||
}
|
||||
},
|
||||
"widgets": {
|
||||
"document": "Document",
|
||||
@@ -104,6 +113,8 @@
|
||||
"sendCode": "Get Security code",
|
||||
"sendText": "Resend in {0}s",
|
||||
"thirdPartyLogin": "Or continue with",
|
||||
"loginAgainTitle": "Please Log In Again",
|
||||
"loginAgainSubTitle": "Your login session has expired. Please log in again to continue.",
|
||||
"layout": {
|
||||
"center": "Align Center",
|
||||
"alignLeft": "Align Left",
|
||||
|
||||
@@ -39,7 +39,16 @@
|
||||
"offline": "离线页面",
|
||||
"offlineError": "哎呀!网络错误",
|
||||
"offlineErrorDesc": "抱歉,无法连接到互联网,请检查您的网络连接并重试。",
|
||||
"coming-soon": "即将推出"
|
||||
"comingSoon": "即将推出",
|
||||
"http": {
|
||||
"requestTimeout": "请求超时,请稍后再试。",
|
||||
"networkError": "网络异常,请检查您的网络连接后重试。",
|
||||
"badRequest": "请求错误。请检查您的输入并重试。",
|
||||
"unauthorized": "未授权。请登录以继续。",
|
||||
"forbidden": "禁止访问, 您没有权限访问此资源。",
|
||||
"notFound": "未找到, 请求的资源不存在。",
|
||||
"internalServerError": "内部服务器错误,请稍后再试。"
|
||||
}
|
||||
},
|
||||
"widgets": {
|
||||
"document": "文档",
|
||||
@@ -104,6 +113,8 @@
|
||||
"sendCode": "获取验证码",
|
||||
"sendText": "{0}秒后重新获取",
|
||||
"thirdPartyLogin": "其他登录方式",
|
||||
"loginAgainTitle": "请重新登录",
|
||||
"loginAgainSubTitle": "您的登录状态已过期,请重新登录以继续。",
|
||||
"layout": {
|
||||
"center": "居中",
|
||||
"alignLeft": "居左",
|
||||
|
||||
@@ -14,13 +14,19 @@ import {
|
||||
useForwardPropsEmits,
|
||||
} from 'radix-vue';
|
||||
|
||||
const props = defineProps<
|
||||
{ class?: HTMLAttributes['class'] } & DialogContentProps
|
||||
>();
|
||||
const props = withDefaults(
|
||||
defineProps<
|
||||
{
|
||||
class?: HTMLAttributes['class'];
|
||||
showClose?: boolean;
|
||||
} & DialogContentProps
|
||||
>(),
|
||||
{ showClose: true },
|
||||
);
|
||||
const emits = defineEmits<{ close: [] } & DialogContentEmits>();
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props;
|
||||
const { class: _, showClose: __, ...delegated } = props;
|
||||
|
||||
return delegated;
|
||||
});
|
||||
@@ -46,6 +52,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
<slot></slot>
|
||||
|
||||
<DialogClose
|
||||
v-if="showClose"
|
||||
class="data-[state=open]:bg-accent data-[state=open]:text-muted-foreground hover:bg-accent hover:text-accent-foreground text-foreground/80 flex-center absolute right-3 top-3 h-6 w-6 rounded-full px-1 text-lg opacity-70 transition-opacity hover:opacity-100 focus:outline-none disabled:pointer-events-none"
|
||||
@click="() => emits('close')"
|
||||
>
|
||||
|
||||
@@ -2,6 +2,7 @@ import './styles/index.css';
|
||||
|
||||
export * from './components';
|
||||
export {
|
||||
VisuallyHidden,
|
||||
useEmitAsProps,
|
||||
useForwardExpose,
|
||||
useForwardProps,
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
"@vben-core/stores": "workspace:*",
|
||||
"@vben-core/tabs-ui": "workspace:*",
|
||||
"@vben-core/toolkit": "workspace:*",
|
||||
"@vben/universal-ui": "workspace:*",
|
||||
"@vueuse/core": "^10.11.0",
|
||||
"vue": "^3.4.31",
|
||||
"vue-router": "^4.4.0"
|
||||
|
||||
@@ -4,7 +4,6 @@ export { default as CozeAssistant } from './coze-assistant.vue';
|
||||
export * from './global-search';
|
||||
export { default as LanguageToggle } from './language-toggle.vue';
|
||||
export { default as AuthenticationLayoutToggle } from './layout-toggle.vue';
|
||||
export * from './login-dialog';
|
||||
export * from './notification';
|
||||
export * from './preferences';
|
||||
export * from './theme-toggle';
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export { default as LoginDialog } from './login-dialog.vue';
|
||||
@@ -1,48 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
||||
import {
|
||||
AuthenticationLogin,
|
||||
AuthenticationProps,
|
||||
LoginAndRegisterParams,
|
||||
} from '@vben/universal-ui';
|
||||
import { Dialog, DialogContent } from '@vben-core/shadcn-ui';
|
||||
|
||||
interface Props extends AuthenticationProps {
|
||||
open: boolean;
|
||||
}
|
||||
|
||||
defineOptions({
|
||||
name: 'LoginDialog',
|
||||
});
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
open: false,
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
login: [LoginAndRegisterParams];
|
||||
}>();
|
||||
|
||||
const loginProps = computed(() => {
|
||||
const { open: _, ...rest } = props;
|
||||
return rest;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<Dialog :open="open" class="flex items-center justify-center">
|
||||
<DialogContent
|
||||
class="top-[50%] w-full translate-y-[-50%] border-none p-0 shadow-xl sm:w-[600px] sm:rounded-2xl"
|
||||
>
|
||||
<div class="p-4">
|
||||
<AuthenticationLogin
|
||||
v-bind="loginProps"
|
||||
@submit="(e) => emit('login', e)"
|
||||
/>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
@@ -399,22 +399,22 @@ async function handleReset() {
|
||||
:disabled="!diffPreference"
|
||||
class="mx-4 w-full"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
@click="handleClearCache"
|
||||
>
|
||||
<IcRoundRestartAlt class="mr-2 size-4" />
|
||||
{{ $t('preferences.clearAndLogout') }}
|
||||
</VbenButton>
|
||||
<VbenButton
|
||||
:disabled="!diffPreference"
|
||||
class="mr-4 w-full"
|
||||
size="sm"
|
||||
variant="default"
|
||||
@click="handleCopy"
|
||||
>
|
||||
<IcRoundFolderCopy class="mr-2 size-3" />
|
||||
{{ $t('preferences.copyPreferences') }}
|
||||
</VbenButton>
|
||||
<VbenButton
|
||||
:disabled="!diffPreference"
|
||||
class="mr-4 w-full"
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
@click="handleClearCache"
|
||||
>
|
||||
<IcRoundRestartAlt class="mr-2 size-4" />
|
||||
{{ $t('preferences.clearAndLogout') }}
|
||||
</VbenButton>
|
||||
</template>
|
||||
</VbenSheet>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export { default as AuthenticationCodeLogin } from './code-login.vue';
|
||||
export { default as AuthenticationForgetPassword } from './forget-password.vue';
|
||||
export { default as AuthenticationLogin } from './login.vue';
|
||||
export { default as AuthenticationLoginExpiredModal } from './login-expired-modal.vue';
|
||||
export { default as AuthenticationQrCodeLogin } from './qrcode-login.vue';
|
||||
export { default as AuthenticationRegister } from './register.vue';
|
||||
export type {
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogTitle,
|
||||
VisuallyHidden,
|
||||
useForwardPropsEmits,
|
||||
} from '@vben-core/shadcn-ui';
|
||||
|
||||
import AuthenticationLogin from './login.vue';
|
||||
import { AuthenticationProps, LoginAndRegisterParams } from './typings';
|
||||
|
||||
interface Props extends AuthenticationProps {}
|
||||
|
||||
defineOptions({
|
||||
name: 'LoginExpiredModal',
|
||||
});
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {});
|
||||
|
||||
const emit = defineEmits<{
|
||||
submit: [LoginAndRegisterParams];
|
||||
}>();
|
||||
|
||||
const open = defineModel<boolean>('open');
|
||||
|
||||
const forwarded = useForwardPropsEmits(props, emit);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<Dialog v-model:open="open">
|
||||
<DialogContent
|
||||
:show-close="false"
|
||||
class="top-1/2 h-full w-full translate-y-[-50%] border-none p-4 py-12 text-center shadow-xl sm:w-[600px] sm:rounded-2xl md:h-[unset] md:px-14 md:pt-12"
|
||||
@escape-key-down="(e) => e.preventDefault()"
|
||||
@interact-outside="(e) => e.preventDefault()"
|
||||
>
|
||||
<VisuallyHidden>
|
||||
<DialogTitle />
|
||||
<DialogDescription />
|
||||
</VisuallyHidden>
|
||||
<AuthenticationLogin
|
||||
v-bind="forwarded"
|
||||
:show-forget-password="false"
|
||||
:show-register="false"
|
||||
:show-remember-me="false"
|
||||
:sub-title="$t('authentication.loginAgainSubTitle')"
|
||||
:title="$t('authentication.loginAgainTitle')"
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
@@ -31,7 +31,10 @@ withDefaults(defineProps<Props>(), {
|
||||
showForgetPassword: true,
|
||||
showQrcodeLogin: true,
|
||||
showRegister: true,
|
||||
showRememberMe: true,
|
||||
showThirdPartyLogin: true,
|
||||
subTitle: '',
|
||||
title: '',
|
||||
usernamePlaceholder: '',
|
||||
});
|
||||
|
||||
@@ -89,10 +92,10 @@ function handleGo(path: string) {
|
||||
<template>
|
||||
<div @keypress.enter.prevent="handleSubmit">
|
||||
<Title>
|
||||
{{ $t('authentication.welcomeBack') }} 👋🏻
|
||||
{{ title || `${$t('authentication.welcomeBack')} 👋🏻` }}
|
||||
<template #desc>
|
||||
<span class="text-muted-foreground">
|
||||
{{ $t('authentication.loginSubtitle') }}
|
||||
{{ subTitle || $t('authentication.loginSubtitle') }}
|
||||
</span>
|
||||
</template>
|
||||
</Title>
|
||||
@@ -120,7 +123,7 @@ function handleGo(path: string) {
|
||||
/>
|
||||
|
||||
<div class="mb-6 mt-4 flex justify-between">
|
||||
<div class="flex-center flex">
|
||||
<div v-if="showRememberMe" class="flex-center">
|
||||
<VbenCheckbox v-model:checked="formState.rememberMe" name="rememberMe">
|
||||
{{ $t('authentication.rememberMe') }}
|
||||
</VbenCheckbox>
|
||||
@@ -133,10 +136,6 @@ function handleGo(path: string) {
|
||||
>
|
||||
{{ $t('authentication.forgetPassword') }}
|
||||
</span>
|
||||
|
||||
<!-- <VbenButton variant="ghost" @click="handleGo('/auth/forget-password')">
|
||||
忘记密码?
|
||||
</VbenButton> -->
|
||||
</div>
|
||||
<VbenButton :loading="loading" class="w-full" @click="handleSubmit">
|
||||
{{ $t('common.login') }}
|
||||
@@ -159,14 +158,6 @@ function handleGo(path: string) {
|
||||
>
|
||||
{{ $t('authentication.qrcodeLogin') }}
|
||||
</VbenButton>
|
||||
<!-- <VbenButton
|
||||
:loading="loading"
|
||||
variant="outline"
|
||||
class="w-1/3"
|
||||
@click="handleGo('/auth/register')"
|
||||
>
|
||||
创建账号
|
||||
</VbenButton> -->
|
||||
</div>
|
||||
|
||||
<!-- 第三方登录 -->
|
||||
|
||||
@@ -3,7 +3,6 @@ interface AuthenticationProps {
|
||||
* @zh_CN 验证码登录路径
|
||||
*/
|
||||
codeLoginPath?: string;
|
||||
|
||||
/**
|
||||
* @zh_CN 忘记密码路径
|
||||
*/
|
||||
@@ -33,7 +32,6 @@ interface AuthenticationProps {
|
||||
* @zh_CN 是否显示验证码登录
|
||||
*/
|
||||
showCodeLogin?: boolean;
|
||||
|
||||
/**
|
||||
* @zh_CN 是否显示忘记密码
|
||||
*/
|
||||
@@ -49,11 +47,26 @@ interface AuthenticationProps {
|
||||
*/
|
||||
showRegister?: boolean;
|
||||
|
||||
/**
|
||||
* @zh_CN 是否显示记住账号
|
||||
*/
|
||||
showRememberMe?: boolean;
|
||||
|
||||
/**
|
||||
* @zh_CN 是否显示第三方登录
|
||||
*/
|
||||
showThirdPartyLogin?: boolean;
|
||||
|
||||
/**
|
||||
* @zh_CN 登录框子标题
|
||||
*/
|
||||
subTitle?: string;
|
||||
|
||||
/**
|
||||
* @zh_CN 登录框标题
|
||||
*/
|
||||
title?: string;
|
||||
|
||||
/**
|
||||
* @zh_CN 用户名占位符
|
||||
*/
|
||||
|
||||
@@ -52,7 +52,7 @@ const titleText = computed(() => {
|
||||
return $t('fallback.offlineError');
|
||||
}
|
||||
case 'comming-soon': {
|
||||
return $t('fallback.coming-soon');
|
||||
return $t('fallback.comingSoon');
|
||||
}
|
||||
default: {
|
||||
return '';
|
||||
|
||||
Reference in New Issue
Block a user