feat: 开发编辑工作台
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
import { ColorPicker, Empty, Input, InputNumber, Select, Slider, Switch } from 'antd';
|
||||
import type { AnnotationElement } from '@/types/annotation';
|
||||
import { FONT_FAMILIES } from '@/utils/annotation';
|
||||
|
||||
interface ElementPropsPanelProps {
|
||||
element: AnnotationElement | null;
|
||||
onUpdate: (id: number, patch: Partial<AnnotationElement>) => void;
|
||||
}
|
||||
|
||||
const FONT_OPTIONS = FONT_FAMILIES.map((f) => ({ value: f, label: f }));
|
||||
|
||||
const ElementPropsPanel = ({ element, onUpdate }: ElementPropsPanelProps) => {
|
||||
if (!element) {
|
||||
return (
|
||||
<div className="props-empty">
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="未选中元素" />
|
||||
<p className="props-hint">点击画布上的文字或标尺进行编辑</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const update = (patch: Partial<AnnotationElement>) => onUpdate(element.id, patch);
|
||||
|
||||
return (
|
||||
<div className="props-panel">
|
||||
{element.type === 'text' ? (
|
||||
<>
|
||||
<div className="prop-row">
|
||||
<span className="prop-label">内容</span>
|
||||
<Input.TextArea
|
||||
value={element.content}
|
||||
autoSize={{ minRows: 1, maxRows: 4 }}
|
||||
onChange={(e) => update({ content: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
<div className="prop-row">
|
||||
<span className="prop-label">字体</span>
|
||||
<Select
|
||||
value={element.fontFamily}
|
||||
options={FONT_OPTIONS}
|
||||
onChange={(v) => update({ fontFamily: v })}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
</div>
|
||||
<div className="prop-row">
|
||||
<span className="prop-label">字号</span>
|
||||
<InputNumber
|
||||
value={element.fontSize}
|
||||
min={8}
|
||||
onChange={(v) => update({ fontSize: v ?? 18 })}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
</div>
|
||||
<div className="prop-row">
|
||||
<span className="prop-label">加粗</span>
|
||||
<Switch checked={element.bold} onChange={(v) => update({ bold: v })} />
|
||||
</div>
|
||||
<div className="prop-row">
|
||||
<span className="prop-label">文字颜色</span>
|
||||
<ColorPicker
|
||||
value={element.color}
|
||||
onChange={(c) => update({ color: c.toHexString() })}
|
||||
/>
|
||||
</div>
|
||||
<div className="prop-row">
|
||||
<span className="prop-label">描边颜色</span>
|
||||
<ColorPicker
|
||||
value={element.strokeColor}
|
||||
onChange={(c) => update({ strokeColor: c.toHexString() })}
|
||||
/>
|
||||
</div>
|
||||
<div className="prop-row">
|
||||
<span className="prop-label">描边宽度</span>
|
||||
<InputNumber
|
||||
value={element.strokeWidth}
|
||||
min={0}
|
||||
onChange={(v) => update({ strokeWidth: v ?? 0 })}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="prop-row">
|
||||
<span className="prop-label">长度(px)</span>
|
||||
<InputNumber
|
||||
value={Math.round(element.length)}
|
||||
min={4}
|
||||
onChange={(v) => update({ length: v ?? 50 })}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
</div>
|
||||
<div className="prop-row">
|
||||
<span className="prop-label">线色</span>
|
||||
<ColorPicker
|
||||
value={element.lineColor}
|
||||
onChange={(c) => update({ lineColor: c.toHexString() })}
|
||||
/>
|
||||
</div>
|
||||
<div className="prop-row">
|
||||
<span className="prop-label">线宽</span>
|
||||
<InputNumber
|
||||
value={element.lineWidth}
|
||||
min={1}
|
||||
onChange={(v) => update({ lineWidth: v ?? 2 })}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
</div>
|
||||
<div className="prop-row">
|
||||
<span className="prop-label">T字大小</span>
|
||||
<InputNumber
|
||||
value={Math.round(element.tSize)}
|
||||
min={4}
|
||||
onChange={(v) => update({ tSize: v ?? 14 })}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
</div>
|
||||
<div className="prop-row">
|
||||
<span className="prop-label">标签文字</span>
|
||||
<Input value={element.label} onChange={(e) => update({ label: e.target.value })} />
|
||||
</div>
|
||||
<div className="prop-row">
|
||||
<span className="prop-label">标签颜色</span>
|
||||
<ColorPicker
|
||||
value={element.labelColor}
|
||||
onChange={(c) => update({ labelColor: c.toHexString() })}
|
||||
/>
|
||||
</div>
|
||||
<div className="prop-row">
|
||||
<span className="prop-label">标签字号</span>
|
||||
<InputNumber
|
||||
value={Math.round(element.labelFontSize)}
|
||||
min={8}
|
||||
onChange={(v) => update({ labelFontSize: v ?? 14 })}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="prop-row">
|
||||
<span className="prop-label">透明度</span>
|
||||
<Slider
|
||||
min={0.05}
|
||||
max={1}
|
||||
step={0.05}
|
||||
value={element.opacity}
|
||||
onChange={(v) => update({ opacity: v })}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
</div>
|
||||
<div className="prop-row">
|
||||
<span className="prop-label">旋转(°)</span>
|
||||
<InputNumber
|
||||
value={Math.round(element.rotation)}
|
||||
min={0}
|
||||
max={360}
|
||||
onChange={(v) => update({ rotation: (v ?? 0) % 360 })}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ElementPropsPanel;
|
||||
Reference in New Issue
Block a user