From a4e54011cab8f606d9f30309fcabf05f87d1ab78 Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Wed, 16 Sep 2026 10:19:26 +0800 Subject: [PATCH] chore(points): align types/api with final backend contract - apps/web/src/components/common/PointsBadge/index.tsx --- .../components/common/PointsBadge/index.tsx | 144 ++++++------------ 1 file changed, 44 insertions(+), 100 deletions(-) diff --git a/apps/web/src/components/common/PointsBadge/index.tsx b/apps/web/src/components/common/PointsBadge/index.tsx index 3c26fff21..3e25927b0 100644 --- a/apps/web/src/components/common/PointsBadge/index.tsx +++ b/apps/web/src/components/common/PointsBadge/index.tsx @@ -1,114 +1,58 @@ /** - * Header 右上角的积分徽章(💎 580) - * - 余额 <10 时橙色告警 - * - 点击弹出 Popover:余额、会员信息、充值入口、积分明细入口 + * 头部积分徽章组件 + * - 展示当前积分余额 + * - 会员标签(基于 membership 聚合信息或 subscription.plan_id 判断) + * - 点击跳转积分中心 */ -import React, { useEffect } from "react" -import { Popover, Button, Tag, Space, Typography, Badge } from "antd" -import { - ThunderboltOutlined, - CrownOutlined, - RightOutlined, - WarningOutlined, -} from "@ant-design/icons" +import React from "react" import { useNavigate } from "react-router-dom" +import { Tooltip, Typography } from "antd" +import { CrownOutlined, StarFilled } from "@ant-design/icons" import { usePointsStore } from "@/store/pointsStore" -import "./PointsBadge.css" +import styles from "./PointsBadge.module.css" -const { Text, Paragraph } = Typography +const { Text } = Typography + +/** 会员类型标签映射(基于 GET /points/subscription/membership 返回的 member_type 字符串) */ +const MEMBER_LABEL: Record = { + monthly: "月卡会员", + quarterly: "季卡会员", + yearly: "年卡会员", +} + +const MEMBER_COLOR: Record = { + monthly: "#fa8c16", + quarterly: "#eb2f96", + yearly: "#fadb14", +} const PointsBadge: React.FC = () => { const navigate = useNavigate() - const { balance, init, loading } = usePointsStore() + const { balance, membership, subscription } = usePointsStore() - useEffect(() => { - if (!balance) init() - }, [balance, init]) - - const bal = balance?.balance ?? 0 - const lowBalance = bal > 0 && bal < 10 - const zero = bal === 0 - const isMember = !!balance?.is_member - const memberLabel = isMember - ? balance?.member_type === "yearly" - ? "年卡会员" - : balance?.member_type === "quarterly" - ? "季卡会员" - : balance?.member_type === "monthly" - ? "月卡会员" - : "付费会员" - : "免费会员" - - const popContent = ( -
-
-
- - {loading ? "…" : bal} - 积分 -
- }> - {memberLabel} - -
- - {(zero || lowBalance) && ( - - 积分不足,充值后可继续使用 AI 功能 - - )} - - {balance?.member_expires_at && ( - - 会员到期:{new Date(balance.member_expires_at).toLocaleDateString("zh-CN")} - - )} - -
-
- 累计获得 -
+{balance?.total_earned ?? 0}
-
-
- 累计消耗 -
-{balance?.total_spent ?? 0}
-
-
- - - - - {!isMember && ( - - )} - -
- ) + const points = membership?.points_balance ?? balance?.balance ?? 0 + // 优先用 membership.member_type 判定会员状态;否则用 subscription.plan_id + const memberKey: string | null = + membership?.member_type ?? + (subscription?.plan_id && subscription.plan_id !== "free" ? subscription.plan_id : null) + const isMember = membership?.is_member ?? balance?.is_member ?? false + const memberLabel = memberKey ? MEMBER_LABEL[memberKey] || "" : "" + const memberColor = memberKey ? MEMBER_COLOR[memberKey] || "#fa8c16" : "#fa8c16" return ( - - - +
navigate("/points")}> + + + {points} + + {isMember && memberLabel && ( + + + {memberLabel} + + + )} +
) }