import React from 'react'; import { ScrollArea } from '../../ui/scroll-area'; import { Input } from '../../ui/input'; import { Button } from '../../ui/button'; import { Label } from '../../ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../../ui/select'; import { Switch } from '../../ui/switch'; import type { LabelTemplate, LabelElement, LabelType, Unit, Rotation, Border, AppliedLocation, } from '../../../types/labelTemplate'; import type { LocationDto } from '../../../types/location'; import { Checkbox } from '../../ui/checkbox'; interface PropertiesPanelProps { template: LabelTemplate; selectedElement: LabelElement | null; onTemplateChange: (patch: Partial) => void; onElementChange: (id: string, patch: Partial) => void; onDeleteElement?: (id: string) => void; /** 门店列表:appliedLocation=SPECIFIED 时勾选 */ locations?: LocationDto[]; /** 编辑已有模板时禁止修改 Template Code */ readOnlyTemplateCode?: boolean; } export function PropertiesPanel({ template, selectedElement, onTemplateChange, onElementChange, onDeleteElement, locations = [], readOnlyTemplateCode = false, }: PropertiesPanelProps) { if (selectedElement) { return (
Properties (Element)
onElementChange(selectedElement.id, { x: Number(e.target.value) || 0, }) } className="h-8 text-sm" />
onElementChange(selectedElement.id, { y: Number(e.target.value) || 0, }) } className="h-8 text-sm" />
onElementChange(selectedElement.id, { width: Math.max(1, Number(e.target.value) || 0), }) } className="h-8 text-sm" />
onElementChange(selectedElement.id, { height: Math.max(1, Number(e.target.value) || 0), }) } className="h-8 text-sm" />
onElementChange(selectedElement.id, { config: { ...selectedElement.config, ...config } }) } /> {onDeleteElement && (
)}
); } return (
Properties (Template)
onTemplateChange({ id: e.target.value.trim() })} className="h-8 text-sm mt-1" placeholder="e.g. TPL_TEST_001" />
onTemplateChange({ name: e.target.value })} className="h-8 text-sm mt-1" />
{template.appliedLocation === "SPECIFIED" && (
{locations.length === 0 ? (

No locations loaded.

) : ( locations.map((loc) => { const checked = (template.appliedLocationIds ?? []).includes(loc.id); return ( ); }) )}
)}
onTemplateChange({ width: Math.max(0.1, Number(e.target.value) || 0) }) } className="h-8 text-sm" />
onTemplateChange({ height: Math.max(0.1, Number(e.target.value) || 0) }) } className="h-8 text-sm" />
onTemplateChange({ showRuler: v })} />
onTemplateChange({ showGrid: v })} />
); } function ElementConfigFields({ element, onChange, }: { element: LabelElement; onChange: (config: Record) => void; }) { const cfg = element.config as Record; const update = (key: string, value: unknown) => onChange({ [key]: value }); switch (element.type) { case 'TEXT_STATIC': case 'TEXT_PRODUCT': case 'TEXT_PRICE': return ( <>
update('text', e.target.value)} className="h-8 text-sm mt-1" />
update('prefix', e.target.value)} className="h-8 text-sm mt-1" />
update('fontSize', Number(e.target.value) || 14)} className="h-8 text-sm mt-1" />
); case 'BARCODE': return ( <>
update('data', e.target.value)} className="h-8 text-sm mt-1" />
update('showText', v)} />
); case 'QRCODE': return (
update('data', e.target.value)} className="h-8 text-sm mt-1" />
); case 'IMAGE': return ( <>
update('src', e.target.value)} className="h-8 text-sm mt-1" placeholder="输入图片URL或路径" />
); case 'DATE': return ( <>
update('format', e.target.value)} className="h-8 text-sm mt-1" placeholder="YYYY-MM-DD" />
update('offsetDays', Number(e.target.value) || 0)} className="h-8 text-sm mt-1" />
); case 'TIME': return (
update('format', e.target.value)} className="h-8 text-sm mt-1" placeholder="HH:mm" />
); case 'DURATION': return ( <>
update('format', e.target.value)} className="h-8 text-sm mt-1" placeholder="YYYY-MM-DD" />
update('offsetDays', Number(e.target.value) || 3)} className="h-8 text-sm mt-1" />
); case 'WEIGHT': return ( <>
update('value', Number(e.target.value) || 0)} className="h-8 text-sm mt-1" />
); case 'WEIGHT_PRICE': return ( <>
update('unitPrice', Number(e.target.value) || 0)} className="h-8 text-sm mt-1" />
update('weight', Number(e.target.value) || 0)} className="h-8 text-sm mt-1" />
update('currency', e.target.value)} className="h-8 text-sm mt-1" />
); case 'NUTRITION': return ( <>
update('calories', Number(e.target.value) || 0)} className="h-8 text-sm mt-1" />
update('fat', e.target.value)} className="h-8 text-sm mt-1" />
update('protein', e.target.value)} className="h-8 text-sm mt-1" />
update('carbs', e.target.value)} className="h-8 text-sm mt-1" />
); case 'BLANK': return (
Blank spacer; no configuration needed.
); default: return (
Config for {element.type} (edit in code if needed)
); } }