init: 信创文件安全传输系统内外网本地 Docker 部署
包含 s-file 后端、React 前端、MeshSafe 安全检测对接,以及内外网两套 compose/nginx/配置(仅前端 28180/28280 对外发布,其余端口不暴露)。 说明:deploy/config/application-iw.yml 与 application-ow.yml 按维护者 明确要求原样入库,其中含本机 Docker 用的库口令、RabbitMQ 口令与 JWT 密钥;上生产前需替换。
This commit is contained in:
109
frontend/src/components/ExcelPreview.tsx
Normal file
109
frontend/src/components/ExcelPreview.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import axios from "axios";
|
||||
import * as XLSX from "xlsx";
|
||||
|
||||
type Props = {
|
||||
url?: string;
|
||||
};
|
||||
|
||||
export function ExcelPreview({ url }: Props) {
|
||||
const [sheets, setSheets] = useState<{ name: string; data: any[][] }[]>([]);
|
||||
const [activeSheet, setActiveSheet] = useState(0);
|
||||
|
||||
const { data, isLoading, error, isError } = useQuery({
|
||||
queryKey: ["excel", url],
|
||||
queryFn: () => axios.get(url!, { responseType: "arraybuffer" }),
|
||||
enabled: !!url,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (data?.data) {
|
||||
try {
|
||||
const workbook = XLSX.read(data.data, { type: "array" });
|
||||
const sheetData = workbook.SheetNames.map((name) => {
|
||||
const worksheet = workbook.Sheets[name];
|
||||
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
|
||||
return { name, data: jsonData as any[][] };
|
||||
});
|
||||
setSheets(sheetData);
|
||||
} catch (err) {
|
||||
|
||||
}
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
if (!url) {
|
||||
return (
|
||||
<div className="w-full h-full flex items-center justify-center text-muted-foreground">
|
||||
Excel 文件地址无效
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<div>加载中...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full text-red-500">
|
||||
{error?.message || "加载失败"}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (sheets.length === 0) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full text-muted-foreground">
|
||||
Excel 文件为空或格式不正确
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const currentSheet = sheets[activeSheet];
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
{sheets.length > 1 && (
|
||||
<div className="flex-none border-b bg-slate-50 dark:bg-slate-900 p-2 flex gap-2 overflow-x-auto">
|
||||
{sheets.map((sheet, index) => (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => setActiveSheet(index)}
|
||||
className={`px-4 py-2 rounded text-sm whitespace-nowrap transition-colors ${
|
||||
activeSheet === index
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "bg-white dark:bg-slate-800 hover:bg-slate-100 dark:hover:bg-slate-700"
|
||||
}`}
|
||||
>
|
||||
{sheet.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex-auto overflow-auto">
|
||||
<table className="w-full border-collapse text-sm">
|
||||
<tbody>
|
||||
{currentSheet.data.map((row, rowIndex) => (
|
||||
<tr key={rowIndex} className="border-b">
|
||||
{row.map((cell, cellIndex) => (
|
||||
<td
|
||||
key={cellIndex}
|
||||
className="border-r px-3 py-2 whitespace-nowrap"
|
||||
>
|
||||
{cell !== null && cell !== undefined ? String(cell) : ""}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user