0302-отрефакторил в нормальный вид на мобилу и десктоп

сразу выкинул пути в импортах и добавил алиас для корня
This commit is contained in:
2026-02-03 12:49:20 +03:00
parent ea1e5bbf6a
commit 51bc5bf8f0
50 changed files with 12878 additions and 490 deletions

View File

@@ -0,0 +1,206 @@
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;