* feat: org CRUD (#3380) * feat: add org schema * feat: org manage UI * feat: OrgInfoModal * feat: org tree view * feat: org management * fix: init root org * feat: org permission for app * feat: org support for dataset * fix: disable org role control * styles: opt type signatures * fix: remove unused permission * feat: delete org collaborator * perf: Team org ui (#3499) * perf: org ui * perf: org ui * feat: org auth for app & dataset (#3498) * feat: auth org resource permission * feat: org auth support for app & dataset * perf: org permission check (#3500) * i18n (#3501) * name * i18n * feat: support dataset changeOwner (#3483) * feat: support dataset changeOwner * chore: update dataset change owner api * feat: permission manage UI for org (#3503) * perf: password check;perf: image upload check;perf: sso login check (#3509) * perf: password check * perf: image upload check * perf: sso login check * force show update notification modal & fix login page text (#3512) * fix login page English text * update notification modal * perf: notify account (#3515) * perf(plugin): improve searXNG empty result handling and documentation (#3507) * perf(plugin): improve searXNG empty result handling and documentation * 修改了文档和代码部分无搜索的结果的反馈 * refactor: org pathId (#3516) * optimize payment process (#3517) * feat: support wecom sso (#3518) * feat: support wecom sso * chore: remove unused wecom js-sdk dependency * fix qrcode script (#3520) * fix qrcode script * i18n * perf: full text collection and search code;perf: rename function (#3519) * perf: full text collection and search code * perf: rename function * perf: notify modal * remove invalid code * perf: sso login * perf: pay process * 4.8.18 test (#3524) * perf: remove local token * perf: index * perf: file encoding;perf: leave team code;@c121914yu perf: full text search code (#3528) * perf: text encoding * perf: leave team code * perf: full text search code * fix: http status * perf: embedding search and vector avatar * perf: async read file (#3531) * refactor: team permission manager (#3535) * perf: classify org, group and member * refactor: team per manager * fix: missing functions * 4.8.18 test (#3543) * perf: login check * doc * perf: llm model config * perf: team clb config * fix: MemberModal UI (#3553) * fix: adapt MemberModal title and icon * fix: adapt member modal * fix: search input placeholder * fix: add button text * perf: org permission (#3556) * docs:用户答疑的官方文档补充 (#3540) * docs:用户答疑的官方文档补充 * 问题回答的内容修补 * share link random avatar (#3541) * share link random avatar * fix * delete unused code * share page avatar (#3558) * feat: init 4818 * share page avatar * feat: tmp upgrade code (#3559) * feat: tmp upgrade code * fulltext search test * update action * full text tmp code (#3561) * full text tmp code * fix: init * fix: init * remove tmp code * remove tmp code * 4818-alpha * 4.8.18 test (#3562) * full text tmp code * fix: init * upgrade code * account log * account log * perf: dockerfile * upgrade code * chore: update docs app template submission (#3564) --------- Co-authored-by: a.e. <49438478+I-Info@users.noreply.github.com> Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com> Co-authored-by: heheer <heheer@sealos.io> Co-authored-by: Jiangween <145003935+Jiangween@users.noreply.github.com>
337 lines
11 KiB
TypeScript
337 lines
11 KiB
TypeScript
import React, { useMemo, useState } from 'react';
|
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
|
import { Box, Button, Flex, Grid, HStack } from '@chakra-ui/react';
|
|
import { useTranslation } from 'next-i18next';
|
|
import { StandardSubLevelEnum, SubModeEnum } from '@fastgpt/global/support/wallet/sub/constants';
|
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
|
import { standardSubLevelMap } from '@fastgpt/global/support/wallet/sub/constants';
|
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
|
import { TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type';
|
|
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';
|
|
|
|
export enum PackageChangeStatusEnum {
|
|
buy = 'buy',
|
|
renewal = 'renewal',
|
|
upgrade = 'upgrade'
|
|
}
|
|
|
|
const Standard = ({
|
|
standardPlan: myStandardPlan,
|
|
onPaySuccess
|
|
}: {
|
|
standardPlan?: TeamSubSchema;
|
|
onPaySuccess?: () => void;
|
|
}) => {
|
|
const { t } = useTranslation();
|
|
|
|
const packagePayTextMap = {
|
|
[PackageChangeStatusEnum.buy]: t('common:pay.package_tip.buy'),
|
|
[PackageChangeStatusEnum.renewal]: t('common:pay.package_tip.renewal'),
|
|
[PackageChangeStatusEnum.upgrade]: t('common:pay.package_tip.upgrade')
|
|
};
|
|
|
|
const [packageChange, setPackageChange] = useState<PackageChangeStatusEnum>();
|
|
const { subPlans, feConfigs } = useSystemStore();
|
|
const [selectSubMode, setSelectSubMode] = useState<`${SubModeEnum}`>(SubModeEnum.month);
|
|
|
|
const standardSubList = useMemo(() => {
|
|
return subPlans?.standard
|
|
? Object.entries(subPlans.standard).map(([level, value]) => {
|
|
return {
|
|
price: value.price * (selectSubMode === SubModeEnum.month ? 1 : 10),
|
|
level: level as `${StandardSubLevelEnum}`,
|
|
...standardSubLevelMap[level as `${StandardSubLevelEnum}`],
|
|
label: value.name || standardSubLevelMap[level as `${StandardSubLevelEnum}`].label, // custom label
|
|
maxTeamMember: value.maxTeamMember,
|
|
maxAppAmount: value.maxAppAmount,
|
|
maxDatasetAmount: value.maxDatasetAmount,
|
|
chatHistoryStoreDuration: value.chatHistoryStoreDuration,
|
|
maxDatasetSize: value.maxDatasetSize,
|
|
permissionCustomApiKey: value.permissionCustomApiKey,
|
|
permissionCustomCopyright: value.permissionCustomCopyright,
|
|
trainingWeight: value.trainingWeight,
|
|
permissionReRank: value.permissionReRank,
|
|
totalPoints: value.totalPoints * (selectSubMode === SubModeEnum.month ? 1 : 12),
|
|
permissionWebsiteSync: value.permissionWebsiteSync
|
|
};
|
|
})
|
|
: [];
|
|
}, [subPlans?.standard, selectSubMode]);
|
|
|
|
// Pay code
|
|
const [qrPayData, setQRPayData] = useState<QRPayProps>();
|
|
|
|
/* Get pay code */
|
|
const { runAsync: onPay, loading: isLoading } = useRequest2(getWxPayQRCode, {
|
|
onSuccess(res) {
|
|
setQRPayData({
|
|
readPrice: res.readPrice,
|
|
codeUrl: res.codeUrl,
|
|
billId: res.billId
|
|
});
|
|
}
|
|
});
|
|
|
|
return (
|
|
<>
|
|
<Flex flexDirection={'column'} alignItems={'center'} position={'relative'}>
|
|
<Box>
|
|
<RowTabs
|
|
list={[
|
|
{
|
|
label: t('common:support.wallet.subscription.mode.Month'),
|
|
value: SubModeEnum.month
|
|
},
|
|
{
|
|
label: (
|
|
<Flex>
|
|
<Box whiteSpace={'nowrap'}>
|
|
{t('common:support.wallet.subscription.mode.Year')}
|
|
</Box>
|
|
<Box
|
|
whiteSpace={'nowrap'}
|
|
ml={1}
|
|
color={selectSubMode === SubModeEnum.month ? 'red.600' : 'auto'}
|
|
>
|
|
({t('common:support.wallet.subscription.mode.Year sale')})
|
|
</Box>
|
|
</Flex>
|
|
),
|
|
value: SubModeEnum.year
|
|
}
|
|
]}
|
|
value={selectSubMode}
|
|
onChange={(e) => setSelectSubMode(e as `${SubModeEnum}`)}
|
|
/>
|
|
</Box>
|
|
{/* card */}
|
|
<Grid
|
|
mt={[10, '48px']}
|
|
gridTemplateColumns={['1fr', 'repeat(2,1fr)', 'repeat(4,1fr)']}
|
|
gap={[4, 6, 8]}
|
|
w={'100%'}
|
|
maxW={'1440px'}
|
|
minH={'550px'}
|
|
>
|
|
{standardSubList.map((item) => {
|
|
const isCurrentPlan = item.level === myStandardPlan?.currentSubLevel;
|
|
|
|
const isHigherLevel =
|
|
standardSubLevelMap[item.level].weight >
|
|
standardSubLevelMap[myStandardPlan?.currentSubLevel || StandardSubLevelEnum.free]
|
|
.weight;
|
|
|
|
return (
|
|
<Box
|
|
key={item.level}
|
|
pos={'relative'}
|
|
flex={'1 0 0'}
|
|
bg={isCurrentPlan ? 'blue.50' : 'rgba(255, 255, 255, 0.90)'}
|
|
p={'28px'}
|
|
borderRadius={'xl'}
|
|
borderWidth={isCurrentPlan ? '4px' : '1.5px'}
|
|
boxShadow={'1.5'}
|
|
{...(isCurrentPlan
|
|
? {
|
|
borderColor: 'primary.600'
|
|
}
|
|
: {
|
|
borderColor: 'myGray.150'
|
|
})}
|
|
>
|
|
{isCurrentPlan && (
|
|
<Box
|
|
position={'absolute'}
|
|
right={0}
|
|
top={'1.62rem'}
|
|
px={3}
|
|
py={'0.38rem'}
|
|
color={'blue.700'}
|
|
fontSize={'xs'}
|
|
bg={'blue.200'}
|
|
fontWeight={'500'}
|
|
borderLeftRadius={'sm'}
|
|
>
|
|
{t('common:is_using')}
|
|
</Box>
|
|
)}
|
|
<Box fontSize={'md'} fontWeight={'500'} color={'myGray.900'}>
|
|
{t(item.label as any)}
|
|
</Box>
|
|
<Box fontSize={['32px', '42px']} fontWeight={'bold'} color={'myGray.900'}>
|
|
¥{item.price}
|
|
</Box>
|
|
<Box color={'myGray.500'} h={'40px'} fontSize={'xs'}>
|
|
{t(item.desc as any, { title: feConfigs?.systemTitle })}
|
|
</Box>
|
|
|
|
{/* Button */}
|
|
{(() => {
|
|
if (item.level === StandardSubLevelEnum.free) {
|
|
return (
|
|
<Button
|
|
mt={4}
|
|
mb={6}
|
|
_active={{}}
|
|
_hover={{}}
|
|
boxShadow={'0'}
|
|
cursor={'default'}
|
|
w={'100%'}
|
|
isDisabled
|
|
variant={'whiteBase'}
|
|
>
|
|
{t('common:free')}
|
|
</Button>
|
|
);
|
|
}
|
|
// feature:
|
|
// if (
|
|
// item.level === myStandardPlan?.nextSubLevel &&
|
|
// selectSubMode === myStandardPlan?.nextMode
|
|
// ) {
|
|
// return (
|
|
// <Button mt={4} mb={6} w={'100%'} variant={'whiteBase'} isDisabled>
|
|
// {t('common:support.wallet.subscription.Next plan')}
|
|
// </Button>
|
|
// );
|
|
// }
|
|
if (isCurrentPlan) {
|
|
return (
|
|
<Button
|
|
mt={4}
|
|
mb={6}
|
|
w={'100%'}
|
|
variant={'primary'}
|
|
isLoading={isLoading}
|
|
onClick={() => {
|
|
setPackageChange(PackageChangeStatusEnum.renewal);
|
|
onPay({
|
|
type: BillTypeEnum.standSubPlan,
|
|
level: item.level,
|
|
subMode: selectSubMode
|
|
});
|
|
}}
|
|
>
|
|
{t('user:bill.renew_plan')}
|
|
</Button>
|
|
);
|
|
}
|
|
if (isHigherLevel) {
|
|
return (
|
|
<Button
|
|
mt={4}
|
|
mb={6}
|
|
w={'100%'}
|
|
variant={'primaryGhost'}
|
|
isLoading={isLoading}
|
|
onClick={() => {
|
|
setPackageChange(PackageChangeStatusEnum.upgrade);
|
|
onPay({
|
|
type: BillTypeEnum.standSubPlan,
|
|
level: item.level,
|
|
subMode: selectSubMode
|
|
});
|
|
}}
|
|
>
|
|
{t('common:support.wallet.subscription.Upgrade plan')}
|
|
</Button>
|
|
);
|
|
}
|
|
return (
|
|
<Button
|
|
mt={4}
|
|
mb={6}
|
|
w={'100%'}
|
|
variant={'primaryGhost'}
|
|
isLoading={isLoading}
|
|
onClick={() => {
|
|
setPackageChange(PackageChangeStatusEnum.buy);
|
|
onPay({
|
|
type: BillTypeEnum.standSubPlan,
|
|
level: item.level,
|
|
subMode: selectSubMode
|
|
});
|
|
}}
|
|
>
|
|
{t('user:bill.buy_plan')}
|
|
</Button>
|
|
);
|
|
})()}
|
|
|
|
{/* function list */}
|
|
<StandardPlanContentList level={item.level} mode={selectSubMode} />
|
|
</Box>
|
|
);
|
|
})}
|
|
</Grid>
|
|
|
|
{!!qrPayData && packageChange && (
|
|
<QRCodePayModal
|
|
tip={packagePayTextMap[packageChange]}
|
|
onSuccess={onPaySuccess}
|
|
{...qrPayData}
|
|
/>
|
|
)}
|
|
</Flex>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default React.memo(Standard);
|
|
|
|
const RowTabs = ({
|
|
list,
|
|
value,
|
|
onChange
|
|
}: {
|
|
list: {
|
|
icon?: string;
|
|
label: string | React.ReactNode;
|
|
value: string;
|
|
}[];
|
|
value: string;
|
|
onChange: (e: string) => void;
|
|
}) => {
|
|
return (
|
|
<Box
|
|
display={'inline-flex'}
|
|
px={'3px'}
|
|
py={'3px'}
|
|
borderRadius={'md'}
|
|
borderWidth={'1px'}
|
|
borderColor={'primary.300'}
|
|
bg={'primary.50'}
|
|
gap={'4px'}
|
|
>
|
|
{list.map((item) => (
|
|
<Flex
|
|
key={item.value}
|
|
alignItems={'center'}
|
|
justifyContent={'center'}
|
|
cursor={'pointer'}
|
|
borderRadius={'sm'}
|
|
px={'12px'}
|
|
py={'7px'}
|
|
userSelect={'none'}
|
|
w={['150px', '170px']}
|
|
{...(value === item.value
|
|
? {
|
|
color: 'white',
|
|
boxShadow: '1.5',
|
|
bg: 'primary.600'
|
|
}
|
|
: {
|
|
onClick: () => onChange(item.value)
|
|
})}
|
|
>
|
|
{item.icon && <MyIcon name={item.icon as any} mr={1} w={'14px'} />}
|
|
<Box fontWeight={'500'}>{item.label}</Box>
|
|
</Flex>
|
|
))}
|
|
</Box>
|
|
);
|
|
};
|