Files
xiaoxia-saas/apps/web/src/pages/subscription/Billing.tsx
T
Audit Bot 07ee86dc65
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 210h57m58s
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Failing after 210h58m2s
fix: resolve all ESLint warnings for clean lint output
- Fix 48x @typescript-eslint/no-explicit-any: err: any → err: unknown with type narrowing
- Fix 3x @typescript-eslint/no-unused-vars: remove unused imports/variables
- Fix 2x react-refresh/only-export-components: add eslint-disable comments
- Fix 1x react-hooks/exhaustive-deps: wrap in useCallback
- 28 files modified across app/ and src/pages/
- ESLint now passes with 0 errors + 0 warnings (--max-warnings 0)
2026-06-30 17:56:10 +08:00

115 lines
3.6 KiB
TypeScript

/**
* 账单管理页面
* 展示当前订阅信息 + 自动续费开关
*/
import React, { useState, useEffect } from 'react';
import { Switch, message, Spin } from 'antd';
import { getCurrentSubscription, toggleAutoRenew } from '@/api/subscription';
import type { SubscriptionInfo } from '@/api/subscription';
import './Billing.css';
const formatDate = (iso: string): string => {
const d = new Date(iso);
return d.toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit' });
};
const Billing: React.FC = () => {
const [subscription, setSubscription] = useState<SubscriptionInfo | null>(null);
const [loading, setLoading] = useState(true);
const [autoRenewChecked, setAutoRenewChecked] = useState(false);
const [autoRenewLoading, setAutoRenewLoading] = useState(false);
useEffect(() => {
loadData();
}, []);
const loadData = async () => {
try {
const data = await getCurrentSubscription();
setSubscription(data);
setAutoRenewChecked(data.auto_renew);
} catch (err: unknown) {
if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('加载订阅数据失败');
} finally {
setLoading(false);
}
};
const handleToggleAutoRenew = async (checked: boolean) => {
setAutoRenewLoading(true);
try {
const res = await toggleAutoRenew(checked);
message.success(res.message);
setAutoRenewChecked(checked);
if (subscription) {
setSubscription({ ...subscription, auto_renew: checked });
}
} catch (err: unknown) {
if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('操作失败');
} finally {
setAutoRenewLoading(false);
}
};
if (loading) {
return (
<div className="xx-billing-page">
<Spin size="large" />
</div>
);
}
return (
<div className="xx-billing-page">
{subscription && (
<>
{/* 当前订阅概览 */}
<div className="xx-billing-overview">
<h2>当前订阅</h2>
<div className="xx-overview-details">
<div className="xx-overview-item">
<span className="xx-label">套餐</span>
<span className="xx-value">{subscription.plan_name}</span>
</div>
<div className="xx-overview-item">
<span className="xx-label">计费周期</span>
<span className="xx-value">
{subscription.billing_cycle === 'monthly' ? '月付' : '年付'}
</span>
</div>
<div className="xx-overview-item">
<span className="xx-label">下次扣费</span>
<span className="xx-value">{formatDate(subscription.current_period_end)}</span>
</div>
</div>
</div>
{/* 自动续费 */}
<div className="xx-billing-auto-renew">
<h2>自动续费</h2>
<div className="xx-auto-renew-row">
<div className="xx-auto-renew-info">
<p className="xx-auto-renew-title">到期自动续费</p>
<p className="xx-auto-renew-desc">
开启后,将在每个计费周期结束时自动扣费续期,避免服务中断。
</p>
</div>
<Switch
checked={autoRenewChecked}
onChange={handleToggleAutoRenew}
loading={autoRenewLoading}
checkedChildren="开"
unCheckedChildren="关"
/>
</div>
</div>
</>
)}
</div>
);
};
export default Billing;
export const Component = Billing;