2026-03-25 13:46:26 +08:00
|
|
|
|
import React, { useState, useMemo } from 'react';
|
|
|
|
|
|
import {
|
|
|
|
|
|
Card, Row, Col, Switch, Tag, Typography, Space, Button, Modal,
|
|
|
|
|
|
Progress, Divider, Alert, Tabs, Table, message, Statistic,
|
|
|
|
|
|
Timeline, Badge, Tooltip, Radio, Empty, Steps, theme, ConfigProvider
|
|
|
|
|
|
} from 'antd';
|
|
|
|
|
|
import type { ColumnsType } from 'antd/es/table';
|
2026-03-22 11:25:28 +08:00
|
|
|
|
import {
|
|
|
|
|
|
CrownOutlined,
|
|
|
|
|
|
RobotOutlined,
|
|
|
|
|
|
ShopOutlined,
|
|
|
|
|
|
GlobalOutlined,
|
|
|
|
|
|
LineChartOutlined,
|
|
|
|
|
|
ApiOutlined,
|
|
|
|
|
|
CheckCircleOutlined,
|
|
|
|
|
|
CloseCircleOutlined,
|
|
|
|
|
|
ThunderboltOutlined,
|
|
|
|
|
|
StarOutlined,
|
2026-03-25 13:46:26 +08:00
|
|
|
|
ArrowUpOutlined,
|
|
|
|
|
|
ArrowDownOutlined,
|
|
|
|
|
|
InfoCircleOutlined,
|
|
|
|
|
|
GiftOutlined,
|
|
|
|
|
|
SafetyOutlined,
|
|
|
|
|
|
RocketOutlined,
|
|
|
|
|
|
TeamOutlined,
|
|
|
|
|
|
ClockCircleOutlined,
|
|
|
|
|
|
DollarOutlined,
|
|
|
|
|
|
CheckSquareOutlined,
|
|
|
|
|
|
CloseSquareOutlined,
|
|
|
|
|
|
RightOutlined,
|
2026-03-22 11:25:28 +08:00
|
|
|
|
} from '@ant-design/icons';
|
|
|
|
|
|
import { useUser, FEATURES, ROLE_CONFIG } from '@/contexts/UserContext';
|
|
|
|
|
|
|
|
|
|
|
|
const { Title, Text, Paragraph } = Typography;
|
|
|
|
|
|
|
|
|
|
|
|
interface FeatureConfig {
|
|
|
|
|
|
key: string;
|
|
|
|
|
|
name: string;
|
|
|
|
|
|
description: string;
|
|
|
|
|
|
icon: React.ReactNode;
|
|
|
|
|
|
requiredPlan: string[];
|
|
|
|
|
|
benefits: string[];
|
|
|
|
|
|
limitations?: string[];
|
2026-03-25 13:46:26 +08:00
|
|
|
|
popularity?: number;
|
|
|
|
|
|
category: 'core' | 'advanced' | 'enterprise';
|
2026-03-22 11:25:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const FEATURE_CONFIGS: FeatureConfig[] = [
|
|
|
|
|
|
{
|
|
|
|
|
|
key: FEATURES.AI_OPERATIONS,
|
|
|
|
|
|
name: 'AI运营中心',
|
|
|
|
|
|
description: '智能运营助手,自动化执行日常运营任务',
|
|
|
|
|
|
icon: <RobotOutlined style={{ fontSize: 24 }} />,
|
|
|
|
|
|
requiredPlan: ['basic', 'pro', 'enterprise'],
|
|
|
|
|
|
benefits: [
|
|
|
|
|
|
'自动选品推荐',
|
|
|
|
|
|
'智能定价策略',
|
|
|
|
|
|
'自动上下架',
|
|
|
|
|
|
'AI决策日志追踪',
|
|
|
|
|
|
'策略市场访问',
|
|
|
|
|
|
],
|
|
|
|
|
|
limitations: [
|
|
|
|
|
|
'免费版: 每日仅3次AI调用',
|
|
|
|
|
|
'基础版: 每日100次AI调用',
|
|
|
|
|
|
'专业版: 无限AI调用',
|
|
|
|
|
|
],
|
2026-03-25 13:46:26 +08:00
|
|
|
|
popularity: 95,
|
|
|
|
|
|
category: 'core',
|
2026-03-22 11:25:28 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: FEATURES.AUTO_PRICING,
|
|
|
|
|
|
name: '自动定价',
|
|
|
|
|
|
description: '基于市场数据和竞争分析的智能定价系统',
|
|
|
|
|
|
icon: <ThunderboltOutlined style={{ fontSize: 24 }} />,
|
|
|
|
|
|
requiredPlan: ['pro', 'enterprise'],
|
|
|
|
|
|
benefits: [
|
|
|
|
|
|
'实时竞品价格监控',
|
|
|
|
|
|
'动态定价策略',
|
|
|
|
|
|
'利润率自动优化',
|
|
|
|
|
|
'价格预警通知',
|
|
|
|
|
|
],
|
|
|
|
|
|
limitations: [
|
|
|
|
|
|
'基础版: 仅查看定价建议',
|
|
|
|
|
|
'专业版: 自动执行定价',
|
|
|
|
|
|
],
|
2026-03-25 13:46:26 +08:00
|
|
|
|
popularity: 88,
|
|
|
|
|
|
category: 'advanced',
|
2026-03-22 11:25:28 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: FEATURES.MULTI_SHOP,
|
|
|
|
|
|
name: '多店铺管理',
|
|
|
|
|
|
description: '统一管理多个电商平台的店铺',
|
|
|
|
|
|
icon: <ShopOutlined style={{ fontSize: 24 }} />,
|
|
|
|
|
|
requiredPlan: ['basic', 'pro', 'enterprise'],
|
|
|
|
|
|
benefits: [
|
|
|
|
|
|
'跨平台商品同步',
|
|
|
|
|
|
'统一库存管理',
|
|
|
|
|
|
'多店铺数据报表',
|
|
|
|
|
|
'批量操作支持',
|
|
|
|
|
|
],
|
|
|
|
|
|
limitations: [
|
|
|
|
|
|
'免费版: 仅支持1个店铺',
|
|
|
|
|
|
'基础版: 最多3个店铺',
|
|
|
|
|
|
'专业版: 最多10个店铺',
|
|
|
|
|
|
'企业版: 无限店铺',
|
|
|
|
|
|
],
|
2026-03-25 13:46:26 +08:00
|
|
|
|
popularity: 92,
|
|
|
|
|
|
category: 'core',
|
2026-03-22 11:25:28 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: FEATURES.B2B_TRADE,
|
|
|
|
|
|
name: 'B2B贸易',
|
|
|
|
|
|
description: '企业级批发贸易管理功能',
|
|
|
|
|
|
icon: <ShopOutlined style={{ fontSize: 24 }} />,
|
|
|
|
|
|
requiredPlan: ['basic', 'pro', 'enterprise'],
|
|
|
|
|
|
benefits: [
|
|
|
|
|
|
'供应商管理',
|
|
|
|
|
|
'批量订单处理',
|
|
|
|
|
|
'B2B专属定价',
|
|
|
|
|
|
'合同管理',
|
|
|
|
|
|
],
|
2026-03-25 13:46:26 +08:00
|
|
|
|
popularity: 75,
|
|
|
|
|
|
category: 'core',
|
2026-03-22 11:25:28 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: FEATURES.INDEPENDENT_SITE,
|
|
|
|
|
|
name: '独立站',
|
|
|
|
|
|
description: '创建和管理独立电商网站',
|
|
|
|
|
|
icon: <GlobalOutlined style={{ fontSize: 24 }} />,
|
|
|
|
|
|
requiredPlan: ['pro', 'enterprise'],
|
|
|
|
|
|
benefits: [
|
|
|
|
|
|
'自定义品牌站点',
|
|
|
|
|
|
'独立域名绑定',
|
|
|
|
|
|
'SEO优化工具',
|
|
|
|
|
|
'独立支付集成',
|
|
|
|
|
|
],
|
2026-03-25 13:46:26 +08:00
|
|
|
|
popularity: 65,
|
|
|
|
|
|
category: 'advanced',
|
2026-03-22 11:25:28 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: FEATURES.ADVANCED_ANALYTICS,
|
|
|
|
|
|
name: '高级数据分析',
|
|
|
|
|
|
description: '深度数据分析和可视化报表',
|
|
|
|
|
|
icon: <LineChartOutlined style={{ fontSize: 24 }} />,
|
|
|
|
|
|
requiredPlan: ['pro', 'enterprise'],
|
|
|
|
|
|
benefits: [
|
|
|
|
|
|
'自定义报表',
|
|
|
|
|
|
'数据导出',
|
|
|
|
|
|
'趋势预测',
|
|
|
|
|
|
'竞品分析报告',
|
|
|
|
|
|
],
|
|
|
|
|
|
limitations: [
|
|
|
|
|
|
'基础版: 仅基础报表',
|
|
|
|
|
|
'专业版: 完整分析功能',
|
|
|
|
|
|
],
|
2026-03-25 13:46:26 +08:00
|
|
|
|
popularity: 80,
|
|
|
|
|
|
category: 'advanced',
|
2026-03-22 11:25:28 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: FEATURES.API_ACCESS,
|
|
|
|
|
|
name: 'API访问',
|
|
|
|
|
|
description: '开放API接口,支持系统集成',
|
|
|
|
|
|
icon: <ApiOutlined style={{ fontSize: 24 }} />,
|
|
|
|
|
|
requiredPlan: ['enterprise'],
|
|
|
|
|
|
benefits: [
|
|
|
|
|
|
'RESTful API',
|
|
|
|
|
|
'Webhook支持',
|
|
|
|
|
|
'SDK下载',
|
|
|
|
|
|
'技术文档',
|
|
|
|
|
|
],
|
2026-03-25 13:46:26 +08:00
|
|
|
|
popularity: 55,
|
|
|
|
|
|
category: 'enterprise',
|
2026-03-22 11:25:28 +08:00
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
const PLAN_DETAILS = {
|
|
|
|
|
|
free: {
|
|
|
|
|
|
name: '免费版',
|
|
|
|
|
|
price: 0,
|
2026-03-25 13:46:26 +08:00
|
|
|
|
color: '#d9d9d9',
|
|
|
|
|
|
gradient: 'linear-gradient(135deg, #d9d9d9 0%, #bfbfbf 100%)',
|
2026-03-22 11:25:28 +08:00
|
|
|
|
features: ['基础商品管理', '订单管理', '基础报表'],
|
|
|
|
|
|
limitations: ['1个店铺', '每日3次AI调用', '无API访问'],
|
2026-03-25 13:46:26 +08:00
|
|
|
|
recommended: false,
|
|
|
|
|
|
popular: false,
|
|
|
|
|
|
icon: <ClockCircleOutlined />,
|
|
|
|
|
|
description: '适合个人用户和小规模卖家',
|
2026-03-22 11:25:28 +08:00
|
|
|
|
},
|
|
|
|
|
|
basic: {
|
|
|
|
|
|
name: '基础版',
|
|
|
|
|
|
price: 99,
|
2026-03-25 13:46:26 +08:00
|
|
|
|
color: '#1890ff',
|
|
|
|
|
|
gradient: 'linear-gradient(135deg, #1890ff 0%, #096dd9 100%)',
|
2026-03-22 11:25:28 +08:00
|
|
|
|
features: ['多店铺管理(3个)', 'AI运营(100次/日)', 'B2B贸易', '基础定价建议'],
|
|
|
|
|
|
limitations: ['无独立站', '无API访问'],
|
2026-03-25 13:46:26 +08:00
|
|
|
|
recommended: true,
|
|
|
|
|
|
popular: true,
|
|
|
|
|
|
icon: <RocketOutlined />,
|
|
|
|
|
|
description: '适合成长中的电商卖家',
|
2026-03-22 11:25:28 +08:00
|
|
|
|
},
|
|
|
|
|
|
pro: {
|
|
|
|
|
|
name: '专业版',
|
|
|
|
|
|
price: 299,
|
2026-03-25 13:46:26 +08:00
|
|
|
|
color: '#faad14',
|
|
|
|
|
|
gradient: 'linear-gradient(135deg, #faad14 0%, #d48806 100%)',
|
2026-03-22 11:25:28 +08:00
|
|
|
|
features: ['多店铺管理(10个)', '无限AI调用', '自动定价', '独立站', '高级分析'],
|
|
|
|
|
|
limitations: ['无API访问'],
|
2026-03-25 13:46:26 +08:00
|
|
|
|
recommended: false,
|
|
|
|
|
|
popular: false,
|
|
|
|
|
|
icon: <StarOutlined />,
|
|
|
|
|
|
description: '适合专业电商运营团队',
|
2026-03-22 11:25:28 +08:00
|
|
|
|
},
|
|
|
|
|
|
enterprise: {
|
|
|
|
|
|
name: '企业版',
|
|
|
|
|
|
price: 999,
|
2026-03-25 13:46:26 +08:00
|
|
|
|
color: '#722ed1',
|
|
|
|
|
|
gradient: 'linear-gradient(135deg, #722ed1 0%, #531dab 100%)',
|
2026-03-22 11:25:28 +08:00
|
|
|
|
features: ['无限店铺', '全部功能', 'API访问', '专属客服', '定制开发'],
|
|
|
|
|
|
limitations: [],
|
2026-03-25 13:46:26 +08:00
|
|
|
|
recommended: false,
|
|
|
|
|
|
popular: false,
|
|
|
|
|
|
icon: <CrownOutlined />,
|
|
|
|
|
|
description: '适合大型企业和定制化需求',
|
2026-03-22 11:25:28 +08:00
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-03-25 13:46:26 +08:00
|
|
|
|
const USAGE_DATA = {
|
|
|
|
|
|
aiCalls: { used: 45, total: 100, plan: 'basic' },
|
|
|
|
|
|
shops: { used: 2, total: 3, plan: 'basic' },
|
|
|
|
|
|
storage: { used: 75, total: 100, unit: 'GB', plan: 'basic' },
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
interface PlanComparisonData {
|
|
|
|
|
|
key: string;
|
|
|
|
|
|
name: string;
|
|
|
|
|
|
icon: React.ReactNode;
|
|
|
|
|
|
description: string;
|
|
|
|
|
|
popularity: number;
|
|
|
|
|
|
category: 'core' | 'advanced' | 'enterprise';
|
|
|
|
|
|
free: boolean;
|
|
|
|
|
|
basic: boolean;
|
|
|
|
|
|
pro: boolean;
|
|
|
|
|
|
enterprise: boolean;
|
|
|
|
|
|
current: boolean;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-22 11:25:28 +08:00
|
|
|
|
const SubscriptionManage: React.FC = () => {
|
|
|
|
|
|
const { currentUser, hasFeature, isPaidUser, getPlanLabel } = useUser();
|
|
|
|
|
|
const [previewFeature, setPreviewFeature] = useState<FeatureConfig | null>(null);
|
|
|
|
|
|
const [simulatedPlan, setSimulatedPlan] = useState<string | null>(null);
|
2026-03-25 13:46:26 +08:00
|
|
|
|
const [billingCycle, setBillingCycle] = useState<'monthly' | 'yearly'>('monthly');
|
|
|
|
|
|
const [selectedTab, setSelectedTab] = useState('overview');
|
2026-03-22 11:25:28 +08:00
|
|
|
|
|
|
|
|
|
|
const currentPlan = currentUser.subscription?.plan || 'free';
|
|
|
|
|
|
|
|
|
|
|
|
const handleFeaturePreview = (feature: FeatureConfig) => {
|
|
|
|
|
|
setPreviewFeature(feature);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleSimulatePlan = (plan: string) => {
|
|
|
|
|
|
setSimulatedPlan(plan);
|
2026-03-25 13:46:26 +08:00
|
|
|
|
message.success(`已切换到${PLAN_DETAILS[plan as keyof typeof PLAN_DETAILS].name}预览模式`);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleUpgrade = (plan: string) => {
|
|
|
|
|
|
message.info(`升级到${PLAN_DETAILS[plan as keyof typeof PLAN_DETAILS].name}功能开发中`);
|
2026-03-22 11:25:28 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const isFeatureAvailable = (featureKey: string) => {
|
|
|
|
|
|
if (simulatedPlan) {
|
|
|
|
|
|
const feature = FEATURE_CONFIGS.find(f => f.key === featureKey);
|
|
|
|
|
|
return feature?.requiredPlan.includes(simulatedPlan) ?? false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return hasFeature(featureKey);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-03-25 13:46:26 +08:00
|
|
|
|
const getDiscount = () => {
|
|
|
|
|
|
return billingCycle === 'yearly' ? 20 : 0;
|
|
|
|
|
|
};
|
2026-03-22 11:25:28 +08:00
|
|
|
|
|
2026-03-25 13:46:26 +08:00
|
|
|
|
const getAnnualPrice = (monthlyPrice: number) => {
|
|
|
|
|
|
const annualPrice = monthlyPrice * 12;
|
|
|
|
|
|
const discount = getDiscount();
|
|
|
|
|
|
return Math.round(annualPrice * (1 - discount / 100));
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const getDisplayPrice = (price: number) => {
|
|
|
|
|
|
if (billingCycle === 'yearly') {
|
|
|
|
|
|
return Math.round(price * (1 - getDiscount() / 100));
|
|
|
|
|
|
}
|
|
|
|
|
|
return price;
|
|
|
|
|
|
};
|
2026-03-22 11:25:28 +08:00
|
|
|
|
|
2026-03-25 13:46:26 +08:00
|
|
|
|
const planComparisonData = useMemo((): PlanComparisonData[] => {
|
|
|
|
|
|
return FEATURE_CONFIGS.map(feature => ({
|
|
|
|
|
|
key: feature.key,
|
|
|
|
|
|
name: feature.name,
|
|
|
|
|
|
icon: feature.icon,
|
|
|
|
|
|
description: feature.description,
|
|
|
|
|
|
popularity: feature.popularity || 0,
|
|
|
|
|
|
category: feature.category,
|
|
|
|
|
|
free: feature.requiredPlan.includes('free'),
|
|
|
|
|
|
basic: feature.requiredPlan.includes('basic'),
|
|
|
|
|
|
pro: feature.requiredPlan.includes('pro'),
|
|
|
|
|
|
enterprise: feature.requiredPlan.includes('enterprise'),
|
|
|
|
|
|
current: isFeatureAvailable(feature.key),
|
|
|
|
|
|
}));
|
|
|
|
|
|
}, [simulatedPlan]);
|
|
|
|
|
|
|
|
|
|
|
|
const upgradePath = useMemo(() => {
|
|
|
|
|
|
const planOrder = ['free', 'basic', 'pro', 'enterprise'];
|
|
|
|
|
|
const currentIndex = planOrder.indexOf(currentPlan);
|
|
|
|
|
|
return planOrder.slice(currentIndex + 1);
|
|
|
|
|
|
}, [currentPlan]);
|
|
|
|
|
|
|
|
|
|
|
|
const renderPlanCard = (planKey: string, plan: any) => (
|
|
|
|
|
|
<Col xs={24} sm={12} lg={6} key={planKey}>
|
|
|
|
|
|
<Card
|
|
|
|
|
|
hoverable
|
|
|
|
|
|
style={{
|
|
|
|
|
|
height: '100%',
|
|
|
|
|
|
borderColor: currentPlan === planKey ? '#1890ff' : undefined,
|
|
|
|
|
|
borderWidth: currentPlan === planKey ? 2 : 1,
|
|
|
|
|
|
borderRadius: '12px',
|
|
|
|
|
|
transition: 'all 0.3s ease',
|
|
|
|
|
|
background: planKey === simulatedPlan ? 'linear-gradient(135deg, #e6f7ff 0%, #bae7ff 100%)' : undefined,
|
|
|
|
|
|
}}
|
|
|
|
|
|
bodyStyle={{ padding: '24px' }}
|
|
|
|
|
|
onClick={() => handleSimulatePlan(planKey)}
|
|
|
|
|
|
>
|
|
|
|
|
|
<div style={{ textAlign: 'center' }}>
|
|
|
|
|
|
<div style={{ marginBottom: '12px' }}>
|
|
|
|
|
|
<div style={{
|
|
|
|
|
|
fontSize: '32px',
|
|
|
|
|
|
color: plan.color,
|
|
|
|
|
|
marginBottom: '8px'
|
|
|
|
|
|
}}>
|
|
|
|
|
|
{plan.icon}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<Tag
|
|
|
|
|
|
color={plan.color}
|
|
|
|
|
|
style={{
|
|
|
|
|
|
fontSize: '14px',
|
|
|
|
|
|
padding: '4px 16px',
|
|
|
|
|
|
borderRadius: '20px',
|
|
|
|
|
|
fontWeight: 500
|
2026-03-22 11:25:28 +08:00
|
|
|
|
}}
|
|
|
|
|
|
>
|
2026-03-25 13:46:26 +08:00
|
|
|
|
{plan.name}
|
|
|
|
|
|
</Tag>
|
|
|
|
|
|
{plan.recommended && (
|
|
|
|
|
|
<Tag
|
|
|
|
|
|
color="gold"
|
|
|
|
|
|
style={{
|
|
|
|
|
|
marginLeft: '8px',
|
|
|
|
|
|
borderRadius: '20px',
|
|
|
|
|
|
fontWeight: 500
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
推荐
|
|
|
|
|
|
</Tag>
|
|
|
|
|
|
)}
|
|
|
|
|
|
{currentPlan === planKey && (
|
|
|
|
|
|
<Tag
|
|
|
|
|
|
color="green"
|
|
|
|
|
|
style={{
|
|
|
|
|
|
marginLeft: '8px',
|
|
|
|
|
|
borderRadius: '20px',
|
|
|
|
|
|
fontWeight: 500
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
当前套餐
|
|
|
|
|
|
</Tag>
|
|
|
|
|
|
)}
|
|
|
|
|
|
{simulatedPlan === planKey && currentPlan !== planKey && (
|
|
|
|
|
|
<Tag
|
|
|
|
|
|
color="blue"
|
|
|
|
|
|
style={{
|
|
|
|
|
|
marginLeft: '8px',
|
|
|
|
|
|
borderRadius: '20px',
|
|
|
|
|
|
fontWeight: 500
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
预览中
|
|
|
|
|
|
</Tag>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div style={{ margin: '16px 0' }}>
|
|
|
|
|
|
{billingCycle === 'yearly' && planKey !== 'free' && (
|
|
|
|
|
|
<div style={{ marginBottom: '8px' }}>
|
|
|
|
|
|
<Text type="secondary" style={{ fontSize: '12px', textDecoration: 'line-through' }}>
|
|
|
|
|
|
¥{plan.price * 12}/年
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
<Tag color="red" style={{ marginLeft: '8px', fontSize: '12px' }}>
|
|
|
|
|
|
省{getDiscount()}%
|
2026-03-22 11:25:28 +08:00
|
|
|
|
</Tag>
|
|
|
|
|
|
</div>
|
2026-03-25 13:46:26 +08:00
|
|
|
|
)}
|
|
|
|
|
|
<Title level={3} style={{ margin: 0, color: plan.color }}>
|
|
|
|
|
|
¥{getDisplayPrice(plan.price)}
|
|
|
|
|
|
<Text type="secondary" style={{ fontSize: '14px', fontWeight: 'normal' }}>
|
|
|
|
|
|
/{billingCycle === 'yearly' ? '年' : '月'}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
</Title>
|
|
|
|
|
|
{billingCycle === 'yearly' && planKey !== 'free' && (
|
|
|
|
|
|
<Text type="secondary" style={{ fontSize: '12px' }}>
|
|
|
|
|
|
平均 ¥{Math.round(getDisplayPrice(plan.price) / 12)}/月
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<Paragraph
|
|
|
|
|
|
type="secondary"
|
|
|
|
|
|
style={{
|
|
|
|
|
|
fontSize: '13px',
|
|
|
|
|
|
marginBottom: '16px',
|
|
|
|
|
|
minHeight: '40px'
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
{plan.description}
|
|
|
|
|
|
</Paragraph>
|
|
|
|
|
|
|
|
|
|
|
|
<Divider style={{ margin: '12px 0' }} />
|
|
|
|
|
|
|
|
|
|
|
|
<div style={{ textAlign: 'left', marginBottom: '16px' }}>
|
|
|
|
|
|
{plan.features.slice(0, 4).map((f: string, i: number) => (
|
|
|
|
|
|
<div key={i} style={{ marginBottom: '8px', fontSize: '13px' }}>
|
|
|
|
|
|
<CheckCircleOutlined style={{ color: '#52c41a', marginRight: '8px', fontSize: '14px' }} />
|
|
|
|
|
|
<Text>{f}</Text>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
))}
|
|
|
|
|
|
{plan.features.length > 4 && (
|
|
|
|
|
|
<Text type="secondary" style={{ fontSize: '12px' }}>
|
|
|
|
|
|
+{plan.features.length - 4} 更多功能
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{plan.limitations.length > 0 && (
|
|
|
|
|
|
<div style={{ textAlign: 'left', marginBottom: '16px' }}>
|
|
|
|
|
|
{plan.limitations.slice(0, 2).map((l: string, i: number) => (
|
|
|
|
|
|
<div key={i} style={{ marginBottom: '6px', fontSize: '12px' }}>
|
|
|
|
|
|
<CloseCircleOutlined style={{ color: '#d9d9d9', marginRight: '8px', fontSize: '12px' }} />
|
|
|
|
|
|
<Text type="secondary">{l}</Text>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
<Button
|
|
|
|
|
|
type={currentPlan === planKey ? 'default' : 'primary'}
|
|
|
|
|
|
block
|
|
|
|
|
|
size="large"
|
|
|
|
|
|
style={{
|
|
|
|
|
|
borderRadius: '8px',
|
|
|
|
|
|
height: '44px',
|
|
|
|
|
|
fontSize: '16px',
|
|
|
|
|
|
fontWeight: 500
|
|
|
|
|
|
}}
|
|
|
|
|
|
disabled={currentPlan === planKey}
|
|
|
|
|
|
onClick={() => currentPlan !== planKey && handleUpgrade(planKey)}
|
|
|
|
|
|
>
|
|
|
|
|
|
{currentPlan === planKey ? '当前套餐' : '立即升级'}
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
2026-03-22 11:25:28 +08:00
|
|
|
|
</Card>
|
2026-03-25 13:46:26 +08:00
|
|
|
|
</Col>
|
|
|
|
|
|
);
|
2026-03-22 11:25:28 +08:00
|
|
|
|
|
2026-03-25 13:46:26 +08:00
|
|
|
|
const renderUsageOverview = () => (
|
|
|
|
|
|
<Row gutter={[16, 16]}>
|
|
|
|
|
|
<Col xs={24} md={12} lg={8}>
|
|
|
|
|
|
<Card>
|
|
|
|
|
|
<Statistic
|
|
|
|
|
|
title={
|
|
|
|
|
|
<Space>
|
|
|
|
|
|
<RobotOutlined style={{ color: '#1890ff' }} />
|
|
|
|
|
|
<span>AI调用次数</span>
|
|
|
|
|
|
</Space>
|
|
|
|
|
|
}
|
|
|
|
|
|
value={USAGE_DATA.aiCalls.used}
|
|
|
|
|
|
suffix={`/ ${USAGE_DATA.aiCalls.total}`}
|
|
|
|
|
|
valueStyle={{ color: '#1890ff' }}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Progress
|
|
|
|
|
|
percent={USAGE_DATA.aiCalls.used}
|
|
|
|
|
|
status={USAGE_DATA.aiCalls.used >= 80 ? 'exception' : 'normal'}
|
|
|
|
|
|
strokeColor={USAGE_DATA.aiCalls.used >= 80 ? '#ff4d4f' : '#1890ff'}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Text type="secondary" style={{ fontSize: '12px' }}>
|
|
|
|
|
|
剩余 {USAGE_DATA.aiCalls.total - USAGE_DATA.aiCalls.used} 次
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
<Col xs={24} md={12} lg={8}>
|
|
|
|
|
|
<Card>
|
|
|
|
|
|
<Statistic
|
|
|
|
|
|
title={
|
|
|
|
|
|
<Space>
|
|
|
|
|
|
<ShopOutlined style={{ color: '#52c41a' }} />
|
|
|
|
|
|
<span>店铺数量</span>
|
|
|
|
|
|
</Space>
|
|
|
|
|
|
}
|
|
|
|
|
|
value={USAGE_DATA.shops.used}
|
|
|
|
|
|
suffix={`/ ${USAGE_DATA.shops.total}`}
|
|
|
|
|
|
valueStyle={{ color: '#52c41a' }}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Progress
|
|
|
|
|
|
percent={Math.round((USAGE_DATA.shops.used / USAGE_DATA.shops.total) * 100)}
|
|
|
|
|
|
status="normal"
|
|
|
|
|
|
strokeColor="#52c41a"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Text type="secondary" style={{ fontSize: '12px' }}>
|
|
|
|
|
|
剩余 {USAGE_DATA.shops.total - USAGE_DATA.shops.used} 个
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
<Col xs={24} md={12} lg={8}>
|
|
|
|
|
|
<Card>
|
|
|
|
|
|
<Statistic
|
|
|
|
|
|
title={
|
|
|
|
|
|
<Space>
|
|
|
|
|
|
<DollarOutlined style={{ color: '#faad14' }} />
|
|
|
|
|
|
<span>存储空间</span>
|
|
|
|
|
|
</Space>
|
|
|
|
|
|
}
|
|
|
|
|
|
value={USAGE_DATA.storage.used}
|
|
|
|
|
|
suffix={`/ ${USAGE_DATA.storage.total} ${USAGE_DATA.storage.unit}`}
|
|
|
|
|
|
valueStyle={{ color: '#faad14' }}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Progress
|
|
|
|
|
|
percent={USAGE_DATA.storage.used}
|
|
|
|
|
|
status={USAGE_DATA.storage.used >= 80 ? 'exception' : 'normal'}
|
|
|
|
|
|
strokeColor={USAGE_DATA.storage.used >= 80 ? '#ff4d4f' : '#faad14'}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Text type="secondary" style={{ fontSize: '12px' }}>
|
|
|
|
|
|
剩余 {USAGE_DATA.storage.total - USAGE_DATA.storage.used} {USAGE_DATA.storage.unit}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
</Row>
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const renderFeatureComparison = () => {
|
|
|
|
|
|
const columns: ColumnsType<PlanComparisonData> = [
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '功能',
|
|
|
|
|
|
dataIndex: 'name',
|
|
|
|
|
|
key: 'name',
|
|
|
|
|
|
width: 200,
|
|
|
|
|
|
render: (name: string, record: PlanComparisonData) => (
|
|
|
|
|
|
<Space direction="vertical" size={0}>
|
|
|
|
|
|
<Space>
|
|
|
|
|
|
{record.icon}
|
|
|
|
|
|
<Text strong>{name}</Text>
|
|
|
|
|
|
</Space>
|
|
|
|
|
|
<Text type="secondary" style={{ fontSize: '12px' }}>
|
|
|
|
|
|
{record.description}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<Tooltip title="用户使用热度">
|
|
|
|
|
|
<Progress
|
|
|
|
|
|
percent={record.popularity}
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
showInfo={false}
|
|
|
|
|
|
strokeColor="#1890ff"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</Tooltip>
|
|
|
|
|
|
<Text type="secondary" style={{ fontSize: '11px', marginLeft: '8px' }}>
|
|
|
|
|
|
{record.popularity}% 热度
|
2026-03-22 11:25:28 +08:00
|
|
|
|
</Text>
|
|
|
|
|
|
</div>
|
2026-03-25 13:46:26 +08:00
|
|
|
|
</Space>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '免费版',
|
|
|
|
|
|
key: 'free',
|
|
|
|
|
|
align: 'center' as const,
|
|
|
|
|
|
render: (_: any, record: PlanComparisonData) =>
|
|
|
|
|
|
record.free ?
|
|
|
|
|
|
<CheckCircleOutlined style={{ color: '#52c41a', fontSize: '18px' }} /> :
|
|
|
|
|
|
<CloseCircleOutlined style={{ color: '#d9d9d9', fontSize: '18px' }} />,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '基础版',
|
|
|
|
|
|
key: 'basic',
|
|
|
|
|
|
align: 'center' as const,
|
|
|
|
|
|
render: (_: any, record: PlanComparisonData) =>
|
|
|
|
|
|
record.basic ?
|
|
|
|
|
|
<CheckCircleOutlined style={{ color: '#52c41a', fontSize: '18px' }} /> :
|
|
|
|
|
|
<CloseCircleOutlined style={{ color: '#d9d9d9', fontSize: '18px' }} />,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '专业版',
|
|
|
|
|
|
key: 'pro',
|
|
|
|
|
|
align: 'center' as const,
|
|
|
|
|
|
render: (_: any, record: PlanComparisonData) =>
|
|
|
|
|
|
record.pro ?
|
|
|
|
|
|
<CheckCircleOutlined style={{ color: '#52c41a', fontSize: '18px' }} /> :
|
|
|
|
|
|
<CloseCircleOutlined style={{ color: '#d9d9d9', fontSize: '18px' }} />,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '企业版',
|
|
|
|
|
|
key: 'enterprise',
|
|
|
|
|
|
align: 'center' as const,
|
|
|
|
|
|
render: (_: any, record: PlanComparisonData) =>
|
|
|
|
|
|
record.enterprise ?
|
|
|
|
|
|
<CheckCircleOutlined style={{ color: '#52c41a', fontSize: '18px' }} /> :
|
|
|
|
|
|
<CloseCircleOutlined style={{ color: '#d9d9d9', fontSize: '18px' }} />,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '当前状态',
|
|
|
|
|
|
key: 'status',
|
|
|
|
|
|
align: 'center' as const,
|
|
|
|
|
|
render: (_: any, record: PlanComparisonData) => (
|
|
|
|
|
|
<Tag color={record.current ? 'green' : 'default'} style={{ borderRadius: '12px' }}>
|
|
|
|
|
|
{record.current ? '已开通' : '未开通'}
|
|
|
|
|
|
</Tag>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '操作',
|
|
|
|
|
|
key: 'action',
|
|
|
|
|
|
width: 100,
|
|
|
|
|
|
align: 'center' as const,
|
|
|
|
|
|
render: (_: any, record: PlanComparisonData) => (
|
|
|
|
|
|
<Button
|
|
|
|
|
|
type="link"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
onClick={() => handleFeaturePreview(FEATURE_CONFIGS.find(f => f.key === record.key)!)}
|
|
|
|
|
|
>
|
|
|
|
|
|
详情
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Table
|
|
|
|
|
|
columns={columns}
|
|
|
|
|
|
dataSource={planComparisonData}
|
|
|
|
|
|
rowKey="key"
|
|
|
|
|
|
pagination={false}
|
|
|
|
|
|
size="middle"
|
|
|
|
|
|
rowClassName={(record) => record.current ? 'highlight-row' : ''}
|
|
|
|
|
|
/>
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const renderUpgradePath = () => {
|
|
|
|
|
|
const steps = upgradePath.map((planKey, index) => ({
|
|
|
|
|
|
title: PLAN_DETAILS[planKey as keyof typeof PLAN_DETAILS].name,
|
|
|
|
|
|
description: PLAN_DETAILS[planKey as keyof typeof PLAN_DETAILS].description,
|
|
|
|
|
|
icon: PLAN_DETAILS[planKey as keyof typeof PLAN_DETAILS].icon,
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Card title="升级路径">
|
|
|
|
|
|
<Alert
|
|
|
|
|
|
message="升级建议"
|
|
|
|
|
|
description="根据您的使用情况,我们推荐您升级到更高级别的套餐以获得更多功能"
|
|
|
|
|
|
type="info"
|
|
|
|
|
|
showIcon
|
|
|
|
|
|
style={{ marginBottom: '24px' }}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Steps
|
|
|
|
|
|
current={0}
|
|
|
|
|
|
items={steps}
|
|
|
|
|
|
style={{ marginBottom: '24px' }}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Space direction="vertical" style={{ width: '100%' }}>
|
|
|
|
|
|
{upgradePath.map((planKey, index) => {
|
|
|
|
|
|
const plan = PLAN_DETAILS[planKey as keyof typeof PLAN_DETAILS];
|
|
|
|
|
|
const additionalFeatures = FEATURE_CONFIGS.filter(f =>
|
|
|
|
|
|
f.requiredPlan.includes(planKey) && !isFeatureAvailable(f.key)
|
|
|
|
|
|
);
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Card
|
|
|
|
|
|
key={planKey}
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
style={{
|
|
|
|
|
|
borderLeft: `4px solid ${plan.color}`,
|
|
|
|
|
|
marginBottom: '12px'
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Row gutter={16} align="middle">
|
|
|
|
|
|
<Col flex="auto">
|
|
|
|
|
|
<Space direction="vertical" size={0}>
|
|
|
|
|
|
<Space>
|
|
|
|
|
|
<Text strong style={{ fontSize: '16px' }}>
|
|
|
|
|
|
{plan.name}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
<Tag color={plan.color} style={{ borderRadius: '12px' }}>
|
|
|
|
|
|
¥{getDisplayPrice(plan.price)}/{billingCycle === 'yearly' ? '年' : '月'}
|
|
|
|
|
|
</Tag>
|
|
|
|
|
|
</Space>
|
|
|
|
|
|
<Text type="secondary" style={{ fontSize: '13px' }}>
|
|
|
|
|
|
{plan.description}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<Text type="secondary" style={{ fontSize: '12px' }}>
|
|
|
|
|
|
新增功能:
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
<Space wrap style={{ marginLeft: '8px' }}>
|
|
|
|
|
|
{additionalFeatures.slice(0, 3).map(f => (
|
|
|
|
|
|
<Tag key={f.key} color="blue" style={{ fontSize: '11px' }}>
|
|
|
|
|
|
{f.name}
|
|
|
|
|
|
</Tag>
|
|
|
|
|
|
))}
|
|
|
|
|
|
{additionalFeatures.length > 3 && (
|
|
|
|
|
|
<Tag color="default" style={{ fontSize: '11px' }}>
|
|
|
|
|
|
+{additionalFeatures.length - 3}
|
|
|
|
|
|
</Tag>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Space>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Space>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
<Col>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
icon={<RightOutlined />}
|
|
|
|
|
|
onClick={() => handleUpgrade(planKey)}
|
|
|
|
|
|
>
|
|
|
|
|
|
升级
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
</Row>
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
);
|
|
|
|
|
|
})}
|
|
|
|
|
|
</Space>
|
2026-03-22 11:25:28 +08:00
|
|
|
|
</Card>
|
2026-03-25 13:46:26 +08:00
|
|
|
|
);
|
|
|
|
|
|
};
|
2026-03-22 11:25:28 +08:00
|
|
|
|
|
2026-03-25 13:46:26 +08:00
|
|
|
|
return (
|
|
|
|
|
|
<div className="subscription-manage" style={{ padding: '24px', background: '#f5f5f5', minHeight: '100vh' }}>
|
|
|
|
|
|
<ConfigProvider
|
|
|
|
|
|
theme={{
|
|
|
|
|
|
token: {
|
|
|
|
|
|
colorPrimary: '#1890ff',
|
|
|
|
|
|
borderRadius: 8,
|
|
|
|
|
|
},
|
|
|
|
|
|
}}
|
2026-03-22 11:25:28 +08:00
|
|
|
|
>
|
2026-03-25 13:46:26 +08:00
|
|
|
|
<div className="page-header" style={{ marginBottom: '24px' }}>
|
|
|
|
|
|
<Row gutter={[16, 16]} align="middle">
|
|
|
|
|
|
<Col xs={24} md={16}>
|
|
|
|
|
|
<Space direction="vertical" size={4}>
|
|
|
|
|
|
<Title level={2} style={{ margin: 0 }}>
|
|
|
|
|
|
<CrownOutlined style={{ marginRight: '8px', color: '#faad14' }} />
|
|
|
|
|
|
订阅管理中心
|
|
|
|
|
|
</Title>
|
|
|
|
|
|
<Paragraph type="secondary" style={{ margin: 0 }}>
|
|
|
|
|
|
管理您的订阅计划和付费功能,预览不同套餐的功能差异
|
|
|
|
|
|
</Paragraph>
|
|
|
|
|
|
</Space>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
<Col xs={24} md={8} style={{ textAlign: 'right' }}>
|
|
|
|
|
|
<Space>
|
|
|
|
|
|
<Radio.Group
|
|
|
|
|
|
value={billingCycle}
|
|
|
|
|
|
onChange={(e) => setBillingCycle(e.target.value)}
|
|
|
|
|
|
size="large"
|
|
|
|
|
|
>
|
|
|
|
|
|
<Radio.Button value="monthly">月付</Radio.Button>
|
|
|
|
|
|
<Radio.Button value="yearly">
|
|
|
|
|
|
年付
|
|
|
|
|
|
{getDiscount() > 0 && (
|
|
|
|
|
|
<Tag color="red" style={{ marginLeft: '4px' }}>
|
|
|
|
|
|
省{getDiscount()}%
|
|
|
|
|
|
</Tag>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Radio.Button>
|
|
|
|
|
|
</Radio.Group>
|
|
|
|
|
|
</Space>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
</Row>
|
|
|
|
|
|
</div>
|
2026-03-22 11:25:28 +08:00
|
|
|
|
|
2026-03-25 13:46:26 +08:00
|
|
|
|
<Alert
|
|
|
|
|
|
message="预览模式"
|
|
|
|
|
|
description="点击下方套餐卡片可以预览不同套餐的功能权限,实际功能以当前订阅为准"
|
|
|
|
|
|
type="info"
|
|
|
|
|
|
showIcon
|
|
|
|
|
|
icon={<InfoCircleOutlined />}
|
|
|
|
|
|
closable
|
|
|
|
|
|
style={{ marginBottom: '24px', borderRadius: '8px' }}
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<Tabs
|
|
|
|
|
|
activeKey={selectedTab}
|
|
|
|
|
|
onChange={setSelectedTab}
|
|
|
|
|
|
size="large"
|
|
|
|
|
|
items={[
|
|
|
|
|
|
{
|
|
|
|
|
|
key: 'overview',
|
|
|
|
|
|
label: (
|
|
|
|
|
|
<span>
|
|
|
|
|
|
<DollarOutlined />
|
|
|
|
|
|
套餐概览
|
|
|
|
|
|
</span>
|
|
|
|
|
|
),
|
|
|
|
|
|
children: (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<Row gutter={[16, 16]} style={{ marginBottom: '24px' }}>
|
|
|
|
|
|
{Object.entries(PLAN_DETAILS).map(([key, plan]) =>
|
|
|
|
|
|
renderPlanCard(key, plan)
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Row>
|
2026-03-22 11:25:28 +08:00
|
|
|
|
|
2026-03-25 13:46:26 +08:00
|
|
|
|
<Card title="功能使用情况" style={{ marginBottom: '24px' }}>
|
|
|
|
|
|
{renderUsageOverview()}
|
|
|
|
|
|
</Card>
|
2026-03-22 11:25:28 +08:00
|
|
|
|
|
2026-03-25 13:46:26 +08:00
|
|
|
|
<Card title="当前订阅状态">
|
|
|
|
|
|
<Row gutter={24}>
|
|
|
|
|
|
<Col xs={24} md={8}>
|
|
|
|
|
|
<div style={{ textAlign: 'center', padding: '24px', background: '#fafafa', borderRadius: '8px' }}>
|
|
|
|
|
|
<Tag
|
|
|
|
|
|
color={PLAN_DETAILS[currentPlan as keyof typeof PLAN_DETAILS]?.color || 'default'}
|
|
|
|
|
|
style={{
|
|
|
|
|
|
fontSize: '16px',
|
|
|
|
|
|
padding: '8px 24px',
|
|
|
|
|
|
borderRadius: '20px',
|
|
|
|
|
|
marginBottom: '12px'
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
{getPlanLabel()}
|
|
|
|
|
|
</Tag>
|
|
|
|
|
|
<Title level={4} style={{ marginTop: '12px', marginBottom: '8px' }}>当前套餐</Title>
|
|
|
|
|
|
<Text type="secondary" style={{ fontSize: '13px' }}>
|
|
|
|
|
|
{currentUser.subscription?.expiresAt ?
|
|
|
|
|
|
`到期时间: ${currentUser.subscription.expiresAt}` :
|
|
|
|
|
|
'永久有效'}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
<Col xs={24} md={8}>
|
|
|
|
|
|
<div style={{ textAlign: 'center', padding: '24px', background: '#fafafa', borderRadius: '8px' }}>
|
|
|
|
|
|
<Title level={2} style={{ color: '#52c41a', marginBottom: '8px' }}>
|
|
|
|
|
|
{currentUser.subscription?.features.length || 0}
|
|
|
|
|
|
</Title>
|
|
|
|
|
|
<Text type="secondary">已开通功能</Text>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
<Col xs={24} md={8}>
|
|
|
|
|
|
<div style={{ textAlign: 'center', padding: '24px', background: '#fafafa', borderRadius: '8px' }}>
|
|
|
|
|
|
<Title level={2} style={{ color: '#1890ff', marginBottom: '8px' }}>
|
|
|
|
|
|
{FEATURE_CONFIGS.filter(f => !hasFeature(f.key)).length}
|
|
|
|
|
|
</Title>
|
|
|
|
|
|
<Text type="secondary">可升级功能</Text>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
</Row>
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
</>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: 'comparison',
|
|
|
|
|
|
label: (
|
|
|
|
|
|
<span>
|
|
|
|
|
|
<LineChartOutlined />
|
|
|
|
|
|
功能对比
|
|
|
|
|
|
</span>
|
|
|
|
|
|
),
|
|
|
|
|
|
children: (
|
|
|
|
|
|
<Card title="套餐功能对比表">
|
|
|
|
|
|
{renderFeatureComparison()}
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: 'upgrade',
|
|
|
|
|
|
label: (
|
|
|
|
|
|
<span>
|
|
|
|
|
|
<RocketOutlined />
|
|
|
|
|
|
升级路径
|
|
|
|
|
|
</span>
|
|
|
|
|
|
),
|
|
|
|
|
|
children: renderUpgradePath(),
|
|
|
|
|
|
},
|
|
|
|
|
|
]}
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<Modal
|
|
|
|
|
|
title={
|
2026-03-22 11:25:28 +08:00
|
|
|
|
<Space>
|
2026-03-25 13:46:26 +08:00
|
|
|
|
{previewFeature?.icon}
|
|
|
|
|
|
<span>{previewFeature?.name}</span>
|
2026-03-22 11:25:28 +08:00
|
|
|
|
</Space>
|
2026-03-25 13:46:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
open={!!previewFeature}
|
|
|
|
|
|
onCancel={() => setPreviewFeature(null)}
|
|
|
|
|
|
footer={[
|
|
|
|
|
|
<Button key="close" onClick={() => setPreviewFeature(null)}>
|
|
|
|
|
|
关闭
|
|
|
|
|
|
</Button>,
|
|
|
|
|
|
!isFeatureAvailable(previewFeature?.key || '') && (
|
|
|
|
|
|
<Button
|
|
|
|
|
|
key="upgrade"
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
icon={<RocketOutlined />}
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
setPreviewFeature(null);
|
|
|
|
|
|
setSelectedTab('upgrade');
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
查看升级方案
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
),
|
|
|
|
|
|
]}
|
|
|
|
|
|
width={700}
|
|
|
|
|
|
>
|
|
|
|
|
|
{previewFeature && (
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<Paragraph style={{ fontSize: '15px', marginBottom: '24px' }}>
|
|
|
|
|
|
{previewFeature.description}
|
|
|
|
|
|
</Paragraph>
|
|
|
|
|
|
|
|
|
|
|
|
<Space direction="vertical" style={{ width: '100%', marginBottom: '24px' }}>
|
|
|
|
|
|
<Title level={5}>功能权益</Title>
|
|
|
|
|
|
{previewFeature.benefits.map((b: string, i: number) => (
|
|
|
|
|
|
<div key={i} style={{ marginBottom: '12px' }}>
|
|
|
|
|
|
<CheckCircleOutlined style={{ color: '#52c41a', marginRight: '12px', fontSize: '16px' }} />
|
|
|
|
|
|
<Text style={{ fontSize: '14px' }}>{b}</Text>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</Space>
|
2026-03-22 11:25:28 +08:00
|
|
|
|
|
2026-03-25 13:46:26 +08:00
|
|
|
|
{previewFeature.limitations && (
|
|
|
|
|
|
<Space direction="vertical" style={{ width: '100%', marginBottom: '24px' }}>
|
|
|
|
|
|
<Title level={5}>版本限制</Title>
|
|
|
|
|
|
{previewFeature.limitations.map((l: string, i: number) => (
|
|
|
|
|
|
<div key={i} style={{ marginBottom: '8px' }}>
|
|
|
|
|
|
<Text type="secondary" style={{ fontSize: '13px' }}>
|
|
|
|
|
|
• {l}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</Space>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
<Divider />
|
|
|
|
|
|
|
|
|
|
|
|
<Space direction="vertical" style={{ width: '100%', marginBottom: '16px' }}>
|
|
|
|
|
|
<Title level={5}>所需套餐</Title>
|
|
|
|
|
|
<Space>
|
|
|
|
|
|
{previewFeature.requiredPlan.map((p: string) => (
|
|
|
|
|
|
<Tag
|
|
|
|
|
|
key={p}
|
|
|
|
|
|
color={PLAN_DETAILS[p as keyof typeof PLAN_DETAILS]?.color}
|
|
|
|
|
|
style={{
|
|
|
|
|
|
fontSize: '14px',
|
|
|
|
|
|
padding: '4px 12px',
|
|
|
|
|
|
borderRadius: '12px'
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
{PLAN_DETAILS[p as keyof typeof PLAN_DETAILS]?.name}
|
|
|
|
|
|
</Tag>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</Space>
|
|
|
|
|
|
</Space>
|
|
|
|
|
|
|
|
|
|
|
|
<div style={{
|
|
|
|
|
|
padding: '16px',
|
|
|
|
|
|
background: isFeatureAvailable(previewFeature.key) ? '#f6ffed' : '#fff7e6',
|
|
|
|
|
|
borderRadius: '8px',
|
|
|
|
|
|
border: `1px solid ${isFeatureAvailable(previewFeature.key) ? '#b7eb8f' : '#ffd591'}`
|
|
|
|
|
|
}}>
|
|
|
|
|
|
<Space>
|
|
|
|
|
|
{isFeatureAvailable(previewFeature.key) ? (
|
|
|
|
|
|
<CheckCircleOutlined style={{ color: '#52c41a', fontSize: '20px' }} />
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<InfoCircleOutlined style={{ color: '#faad14', fontSize: '20px' }} />
|
|
|
|
|
|
)}
|
|
|
|
|
|
<Text
|
|
|
|
|
|
strong
|
|
|
|
|
|
style={{
|
|
|
|
|
|
color: isFeatureAvailable(previewFeature.key) ? '#52c41a' : '#faad14',
|
|
|
|
|
|
fontSize: '15px'
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
{isFeatureAvailable(previewFeature.key) ? '✓ 您已拥有此功能' : '✗ 需要升级套餐'}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
</Space>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{previewFeature.popularity && (
|
|
|
|
|
|
<div style={{ marginTop: '16px' }}>
|
|
|
|
|
|
<Space>
|
|
|
|
|
|
<Text type="secondary">用户使用热度:</Text>
|
|
|
|
|
|
<Progress
|
|
|
|
|
|
percent={previewFeature.popularity}
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
strokeColor="#1890ff"
|
|
|
|
|
|
format={(percent) => `${percent}%`}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</Space>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
2026-03-22 11:25:28 +08:00
|
|
|
|
</div>
|
2026-03-25 13:46:26 +08:00
|
|
|
|
)}
|
|
|
|
|
|
</Modal>
|
|
|
|
|
|
</ConfigProvider>
|
2026-03-22 11:25:28 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-03-25 13:46:26 +08:00
|
|
|
|
export default SubscriptionManage;
|