This commit is contained in:
2026-08-10 16:00:44 +08:00
parent e8e590948c
commit 7777454b16
+121 -10
View File
@@ -28,6 +28,7 @@ interface InvoiceHeader {
bankName?: string;
registerPhone?: string;
bankAccount?: string;
email?: string;
isDefault: boolean;
}
@@ -60,6 +61,8 @@ const InvoicePage: React.FC = () => {
const [orderTotal, setOrderTotal] = useState(0);
const [orderPage, setOrderPage] = useState(1);
const [orderPageSize] = useState(10);
const [occupiedOrderIds, setOccupiedOrderIds] = useState<Set<string>>(new Set());
const [orderDateFilter, setOrderDateFilter] = useState<[string | null, string | null]>([null, null]);
// 说明弹窗
const [noticeModalOpen, setNoticeModalOpen] = useState(false);
@@ -79,14 +82,51 @@ const InvoicePage: React.FC = () => {
return `FP${year}${month}${day}${rand}`;
};
// 加载订单数据
// 加载已开票订单ID列表
const loadOccupiedOrderIds = async () => {
try {
const data = await getInvoices({ page: 1, pageSize: 100 });
const ids = new Set<string>();
(data.items || []).forEach((inv: any) => {
if (inv.status === 'processing' || inv.status === 'success') {
(inv.orders || []).forEach((o: any) => {
ids.add(o.orderId || o.order_id || o.orderNo || o.order_no);
});
}
});
setOccupiedOrderIds(ids);
} catch {
// 忽略
}
};
// 加载订单数据(显示所有已支付订单,已开票的标记 _occupied)
const loadOrderData = async (page: number = orderPage) => {
setOrderLoading(true);
try {
const data = await getPaymentOrders(page, orderPageSize);
const items = data.items || [];
// 只显示已支付订单
let items = (data.items || []).filter((o: any) => (o.status || o.payment_status) === 'paid');
// 日期过滤
if (orderDateFilter[0]) {
items = items.filter((o: any) => {
const created = o.created_at || o.createdAt;
return created && created >= orderDateFilter[0];
});
}
if (orderDateFilter[1]) {
items = items.filter((o: any) => {
const created = o.created_at || o.createdAt;
return created && created <= orderDateFilter[1] + 'T23:59:59';
});
}
// 标记已开票订单
items = items.map((o: any) => ({
...o,
_occupied: occupiedOrderIds.has(o.id || o.order_no || o.orderNo),
}));
setOrderData(items);
setOrderTotal(data.total || 0);
setOrderTotal(items.length);
} catch {
setOrderData([]);
}
@@ -265,8 +305,12 @@ const InvoicePage: React.FC = () => {
{
title: '状态',
key: 'status',
width: 100,
width: 120,
render: (_: any, record: any) => {
// 已开票标签
if (record._occupied) {
return <Tag color="orange" style={{ fontWeight: 500, padding: '4px 12px' }}></Tag>;
}
const status = record.status || 'pending';
const statusConfig: Record<string, { color: string; label: string; bg: string }> = {
pending: { color: '#f59e0b', label: '待支付', bg: 'rgba(245,158,11,0.1)' },
@@ -386,6 +430,7 @@ const InvoicePage: React.FC = () => {
bankName: values.bankName,
registerPhone: values.registerPhone,
bankAccount: values.bankAccount,
email: values.email,
isDefault: values.isDefault === 'yes' || (headers.length === 0 && !allHeadersForSelect.some(h => h.isDefault)),
};
if (newHeader.isDefault) {
@@ -438,12 +483,14 @@ const InvoicePage: React.FC = () => {
message.warning('请选择一个发票抬头');
return;
}
if (!email.trim()) {
// 优先使用弹窗中填写的邮箱,否则使用抬头中保存的邮箱
const finalEmail = email.trim() || selectedHeader?.email || '';
if (!finalEmail) {
message.warning('请输入电子邮箱');
return;
}
const emailRegex = /^[\w.\-]+@[\w.\-]+\.\w+$/;
if (!emailRegex.test(email.trim())) {
if (!emailRegex.test(finalEmail)) {
message.warning('请输入正确的邮箱格式');
return;
}
@@ -461,7 +508,7 @@ const InvoicePage: React.FC = () => {
headerRegisterPhone: selectedHeader?.registerPhone,
headerBankName: selectedHeader?.bankName,
headerBankAccount: selectedHeader?.bankAccount,
email: email.trim(),
email: finalEmail,
orderIds,
});
message.success('开票申请已提交,请等待审核');
@@ -485,10 +532,10 @@ const InvoicePage: React.FC = () => {
const currentPageKeys = orderData.map((o: any) => o.order_no || o.orderNo || o.id);
// First remove all keys from current page
currentPageKeys.forEach(key => newMap.delete(key));
// Then add back the selected ones
// Then add back the selected ones(排除已开票订单)
keys.forEach(key => {
const row = selectedRows.find((r: any) => (r.order_no || r.orderNo || r.id) === key);
if (row) {
if (row && !row._occupied) {
newMap.set(key as string, row);
}
});
@@ -497,6 +544,8 @@ const InvoicePage: React.FC = () => {
preserveSelectedRowKeys: true,
getCheckboxProps: (record: any) => ({
name: record.order_no || record.orderNo || record.id,
// 已开票订单不可选
disabled: !!record._occupied,
}),
};
@@ -769,9 +818,51 @@ const InvoicePage: React.FC = () => {
closeIcon={<CloseOutlined style={{ fontSize: 16, color: '#94a3b8' }} />}
>
<div style={{ padding: '0 24px 24px' }}>
{/* 日期筛选 */}
<div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '12px 0', borderBottom: '1px solid #f1f5f9', marginBottom: 12 }}>
<span style={{ color: '#94a3b8', fontSize: 13 }}></span>
<Input
type="date"
size="small"
value={orderDateFilter[0] || ''}
onChange={(e) => {
const val = e.target.value || null;
setOrderDateFilter([val, orderDateFilter[1]]);
setOrderPage(1);
setTimeout(() => loadOrderData(1), 0);
}}
style={{ width: 150 }}
/>
<span style={{ color: '#94a3b8' }}></span>
<Input
type="date"
size="small"
value={orderDateFilter[1] || ''}
onChange={(e) => {
const val = e.target.value || null;
setOrderDateFilter([orderDateFilter[0], val]);
setOrderPage(1);
setTimeout(() => loadOrderData(1), 0);
}}
style={{ width: 150 }}
/>
{(orderDateFilter[0] || orderDateFilter[1]) && (
<Button
type="link"
size="small"
onClick={() => {
setOrderDateFilter([null, null]);
setOrderPage(1);
loadOrderData(1);
}}
>
</Button>
)}
</div>
<Spin spinning={orderLoading}>
{orderData.length === 0 ? (
<Empty description={<span style={{ color: '#94a3b8' }}></span>} />
<Empty description={<span style={{ color: '#94a3b8' }}></span>} />
) : (
<>
<Table
@@ -842,6 +933,15 @@ const InvoicePage: React.FC = () => {
style={{ maxWidth: 360 }}
allowClear
/>
{selectedHeader?.email && !email && (
<Button
type="link"
size="small"
onClick={() => setEmail(selectedHeader.email || '')}
>
使{selectedHeader.email}
</Button>
)}
</div>
</div>
<Table
@@ -1026,6 +1126,17 @@ const InvoicePage: React.FC = () => {
>
<Input placeholder="请输入银行账号" />
</Form.Item>
<Form.Item
name="email"
label="接收邮箱:"
rules={[
{ required: true, message: '请输入接收邮箱' },
{ pattern: /^[\w.\-]+@[\w.\-]+\.\w+$/, message: '请输入正确的邮箱格式' },
]}
>
<Input placeholder="发票将发送至该邮箱" />
</Form.Item>
</div>
<Form.Item