aura-web/src/pages/Pricing/PricingLogic.ts

196 lines
5.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import { useState, useEffect, useMemo } from 'react';
import { App as AntApp } from 'antd';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
import { MembershipAPI, MembershipInfo } from '../../api/membership';
export interface PricingTier {
id: string;
name: string;
price: string;
yearlyPrice?: string;
yearlyMonthlyPrice?: string;
unit: string;
description: string;
features: string[];
points?: string;
models?: string;
bonus?: string;
tag?: string;
buttonText: string;
type: 'personal' | 'enterprise';
}
export const PERSONAL_TIERS: PricingTier[] = [
{
id: 'trial',
name: 'Trial',
price: '0',
unit: '人民币/月',
description: '适合首次探索和轻量使用',
features: ['首次探索和轻量任务。', '试用期后,可以升级至更高级版本。'],
points: '每日登录积分 10',
bonus: 'Aura Work: 7天试用积分',
tag: '体验',
buttonText: '当前订阅',
type: 'personal',
},
{
id: 'pro',
name: 'Pro',
price: '139',
yearlyPrice: '1390',
yearlyMonthlyPrice: '116',
unit: '人民币/月',
description: '每周工作与日常执行',
features: ['适合每周轻量调研、数据分析和任务跟进。', '为稳定的每周产出而设,无需顾虑额外消耗', '限时优惠:订阅用户可享受更多专属权益。'],
points: '每日登录积分 30',
models: '高质量模型 50次 / 3小时',
bonus: '会员积分 4000 / 月',
tag: '热门',
buttonText: '开始',
type: 'personal',
},
{
id: 'ultra',
name: 'Ultra',
price: '1399',
yearlyPrice: '13990',
yearlyMonthlyPrice: '1116',
unit: '人民币/月',
description: '超大项目与更高配置',
features: ['适合高强度任务与重度使用场景', '面向需要更高积分与更大灵活性的资源用户', '限时优惠:订阅用户可享受更多专属权益。', '个人套餐顶配版,包含全部能力'],
points: '每日登录积分 100',
models: '无限制使用高质量模型',
bonus: '会员积分 40000 / 月',
tag: '旗舰',
buttonText: '开始',
type: 'personal',
},
];
export const ENTERPRISE_TIERS: PricingTier[] = [
{
id: 'ent_basic',
name: '企业入门版',
price: '20000',
unit: '人民币/年',
description: '适合快速起步的小团队',
features: ['所有子账号共享企业积分池', '全员共享插件', '企业发票 (增值税普票)', '用量报告与成员消耗排行'],
points: '企业积分池 35000 / 月',
models: '高质量模型配额 100次 / 3小时',
bonus: '子账号上限 5',
tag: '基础',
buttonText: '立即开通',
type: 'enterprise',
},
{
id: 'ent_standard',
name: '企业标准版',
price: '40000',
unit: '人民币/年',
description: '适合日常协作提效',
features: ['所有子账号共享企业积分池', '全员共享插件', '企业发票 (增值税普票)', '用量报告与成员消耗排行', '成员角色管理'],
points: '企业积分池 80000 / 月',
models: '高质量模型配额 100次 / 3小时',
bonus: '子账号上限 15',
tag: '热门',
buttonText: '立即开通',
type: 'enterprise',
},
{
id: 'custom',
name: '按需定制',
price: '面议',
unit: '',
description: '适合大型团队 / 行业方案',
features: ['独立部署 / 专属网络环境', '统一登录 / 操作记录可追溯', '行业专属插件与技能', '服务保障与 7×24 专属支持', '专属折扣'],
points: '企业积分池 面议',
models: '高质量模型配额 不限量',
bonus: '子账号上限 不限量',
tag: '专享',
buttonText: '联系我们',
type: 'enterprise',
},
];
export function usePricingLogic() {
const navigate = useNavigate();
const { tierId } = useParams();
const [searchParams] = useSearchParams();
const cycle = searchParams.get('cycle') || 'monthly';
const [activeTab, setActiveTab] = useState<'personal' | 'enterprise'>('personal');
const [billingCycle, setBillingCycle] = useState<'monthly' | 'yearly'>(cycle as any);
const [membership, setMembership] = useState<MembershipInfo | null>(null);
const [loading, setLoading] = useState(false);
const [timeLeft, setTimeLeft] = useState(600); // 10 minutes in seconds
const { message } = AntApp.useApp();
// 根据 URL 参数计算当前的支付信息
const paymentInfo = useMemo(() => {
if (!tierId) return null;
const allTiers = [...PERSONAL_TIERS, ...ENTERPRISE_TIERS];
const tier = allTiers.find(t => t.id === tierId);
if (!tier) return null;
return {
tier,
duration: cycle === 'monthly' ? 30 : 365
};
}, [tierId, cycle]);
useEffect(() => {
loadMembership();
}, []);
useEffect(() => {
let timer: any;
if (paymentInfo && timeLeft > 0) {
timer = setInterval(() => {
setTimeLeft((prev) => prev - 1);
}, 1000);
} else if (timeLeft === 0) {
navigate('/pricing', { replace: true });
message.error('支付超时,请重新发起');
}
return () => clearInterval(timer);
}, [paymentInfo, timeLeft, navigate]);
const loadMembership = async () => {
try {
const info = await MembershipAPI.getMe();
setMembership(info);
} catch (e) {
console.error('Failed to load membership', e);
}
};
const handleSubscribe = async (tier: PricingTier) => {
if (tier.price === '面议' || tier.id === 'trial') return;
// 使用子路由跳转
navigate(`/pricing/pay/${tier.id}?cycle=${billingCycle}`);
setTimeLeft(600);
};
const cancelPayment = () => {
navigate('/pricing');
};
return {
activeTab,
setActiveTab,
billingCycle,
setBillingCycle,
membership,
loading,
handleSubscribe,
paymentInfo,
timeLeft,
cancelPayment,
PERSONAL_TIERS,
ENTERPRISE_TIERS,
};
}
export type PricingLogicOutput = ReturnType<typeof usePricingLogic>;