feat(subscription): 实现订阅管理前端页面
- 新增 api/subscription.ts: 订阅 API 模块(mock数据+接口定义) - 新增 UpgradeSubscription.tsx + .css: 升级/降级/续费页面 - 新增 Billing.tsx + .css: 账单管理页面
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* 订阅 API 模块
|
||||
* 提供订阅管理相关接口(当前使用 mock 数据,后端就绪后切换)
|
||||
*/
|
||||
import apiClient from './client';
|
||||
|
||||
/** 套餐类型 */
|
||||
export type PlanType = 'free' | 'standard' | 'pro' | 'enterprise';
|
||||
|
||||
/** 订阅状态 */
|
||||
export type SubscriptionStatus = 'active' | 'expired' | 'cancelled' | 'trial';
|
||||
|
||||
/** 账单状态 */
|
||||
export type BillingStatus = 'paid' | 'pending' | 'failed' | 'refunded';
|
||||
|
||||
/** 计费周期 */
|
||||
export type BillingCycle = 'monthly' | 'yearly';
|
||||
|
||||
/** 套餐信息 */
|
||||
export interface Plan {
|
||||
id: PlanType;
|
||||
name: string;
|
||||
price: number | null;
|
||||
yearly_price?: number | null;
|
||||
description: string;
|
||||
recommended: boolean;
|
||||
features: string[];
|
||||
}
|
||||
|
||||
/** 当前订阅信息 */
|
||||
export interface SubscriptionInfo {
|
||||
id: string;
|
||||
plan_id: PlanType;
|
||||
plan_name: string;
|
||||
status: SubscriptionStatus;
|
||||
billing_cycle: BillingCycle;
|
||||
current_period_start: string;
|
||||
current_period_end: string;
|
||||
amount: number;
|
||||
auto_renew: boolean;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
/** 账单记录 */
|
||||
export interface BillingRecord {
|
||||
id: string;
|
||||
plan_name: string;
|
||||
amount: number;
|
||||
billing_cycle: BillingCycle;
|
||||
status: BillingStatus;
|
||||
payment_method: string;
|
||||
created_at: string;
|
||||
invoice_url?: string;
|
||||
}
|
||||
|
||||
/** 升级/降级请求 */
|
||||
export interface ChangePlanRequest {
|
||||
target_plan_id: PlanType;
|
||||
billing_cycle: BillingCycle;
|
||||
}
|
||||
|
||||
/** 升级/降级响应 */
|
||||
export interface ChangePlanResponse {
|
||||
success: boolean;
|
||||
message: string;
|
||||
new_subscription?: SubscriptionInfo;
|
||||
}
|
||||
|
||||
// ============ Mock 数据 ============
|
||||
|
||||
const MOCK_SUBSCRIPTION: SubscriptionInfo = {
|
||||
id: 'sub-001',
|
||||
plan_id: 'standard',
|
||||
plan_name: '标准版',
|
||||
status: 'active',
|
||||
billing_cycle: 'monthly',
|
||||
current_period_start: '2026-06-01T00:00:00Z',
|
||||
current_period_end: '2026-07-01T00:00:00Z',
|
||||
amount: 99,
|
||||
auto_renew: true,
|
||||
created_at: '2026-03-01T00:00:00Z',
|
||||
};
|
||||
|
||||
const MOCK_BILLING_RECORDS: BillingRecord[] = [
|
||||
{
|
||||
id: 'bill-001', plan_name: '标准版', amount: 99,
|
||||
billing_cycle: 'monthly', status: 'paid', payment_method: '微信支付',
|
||||
created_at: '2026-06-01T00:00:00Z', invoice_url: '#',
|
||||
},
|
||||
{
|
||||
id: 'bill-002', plan_name: '标准版', amount: 99,
|
||||
billing_cycle: 'monthly', status: 'paid', payment_method: '微信支付',
|
||||
created_at: '2026-05-01T00:00:00Z', invoice_url: '#',
|
||||
},
|
||||
{
|
||||
id: 'bill-003', plan_name: '标准版', amount: 99,
|
||||
billing_cycle: 'monthly', status: 'paid', payment_method: '支付宝',
|
||||
created_at: '2026-04-01T00:00:00Z', invoice_url: '#',
|
||||
},
|
||||
];
|
||||
|
||||
/** 是否使用 mock 数据(后端就绪后改为 false) */
|
||||
const USE_MOCK = true;
|
||||
|
||||
// ============ API 函数 ============
|
||||
|
||||
/** 获取当前订阅信息 */
|
||||
export const getCurrentSubscription = async (): Promise<SubscriptionInfo> => {
|
||||
if (USE_MOCK) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 300));
|
||||
return MOCK_SUBSCRIPTION;
|
||||
}
|
||||
const response = await apiClient.get('/subscription/current');
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/** 获取账单记录列表 */
|
||||
export const getBillingRecords = async (): Promise<BillingRecord[]> => {
|
||||
if (USE_MOCK) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 300));
|
||||
return MOCK_BILLING_RECORDS;
|
||||
}
|
||||
const response = await apiClient.get('/subscription/billing');
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/** 升级/降级套餐 */
|
||||
export const changePlan = async (request: ChangePlanRequest): Promise<ChangePlanResponse> => {
|
||||
if (USE_MOCK) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
return {
|
||||
success: true,
|
||||
message: '套餐变更成功',
|
||||
new_subscription: {
|
||||
...MOCK_SUBSCRIPTION,
|
||||
plan_id: request.target_plan_id,
|
||||
plan_name: request.target_plan_id === 'pro' ? '专业版' : request.target_plan_id === 'standard' ? '标准版' : '体验版',
|
||||
},
|
||||
};
|
||||
}
|
||||
const response = await apiClient.post('/subscription/change', request);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/** 取消订阅 */
|
||||
export const cancelSubscription = async (): Promise<{ success: boolean; message: string }> => {
|
||||
if (USE_MOCK) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 800));
|
||||
return { success: true, message: '订阅已取消,当前周期结束后停止服务' };
|
||||
}
|
||||
const response = await apiClient.post('/subscription/cancel');
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/** 切换自动续费 */
|
||||
export const toggleAutoRenew = async (enabled: boolean): Promise<{ success: boolean; message: string }> => {
|
||||
if (USE_MOCK) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 300));
|
||||
return { success: true, message: enabled ? '已开启自动续费' : '已关闭自动续费' };
|
||||
}
|
||||
const response = await apiClient.post('/subscription/auto-renew', { enabled });
|
||||
return response.data;
|
||||
};
|
||||
Reference in New Issue
Block a user