430 lines
13 KiB
TypeScript
430 lines
13 KiB
TypeScript
|
|
import React, { useState } from 'react';
|
|||
|
|
import { Card, Row, Col, Switch, Tag, Typography, Space, Button, Modal, Progress, Divider, Alert, Tabs, Table, message } from 'antd';
|
|||
|
|
import {
|
|||
|
|
CrownOutlined,
|
|||
|
|
RobotOutlined,
|
|||
|
|
ShopOutlined,
|
|||
|
|
GlobalOutlined,
|
|||
|
|
LineChartOutlined,
|
|||
|
|
ApiOutlined,
|
|||
|
|
CheckCircleOutlined,
|
|||
|
|
CloseCircleOutlined,
|
|||
|
|
ThunderboltOutlined,
|
|||
|
|
StarOutlined,
|
|||
|
|
} 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[];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
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调用',
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
key: FEATURES.AUTO_PRICING,
|
|||
|
|
name: '自动定价',
|
|||
|
|
description: '基于市场数据和竞争分析的智能定价系统',
|
|||
|
|
icon: <ThunderboltOutlined style={{ fontSize: 24 }} />,
|
|||
|
|
requiredPlan: ['pro', 'enterprise'],
|
|||
|
|
benefits: [
|
|||
|
|
'实时竞品价格监控',
|
|||
|
|
'动态定价策略',
|
|||
|
|
'利润率自动优化',
|
|||
|
|
'价格预警通知',
|
|||
|
|
],
|
|||
|
|
limitations: [
|
|||
|
|
'基础版: 仅查看定价建议',
|
|||
|
|
'专业版: 自动执行定价',
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
key: FEATURES.MULTI_SHOP,
|
|||
|
|
name: '多店铺管理',
|
|||
|
|
description: '统一管理多个电商平台的店铺',
|
|||
|
|
icon: <ShopOutlined style={{ fontSize: 24 }} />,
|
|||
|
|
requiredPlan: ['basic', 'pro', 'enterprise'],
|
|||
|
|
benefits: [
|
|||
|
|
'跨平台商品同步',
|
|||
|
|
'统一库存管理',
|
|||
|
|
'多店铺数据报表',
|
|||
|
|
'批量操作支持',
|
|||
|
|
],
|
|||
|
|
limitations: [
|
|||
|
|
'免费版: 仅支持1个店铺',
|
|||
|
|
'基础版: 最多3个店铺',
|
|||
|
|
'专业版: 最多10个店铺',
|
|||
|
|
'企业版: 无限店铺',
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
key: FEATURES.B2B_TRADE,
|
|||
|
|
name: 'B2B贸易',
|
|||
|
|
description: '企业级批发贸易管理功能',
|
|||
|
|
icon: <ShopOutlined style={{ fontSize: 24 }} />,
|
|||
|
|
requiredPlan: ['basic', 'pro', 'enterprise'],
|
|||
|
|
benefits: [
|
|||
|
|
'供应商管理',
|
|||
|
|
'批量订单处理',
|
|||
|
|
'B2B专属定价',
|
|||
|
|
'合同管理',
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
key: FEATURES.INDEPENDENT_SITE,
|
|||
|
|
name: '独立站',
|
|||
|
|
description: '创建和管理独立电商网站',
|
|||
|
|
icon: <GlobalOutlined style={{ fontSize: 24 }} />,
|
|||
|
|
requiredPlan: ['pro', 'enterprise'],
|
|||
|
|
benefits: [
|
|||
|
|
'自定义品牌站点',
|
|||
|
|
'独立域名绑定',
|
|||
|
|
'SEO优化工具',
|
|||
|
|
'独立支付集成',
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
key: FEATURES.ADVANCED_ANALYTICS,
|
|||
|
|
name: '高级数据分析',
|
|||
|
|
description: '深度数据分析和可视化报表',
|
|||
|
|
icon: <LineChartOutlined style={{ fontSize: 24 }} />,
|
|||
|
|
requiredPlan: ['pro', 'enterprise'],
|
|||
|
|
benefits: [
|
|||
|
|
'自定义报表',
|
|||
|
|
'数据导出',
|
|||
|
|
'趋势预测',
|
|||
|
|
'竞品分析报告',
|
|||
|
|
],
|
|||
|
|
limitations: [
|
|||
|
|
'基础版: 仅基础报表',
|
|||
|
|
'专业版: 完整分析功能',
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
key: FEATURES.API_ACCESS,
|
|||
|
|
name: 'API访问',
|
|||
|
|
description: '开放API接口,支持系统集成',
|
|||
|
|
icon: <ApiOutlined style={{ fontSize: 24 }} />,
|
|||
|
|
requiredPlan: ['enterprise'],
|
|||
|
|
benefits: [
|
|||
|
|
'RESTful API',
|
|||
|
|
'Webhook支持',
|
|||
|
|
'SDK下载',
|
|||
|
|
'技术文档',
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
const PLAN_DETAILS = {
|
|||
|
|
free: {
|
|||
|
|
name: '免费版',
|
|||
|
|
price: 0,
|
|||
|
|
color: 'default',
|
|||
|
|
features: ['基础商品管理', '订单管理', '基础报表'],
|
|||
|
|
limitations: ['1个店铺', '每日3次AI调用', '无API访问'],
|
|||
|
|
},
|
|||
|
|
basic: {
|
|||
|
|
name: '基础版',
|
|||
|
|
price: 99,
|
|||
|
|
color: 'blue',
|
|||
|
|
features: ['多店铺管理(3个)', 'AI运营(100次/日)', 'B2B贸易', '基础定价建议'],
|
|||
|
|
limitations: ['无独立站', '无API访问'],
|
|||
|
|
},
|
|||
|
|
pro: {
|
|||
|
|
name: '专业版',
|
|||
|
|
price: 299,
|
|||
|
|
color: 'gold',
|
|||
|
|
features: ['多店铺管理(10个)', '无限AI调用', '自动定价', '独立站', '高级分析'],
|
|||
|
|
limitations: ['无API访问'],
|
|||
|
|
},
|
|||
|
|
enterprise: {
|
|||
|
|
name: '企业版',
|
|||
|
|
price: 999,
|
|||
|
|
color: 'purple',
|
|||
|
|
features: ['无限店铺', '全部功能', 'API访问', '专属客服', '定制开发'],
|
|||
|
|
limitations: [],
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
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 currentPlan = currentUser.subscription?.plan || 'free';
|
|||
|
|
|
|||
|
|
const handleFeaturePreview = (feature: FeatureConfig) => {
|
|||
|
|
setPreviewFeature(feature);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const handleSimulatePlan = (plan: string) => {
|
|||
|
|
setSimulatedPlan(plan);
|
|||
|
|
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 columns = [
|
|||
|
|
{
|
|||
|
|
title: '功能',
|
|||
|
|
dataIndex: 'name',
|
|||
|
|
key: 'name',
|
|||
|
|
render: (name: string, record: FeatureConfig) => (
|
|||
|
|
<Space>
|
|||
|
|
{record.icon}
|
|||
|
|
<span>{name}</span>
|
|||
|
|
</Space>
|
|||
|
|
),
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
title: '免费版',
|
|||
|
|
key: 'free',
|
|||
|
|
render: (_: any, record: FeatureConfig) =>
|
|||
|
|
record.requiredPlan.includes('free') ?
|
|||
|
|
<CheckCircleOutlined style={{ color: '#52c41a' }} /> :
|
|||
|
|
<CloseCircleOutlined style={{ color: '#d9d9d9' }} />,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
title: '基础版',
|
|||
|
|
key: 'basic',
|
|||
|
|
render: (_: any, record: FeatureConfig) =>
|
|||
|
|
record.requiredPlan.includes('basic') ?
|
|||
|
|
<CheckCircleOutlined style={{ color: '#52c41a' }} /> :
|
|||
|
|
<CloseCircleOutlined style={{ color: '#d9d9d9' }} />,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
title: '专业版',
|
|||
|
|
key: 'pro',
|
|||
|
|
render: (_: any, record: FeatureConfig) =>
|
|||
|
|
record.requiredPlan.includes('pro') ?
|
|||
|
|
<CheckCircleOutlined style={{ color: '#52c41a' }} /> :
|
|||
|
|
<CloseCircleOutlined style={{ color: '#d9d9d9' }} />,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
title: '企业版',
|
|||
|
|
key: 'enterprise',
|
|||
|
|
render: (_: any, record: FeatureConfig) =>
|
|||
|
|
record.requiredPlan.includes('enterprise') ?
|
|||
|
|
<CheckCircleOutlined style={{ color: '#52c41a' }} /> :
|
|||
|
|
<CloseCircleOutlined style={{ color: '#d9d9d9' }} />,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
title: '当前状态',
|
|||
|
|
key: 'status',
|
|||
|
|
render: (_: any, record: FeatureConfig) => (
|
|||
|
|
<Tag color={isFeatureAvailable(record.key) ? 'green' : 'default'}>
|
|||
|
|
{isFeatureAvailable(record.key) ? '已开通' : '未开通'}
|
|||
|
|
</Tag>
|
|||
|
|
),
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
title: '操作',
|
|||
|
|
key: 'action',
|
|||
|
|
render: (_: any, record: FeatureConfig) => (
|
|||
|
|
<Button type="link" onClick={() => handleFeaturePreview(record)}>
|
|||
|
|
查看详情
|
|||
|
|
</Button>
|
|||
|
|
),
|
|||
|
|
},
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="subscription-manage">
|
|||
|
|
<div className="page-header" style={{ marginBottom: 24 }}>
|
|||
|
|
<Title level={2}>
|
|||
|
|
<CrownOutlined style={{ marginRight: 8 }} />
|
|||
|
|
订阅管理中心
|
|||
|
|
</Title>
|
|||
|
|
<Paragraph type="secondary">
|
|||
|
|
管理您的订阅计划和付费功能,预览不同套餐的功能差异
|
|||
|
|
</Paragraph>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<Alert
|
|||
|
|
message="临时预览模式"
|
|||
|
|
description="点击下方套餐卡片可以预览不同套餐的功能权限,实际功能以订阅为准"
|
|||
|
|
type="info"
|
|||
|
|
showIcon
|
|||
|
|
style={{ marginBottom: 24 }}
|
|||
|
|
/>
|
|||
|
|
|
|||
|
|
<Row gutter={16} style={{ marginBottom: 24 }}>
|
|||
|
|
{Object.entries(PLAN_DETAILS).map(([key, plan]) => (
|
|||
|
|
<Col span={6} key={key}>
|
|||
|
|
<Card
|
|||
|
|
hoverable
|
|||
|
|
style={{
|
|||
|
|
borderColor: currentPlan === key ? '#1890ff' : undefined,
|
|||
|
|
borderWidth: currentPlan === key ? 2 : 1,
|
|||
|
|
}}
|
|||
|
|
onClick={() => handleSimulatePlan(key)}
|
|||
|
|
>
|
|||
|
|
<div style={{ textAlign: 'center' }}>
|
|||
|
|
<Tag color={plan.color} style={{ marginBottom: 8 }}>
|
|||
|
|
{plan.name}
|
|||
|
|
</Tag>
|
|||
|
|
{currentPlan === key && (
|
|||
|
|
<Tag color="green" style={{ marginLeft: 4 }}>当前套餐</Tag>
|
|||
|
|
)}
|
|||
|
|
{simulatedPlan === key && currentPlan !== key && (
|
|||
|
|
<Tag color="blue" style={{ marginLeft: 4 }}>预览中</Tag>
|
|||
|
|
)}
|
|||
|
|
<Title level={3} style={{ margin: '8px 0' }}>
|
|||
|
|
¥{plan.price}
|
|||
|
|
<Text type="secondary" style={{ fontSize: 14 }}>/月</Text>
|
|||
|
|
</Title>
|
|||
|
|
<Divider style={{ margin: '12px 0' }} />
|
|||
|
|
<div style={{ textAlign: 'left' }}>
|
|||
|
|
{plan.features.map((f, i) => (
|
|||
|
|
<div key={i} style={{ marginBottom: 4 }}>
|
|||
|
|
<CheckCircleOutlined style={{ color: '#52c41a', marginRight: 8 }} />
|
|||
|
|
<Text>{f}</Text>
|
|||
|
|
</div>
|
|||
|
|
))}
|
|||
|
|
{plan.limitations.map((l, i) => (
|
|||
|
|
<div key={i} style={{ marginBottom: 4 }}>
|
|||
|
|
<CloseCircleOutlined style={{ color: '#d9d9d9', marginRight: 8 }} />
|
|||
|
|
<Text type="secondary">{l}</Text>
|
|||
|
|
</div>
|
|||
|
|
))}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</Card>
|
|||
|
|
</Col>
|
|||
|
|
))}
|
|||
|
|
</Row>
|
|||
|
|
|
|||
|
|
<Card title="功能对比表" style={{ marginBottom: 24 }}>
|
|||
|
|
<Table
|
|||
|
|
columns={columns}
|
|||
|
|
dataSource={FEATURE_CONFIGS}
|
|||
|
|
rowKey="key"
|
|||
|
|
pagination={false}
|
|||
|
|
/>
|
|||
|
|
</Card>
|
|||
|
|
|
|||
|
|
<Card title="当前订阅状态">
|
|||
|
|
<Row gutter={24}>
|
|||
|
|
<Col span={8}>
|
|||
|
|
<div style={{ textAlign: 'center', padding: 24 }}>
|
|||
|
|
<Tag color={PLAN_DETAILS[currentPlan as keyof typeof PLAN_DETAILS]?.color || 'default'} style={{ fontSize: 16, padding: '4px 12px' }}>
|
|||
|
|
{getPlanLabel()}
|
|||
|
|
</Tag>
|
|||
|
|
<Title level={4} style={{ marginTop: 16 }}>当前套餐</Title>
|
|||
|
|
<Text type="secondary">
|
|||
|
|
{currentUser.subscription?.expiresAt ?
|
|||
|
|
`到期时间: ${currentUser.subscription.expiresAt}` :
|
|||
|
|
'永久有效'}
|
|||
|
|
</Text>
|
|||
|
|
</div>
|
|||
|
|
</Col>
|
|||
|
|
<Col span={8}>
|
|||
|
|
<div style={{ textAlign: 'center', padding: 24 }}>
|
|||
|
|
<Title level={2}>{currentUser.subscription?.features.length || 0}</Title>
|
|||
|
|
<Text type="secondary">已开通功能</Text>
|
|||
|
|
</div>
|
|||
|
|
</Col>
|
|||
|
|
<Col span={8}>
|
|||
|
|
<div style={{ textAlign: 'center', padding: 24 }}>
|
|||
|
|
<Title level={2}>{FEATURE_CONFIGS.filter(f => !hasFeature(f.key)).length}</Title>
|
|||
|
|
<Text type="secondary">可升级功能</Text>
|
|||
|
|
</div>
|
|||
|
|
</Col>
|
|||
|
|
</Row>
|
|||
|
|
</Card>
|
|||
|
|
|
|||
|
|
<Modal
|
|||
|
|
title={previewFeature?.name}
|
|||
|
|
open={!!previewFeature}
|
|||
|
|
onCancel={() => setPreviewFeature(null)}
|
|||
|
|
footer={[
|
|||
|
|
<Button key="close" onClick={() => setPreviewFeature(null)}>
|
|||
|
|
关闭
|
|||
|
|
</Button>,
|
|||
|
|
!isFeatureAvailable(previewFeature?.key || '') && (
|
|||
|
|
<Button key="upgrade" type="primary">
|
|||
|
|
升级套餐
|
|||
|
|
</Button>
|
|||
|
|
),
|
|||
|
|
]}
|
|||
|
|
width={600}
|
|||
|
|
>
|
|||
|
|
{previewFeature && (
|
|||
|
|
<div>
|
|||
|
|
<Paragraph>{previewFeature.description}</Paragraph>
|
|||
|
|
|
|||
|
|
<Title level={5}>功能权益</Title>
|
|||
|
|
{previewFeature.benefits.map((b, i) => (
|
|||
|
|
<div key={i} style={{ marginBottom: 8 }}>
|
|||
|
|
<CheckCircleOutlined style={{ color: '#52c41a', marginRight: 8 }} />
|
|||
|
|
{b}
|
|||
|
|
</div>
|
|||
|
|
))}
|
|||
|
|
|
|||
|
|
{previewFeature.limitations && (
|
|||
|
|
<>
|
|||
|
|
<Title level={5} style={{ marginTop: 16 }}>版本限制</Title>
|
|||
|
|
{previewFeature.limitations.map((l, i) => (
|
|||
|
|
<div key={i} style={{ marginBottom: 8 }}>
|
|||
|
|
<Text type="secondary">• {l}</Text>
|
|||
|
|
</div>
|
|||
|
|
))}
|
|||
|
|
</>
|
|||
|
|
)}
|
|||
|
|
|
|||
|
|
<Divider />
|
|||
|
|
|
|||
|
|
<Title level={5}>所需套餐</Title>
|
|||
|
|
<Space>
|
|||
|
|
{previewFeature.requiredPlan.map(p => (
|
|||
|
|
<Tag key={p} color={PLAN_DETAILS[p as keyof typeof PLAN_DETAILS]?.color}>
|
|||
|
|
{PLAN_DETAILS[p as keyof typeof PLAN_DETAILS]?.name}
|
|||
|
|
</Tag>
|
|||
|
|
))}
|
|||
|
|
</Space>
|
|||
|
|
|
|||
|
|
<div style={{ marginTop: 16 }}>
|
|||
|
|
<Tag color={isFeatureAvailable(previewFeature.key) ? 'green' : 'orange'}>
|
|||
|
|
{isFeatureAvailable(previewFeature.key) ? '✓ 您已拥有此功能' : '✗ 需要升级套餐'}
|
|||
|
|
</Tag>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
</Modal>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export default SubscriptionManage;
|