From bef8a12064d4f2f5cca41df8a80e5e9b1b533363 Mon Sep 17 00:00:00 2001 From: shanshanzhong Date: Thu, 11 Jun 2026 21:25:40 -0700 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=8A=9F=E8=83=BD(#25):=20=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E4=B8=AD=E5=BF=83=E6=8B=86=E5=88=86=E6=8F=90=E7=8E=B0?= =?UTF-8?q?/=E9=80=80=E6=AC=BE=20tab=20=E5=B9=B6=E9=80=82=E9=85=8D?= =?UTF-8?q?=E6=96=B0=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../assets/locales/en-US/affiliate.json | 23 +- .../assets/locales/zh-CN/affiliate.json | 23 +- .../user/affiliate/commission-return.test.ts | 37 ++ .../user/affiliate/commission-return.ts | 27 ++ .../src/sections/user/affiliate/index.tsx | 357 ++++++++++++------ packages/ui/src/services/user/typings.d.ts | 26 ++ packages/ui/src/services/user/user.ts | 20 +- 7 files changed, 394 insertions(+), 119 deletions(-) create mode 100644 apps/user/src/sections/user/affiliate/commission-return.test.ts create mode 100644 apps/user/src/sections/user/affiliate/commission-return.ts diff --git a/apps/user/public/assets/locales/en-US/affiliate.json b/apps/user/public/assets/locales/en-US/affiliate.json index a17fcb8..d5f0a9c 100644 --- a/apps/user/public/assets/locales/en-US/affiliate.json +++ b/apps/user/public/assets/locales/en-US/affiliate.json @@ -16,6 +16,10 @@ "inviteCode": "Invite Code", "inviteRecords": "Invite Records", "noReason": "None", + "recordsTabs": { + "withdrawal": "Withdrawal Records", + "refund": "Refund Records" + }, "rejectReason": "Reject Reason", "registrationTime": "Registration Time", "submitWithdraw": "Submit Request", @@ -28,10 +32,27 @@ "withdrawDescription": "Submit the withdrawal amount and payout details, then wait for review.", "withdrawError": "Failed to submit withdrawal request", "withdrawRecords": "Withdrawal Records", + "withdrawalRecordsDescription": "Review your withdrawal requests, statuses, and payout details.", + "withdrawalEmpty": "No withdrawal records yet", "withdrawStatus": { "approved": "Approved", "pending": "Pending Review", "rejected": "Rejected" }, - "withdrawSuccess": "Withdrawal request submitted" + "withdrawSuccess": "Withdrawal request submitted", + "commissionReturn": { + "description": "Review order refunds and returned withdrawal commissions.", + "empty": "No refund records yet", + "eventType": { + "orderRefund": "Order Refund", + "withdrawRejectReturn": "Withdrawal Rejected Return", + "withdrawCancelReturn": "Withdrawal Cancel Return", + "unknown": "Commission Return" + }, + "fields": { + "eventType": "Type", + "orderNo": "Order No.", + "createdAt": "Created At" + } + } } diff --git a/apps/user/public/assets/locales/zh-CN/affiliate.json b/apps/user/public/assets/locales/zh-CN/affiliate.json index 0b331b0..387b4f9 100644 --- a/apps/user/public/assets/locales/zh-CN/affiliate.json +++ b/apps/user/public/assets/locales/zh-CN/affiliate.json @@ -16,6 +16,10 @@ "inviteCode": "邀请码", "inviteRecords": "邀请记录", "noReason": "无", + "recordsTabs": { + "withdrawal": "提现记录", + "refund": "退款记录" + }, "rejectReason": "拒绝原因", "registrationTime": "注册时间", "submitWithdraw": "提交申请", @@ -28,10 +32,27 @@ "withdrawDescription": "填写提现金额和收款信息后提交申请,等待后台审核。", "withdrawError": "提现申请提交失败", "withdrawRecords": "提现记录", + "withdrawalRecordsDescription": "查看你的提现申请、审核状态和收款信息。", + "withdrawalEmpty": "暂无提现记录", "withdrawStatus": { "approved": "已通过", "pending": "审核中", "rejected": "已拒绝" }, - "withdrawSuccess": "提现申请已提交" + "withdrawSuccess": "提现申请已提交", + "commissionReturn": { + "description": "查看订单退款和提现退回记录。", + "empty": "暂无退款记录", + "eventType": { + "orderRefund": "订单退款", + "withdrawRejectReturn": "提现拒绝退回", + "withdrawCancelReturn": "用户取消提现退回", + "unknown": "佣金返回" + }, + "fields": { + "eventType": "类型", + "orderNo": "订单号", + "createdAt": "创建时间" + } + } } diff --git a/apps/user/src/sections/user/affiliate/commission-return.test.ts b/apps/user/src/sections/user/affiliate/commission-return.test.ts new file mode 100644 index 0000000..90f7015 --- /dev/null +++ b/apps/user/src/sections/user/affiliate/commission-return.test.ts @@ -0,0 +1,37 @@ +import { describe, expect, it, vi } from "vitest"; +import { getCommissionReturnEventLabel } from "./commission-return"; + +const translations = { + "commissionReturn.eventType.orderRefund": "订单退款", + "commissionReturn.eventType.withdrawRejectReturn": "提现拒绝退回", + "commissionReturn.eventType.withdrawCancelReturn": "用户取消提现退回", + "commissionReturn.eventType.unknown": "佣金返回", +} as const; + +describe("getCommissionReturnEventLabel", () => { + const t = (key: string, defaultValue: string) => + translations[key as keyof typeof translations] ?? defaultValue ?? key; + + it("maps 333 to order refund", () => { + expect(getCommissionReturnEventLabel(333, t)).toBe("订单退款"); + }); + + it("maps 337 to withdraw reject return", () => { + expect(getCommissionReturnEventLabel(337, t)).toBe("提现拒绝退回"); + }); + + it("maps 338 to withdraw cancel return", () => { + expect(getCommissionReturnEventLabel(338, t)).toBe("用户取消提现退回"); + }); + + it("falls back for unknown event types and warns", () => { + const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => { + /* intentional noop */ + }); + + expect(getCommissionReturnEventLabel(999, t)).toBe("佣金返回"); + expect(warnSpy).toHaveBeenCalledWith(999); + + warnSpy.mockRestore(); + }); +}); diff --git a/apps/user/src/sections/user/affiliate/commission-return.ts b/apps/user/src/sections/user/affiliate/commission-return.ts new file mode 100644 index 0000000..4c77bd7 --- /dev/null +++ b/apps/user/src/sections/user/affiliate/commission-return.ts @@ -0,0 +1,27 @@ +export type AffiliateTranslation = ( + key: string, + defaultValue: string +) => string; + +export function getCommissionReturnEventLabel( + eventType: number, + t: AffiliateTranslation +) { + switch (eventType) { + case 333: + return t("commissionReturn.eventType.orderRefund", "Order Refund"); + case 337: + return t( + "commissionReturn.eventType.withdrawRejectReturn", + "Withdrawal Rejected Return" + ); + case 338: + return t( + "commissionReturn.eventType.withdrawCancelReturn", + "Withdrawal Cancel Return" + ); + default: + console.warn(eventType); + return t("commissionReturn.eventType.unknown", "Commission Return"); + } +} diff --git a/apps/user/src/sections/user/affiliate/index.tsx b/apps/user/src/sections/user/affiliate/index.tsx index 989770d..5e3e983 100644 --- a/apps/user/src/sections/user/affiliate/index.tsx +++ b/apps/user/src/sections/user/affiliate/index.tsx @@ -35,13 +35,21 @@ import { SelectTrigger, SelectValue, } from "@workspace/ui/components/select"; +import { + Tabs, + TabsContent, + TabsList, + TabsTrigger, +} from "@workspace/ui/components/tabs"; import { Textarea } from "@workspace/ui/components/textarea"; import { ConfirmButton } from "@workspace/ui/composed/confirm-button"; +import Empty from "@workspace/ui/composed/empty"; import type { ProListActions } from "@workspace/ui/composed/pro-list/pro-list"; import { ProList } from "@workspace/ui/composed/pro-list/pro-list"; import { UploadImage } from "@workspace/ui/composed/upload-image"; import { commissionWithdraw, + queryCommissionReturnLog, queryUserAffiliate, queryUserAffiliateList, queryWithdrawalLog, @@ -59,6 +67,7 @@ import { toast } from "sonner"; import { z } from "zod"; import { Display } from "@/components/display"; import { useGlobalStore } from "@/stores/global"; +import { getCommissionReturnEventLabel } from "./commission-return"; const WITHDRAWAL_METHODS = [ { value: 1, label: "支付宝" }, @@ -111,13 +120,21 @@ function QrCodeView({ url }: { url: string }) { 收款码 {zoomOpen && ( - 收款码 + 收款码 )} @@ -157,14 +174,12 @@ const withdrawalFormSchema = z path: ["account"], }); } - } else if (data.method === 0) { - if (!data.account && !data.content) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: "收款账号和备注至少填一项", - path: ["account"], - }); - } + } else if (data.method === 0 && !(data.account || data.content)) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "收款账号和备注至少填一项", + path: ["account"], + }); } }); @@ -173,6 +188,7 @@ type WithdrawalFormValues = z.infer; export default function Affiliate() { const { t } = useTranslation("affiliate"); const { user, common, getUserInfo } = useGlobalStore(); + const [activeRecordTab, setActiveRecordTab] = useState("withdrawal"); const [withdrawalOpen, setWithdrawalOpen] = useState(false); const [submitting, startTransition] = useTransition(); const [qrUploading, setQrUploading] = useState(false); @@ -421,9 +437,7 @@ export default function Affiliate() { 收款码 - - * - + * {watchedQrCodeUrl ? (
@@ -446,7 +460,9 @@ export default function Affiliate() { returnType="file" >
- {qrUploading ? "上传中..." : "点击上传\n收款码"} + {qrUploading + ? "上传中..." + : "点击上传\n收款码"}
)} @@ -519,9 +535,7 @@ export default function Affiliate() { - } - /> + + + {t("withdrawRecords", "Withdrawal Records")} + + {activeRecordTab === "withdrawal" + ? t( + "withdrawalRecordsDescription", + "Review your withdrawal requests, statuses, and payout details." + ) + : t( + "commissionReturn.description", + "Review order refunds and returned withdrawal commissions." + )} + + + + + {t("recordsTabs.withdrawal", "Withdrawal Records")} + + + {t("recordsTabs.refund", "Refund Records")} + + + + + > + action={withdrawalListActionRef} + empty={ + -
-
    -
  • - 收款方式 - {getWithdrawalMethodLabel(item.method)} -
  • - {item.account && ( -
  • - 收款账号 - {item.account} -
  • - )} - {item.qr_code_url && ( -
  • - 收款码 - -
  • - )} - {item.content && ( -
  • - - {t("withdrawal.record.content", "Payout Information")} - - - {item.content} - -
  • - )} -
  • - - {t("withdrawal.record.createdAt", "Created At")} - - -
  • - {item.reason && ( -
  • - - {t("withdrawal.record.reason", "Reject Reason")} - - - {item.reason} - -
  • - )} -
- - - ); - }} - request={async (pagination) => { - const response = await queryWithdrawalLog({ - page: pagination.page, - size: pagination.size, - }); - return { - list: response.data.data?.list || [], - total: response.data.data?.total || 0, - }; - }} - /> + /> + } + renderItem={(item) => { + const statusMeta = getWithdrawalStatusMeta(item.status, t); + return ( + + +
+
+ +
+
+ + {statusMeta.label} + + {item.status === 0 && ( + { + await cancelMutation.mutateAsync(item.id); + }} + title="取消提现申请" + trigger={ + + } + /> + )} +
+
+
    +
  • + + 收款方式 + + {getWithdrawalMethodLabel(item.method)} +
  • + {item.account && ( +
  • + + 收款账号 + + {item.account} +
  • + )} + {item.qr_code_url && ( +
  • + + 收款码 + + +
  • + )} + {item.content && ( +
  • + + {t( + "withdrawal.record.content", + "Payout Information" + )} + + + {item.content} + +
  • + )} +
  • + + {t("withdrawal.record.createdAt", "Created At")} + + +
  • + {item.reason && ( +
  • + + {t("withdrawal.record.reason", "Reject Reason")} + + + {item.reason} + +
  • + )} +
+
+
+ ); + }} + request={async (pagination) => { + const response = await queryWithdrawalLog({ + page: pagination.page, + size: pagination.size, + }); + return { + list: response.data.data?.list || [], + total: response.data.data?.total || 0, + }; + }} + /> + + + + > + empty={ + + } + renderItem={(item) => ( + + +
+
+ +
+ + {getCommissionReturnEventLabel(item.event_type, t)} + +
+
    +
  • + + {t("commissionReturn.fields.eventType", "Type")} + + + {getCommissionReturnEventLabel(item.event_type, t)} + +
  • +
  • + + {t("commissionReturn.fields.orderNo", "Order No.")} + + + {item.order_no || "-"} + +
  • +
  • + + {t( + "commissionReturn.fields.createdAt", + "Created At" + )} + + +
  • +
+
+
+ )} + request={async (pagination) => { + const response = await queryCommissionReturnLog({ + page: pagination.page, + size: pagination.size, + }); + return { + list: response.data.data?.list || [], + total: response.data.data?.total || 0, + }; + }} + /> +
+ + + > header={{ title: t("inviteRecords", "Invite Records"), diff --git a/packages/ui/src/services/user/typings.d.ts b/packages/ui/src/services/user/typings.d.ts index 9a5e1be..b6c5b7b 100644 --- a/packages/ui/src/services/user/typings.d.ts +++ b/packages/ui/src/services/user/typings.d.ts @@ -170,6 +170,17 @@ declare namespace API { timestamp: number; }; + type CommissionReturnLog = { + id: number; + user_id: number; + amount: number; + event_type: number; + order_no?: string; + content?: string; + created_at: number; + updated_at: number; + }; + type CommissionWithdrawRequest = { amount: number; method: number; @@ -821,6 +832,21 @@ declare namespace API { size: number; }; + type QueryCommissionReturnLogListRequest = { + page: number; + size: number; + }; + + type QueryCommissionReturnLogListResponse = { + list: CommissionReturnLog[]; + total: number; + }; + + type QueryCommissionReturnLogParams = { + page: number; + size: number; + }; + type QueryUserSubscribeListResponse = { list: UserSubscribe[]; total: number; diff --git a/packages/ui/src/services/user/user.ts b/packages/ui/src/services/user/user.ts index 16a6950..e77e075 100644 --- a/packages/ui/src/services/user/user.ts +++ b/packages/ui/src/services/user/user.ts @@ -1,4 +1,3 @@ -// @ts-expect-error /* eslint-disable */ import request from "@workspace/ui/lib/request"; @@ -471,6 +470,25 @@ export async function queryWithdrawalLog( ); } +/** Query Commission Return Log GET /v1/public/user/commission_return_log */ +export async function queryCommissionReturnLog( + params: API.QueryCommissionReturnLogParams, + options?: { [key: string]: any } +) { + return request< + API.Response & { data?: API.QueryCommissionReturnLogListResponse } + >( + `${import.meta.env.VITE_API_PREFIX || ""}/v1/public/user/commission_return_log`, + { + method: "GET", + params: { + ...params, + }, + ...(options || {}), + } + ); +} + /** Cancel Withdrawal POST /v1/public/user/withdrawal_cancel */ export async function withdrawalCancel( body: API.WithdrawalCancelRequest,