From a13bbc7a6832e23c30ba0139ed0050a9d91a4556 Mon Sep 17 00:00:00 2001 From: tlartem Date: Thu, 24 Jul 2025 13:55:43 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BD=D0=BE=D0=B2=D1=8B=D0=B5=20=D1=81=D1=82?= =?UTF-8?q?=D0=B8=D0=BB=D0=B8=20=D1=80=D0=B5=D0=B4=D0=B0=D0=BA=D1=82=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20=D1=8D=D0=BB=D0=B5?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../StandardsElement/StandardsEditor.tsx | 67 +- .../TemplateManager/ElementConstructor.tsx | 729 +++++++++--------- .../TemplateManager/VisualLayoutEditor.tsx | 16 +- 3 files changed, 424 insertions(+), 388 deletions(-) diff --git a/src/component/StandardsElement/StandardsEditor.tsx b/src/component/StandardsElement/StandardsEditor.tsx index de384b6..5737b87 100644 --- a/src/component/StandardsElement/StandardsEditor.tsx +++ b/src/component/StandardsElement/StandardsEditor.tsx @@ -44,35 +44,28 @@ export const StandardsEditor: React.FC = ({ } return ( -
+
- +
+ + +

- Настройте параметры для выбора измерительных эталонов -

-
- -
- - -

- Максимальное количество эталонов для выбора + Максимальное количество эталонов для выбора (0-12)

@@ -105,16 +98,6 @@ export const StandardsEditor: React.FC = ({

Эталоны, которые будут выбраны по умолчанию при создании протокола

- - setIsStandardsDialogOpen(false)} - value={safeConfig.selectedStandards} - onChange={standardIds => { - updateConfig({ selectedStandards: standardIds }) - }} - maxItems={safeConfig.maxItems} - />
@@ -130,6 +113,16 @@ export const StandardsEditor: React.FC = ({
+ + setIsStandardsDialogOpen(false)} + value={safeConfig.selectedStandards} + onChange={standardIds => { + updateConfig({ selectedStandards: standardIds }) + }} + maxItems={safeConfig.maxItems} + /> ) } diff --git a/src/component/TemplateManager/ElementConstructor.tsx b/src/component/TemplateManager/ElementConstructor.tsx index 0869707..a419e97 100644 --- a/src/component/TemplateManager/ElementConstructor.tsx +++ b/src/component/TemplateManager/ElementConstructor.tsx @@ -11,9 +11,20 @@ import { Template, TemplateElement, } from '@/type/template' -import { Plus, Trash2 } from 'lucide-react' +import { + AlertTriangle, + Eye, + HelpCircle, + Plus, + Settings2, + Tag, + Target, + Trash2, + Type, +} from 'lucide-react' import React, { useState } from 'react' import { Button } from '../ui/button' +import { Card, CardContent, CardHeader, CardTitle } from '../ui/card' import { Checkbox } from '../ui/checkbox' import { Dialog, @@ -23,6 +34,7 @@ import { DialogTitle, } from '../ui/dialog' import { Input } from '../ui/input' +import { Label } from '../ui/label' import { Select, SelectContent, @@ -30,6 +42,7 @@ import { SelectTrigger, SelectValue, } from '../ui/select' +import { Separator } from '../ui/separator' import { ElementFormulaBar } from './ElementFormulaBar' import { VisualLayoutEditor } from './VisualLayoutEditor' @@ -79,72 +92,92 @@ const CellTargetEditor: React.FC<{ } return ( -
-
- - -
-
- {targets.map((target, index) => ( -
-
-
- - -
-
- - handleCellInputChange(index, e.target.value)} - className="h-7 text-xs" - /> -
-
- + + +
+
+ + Целевые ячейки +
+ +
+
+ +
+ {targets.map((target, index) => ( +
+ {/* Кнопки выбора листа */} +
+ + +
+ + {/* Поле ввода ячейки */} + handleCellInputChange(index, e.target.value)} + className="h-6 w-16 px-2 text-xs" + /> + + {/* Кнопка удаления */} + +
+ ))} + + {targets.length === 0 && ( +
+ +

+ Добавьте ячейки для связи с элементом +

+

+ Например: A1, B5, C10 +

+
+ )} +
+ + {targets.length > 0 && ( +
+ + Всего ячеек: {targets.length}
- ))} - {targets.length === 0 && ( -

- Добавьте ячейки для связи с элементом -

)} -
-
+ + ) } @@ -156,42 +189,40 @@ const ElementPreview: React.FC<{ element: Partial }> = ({ ? getElementDefinition(element.type) : null - if (elementDefinition && elementDefinition.Preview) { - return - } - - // Fallback для неизвестных типов элементов return ( -
-
Предпросмотр
-
- {element.label && ( - - )} - -
- {element.type - ? `Элемент типа "${element.type}" не найден` - : 'Выберите тип элемента'} + + +
+ + Предпросмотр элемента
+
+ +
+ {elementDefinition && elementDefinition.Preview ? ( + + ) : ( +
+ {element.label && ( + + )} - {element.targetCells && element.targetCells.length > 0 && ( -
- {element.targetCells.map((target, index) => ( - - {target.sheet}!{target.cell} - - ))} -
- )} -
-
+
+ + {element.type + ? `Элемент типа "${element.type}" не найден` + : 'Выберите тип элемента'} +
+
+ )} +
+ + ) } @@ -228,179 +259,113 @@ const ElementForm: React.FC = ({ formData, setFormData }) => { } const needsOptions = - formData.type === 'select' || - formData.type === 'radio' || - formData.type === 'button_group' + formData.type === 'select' || formData.type === 'button_group' - // Если есть определение элемента в реестре, используем его редактор - if (elementDefinition && elementDefinition.Editor) { - return ( -
- {/* Форма настроек */} -
-
- - -
- -
-
- - - setFormData(prev => ({ ...prev, label: e.target.value })) - } - className="h-9" - /> -
- -
- - - setFormData(prev => ({ - ...prev, - placeholder: e.target.value, - })) - } - className="h-9" - /> -
-
- -
- - setFormData(prev => ({ ...prev, required: !!checked })) - } - /> - -
- - - setFormData(prev => ({ ...prev, targetCells: targets })) - } - /> - - {/* Специфичный редактор элемента */} - - setFormData(prev => ({ ...prev, ...newConfig })) - } - /> -
- - {/* Live Preview */} -
- -
-
- ) - } - - // Fallback для старых элементов return ( -
- {/* Форма настроек */} -
-
- - { + const newType = value as ElementType + const elementDefinition = getElementDefinition(newType) - setFormData(prev => { - const updates: Partial = { - type: newType, - // Сбрасываем targetCells при смене типа - targetCells: [], + setFormData(prev => { + const updates: Partial = { + type: newType, + targetCells: [], + } + + if (elementDefinition && elementDefinition.defaultConfig) { + Object.assign(updates, elementDefinition.defaultConfig) + } + + return { ...prev, ...updates } + }) + }} + > + + + + + {getAvailableElementTypes().map(({ type, label, icon }) => ( + +
+ {icon} + {label} +
+
+ ))} +
+ +
+ +
+
+ + + setFormData(prev => ({ ...prev, label: e.target.value })) + } + className="h-9" + /> +
+ +
+ + + setFormData(prev => ({ + ...prev, + placeholder: e.target.value, + })) + } + className="h-9" + /> +
+
+ +
+ + setFormData(prev => ({ ...prev, required: !!checked })) } + /> + +
+ + - // Применяем конфиг по умолчанию из определения элемента - if (elementDefinition && elementDefinition.defaultConfig) { - Object.assign(updates, elementDefinition.defaultConfig) - } - - return { ...prev, ...updates } - }) - }} - > - - - - - {getAvailableElementTypes().map(({ type, label, icon }) => ( - -
- {icon} - {label} -
-
- ))} -
- -
- -
- - - setFormData(prev => ({ ...prev, label: e.target.value })) - } - className="h-9" - /> -
- + {/* Целевые ячейки */} @@ -408,56 +373,94 @@ const ElementForm: React.FC = ({ formData, setFormData }) => { } /> + {/* Специфичный редактор элемента */} + {elementDefinition && elementDefinition.Editor && ( + + +
+ + + Дополнительные настройки + +
+
+ + + setFormData(prev => ({ ...prev, ...newConfig })) + } + /> + +
+ )} + + {/* Варианты для select/radio/button_group */} {needsOptions && ( -
-
- - -
-
- {formData.options?.map((option, index) => ( -
- - handleUpdateOption(index, 'value', e.target.value) - } - className="h-7 text-xs" - /> - - handleUpdateOption(index, 'label', e.target.value) - } - className="h-7 text-xs" - /> - + + +
+
+ + Варианты выбора
- ))} -
-
+ +
+ + +
+ {formData.options?.map((option, index) => ( +
+ + handleUpdateOption(index, 'value', e.target.value) + } + className="h-8 text-xs" + /> + + handleUpdateOption(index, 'label', e.target.value) + } + className="h-8 text-xs" + /> + +
+ ))} + {(!formData.options || formData.options.length === 0) && ( +
+ +

+ Добавьте варианты выбора +

+
+ )} +
+
+ )}
- {/* Live Preview */} -
+ {/* Предпросмотр */} +
@@ -661,11 +664,6 @@ export const ElementConstructor: React.FC = ({ resetForm() } - // const reordered = (reorderedElements: TemplateElement[]) => { - // onElementsReorder(reorderedElements); - // onLayoutSettingsChange({ ...layoutSettings, showGrid: true }); - // }; - return (
= ({ {/* Диалог добавления элемента */} - - - Добавить новый элемент + + + + + Добавить новый элемент + - Настройте параметры нового элемента формы. + Создайте и настройте новый элемент формы. Вы можете увидеть + предпросмотр справа. - -
- - +
+ +
+ +
+
+ {!formData.label && ( +
+ + Заполните обязательные поля +
+ )} +
+
+ + +
@@ -730,21 +749,43 @@ export const ElementConstructor: React.FC = ({ {/* Диалог редактирования элемента */} {editingElement && ( handleCancelEdit()}> - - - Редактировать элемент + + + + + Редактировать элемент + - Измените параметры элемента формы. + Измените параметры элемента " + {editingElement.label || editingElement.name}". - -
- - +
+ +
+ +
+
+ {!formData.label && ( +
+ + Заполните обязательные поля +
+ )} +
+
+ + +
diff --git a/src/component/TemplateManager/VisualLayoutEditor.tsx b/src/component/TemplateManager/VisualLayoutEditor.tsx index afd6765..aeedbd7 100644 --- a/src/component/TemplateManager/VisualLayoutEditor.tsx +++ b/src/component/TemplateManager/VisualLayoutEditor.tsx @@ -150,14 +150,16 @@ const DraggableElement: React.FC = React.memo( > {/* Controls (only visible when selected or hovered) */}
{/* Индикатор ячеек */} {element.targetCells && element.targetCells.length > 0 && (
- +
+ +
Целевые ячейки:
{element.targetCells.map((cell, i) => ( @@ -177,19 +179,19 @@ const DraggableElement: React.FC = React.memo( )}
@@ -286,7 +288,7 @@ export const VisualLayoutEditor: React.FC = ({ onClick={handleCanvasClick} >