支付统计增加手机号
This commit is contained in:
Vendored
+3
-3
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -28,7 +28,7 @@
|
|||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
<script type="module" crossorigin src="/assets/index-B5S0hm2T.js"></script>
|
<script type="module" crossorigin src="/assets/index-BzY_VVvl.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-D7ShJUt4.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-D7ShJUt4.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -387,10 +387,11 @@ export async function getPaymentStats(params?: {
|
|||||||
return api.get(url);
|
return api.get(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAdminPaymentOrders(params?: { method?: string; status?: string; startDate?: string; endDate?: string; page?: number; pageSize?: number }): Promise<{ items: any[]; total: number }> {
|
export async function getAdminPaymentOrders(params?: { method?: string; status?: string; phone?: string; startDate?: string; endDate?: string; page?: number; pageSize?: number }): Promise<{ items: any[]; total: number }> {
|
||||||
const qs = new URLSearchParams();
|
const qs = new URLSearchParams();
|
||||||
if (params?.method) qs.set('payment_method', params.method);
|
if (params?.method) qs.set('payment_method', params.method);
|
||||||
if (params?.status) qs.set('status', params.status);
|
if (params?.status) qs.set('status', params.status);
|
||||||
|
if (params?.phone) qs.set('phone', params.phone);
|
||||||
if (params?.startDate) qs.set('start_date', params.startDate);
|
if (params?.startDate) qs.set('start_date', params.startDate);
|
||||||
if (params?.endDate) qs.set('end_date', params.endDate);
|
if (params?.endDate) qs.set('end_date', params.endDate);
|
||||||
if (params?.page) qs.set('page', String(params.page));
|
if (params?.page) qs.set('page', String(params.page));
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Card, Col, Row, Space, Table, Tag, Typography, Statistic, message, Select, DatePicker, Button, ConfigProvider, Popconfirm
|
Card, Col, Input, Row, Space, Table, Tag, Typography, Statistic, message, Select, DatePicker, Button, ConfigProvider, Popconfirm
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import zhCN from 'antd/locale/zh_CN';
|
import zhCN from 'antd/locale/zh_CN';
|
||||||
import {
|
import {
|
||||||
DollarOutlined, CheckCircleOutlined, ClockCircleOutlined, CloseCircleOutlined, ReloadOutlined, UndoOutlined
|
DollarOutlined, CheckCircleOutlined, ClockCircleOutlined, CloseCircleOutlined, ReloadOutlined, UndoOutlined, SearchOutlined
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { getPaymentStats, getAdminPaymentOrders, refundPaymentOrder } from '../api';
|
import { getPaymentStats, getAdminPaymentOrders, refundPaymentOrder } from '../api';
|
||||||
import { formatDate } from '../utils/formatDate';
|
import { formatDate } from '../utils/formatDate';
|
||||||
@@ -22,6 +22,7 @@ const AdminPaymentStats: React.FC = () => {
|
|||||||
const [filters, setFilters] = useState<{
|
const [filters, setFilters] = useState<{
|
||||||
paymentMethod?: string;
|
paymentMethod?: string;
|
||||||
status?: string;
|
status?: string;
|
||||||
|
phone?: string;
|
||||||
startDate: string;
|
startDate: string;
|
||||||
endDate: string;
|
endDate: string;
|
||||||
}>({
|
}>({
|
||||||
@@ -36,6 +37,7 @@ const AdminPaymentStats: React.FC = () => {
|
|||||||
getPaymentStats(filters),
|
getPaymentStats(filters),
|
||||||
getAdminPaymentOrders({
|
getAdminPaymentOrders({
|
||||||
...filters,
|
...filters,
|
||||||
|
phone: filters.phone,
|
||||||
page: orderPage,
|
page: orderPage,
|
||||||
pageSize: orderPageSize,
|
pageSize: orderPageSize,
|
||||||
}),
|
}),
|
||||||
@@ -61,6 +63,7 @@ const AdminPaymentStats: React.FC = () => {
|
|||||||
startDate: dayjs().format('YYYY-MM-DD'),
|
startDate: dayjs().format('YYYY-MM-DD'),
|
||||||
endDate: dayjs().format('YYYY-MM-DD'),
|
endDate: dayjs().format('YYYY-MM-DD'),
|
||||||
});
|
});
|
||||||
|
setOrderPage(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRefund = async (orderNo: string) => {
|
const handleRefund = async (orderNo: string) => {
|
||||||
@@ -101,6 +104,7 @@ const AdminPaymentStats: React.FC = () => {
|
|||||||
const columns = [
|
const columns = [
|
||||||
{ title: '订单号', dataIndex: 'orderNo', key: 'orderNo', width: 200 },
|
{ title: '订单号', dataIndex: 'orderNo', key: 'orderNo', width: 200 },
|
||||||
{ title: '用户', dataIndex: 'username', key: 'username', width: 120 },
|
{ title: '用户', dataIndex: 'username', key: 'username', width: 120 },
|
||||||
|
{ title: '手机号', dataIndex: 'phone', key: 'phone', width: 120, render: (v: string) => v || '-' },
|
||||||
{
|
{
|
||||||
title: '支付方式', dataIndex: 'paymentMethod', key: 'paymentMethod', width: 100,
|
title: '支付方式', dataIndex: 'paymentMethod', key: 'paymentMethod', width: 100,
|
||||||
render: (m: string) => {
|
render: (m: string) => {
|
||||||
@@ -266,7 +270,19 @@ const AdminPaymentStats: React.FC = () => {
|
|||||||
<Option value="refunded">已退款</Option>
|
<Option value="refunded">已退款</Option>
|
||||||
</Select>
|
</Select>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={24} sm={12} md={8}>
|
<Col xs={24} sm={12} md={5}>
|
||||||
|
<span style={{ marginRight: 8 }}>手机号:</span>
|
||||||
|
<Input
|
||||||
|
placeholder="搜索手机号"
|
||||||
|
allowClear
|
||||||
|
value={filters.phone}
|
||||||
|
onChange={(e) => setFilters(prev => ({ ...prev, phone: e.target.value }))}
|
||||||
|
onPressEnter={() => { setOrderPage(1); load(); }}
|
||||||
|
suffix={<SearchOutlined style={{ color: '#94a3b8' }} />}
|
||||||
|
style={{ width: 180 }}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col xs={24} sm={12} md={6}>
|
||||||
<span style={{ marginRight: 8 }}>日期范围:</span>
|
<span style={{ marginRight: 8 }}>日期范围:</span>
|
||||||
<RangePicker
|
<RangePicker
|
||||||
value={[
|
value={[
|
||||||
@@ -276,7 +292,7 @@ const AdminPaymentStats: React.FC = () => {
|
|||||||
onChange={handleDateChange}
|
onChange={handleDateChange}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={24} sm={12} md={4}>
|
<Col xs={24} sm={12} md={3}>
|
||||||
<Button icon={<ReloadOutlined />} onClick={handleReset}>
|
<Button icon={<ReloadOutlined />} onClick={handleReset}>
|
||||||
重置
|
重置
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -836,13 +836,14 @@ async def list_payment_orders(
|
|||||||
page_size: int = Query(20, ge=1, le=500),
|
page_size: int = Query(20, ge=1, le=500),
|
||||||
payment_method: str | None = Query(None),
|
payment_method: str | None = Query(None),
|
||||||
status: str | None = Query(None),
|
status: str | None = Query(None),
|
||||||
|
phone: str | None = Query(None, description="按用户手机号模糊搜索"),
|
||||||
start_date: str | None = Query(None),
|
start_date: str | None = Query(None),
|
||||||
end_date: str | None = Query(None),
|
end_date: str | None = Query(None),
|
||||||
admin: User = Depends(get_admin_user),
|
admin: User = Depends(get_admin_user),
|
||||||
db: AsyncSession = Depends(get_db),
|
db: AsyncSession = Depends(get_db),
|
||||||
):
|
):
|
||||||
"""Return paginated payment orders for admin dashboard."""
|
"""Return paginated payment orders for admin dashboard."""
|
||||||
query = select(PaymentOrder, User.username).join(User, PaymentOrder.user_id == User.id)
|
query = select(PaymentOrder, User.username, User.phone).join(User, PaymentOrder.user_id == User.id)
|
||||||
count_query = select(func.count(PaymentOrder.id))
|
count_query = select(func.count(PaymentOrder.id))
|
||||||
|
|
||||||
filters = []
|
filters = []
|
||||||
@@ -850,6 +851,8 @@ async def list_payment_orders(
|
|||||||
filters.append(PaymentOrder.payment_method == payment_method)
|
filters.append(PaymentOrder.payment_method == payment_method)
|
||||||
if status:
|
if status:
|
||||||
filters.append(PaymentOrder.status == status)
|
filters.append(PaymentOrder.status == status)
|
||||||
|
if phone:
|
||||||
|
filters.append(User.phone.ilike(f"%{phone.strip()}%"))
|
||||||
if start_date:
|
if start_date:
|
||||||
filters.append(PaymentOrder.created_at >= datetime.fromisoformat(start_date).replace(tzinfo=CST))
|
filters.append(PaymentOrder.created_at >= datetime.fromisoformat(start_date).replace(tzinfo=CST))
|
||||||
if end_date:
|
if end_date:
|
||||||
@@ -873,6 +876,7 @@ async def list_payment_orders(
|
|||||||
"userId": o.user_id,
|
"userId": o.user_id,
|
||||||
"user_id": o.user_id,
|
"user_id": o.user_id,
|
||||||
"username": username,
|
"username": username,
|
||||||
|
"phone": user_phone,
|
||||||
"amount": round(float(o.amount), 2),
|
"amount": round(float(o.amount), 2),
|
||||||
"credits": round(float(o.credits), 2),
|
"credits": round(float(o.credits), 2),
|
||||||
"paymentMethod": o.payment_method,
|
"paymentMethod": o.payment_method,
|
||||||
@@ -885,7 +889,7 @@ async def list_payment_orders(
|
|||||||
"createdAt": o.created_at.isoformat() if o.created_at else None,
|
"createdAt": o.created_at.isoformat() if o.created_at else None,
|
||||||
"created_at": o.created_at.isoformat() if o.created_at else None,
|
"created_at": o.created_at.isoformat() if o.created_at else None,
|
||||||
}
|
}
|
||||||
for o, username in rows
|
for o, username, user_phone in rows
|
||||||
]
|
]
|
||||||
|
|
||||||
return {"items": items, "total": total, "page": page, "page_size": page_size}
|
return {"items": items, "total": total, "page": page, "page_size": page_size}
|
||||||
|
|||||||
Reference in New Issue
Block a user