архитектура добавления элементов, список эталонов
This commit is contained in:
166
ARCHITECTURE.md
Normal file
166
ARCHITECTURE.md
Normal file
@@ -0,0 +1,166 @@
|
||||
# Архитектура системы элементов
|
||||
|
||||
## Обзор
|
||||
|
||||
Система была переработана для обеспечения универсальности и расширяемости. Теперь все элементы регистрируются в едином реестре, что позволяет легко добавлять новые типы элементов без изменения основного кода.
|
||||
|
||||
## Структура
|
||||
|
||||
### 1. Реестр элементов (`src/lib/element-registry.ts`)
|
||||
|
||||
Центральный реестр, который содержит все определения элементов:
|
||||
|
||||
```typescript
|
||||
export interface ElementDefinition<Config = any, Value = any> {
|
||||
type: ElementType; // Уникальный тип элемента
|
||||
label: string; // Человекочитаемое имя
|
||||
icon: ReactNode; // Иконка для UI
|
||||
|
||||
Editor: React.FC<{ config: Config; onChange(c: Config): void }>;
|
||||
Preview: React.FC<{ config: Config }>;
|
||||
Render: React.FC<{ config: Config; value: Value; onChange?(v: Value): void }>;
|
||||
|
||||
defaultConfig: Config; // Конфигурация по умолчанию
|
||||
mapToCells?(config: Config): CellTarget[]; // Маппинг на ячейки Excel
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Базовые элементы (`src/components/BasicElements/`)
|
||||
|
||||
Стандартные элементы формы:
|
||||
- `TextElement.tsx` - текстовое поле
|
||||
- `SelectElement.tsx` - выпадающий список
|
||||
- `NumberElement.tsx` - числовое поле
|
||||
- `DateElement.tsx` - поле даты
|
||||
- `TextareaElement.tsx` - многострочный текст
|
||||
- `CheckboxElement.tsx` - чекбокс
|
||||
- `RadioElement.tsx` - радиокнопки
|
||||
|
||||
### 3. Специальные элементы (`src/components/StandardsElement/`)
|
||||
|
||||
Сложные элементы с собственной логикой:
|
||||
- `StandardsDialog.tsx` - диалог выбора эталонов
|
||||
- `StandardsEditor.tsx` - редактор настроек
|
||||
- `StandardsPreview.tsx` - превью элемента
|
||||
- `definition.tsx` - определение элемента
|
||||
|
||||
## Как добавить новый элемент
|
||||
|
||||
### 1. Создать компоненты
|
||||
|
||||
```typescript
|
||||
// MyElement/MyElementEditor.tsx
|
||||
export const MyElementEditor: React.FC<{ config: MyConfig; onChange: (config: MyConfig) => void }> = ({ config, onChange }) => {
|
||||
// Логика редактирования
|
||||
};
|
||||
|
||||
// MyElement/MyElementPreview.tsx
|
||||
export const MyElementPreview: React.FC<{ config: MyConfig }> = ({ config }) => {
|
||||
// Логика превью
|
||||
};
|
||||
|
||||
// MyElement/MyElementRender.tsx
|
||||
export const MyElementRender: React.FC<{ config: MyConfig; value: string; onChange?: (value: string) => void }> = ({ config, value, onChange }) => {
|
||||
// Логика рендеринга
|
||||
};
|
||||
```
|
||||
|
||||
### 2. Создать определение
|
||||
|
||||
```typescript
|
||||
// MyElement/definition.tsx
|
||||
export const myElementDefinition: ElementDefinition<MyConfig, string> = {
|
||||
type: "my_element",
|
||||
label: "Мой элемент",
|
||||
icon: <MyIcon className="h-4 w-4" />,
|
||||
defaultConfig: {
|
||||
// конфигурация по умолчанию
|
||||
},
|
||||
Editor: MyElementEditor,
|
||||
Preview: MyElementPreview,
|
||||
Render: MyElementRender,
|
||||
mapToCells: (config) => {
|
||||
// логика маппинга на ячейки Excel
|
||||
return [];
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### 3. Зарегистрировать в реестре
|
||||
|
||||
```typescript
|
||||
// src/lib/element-registry-init.ts
|
||||
import { myElementDefinition } from "@/components/MyElement/definition";
|
||||
|
||||
export function initializeElementRegistry() {
|
||||
// ... существующие элементы
|
||||
registerElement("my_element", myElementDefinition);
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Добавить тип в types/template.ts
|
||||
|
||||
```typescript
|
||||
export type ElementType =
|
||||
| 'text' | 'select' | 'radio' | 'checkbox' | 'number'
|
||||
| 'textarea' | 'date' | 'standards'
|
||||
| 'my_element'; // <-- новый тип
|
||||
```
|
||||
|
||||
## Особенности элемента "Измерительные эталоны"
|
||||
|
||||
### Конфигурация
|
||||
|
||||
```typescript
|
||||
interface StandardsConfig {
|
||||
registryNumber: string; // ГРСИ
|
||||
sheet: string; // Лист Excel
|
||||
startCell: string; // Первая ячейка (например "A10")
|
||||
maxItems: number; // Обычно 7
|
||||
targetCells: CellTarget[];
|
||||
}
|
||||
```
|
||||
|
||||
### Логика маппинга
|
||||
|
||||
Элемент автоматически генерирует ячейки для записи выбранных эталонов:
|
||||
|
||||
```typescript
|
||||
mapToCells: (cfg) => {
|
||||
const res: CellTarget[] = [];
|
||||
const parsed = parseCellAddress(cfg.startCell);
|
||||
|
||||
if (!parsed) return res;
|
||||
|
||||
const { column, row } = parsed;
|
||||
|
||||
// Генерируем ячейки вертикально (A1, A2, A3...)
|
||||
for (let i = 0; i < cfg.maxItems; i++) {
|
||||
res.push({
|
||||
sheet: cfg.sheet,
|
||||
cell: `${column}${row + i}`,
|
||||
displayName: `Эталон ${i + 1}`,
|
||||
});
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
### Ограничения
|
||||
|
||||
- Максимум 7 эталонов (настраивается)
|
||||
- Автоматическое заполнение только выбранных позиций
|
||||
- Оставшиеся ячейки остаются пустыми
|
||||
|
||||
## Преимущества новой архитектуры
|
||||
|
||||
1. **Расширяемость**: Добавление новых элементов требует минимальных изменений
|
||||
2. **Универсальность**: Единый интерфейс для всех элементов
|
||||
3. **Типобезопасность**: TypeScript обеспечивает корректность типов
|
||||
4. **Модульность**: Каждый элемент самодостаточен
|
||||
5. **Переиспользование**: Компоненты можно использовать в разных контекстах
|
||||
|
||||
## Миграция
|
||||
|
||||
Старые элементы продолжают работать через fallback-логику в `ElementConstructor.tsx`. Для полной миграции рекомендуется перевести все элементы на новую архитектуру.
|
||||
Reference in New Issue
Block a user