156 lines
6.5 KiB
TypeScript
156 lines
6.5 KiB
TypeScript
import { Badge } from '@/component/ui/badge'
|
||
import { Button } from '@/component/ui/button'
|
||
import { Label } from '@/component/ui/label'
|
||
import { ElementDefinition } from '@/entitiy/element/model/interface'
|
||
import { CellTarget } from '@/type/template'
|
||
import { FileText, Settings, Weight } from 'lucide-react'
|
||
import { useEffect, useState } from 'react'
|
||
import { useStandards } from './hooks'
|
||
import { getStandardById } from './mockStandards'
|
||
import { StandardsEditor } from './StandardsEditor'
|
||
import { StandardsPreview } from './StandardsPreview'
|
||
import { StandardsSelector } from './StandardsSelector'
|
||
|
||
interface StandardsConfig {
|
||
sheet: string // Лист Excel
|
||
startCell: string // Первая ячейка (например "A10")
|
||
maxItems: number // Обычно 7
|
||
targetCells: CellTarget[]
|
||
selectedStandards?: string[] // Выбранные эталоны (ID)
|
||
}
|
||
|
||
export const standardsDefinition: ElementDefinition<StandardsConfig, string[]> =
|
||
{
|
||
type: 'standards',
|
||
label: 'Измерительные эталоны',
|
||
icon: <Weight className="h-4 w-4" />,
|
||
version: 1,
|
||
defaultConfig: {
|
||
sheet: 'L',
|
||
startCell: 'A1',
|
||
maxItems: 7,
|
||
targetCells: [],
|
||
selectedStandards: [], // По умолчанию пустой массив
|
||
},
|
||
Editor: StandardsEditor,
|
||
Preview: StandardsPreview, // Использует моки для демонстрации
|
||
Render: ({ config, value, onChange }) => {
|
||
const [isStandardsDialogOpen, setIsStandardsDialogOpen] = useState(false)
|
||
const [selectedStandardIds, setSelectedStandardIds] = useState<string[]>(
|
||
// Инициализируем из config.selectedStandards или value
|
||
config.selectedStandards || (Array.isArray(value) ? value : [])
|
||
)
|
||
|
||
// Получаем реальные данные эталонов
|
||
const { data: standards = [] } = useStandards()
|
||
|
||
// Синхронизируем selectedStandardIds с config и value
|
||
useEffect(() => {
|
||
const standards =
|
||
config.selectedStandards || (Array.isArray(value) ? value : [])
|
||
setSelectedStandardIds(standards)
|
||
}, [config.selectedStandards, value])
|
||
|
||
// Получаем данные выбранных эталонов из реального API
|
||
const selectedStandardsData = selectedStandardIds
|
||
.map(id => standards.find(s => s.id === id))
|
||
.filter(Boolean)
|
||
|
||
return (
|
||
<div className="space-y-2">
|
||
{/* Блок с измерительными эталонами */}
|
||
<div>
|
||
<div className="mb-2 flex items-center justify-between">
|
||
<Label className="text-sm font-medium">Эталоны</Label>
|
||
<Button
|
||
variant="outline"
|
||
size="sm"
|
||
onClick={() => setIsStandardsDialogOpen(true)}
|
||
className="h-7 px-2"
|
||
>
|
||
<Settings className="mr-1 h-3 w-3" />
|
||
Настроить
|
||
</Button>
|
||
</div>
|
||
|
||
<div className="space-y-2">
|
||
{selectedStandardsData.length > 0 ? (
|
||
<div className="space-y-1">
|
||
{selectedStandardsData.map(
|
||
(standard, index) =>
|
||
standard && (
|
||
<div
|
||
key={standard.id}
|
||
className="flex items-center gap-2 rounded-md bg-muted/30 p-2 text-sm"
|
||
>
|
||
<div className="flex h-4 w-4 shrink-0 items-center justify-center rounded-full bg-primary/10">
|
||
<span className="text-xs font-medium text-primary">
|
||
{index + 1}
|
||
</span>
|
||
</div>
|
||
<div className="min-w-0 flex-1">
|
||
<div className="truncate text-xs font-medium text-foreground">
|
||
{standard.name}
|
||
</div>
|
||
<div className="mt-0.5 flex items-center gap-1 text-xs text-muted-foreground">
|
||
<FileText className="h-2.5 w-2.5" />
|
||
<span className="hidden text-xs sm:inline">
|
||
{standard.registry_number}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<Badge
|
||
variant="secondary"
|
||
className="shrink-0 px-1.5 py-0.5 text-xs"
|
||
>
|
||
{standard.range}
|
||
</Badge>
|
||
</div>
|
||
)
|
||
)}
|
||
</div>
|
||
) : (
|
||
<div className="rounded-md border border-dashed bg-muted/30 p-3 text-center text-sm text-muted-foreground">
|
||
<Settings className="mx-auto mb-1 h-4 w-4 opacity-50" />
|
||
Эталоны не выбраны
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
<StandardsSelector
|
||
isOpen={isStandardsDialogOpen}
|
||
onClose={() => setIsStandardsDialogOpen(false)}
|
||
value={selectedStandardIds}
|
||
onChange={standardIds => {
|
||
setSelectedStandardIds(standardIds)
|
||
// Сохраняем ID эталонов в value для записи в Excel
|
||
onChange?.(standardIds)
|
||
}}
|
||
maxItems={config.maxItems}
|
||
/>
|
||
</div>
|
||
)
|
||
},
|
||
mapToCells: cfg => {
|
||
// Используем только ячейки, указанные пользователем в targetCells
|
||
return cfg.targetCells || []
|
||
},
|
||
mapToCellValues: (cfg, value) => {
|
||
// Используем targetCells из конфигурации
|
||
const cells = cfg.targetCells || []
|
||
|
||
// Конвертируем ID эталонов в их названия для записи в Excel
|
||
// Здесь используем моки для быстрого доступа к данным при записи в Excel
|
||
const standardIds = Array.isArray(value) ? value : []
|
||
const standardNames = standardIds.map(
|
||
id => getStandardById(id)?.name || ''
|
||
)
|
||
|
||
return cells.map((target, idx) => ({
|
||
target,
|
||
value: standardNames[idx] || '', // Берем значение по индексу или пустую строку
|
||
}))
|
||
},
|
||
}
|