Compare commits

...

3 Commits

Author SHA1 Message Date
Archer
a997f277ad 4.8.16 test (#3437)
* perf: auto login code

* perf: invoice phone
2024-12-19 14:52:19 +08:00
papapatrick
1f4e5f6d71 perf: add contact phone number in invoice header (#3427) 2024-12-19 14:01:07 +08:00
heheer
ce2e926d76 fix: add sso auto login config (#3436) 2024-12-19 13:33:56 +08:00
11 changed files with 158 additions and 106 deletions

View File

@@ -58,6 +58,7 @@ export type FastGPTFeConfigsType = {
icon?: string; icon?: string;
title?: string; title?: string;
url?: string; url?: string;
autoLogin?: boolean;
}; };
oauth?: { oauth?: {
github?: string; github?: string;

View File

@@ -101,6 +101,7 @@ export type TeamInvoiceHeaderType = {
bankName?: string; bankName?: string;
bankAccount?: string; bankAccount?: string;
needSpecialInvoice: boolean; needSpecialInvoice: boolean;
contactPhone: string;
emailAddress: string; emailAddress: string;
}; };

View File

@@ -9,6 +9,8 @@
"company_phone": "Company phone number", "company_phone": "Company phone number",
"completed": "Completed", "completed": "Completed",
"confirm": "confirm", "confirm": "confirm",
"contact_phone": "Contact phone number",
"contact_phone_void": "Contact phone number format error",
"default_header": "Default header", "default_header": "Default header",
"detail": "Details", "detail": "Details",
"email_address": "Email address", "email_address": "Email address",
@@ -43,9 +45,11 @@
"support_wallet_bill_tag_invoice": "bill invoice", "support_wallet_bill_tag_invoice": "bill invoice",
"support_wallet_invoicing": "Invoicing", "support_wallet_invoicing": "Invoicing",
"time": "time", "time": "time",
"total_amount": "lump sum",
"type": "type", "type": "type",
"unit_code": "unified credit code", "unit_code": "unified credit code",
"unit_code_void": "Unified credit code format error",
"update": "renew", "update": "renew",
"yes": "yes", "yes": "yes",
"yuan": "¥{{amount}}" "yuan": "¥{{amount}}"
} }

View File

@@ -9,6 +9,8 @@
"company_phone": "公司电话", "company_phone": "公司电话",
"completed": "已完成", "completed": "已完成",
"confirm": "确认", "confirm": "确认",
"contact_phone": "联系电话",
"contact_phone_void": "联系电话格式错误",
"default_header": "默认抬头", "default_header": "默认抬头",
"detail": "详情", "detail": "详情",
"email_address": "邮箱地址", "email_address": "邮箱地址",
@@ -43,8 +45,10 @@
"support_wallet_bill_tag_invoice": "账单发票", "support_wallet_bill_tag_invoice": "账单发票",
"support_wallet_invoicing": "开票", "support_wallet_invoicing": "开票",
"time": "时间", "time": "时间",
"total_amount": "总金额",
"type": "类型", "type": "类型",
"unit_code": "统一信用代码", "unit_code": "统一信用代码",
"unit_code_void": "统一信用代码格式错误",
"update": "更新", "update": "更新",
"yes": "是", "yes": "是",
"yuan": "{{amount}}元" "yuan": "{{amount}}元"

View File

@@ -9,6 +9,8 @@
"company_phone": "公司電話", "company_phone": "公司電話",
"completed": "已完成", "completed": "已完成",
"confirm": "確認", "confirm": "確認",
"contact_phone": "聯絡電話",
"contact_phone_void": "聯絡電話格式錯誤",
"default_header": "預設抬頭", "default_header": "預設抬頭",
"detail": "詳情", "detail": "詳情",
"email_address": "郵件地址", "email_address": "郵件地址",
@@ -43,9 +45,11 @@
"support_wallet_bill_tag_invoice": "帳單發票", "support_wallet_bill_tag_invoice": "帳單發票",
"support_wallet_invoicing": "開票", "support_wallet_invoicing": "開票",
"time": "時間", "time": "時間",
"total_amount": "總金額",
"type": "類型", "type": "類型",
"unit_code": "統一信用代碼", "unit_code": "統一信用代碼",
"unit_code_void": "統一信用代碼格式錯誤",
"update": "更新", "update": "更新",
"yes": "是", "yes": "是",
"yuan": "{{amount}}元" "yuan": "{{amount}}元"
} }

