diff --git a/apps/web/src/pages/points/Center.tsx b/apps/web/src/pages/points/Center.tsx index 0e0d844a5..d53251a04 100644 --- a/apps/web/src/pages/points/Center.tsx +++ b/apps/web/src/pages/points/Center.tsx @@ -1,309 +1,154 @@ /** - * 积分中心主页(/app/points) - * 展示余额、会员信息、本月统计、快捷入口 + * 积分中心首页 + * 展示: + * - 当前积分余额 + 会员状态(来自 balance + membership) + * - 每日免费额度(来自 dailyUsage) + * - 快捷入口(充值 / 消费记录 / 订阅) */ import React, { useEffect } from "react" -import { - Card, - Col, - Row, - Statistic, - Button, - Space, - Tag, - Typography, - Progress, - List, - Avatar, - Empty, -} from "antd" +import { Card, Button, Space, Statistic, Tag, Progress, Alert } from "antd" +import { useNavigate } from "react-router-dom" import { ThunderboltOutlined, - CrownFilled, - ArrowUpOutlined, - ArrowDownOutlined, + CrownOutlined, HistoryOutlined, - WalletOutlined, - FileTextOutlined, - InfoCircleOutlined, + PlusCircleOutlined, + GiftOutlined, } from "@ant-design/icons" -import { useNavigate } from "react-router-dom" -import PageHead from "@/components/layout/PageHead" import { usePointsStore } from "@/store/pointsStore" -import "./Points.css" +import styles from "./Points.css" -const { Text } = Typography - -const SOURCE_NAME: Record = { - recharge: "充值", - task_reward: "任务奖励", - ai_voice: "AI 配音", - ai_digital_human: "AI 数字人", - ai_video: "智能混剪", - voice_clone_train: "声音克隆训练", - voice_clone_synth: "声音克隆合成", - douyin_extract: "抖音提取", - ai_rewrite: "AI 改写", - ai_title: "AI 标题", - ai_cover: "AI 封面", - subscription_bonus: "会员赠送", - admin_adjust: "管理员调整", - refund: "失败退还", +const MEMBER_LABEL: Record = { + monthly: "月卡会员", + quarterly: "季卡会员", + yearly: "年卡会员", } -const PointsCenter: React.FC = () => { +const PointsCenterPage: React.FC = () => { const navigate = useNavigate() - const { balance, subscription, init, loading } = usePointsStore() + const { balance, dailyUsage, membership, subscription, rules, init } = usePointsStore() useEffect(() => { init() }, [init]) - const bal = balance?.balance ?? 0 - const earned = balance?.total_earned ?? 0 - const spent = balance?.total_spent ?? 0 - const isMember = !!balance?.is_member - const freeUsed = balance?.free_clips_used ?? 0 - const freeLimit = balance?.free_clips_limit ?? 2 - const freeRemain = balance?.free_clips_remaining ?? (isMember ? 0 : 2) - - // 近 5 条流水 mock(实际从 transactions 页加载) - const recentTx = [ - { type: "spend", source: "ai_voice", amount: 1, time: "今天 10:30" }, - { type: "spend", source: "ai_video", amount: 3, time: "今天 09:15" }, - { type: "earn", source: "task_reward", amount: 20, time: "昨天" }, - ] + const isMember = membership?.is_member ?? balance?.is_member ?? false + const memberKey = + membership?.member_type ?? + (subscription?.plan_id && subscription.plan_id !== "free" ? subscription.plan_id : null) + const memberLabel = memberKey ? MEMBER_LABEL[memberKey] || "" : "" + const freeLimit = dailyUsage?.free_clips_limit ?? 0 + const freeUsed = dailyUsage?.free_clips_used ?? 0 + const freeRemain = dailyUsage?.free_clips_remaining ?? 0 + const freePercent = freeLimit > 0 ? Math.round((freeUsed / freeLimit) * 100) : 0 + const freeMultiplier = rules?.free_user_multiplier ?? 1.15 return ( -
- +

积分中心

- actions={ - - - - - } - /> - - {/* 顶部大卡 */} - - - - - - 当前可用积分 - -
- - {loading ? "…" : bal.toLocaleString()} -
- - {isMember ? ( - } style={{ padding: "4px 10px" }}> - {subscription?.member_type === "yearly" - ? "年卡" - : subscription?.member_type === "quarterly" - ? "季卡" - : "月卡"} - 会员 - - ) : ( - - 免费会员 - - )} - {balance?.member_expires_at && ( - - 到期 {new Date(balance.member_expires_at).toLocaleDateString("zh-CN")} - - )} - {!isMember && ( - - )} - -
- - - {/* 今日免费额度 */} - {!isMember && freeLimit > 0 && ( -
-
- 今日免费混剪 -
- `${freeUsed}/${freeLimit} 条`} - /> - - 剩余 {freeRemain} 条免费混剪,超出部分消耗积分 - -
- )} - -
+ {/* 余额卡片 */} + +
+ } + valueStyle={{ color: "#faad14", fontSize: 36, fontWeight: 700 }} + /> + {isMember && memberLabel && ( + } className={styles.memberTag}> + {memberLabel} + {membership?.max_resolution ? ` · ${membership.max_resolution}` : ""} + + )} +
+ + + + + + {balance?.member_expires_at && isMember && ( +
+ 会员到期时间:{new Date(balance.member_expires_at).toLocaleDateString("zh-CN")} +
+ )}
- {/* 统计 */} - - - - } - valueStyle={{ color: "#10b981" }} + {/* 每日免费额度卡片 */} + + {!isMember && freeLimit > 0 ? ( + <> + `${freeUsed}/${freeLimit}`} /> - - - - - } - valueStyle={{ color: "#ef4444" }} - /> - - - - - } - valueStyle={{ color: "#8b5cf6" }} - /> - - - - - } - valueStyle={{ color: "#f59e0b" }} - /> - - - +
+ 今日还剩 {freeRemain} 次免费生成额度 + {dailyUsage?.reset_at && ( + + ( + {new Date(dailyUsage.reset_at).toLocaleTimeString("zh-CN", { + hour: "2-digit", + minute: "2-digit", + })}{" "} + 重置) + + )} +
+ + ) : isMember ? ( + + ) : ( + + )} +
- {/* 快捷入口 & 最近流水 */} - - - - - 最近流水 - - } - extra={ navigate("/app/points/transactions")}>查看全部 →} - > - {recentTx.length === 0 ? ( - - ) : ( - ( - - : } - /> - } - title={SOURCE_NAME[item.source] ?? item.source} - description={item.time} - /> -
- {item.type === "earn" ? "+" : "-"} - {item.amount} -
-
- )} - /> - )} -
- - - - - - - - - - - -
+ {/* 快捷提示 */} + {!isMember && rules && ( + navigate("/subscription")}> + 开通会员 + + } + style={{ marginTop: 16 }} + /> + )} + + {/* 累计统计 */} + +
+ + +
+
) } -export default PointsCenter -export const Component = PointsCenter +export default PointsCenterPage