Add Excel export feature for full production data

Add "导出全部" button that exports all contract production data to an
.xlsx file via a new API endpoint using exceljs, with formatted headers,
date columns, auto-width, and frozen header row.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Misaka_Company
2026-06-10 17:35:48 +08:00
parent 2c66faf207
commit 8235a29f82
4 changed files with 1128 additions and 7 deletions

View File

@@ -14,7 +14,7 @@ import {
Checkbox,
AutoComplete,
} from "antd";
import { SearchOutlined, SettingOutlined, HolderOutlined, ClockCircleOutlined } from "@ant-design/icons";
import { SearchOutlined, SettingOutlined, HolderOutlined, ClockCircleOutlined, DownloadOutlined } from "@ant-design/icons";
import type { ColumnsType } from "antd/es/table";
import {
DndContext,
@@ -333,6 +333,8 @@ function SearchBar({
onSearch,
onClear,
onOpenColumnSettings,
exporting,
onExport,
}: {
loading: boolean;
resultCount: number;
@@ -340,6 +342,8 @@ function SearchBar({
onSearch: (workshopNo: string) => Promise<void>;
onClear: () => void;
onOpenColumnSettings: () => void;
exporting: boolean;
onExport: () => void;
}) {
const [inputValue, setInputValue] = useState("");
const [history, setHistory] = useState<string[]>(() => loadHistory());
@@ -418,6 +422,13 @@ function SearchBar({
<Button icon={<SettingOutlined />} onClick={onOpenColumnSettings}>
</Button>
<Button
icon={<DownloadOutlined />}
onClick={onExport}
loading={exporting}
>
</Button>
</Space>
{searched && !loading && (
<Text type="secondary" style={{ marginLeft: 16 }}>
@@ -438,6 +449,7 @@ export default function Home() {
const [searched, setSearched] = useState(false);
const [columnConfig, setColumnConfig] = useState<ColumnConfig[]>(loadConfig);
const [modalOpen, setModalOpen] = useState(false);
const [exporting, setExporting] = useState(false);
// Persist config
useEffect(() => {
@@ -473,6 +485,31 @@ export default function Home() {
setError(null);
}, []);
const handleExport = useCallback(async () => {
setExporting(true);
try {
const res = await fetch("/api/export-excel");
if (!res.ok) {
const json = await res.json().catch(() => null);
setError(json?.error || "导出失败");
return;
}
const blob = await res.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "压力表合同生产数据.xlsx";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
} catch {
setError("导出请求失败");
} finally {
setExporting(false);
}
}, []);
const handleApplyConfig = useCallback((newConfig: ColumnConfig[]) => {
setColumnConfig(newConfig);
setModalOpen(false);
@@ -500,6 +537,8 @@ export default function Home() {
onSearch={handleSearch}
onClear={handleClear}
onOpenColumnSettings={() => setModalOpen(true)}
exporting={exporting}
onExport={handleExport}
/>
<div style={{ flex: 1, minHeight: 0 }}>