1
This commit is contained in:
+36
-36
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -27,7 +27,7 @@
|
|||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
|
||||||
<title>民众智创</title>
|
<title>民众智创</title>
|
||||||
<script type="module" crossorigin src="/assets/index-B6hD5A9D.js"></script>
|
<script type="module" crossorigin src="/assets/index-Chp4mfc5.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-DtYH0-uL.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-DtYH0-uL.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Table, Tag, Empty, Spin, Pagination, Button, Typography, Modal, Form, Input, Radio, message, Space } from 'antd';
|
import { Table, Tag, Empty, Spin, Pagination, Button, Typography, Modal, Form, Input, Radio, message, Space, DatePicker } from 'antd';
|
||||||
import { FileTextOutlined, PlusOutlined, CloseOutlined, AlipayCircleOutlined, WechatOutlined } from '@ant-design/icons';
|
import { FileTextOutlined, PlusOutlined, CloseOutlined, AlipayCircleOutlined, WechatOutlined } from '@ant-design/icons';
|
||||||
import { getPaymentOrders, createInvoice, getInvoices, getInvoiceDetail } from '../api';
|
import { getPaymentOrders, createInvoice, getInvoices, getInvoiceDetail } from '../api';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
interface InvoiceRecord {
|
interface InvoiceRecord {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -62,7 +63,14 @@ const InvoicePage: React.FC = () => {
|
|||||||
const [orderPage, setOrderPage] = useState(1);
|
const [orderPage, setOrderPage] = useState(1);
|
||||||
const [orderPageSize] = useState(10);
|
const [orderPageSize] = useState(10);
|
||||||
const [occupiedOrderIds, setOccupiedOrderIds] = useState<Set<string>>(new Set());
|
const [occupiedOrderIds, setOccupiedOrderIds] = useState<Set<string>>(new Set());
|
||||||
const [orderDateFilter, setOrderDateFilter] = useState<[string | null, string | null]>([null, null]);
|
// 默认日期范围:当前月份
|
||||||
|
const [orderDateFilter, setOrderDateFilter] = useState<[string | null, string | null]>(() => {
|
||||||
|
const now = new Date();
|
||||||
|
const start = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-01`;
|
||||||
|
const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate();
|
||||||
|
const end = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(lastDay).padStart(2, '0')}`;
|
||||||
|
return [start, end];
|
||||||
|
});
|
||||||
|
|
||||||
// 说明弹窗
|
// 说明弹窗
|
||||||
const [noticeModalOpen, setNoticeModalOpen] = useState(false);
|
const [noticeModalOpen, setNoticeModalOpen] = useState(false);
|
||||||
@@ -82,8 +90,8 @@ const InvoicePage: React.FC = () => {
|
|||||||
return `FP${year}${month}${day}${rand}`;
|
return `FP${year}${month}${day}${rand}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 加载已开票订单ID列表
|
// 加载已开票订单ID列表,返回 occupied Set
|
||||||
const loadOccupiedOrderIds = async () => {
|
const loadOccupiedOrderIds = async (): Promise<Set<string>> => {
|
||||||
try {
|
try {
|
||||||
const data = await getInvoices({ page: 1, pageSize: 100 });
|
const data = await getInvoices({ page: 1, pageSize: 100 });
|
||||||
const ids = new Set<string>();
|
const ids = new Set<string>();
|
||||||
@@ -95,13 +103,14 @@ const InvoicePage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
setOccupiedOrderIds(ids);
|
setOccupiedOrderIds(ids);
|
||||||
|
return ids;
|
||||||
} catch {
|
} catch {
|
||||||
// 忽略
|
return occupiedOrderIds;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 加载订单数据(显示所有已支付订单,已开票的标记 _occupied)
|
// 加载订单数据(显示所有已支付订单,已开票的标记 _occupied)
|
||||||
const loadOrderData = async (page: number = orderPage) => {
|
const loadOrderData = async (page: number = orderPage, occupiedIds?: Set<string>) => {
|
||||||
setOrderLoading(true);
|
setOrderLoading(true);
|
||||||
try {
|
try {
|
||||||
const data = await getPaymentOrders(page, orderPageSize);
|
const data = await getPaymentOrders(page, orderPageSize);
|
||||||
@@ -121,9 +130,10 @@ const InvoicePage: React.FC = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 标记已开票订单
|
// 标记已开票订单
|
||||||
|
const occIds = occupiedIds || occupiedOrderIds;
|
||||||
items = items.map((o: any) => ({
|
items = items.map((o: any) => ({
|
||||||
...o,
|
...o,
|
||||||
_occupied: occupiedOrderIds.has(o.id || o.order_no || o.orderNo),
|
_occupied: occIds.has(o.id || o.order_no || o.orderNo),
|
||||||
}));
|
}));
|
||||||
setOrderData(items);
|
setOrderData(items);
|
||||||
setOrderTotal(items.length);
|
setOrderTotal(items.length);
|
||||||
@@ -162,12 +172,16 @@ const InvoicePage: React.FC = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadInvoices();
|
loadInvoices();
|
||||||
|
loadOccupiedOrderIds();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (issueModalOpen) {
|
if (issueModalOpen) {
|
||||||
loadOrderData(1);
|
// 先刷新已开票订单列表,再加载订单数据
|
||||||
|
loadOccupiedOrderIds().then((ids) => {
|
||||||
setOrderPage(1);
|
setOrderPage(1);
|
||||||
|
loadOrderData(1, ids);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, [issueModalOpen]);
|
}, [issueModalOpen]);
|
||||||
|
|
||||||
@@ -421,15 +435,16 @@ const InvoicePage: React.FC = () => {
|
|||||||
const handleAddHeader = async () => {
|
const handleAddHeader = async () => {
|
||||||
try {
|
try {
|
||||||
const values = await headerForm.validateFields();
|
const values = await headerForm.validateFields();
|
||||||
|
const isCompany = values.type === 'company';
|
||||||
const newHeader: InvoiceHeader = {
|
const newHeader: InvoiceHeader = {
|
||||||
id: Date.now().toString(),
|
id: Date.now().toString(),
|
||||||
name: values.name,
|
name: values.name,
|
||||||
type: values.type,
|
type: values.type,
|
||||||
taxNo: values.taxNo,
|
taxNo: isCompany ? values.taxNo : undefined,
|
||||||
registerAddress: values.registerAddress,
|
registerAddress: isCompany ? values.registerAddress : undefined,
|
||||||
bankName: values.bankName,
|
bankName: isCompany ? values.bankName : undefined,
|
||||||
registerPhone: values.registerPhone,
|
registerPhone: isCompany ? values.registerPhone : undefined,
|
||||||
bankAccount: values.bankAccount,
|
bankAccount: isCompany ? values.bankAccount : undefined,
|
||||||
email: values.email,
|
email: values.email,
|
||||||
isDefault: values.isDefault === 'yes' || (headers.length === 0 && !allHeadersForSelect.some(h => h.isDefault)),
|
isDefault: values.isDefault === 'yes' || (headers.length === 0 && !allHeadersForSelect.some(h => h.isDefault)),
|
||||||
};
|
};
|
||||||
@@ -821,44 +836,27 @@ const InvoicePage: React.FC = () => {
|
|||||||
{/* 日期筛选 */}
|
{/* 日期筛选 */}
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '12px 0', borderBottom: '1px solid #f1f5f9', marginBottom: 12 }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '12px 0', borderBottom: '1px solid #f1f5f9', marginBottom: 12 }}>
|
||||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>支付时间:</span>
|
<span style={{ color: '#94a3b8', fontSize: 13 }}>支付时间:</span>
|
||||||
<Input
|
<DatePicker.RangePicker
|
||||||
type="date"
|
|
||||||
size="small"
|
size="small"
|
||||||
value={orderDateFilter[0] || ''}
|
value={orderDateFilter[0] ? [dayjs(orderDateFilter[0]), orderDateFilter[1] ? dayjs(orderDateFilter[1]) : null] : [null, null]}
|
||||||
onChange={(e) => {
|
onChange={(dates) => {
|
||||||
const val = e.target.value || null;
|
if (dates && dates[0] && dates[1]) {
|
||||||
setOrderDateFilter([val, orderDateFilter[1]]);
|
const start = dates[0].format('YYYY-MM-DD');
|
||||||
|
const end = dates[1].format('YYYY-MM-DD');
|
||||||
|
setOrderDateFilter([start, end]);
|
||||||
|
} else {
|
||||||
|
// 清除时恢复默认当前月份
|
||||||
|
const now = new Date();
|
||||||
|
const start = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-01`;
|
||||||
|
const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate();
|
||||||
|
const end = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(lastDay).padStart(2, '0')}`;
|
||||||
|
setOrderDateFilter([start, end]);
|
||||||
|
}
|
||||||
setOrderPage(1);
|
setOrderPage(1);
|
||||||
setTimeout(() => loadOrderData(1), 0);
|
setTimeout(() => loadOrderData(1), 0);
|
||||||
}}
|
}}
|
||||||
style={{ width: 150 }}
|
style={{ width: 280 }}
|
||||||
/>
|
/>
|
||||||
<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>
|
</div>
|
||||||
<Spin spinning={orderLoading}>
|
<Spin spinning={orderLoading}>
|
||||||
{orderData.length === 0 ? (
|
{orderData.length === 0 ? (
|
||||||
@@ -1087,15 +1085,23 @@ const InvoicePage: React.FC = () => {
|
|||||||
</Radio.Group>
|
</Radio.Group>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
|
{/* 监听抬头类型,用于条件渲染 */}
|
||||||
|
<Form.Item noStyle shouldUpdate={(prev, cur) => prev.type !== cur.type}>
|
||||||
|
{() => {
|
||||||
|
const type = headerForm.getFieldValue('type');
|
||||||
|
const isCompany = type === 'company';
|
||||||
|
return (
|
||||||
|
<>
|
||||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0 16px' }}>
|
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0 16px' }}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="name"
|
name="name"
|
||||||
label="公司名称:"
|
label={isCompany ? '公司名称:' : '个人姓名:'}
|
||||||
rules={[{ required: true, message: '请输入公司名称' }]}
|
rules={[{ required: true, message: isCompany ? '请输入公司名称' : '请输入个人姓名' }]}
|
||||||
>
|
>
|
||||||
<Input placeholder="请输入公司名称" />
|
<Input placeholder={isCompany ? '请输入公司名称' : '请输入个人姓名'} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
|
{isCompany && (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="taxNo"
|
name="taxNo"
|
||||||
label="公司税号:"
|
label="公司税号:"
|
||||||
@@ -1103,34 +1109,43 @@ const InvoicePage: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<Input placeholder="请输入公司税号" />
|
<Input placeholder="请输入公司税号" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isCompany && (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="registerAddress"
|
name="registerAddress"
|
||||||
label="注册地址:"
|
label="注册地址:"
|
||||||
>
|
>
|
||||||
<Input placeholder="请输入注册地址" />
|
<Input placeholder="请输入注册地址" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isCompany && (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="bankName"
|
name="bankName"
|
||||||
label="开户银行:"
|
label="开户银行:"
|
||||||
>
|
>
|
||||||
<Input placeholder="请输入开户银行" />
|
<Input placeholder="请输入开户银行" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isCompany && (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="registerPhone"
|
name="registerPhone"
|
||||||
label="注册电话:"
|
label="注册电话:"
|
||||||
>
|
>
|
||||||
<Input placeholder="请输入注册电话" />
|
<Input placeholder="请输入注册电话" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isCompany && (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="bankAccount"
|
name="bankAccount"
|
||||||
label="银行账号:"
|
label="银行账号:"
|
||||||
>
|
>
|
||||||
<Input placeholder="请输入银行账号" />
|
<Input placeholder="请输入银行账号" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="email"
|
name="email"
|
||||||
@@ -1143,6 +1158,10 @@ const InvoicePage: React.FC = () => {
|
|||||||
<Input placeholder="发票将发送至该邮箱" />
|
<Input placeholder="发票将发送至该邮箱" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="isDefault"
|
name="isDefault"
|
||||||
|
|||||||
Reference in New Issue
Block a user