mirror of
https://github.com/serty2005/rmser.git
synced 2026-02-04 19:02:33 -06:00
207 lines
6.0 KiB
TypeScript
207 lines
6.0 KiB
TypeScript
import React, { useState, useEffect } from "react";
|
||
import { Modal, Button, message } from "antd";
|
||
import {
|
||
ZoomInOutlined,
|
||
ZoomOutOutlined,
|
||
UndoOutlined,
|
||
} from "@ant-design/icons";
|
||
import * as XLSX from "xlsx";
|
||
import { apiClient } from "../api";
|
||
|
||
interface ExcelPreviewModalProps {
|
||
visible: boolean;
|
||
onCancel: () => void;
|
||
fileUrl: string;
|
||
}
|
||
|
||
/**
|
||
* Компонент для предпросмотра Excel файлов
|
||
* Позволяет просматривать содержимое Excel файлов с возможностью масштабирования
|
||
*/
|
||
const ExcelPreviewModal: React.FC<ExcelPreviewModalProps> = ({
|
||
visible,
|
||
onCancel,
|
||
fileUrl,
|
||
}) => {
|
||
// Данные таблицы из Excel файла
|
||
const [data, setData] = useState<
|
||
(string | number | boolean | null | undefined)[][]
|
||
>([]);
|
||
// Масштаб отображения таблицы
|
||
const [scale, setScale] = useState<number>(1);
|
||
|
||
/**
|
||
* Загрузка и парсинг Excel файла при изменении видимости или URL файла
|
||
*/
|
||
useEffect(() => {
|
||
const loadExcelFile = async () => {
|
||
if (!visible || !fileUrl) {
|
||
return;
|
||
}
|
||
|
||
console.log("ExcelPreviewModal: Start loading", fileUrl);
|
||
|
||
try {
|
||
// Загрузка файла через apiClient с авторизацией
|
||
const response = await apiClient.get(fileUrl, {
|
||
responseType: "arraybuffer",
|
||
});
|
||
console.log(
|
||
"ExcelPreviewModal: Got response",
|
||
response.status,
|
||
response.data.byteLength
|
||
);
|
||
const arrayBuffer = response.data;
|
||
|
||
// Чтение Excel файла
|
||
const workbook = XLSX.read(arrayBuffer, { type: "array" });
|
||
console.log("ExcelPreviewModal: Workbook parsed", workbook.SheetNames);
|
||
|
||
// Получение первого листа
|
||
const firstSheetName = workbook.SheetNames[0];
|
||
const sheet = workbook.Sheets[firstSheetName];
|
||
|
||
// Преобразование листа в JSON-массив массивов
|
||
const jsonData = XLSX.utils.sheet_to_json(sheet, {
|
||
header: 1,
|
||
}) as (string | number | boolean | null | undefined)[][];
|
||
|
||
setData(jsonData);
|
||
console.log("ExcelPreviewModal: Data set, rows:", jsonData.length);
|
||
// Сброс масштаба при загрузке нового файла
|
||
setScale(1);
|
||
} catch (error) {
|
||
console.error("ExcelPreviewModal Error:", error);
|
||
|
||
// Обработка ошибок авторизации (401) обрабатывается в интерсепторе apiClient
|
||
if (error && typeof error === "object" && "response" in error) {
|
||
const axiosError = error as { response?: { status?: number } };
|
||
if (axiosError.response?.status === 401) {
|
||
message.error(
|
||
"Ошибка авторизации. Необходима повторная авторизация."
|
||
);
|
||
} else {
|
||
message.error("Не удалось загрузить Excel файл");
|
||
}
|
||
} else {
|
||
message.error("Не удалось загрузить Excel файл");
|
||
}
|
||
|
||
setData([]);
|
||
}
|
||
};
|
||
|
||
loadExcelFile();
|
||
}, [visible, fileUrl]);
|
||
|
||
/**
|
||
* Увеличение масштаба
|
||
*/
|
||
const handleZoomIn = () => {
|
||
setScale((prev) => Math.min(prev + 0.1, 3));
|
||
};
|
||
|
||
/**
|
||
* Уменьшение масштаба
|
||
*/
|
||
const handleZoomOut = () => {
|
||
setScale((prev) => Math.max(prev - 0.1, 0.5));
|
||
};
|
||
|
||
/**
|
||
* Сброс масштаба до исходного значения
|
||
*/
|
||
const handleReset = () => {
|
||
setScale(1);
|
||
};
|
||
|
||
/**
|
||
* Обработчик закрытия модалки
|
||
*/
|
||
const handleCancel = () => {
|
||
setData([]);
|
||
onCancel();
|
||
};
|
||
|
||
return (
|
||
<Modal
|
||
open={visible}
|
||
onCancel={handleCancel}
|
||
width="90%"
|
||
footer={null}
|
||
title="Предпросмотр Excel"
|
||
style={{ top: 20, zIndex: 10000 }}
|
||
>
|
||
{/* Панель инструментов для управления масштабом */}
|
||
<div style={{ marginBottom: 16, display: "flex", gap: 8 }}>
|
||
<Button
|
||
icon={<ZoomInOutlined />}
|
||
onClick={handleZoomIn}
|
||
disabled={scale >= 3}
|
||
>
|
||
Увеличить (+)
|
||
</Button>
|
||
<Button
|
||
icon={<ZoomOutOutlined />}
|
||
onClick={handleZoomOut}
|
||
disabled={scale <= 0.5}
|
||
>
|
||
Уменьшить (-)
|
||
</Button>
|
||
<Button
|
||
icon={<UndoOutlined />}
|
||
onClick={handleReset}
|
||
disabled={scale === 1}
|
||
>
|
||
Сброс
|
||
</Button>
|
||
<span style={{ marginLeft: "auto", lineHeight: "32px" }}>
|
||
Масштаб: {Math.round(scale * 100)}%
|
||
</span>
|
||
</div>
|
||
|
||
{/* Контейнер с прокруткой для таблицы */}
|
||
<div
|
||
style={{
|
||
height: 600,
|
||
overflow: "auto",
|
||
border: "1px solid #d9d9d9",
|
||
borderRadius: 4,
|
||
}}
|
||
>
|
||
{/* Таблица с данными Excel */}
|
||
<table
|
||
style={{
|
||
borderCollapse: "collapse",
|
||
transform: `scale(${scale})`,
|
||
transformOrigin: "top left",
|
||
width: "100%",
|
||
}}
|
||
>
|
||
<tbody>
|
||
{data.map((row, rowIndex) => (
|
||
<tr key={rowIndex}>
|
||
{row.map((cell, cellIndex) => (
|
||
<td
|
||
key={cellIndex}
|
||
style={{
|
||
border: "1px solid #ccc",
|
||
padding: "8px",
|
||
minWidth: 100,
|
||
whiteSpace: "nowrap",
|
||
}}
|
||
>
|
||
{cell !== undefined && cell !== null ? String(cell) : ""}
|
||
</td>
|
||
))}
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</Modal>
|
||
);
|
||
};
|
||
|
||
export default ExcelPreviewModal;
|