diff --git a/src/api/membership.ts b/src/api/membership.ts index 6eed303..4d8f580 100644 --- a/src/api/membership.ts +++ b/src/api/membership.ts @@ -104,8 +104,53 @@ export const MembershipAPI = { /** * 发起会员购买/升级 - * @returns 返回 payUrl + * @returns 返回订单详情及支付 URL */ subscribe: (payload: { tier: string; durationDays: number }) => - api.post>('/membership/subscribe', payload).then(r => r.data.data), + api.post>('/membership/subscribe', payload).then(r => r.data.data), + + /** + * 查询支付状态 + */ + queryPayStatus: (payOrderNo: string) => + api.post>('/membership/pay/query', { pay_order_no: payOrderNo }).then(r => r.data.data), + + /** + * 关闭支付订单 + */ + closePayOrder: (payOrderNo: string) => + api.post>('/membership/pay/close', { pay_order_no: payOrderNo }).then(r => r.data), + + /** + * 获取订单列表 + */ + getOrders: () => + api.get>>('/membership/orders').then(r => r.data.data), + + /** + * 获取所有会员方案 + */ + getPlans: () => + api.get>('/membership/plans').then(r => r.data.data), }; diff --git a/src/pages/Pricing/PricingLogic.ts b/src/pages/Pricing/PricingLogic.ts index 81a0f3b..e55a6d0 100644 --- a/src/pages/Pricing/PricingLogic.ts +++ b/src/pages/Pricing/PricingLogic.ts @@ -6,112 +6,85 @@ import { MembershipAPI, MembershipInfo } from '../../api/membership'; export interface PricingTier { id: string; name: string; - price: string; - yearlyPrice?: string; - yearlyMonthlyPrice?: string; - unit: string; + badge?: string; description: string; - features: string[]; - points?: string; - models?: string; - bonus?: string; - tag?: string; - buttonText: string; + billing: { + monthly: { + price: number; + originalPrice?: number; + text: string; + subtext?: string; + discountText?: string; + discountRate?: number; + }; + yearly?: { + price: number; + originalPrice?: number; + text: string; + subtext?: string; + discountText?: string; + discountRate?: number; + }; + }; + features: Array<{ + icon: string; + text: string; + }>; + details: string[]; + limits: { + tier: string; + name: string; + maxSubAccounts: number; + maxTokens: number; + maxAgents: number; + maxKBSize: number; + }; 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', - }, -]; +const TIER_WEIGHTS: Record = { + 'trial': 1, + 'pro': 2, + 'ultra': 3, + 'ent_basic': 10, + 'ent_standard': 11, + 'custom': 99 +}; -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', +const CUSTOM_PLAN: PricingTier = { + id: 'custom', + name: '按需定制', + badge: '专享', + description: '适合大型团队 / 行业方案', + billing: { + monthly: { + price: 0, + text: '面议', + subtext: '专属优惠与支持', + discountRate: 1 + } }, - { - id: 'ent_standard', - name: '企业标准版', - price: '40000', - unit: '人民币/年', - description: '适合日常协作提效', - features: ['所有子账号共享企业积分池', '全员共享插件', '企业发票 (增值税普票)', '用量报告与成员消耗排行', '成员角色管理'], - points: '企业积分池 80000 / 月', - models: '高质量模型配额 100次 / 3小时', - bonus: '子账号上限 15', - tag: '热门', - buttonText: '立即开通', - type: 'enterprise', - }, - { - id: 'custom', + features: [ + { icon: 'Zap', text: '独立部署 / 专属网络环境' }, + { icon: 'Check', text: '统一登录 / 操作记录可追溯' }, + { icon: 'Crown', text: '行业专属插件与技能' }, + { icon: 'Infinity', text: '服务保障与 7×24 专属支持' } + ], + details: [ + '企业积分池 面议', + '高质量模型配额 不限量', + '子账号上限 不限量' + ], + limits: { + tier: 'custom', name: '按需定制', - price: '面议', - unit: '', - description: '适合大型团队 / 行业方案', - features: ['独立部署 / 专属网络环境', '统一登录 / 操作记录可追溯', '行业专属插件与技能', '服务保障与 7×24 专属支持', '专属折扣'], - points: '企业积分池 面议', - models: '高质量模型配额 不限量', - bonus: '子账号上限 不限量', - tag: '专享', - buttonText: '联系我们', - type: 'enterprise', + maxSubAccounts: -1, + maxTokens: -1, + maxAgents: -1, + maxKBSize: -1 }, -]; + type: 'enterprise' +}; export function usePricingLogic() { const navigate = useNavigate(); @@ -122,38 +95,164 @@ export function usePricingLogic() { const [activeTab, setActiveTab] = useState<'personal' | 'enterprise'>('personal'); const [billingCycle, setBillingCycle] = useState<'monthly' | 'yearly'>(cycle as any); const [membership, setMembership] = useState(null); + const [plans, setPlans] = useState([]); const [loading, setLoading] = useState(false); - const [timeLeft, setTimeLeft] = useState(600); // 10 minutes in seconds + const [orderInfo, setOrderInfo] = useState<{ + order_id: string; + pay_url: string; + pay_expire_at: string; + pay_is_expired: boolean; + } | null>(null); + const [payStatus, setPayStatus] = useState<'PENDING' | 'SUCCESS' | 'CLOSED' | 'FAIL' | 'EXPIRED'>('PENDING'); + const [timeLeft, setTimeLeft] = useState(0); const { message } = AntApp.useApp(); + // 分类方案 + const PERSONAL_TIERS = useMemo(() => plans.filter(p => p.type === 'personal'), [plans]); + const ENTERPRISE_TIERS = useMemo(() => plans.filter(p => p.type === 'enterprise'), [plans]); + // 根据 URL 参数计算当前的支付信息 - const paymentInfo = useMemo(() => { - if (!tierId) return null; - const allTiers = [...PERSONAL_TIERS, ...ENTERPRISE_TIERS]; - const tier = allTiers.find(t => t.id === tierId); + const tierInfo = useMemo(() => { + if (!tierId || plans.length === 0) return null; + const tier = plans.find(t => t.id === tierId); if (!tier) return null; return { tier, - duration: cycle === 'monthly' ? 30 : 365 + duration: billingCycle === 'monthly' ? 30 : 365 }; - }, [tierId, cycle]); + }, [tierId, billingCycle, plans]); + + // 综合支付信息 + const paymentInfo = useMemo(() => { + if (!tierInfo || !orderInfo) return null; + return { + ...tierInfo, + ...orderInfo, + status: payStatus, + }; + }, [tierInfo, orderInfo, payStatus]); useEffect(() => { loadMembership(); + loadPlans(); }, []); + useEffect(() => { + if (activeTab === 'enterprise') { + setBillingCycle('monthly'); + } + }, [activeTab]); + + const loadPlans = async () => { + setLoading(true); + try { + const res = await MembershipAPI.getPlans(); + // res 结构: { categories: [ { id: 'personal', plans: [...] }, ... ] } + const categories = res.categories || []; + const allPlans: PricingTier[] = []; + + categories.forEach((cat: any) => { + const catPlans = (cat.plans || []).map((p: any) => ({ + ...p, + type: cat.id // 注入 personal 或 enterprise + })); + allPlans.push(...catPlans); + }); + + // 追加静态的“按需定制”方案到企业版 + if (!allPlans.find(p => p.id === 'custom')) { + allPlans.push(CUSTOM_PLAN); + } + + setPlans(allPlans); + } catch (e) { + console.error('Failed to load plans', e); + message.error('加载会员方案失败'); + } finally { + setLoading(false); + } + }; + + // 当进入支付路由时,发起订阅请求 + useEffect(() => { + if (tierId && tierInfo && !orderInfo) { + createOrder(); + } + }, [tierId, tierInfo]); + + // 倒计时逻辑 useEffect(() => { let timer: any; - if (paymentInfo && timeLeft > 0) { + if (orderInfo && timeLeft > 0 && payStatus === 'PENDING') { timer = setInterval(() => { - setTimeLeft((prev) => prev - 1); + setTimeLeft((prev) => { + if (prev <= 1) { + setPayStatus('EXPIRED'); + return 0; + } + return prev - 1; + }); }, 1000); - } else if (timeLeft === 0) { - navigate('/pricing', { replace: true }); - message.error('支付超时,请重新发起'); } return () => clearInterval(timer); - }, [paymentInfo, timeLeft, navigate]); + }, [orderInfo, timeLeft, payStatus]); + + // 轮询支付状态 + useEffect(() => { + let pollTimer: any; + if (orderInfo && payStatus === 'PENDING') { + pollTimer = setInterval(async () => { + try { + const res = await MembershipAPI.queryPayStatus(orderInfo.order_id); + if (res.status === 'SUCCESS') { + setPayStatus('SUCCESS'); + message.success('支付成功!'); + loadMembership(); // 刷新会员信息 + clearInterval(pollTimer); + } else if (res.status === 'CLOSED' || res.status === 'FAIL') { + setPayStatus(res.status); + message.error(`支付失败: ${res.status}`); + clearInterval(pollTimer); + } else if (res.pay_is_expired) { + setPayStatus('EXPIRED'); + message.warning('支付已过期,请重新下单'); + clearInterval(pollTimer); + } + } catch (e) { + console.error('Polling payment status failed', e); + } + }, 2000); + } + return () => clearInterval(pollTimer); + }, [orderInfo, payStatus]); + + const createOrder = async () => { + if (!tierId || !tierInfo) return; + setLoading(true); + try { + const res = await MembershipAPI.subscribe({ + tier: tierId, + durationDays: tierInfo.duration, + }); + setOrderInfo(res); + setPayStatus('PENDING'); + + // 计算剩余秒数 + const expireTime = new Date(res.pay_expire_at).getTime(); + const now = new Date().getTime(); + const diff = Math.floor((expireTime - now) / 1000); + setTimeLeft(diff > 0 ? diff : 0); + + if (res.pay_is_expired) { + setPayStatus('EXPIRED'); + } + } catch (e: any) { + message.error(e.response?.data?.message || '创建订单失败,请稍后重试'); + navigate('/pricing'); + } finally { + setLoading(false); + } + }; const loadMembership = async () => { try { @@ -165,17 +264,48 @@ export function usePricingLogic() { }; const handleSubscribe = async (tier: PricingTier) => { - if (tier.price === '面议' || tier.id === 'trial') return; + // 基础校验 + if (tier.id === 'custom') { + message.info('请联系您的专属大客户经理或拨打客服热线进行面议'); + return; + } + + const price = billingCycle === 'yearly' && tier.billing.yearly + ? tier.billing.yearly.price + : tier.billing.monthly.price; + + if (tier.id === 'trial' && membership?.tier === 'trial') { + message.info('您当前已在试用期内'); + return; + } // 使用子路由跳转 navigate(`/pricing/pay/${tier.id}?cycle=${billingCycle}`); - setTimeLeft(600); }; - const cancelPayment = () => { + const cancelPayment = async () => { + if (orderInfo) { + try { + await MembershipAPI.closePayOrder(orderInfo.order_id); + } catch (e) { + console.error('Failed to close order', e); + } + } + setOrderInfo(null); + setPayStatus('PENDING'); navigate('/pricing'); }; + const getTierStatus = (targetTierId: string) => { + if (!membership) return 'none'; + const currentWeight = TIER_WEIGHTS[membership.tier] || 0; + const targetWeight = TIER_WEIGHTS[targetTierId] || 0; + + if (membership.tier === targetTierId) return 'current'; + if (currentWeight > targetWeight) return 'included'; + return 'none'; + }; + return { activeTab, setActiveTab, @@ -189,6 +319,7 @@ export function usePricingLogic() { cancelPayment, PERSONAL_TIERS, ENTERPRISE_TIERS, + getTierStatus, }; } diff --git a/src/pages/Pricing/components/PricingH5.tsx b/src/pages/Pricing/components/PricingH5.tsx index 8674bd4..74ac76b 100644 --- a/src/pages/Pricing/components/PricingH5.tsx +++ b/src/pages/Pricing/components/PricingH5.tsx @@ -1,5 +1,16 @@ -import { CheckOutlined, ArrowLeftOutlined, LoadingOutlined } from '@ant-design/icons'; -import { Button, Spin, QRCode, Divider } from 'antd'; +import { + CheckOutlined, + ArrowLeftOutlined, + LoadingOutlined, + CheckCircleOutlined, + CloseCircleOutlined, + ExclamationCircleOutlined, + ThunderboltOutlined, + CrownOutlined, + TeamOutlined, + RadiusSettingOutlined +} from '@ant-design/icons'; +import { Button, Spin, QRCode, Divider, Result, Tag } from 'antd'; import type { PricingLogicOutput } from '../PricingLogic'; import '../styles/pricing.css'; @@ -7,8 +18,19 @@ interface Props { logic: PricingLogicOutput; } +const FeatureIcon = ({ name }: { name: string }) => { + switch (name) { + case 'Check': return ; + case 'Zap': return ; + case 'Crown': return ; + case 'Users': return ; + case 'Infinity': return ; + default: return ; + } +}; + const DiamondIcon = () => ( - + ); export default function PricingH5({ logic }: Props) { @@ -17,6 +39,7 @@ export default function PricingH5({ logic }: Props) { setActiveTab, billingCycle, setBillingCycle, + membership, loading, handleSubscribe, paymentInfo, @@ -24,13 +47,53 @@ export default function PricingH5({ logic }: Props) { cancelPayment, PERSONAL_TIERS, ENTERPRISE_TIERS, + getTierStatus, } = logic; if (paymentInfo) { - const { tier, duration } = paymentInfo; + const { tier, duration, status, pay_url } = paymentInfo; const minutes = Math.floor(timeLeft / 60); const seconds = timeLeft % 60; - const price = duration === 30 ? tier.price : (tier.yearlyPrice || tier.price); + const billing = billingCycle === 'yearly' && tier.billing.yearly ? tier.billing.yearly : tier.billing.monthly; + const price = billing.price; + + if (status === 'SUCCESS') { + return ( +
+
+ + 完成 + , + ]} + /> +
+
+ ); + } + + if (status === 'CLOSED' || status === 'FAIL' || status === 'EXPIRED') { + return ( +
+
+ + 返回重试 + , + ]} + /> +
+
+ ); + } return (
@@ -69,7 +132,7 @@ export default function PricingH5({ logic }: Props) {
- +
正在等待支付结果...
@@ -100,23 +163,25 @@ export default function PricingH5({ logic }: Props) {

选择套餐

-
-
setActiveTab('personal')} - > - 个人版 -
-
setActiveTab('enterprise')} - > - 企业版 +
+
+
setActiveTab('personal')} + > + 个人版 +
+
setActiveTab('enterprise')} + > + 企业版 +
{activeTab === 'personal' && ( -
+
setBillingCycle('yearly')} > - 年付 -2月 + 年付 更优
@@ -139,66 +204,68 @@ export default function PricingH5({ logic }: Props) {
) : (
- {tiers.map((tier) => ( -
- {tier.tag &&
{tier.tag}
} - -
{tier.name}
- -
- {tier.price !== '面议' && ¥} - - {billingCycle === 'yearly' && tier.yearlyMonthlyPrice - ? tier.yearlyMonthlyPrice - : tier.price} - - {tier.unit && {tier.unit}} -
- - {billingCycle === 'yearly' && tier.yearlyPrice && ( -
- ¥{tier.yearlyPrice} / 年 + {tiers.map((tier) => { + const billing = billingCycle === 'yearly' && tier.billing.yearly ? tier.billing.yearly : tier.billing.monthly; + + return ( +
+ {tier.badge &&
{tier.badge}
} + +
{tier.name}
+ +
+ {billing.text.split(' ')[0]} + {billing.text.split(' ')[1] + ' ' + billing.text.split(' ')[2]}
- )} - -
{tier.description}
- - -
- {tier.points && ( -
- {tier.points} -
- )} - {tier.bonus && ( -
- {tier.bonus} -
- )} - {tier.models && ( -
- {tier.models} + {billing.subtext && ( +
+ {billing.subtext} + {billing.discountText && {billing.discountText}}
)} + +
{tier.description}
+ + + +
    + {tier.features.map((feature, idx) => ( +
  • + + {feature.text} +
  • + ))} +
+ + + +
+ {tier.details.map((detail, idx) => ( +
+ + {detail} +
+ ))} +
- -
    - {tier.features.map((feature, idx) => ( -
  • - - {feature} -
  • - ))} -
-
- ))} + ); + })}
)}
diff --git a/src/pages/Pricing/components/PricingWeb.tsx b/src/pages/Pricing/components/PricingWeb.tsx index 2326ce7..cecba6b 100644 --- a/src/pages/Pricing/components/PricingWeb.tsx +++ b/src/pages/Pricing/components/PricingWeb.tsx @@ -1,22 +1,44 @@ -import { CheckOutlined, ArrowLeftOutlined, LoadingOutlined } from '@ant-design/icons'; -import { Button, Spin, QRCode, Space, Divider } from 'antd'; -import type { PricingLogicOutput, PricingTier } from '../PricingLogic'; +import { + CheckOutlined, + ArrowLeftOutlined, + LoadingOutlined, + ThunderboltOutlined, + CrownOutlined, + TeamOutlined, + RadiusSettingOutlined +} from '@ant-design/icons'; +import { Button, Spin, QRCode, Divider, Result, Tag } from 'antd'; +import type { PricingLogicOutput } from '../PricingLogic'; import '../styles/pricing.css'; +import { useNavigate } from 'react-router-dom'; interface Props { logic: PricingLogicOutput; } +const FeatureIcon = ({ name }: { name: string }) => { + switch (name) { + case 'Check': return ; + case 'Zap': return ; + case 'Crown': return ; + case 'Users': return ; + case 'Infinity': return ; + default: return ; + } +}; + const DiamondIcon = () => ( - + ); export default function PricingWeb({ logic }: Props) { + const navigate = useNavigate(); const { activeTab, setActiveTab, billingCycle, setBillingCycle, + membership, loading, handleSubscribe, paymentInfo, @@ -24,13 +46,53 @@ export default function PricingWeb({ logic }: Props) { cancelPayment, PERSONAL_TIERS, ENTERPRISE_TIERS, + getTierStatus, } = logic; if (paymentInfo) { - const { tier, duration } = paymentInfo; + const { tier, duration, status, pay_url } = paymentInfo; const minutes = Math.floor(timeLeft / 60); const seconds = timeLeft % 60; - const price = duration === 30 ? tier.price : (tier.yearlyPrice || tier.price); + const billing = billingCycle === 'yearly' && tier.billing.yearly ? tier.billing.yearly : tier.billing.monthly; + const price = billing.price; + + if (status === 'SUCCESS') { + return ( +
+
+ navigate('/chat')}> + 去对话 + , + ]} + /> +
+
+ ); + } + + if (status === 'CLOSED' || status === 'FAIL' || status === 'EXPIRED') { + return ( +
+
+ + 返回重新下单 + , + ]} + /> +
+
+ ); + } return (
@@ -69,7 +131,7 @@ export default function PricingWeb({ logic }: Props) {
- +
正在等待支付结果...
@@ -100,35 +162,37 @@ export default function PricingWeb({ logic }: Props) {

选择适合你工作方式的套餐

-
-
setActiveTab('personal')} - > - 个人版 -
-
setActiveTab('enterprise')} - > - 企业版 +
+
+
setActiveTab('personal')} + > + 个人版 +
+
setActiveTab('enterprise')} + > + 企业版 +
{activeTab === 'personal' && ( -
+
setBillingCycle('monthly')} > - 连续包月 + 按月支付
setBillingCycle('yearly')} > - 连续包年 立省两月 + 按年支付 更优惠
@@ -139,66 +203,71 @@ export default function PricingWeb({ logic }: Props) {
) : (
- {tiers.map((tier) => ( -
- {tier.tag &&
{tier.tag}
} - -
{tier.name}
- -
- {tier.price !== '面议' && ¥} - - {billingCycle === 'yearly' && tier.yearlyMonthlyPrice - ? tier.yearlyMonthlyPrice - : tier.price} - - {tier.unit && {tier.unit}} -
- - {billingCycle === 'yearly' && tier.yearlyPrice && ( -
- ¥{tier.yearlyPrice} / 年 + {tiers.map((tier) => { + const billing = billingCycle === 'yearly' && tier.billing.yearly ? tier.billing.yearly : tier.billing.monthly; + const showPrice = tier.type === 'enterprise' ? (billing.price > 0 ? billing.price : '面议') : (billing.price > 0 ? billing.price : '免费'); + + return ( +
+ {tier.badge &&
{tier.badge}
} + +
{tier.name}
+ +
+ {showPrice} + {billing.price > 0 && {billing.text.split(' ')[1] + ' ' + billing.text.split(' ')[2]}}
- )} - - {billingCycle === 'monthly' &&
{tier.description}
} - - -
- {tier.points && ( -
- {tier.points} -
- )} - {tier.bonus && ( -
- {tier.bonus} -
- )} - {tier.models && ( -
- {tier.models} + {billing.subtext && ( +
+ {billing.subtext} + {billing.discountText && {billing.discountText}}
)} + +
{tier.description}
+ +
+ +
+ +
    + {tier.features.map((feature, idx) => ( +
  • + + {feature.text} +
  • + ))} +
+ + + +
+ {tier.details.map((detail, idx) => ( +
+ + {detail} +
+ ))} +
- -
    - {tier.features.map((feature, idx) => ( -
  • - - {feature} -
  • - ))} -
-
- ))} + ); + })}
)}
diff --git a/src/pages/Pricing/styles/pricing.css b/src/pages/Pricing/styles/pricing.css index d819501..2dd6b3b 100644 --- a/src/pages/Pricing/styles/pricing.css +++ b/src/pages/Pricing/styles/pricing.css @@ -150,23 +150,41 @@ border-radius: 24px; font-weight: 600; margin-bottom: 24px; - border: none; + border: 1px solid transparent; cursor: pointer; transition: all 0.3s; background: #1f2937; color: #fff; } -.pricing-card.highlight .pricing-card-btn { - background: var(--color-brand); +.pricing-card-btn:hover:not(:disabled) { + background: #374151 !important; + color: #fff !important; + border-color: #374151 !important; } -.btn-trial { - background: var(--color-fill-secondary); - color: var(--color-text-tertiary); - cursor: not-allowed; +.pricing-card.highlight .pricing-card-btn { + background: var(--color-brand); + border-color: var(--color-brand); } +.pricing-card.highlight .pricing-card-btn:hover:not(:disabled) { + background: #059669 !important; + border-color: #059669 !important; + color: #fff !important; +} + +.pricing-card-btn:disabled, +.pricing-card-btn.ant-btn-disabled { + background: #f3f4f6 !important; + color: #9ca3af !important; + border-color: #e5e7eb !important; + cursor: not-allowed !important; + opacity: 1 !important; + pointer-events: none; +} + + .pricing-card-features { margin-top: 24px; padding-top: 24px; diff --git a/src/pages/Profile/ProfileLogic.ts b/src/pages/Profile/ProfileLogic.ts index 3b64ac5..aad7b50 100644 --- a/src/pages/Profile/ProfileLogic.ts +++ b/src/pages/Profile/ProfileLogic.ts @@ -9,6 +9,7 @@ export function useProfileLogic() { const [membership, setMembership] = useState(null); const [members, setMembers] = useState([]); const [myAgents, setMyAgents] = useState([]); + const [orders, setOrders] = useState([]); const [loading, setLoading] = useState(false); const { message, modal } = AntApp.useApp(); @@ -50,6 +51,14 @@ export function useProfileLogic() { console.error('Failed to fetch agents', e); } } + + // 4. 获取订单记录 + try { + const orderList = await MembershipAPI.getOrders(); + setOrders(orderList); + } catch (e) { + console.error('Failed to fetch orders', e); + } } catch (e) { console.error('Failed to init profile', e); } finally { @@ -125,6 +134,7 @@ export function useProfileLogic() { membership, members, myAgents, + orders, loading, init, handleAddChild, diff --git a/src/pages/Profile/components/ProfileH5.tsx b/src/pages/Profile/components/ProfileH5.tsx index 35630b3..1903048 100644 --- a/src/pages/Profile/components/ProfileH5.tsx +++ b/src/pages/Profile/components/ProfileH5.tsx @@ -1,6 +1,6 @@ import { useState } from 'react'; -import { Button, Modal, Input, Select, Space, Tag, InputNumber, Popconfirm, List, Card, Checkbox, Divider } from 'antd'; -import { UserAddOutlined, DeleteOutlined, KeyOutlined, DashboardOutlined } from '@ant-design/icons'; +import { Button, Modal, Input, Select, Space, Tag, InputNumber, Popconfirm, List, Card, Checkbox, Divider, Tabs } from 'antd'; +import { UserAddOutlined, DeleteOutlined, KeyOutlined, DashboardOutlined, HistoryOutlined, TeamOutlined } from '@ant-design/icons'; import type { ProfileLogicOutput } from '../ProfileLogic'; import '../styles/profile.css'; @@ -14,6 +14,7 @@ export default function ProfileH5({ logic }: Props) { membership, members, myAgents, + orders, loading, handleAddChild, handleRemoveChild, @@ -67,81 +68,162 @@ export default function ProfileH5({ logic }: Props) {
-
-
-

子账号管理

- -
- - ( - { setSelectedMember(record); setQuotaModalVisible(true); }} />, - { - setSelectedMember(record); - setSelectedAgentIds(record.authorizedAgentIds || []); - setAuthModalVisible(true); - }} - />, - handleRemoveChild(record.id)}> - - , - ]} - > - {record.child_name?.charAt(0)}
} - title={record.child_name} - description={ -
-
{record.child_phone}
-
- {record.allocatedTokens?.toLocaleString()} Tokens -
-
- {record.authorizedAgentIds?.map((id: string) => { - const agent = myAgents.find(a => a.id === id); - return ( - handleRevokeAgent(record.child_user_id, [id])} - style={{ borderRadius: 4, fontSize: 10, margin: '2px', display: 'flex', width: 'fit-content' }} - > - 🤖 {agent?.name || '未知'} - - ); - })} - {record.authorizedAgentIds?.length > 0 && ( - handleRevokeAgent(record.child_user_id, record.authorizedAgentIds)} - > - - - )} -
+
+ + + 成员 + + ), + children: ( +
+
+

子账号管理

+
- } - /> - - )} + + ( + { setSelectedMember(record); setQuotaModalVisible(true); }} />, + { + setSelectedMember(record); + setSelectedAgentIds(record.authorizedAgentIds || []); + setAuthModalVisible(true); + }} + />, + handleRemoveChild(record.id)}> + + , + ]} + > + {record.child_name?.charAt(0)}
} + title={record.child_name} + description={ +
+
{record.child_phone}
+
+ {record.allocatedTokens?.toLocaleString()} Tokens +
+
+ {record.authorizedAgentIds?.map((id: string) => { + const agent = myAgents.find(a => a.id === id); + return ( + handleRevokeAgent(record.child_user_id, [id])} + style={{ borderRadius: 4, fontSize: 10, margin: '2px', display: 'flex', width: 'fit-content' }} + > + 🤖 {agent?.name || '未知'} + + ); + })} + {record.authorizedAgentIds?.length > 0 && ( + handleRevokeAgent(record.child_user_id, record.authorizedAgentIds)} + > + + + )} +
+
+ } + /> + + )} + /> +
+ ), + }, + { + key: 'orders', + label: ( + + + 订单 + + ), + children: ( +
+
+

订阅记录

+
+ + { + const tierNames: Record = { + trial: '个人试用版', + pro: '个人专业版', + ultra: '个人旗舰版', + ent_basic: '企业入门版', + ent_standard: '企业标准版' + }; + return ( + +
+
+
{tierNames[record.tier] || record.tier}
+
+ 订单号: {record.pay_order_no} +
+
+ {new Date(record.created_at).toLocaleString()} +
+
+
+
¥{record.amount}
+ {(() => { + const statusMap: Record = { + PENDING: { color: 'processing', text: '待支付' }, + SUCCESS: { color: 'success', text: '已支付' }, + FAIL: { color: 'error', text: '失败' }, + CLOSED: { color: 'default', text: '关闭' } + }; + const config = statusMap[record.status] || { color: 'default', text: record.status }; + return ( + + {config.text} + + ); + })()} +
+
+
+ ); + }} + /> +
+ ), + }, + ]} />
diff --git a/src/pages/Profile/components/ProfileWeb.tsx b/src/pages/Profile/components/ProfileWeb.tsx index 977217b..5ec987e 100644 --- a/src/pages/Profile/components/ProfileWeb.tsx +++ b/src/pages/Profile/components/ProfileWeb.tsx @@ -1,6 +1,6 @@ import { useState } from 'react'; -import { Button, Table, Modal, Input, Select, Space, Tag, InputNumber, Popconfirm, Checkbox, Divider } from 'antd'; -import { UserAddOutlined, DeleteOutlined, KeyOutlined, DashboardOutlined } from '@ant-design/icons'; +import { Button, Table, Modal, Input, Select, Space, Tag, InputNumber, Popconfirm, Checkbox, Divider, Tabs } from 'antd'; +import { UserAddOutlined, DeleteOutlined, KeyOutlined, DashboardOutlined, HistoryOutlined, TeamOutlined } from '@ant-design/icons'; import type { ProfileLogicOutput } from '../ProfileLogic'; import '../styles/profile.css'; @@ -14,6 +14,7 @@ export default function ProfileWeb({ logic }: Props) { membership, members, myAgents, + orders, loading, handleAddChild, handleRemoveChild, @@ -133,6 +134,56 @@ export default function ProfileWeb({ logic }: Props) { ), }, ]; + + const orderColumns = [ + { + title: '订单编号', + dataIndex: 'id', + key: 'id', + }, + { + title: '订阅方案', + dataIndex: 'tier', + key: 'tier', + render: (tier: string) => { + const tierNames: Record = { + trial: '个人试用版', + pro: '个人专业版', + ultra: '个人旗舰版', + ent_basic: '企业入门版', + ent_standard: '企业标准版' + }; + return tierNames[tier] || tier; + } + }, + { + title: '金额', + dataIndex: 'amount', + key: 'amount', + render: (amount: string) => `¥${amount}`, + }, + { + title: '状态', + dataIndex: 'status', + key: 'status', + render: (status: string) => { + const statusMap: Record = { + PENDING: { color: 'processing', text: '待支付' }, + SUCCESS: { color: 'success', text: '支付成功' }, + FAIL: { color: 'error', text: '支付失败' }, + CLOSED: { color: 'default', text: '已关闭' } + }; + const config = statusMap[status] || { color: 'default', text: status }; + return {config.text}; + }, + }, + { + title: '下单时间', + dataIndex: 'created_at', + key: 'created_at', + render: (val: number) => val ? new Date(val).toLocaleString() : '-', + }, + ]; return (
@@ -169,26 +220,69 @@ export default function ProfileWeb({ logic }: Props) {
-
-
-

子账号管理

- -
+
+ + + 子账号管理 + + ), + children: ( +
+
+

子账号管理

+ +
- + + ), + }, + { + key: 'orders', + label: ( + + + 订阅记录 + + ), + children: ( +
+
+

订阅记录

+
+ +
+ + ), + }, + ]} />