优化:移除提现类型字段 (#73)
## 修改内容 - 删除提现审核 6 个状态子页面共用的提现类型筛选项 - 删除列表和详情抽屉中的提现类型字段 - 筛选栏仅保留:用户手机号、开始时间-结束时间、倒序、重置 - 日期与排序字段固定为申请时间,并同步清理无用前端状态和请求参数 - 同步收窄表格横向滚动宽度 ## 后端评估 - 无需后端改动 - source 仍是提现账本分流、扣款、退款与对账所需的业务字段,保留 API 兼容性 ## 本地验证 - npx tsc --noEmit - npm run build - git diff --check --------- Co-authored-by: guke <guke@wonderable.ai> Co-authored-by: linkeyu <798648091@qq.com> Reviewed-on: #73 Co-authored-by: linkeyu <linkeyu@wonderable.ai> Co-committed-by: linkeyu <linkeyu@wonderable.ai>
This commit was merged in pull request #73.
This commit is contained in:
@@ -29,7 +29,6 @@ import {
|
||||
CheckCircleOutlined,
|
||||
ClearOutlined,
|
||||
CloseCircleOutlined,
|
||||
FilterOutlined,
|
||||
ReloadOutlined,
|
||||
SearchOutlined,
|
||||
SortAscendingOutlined,
|
||||
@@ -86,42 +85,6 @@ const STATUS_TABS = [
|
||||
];
|
||||
|
||||
const REJECT_TEMPLATES = ['账号异常', '提现金额异常', '微信实名不匹配', '风控拦截', '其他原因'];
|
||||
const QUICK_FILTER_OPTIONS = [
|
||||
{ value: 'abnormal', label: '异常单' },
|
||||
{ value: 'failed', label: '打款失败' },
|
||||
{ value: 'overdue_reviewing', label: '待审核超30分钟' },
|
||||
{ value: 'pending_overdue', label: '打款中超15分钟' },
|
||||
{ value: 'today', label: '今日申请' },
|
||||
{ value: 'high_risk', label: '高风险用户' },
|
||||
];
|
||||
const QUICK_FILTER_STATUS: Record<string, string> = {
|
||||
abnormal: 'all',
|
||||
failed: 'failed',
|
||||
overdue_reviewing: 'reviewing',
|
||||
pending_overdue: 'pending',
|
||||
high_risk: 'all',
|
||||
};
|
||||
const SORT_OPTIONS = [
|
||||
{ value: 'created_at', label: '申请时间' },
|
||||
{ value: 'amount_cents', label: '提现金额' },
|
||||
];
|
||||
// 提现类型:coin_cash=福利页提现(金币兑换现金) / invite_cash=邀请提现(邀请奖励金)
|
||||
const SOURCE_LABEL: Record<string, string> = {
|
||||
coin_cash: '福利页提现',
|
||||
invite_cash: '邀请提现',
|
||||
};
|
||||
const SOURCE_COLOR: Record<string, string> = {
|
||||
coin_cash: 'blue',
|
||||
invite_cash: 'purple',
|
||||
};
|
||||
const SOURCE_OPTIONS = [
|
||||
{ value: 'coin_cash', label: '福利页提现' },
|
||||
{ value: 'invite_cash', label: '邀请提现' },
|
||||
];
|
||||
const DATE_FIELD_OPTIONS = [
|
||||
{ value: 'created_at', label: '申请时间' },
|
||||
{ value: 'updated_at', label: '更新时间' },
|
||||
];
|
||||
|
||||
function statusTag(status: string) {
|
||||
return <Tag color={STATUS_COLOR[status]}>{STATUS_LABEL[status] || status}</Tag>;
|
||||
@@ -197,21 +160,15 @@ export default function WithdrawsPage() {
|
||||
const [searchDraft, setSearchDraft] = useState('');
|
||||
const [keyword, setKeyword] = useState('');
|
||||
const [dateRange, setDateRange] = useState<[Dayjs, Dayjs] | null>(null);
|
||||
const [dateField, setDateField] = useState<'created_at' | 'updated_at'>('created_at');
|
||||
const [sortBy, setSortBy] = useState<'created_at' | 'amount_cents'>('created_at');
|
||||
const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc');
|
||||
const [quickFilter, setQuickFilter] = useState<string | undefined>(undefined);
|
||||
const [source, setSource] = useState<string | undefined>(undefined);
|
||||
|
||||
const filters: Record<string, unknown> = {};
|
||||
if (activeStatus !== 'all') filters.status = activeStatus;
|
||||
if (source) filters.source = source;
|
||||
if (keyword.trim()) filters.keyword = keyword.trim();
|
||||
if (dateRange?.[0]) filters.date_from = dateRange[0].startOf('day').toISOString();
|
||||
if (dateRange?.[1]) filters.date_to = dateRange[1].endOf('day').toISOString();
|
||||
if (quickFilter) filters.quick_filter = quickFilter;
|
||||
filters.date_field = dateField;
|
||||
filters.sort_by = sortBy;
|
||||
filters.date_field = 'created_at';
|
||||
filters.sort_by = 'created_at';
|
||||
filters.sort_order = sortOrder;
|
||||
const filterKey = JSON.stringify(filters);
|
||||
const { items, total, page, pageSize, loading, onChange: onPageChange, reload } =
|
||||
@@ -226,27 +183,7 @@ export default function WithdrawsPage() {
|
||||
setSearchDraft('');
|
||||
setKeyword('');
|
||||
setDateRange(null);
|
||||
setDateField('created_at');
|
||||
setSortBy('created_at');
|
||||
setSortOrder('desc');
|
||||
setQuickFilter(undefined);
|
||||
setSource(undefined);
|
||||
};
|
||||
|
||||
const applyQuickFilter = (value?: string) => {
|
||||
setQuickFilter(value);
|
||||
const targetStatus = value ? QUICK_FILTER_STATUS[value] : undefined;
|
||||
if (targetStatus) setActiveStatus(targetStatus);
|
||||
};
|
||||
|
||||
const changeStatus = (status: string) => {
|
||||
setActiveStatus(status);
|
||||
setQuickFilter((current) => {
|
||||
if (!current) return current;
|
||||
const targetStatus = QUICK_FILTER_STATUS[current];
|
||||
if (!targetStatus || status === targetStatus) return current;
|
||||
return undefined;
|
||||
});
|
||||
};
|
||||
|
||||
const loadSummary = useCallback(async () => {
|
||||
@@ -520,12 +457,6 @@ export default function WithdrawsPage() {
|
||||
width: 120,
|
||||
render: (v: number) => <Text strong>{yuan(v)}</Text>,
|
||||
},
|
||||
{
|
||||
title: '提现类型',
|
||||
dataIndex: 'source',
|
||||
width: 110,
|
||||
render: (v: string) => <Tag color={SOURCE_COLOR[v] || 'default'}>{SOURCE_LABEL[v] || v}</Tag>,
|
||||
},
|
||||
{
|
||||
title: '累计提现',
|
||||
dataIndex: 'cumulative_success_cents',
|
||||
@@ -798,7 +729,7 @@ export default function WithdrawsPage() {
|
||||
<Card>
|
||||
<Tabs
|
||||
activeKey={activeStatus}
|
||||
onChange={changeStatus}
|
||||
onChange={setActiveStatus}
|
||||
items={STATUS_TABS.map((tab) => {
|
||||
const count = summaryCount(summary, tab.key);
|
||||
return {
|
||||
@@ -820,89 +751,55 @@ export default function WithdrawsPage() {
|
||||
align="start"
|
||||
style={{
|
||||
width: '100%',
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: 12,
|
||||
paddingBottom: 12,
|
||||
borderBottom: '1px solid #f0f0f0',
|
||||
}}
|
||||
>
|
||||
<Space wrap>
|
||||
<Input.Search
|
||||
allowClear
|
||||
enterButton="搜索"
|
||||
prefix={<SearchOutlined />}
|
||||
placeholder="用户手机号"
|
||||
value={searchDraft}
|
||||
style={{ width: 240 }}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
setSearchDraft(value);
|
||||
if (!value) setKeyword('');
|
||||
}}
|
||||
onSearch={(value) => setKeyword(value.trim())}
|
||||
/>
|
||||
<Select
|
||||
value={source}
|
||||
allowClear
|
||||
placeholder="提现类型"
|
||||
style={{ width: 140 }}
|
||||
options={SOURCE_OPTIONS}
|
||||
onChange={(value?: string) => setSource(value)}
|
||||
/>
|
||||
<Select
|
||||
value={quickFilter}
|
||||
allowClear
|
||||
placeholder="快捷筛选"
|
||||
style={{ width: 170 }}
|
||||
suffixIcon={<FilterOutlined />}
|
||||
options={QUICK_FILTER_OPTIONS}
|
||||
onChange={applyQuickFilter}
|
||||
/>
|
||||
<Select
|
||||
value={dateField}
|
||||
style={{ width: 120 }}
|
||||
options={DATE_FIELD_OPTIONS}
|
||||
onChange={(value: 'created_at' | 'updated_at') => setDateField(value)}
|
||||
/>
|
||||
<RangePicker
|
||||
value={dateRange}
|
||||
allowClear
|
||||
style={{ width: 260 }}
|
||||
presets={[
|
||||
{ label: '今天', value: [dayjs().startOf('day'), dayjs().endOf('day')] },
|
||||
{
|
||||
label: '最近7天',
|
||||
value: [dayjs().subtract(6, 'day').startOf('day'), dayjs().endOf('day')],
|
||||
},
|
||||
{
|
||||
label: '最近30天',
|
||||
value: [dayjs().subtract(29, 'day').startOf('day'), dayjs().endOf('day')],
|
||||
},
|
||||
]}
|
||||
onChange={(values) => setDateRange(values as [Dayjs, Dayjs] | null)}
|
||||
/>
|
||||
</Space>
|
||||
<Space wrap>
|
||||
<Select
|
||||
value={sortBy}
|
||||
style={{ width: 130 }}
|
||||
suffixIcon={<SortAscendingOutlined />}
|
||||
options={SORT_OPTIONS}
|
||||
onChange={(value: 'created_at' | 'amount_cents') => setSortBy(value)}
|
||||
/>
|
||||
<Select
|
||||
value={sortOrder}
|
||||
style={{ width: 96 }}
|
||||
options={[
|
||||
{ value: 'desc', label: '倒序' },
|
||||
{ value: 'asc', label: '正序' },
|
||||
]}
|
||||
onChange={(value: 'asc' | 'desc') => setSortOrder(value)}
|
||||
/>
|
||||
<Button icon={<ClearOutlined />} onClick={resetFilters}>
|
||||
重置
|
||||
</Button>
|
||||
</Space>
|
||||
<Input.Search
|
||||
allowClear
|
||||
enterButton="搜索"
|
||||
prefix={<SearchOutlined />}
|
||||
placeholder="用户手机号"
|
||||
value={searchDraft}
|
||||
style={{ width: 240 }}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
setSearchDraft(value);
|
||||
if (!value) setKeyword('');
|
||||
}}
|
||||
onSearch={(value) => setKeyword(value.trim())}
|
||||
/>
|
||||
<RangePicker
|
||||
value={dateRange}
|
||||
allowClear
|
||||
style={{ width: 260 }}
|
||||
presets={[
|
||||
{ label: '今天', value: [dayjs().startOf('day'), dayjs().endOf('day')] },
|
||||
{
|
||||
label: '最近7天',
|
||||
value: [dayjs().subtract(6, 'day').startOf('day'), dayjs().endOf('day')],
|
||||
},
|
||||
{
|
||||
label: '最近30天',
|
||||
value: [dayjs().subtract(29, 'day').startOf('day'), dayjs().endOf('day')],
|
||||
},
|
||||
]}
|
||||
onChange={(values) => setDateRange(values as [Dayjs, Dayjs] | null)}
|
||||
/>
|
||||
<Select
|
||||
value={sortOrder}
|
||||
style={{ width: 96 }}
|
||||
suffixIcon={<SortAscendingOutlined />}
|
||||
options={[
|
||||
{ value: 'desc', label: '倒序' },
|
||||
{ value: 'asc', label: '正序' },
|
||||
]}
|
||||
onChange={(value: 'asc' | 'desc') => setSortOrder(value)}
|
||||
/>
|
||||
<Button icon={<ClearOutlined />} onClick={resetFilters}>
|
||||
重置
|
||||
</Button>
|
||||
</Space>
|
||||
{canManage && selectedOrders.length > 0 && (
|
||||
<Space
|
||||
@@ -974,7 +871,7 @@ export default function WithdrawsPage() {
|
||||
showTotal: (t) => `共 ${t} 条`,
|
||||
onChange: onPageChange,
|
||||
}}
|
||||
scroll={{ x: 1460 }}
|
||||
scroll={{ x: 1350 }}
|
||||
onRow={(record) => ({
|
||||
onClick: () => openDetail(record),
|
||||
style: { cursor: 'pointer' },
|
||||
@@ -1006,11 +903,6 @@ export default function WithdrawsPage() {
|
||||
<Descriptions.Item label="金额">
|
||||
<Text strong>{yuan(detail.order.amount_cents)}</Text>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="提现类型">
|
||||
<Tag color={SOURCE_COLOR[detail.order.source] || 'default'}>
|
||||
{SOURCE_LABEL[detail.order.source] || detail.order.source}
|
||||
</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="提现实名">
|
||||
{detail.order.user_name || '-'}
|
||||
</Descriptions.Item>
|
||||
|
||||
Reference in New Issue
Block a user