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:
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) => (
|
||||||
|
|||||||
Reference in New Issue
Block a user