Revert "sub plan page (#885)" (#886)

This reverts commit 443ad37b6a.
This commit is contained in:
Archer
2024-02-23 17:48:15 +08:00
committed by GitHub
parent 443ad37b6a
commit fd9b6291af
246 changed files with 4281 additions and 6286 deletions

View File

@@ -7,111 +7,92 @@ import {
NumberIncrementStepper,
NumberInputField,
NumberInputStepper,
Button
Button,
useDisclosure,
ModalBody,
ModalFooter
} from '@chakra-ui/react';
import { TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type';
import { useTranslation } from 'next-i18next';
import React, { useCallback, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { formatTime2YMDHM } from '@fastgpt/global/common/string/time';
import MySelect from '@/components/Select';
import {
SubStatusEnum,
SubTypeEnum,
subSelectMap
} from '@fastgpt/global/support/wallet/sub/constants';
import { useRequest } from '@/web/common/hooks/useRequest';
import {
posCheckTeamDatasetSizeSub,
postUpdateTeamDatasetSizeSub,
putTeamDatasetSubStatus
} from '@/web/support/wallet/sub/api';
import { SubDatasetSizePreviewCheckResponse } from '@fastgpt/global/support/wallet/sub/api.d';
import { useRouter } from 'next/router';
import { useForm } from 'react-hook-form';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { getWxPayQRCode } from '@/web/support/wallet/bill/api';
import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants';
import QRCodePayModal, { type QRPayProps } from '@/components/support/wallet/QRCodePayModal';
import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { useUserStore } from '@/web/support/user/useUserStore';
import MyModal from '@/components/MyModal';
const ExtraPlan = () => {
const ExtraPlan = ({ extraDatasetSize }: { extraDatasetSize?: TeamSubSchema }) => {
const { t } = useTranslation();
const router = useRouter();
const { toast } = useToast();
const { subPlans } = useSystemStore();
const [loading, setLoading] = useState(false);
const [qrPayData, setQRPayData] = useState<QRPayProps>();
// extra dataset
const extraDatasetPrice = subPlans?.extraDatasetSize?.price || 0;
const { register: registerDatasetSize, handleSubmit: handleSubmitDatasetSize } = useForm({
defaultValues: {
datasetSize: 0,
month: 1
}
});
const onclickBuyDatasetSize = useCallback(
async ({ datasetSize, month }: { datasetSize: number; month: number }) => {
try {
const datasetSizePayAmount = datasetSize * month * extraDatasetPrice;
if (datasetSizePayAmount === 0) {
return toast({
status: 'warning',
title: '购买数量不能为0'
});
}
setLoading(true);
const [datasetSize, setDatasetSize] = useState(0);
const [isRenew, setIsRenew] = useState('false');
const router = useRouter();
const { userInfo } = useUserStore();
const res = await getWxPayQRCode({
type: BillTypeEnum.extraDatasetSub,
month,
extraDatasetSize: datasetSize
});
setQRPayData({
readPrice: res.readPrice,
codeUrl: res.codeUrl,
billId: res.billId
});
} catch (err) {
toast({
title: getErrText(err),
status: 'error'
});
}
setLoading(false);
const [confirmPayExtraDatasetSizeData, setConfirmPayExtraDatasetSizeData] =
useState<SubDatasetSizePreviewCheckResponse>();
useEffect(() => {
setDatasetSize((extraDatasetSize?.nextExtraDatasetSize || 0) / 1000);
setIsRenew(extraDatasetSize?.status === SubStatusEnum.active ? 'true' : 'false');
}, [extraDatasetSize]);
const { mutate: onUpdateExtraDatasetSizeStatus } = useRequest({
mutationFn: (e: 'true' | 'false') => {
setIsRenew(e);
return putTeamDatasetSubStatus({
status: subSelectMap[e],
type: SubTypeEnum.extraDatasetSize
});
},
[extraDatasetPrice, toast]
);
// extra ai points
const extraPointsPrice = subPlans?.extraPoints?.price || 0;
const { register: registerExtraPoints, handleSubmit: handleSubmitExtraPoints } = useForm({
defaultValues: {
points: 0,
month: 1
}
successToast: t('common.Update success'),
errorToast: t('common.error.Update error')
});
const onclickBuyExtraPoints = useCallback(
async ({ points, month }: { points: number; month: number }) => {
try {
const payAmount = points * month * extraPointsPrice;
if (payAmount === 0) {
return toast({
status: 'warning',
title: '购买数量不能为0'
});
}
setLoading(true);
const res = await getWxPayQRCode({
type: BillTypeEnum.extraPoints,
month,
extraPoints: points
});
setQRPayData({
readPrice: res.readPrice,
codeUrl: res.codeUrl,
billId: res.billId
});
} catch (err) {
toast({
title: getErrText(err),
status: 'error'
});
}
setLoading(false);
},
[extraPointsPrice, toast]
const { mutate: onClickUpdateExtraDatasetPlan, isLoading: isPayingExtraDatasetSize } = useRequest(
{
mutationFn: () => postUpdateTeamDatasetSizeSub({ size: datasetSize }),
onSuccess() {
setTimeout(() => {
router.reload();
}, 100);
},
successToast: t('common.Update success'),
errorToast: t('common.error.Update error')
}
);
const { mutate: onClickPreviewCheck, isLoading: isFetchingPreviewCheck } = useRequest({
mutationFn: () =>
posCheckTeamDatasetSizeSub({
size: datasetSize
}),
onSuccess(res: SubDatasetSizePreviewCheckResponse) {
if (!res.payForNewSub) {
onClickUpdateExtraDatasetPlan('');
return;
} else {
setConfirmPayExtraDatasetSizeData(res);
}
},
errorToast: t('common.error.Update error')
});
return (
<Flex
@@ -120,13 +101,13 @@ const ExtraPlan = () => {
alignItems={'center'}
position={'relative'}
>
<Box id={'extra-plan'} fontWeight={'bold'} fontSize={['24px', '36px']}>
<Box fontWeight={'bold'} fontSize={['24px', '36px']}>
{t('support.wallet.subscription.Extra plan')}
</Box>
<Box mt={8} mb={10} color={'myGray.500'} fontSize={'lg'}>
{t('support.wallet.subscription.Extra plan tip')}
</Box>
<Grid mt={8} gridTemplateColumns={['1fr', '1fr 1fr']} gap={5} w={['100%', 'auto']}>
<Grid mt={8} gridTemplateColumns={['1fr', '1fr']}>
<Box
bg={'rgba(255, 255, 255, 0.90)'}
px={'32px'}
@@ -135,60 +116,78 @@ const ExtraPlan = () => {
borderWidth={'1px'}
borderColor={'myGray.150'}
boxShadow={'1.5'}
w={['100%', '500px']}
>
<Flex borderBottomWidth={'1px'} borderBottomColor={'myGray.200'}>
<Flex w={['100%', '500px']} borderBottomWidth={'1px'} borderBottomColor={'myGray.200'}>
<Box flex={'1 0 0'}>
<Box fontSize={'xl'} color={'primary.600'}>
{t('support.wallet.subscription.Extra dataset size')}
</Box>
<Box mt={3} fontSize={['28px', '32px']} fontWeight={'bold'}>
{extraDatasetPrice}/1000{' '}
<Box mt={3} fontSize={['32px', '38px']} fontWeight={'bold'}>
{extraDatasetPrice}/1k{' '}
<Box ml={1} as={'span'} fontSize={'lg'} color={'myGray.600'} fontWeight={'normal'}>
/{t('common.month')}
</Box>
</Box>
</Box>
<MyIcon
display={['none', 'display']}
mt={'-30px'}
transform={'translateX(20px)'}
name={'support/bill/extraDatasetsize'}
transform={'translate(20px,-20px)'}
name={'support/pay/extraDatasetsize'}
fill={'none'}
/>
</Flex>
<Box>
<Flex mt={4}>
<MyIcon mr={2} name={'support/bill/shoppingCart'} w={'16px'} color={'primary.600'} />
</Flex>
<Flex mt={4} alignItems={'center'}>
<Box flex={['0 0 100px', '0 0 200px']}>
{t('support.wallet.subscription.Month amount')}
<Box flex={'0 0 200px'}>
{t('support.wallet.subscription.Current dataset store')}:{' '}
</Box>
<Flex alignItems={'center'} mt={1} w={'180px'} position={'relative'}>
<NumberInput size={'sm'} flex={1} step={1} min={1} max={12} position={'relative'}>
<NumberInputField
pr={'30px'}
{...registerDatasetSize('month', {
required: true,
min: 1,
max: 12,
valueAsNumber: true
})}
/>
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
<Box position={'absolute'} right={'20px'} color={'myGray.500'} fontSize={'xs'}>
{t('common.month')}
<Box fontWeight={'bold'} flex={1}>
{extraDatasetSize?.currentExtraDatasetSize || 0}
{t('core.dataset.data.unit')}
</Box>
</Flex>
{extraDatasetSize?.nextExtraDatasetSize !== undefined && (
<Flex mt={4}>
<Box flex={'0 0 200px'}>
{t('support.wallet.subscription.Next sub dataset size')}:
</Box>
<Box fontWeight={'bold'} flex={1}>
{extraDatasetSize?.nextExtraDatasetSize || 0}
{t('core.dataset.data.unit')}
</Box>
</Flex>
)}
{!!extraDatasetSize?.startTime && (
<Flex mt={3}>
<Box flex={'0 0 200px'}>: </Box>
<Box>{formatTime2YMDHM(extraDatasetSize?.startTime)}</Box>
</Flex>
)}
{!!extraDatasetSize?.expiredTime && (
<Flex mt={3}>
<Box flex={'0 0 200px'}>: </Box>
<Box>{formatTime2YMDHM(extraDatasetSize?.expiredTime)}</Box>
</Flex>
)}
<Flex mt={3} alignItems={'center'}>
<Box flex={'0 0 200px'}>: </Box>
<MySelect
value={isRenew}
size={'sm'}
w={'180px'}
bg={'myGray.50'}
boxShadow={'none'}
list={[
{ label: '自动续费', value: 'true' },
{ label: '不自动续费', value: 'false' }
]}
onchange={(e) => {
if (!extraDatasetSize) return;
onUpdateExtraDatasetSizeStatus(e);
}}
/>
</Flex>
<Flex mt={4} alignItems={'center'}>
<Box flex={['0 0 100px', '0 0 200px']}>
<Box flex={'0 0 200px'}>
{t('support.wallet.subscription.Update extra dataset size')}
</Box>
<Flex alignItems={'center'} mt={1} w={'180px'} position={'relative'}>
@@ -198,18 +197,13 @@ const ExtraPlan = () => {
min={0}
max={10000}
step={1}
value={datasetSize}
position={'relative'}
onChange={(e) => {
setDatasetSize(Number(e));
}}
>
<NumberInputField
pr={'30px'}
{...registerDatasetSize('datasetSize', {
required: true,
min: 0,
max: 10000,
valueAsNumber: true
})}
step={1}
/>
<NumberInputField pr={'30px'} value={datasetSize} step={1} min={0} max={10000} />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
@@ -221,124 +215,88 @@ const ExtraPlan = () => {
</Flex>
</Flex>
<Button
isDisabled={datasetSize * 1000 === extraDatasetSize?.nextExtraDatasetSize}
mt={6}
w={'100%'}
variant={'primaryGhost'}
isLoading={loading}
onClick={handleSubmitDatasetSize(onclickBuyDatasetSize)}
isLoading={isPayingExtraDatasetSize || isFetchingPreviewCheck}
onClick={onClickPreviewCheck}
>
{t('support.wallet.Buy')}
</Button>
</Box>
</Box>
{/* points */}
<Box
bg={'rgba(255, 255, 255, 0.90)'}
w={['100%', '500px']}
px={'32px'}
py={'24px'}
borderRadius={'2xl'}
borderWidth={'1px'}
borderColor={'myGray.150'}
boxShadow={'1.5'}
>
<Flex borderBottomWidth={'1px'} borderBottomColor={'myGray.200'}>
<Box flex={'1 0 0'}>
<Box fontSize={'xl'} color={'primary.600'}>
{t('support.wallet.subscription.Extra ai points')}
</Box>
<Box mt={3} fontSize={['28px', '32px']} fontWeight={'bold'}>
{extraPointsPrice}/1000{' '}
<Box ml={1} as={'span'} fontSize={'lg'} color={'myGray.600'} fontWeight={'normal'}>
/{t('common.month')}
</Box>
</Box>
</Box>
<MyIcon
display={['none', 'display']}
mt={'-30px'}
transform={'translateX(20px)'}
name={'support/bill/extraPoints'}
fill={'none'}
/>
</Flex>
<Box>
<Flex mt={4}>
<MyIcon mr={2} name={'support/bill/shoppingCart'} w={'16px'} color={'primary.600'} />
</Flex>
<Flex mt={4} alignItems={'center'}>
<Box flex={['0 0 100px', '0 0 200px']}>
{t('support.wallet.subscription.Month amount')}
</Box>
<Flex alignItems={'center'} mt={1} w={'180px'} position={'relative'}>
<NumberInput size={'sm'} flex={1} step={1} min={1} max={12} position={'relative'}>
<NumberInputField
pr={'30px'}
{...registerExtraPoints('month', {
required: true,
min: 1,
max: 12,
valueAsNumber: true
})}
/>
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
<Box position={'absolute'} right={'20px'} color={'myGray.500'} fontSize={'xs'}>
{t('common.month')}
</Box>
</Flex>
</Flex>
<Flex mt={4} alignItems={'center'}>
<Box flex={['0 0 100px', '0 0 200px']}>
{t('support.wallet.subscription.Update extra ai points')}
</Box>
<Flex alignItems={'center'} mt={1} w={'180px'} position={'relative'}>
<NumberInput
size={'sm'}
flex={1}
min={0}
max={10000}
step={1}
position={'relative'}
>
<NumberInputField
pr={'30px'}
step={1}
{...registerExtraPoints('points', {
required: true,
min: 0,
max: 10000,
valueAsNumber: true
})}
/>
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
<Box position={'absolute'} right={'20px'} color={'myGray.500'} fontSize={'xs'}>
000
</Box>
</Flex>
</Flex>
<Button
mt={6}
w={'100%'}
variant={'primaryGhost'}
isLoading={loading}
onClick={handleSubmitExtraPoints(onclickBuyExtraPoints)}
>
{t('support.wallet.Buy')}
{t('common.change')}
</Button>
</Box>
</Box>
</Grid>
{!!qrPayData && <QRCodePayModal {...qrPayData} />}
{/* extra dataset size modal */}
{!!confirmPayExtraDatasetSizeData && (
<MyModal
isOpen
onClose={() => setConfirmPayExtraDatasetSizeData(undefined)}
title={t('support.wallet.Confirm pay')}
iconSrc="common/confirm/rightTip"
>
<ModalBody px={8} py={5}>
<Flex>
<Box flex={'0 0 120px'} color={'myGray.600'}>
</Box>
<Box>{extraDatasetSize?.currentExtraDatasetSize || 0}</Box>
</Flex>
<Flex mt={4}>
<Box flex={'0 0 120px'} color={'myGray.600'}>
</Box>
<Box>{confirmPayExtraDatasetSizeData.newSubSize}</Box>
</Flex>
<Flex mt={4}>
<Box flex={'0 0 120px'} color={'myGray.600'}>
</Box>
<Box>{formatStorePrice2Read(confirmPayExtraDatasetSizeData.newPlanPrice)}</Box>
</Flex>
<Flex mt={4}>
<Box flex={'0 0 120px'} color={'myGray.600'}>
</Box>
<Box>30</Box>
</Flex>
{/* <Flex>
<Box flex={'0 0 120px'}>账号余额:</Box>
<Box>{formatStorePrice2Read(userInfo?.team?.balance).toFixed(3)}元</Box>
</Flex> */}
</ModalBody>
<ModalFooter mx={8} px={0} borderTopWidth={'1px'} borderTopColor={'myGray.200'}>
<Box color={'myGray.600'}></Box>
{confirmPayExtraDatasetSizeData.balanceEnough ? (
<>
<Box flex={'1 0 0'}>
{formatStorePrice2Read(userInfo?.team?.balance).toFixed(2)}
</Box>
<Button
isLoading={isPayingExtraDatasetSize}
onClick={() => onClickUpdateExtraDatasetPlan('')}
>
{formatStorePrice2Read(confirmPayExtraDatasetSizeData.payPrice).toFixed(2)}
</Button>
</>
) : (
<>
<Box color={'red.600'} flex={'1 0 0'}>
</Box>
<Button
isLoading={isPayingExtraDatasetSize}
onClick={() => router.push('/account')}
>
</Button>
</>
)}
</ModalFooter>
</MyModal>
)}
</Flex>
);
};

View File

@@ -4,40 +4,7 @@ import { useTranslation } from 'next-i18next';
const FAQ = () => {
const { t } = useTranslation();
const faqs = [
{
title: '订阅套餐会自动续费么?',
desc: '当前套餐过期后,系统会自动根据“未来套餐”进行续费,系统会尝试从账户余额进行扣费,如果您需要自动续费,请在账户余额中预留额度。'
},
{
title: '能否切换订阅套餐?',
desc: '当前套餐价格大于新套餐时,无法立即切换,将会在当前套餐过期后以“续费”形式进行切换。\n当前套餐价格小于新套餐时系统会自动计算当前套餐剩余余额您可支付差价进行套餐切换。'
},
{
title: '什么是AI积分',
desc: '每次调用AI模型时都会消耗一定的AI积分。具体的计算标准可参考上方的“AI 积分计算标准”。'
},
{
title: 'AI积分会过期么',
desc: '会过期。当前套餐过期后AI积分将会清空并更新为新套餐的AI积分。年度套餐的AI积分时长为1年而不是每个月。'
},
{
title: '知识库索引怎么计算?',
desc: '知识库索引是系统存储的最小单位。通常每条知识库数据对应一条索引,但也会有多条索引的情况。你可以在知识库数据的编辑面板,查看该数据的索引数量和具体内容。'
},
{
title: '额外资源包可以叠加么?',
desc: '可以的。每次购买的资源包都是独立的在其有效期内将会叠加使用。AI积分会优先扣除最先过期的资源包。'
},
{
title: '知识库索引超出会删除么?',
desc: '不会,知识库索引超出时,仅无法插入新的知识库索引。'
},
{
title: '免费版数据会清除么?',
desc: '免费版用户15天无使用记录后会自动清除所有知识库内容。'
}
];
const faqs = [{ title: '怎么付费', describe: '2222' }];
return (
<Flex
@@ -51,25 +18,22 @@ const FAQ = () => {
{t('support.wallet.subscription.FAQ')}
</Box>
<Grid mt={4} gridTemplateColumns={['1fr', '1fr 1fr']} gap={4} w={'100%'}>
{faqs.map((item, i) => (
<Box
key={i}
py={4}
px={5}
borderRadius={'lg'}
borderWidth={'1px'}
borderColor={'myGray.150'}
bg={'rgba(255,255,255,0.9)'}
_hover={{
borderColor: 'primary.300'
}}
>
<Box fontWeight={'bold'}>{item.title}</Box>
<Box fontSize={'sm'} color={'myGray.600'} whiteSpace={'pre-wrap'}>
{item.desc}
</Box>
<Box
py={2}
px={4}
borderRadius={'lg'}
borderWidth={'1px'}
borderColor={'myGray.150'}
bg={'rgba(255,255,255,0.9)'}
_hover={{
borderColor: 'primary.300'
}}
>
<Box fontSize={'lg'} fontWeight={'500'}>
</Box>
))}
<Box color={'myGray.500'}>2222</Box>
</Box>
</Grid>
</Flex>
);

View File

@@ -15,7 +15,7 @@ const Points = () => {
alignItems={'center'}
position={'relative'}
>
<Box id="point-card" fontWeight={'bold'} fontSize={['24px', '36px']}>
<Box fontWeight={'bold'} fontSize={['24px', '36px']}>
{t('support.wallet.subscription.Ai points')}
</Box>
<Grid gap={6} mt={['30px', '48px']} w={'100%'}>
@@ -42,7 +42,7 @@ const Points = () => {
{llmModelList?.map((item, i) => (
<Flex key={item.model} py={4} bg={i % 2 !== 0 ? 'myGray.50' : ''}>
<Box flex={'1 0 0'}>{item.name}</Box>
<Box flex={'1 0 0'}>{item.charsPointsPrice} / 1000</Box>
<Box flex={'1 0 0'}>5 / 1000</Box>
</Flex>
))}
</Box>
@@ -67,7 +67,7 @@ const Points = () => {
{vectorModelList?.map((item, i) => (
<Flex key={item.model} py={4} bg={i % 2 !== 0 ? 'myGray.50' : ''}>
<Box flex={'1 0 0'}>{item.name}</Box>
<Box flex={'1 0 0'}>{item.charsPointsPrice} / 1000</Box>
<Box flex={'1 0 0'}>5 / 1000</Box>
</Flex>
))}
</Box>
@@ -89,7 +89,7 @@ const Points = () => {
{audioSpeechModelList?.map((item, i) => (
<Flex key={item.model} py={4} bg={i % 2 !== 0 ? 'myGray.50' : ''}>
<Box flex={'1 0 0'}>{item.name}</Box>
<Box flex={'1 0 0'}>{item.charsPointsPrice} / 1000</Box>
<Box flex={'1 0 0'}>5 / 1000</Box>
</Flex>
))}
</Box>
@@ -110,7 +110,7 @@ const Points = () => {
<Box flex={4} textAlign={'center'} h={'100%'}>
<Flex py={4}>
<Box flex={'1 0 0'}>{whisperModel?.name}</Box>
<Box flex={'1 0 0'}>{whisperModel?.charsPointsPrice} / </Box>
<Box flex={'1 0 0'}>{whisperModel?.inputPrice} / </Box>
</Flex>
</Box>
</Box>

View File

@@ -1,8 +1,9 @@
import React, { useMemo, useState } from 'react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { Box, Button, Flex, Grid, ModalBody, ModalFooter } from '@chakra-ui/react';
import { Box, Button, Flex, Grid } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import { StandardSubLevelEnum, SubModeEnum } from '@fastgpt/global/support/wallet/sub/constants';
import { useUserStore } from '@/web/support/user/useUserStore';
import { postCheckStandardSub, postUpdateStandardSub } from '@/web/support/wallet/sub/api';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { standardSubLevelMap } from '@fastgpt/global/support/wallet/sub/constants';
@@ -10,21 +11,9 @@ import { StandardSubPlanParams } from '@fastgpt/global/support/wallet/sub/api';
import { useRequest } from '@/web/common/hooks/useRequest';
import { StandardSubPlanUpdateResponse } from '@fastgpt/global/support/wallet/sub/api.d';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/usage/tools';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools';
import { TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type';
import MyModal from '@/components/MyModal';
import QRCodePayModal, { type QRPayProps } from '@/components/support/wallet/QRCodePayModal';
import { getWxPayQRCode } from '@/web/support/wallet/bill/api';
import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants';
import StandardPlanContentList from '@/components/support/wallet/StandardPlanContentList';
type ConfirmPayModalProps = {
teamBalance: number;
totalPrice: number;
payPrice: number;
planProps: StandardSubPlanParams;
};
const Standard = ({
standardPlan,
@@ -36,7 +25,7 @@ const Standard = ({
const { t } = useTranslation();
const { subPlans, feConfigs } = useSystemStore();
const { toast } = useToast();
const [confirmPayData, setConfirmPayData] = useState<ConfirmPayModalProps>();
const { ConfirmModal, openConfirm } = useConfirm({});
const [selectSubMode, setSelectSubMode] = useState<`${SubModeEnum}`>(SubModeEnum.month);
@@ -52,12 +41,12 @@ const Standard = ({
maxDatasetAmount: value.maxDatasetAmount,
chatHistoryStoreDuration: value.chatHistoryStoreDuration,
maxDatasetSize: value.maxDatasetSize,
permissionCustomApiKey: value.permissionCustomApiKey,
permissionCustomCopyright: value.permissionCustomCopyright,
customApiKey: value.customApiKey,
customCopyright: value.customCopyright,
trainingWeight: value.trainingWeight,
permissionReRank: value.permissionReRank,
reRankWeight: value.reRankWeight,
totalPoints: value.totalPoints * (selectSubMode === SubModeEnum.month ? 1 : 12),
permissionWebsiteSync: value.permissionWebsiteSync
websiteSyncInterval: value.websiteSyncInterval
};
})
: [];
@@ -75,21 +64,41 @@ const Standard = ({
const { mutate: onclickPreCheckStandPlan, isLoading: isCheckingStandardPlan } = useRequest({
mutationFn: (data: StandardSubPlanParams) => postCheckStandardSub(data),
onSuccess(res: StandardSubPlanUpdateResponse) {
if (!res.balanceEnough) {
return toast({
status: 'warning',
title: t('support.wallet.Balance not enough tip')
});
}
if (res.payPrice === undefined) {
onclickUpdateStandardPlan({
level: res.nextSubLevel,
mode: res.nextMode
});
} else if (res.payPrice > 0) {
openConfirm(
() =>
onclickUpdateStandardPlan({
level: res.nextSubLevel,
mode: res.nextMode
}),
undefined,
t('support.wallet.subscription.Standard plan pay confirm', {
payPrice: formatStorePrice2Read(res.payPrice).toFixed(2)
})
)();
} else {
setConfirmPayData({
teamBalance: res.teamBalance,
totalPrice: res.planPrice,
payPrice: res.payPrice,
planProps: {
level: res.nextSubLevel,
mode: res.nextMode
}
});
openConfirm(
() =>
onclickUpdateStandardPlan({
level: res.nextSubLevel,
mode: res.nextMode
}),
undefined,
t('support.wallet.subscription.Refund plan and pay confirm', {
amount: formatStorePrice2Read(Math.abs(res.payPrice)).toFixed(2)
})
)();
}
}
});
@@ -129,114 +138,148 @@ const Standard = ({
gap={[4, 6, 8]}
w={'100%'}
>
{standardSubList.map((item) => {
const isCurrentPlan =
item.level === standardPlan?.currentSubLevel &&
selectSubMode === standardPlan?.currentMode;
return (
<Box
key={item.level}
flex={'1 0 0'}
bg={'rgba(255, 255, 255, 0.90)'}
p={'28px'}
borderRadius={'2xl'}
borderWidth={'1.5px'}
boxShadow={'1.5'}
{...(isCurrentPlan
? {
borderColor: 'primary.600'
}
: {
borderColor: 'myGray.150'
})}
>
<Box fontSize={'lg'} fontWeight={'500'}>
{t(item.label)}
</Box>
<Box fontSize={['32px', '42px']} fontWeight={'bold'}>
{item.price}
</Box>
<Box color={'myGray.500'} h={'40px'} fontSize={'xs'}>
{t(item.desc, { title: feConfigs?.systemTitle })}
</Box>
{(() => {
if (
item.level === StandardSubLevelEnum.free &&
selectSubMode === SubModeEnum.year
) {
return (
<Button isDisabled mt={4} mb={6} w={'100%'} variant={'solid'}>
{t('support.wallet.subscription.Nonsupport')}
</Button>
);
}
if (
item.level === standardPlan?.nextSubLevel &&
selectSubMode === standardPlan?.nextMode
) {
return (
<Button mt={4} mb={6} w={'100%'} variant={'whiteBase'} isDisabled>
{t('support.wallet.subscription.Next plan')}
</Button>
);
}
if (isCurrentPlan) {
return (
<Button
mt={4}
mb={6}
w={'100%'}
variant={'whiteBase'}
isDisabled={
item.level === standardPlan?.nextSubLevel &&
selectSubMode === standardPlan?.nextMode
}
onClick={() =>
onclickPreCheckStandPlan({
level: item.level,
mode: selectSubMode
})
}
>
{t('support.wallet.subscription.Current plan')}
</Button>
);
}
{standardSubList.map((item) => (
<Box
key={item.level}
bg={'rgba(255, 255, 255, 0.90)'}
p={'28px'}
borderRadius={'2xl'}
borderWidth={'1px'}
borderColor={'myGray.150'}
boxShadow={'1.5'}
>
<Box fontSize={'lg'} fontWeight={'500'}>
{t(item.label)}
</Box>
<Box fontSize={['32px', '42px']} fontWeight={'bold'}>
{item.price}
</Box>
<Box color={'myGray.500'} h={'40px'}>
{t(item.desc, { title: feConfigs?.systemTitle })}
</Box>
{(() => {
if (item.level === StandardSubLevelEnum.free && selectSubMode === SubModeEnum.year) {
return (
<Button
mt={4}
mb={6}
w={'100%'}
variant={'primaryGhost'}
isLoading={isUpdatingStandardPlan || isCheckingStandardPlan}
onClick={() =>
onclickPreCheckStandPlan({
level: item.level,
mode: selectSubMode
})
}
>
{t('support.wallet.subscription.Buy now')}
<Button isDisabled mt={4} mb={6} w={'100%'} variant={'solid'}>
{t('support.wallet.subscription.Nonsupport')}
</Button>
);
})()}
}
if (
item.level === standardPlan?.currentSubLevel &&
selectSubMode === standardPlan?.currentMode
) {
return (
<Button mt={4} mb={6} w={'100%'} variant={'whiteBase'} isDisabled>
{t('support.wallet.subscription.Current plan')}
</Button>
);
}
if (
item.level === standardPlan?.nextSubLevel &&
selectSubMode === standardPlan?.nextMode
) {
return (
<Button mt={4} mb={6} w={'100%'} variant={'whiteBase'} isDisabled>
{t('support.wallet.subscription.Next plan')}
</Button>
);
}
return (
<Button
mt={4}
mb={6}
w={'100%'}
variant={'primaryGhost'}
isLoading={isUpdatingStandardPlan || isCheckingStandardPlan}
onClick={() =>
onclickPreCheckStandPlan({
level: item.level,
mode: selectSubMode
})
}
>
{t('support.wallet.subscription.Buy now')}
</Button>
);
})()}
{/* function list */}
<StandardPlanContentList level={item.level} mode={selectSubMode} />
</Box>
);
})}
{/* function list */}
<Grid gap={4}>
<Flex alignItems={'center'}>
<MyIcon name={'price/right'} w={'16px'} mr={3} />
<Box color={'myGray.600'}>
{t('support.wallet.subscription.function.Max members', {
amount: item.maxTeamMember
})}
</Box>
</Flex>
<Flex alignItems={'center'}>
<MyIcon name={'price/right'} w={'16px'} mr={3} />
<Box color={'myGray.600'}>
{t('support.wallet.subscription.function.Max app', {
amount: item.maxAppAmount
})}
</Box>
</Flex>
<Flex alignItems={'center'}>
<MyIcon name={'price/right'} w={'16px'} mr={3} />
<Box color={'myGray.600'}>
{t('support.wallet.subscription.function.Max dataset', {
amount: item.maxDatasetAmount
})}
</Box>
</Flex>
<Flex alignItems={'center'}>
<MyIcon name={'price/right'} w={'16px'} mr={3} />
<Box color={'myGray.600'}>
{t('support.wallet.subscription.function.History store', {
amount: item.chatHistoryStoreDuration
})}
</Box>
</Flex>
<Flex alignItems={'center'}>
<MyIcon name={'price/right'} w={'16px'} mr={3} />
<Box color={'myGray.600'}>
{t('support.wallet.subscription.function.Max dataset size', {
amount: item.maxDatasetSize
})}
</Box>
</Flex>
<Flex alignItems={'center'}>
<MyIcon name={'price/right'} w={'16px'} mr={3} />
<Box color={'myGray.600'}>
{t('support.wallet.subscription.function.Points', {
amount: item.totalPoints
})}
</Box>
</Flex>
<Flex alignItems={'center'}>
<MyIcon name={'price/right'} w={'16px'} mr={3} />
<Box color={'myGray.600'}>
{t('support.wallet.subscription.Training weight', {
weight: item.trainingWeight
})}
</Box>
</Flex>
{!!item.customApiKey && (
<Flex alignItems={'center'}>
<MyIcon name={'price/right'} w={'16px'} mr={3} />
<Box color={'myGray.600'}>API Key</Box>
</Flex>
)}
{!!item.websiteSyncInterval && (
<Flex alignItems={'center'}>
<MyIcon name={'price/right'} w={'16px'} mr={3} />
<Box color={'myGray.600'}>{item.websiteSyncInterval} h/ web站点同步</Box>
</Flex>
)}
</Grid>
</Box>
))}
</Grid>
{!!confirmPayData && (
<ConfirmPayModal
{...confirmPayData}
onClose={() => setConfirmPayData(undefined)}
onConfirmPay={() => onclickUpdateStandardPlan(confirmPayData.planProps)}
/>
)}
<ConfirmModal />
</Flex>
);
};
@@ -295,87 +338,3 @@ const RowTabs = ({
</Box>
);
};
const ConfirmPayModal = ({
teamBalance,
totalPrice,
payPrice,
onClose,
onConfirmPay
}: ConfirmPayModalProps & { onClose: () => void; onConfirmPay: () => void }) => {
const { t } = useTranslation();
const [qrPayData, setQRPayData] = useState<QRPayProps>();
const formatPayPrice = Math.ceil(formatStorePrice2Read(payPrice));
const formatTeamBalance = Math.floor(formatStorePrice2Read(teamBalance));
const { mutate: handleClickPay, isLoading } = useRequest({
mutationFn: async (amount: number) => {
// 获取支付二维码
return getWxPayQRCode({
type: BillTypeEnum.balance,
balance: amount
});
},
onSuccess(res) {
setQRPayData({
readPrice: res.readPrice,
codeUrl: res.codeUrl,
billId: res.billId
});
}
});
return (
<MyModal
isOpen
iconSrc="modal/confirmPay"
title={t('support.wallet.Confirm pay')}
onClose={onClose}
>
<ModalBody py={5} px={9}>
<Flex>
<Box flex={'0 0 100px'}></Box>
<Box>{formatStorePrice2Read(totalPrice)}</Box>
</Flex>
<Flex mt={6}>
<Box flex={'0 0 100px'}></Box>
<Box>{Math.floor(formatStorePrice2Read(totalPrice - payPrice))}</Box>
</Flex>
<Flex mt={6}>
<Box flex={'0 0 100px'}></Box>
<Box>{formatPayPrice}</Box>
</Flex>
</ModalBody>
<ModalFooter
borderTopWidth={'1px'}
borderTopColor={'borderColor.base'}
mx={9}
justifyContent={'flex-start'}
px={0}
>
<Box>: </Box>
<Box ml={2} flex={1}>
{formatTeamBalance}
</Box>
{teamBalance >= payPrice ? (
<Button size={'sm'} onClick={onConfirmPay}>
</Button>
) : (
<Button
size={'sm'}
isLoading={isLoading}
onClick={() => {
handleClickPay(Math.ceil(formatStorePrice2Read(payPrice - teamBalance)));
}}
>
</Button>
)}
</ModalFooter>
{!!qrPayData && <QRCodePayModal {...qrPayData} onSuccess={onConfirmPay} />}
</MyModal>
);
};

View File

@@ -3,56 +3,48 @@ import { serviceSideProps } from '@/web/common/utils/i18n';
import { Box, Image } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import { useUserStore } from '@/web/support/user/useUserStore';
import { getTeamPlanStatus } from '@/web/support/wallet/sub/api';
import { getTeamDatasetValidSub } from '@/web/support/wallet/sub/api';
import { useQuery } from '@tanstack/react-query';
import StandardPlan from './components/Standard';
import ExtraPlan from './components/ExtraPlan';
import PointsCard from './components/Points';
import FAQ from './components/FAQ';
import { getToken } from '@/web/support/user/auth';
import Script from 'next/script';
const PriceBox = () => {
const { t } = useTranslation();
const { userInfo } = useUserStore();
const { data: teamSubPlan, refetch: refetchTeamSubPlan } = useQuery(
['getTeamPlanStatus'],
getTeamPlanStatus,
['getTeamDatasetValidSub'],
getTeamDatasetValidSub,
{
enabled: !!getToken() || !!userInfo
enabled: !!userInfo
}
);
return (
<>
<Script src="/js/qrcode.min.js" strategy="lazyOnload"></Script>
<Box
h={'100%'}
overflow={'overlay'}
w={'100%'}
px={['20px', '5vw']}
py={['30px', '80px']}
backgroundImage={'url(/imgs/priceBg.svg)'}
backgroundSize={'cover'}
backgroundRepeat={'no-repeat'}
>
{/* standard sub */}
<StandardPlan
standardPlan={teamSubPlan?.standard}
refetchTeamSubPlan={refetchTeamSubPlan}
/>
<Box
h={'100%'}
overflow={'overlay'}
w={'100%'}
px={['20px', '5vw']}
py={['30px', '80px']}
backgroundImage={'url(/imgs/priceBg.svg)'}
backgroundSize={'cover'}
backgroundRepeat={'no-repeat'}
>
{/* standard sub */}
<StandardPlan standardPlan={teamSubPlan?.standard} refetchTeamSubPlan={refetchTeamSubPlan} />
<ExtraPlan />
<ExtraPlan extraDatasetSize={teamSubPlan?.extraDatasetSize} />
{/* points */}
<PointsCard />
{/* points */}
<PointsCard />
{/* question */}
<FAQ />
</Box>
</>
{/* question */}
<FAQ />
</Box>
);
};