View File

@@ -841,6 +841,7 @@
"dataset.Create Folder": "建立資料夾", "dataset.Create Folder": "建立資料夾",
"dataset.Create manual collection": "建立手動資料集", "dataset.Create manual collection": "建立手動資料集",
"dataset.Delete Dataset Error": "刪除知識庫錯誤", "dataset.Delete Dataset Error": "刪除知識庫錯誤",
"dataset.Edit API Service": "編輯 API 檔案介面",
"dataset.Edit Folder": "編輯資料夾", "dataset.Edit Folder": "編輯資料夾",
"dataset.Edit Info": "編輯資訊", "dataset.Edit Info": "編輯資訊",
"dataset.Export": "匯出", "dataset.Export": "匯出",

View File

@@ -8,6 +8,8 @@ import {
Button, Button,
Checkbox, Checkbox,
Flex, Flex,
ModalBody,
ModalFooter,
Table, Table,
TableContainer, TableContainer,
Tbody, Tbody,
@@ -97,6 +99,7 @@ const ApplyInvoiceModal = ({ onClose }: { onClose: () => void }) => {
bankName: '', bankName: '',
bankAccount: '', bankAccount: '',
needSpecialInvoice: false, needSpecialInvoice: false,
contactPhone: '',
emailAddress: '' emailAddress: ''
} }
}); });
@@ -117,19 +120,69 @@ const ApplyInvoiceModal = ({ onClose }: { onClose: () => void }) => {
isOpen={true} isOpen={true}
isCentered isCentered
iconSrc="/imgs/modal/invoice.svg" iconSrc="/imgs/modal/invoice.svg"
minHeight={'42.25rem'}
w={'43rem'} w={'43rem'}
onClose={onClose} onClose={onClose}
isLoading={isLoading} isLoading={isLoading}
title={t('account_bill:support_wallet_apply_invoice')} title={t('account_bill:support_wallet_apply_invoice')}
> >
{!isOpenSettleModal ? ( {isOpenSettleModal ? (
<Box px={['1.6rem', '3.25rem']} py={['1rem', '2rem']}> <>
<Box fontWeight={500} fontSize={'1rem'} pb={'0.75rem'}> <ModalBody>
{t('account_bill:support_wallet_apply_invoice')} <Box w={'100%'} fontSize={'0.875rem'}>
</Box> <Flex w={'100%'} justifyContent={'space-between'}>
<Box h={'27.9rem'} overflow={'auto'}> <Box>{t('account_bill:total_amount')}</Box>
<TableContainer> <Box>{t('account_bill:yuan', { amount: formatStorePrice2Read(totalPrice) })}</Box>
</Flex>
<Box w={'100%'} py={4}>
<Divider showBorderBottom={false} />
</Box>
</Box>
<MyBox isLoading={isLoadingHeader}>
<Flex justify={'center'}>
<InvoiceHeaderSingleForm inputForm={inputForm} required />
</Flex>
</MyBox>
<Flex
align={'center'}
w={'19.8rem'}
h={'1.75rem'}
mt={4}
px={'0.75rem'}
py={'0.38rem'}
bg={'blue.50'}
borderRadius={'sm'}
color={'blue.600'}
>
<MyIcon name="infoRounded" w={'14px'} h={'14px'} />
<Box ml={2} fontSize={'0.6875rem'}>
{t('account_bill:invoice_sending_info')}
</Box>
</Flex>
</ModalBody>
<ModalFooter>
<Button variant={'outline'} mr={'0.75rem'} px="0" onClick={handleBack}>
<Flex alignItems={'center'}>
<Box px={'1.25rem'} py={'0.5rem'}>
{t('account_bill:back')}
</Box>
</Flex>
</Button>
<Button isLoading={isSubmitting} px="0" onClick={inputForm.handleSubmit(onSubmitApply)}>
<Flex alignItems={'center'}>
<Box px={'1.25rem'} py={'0.5rem'}>
{t('account_bill:confirm')}
</Box>
</Flex>
</Button>
</ModalFooter>
</>
) : (
<>
<ModalBody>
<Box fontWeight={500} fontSize={'1rem'} pb={'0.75rem'}>
{t('account_bill:support_wallet_apply_invoice')}
</Box>
<TableContainer minH={'50vh'}>
<Table> <Table>
<Thead> <Thead>
<Tr> <Tr>
@@ -201,8 +254,8 @@ const ApplyInvoiceModal = ({ onClose }: { onClose: () => void }) => {
</Flex> </Flex>
)} )}
</TableContainer> </TableContainer>
</Box> </ModalBody>
<Flex pt={'2.5rem'} justify={'flex-end'}> <ModalFooter>
<Button <Button
variant={'primary'} variant={'primary'}
px="0" px="0"
@@ -220,57 +273,8 @@ const ApplyInvoiceModal = ({ onClose }: { onClose: () => void }) => {
</Box> </Box>
</Flex> </Flex>
</Button> </Button>
</Flex> </ModalFooter>
</Box> </>
) : (
<Box px={['1.6rem', '3.25rem']} py={['1rem', '2rem']}>
<Box w={'100%'} fontSize={'0.875rem'}>
<Flex w={'100%'} justifyContent={'space-between'}>
<Box>{t('account_bill:support_wallet_amount')}</Box>
<Box>{t('account_bill:yuan', { amount: formatStorePrice2Read(totalPrice) })}</Box>
</Flex>
<Box w={'100%'} py={4}>
<Divider showBorderBottom={false} />
</Box>
</Box>
<MyBox isLoading={isLoadingHeader}>
<Flex justify={'center'}>
<InvoiceHeaderSingleForm inputForm={inputForm} />
</Flex>
</MyBox>
<Flex
align={'center'}
w={'19.8rem'}
h={'1.75rem'}
mt={4}
px={'0.75rem'}
py={'0.38rem'}
bg={'blue.50'}
borderRadius={'sm'}
color={'blue.600'}
>
<MyIcon name="infoRounded" w={'14px'} h={'14px'} />
<Box ml={2} fontSize={'0.6875rem'}>
{t('account_bill:invoice_sending_info')}
</Box>
</Flex>
<Flex justify={'flex-end'} w={'100%'} pt={[3, 7]}>
<Button variant={'outline'} mr={'0.75rem'} px="0" onClick={handleBack}>
<Flex alignItems={'center'}>
<Box px={'1.25rem'} py={'0.5rem'}>
{t('account_bill:back')}
</Box>
</Flex>
</Button>
<Button isLoading={isSubmitting} px="0" onClick={inputForm.handleSubmit(onSubmitApply)}>
<Flex alignItems={'center'}>
<Box px={'1.25rem'} py={'0.5rem'}>
{t('account_bill:confirm')}
</Box>
</Flex>
</Button>
</Flex>
</Box>
)} )}
</MyModal> </MyModal>
); );

