Упрощение кода и улучшение стиля компонента TemplateEditor
This commit is contained in:
@@ -1,16 +1,16 @@
|
|||||||
import { ArrowLeft, Save, Upload, Wrench } from 'lucide-react';
|
import { ArrowLeft, Save, Upload, Wrench } from 'lucide-react'
|
||||||
import React, { useRef, useState } from 'react';
|
import React, { useRef, useState } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom'
|
||||||
import * as XLSX from 'xlsx';
|
import * as XLSX from 'xlsx'
|
||||||
import { Template } from '../../types/template';
|
import { Template } from '../../types/template'
|
||||||
import DualSpreadsheet from '../DualSpreadsheet/DualSpreadsheet';
|
import DualSpreadsheet from '../DualSpreadsheet/DualSpreadsheet'
|
||||||
import { Button } from '../ui/button';
|
import { Button } from '../ui/button'
|
||||||
|
|
||||||
interface TemplateEditorProps {
|
interface TemplateEditorProps {
|
||||||
template: Template;
|
template: Template
|
||||||
onSave: (template: Template) => void;
|
onSave: (template: Template) => void
|
||||||
onCancel: () => void;
|
onCancel: () => void
|
||||||
onBack: () => void;
|
onBack: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TemplateEditor: React.FC<TemplateEditorProps> = ({
|
export const TemplateEditor: React.FC<TemplateEditorProps> = ({
|
||||||
@@ -19,80 +19,81 @@ export const TemplateEditor: React.FC<TemplateEditorProps> = ({
|
|||||||
onCancel,
|
onCancel,
|
||||||
onBack,
|
onBack,
|
||||||
}) => {
|
}) => {
|
||||||
const [editedTemplate, setEditedTemplate] = useState<Template>(template);
|
const [editedTemplate, setEditedTemplate] = useState<Template>(template)
|
||||||
const [excelData, setExcelData] = useState<Record<string, any>>(template.excelData || {});
|
const [excelData, setExcelData] = useState<Record<string, any>>(
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
template.excelData || {},
|
||||||
const navigate = useNavigate();
|
)
|
||||||
|
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
const handleFileUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleFileUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const file = event.target.files?.[0];
|
const file = event.target.files?.[0]
|
||||||
if (!file) return;
|
if (!file) return
|
||||||
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader()
|
||||||
reader.onload = (e) => {
|
reader.onload = (e) => {
|
||||||
try {
|
try {
|
||||||
const data = new Uint8Array(e.target?.result as ArrayBuffer);
|
const data = new Uint8Array(e.target?.result as ArrayBuffer)
|
||||||
const workbook = XLSX.read(data, { type: 'array' });
|
const workbook = XLSX.read(data, { type: 'array' })
|
||||||
|
|
||||||
// Читаем первый лист
|
// Читаем первый лист
|
||||||
const firstSheetName = workbook.SheetNames[0];
|
const firstSheetName = workbook.SheetNames[0]
|
||||||
const worksheet = workbook.Sheets[firstSheetName];
|
const worksheet = workbook.Sheets[firstSheetName]
|
||||||
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
|
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 })
|
||||||
|
|
||||||
// Преобразуем в формат для нашей системы
|
// Преобразуем в формат для нашей системы
|
||||||
const formattedData: Record<string, any> = {};
|
const formattedData: Record<string, any> = {}
|
||||||
|
|
||||||
(jsonData as any[][]).forEach((row, rowIndex) => {
|
;(jsonData as any[][]).forEach((row, rowIndex) => {
|
||||||
row.forEach((cell, colIndex) => {
|
row.forEach((cell, colIndex) => {
|
||||||
if (cell !== undefined && cell !== null && cell !== '') {
|
if (cell !== undefined && cell !== null && cell !== '') {
|
||||||
const cellName = String.fromCharCode(65 + colIndex) + (rowIndex + 1);
|
const cellName =
|
||||||
formattedData[cellName] = cell;
|
String.fromCharCode(65 + colIndex) + (rowIndex + 1)
|
||||||
|
formattedData[cellName] = cell
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
setExcelData(formattedData);
|
setExcelData(formattedData)
|
||||||
setEditedTemplate(prev => ({
|
setEditedTemplate((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
excelFile: file,
|
excelFile: file,
|
||||||
excelData: formattedData,
|
excelData: formattedData,
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
}));
|
}))
|
||||||
|
|
||||||
// Принудительно обновляем данные в таблице
|
// Принудительно обновляем данные в таблице
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setExcelData({...formattedData});
|
setExcelData({ ...formattedData })
|
||||||
}, 100);
|
}, 100)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Ошибка при чтении Excel файла:', error);
|
console.error('Ошибка при чтении Excel файла:', error)
|
||||||
alert('Ошибка при чтении Excel файла');
|
alert('Ошибка при чтении Excel файла')
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
reader.readAsArrayBuffer(file);
|
reader.readAsArrayBuffer(file)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
onSave(editedTemplate);
|
onSave(editedTemplate)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleCreateElements = () => {
|
const handleCreateElements = () => {
|
||||||
navigate('/elements');
|
navigate('/elements')
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-screen flex flex-col bg-gray-50">
|
<div className="flex h-screen flex-col bg-background">
|
||||||
{/* Заголовок */}
|
{/* Заголовок */}
|
||||||
<div className="bg-white border-b border-gray-200 p-4 flex-shrink-0">
|
<div className="flex-shrink-0 border-b bg-card px-4 py-3">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-3">
|
||||||
<Button variant="ghost" size="icon" onClick={onBack}>
|
<Button variant="ghost" size="icon" onClick={onBack}>
|
||||||
<ArrowLeft className="h-4 w-4" />
|
<ArrowLeft className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-xl font-semibold text-gray-800">
|
<h1 className="text-lg font-semibold">{editedTemplate.name}</h1>
|
||||||
Редактирование шаблона: {editedTemplate.name}
|
<p className="mt-0.5 text-xs text-muted-foreground">
|
||||||
</h1>
|
|
||||||
<p className="text-sm text-gray-600 mt-1">
|
|
||||||
Настройте Excel шаблон и значения отчета
|
Настройте Excel шаблон и значения отчета
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -100,17 +101,22 @@ export const TemplateEditor: React.FC<TemplateEditorProps> = ({
|
|||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
onClick={handleCreateElements}
|
onClick={handleCreateElements}
|
||||||
className="flex items-center gap-2"
|
className="flex items-center gap-2"
|
||||||
>
|
>
|
||||||
<Wrench className="h-4 w-4" />
|
<Wrench className="h-3.5 w-3.5" />
|
||||||
Создать элементы
|
Создать элементы
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="outline" onClick={onCancel}>
|
<Button variant="outline" size="sm" onClick={onCancel}>
|
||||||
Отмена
|
Отмена
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handleSave} className="flex items-center gap-2">
|
<Button
|
||||||
<Save className="h-4 w-4" />
|
size="sm"
|
||||||
|
onClick={handleSave}
|
||||||
|
className="flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<Save className="h-3.5 w-3.5" />
|
||||||
Сохранить
|
Сохранить
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -118,25 +124,26 @@ export const TemplateEditor: React.FC<TemplateEditorProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Панель загрузки Excel */}
|
{/* Панель загрузки Excel */}
|
||||||
<div className="bg-white border-b border-gray-200 p-4 flex-shrink-0">
|
<div className="flex-shrink-0 border-b bg-muted/30 px-4 py-2">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-3">
|
||||||
<div className="text-sm font-medium text-gray-700">
|
<span className="text-xs font-medium text-muted-foreground">
|
||||||
Excel шаблон:
|
Excel шаблон:
|
||||||
</div>
|
</span>
|
||||||
{editedTemplate.excelFile ? (
|
{editedTemplate.excelFile ? (
|
||||||
<div className="flex items-center gap-2 text-sm text-green-600">
|
<div className="flex items-center gap-2 rounded-md bg-emerald-50 px-2 py-1">
|
||||||
<Upload className="h-4 w-4" />
|
<Upload className="h-3 w-3 text-emerald-600" />
|
||||||
<span>{editedTemplate.excelFile.name}</span>
|
<span className="text-xs text-emerald-700">
|
||||||
|
{editedTemplate.excelFile.name}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-sm text-gray-500">Файл не загружен</div>
|
<div className="text-xs text-muted-foreground">
|
||||||
|
Файл не загружен
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-3">
|
||||||
<div className="text-sm text-gray-500">
|
|
||||||
Элементов создано: {editedTemplate.elements.length}
|
|
||||||
</div>
|
|
||||||
<input
|
<input
|
||||||
ref={fileInputRef}
|
ref={fileInputRef}
|
||||||
type="file"
|
type="file"
|
||||||
@@ -148,21 +155,21 @@ export const TemplateEditor: React.FC<TemplateEditorProps> = ({
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => fileInputRef.current?.click()}
|
onClick={() => fileInputRef.current?.click()}
|
||||||
className="flex items-center gap-2"
|
className="flex h-7 items-center gap-2"
|
||||||
>
|
>
|
||||||
<Upload className="h-4 w-4" />
|
<Upload className="h-3 w-3" />
|
||||||
{editedTemplate.excelFile ? 'Заменить файл' : 'Загрузить Excel'}
|
<span className="text-xs">
|
||||||
|
{editedTemplate.excelFile ? 'Заменить файл' : 'Загрузить Excel'}
|
||||||
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Таблицы - теперь занимают всё оставшееся пространство */}
|
{/* Таблицы - теперь занимают всё оставшееся пространство */}
|
||||||
<div className="flex-1 min-h-0">
|
<div className="min-h-0 flex-1">
|
||||||
<DualSpreadsheet
|
<DualSpreadsheet templateData={{ Report: excelData }} />
|
||||||
templateData={{ Report: excelData }}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user