From e12d935cde63d0a65f4497526aaaf671c6bb7d23 Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Mon, 18 May 2026 20:29:33 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=9A=E5=91=98=E8=AE=A2=E9=98=85=E5=8C=BA?= =?UTF-8?q?=E5=88=86=E4=BD=93=E9=AA=8C=E7=94=A8=E6=88=B7=E5=92=8C=E4=BB=98?= =?UTF-8?q?=E8=B4=B9=E7=94=A8=E6=88=B7=EF=BC=9A=E7=B1=BB=E5=9E=8B=E6=A0=87?= =?UTF-8?q?=E7=AD=BE=E3=80=81=E7=AD=9B=E9=80=89=E5=99=A8=E3=80=81=E6=9D=A5?= =?UTF-8?q?=E6=BA=90=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ops/memberships/MembershipsPageClient.tsx | 61 ++++++++++++++++--- frontend/types/ops.ts | 2 + web/services/ops_api.py | 4 ++ 3 files changed, 57 insertions(+), 10 deletions(-) diff --git a/frontend/components/ops/memberships/MembershipsPageClient.tsx b/frontend/components/ops/memberships/MembershipsPageClient.tsx index 7e971ed1..e6c081ca 100644 --- a/frontend/components/ops/memberships/MembershipsPageClient.tsx +++ b/frontend/components/ops/memberships/MembershipsPageClient.tsx @@ -10,6 +10,7 @@ import type { MembershipEntry } from "@/types/ops"; export function MembershipsPageClient() { const [loading, setLoading] = useState(true); const [memberships, setMemberships] = useState([]); + const [filter, setFilter] = useState<"all" | "paid" | "trial">("all"); const load = async () => { setLoading(true); @@ -22,15 +23,45 @@ export function MembershipsPageClient() { useEffect(() => { void load(); }, []); + const paid = memberships.filter((m) => !m.is_trial); + const trials = memberships.filter((m) => m.is_trial); + const filtered = filter === "paid" ? paid : filter === "trial" ? trials : memberships; + + const planLabel = (code?: string) => { + if (!code) return "—"; + if (code.startsWith("signup_trial")) return "3天体验"; + if (code === "pro_monthly") return "月付"; + if (code === "pro_quarterly") return "季付"; + if (code === "pro_yearly") return "年付"; + return code; + }; + if (loading) return
加载中...
; return (
-
-

会员订阅 ({memberships.length})

- +
+

+ 会员订阅 ({memberships.length}) + + 付费 {paid.length} · 体验 {trials.length} + +

+
+ {(["all", "paid", "trial"] as const).map((f) => ( + + ))} + +
@@ -38,8 +69,8 @@ export function MembershipsPageClient() { + - @@ -47,17 +78,27 @@ export function MembershipsPageClient() { - {memberships.map((m, i) => ( + {filtered.map((m, i) => ( + - - + ))} - {memberships.length === 0 && ( + {filtered.length === 0 && ( )} diff --git a/frontend/types/ops.ts b/frontend/types/ops.ts index 832371f6..6f1e7539 100644 --- a/frontend/types/ops.ts +++ b/frontend/types/ops.ts @@ -109,6 +109,8 @@ export type MembershipEntry = { queued_days?: number; queued_count?: number; plan_code?: string; + source?: string; + is_trial?: boolean; }; export type MembershipsPayload = { diff --git a/web/services/ops_api.py b/web/services/ops_api.py index f5efb511..3ddfa7a1 100644 --- a/web/services/ops_api.py +++ b/web/services/ops_api.py @@ -76,6 +76,8 @@ def list_ops_memberships(request: Request, limit: int = 200) -> Dict[str, Any]: if isinstance(subscription_window, dict) else 0 ) + source = str(item.get("source") or "") + is_trial = source == "signup_trial" or str(item.get("plan_code") or "").startswith("signup_trial") row = { "user_id": user_id, "email": str(auth_user.get("email") or local_user.get("supabase_email") or ""), @@ -83,6 +85,8 @@ def list_ops_memberships(request: Request, limit: int = 200) -> Dict[str, Any]: "username": local_user.get("username"), "registered_at": local_user.get("created_at") or auth_user.get("created_at"), "plan_code": item.get("plan_code"), + "source": source, + "is_trial": is_trial, "starts_at": item.get("starts_at"), "current_expires_at": current_expires_at, "total_expires_at": total_expires_at or current_expires_at,
类型 邮箱用户ID 方案 起始 到期
+ {m.is_trial ? ( + + 体验 + + ) : ( + + 付费 + + )} + {m.email ?? "—"}{m.user_id?.slice(0, 12) ?? "—"}{m.plan_code ?? "—"}{planLabel(m.plan_code)} {m.starts_at?.slice(0, 10) ?? "—"} {m.expires_at?.slice(0, 10) ?? "—"} {m.queued_days ?? 0}
暂无会员