View File

@@ -9,9 +9,11 @@ import { UseFormReturn, useForm } from 'react-hook-form';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
export const InvoiceHeaderSingleForm = ({ export const InvoiceHeaderSingleForm = ({
inputForm inputForm,
required = false
}: { }: {
inputForm: UseFormReturn<TeamInvoiceHeaderType, any>; inputForm: UseFormReturn<TeamInvoiceHeaderType, any>;
required?: boolean;
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -38,11 +40,11 @@ export const InvoiceHeaderSingleForm = ({
alignItems={['flex-start', 'center']} alignItems={['flex-start', 'center']}
flexDir={['column', 'row']} flexDir={['column', 'row']}
> >
<FormLabel required>{t('account_bill:organization_name')}</FormLabel> <FormLabel required={required}>{t('account_bill:organization_name')}</FormLabel>
<Input <Input
{...styles} {...styles}
placeholder={t('account_bill:organization_name')} placeholder={t('account_bill:organization_name')}
{...register('teamName', { required: true })} {...register('teamName', { required })}
/> />
</Flex> </Flex>
<Flex <Flex
@@ -50,11 +52,13 @@ export const InvoiceHeaderSingleForm = ({
alignItems={['flex-start', 'center']} alignItems={['flex-start', 'center']}
flexDir={['column', 'row']} flexDir={['column', 'row']}
> >
<FormLabel required>{t('account_bill:unit_code')}</FormLabel> <FormLabel required={required}>{t('account_bill:unit_code')}</FormLabel>
<Input <Input
{...styles} {...styles}
placeholder={t('account_bill:unit_code')} placeholder={t('account_bill:unit_code')}
{...register('unifiedCreditCode', { required: true })} {...register('unifiedCreditCode', {
required
})}
/> />
</Flex> </Flex>
<Flex <Flex
@@ -62,11 +66,13 @@ export const InvoiceHeaderSingleForm = ({
alignItems={['flex-start', 'center']} alignItems={['flex-start', 'center']}
flexDir={['column', 'row']} flexDir={['column', 'row']}
> >
<FormLabel required={!!needSpecialInvoice}>{t('account_bill:company_address')}</FormLabel> <FormLabel required={!!needSpecialInvoice && required}>
{t('account_bill:company_address')}
</FormLabel>
<Input <Input
{...styles} {...styles}
placeholder={t('account_bill:company_address')} placeholder={t('account_bill:company_address')}
{...register('companyAddress', { required: !!needSpecialInvoice })} {...register('companyAddress', { required: !!needSpecialInvoice && required })}
/> />
</Flex> </Flex>
<Flex <Flex
@@ -74,11 +80,13 @@ export const InvoiceHeaderSingleForm = ({
alignItems={['flex-start', 'center']} alignItems={['flex-start', 'center']}
flexDir={['column', 'row']} flexDir={['column', 'row']}
> >
<FormLabel required={!!needSpecialInvoice}>{t('account_bill:company_phone')}</FormLabel> <FormLabel required={!!needSpecialInvoice && required}>
{t('account_bill:company_phone')}
</FormLabel>
<Input <Input
{...styles} {...styles}
placeholder={t('account_bill:company_phone')} placeholder={t('account_bill:company_phone')}
{...register('companyPhone', { required: !!needSpecialInvoice })} {...register('companyPhone', { required: !!needSpecialInvoice && required })}
/> />
</Flex> </Flex>
<Flex <Flex
@@ -86,11 +94,13 @@ export const InvoiceHeaderSingleForm = ({
alignItems={['flex-start', 'center']} alignItems={['flex-start', 'center']}
flexDir={['column', 'row']} flexDir={['column', 'row']}
> >
<FormLabel required={!!needSpecialInvoice}>{t('account_bill:bank_name')}</FormLabel> <FormLabel required={!!needSpecialInvoice && required}>
{t('account_bill:bank_name')}
</FormLabel>
<Input <Input
{...styles} {...styles}
placeholder={t('account_bill:bank_name')} placeholder={t('account_bill:bank_name')}
{...register('bankName', { required: !!needSpecialInvoice })} {...register('bankName', { required: !!needSpecialInvoice && required })}
/> />
</Flex> </Flex>
<Flex <Flex
@@ -98,11 +108,13 @@ export const InvoiceHeaderSingleForm = ({
alignItems={['flex-start', 'center']} alignItems={['flex-start', 'center']}
flexDir={['column', 'row']} flexDir={['column', 'row']}
> >
<FormLabel required={!!needSpecialInvoice}>{t('account_bill:bank_account')}</FormLabel> <FormLabel required={!!needSpecialInvoice && required}>
{t('account_bill:bank_account')}
</FormLabel>
<Input <Input
{...styles} {...styles}
placeholder={t('account_bill:bank_account')} placeholder={t('account_bill:bank_account')}
{...register('bankAccount', { required: !!needSpecialInvoice })} {...register('bankAccount', { required: !!needSpecialInvoice && required })}
/> />
</Flex> </Flex>
<Flex <Flex
@@ -110,7 +122,7 @@ export const InvoiceHeaderSingleForm = ({
alignItems={['flex-start', 'center']} alignItems={['flex-start', 'center']}
flexDir={['column', 'row']} flexDir={['column', 'row']}
> >
<FormLabel required>{t('account_bill:need_special_invoice')}</FormLabel> <FormLabel required={required}>{t('account_bill:need_special_invoice')}</FormLabel>
{/* @ts-ignore */} {/* @ts-ignore */}
<RadioGroup <RadioGroup
value={`${needSpecialInvoice}`} value={`${needSpecialInvoice}`}
@@ -137,12 +149,30 @@ export const InvoiceHeaderSingleForm = ({
alignItems={['flex-start', 'center']} alignItems={['flex-start', 'center']}
flexDir={['column', 'row']} flexDir={['column', 'row']}
> >
<FormLabel required>{t('account_bill:email_address')}</FormLabel> <FormLabel required={required}>{t('account_bill:contact_phone')}</FormLabel>
<Input
{...styles}
placeholder={t('account_bill:contact_phone')}
{...register('contactPhone', {
required,
pattern: {
value: /^[1]{1}[0-9]{10}$/,
message: t('account_bill:contact_phone_void')
}
})}
/>
</Flex>
<Flex
justify={'space-between'}
alignItems={['flex-start', 'center']}
flexDir={['column', 'row']}
>
<FormLabel required={required}>{t('account_bill:email_address')}</FormLabel>
<Input <Input
{...styles} {...styles}
placeholder={t('account_bill:email_address')} placeholder={t('account_bill:email_address')}
{...register('emailAddress', { {...register('emailAddress', {
required: true, required,
pattern: { pattern: {
value: /(^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$)/, value: /(^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$)/,
message: t('user:password.email_phone_error') message: t('user:password.email_phone_error')
@@ -165,7 +195,8 @@ const InvoiceHeaderForm = () => {
bankName: '', bankName: '',
bankAccount: '', bankAccount: '',
needSpecialInvoice: false, needSpecialInvoice: false,
emailAddress: '' emailAddress: '',
contactPhone: ''
} }
}); });

View File

@@ -163,6 +163,7 @@ function InvoiceDetailModal({
label={t('account_bill:need_special_invoice')} label={t('account_bill:need_special_invoice')}
value={invoice.needSpecialInvoice ? t('account_bill:yes') : t('account_bill:no')} value={invoice.needSpecialInvoice ? t('account_bill:yes') : t('account_bill:no')}
/> />
<LabelItem label={t('account_bill:contact_phone')} value={invoice.contactPhone} />
<LabelItem label={t('account_bill:email_address')} value={invoice.emailAddress} /> <LabelItem label={t('account_bill:email_address')} value={invoice.emailAddress} />
</Flex> </Flex>
</ModalBody> </ModalBody>

View File

@@ -6,7 +6,7 @@ import { OAuthEnum } from '@fastgpt/global/support/user/constant';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import { customAlphabet } from 'nanoid'; import { customAlphabet } from 'nanoid';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { Dispatch, useMemo, useRef } from 'react'; import { Dispatch, useCallback, useEffect, useMemo, useRef } from 'react';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import I18nLngSelector from '@/components/Select/I18nLngSelector'; import I18nLngSelector from '@/components/Select/I18nLngSelector';
import { useSystem } from '@fastgpt/web/hooks/useSystem'; import { useSystem } from '@fastgpt/web/hooks/useSystem';
@@ -23,7 +23,6 @@ interface Props {
const FormLayout = ({ children, setPageType, pageType }: Props) => { const FormLayout = ({ children, setPageType, pageType }: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const router = useRouter(); const router = useRouter();
const { toast } = useToast();
const { setLoginStore, feConfigs } = useSystemStore(); const { setLoginStore, feConfigs } = useSystemStore();
const { lastRoute = '/app/list' } = router.query as { lastRoute: string }; const { lastRoute = '/app/list' } = router.query as { lastRoute: string };
@@ -100,6 +99,24 @@ const FormLayout = ({ children, setPageType, pageType }: Props) => {
[feConfigs?.sso?.url, oAuthList.length] [feConfigs?.sso?.url, oAuthList.length]
); );
const onClickSso = useCallback(() => {
if (!feConfigs?.sso?.url) return;
setLoginStore({
provider: OAuthEnum.sso,
lastRoute,
state: state.current
});
const url = `${feConfigs.sso.url}/login/oauth/authorize?redirect_uri=${encodeURIComponent(redirectUri)}&state=${state.current}`;
window.open(url, '_self');
}, [feConfigs?.sso?.url, lastRoute, redirectUri, setLoginStore]);
useEffect(() => {
if (feConfigs?.sso?.autoLogin) {
onClickSso();
}
}, [feConfigs?.sso?.autoLogin]);
return ( return (
<Flex flexDirection={'column'} h={'100%'}> <Flex flexDirection={'column'} h={'100%'}>
<Flex alignItems={'center'} justify={'space-between'}> <Flex alignItems={'center'} justify={'space-between'}>
@@ -166,24 +183,7 @@ const FormLayout = ({ children, setPageType, pageType }: Props) => {
h={'40px'} h={'40px'}
borderRadius={'sm'} borderRadius={'sm'}
leftIcon={<MyImage alt="" src={feConfigs.sso.icon as any} w="20px" />} leftIcon={<MyImage alt="" src={feConfigs.sso.icon as any} w="20px" />}
onClick={() => { onClick={onClickSso}
const url = feConfigs.sso?.url;
if (!url) {
toast({
title: 'SSO URL is not set',
status: 'error'
});
return;
}
setLoginStore({
provider: OAuthEnum.sso,
lastRoute,
state: state.current
});
const formatUrl = `${url}/login/oauth/authorize?redirect_uri=${encodeURIComponent(redirectUri)}&state=${state.current}`;
window.open(formatUrl, '_self');
}}
> >
{feConfigs.sso.title} {feConfigs.sso.title}
</Button> </Button>

View File

@@ -1,4 +1,4 @@
import React, { useState, useCallback, useEffect } from 'react'; import React, { useState, useCallback, useEffect, useMemo } from 'react';
import { import {
Box, Box,
Button, Button,
@@ -73,7 +73,7 @@ const Login = ({ ChineseRedirectUrl }: { ChineseRedirectUrl: string }) => {
[lastRoute, router, setUserInfo] [lastRoute, router, setUserInfo]
); );
function DynamicComponent({ type }: { type: `${LoginPageTypeEnum}` }) { const DynamicComponent = useMemo(() => {
const TypeMap = { const TypeMap = {
[LoginPageTypeEnum.passwordLogin]: LoginForm, [LoginPageTypeEnum.passwordLogin]: LoginForm,
[LoginPageTypeEnum.register]: RegisterForm, [LoginPageTypeEnum.register]: RegisterForm,
@@ -81,10 +81,11 @@ const Login = ({ ChineseRedirectUrl }: { ChineseRedirectUrl: string }) => {
[LoginPageTypeEnum.wechat]: WechatForm [LoginPageTypeEnum.wechat]: WechatForm
}; };
const Component = TypeMap[type]; // @ts-ignore
const Component = TypeMap[pageType];
return <Component setPageType={setPageType} loginSuccess={loginSuccess} />; return <Component setPageType={setPageType} loginSuccess={loginSuccess} />;
} }, [pageType, loginSuccess]);
/* default login type */ /* default login type */
useEffect(() => { useEffect(() => {
@@ -99,7 +100,7 @@ const Login = ({ ChineseRedirectUrl }: { ChineseRedirectUrl: string }) => {
// init store // init store
setLastChatAppId(''); setLastChatAppId('');
}, [feConfigs.oauth]); }, [feConfigs?.oauth, setLastChatAppId]);
const { const {
isOpen: isOpenRedirect, isOpen: isOpenRedirect,
@@ -171,7 +172,7 @@ const Login = ({ ChineseRedirectUrl }: { ChineseRedirectUrl: string }) => {
> >
<Box w={['100%', '380px']} flex={'1 0 0'}> <Box w={['100%', '380px']} flex={'1 0 0'}>
{pageType ? ( {pageType ? (
<DynamicComponent type={pageType} /> DynamicComponent
) : ( ) : (
<Center w={'full'} h={'full'} position={'relative'}> <Center w={'full'} h={'full'} position={'relative'}>
<Loading fixed={false} /> <Loading fixed={false} />