diff --git a/packages/web/i18n/en/common.json b/packages/web/i18n/en/common.json index e4530f447ade..91b3310b28f0 100644 --- a/packages/web/i18n/en/common.json +++ b/packages/web/i18n/en/common.json @@ -891,7 +891,7 @@ "pay_corporate_payment": "Payment to the public", "pay_money": "Amount payable", "pay_success": "Payment successfully", - "pay_year_tip": "Pay 10 months, enjoy 1 year!", + "pay_year_tip": "Pay {{count}} months, enjoy 1 year!", "permission.Collaborator": "Collaborator", "permission.Manage": "Manage", "permission.No InheritPermission": "Permission Inheritance Restricted", @@ -1051,6 +1051,10 @@ "support.wallet.subscription.Extra plan": "Extra Resource Pack", "support.wallet.subscription.Extra plan tip": "When the standard package is not enough, you can purchase extra resource packs to continue using", "support.wallet.subscription.FAQ": "FAQ", + "support.wallet.subscription.Basic plan tab": "Basic Plan", + "support.wallet.subscription.Extra points tab": "Extra Points", + "support.wallet.subscription.Extra dataset tab": "Extra Dataset Index", + "support.wallet.subscription.Purchase plan": "Purchase Plan", "support.wallet.subscription.Sub plan": "Subscription Package", "support.wallet.subscription.Sub plan tip": "Free to use [{{title}}] or upgrade to a higher package", "support.wallet.subscription.Sub plan tip wecom": "Purchase a plan to enjoy application services", @@ -1061,7 +1065,9 @@ "support.wallet.subscription.ai_model": "AI Language Model", "support.wallet.subscription.extra_plan_disabled_tip": "Please subscribe to a plan first before purchasing extra resource packs.", "support.wallet.subscription.mode.Month": "Month", + "support.wallet.subscription.mode.Month pay": "Monthly", "support.wallet.subscription.mode.Year": "Year", + "support.wallet.subscription.mode.Year pay": "Yearly", "support.wallet.subscription.per_year": "/ year", "support.wallet.subscription.point": " Points", "support.wallet.subscription.standardSubLevel.advanced": "Advanced", diff --git a/packages/web/i18n/zh-CN/account.json b/packages/web/i18n/zh-CN/account.json index 7b5b813dcac2..38daa37a0e35 100644 --- a/packages/web/i18n/zh-CN/account.json +++ b/packages/web/i18n/zh-CN/account.json @@ -19,7 +19,7 @@ "custom_config_details": "定制配置详情", "day": "天", "extra_ai_points": "额外 AI 积分", - "extra_dataset_size": "额外知识库容量", + "extra_dataset_size": "额外知识库索引量", "generation_time": "生成时间", "has_invoice": "是否已开票", "hour": "小时", diff --git a/packages/web/i18n/zh-CN/common.json b/packages/web/i18n/zh-CN/common.json index 22974c251abc..ac62aa0e043f 100644 --- a/packages/web/i18n/zh-CN/common.json +++ b/packages/web/i18n/zh-CN/common.json @@ -112,7 +112,7 @@ "bill_expired": "订单已过期", "bill_not_found": "订单不存在", "bill_not_pay_processed": "非在线订单", - "button.extra_dataset_size_tip": "您正在购买【额外知识库容量】", + "button.extra_dataset_size_tip": "您正在购买【额外知识库索引量】", "button.extra_points_tip": "您正在购买【额外 AI 积分】", "can_copy_content_tip": "无法使用浏览器自动复制,请手动复制下面内容", "cancel_bill": "取消", @@ -891,7 +891,7 @@ "pay_corporate_payment": "对公支付", "pay_money": "应付金额", "pay_success": "支付成功", - "pay_year_tip": "支付 10 个月,畅享 1 年!", + "pay_year_tip": "支付 {{count}} 个月,畅享一年!", "permission.Collaborator": "协作者", "permission.Manage": "管理", "permission.No InheritPermission": "已限制权限,不再继承父级文件夹的权限,", @@ -1023,7 +1023,7 @@ "support.wallet.App_amount_not_sufficient": "您的应用数量已达上限,请升级套餐后继续使用。", "support.wallet.Buy": "购买", "support.wallet.Dataset_amount_not_sufficient": "您的知识库数量已达上限,请升级套餐后继续使用。", - "support.wallet.Dataset_not_sufficient": "您的知识库容量不足,请先升级套餐或购买额外知识库容量后继续使用。", + "support.wallet.Dataset_not_sufficient": "您的知识库容量不足,请先升级套餐或购买额外知识库索引量后继续使用。", "support.wallet.Not sufficient": "您的 AI 积分不足,请先升级套餐或购买额外 AI 积分后继续使用。", "support.wallet.Not_sufficient_contact_admin": "您的 AI 积分不足,请联系团队管理员。", "support.wallet.Team_member_over_size": "您的团队成员数量已达上限,请升级套餐后继续使用。", @@ -1046,11 +1046,15 @@ "support.wallet.subscription.Dataset size": "知识库索引量", "support.wallet.subscription.Extra ai points": "额外 AI 积分", "support.wallet.subscription.Extra dataset description": "支持根据实际需求为知识库索引量延长有效期", - "support.wallet.subscription.Extra dataset size": "额外知识库容量", + "support.wallet.subscription.Extra dataset size": "额外知识库索引量", "support.wallet.subscription.Extra dataset unit": "组/1个月", "support.wallet.subscription.Extra plan": "额外资源包", - "support.wallet.subscription.Extra plan tip": "标准套餐不够时,您可以购买额外资源包继续使用", + "support.wallet.subscription.Extra plan tip": "基础套餐容量不足时,可购买额外资源包继续使用", "support.wallet.subscription.FAQ": "常见问题", + "support.wallet.subscription.Basic plan tab": "基础套餐", + "support.wallet.subscription.Extra points tab": "额外积分", + "support.wallet.subscription.Extra dataset tab": "额外知识库索引量", + "support.wallet.subscription.Purchase plan": "购买套餐", "support.wallet.subscription.Sub plan": "订阅套餐", "support.wallet.subscription.Sub plan tip": "免费使用【{{title}}】或升级更高的套餐", "support.wallet.subscription.Sub plan tip wecom": "购买套餐以享受应用服务", @@ -1061,7 +1065,9 @@ "support.wallet.subscription.ai_model": "AI语言模型", "support.wallet.subscription.extra_plan_disabled_tip": "如需购买额外资源包,请先订阅套餐。", "support.wallet.subscription.mode.Month": "按月", + "support.wallet.subscription.mode.Month pay": "按月付", "support.wallet.subscription.mode.Year": "按年", + "support.wallet.subscription.mode.Year pay": "按年付", "support.wallet.subscription.per_year": "/ 年", "support.wallet.subscription.point": "积分", "support.wallet.subscription.standardSubLevel.advanced": "高级版", diff --git a/packages/web/i18n/zh-Hant/account.json b/packages/web/i18n/zh-Hant/account.json index 7741f372fad7..867890e1aa06 100644 --- a/packages/web/i18n/zh-Hant/account.json +++ b/packages/web/i18n/zh-Hant/account.json @@ -19,7 +19,7 @@ "custom_config_details": "定制配置詳情", "day": "天", "extra_ai_points": "AI 積分運算標準", - "extra_dataset_size": "額外知識庫容量", + "extra_dataset_size": "額外知識庫索引量", "generation_time": "生成時間", "has_invoice": "是否已開票", "hour": "小時", diff --git a/packages/web/i18n/zh-Hant/common.json b/packages/web/i18n/zh-Hant/common.json index 793e88a02b7f..2ed2178b5730 100644 --- a/packages/web/i18n/zh-Hant/common.json +++ b/packages/web/i18n/zh-Hant/common.json @@ -111,7 +111,7 @@ "bill_expired": "訂單已過期", "bill_not_found": "訂單不存在", "bill_not_pay_processed": "非在線訂單", - "button.extra_dataset_size_tip": "您正在購買【額外知識庫容量】", + "button.extra_dataset_size_tip": "您正在購買【額外知識庫索引量】", "button.extra_points_tip": "您正在購買【額外 AI 積分】", "can_copy_content_tip": "無法使用瀏覽器自動複製,請手動複製下面內容", "cancel_bill": "取消", @@ -881,7 +881,7 @@ "pay_corporate_payment": "對公支付", "pay_money": "應付金額", "pay_success": "支付成功", - "pay_year_tip": "支付 10 個月,暢享 1 年!", + "pay_year_tip": "支付 {{count}} 個月,暢享一年!", "permission.Collaborator": "協作者", "permission.Manage": "管理", "permission.No InheritPermission": "已限制權限,不再繼承上層資料夾的權限", @@ -1012,7 +1012,7 @@ "support.wallet.App_amount_not_sufficient": "您的應用數量已達上限,請升級套餐後繼續使用。", "support.wallet.Buy": "購買", "support.wallet.Dataset_amount_not_sufficient": "您的知識庫數量已達上限,請升級套餐後繼續使用。", - "support.wallet.Dataset_not_sufficient": "您的知識庫容量不足,請先升級套餐或購買額外知識庫容量後繼續使用。", + "support.wallet.Dataset_not_sufficient": "您的知識庫容量不足,請先升級套餐或購買額外知識庫索引量後繼續使用。", "support.wallet.Not sufficient": "您的 AI 點數不足,請先升級方案或購買額外 AI 點數後繼續使用。", "support.wallet.Not_sufficient_contact_admin": "您的 AI 點數不足,請聯絡團隊管理員。", "support.wallet.Team_member_over_size": "您的團隊成員數量已達上限,請升級套餐後繼續使用。", @@ -1035,11 +1035,15 @@ "support.wallet.subscription.Dataset size": "知識庫索引量", "support.wallet.subscription.Extra ai points": "額外 AI 點數", "support.wallet.subscription.Extra dataset description": "支持根據實際需求為知識庫索引量延長有效期", - "support.wallet.subscription.Extra dataset size": "額外知識庫容量", + "support.wallet.subscription.Extra dataset size": "額外知識庫索引量", "support.wallet.subscription.Extra dataset unit": "組/1個月", "support.wallet.subscription.Extra plan": "額外資源包", - "support.wallet.subscription.Extra plan tip": "當標準方案不足時,您可以購買額外資源包繼續使用", + "support.wallet.subscription.Extra plan tip": "基礎套餐容量不足時,可購買額外資源包繼續使用", "support.wallet.subscription.FAQ": "常見問題", + "support.wallet.subscription.Basic plan tab": "基礎套餐", + "support.wallet.subscription.Extra points tab": "額外積分", + "support.wallet.subscription.Extra dataset tab": "額外知識庫索引量", + "support.wallet.subscription.Purchase plan": "購買套餐", "support.wallet.subscription.Sub plan": "訂閱方案", "support.wallet.subscription.Sub plan tip": "免費使用【{{title}}】或升級更進階的方案", "support.wallet.subscription.Sub plan tip wecom": "購買方案以享受應用服務", @@ -1050,7 +1054,9 @@ "support.wallet.subscription.ai_model": "AI 語言模型", "support.wallet.subscription.extra_plan_disabled_tip": "如需購買額外資源包,請先訂閱方案。", "support.wallet.subscription.mode.Month": "按月", + "support.wallet.subscription.mode.Month pay": "按月付", "support.wallet.subscription.mode.Year": "按年", + "support.wallet.subscription.mode.Year pay": "按年付", "support.wallet.subscription.per_year": "/ 年", "support.wallet.subscription.point": "點數", "support.wallet.subscription.standardSubLevel.advanced": "高級版", diff --git a/projects/app/public/imgs/price/extraDatasetIcon.svg b/projects/app/public/imgs/price/extraDatasetIcon.svg new file mode 100644 index 000000000000..2f3f5e1038c7 --- /dev/null +++ b/projects/app/public/imgs/price/extraDatasetIcon.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/app/src/pageComponents/price/ExtraPlan.tsx b/projects/app/src/pageComponents/price/ExtraPlan.tsx index 7bff78ed2025..87b10f4f6148 100644 --- a/projects/app/src/pageComponents/price/ExtraPlan.tsx +++ b/projects/app/src/pageComponents/price/ExtraPlan.tsx @@ -17,7 +17,13 @@ import { formatActivityExpirationTime } from './utils'; import { useUserStore } from '@/web/support/user/useUserStore'; import { StandardSubLevelEnum } from '@fastgpt/global/support/wallet/sub/constants'; -const ExtraPlan = ({ onPaySuccess }: { onPaySuccess?: () => void }) => { +const ExtraPlan = ({ + onPaySuccess, + mode = 'all' +}: { + onPaySuccess?: () => void; + mode?: 'all' | 'points' | 'dataset'; +}) => { const { t, i18n } = useTranslation(); const { toast } = useToast(); const { subPlans } = useSystemStore(); @@ -119,388 +125,468 @@ const ExtraPlan = ({ onPaySuccess }: { onPaySuccess?: () => void }) => { subPlans?.activityExpirationTime ); + const showPoints = mode === 'all' || mode === 'points'; + const showDataset = mode === 'all' || mode === 'dataset'; + const isDualCardLayout = mode === 'all'; + + const dualCardStyle = { + borderRadius: '16px', + borderWidth: '1px', + borderColor: '#E8EBF0', + bg: '#FFF', + boxShadow: '0 1px 2px 0 rgba(19, 51, 107, 0.10), 0 0 1px 0 rgba(19, 51, 107, 0.15)' + } as const; + + const dualCardBaseProps = { + display: 'flex' as const, + flexDirection: 'column' as const, + alignItems: 'flex-start' as const, + w: ['100%', '483.016px'], + h: ['auto', '488px'], + p: ['16px', '24px 32px'], + flexShrink: 0, + ...dualCardStyle + }; + return ( - - - - {subPlans?.activityExpirationTime && ( - <> - - - - - )} + + + {showPoints && ( - {t('common:support.wallet.subscription.Extra ai points')} - - {activityExpirationTime} - - - - {extraPointsPackages.map((pkg, index) => ( - 6 ? 1 : 2} - px={[3, 4]} - borderRadius={['8px', 'sm']} - borderWidth={'1px'} - borderColor={selectedPackageIndex === index ? '#3E78FF' : 'myGray.200'} - bg={selectedPackageIndex === index ? 'primary.25' : 'white'} - cursor={'pointer'} - _hover={{ - borderColor: '#3E78FF', - bg: 'primary.25' - }} - onClick={() => setSelectedPackageIndex(index)} - transition={'all 0.2s'} - position={'relative'} - overflow={'hidden'} - > - {!!pkg.activityBonusPoints && ( - - +{formatNumberWithUnit(pkg.activityBonusPoints, i18n.language)} - - )} - - {formatNumberWithUnit(pkg.points, i18n.language)}{' '} - - {t('common:support.wallet.subscription.point')} - - + {subPlans?.activityExpirationTime && ( + <> - {t('common:invalid_time') + ' '} - {getMonthText(pkg.month)} - - - ))} - - - + position={'absolute'} + top={8} + left={'36%'} + width={'55px'} + height={'64px'} + zIndex={0} + bgImage={'url(/imgs/system/extraSnowflake1.svg)'} + backgroundSize="100% 100%" + backgroundRepeat="no-repeat" + /> + + + + )} - {t('common:support.wallet.subscription.total_points')} - - - {selectedPackageIndex !== undefined && extraPointsPackages[selectedPackageIndex] - ? formatNumberWithUnit( - extraPointsPackages[selectedPackageIndex].points + - (extraPointsPackages[selectedPackageIndex]?.activityBonusPoints || 0), - i18n.language - ) - : '--'} + {t('common:support.wallet.subscription.Extra ai points')} + + {activityExpirationTime} + - - - - {t('common:support.wallet.subscription.Update extra price')} - - - {selectedPackageIndex !== undefined && extraPointsPackages[selectedPackageIndex] - ? t('common:extraPointsPrice', { - price: extraPointsPackages[selectedPackageIndex].price - }) - : '--'} - - - - - - - - - {t('common:support.wallet.subscription.Update extra ai points tips')} - - - + {extraPointsPackages.map((pkg, index) => ( + 6 ? 1 : 2} + px={[3, 4]} + borderRadius={['8px', 'sm']} + borderWidth={'1px'} + borderColor={selectedPackageIndex === index ? '#3E78FF' : 'myGray.200'} + bg={selectedPackageIndex === index ? 'primary.25' : 'white'} + cursor={'pointer'} + _hover={{ + borderColor: '#3E78FF', + bg: 'primary.25' + }} + onClick={() => setSelectedPackageIndex(index)} + transition={'all 0.2s'} + position={'relative'} + overflow={'hidden'} + > + {!!pkg.activityBonusPoints && ( + + +{formatNumberWithUnit(pkg.activityBonusPoints, i18n.language)} + + )} + + {formatNumberWithUnit(pkg.points, i18n.language)}{' '} + + {t('common:support.wallet.subscription.point')} + + + + {t('common:invalid_time') + ' '} + {getMonthText(pkg.month)} + + + ))} + - {/* dataset */} - - - - - {t('common:support.wallet.subscription.Extra dataset size')} - - - {`¥${extraDatasetPrice}/1000${t('common:support.wallet.subscription.Extra dataset unit')}`} - + - {t('common:support.wallet.subscription.Extra dataset description')} + {t('common:support.wallet.subscription.total_points')} - - - - - - - - - {t('common:support.wallet.buy_dataset_capacity')} + + {selectedPackageIndex !== undefined && extraPointsPackages[selectedPackageIndex] + ? formatNumberWithUnit( + extraPointsPackages[selectedPackageIndex].points + + (extraPointsPackages[selectedPackageIndex]?.activityBonusPoints || 0), + i18n.language + ) + : '--'} - - - - {t('common:support.wallet.subscription.Dataset size')} - - - - -  {`X 1000${t('common:core.dataset.data.group')}`} - - - - - - - {t('common:invalid_time')} - - + - setValueDatasetSize('month', val)} - /> - - - - - {t('common:support.wallet.subscription.Update extra price')} - - {`¥${(() => { - const price = calculatePrice(extraDatasetPrice, { - type: 'dataset', - size: watchedDatasetSize, - month: watchedDatasetMonth - }); - return Number.isNaN(price) ? 0 : price; - })()}`} - + + {selectedPackageIndex !== undefined && extraPointsPackages[selectedPackageIndex] + ? t('common:extraPointsPrice', { + price: extraPointsPackages[selectedPackageIndex].price + }) + : '--'} + - - - - - - {t('common:support.wallet.subscription.Update extra dataset tips')} + + + + {t('common:support.wallet.subscription.Update extra ai points tips')} - + - - + )} + + {showDataset && ( + + + + + {t('common:support.wallet.subscription.Extra dataset size')} + + + {`¥${extraDatasetPrice}/1000${t('common:support.wallet.subscription.Extra dataset unit')}`} + + + {t('common:support.wallet.subscription.Extra dataset description')} + + + + + + + + + + {t('common:support.wallet.buy_dataset_capacity')} + + + + + + {t('common:support.wallet.subscription.Dataset size')} + + + + +  {`X 1000${t('common:core.dataset.data.group')}`} + + + + + + + {t('common:invalid_time')} + + + setValueDatasetSize('month', val)} + /> + + + + + + {t('common:support.wallet.subscription.Update extra price')} + + + {`¥${(() => { + const price = calculatePrice(extraDatasetPrice, { + type: 'dataset', + size: watchedDatasetSize, + month: watchedDatasetMonth + }); + return Number.isNaN(price) ? 0 : price; + })()}`} + + + + + + + + + + + {t('common:support.wallet.subscription.Update extra dataset tips')} + + + + + )} + {!!qrPayData && ( = { + label: string; + value: T; +}; + +/** 购买套餐页 Tab,选中项为圆角 pill 白底样式 */ +const PricePlanTabs = ({ + list, + value, + onChange +}: { + list: PricePlanTabItem[]; + value: T; + onChange: (value: T) => void; +}) => { + return ( + + {list.map((item) => { + const isActive = value === item.value; + + return ( + { + if (!isActive) { + onChange(item.value); + } + }} + > + + {item.label} + + + ); + })} + + ); +}; + +export default PricePlanTabs; diff --git a/projects/app/src/pageComponents/price/Standard.tsx b/projects/app/src/pageComponents/price/Standard.tsx index 1913cbd7b042..7065800b049e 100644 --- a/projects/app/src/pageComponents/price/Standard.tsx +++ b/projects/app/src/pageComponents/price/Standard.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useMemo, useState } from 'react'; import MyIcon from '@fastgpt/web/components/common/Icon'; import { Box, Button, Flex, Grid } from '@chakra-ui/react'; -import { useTranslation } from 'next-i18next'; +import { Trans, 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'; @@ -32,12 +32,105 @@ const NEW_PLAN_LEVELS = [ StandardSubLevelEnum.custom ]; +/** 按月/按年切换开关,可在页面 header 区域复用 */ +export const BillingModeSwitch = ({ + value, + onChange +}: { + value: `${SubModeEnum}`; + onChange: (mode: `${SubModeEnum}`) => void; +}) => { + const { t } = useTranslation(); + const isYear = value === SubModeEnum.year; + + return ( + + + {t('common:support.wallet.subscription.mode.Month pay')} + + onChange(isYear ? SubModeEnum.month : SubModeEnum.year)} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + onChange(isYear ? SubModeEnum.month : SubModeEnum.year); + } + }} + > + + + + {t('common:support.wallet.subscription.mode.Year pay')} + + + + }} + /> + + + ); +}; + const Standard = ({ standardPlan: myStandardPlan, - onPaySuccess + onPaySuccess, + selectSubMode: controlledSubMode, + onSelectSubModeChange, + hideBillingToggle = false }: { standardPlan?: TeamPlanStandardType; onPaySuccess?: () => void; + selectSubMode?: `${SubModeEnum}`; + onSelectSubModeChange?: (mode: `${SubModeEnum}`) => void; + hideBillingToggle?: boolean; }) => { const { t } = useTranslation(); const { userInfo } = useUserStore(); @@ -53,9 +146,11 @@ const Standard = ({ const [packageChange, setPackageChange] = useState(); const { subPlans, feConfigs } = useSystemStore(); - const [selectSubMode, setSelectSubMode] = useState<`${SubModeEnum}`>( + const [internalSubMode, setInternalSubMode] = useState<`${SubModeEnum}`>( isWecomTeam ? SubModeEnum.year : SubModeEnum.month ); + const selectSubMode = controlledSubMode ?? internalSubMode; + const setSelectSubMode = onSelectSubModeChange ?? setInternalSubMode; const hasActivityExpiration = !!subPlans?.activityExpirationTime && selectSubMode === SubModeEnum.year; @@ -132,7 +227,7 @@ const Standard = ({ }) : []; }, [ - subPlans?.standard, + subPlans, isWecomTeam, selectSubMode, myStandardPlan?.maxTeamMember, @@ -166,56 +261,17 @@ const Standard = ({ return ( <> - - {!isWecomTeam && ( - - - - {t('common:pay_year_tip')} - - - {t('common:support.wallet.subscription.mode.Year')} - - ), - value: SubModeEnum.year - } - ]} - value={selectSubMode} - onChange={(e) => setSelectSubMode(e as `${SubModeEnum}`)} - /> - - - + + {!hideBillingToggle && !isWecomTeam && ( + )} - {/* card */} - {standardSubList.map((item) => { const isCurrentPlan = item.level === myStandardPlan?.currentSubLevel; @@ -238,21 +294,25 @@ const Standard = ({ {hasActivityExpiration && (item.level === StandardSubLevelEnum.basic || @@ -600,7 +660,7 @@ const Standard = ({ ); })} - + {!!qrPayData && packageChange && ( void; -}) => { - return ( - - {list.map((item) => ( - onChange(item.value) - })} - > - {item.icon && } - {item.label} - - ))} - - ); -}; diff --git a/projects/app/src/pages/price/index.tsx b/projects/app/src/pages/price/index.tsx index 30f7524c8bb2..e265b97e57d0 100644 --- a/projects/app/src/pages/price/index.tsx +++ b/projects/app/src/pages/price/index.tsx @@ -1,10 +1,10 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { serviceSideProps } from '@/web/common/i18n/utils'; -import { Box, Button, Flex, HStack, IconButton, VStack } from '@chakra-ui/react'; +import { Box, Button, Flex, HStack, IconButton } from '@chakra-ui/react'; import { useUserStore } from '@/web/support/user/useUserStore'; import { getTeamPlanStatus } from '@/web/support/user/team/api'; -import StandardPlan from '@/pageComponents/price/Standard'; +import StandardPlan, { BillingModeSwitch } from '@/pageComponents/price/Standard'; import ExtraPlan from '@/pageComponents/price/ExtraPlan'; import PointsCard from '@/pageComponents/price/Points'; import FAQ from '@/pageComponents/price/FAQ'; @@ -14,15 +14,34 @@ import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useRouter } from 'next/router'; import { useRequest } from '@fastgpt/web/hooks/useRequest'; import MyLoading from '@fastgpt/web/components/common/MyLoading'; +import PricePlanTabs from '@/pageComponents/price/PricePlanTabs'; +import { SubModeEnum } from '@fastgpt/global/support/wallet/sub/constants'; + +type PriceTabType = 'standard' | 'extraPoints' | 'extraDataset'; + +const EXTRA_PLAN_HASH = 'extra-plan'; + +/** 根据 URL hash 解析默认 Tab(如账户页「购买额外套餐」跳转 /price#extra-plan) */ +const getTabFromHash = (hash: string): PriceTabType | undefined => { + if (hash === EXTRA_PLAN_HASH) { + return 'extraPoints'; + } + return undefined; +}; const PriceBox = () => { const { initUserInfo } = useUserStore(); const { t } = useTranslation(); - const { feConfigs } = useSystemStore(); + const { subPlans } = useSystemStore(); const router = useRouter(); const backButtonRef = useRef(null); const [isButtonInView, setIsButtonInView] = useState(true); + const [userActiveTab, setUserActiveTab] = useState(() => { + if (typeof window === 'undefined') return 'standard'; + return getTabFromHash(window.location.hash.slice(1)) ?? 'standard'; + }); + const [userSubMode, setUserSubMode] = useState<`${SubModeEnum}`>(SubModeEnum.month); const { data: userInfo, loading: userInfoLoading } = useRequest(initUserInfo, { manual: false @@ -33,11 +52,28 @@ const PriceBox = () => { refreshDeps: [userInfo] }); + const hashTab = useMemo(() => { + if (!router.isReady) return undefined; + return getTabFromHash(router.asPath.split('#')[1] ?? ''); + }, [router.isReady, router.asPath]); + + const activeTab = hashTab ?? userActiveTab; + const selectSubMode = subPlans?.activityExpirationTime ? SubModeEnum.year : userSubMode; + + const handleTabChange = useCallback( + (value: PriceTabType) => { + setUserActiveTab(value); + if (router.asPath.includes('#')) { + void router.replace('/price', undefined, { shallow: true }); + } + }, + [router] + ); + // TODO: 封装成一个 hook 来判断滚动态 useEffect(() => { const observer = new IntersectionObserver( ([entry]) => { - // 当按钮在视图内时,隐藏浮动按钮;当按钮不在视图内时,显示浮动按钮 setIsButtonInView(entry.isIntersecting); }, { @@ -60,11 +96,9 @@ const PriceBox = () => { }); const handleBack = useCallback(() => { - // Check if there is history to go back to if (window.history.length > 1) { router.back(); } else { - // No history, navigate to home page router.push('/dashboard/agent'); } }, [router]); @@ -76,10 +110,26 @@ const PriceBox = () => { }, [router]); const isWecomTeam = useMemo(() => !!userInfo?.team?.isWecomTeam, [userInfo?.team?.isWecomTeam]); - - // Show loading state while fetching user info to prevent flash of incorrect content const isLoading = userInfoLoading || teamSubPlanLoading; + const tabList = useMemo( + () => [ + { + label: t('common:support.wallet.subscription.Basic plan tab'), + value: 'standard' as const + }, + { + label: t('common:support.wallet.subscription.Extra points tab'), + value: 'extraPoints' as const + }, + { + label: t('common:support.wallet.subscription.Extra dataset tab'), + value: 'extraDataset' as const + } + ], + [t] + ); + return ( <> {isLoading ? ( @@ -90,81 +140,114 @@ const PriceBox = () => { flexDir={'column'} overflow={'overlay'} w={'100%'} - px={['20px', '5vw']} - py={['30px', '80px']} bg={`linear-gradient(to right, #F8F8FD00, #F7F7FF),url(/imgs/priceBg.svg)`} backgroundSize={'cover'} backgroundRepeat={'no-repeat'} > - {teamSubPlan?.standard?.teamId && ( - - )} - {(!isButtonInView || !teamSubPlan?.standard?.teamId) && ( - } - onClick={handleBack} - /> - )} - - {/* standard sub */} - - - {t('common:support.wallet.subscription.Sub plan')} - - - {isWecomTeam - ? t('common:support.wallet.subscription.Sub plan tip wecom') - : t('common:support.wallet.subscription.Sub plan tip', { - title: feConfigs?.systemTitle - })} - - - - - - {t('user:bill.standard_valid_tip')} + + {teamSubPlan?.standard?.teamId && ( + + )} + {(!isButtonInView || !teamSubPlan?.standard?.teamId) && ( + } + onClick={handleBack} + /> + )} + + + + {t('common:support.wallet.subscription.Purchase plan')} + + + + handleTabChange(value as PriceTabType)} + /> - - - - {/* extra plan */} - - - {t('common:support.wallet.subscription.Extra plan')} - - - {t('common:support.wallet.subscription.Extra plan tip')} - - - - - {/* points */} - - - {/* question */} - + + {activeTab === 'standard' && !isWecomTeam && ( + + + + )} + + {activeTab !== 'standard' && ( + + {t('common:support.wallet.subscription.Extra plan tip')} + + )} + + + {activeTab === 'standard' && ( + + + + + + {t('user:bill.standard_valid_tip')} + + + + )} + + {activeTab !== 'standard' && ( + + + + )} + + + {/* AI 积分计算标准、FAQ:保持原页面布局,不受 tab 区域 maxW / padding 影响 */} + + + + )}