Files
rmser/rmser-view/src/shared/ui/ExcelPreviewModal.tsx
SERTY 51bc5bf8f0 0302-отрефакторил в нормальный вид на мобилу и десктоп
сразу выкинул пути в импортах и добавил алиас для корня
2026-02-03 12:49:20 +03:00

207 lines
6.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;