Files
makemd/dashboard/src/pages/Settings/SubscriptionManage.tsx

1014 lines
34 KiB
TypeScript
Raw Normal View History

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';
import {
CrownOutlined,
RobotOutlined,
ShopOutlined,
GlobalOutlined,
LineChartOutlined,
ApiOutlined,
CheckCircleOutlined,
CloseCircleOutlined,
ThunderboltOutlined,
StarOutlined,
ArrowUpOutlined,
ArrowDownOutlined,
InfoCircleOutlined,
GiftOutlined,
SafetyOutlined,
RocketOutlined,
TeamOutlined,
ClockCircleOutlined,
DollarOutlined,
CheckSquareOutlined,
CloseSquareOutlined,
RightOutlined,
} 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[];
popularity?: number;
category: 'core' | 'advanced' | 'enterprise';
}
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调用',
],
popularity: 95,
category: 'core',
},
{
key: FEATURES.AUTO_PRICING,
name: '自动定价',
description: '基于市场数据和竞争分析的智能定价系统',
icon: <ThunderboltOutlined style={{ fontSize: 24 }} />,
requiredPlan: ['pro', 'enterprise'],
benefits: [
'实时竞品价格监控',
'动态定价策略',
'利润率自动优化',
'价格预警通知',
],
limitations: [
'基础版: 仅查看定价建议',
'专业版: 自动执行定价',
],
popularity: 88,
category: 'advanced',
},
{
key: FEATURES.MULTI_SHOP,
name: '多店铺管理',
description: '统一管理多个电商平台的店铺',
icon: <ShopOutlined style={{ fontSize: 24 }} />,
requiredPlan: ['basic', 'pro', 'enterprise'],
benefits: [
'跨平台商品同步',
'统一库存管理',
'多店铺数据报表',
'批量操作支持',
],
limitations: [
'免费版: 仅支持1个店铺',
'基础版: 最多3个店铺',
'专业版: 最多10个店铺',
'企业版: 无限店铺',
],
popularity: 92,
category: 'core',
},
{
key: FEATURES.B2B_TRADE,
name: 'B2B贸易',
description: '企业级批发贸易管理功能',
icon: <ShopOutlined style={{ fontSize: 24 }} />,
requiredPlan: ['basic', 'pro', 'enterprise'],
benefits: [
'供应商管理',
'批量订单处理',
'B2B专属定价',
'合同管理',
],
popularity: 75,
category: 'core',
},
{
key: FEATURES.INDEPENDENT_SITE,
name: '独立站',
description: '创建和管理独立电商网站',
icon: <GlobalOutlined style={{ fontSize: 24 }} />,
requiredPlan: ['pro', 'enterprise'],
benefits: [
'自定义品牌站点',
'独立域名绑定',
'SEO优化工具',
'独立支付集成',
],
popularity: 65,
category: 'advanced',
},
{
key: FEATURES.ADVANCED_ANALYTICS,
name: '高级数据分析',
description: '深度数据分析和可视化报表',
icon: <LineChartOutlined style={{ fontSize: 24 }} />,
requiredPlan: ['pro', 'enterprise'],
benefits: [
'自定义报表',
'数据导出',
'趋势预测',
'竞品分析报告',
],
limitations: [
'基础版: 仅基础报表',
'专业版: 完整分析功能',
],
popularity: 80,
category: 'advanced',
},
{
key: FEATURES.API_ACCESS,
name: 'API访问',
description: '开放API接口支持系统集成',
icon: <ApiOutlined style={{ fontSize: 24 }} />,
requiredPlan: ['enterprise'],
benefits: [
'RESTful API',
'Webhook支持',
'SDK下载',
'技术文档',
],
popularity: 55,
category: 'enterprise',
},
];
const PLAN_DETAILS = {
free: {
name: '免费版',
price: 0,
color: '#d9d9d9',
gradient: 'linear-gradient(135deg, #d9d9d9 0%, #bfbfbf 100%)',
features: ['基础商品管理', '订单管理', '基础报表'],
limitations: ['1个店铺', '每日3次AI调用', '无API访问'],
recommended: false,
popular: false,
icon: <ClockCircleOutlined />,
description: '适合个人用户和小规模卖家',
},
basic: {
name: '基础版',
price: 99,
color: '#1890ff',
gradient: 'linear-gradient(135deg, #1890ff 0%, #096dd9 100%)',
features: ['多店铺管理(3个)', 'AI运营(100次/日)', 'B2B贸易', '基础定价建议'],
limitations: ['无独立站', '无API访问'],
recommended: true,
popular: true,
icon: <RocketOutlined />,
description: '适合成长中的电商卖家',
},
pro: {
name: '专业版',
price: 299,
color: '#faad14',
gradient: 'linear-gradient(135deg, #faad14 0%, #d48806 100%)',
features: ['多店铺管理(10个)', '无限AI调用', '自动定价', '独立站', '高级分析'],
limitations: ['无API访问'],
recommended: false,
popular: false,
icon: <StarOutlined />,
description: '适合专业电商运营团队',
},
enterprise: {
name: '企业版',
price: 999,
color: '#722ed1',
gradient: 'linear-gradient(135deg, #722ed1 0%, #531dab 100%)',
features: ['无限店铺', '全部功能', 'API访问', '专属客服', '定制开发'],
limitations: [],
recommended: false,
popular: false,
icon: <CrownOutlined />,
description: '适合大型企业和定制化需求',
},
};
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;
}
const SubscriptionManage: React.FC = () => {
const { currentUser, hasFeature, isPaidUser, getPlanLabel } = useUser();
const [previewFeature, setPreviewFeature] = useState<FeatureConfig | null>(null);
const [simulatedPlan, setSimulatedPlan] = useState<string | null>(null);
const [billingCycle, setBillingCycle] = useState<'monthly' | 'yearly'>('monthly');
const [selectedTab, setSelectedTab] = useState('overview');
const currentPlan = currentUser.subscription?.plan || 'free';
const handleFeaturePreview = (feature: FeatureConfig) => {
setPreviewFeature(feature);
};
const handleSimulatePlan = (plan: string) => {
setSimulatedPlan(plan);
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}功能开发中`);
};
const isFeatureAvailable = (featureKey: string) => {
if (simulatedPlan) {
const feature = FEATURE_CONFIGS.find(f => f.key === featureKey);
return feature?.requiredPlan.includes(simulatedPlan) ?? false;
}
return hasFeature(featureKey);
};
const getDiscount = () => {
return billingCycle === 'yearly' ? 20 : 0;
};
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;
};
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
}}
>
{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()}%
</Tag>
</div>
)}
<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>
</Card>
</Col>
);
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}%
</Text>
</div>
</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>
</Card>
);
};
return (
<div className="subscription-manage" style={{ padding: '24px', background: '#f5f5f5', minHeight: '100vh' }}>
<ConfigProvider
theme={{
token: {
colorPrimary: '#1890ff',
borderRadius: 8,
},
}}
>
<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>
<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>
<Card title="功能使用情况" style={{ marginBottom: '24px' }}>
{renderUsageOverview()}
</Card>
<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={
<Space>
{previewFeature?.icon}
<span>{previewFeature?.name}</span>
</Space>
}
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>
{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>
)}
</div>
)}
</Modal>
</ConfigProvider>
</div>
);
};
export default SubscriptionManage;