feat(withdraw-detail): 抽屉占屏一半 + 金币记录页码分页 + 风险提示去分数

- 详情抽屉宽度 720→50%
- 金币记录改 antd 页码分页(10/页,显示「共N条」)
- 风险提示去掉不可解释的分数;历史异常拆成「提现拒绝/提现失败」两类

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
zzhyyyyy
2026-06-21 23:10:16 +08:00
parent 12cc6f7aa6
commit 6777ff945e
2 changed files with 28 additions and 25 deletions
+22 -20
View File
@@ -2,7 +2,6 @@
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { import {
Button,
DatePicker, DatePicker,
Descriptions, Descriptions,
Radio, Radio,
@@ -35,6 +34,8 @@ const SOURCE_COLOR: Record<string, string> = {
signin: 'green', signin: 'green',
}; };
const PAGE_SIZE = 10; // 金币记录每页条数
interface Props { interface Props {
userId: number; userId: number;
user: WithdrawUserSnapshot | null; user: WithdrawUserSnapshot | null;
@@ -48,7 +49,8 @@ export default function UserRewardPanel({ userId, user }: Props) {
const [statsLoading, setStatsLoading] = useState(false); const [statsLoading, setStatsLoading] = useState(false);
const [records, setRecords] = useState<UserCoinRecord[]>([]); const [records, setRecords] = useState<UserCoinRecord[]>([]);
const [nextCursor, setNextCursor] = useState<number | null>(null); const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const [recordsLoading, setRecordsLoading] = useState(false); const [recordsLoading, setRecordsLoading] = useState(false);
// 时间窗口参数:注册至今=不传;自定义区间(选齐两端)=date_from/date_to。 // 时间窗口参数:注册至今=不传;自定义区间(选齐两端)=date_from/date_to。
@@ -76,17 +78,15 @@ export default function UserRewardPanel({ userId, user }: Props) {
}, [userId, paramsKey]); }, [userId, paramsKey]);
const loadRecords = useCallback( const loadRecords = useCallback(
async (cursor: number | null) => { async (p: number) => {
setRecordsLoading(true); setRecordsLoading(true);
try { try {
const reqParams: Record<string, unknown> = { ...JSON.parse(paramsKey), limit: 20 };
if (cursor != null) reqParams.cursor = cursor;
const { data } = await api.get<CursorPage<UserCoinRecord>>( const { data } = await api.get<CursorPage<UserCoinRecord>>(
`/admin/api/users/${userId}/coin-records`, `/admin/api/users/${userId}/coin-records`,
{ params: reqParams }, { params: { ...JSON.parse(paramsKey), limit: PAGE_SIZE, cursor: (p - 1) * PAGE_SIZE } },
); );
setRecords((prev) => (cursor == null ? data.items : [...prev, ...data.items])); setRecords(data.items);
setNextCursor(data.next_cursor); setTotal(data.total ?? 0);
} catch (e) { } catch (e) {
message.error(errMsg(e)); message.error(errMsg(e));
} finally { } finally {
@@ -96,9 +96,11 @@ export default function UserRewardPanel({ userId, user }: Props) {
[userId, paramsKey], [userId, paramsKey],
); );
// 用户或时间筛选变化:回到第 1 页重新拉取
useEffect(() => { useEffect(() => {
loadStats(); loadStats();
loadRecords(null); setPage(1);
loadRecords(1);
}, [loadStats, loadRecords]); }, [loadStats, loadRecords]);
const regDays = user?.created_at ? dayjs().diff(apiTime(user.created_at), 'day') : null; const regDays = user?.created_at ? dayjs().diff(apiTime(user.created_at), 'day') : null;
@@ -203,18 +205,18 @@ export default function UserRewardPanel({ userId, user }: Props) {
columns={columns} columns={columns}
dataSource={records} dataSource={records}
loading={recordsLoading || statsLoading} loading={recordsLoading || statsLoading}
pagination={false} pagination={{
current: page,
pageSize: PAGE_SIZE,
total,
showSizeChanger: false,
onChange: (p) => {
setPage(p);
loadRecords(p);
},
showTotal: (t) => `${t}`,
}}
/> />
<div style={{ marginTop: 12, textAlign: 'center' }}>
<Button
size="small"
onClick={() => loadRecords(nextCursor)}
disabled={!nextCursor}
loading={recordsLoading}
>
{nextCursor ? '加载更多' : '没有更多了'}
</Button>
</div>
</div> </div>
); );
} }
+6 -5
View File
@@ -149,9 +149,10 @@ function riskTags(detail: WithdrawDetail | null) {
if (detail.user?.status && detail.user.status !== 'active') tags.push(`账号状态:${detail.user.status}`); if (detail.user?.status && detail.user.status !== 'active') tags.push(`账号状态:${detail.user.status}`);
if (detail.user && dayjs().diff(utcDayjs(detail.user.created_at), 'hour') < 24) tags.push('新注册用户'); if (detail.user && dayjs().diff(utcDayjs(detail.user.created_at), 'hour') < 24) tags.push('新注册用户');
if ((detail.user?.withdraw_total || 0) >= 3) tags.push('多次提现用户'); if ((detail.user?.withdraw_total || 0) >= 3) tags.push('多次提现用户');
if (detail.recent_withdraws.some((o) => o.status === 'rejected' || o.status === 'failed')) { const rejectedN = detail.recent_withdraws.filter((o) => o.status === 'rejected').length;
tags.push('历史异常单'); const failedN = detail.recent_withdraws.filter((o) => o.status === 'failed').length;
} if (rejectedN) tags.push(`提现拒绝${rejectedN}`);
if (failedN) tags.push(`提现失败${failedN}`);
return tags; return tags;
} }
@@ -858,7 +859,7 @@ export default function WithdrawsPage() {
<Drawer <Drawer
title="提现详情" title="提现详情"
width={720} width="50%"
open={drawerOpen} open={drawerOpen}
onClose={() => setDrawerOpen(false)} onClose={() => setDrawerOpen(false)}
extra={ extra={
@@ -900,7 +901,7 @@ export default function WithdrawsPage() {
<UserRewardPanel userId={detail.order.user_id} user={detail.user} /> <UserRewardPanel userId={detail.order.user_id} user={detail.user} />
<div> <div>
<h3> {detail.risk_score ? <Tag color="red">{detail.risk_score}</Tag> : null}</h3> <h3></h3>
{risks.length ? ( {risks.length ? (
<Space wrap> <Space wrap>
{risks.map((risk) => ( {risks.map((risk) => (