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 href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
|
||||
<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">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
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 { getPaymentOrders, createInvoice, getInvoices, getInvoiceDetail } from '../api';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
interface InvoiceRecord {
|
||||
id: string;
|
||||
@@ -62,7 +63,14 @@ const InvoicePage: React.FC = () => {
|
||||
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 [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);
|
||||
@@ -82,8 +90,8 @@ const InvoicePage: React.FC = () => {
|
||||
return `FP${year}${month}${day}${rand}`;
|
||||
};
|
||||
|
||||
// 加载已开票订单ID列表
|
||||
const loadOccupiedOrderIds = async () => {
|
||||
// 加载已开票订单ID列表,返回 occupied Set
|
||||
const loadOccupiedOrderIds = async (): Promise<Set<string>> => {
|
||||
try {
|
||||
const data = await getInvoices({ page: 1, pageSize: 100 });
|
||||
const ids = new Set<string>();
|
||||
@@ -95,13 +103,14 @@ const InvoicePage: React.FC = () => {
|
||||
}
|
||||
});
|
||||
setOccupiedOrderIds(ids);
|
||||
return ids;
|
||||
} catch {
|
||||
// 忽略
|
||||
return occupiedOrderIds;
|
||||
}
|
||||
};
|
||||
|
||||
// 加载订单数据(显示所有已支付订单,已开票的标记 _occupied)
|
||||
const loadOrderData = async (page: number = orderPage) => {
|
||||
const loadOrderData = async (page: number = orderPage, occupiedIds?: Set<string>) => {
|
||||
setOrderLoading(true);
|
||||
try {
|
||||
const data = await getPaymentOrders(page, orderPageSize);
|
||||
@@ -121,9 +130,10 @@ const InvoicePage: React.FC = () => {
|
||||
});
|
||||
}
|
||||
// 标记已开票订单
|
||||
const occIds = occupiedIds || occupiedOrderIds;
|
||||
items = items.map((o: any) => ({
|
||||
...o,
|
||||
_occupied: occupiedOrderIds.has(o.id || o.order_no || o.orderNo),
|
||||
_occupied: occIds.has(o.id || o.order_no || o.orderNo),
|
||||
}));
|
||||
setOrderData(items);
|
||||
setOrderTotal(items.length);
|
||||
@@ -162,12 +172,16 @@ const InvoicePage: React.FC = () => {
|
||||
|
||||
useEffect(() => {
|
||||
loadInvoices();
|
||||
loadOccupiedOrderIds();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (issueModalOpen) {
|
||||
loadOrderData(1);
|
||||
setOrderPage(1);
|
||||
// 先刷新已开票订单列表,再加载订单数据
|
||||
loadOccupiedOrderIds().then((ids) => {
|
||||
setOrderPage(1);
|
||||
loadOrderData(1, ids);
|
||||
});
|
||||
}
|
||||
}, [issueModalOpen]);
|
||||
|
||||
@@ -421,15 +435,16 @@ const InvoicePage: React.FC = () => {
|
||||
const handleAddHeader = async () => {
|
||||
try {
|
||||
const values = await headerForm.validateFields();
|
||||
const isCompany = values.type === 'company';
|
||||
const newHeader: InvoiceHeader = {
|
||||
id: Date.now().toString(),
|
||||
name: values.name,
|
||||
type: values.type,
|
||||
taxNo: values.taxNo,
|
||||
registerAddress: values.registerAddress,
|
||||
bankName: values.bankName,
|
||||
registerPhone: values.registerPhone,
|
||||
bankAccount: values.bankAccount,
|
||||
taxNo: isCompany ? values.taxNo : undefined,
|
||||
registerAddress: isCompany ? values.registerAddress : undefined,
|
||||
bankName: isCompany ? values.bankName : undefined,
|
||||
registerPhone: isCompany ? values.registerPhone : undefined,
|
||||
bankAccount: isCompany ? values.bankAccount : undefined,
|
||||
email: values.email,
|
||||
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 }}>
|
||||
<span style={{ color: '#94a3b8', fontSize: 13 }}>支付时间:</span>
|
||||
<Input
|
||||
type="date"
|
||||
<DatePicker.RangePicker
|
||||
size="small"
|
||||
value={orderDateFilter[0] || ''}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value || null;
|
||||
setOrderDateFilter([val, orderDateFilter[1]]);
|
||||
value={orderDateFilter[0] ? [dayjs(orderDateFilter[0]), orderDateFilter[1] ? dayjs(orderDateFilter[1]) : null] : [null, null]}
|
||||
onChange={(dates) => {
|
||||
if (dates && dates[0] && dates[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);
|
||||
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>
|
||||
<Spin spinning={orderLoading}>
|
||||
{orderData.length === 0 ? (
|
||||
@@ -1087,62 +1085,83 @@ const InvoicePage: React.FC = () => {
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0 16px' }}>
|
||||
<Form.Item
|
||||
name="name"
|
||||
label="公司名称:"
|
||||
rules={[{ required: true, message: '请输入公司名称' }]}
|
||||
>
|
||||
<Input placeholder="请输入公司名称" />
|
||||
</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' }}>
|
||||
<Form.Item
|
||||
name="name"
|
||||
label={isCompany ? '公司名称:' : '个人姓名:'}
|
||||
rules={[{ required: true, message: isCompany ? '请输入公司名称' : '请输入个人姓名' }]}
|
||||
>
|
||||
<Input placeholder={isCompany ? '请输入公司名称' : '请输入个人姓名'} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="taxNo"
|
||||
label="公司税号:"
|
||||
rules={[{ required: true, message: '请输入公司税号' }]}
|
||||
>
|
||||
<Input placeholder="请输入公司税号" />
|
||||
</Form.Item>
|
||||
{isCompany && (
|
||||
<Form.Item
|
||||
name="taxNo"
|
||||
label="公司税号:"
|
||||
rules={[{ required: true, message: '请输入公司税号' }]}
|
||||
>
|
||||
<Input placeholder="请输入公司税号" />
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Form.Item
|
||||
name="registerAddress"
|
||||
label="注册地址:"
|
||||
>
|
||||
<Input placeholder="请输入注册地址" />
|
||||
</Form.Item>
|
||||
{isCompany && (
|
||||
<Form.Item
|
||||
name="registerAddress"
|
||||
label="注册地址:"
|
||||
>
|
||||
<Input placeholder="请输入注册地址" />
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Form.Item
|
||||
name="bankName"
|
||||
label="开户银行:"
|
||||
>
|
||||
<Input placeholder="请输入开户银行" />
|
||||
</Form.Item>
|
||||
{isCompany && (
|
||||
<Form.Item
|
||||
name="bankName"
|
||||
label="开户银行:"
|
||||
>
|
||||
<Input placeholder="请输入开户银行" />
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Form.Item
|
||||
name="registerPhone"
|
||||
label="注册电话:"
|
||||
>
|
||||
<Input placeholder="请输入注册电话" />
|
||||
</Form.Item>
|
||||
{isCompany && (
|
||||
<Form.Item
|
||||
name="registerPhone"
|
||||
label="注册电话:"
|
||||
>
|
||||
<Input placeholder="请输入注册电话" />
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Form.Item
|
||||
name="bankAccount"
|
||||
label="银行账号:"
|
||||
>
|
||||
<Input placeholder="请输入银行账号" />
|
||||
</Form.Item>
|
||||
{isCompany && (
|
||||
<Form.Item
|
||||
name="bankAccount"
|
||||
label="银行账号:"
|
||||
>
|
||||
<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
|
||||
name="email"
|
||||
label="接收邮箱:"
|
||||
rules={[
|
||||
{ required: true, message: '请输入接收邮箱' },
|
||||
{ pattern: /^[\w.\-]+@[\w.\-]+\.\w+$/, message: '请输入正确的邮箱格式' },
|
||||
]}
|
||||
>
|
||||
<Input placeholder="发票将发送至该邮箱" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="isDefault"
|
||||
|
||||
Reference in New Issue
Block a user