Files
DataMate/frontend/src/pages/DataAnnotation/Template/VisualTemplateBuilder.tsx
Jerry Yan dbf8ec53dd style(ui): 统一预览模态框宽度为响应式尺寸
- 将 CreateAnnotationTaskDialog 中的预览模态框宽度从固定像素改为 80vw
- 将 VisualTemplateBuilder 中的预览抽屉宽度从 600px 改为 80vw
- 将 PreviewPromptModal 中的模态框宽度从 800px 改为 80vw
- 将 Overview 组件中的文本和媒体预览宽度统一改为 80vw
- 将 KnowledgeSetDetail 中的文本和媒体预览宽度统一改为 80vw
- 移除原来固定的像素值,使用响应式单位提升用户体验
2026-02-01 12:49:56 +08:00

163 lines
5.8 KiB
TypeScript

import React, { useState } from "react";
import {
Card,
Button,
Space,
Row,
Col,
Drawer,
Typography,
message,
} from "antd";
import {
PlusOutlined,
EyeOutlined,
CodeOutlined,
AppstoreOutlined,
} from "@ant-design/icons";
import { TagBrowser } from "./components";
const { Paragraph } = Typography;
const PREVIEW_DRAWER_WIDTH = "80vw";
interface VisualTemplateBuilderProps {
onSave?: (templateCode: string) => void;
}
/**
* Visual Template Builder
* Provides a drag-and-drop interface for building Label Studio templates
*/
const VisualTemplateBuilder: React.FC<VisualTemplateBuilderProps> = ({
onSave,
}) => {
const [drawerVisible, setDrawerVisible] = useState(false);
const [previewVisible, setPreviewVisible] = useState(false);
const [selectedTags, setSelectedTags] = useState<
Array<{ name: string; category: "object" | "control" }>
>([]);
const handleTagSelect = (tagName: string, category: "object" | "control") => {
message.info(`选择了 ${category === "object" ? "对象" : "控件"}: ${tagName}`);
setSelectedTags([...selectedTags, { name: tagName, category }]);
setDrawerVisible(false);
};
const handleSave = () => {
// TODO: Generate template XML from selectedTags
message.success("模板保存成功");
onSave?.("<View><!-- Generated template --></View>");
};
return (
<div style={{ padding: "24px" }}>
<Row gutter={16}>
<Col span={24}>
<Card
title="可视化模板构建器"
extra={
<Space>
<Button
icon={<AppstoreOutlined />}
onClick={() => setDrawerVisible(true)}
>
</Button>
<Button
icon={<CodeOutlined />}
onClick={() => setPreviewVisible(true)}
>
</Button>
<Button
type="primary"
icon={<EyeOutlined />}
onClick={handleSave}
>
</Button>
</Space>
}
>
<div
style={{
minHeight: "400px",
border: "2px dashed #d9d9d9",
borderRadius: "8px",
padding: "24px",
textAlign: "center",
}}
>
{selectedTags.length === 0 ? (
<div>
<Paragraph type="secondary">
"浏览标签"
</Paragraph>
<Button
type="dashed"
icon={<PlusOutlined />}
onClick={() => setDrawerVisible(true)}
>
</Button>
</div>
) : (
<Space direction="vertical" size="large">
{selectedTags.map((tag, index) => (
<Card key={index} size="small">
<div>
{tag.category === "object" ? "对象" : "控件"}: {tag.name}
</div>
</Card>
))}
</Space>
)}
</div>
</Card>
</Col>
</Row>
<Drawer
title="标签浏览器"
placement="right"
width={800}
open={drawerVisible}
onClose={() => setDrawerVisible(false)}
>
<TagBrowser onTagSelect={handleTagSelect} />
</Drawer>
<Drawer
title="模板代码预览"
placement="right"
width={PREVIEW_DRAWER_WIDTH}
open={previewVisible}
onClose={() => setPreviewVisible(false)}
>
<pre
style={{
background: "#f5f5f5",
padding: "16px",
borderRadius: "4px",
overflow: "auto",
}}
>
<code>
{`<View>
<!-- 根据选择的标签生成的模板代码 -->
${selectedTags
.map(
(tag) =>
`<${tag.name}${tag.category === "object" ? ' name="obj" value="$data"' : ' name="ctrl" toName="obj"'} />`
)
.join("\n ")}
</View>`}
</code>
</pre>
</Drawer>
</div>
);
};
export default VisualTemplateBuilder;