You've already forked DataMate
feat(DataAnnotation): 新增模板配置表单组件
- 实现了数据对象配置区域,支持添加、删除数据对象字段 - 添加了标签控件配置区域,支持多种控件类型的动态配置 - 集成了TagSelector组件用于对象类型和控件类型的选择 - 实现了表单验证规则,包括必填项和值格式校验 - 添加了动态选项渲染功能,根据控件类型显示相应配置项 - 实现了表单联动逻辑,支持对象选择和控件配置的关联 - 添加了用户友好的界面布局和交互提示功能
This commit is contained in:
@@ -4,21 +4,16 @@ import {
|
||||
Form,
|
||||
Input,
|
||||
Select,
|
||||
Button,
|
||||
Space,
|
||||
message,
|
||||
Divider,
|
||||
Card,
|
||||
Checkbox,
|
||||
} from "antd";
|
||||
import { PlusOutlined, MinusCircleOutlined } from "@ant-design/icons";
|
||||
import {
|
||||
createAnnotationTemplateUsingPost,
|
||||
updateAnnotationTemplateByIdUsingPut,
|
||||
} from "../annotation.api";
|
||||
import type { AnnotationTemplate } from "../annotation.model";
|
||||
import { DataTypeMap, ClassificationMap, AnnotationTypeMap } from "../annotation.const";
|
||||
import TagSelector from "./components/TagSelector";
|
||||
import TemplateConfigurationForm from "../../components/TemplateConfigurationForm";
|
||||
|
||||
const { TextArea } = Input;
|
||||
const { Option } = Select;
|
||||
@@ -113,10 +108,6 @@ const TemplateForm: React.FC<TemplateFormProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const needsOptions = (type: string) => {
|
||||
return ["Choices", "RectangleLabels", "PolygonLabels", "Labels"].includes(type);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={mode === "create" ? "创建模板" : "编辑模板"}
|
||||
@@ -199,222 +190,7 @@ const TemplateForm: React.FC<TemplateFormProps> = ({
|
||||
</Form.Item>
|
||||
</Space>
|
||||
|
||||
<Divider>数据对象</Divider>
|
||||
|
||||
<Form.List name="objects">
|
||||
{(fields, { add, remove }) => (
|
||||
<>
|
||||
{fields.map((field) => (
|
||||
<Card key={field.key} size="small" style={{ marginBottom: 8 }}>
|
||||
<Space align="start" style={{ width: "100%" }}>
|
||||
<Form.Item
|
||||
{...field}
|
||||
label="名称"
|
||||
name={[field.name, "name"]}
|
||||
rules={[{ required: true, message: "必填" }]}
|
||||
style={{ marginBottom: 0, width: 150 }}
|
||||
>
|
||||
<Input placeholder="例如:image" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
{...field}
|
||||
label="类型"
|
||||
name={[field.name, "type"]}
|
||||
rules={[{ required: true, message: "必填" }]}
|
||||
style={{ marginBottom: 0, width: 150 }}
|
||||
>
|
||||
<TagSelector type="object" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
{...field}
|
||||
label="值"
|
||||
name={[field.name, "value"]}
|
||||
rules={[
|
||||
{ required: true, message: "必填" },
|
||||
{ pattern: /^\$/, message: "必须以 $ 开头" },
|
||||
]}
|
||||
style={{ marginBottom: 0, width: 150 }}
|
||||
>
|
||||
<Input placeholder="$image" />
|
||||
</Form.Item>
|
||||
|
||||
{fields.length > 1 && (
|
||||
<MinusCircleOutlined
|
||||
style={{ marginTop: 30, color: "red" }}
|
||||
onClick={() => remove(field.name)}
|
||||
/>
|
||||
)}
|
||||
</Space>
|
||||
</Card>
|
||||
))}
|
||||
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
|
||||
添加对象
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</Form.List>
|
||||
|
||||
<Divider>标签控件</Divider>
|
||||
|
||||
<Form.List name="labels">
|
||||
{(fields, { add, remove }) => (
|
||||
<>
|
||||
{fields.map((field) => (
|
||||
<Card
|
||||
key={field.key}
|
||||
size="small"
|
||||
style={{ marginBottom: 12 }}
|
||||
title={
|
||||
<Space>
|
||||
<span>控件 {fields.indexOf(field) + 1}</span>
|
||||
<Form.Item noStyle shouldUpdate>
|
||||
{() => {
|
||||
const controlType = form.getFieldValue(["labels", field.name, "type"]);
|
||||
const fromName = form.getFieldValue(["labels", field.name, "fromName"]);
|
||||
if (controlType || fromName) {
|
||||
return (
|
||||
<span style={{ fontSize: 12, fontWeight: 'normal', color: '#999' }}>
|
||||
({fromName || '未命名'} - {controlType || '未设置类型'})
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}}
|
||||
</Form.Item>
|
||||
</Space>
|
||||
}
|
||||
extra={
|
||||
<MinusCircleOutlined
|
||||
style={{ color: "red" }}
|
||||
onClick={() => remove(field.name)}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Space direction="vertical" style={{ width: "100%" }} size="middle">
|
||||
{/* Row 1: 控件名称, 标注目标对象, 控件类型 */}
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '180px 220px 1fr auto', gap: 12, alignItems: 'flex-end' }}>
|
||||
<Form.Item
|
||||
{...field}
|
||||
label="来源名称"
|
||||
name={[field.name, "fromName"]}
|
||||
rules={[{ required: true, message: "必填" }]}
|
||||
style={{ marginBottom: 0 }}
|
||||
tooltip="此控件的唯一标识符"
|
||||
>
|
||||
<Input placeholder="例如:choice" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
{...field}
|
||||
label="标注目标对象"
|
||||
name={[field.name, "toName"]}
|
||||
rules={[{ required: true, message: "必填" }]}
|
||||
style={{ marginBottom: 0 }}
|
||||
tooltip="选择此控件将标注哪个数据对象"
|
||||
dependencies={['objects']}
|
||||
>
|
||||
<Select placeholder="选择数据对象">
|
||||
{(form.getFieldValue("objects") || []).map((obj: any, idx: number) => (
|
||||
<Option key={idx} value={obj?.name || ''}>
|
||||
{obj?.name || `对象 ${idx + 1}`} ({obj?.type || '未知类型'})
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
{...field}
|
||||
label="控件类型"
|
||||
name={[field.name, "type"]}
|
||||
rules={[{ required: true, message: "必填" }]}
|
||||
style={{ marginBottom: 0 }}
|
||||
>
|
||||
<TagSelector type="control" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
{...field}
|
||||
label=" "
|
||||
name={[field.name, "required"]}
|
||||
valuePropName="checked"
|
||||
style={{ marginBottom: 0 }}
|
||||
>
|
||||
<Checkbox>必填</Checkbox>
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
{/* Row 2: 取值范围定义(添加选项) - Conditionally rendered based on type */}
|
||||
<Form.Item
|
||||
noStyle
|
||||
shouldUpdate={(prevValues, currentValues) => {
|
||||
const prevType = prevValues.labels?.[field.name]?.type;
|
||||
const currType = currentValues.labels?.[field.name]?.type;
|
||||
return prevType !== currType;
|
||||
}}
|
||||
>
|
||||
{({ getFieldValue }) => {
|
||||
const controlType = getFieldValue(["labels", field.name, "type"]);
|
||||
const fieldName = controlType === "Choices" ? "options" : "labels";
|
||||
|
||||
if (needsOptions(controlType)) {
|
||||
return (
|
||||
<Form.Item
|
||||
{...field}
|
||||
label={controlType === "Choices" ? "选项" : "标签"}
|
||||
name={[field.name, fieldName]}
|
||||
rules={[{ required: true, message: "至少需要一个选项" }]}
|
||||
style={{ marginBottom: 0 }}
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
open={false}
|
||||
placeholder={
|
||||
controlType === "Choices"
|
||||
? "输入选项内容,按回车添加。例如:是、否、不确定"
|
||||
: "输入标签名称,按回车添加。例如:人物、车辆、建筑物"
|
||||
}
|
||||
style={{ width: "100%" }}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}}
|
||||
</Form.Item>
|
||||
|
||||
{/* Row 3: 描述 */}
|
||||
<Form.Item
|
||||
{...field}
|
||||
label="描述"
|
||||
name={[field.name, "description"]}
|
||||
style={{ marginBottom: 0 }}
|
||||
tooltip="向标注人员显示的帮助信息"
|
||||
>
|
||||
<Input placeholder="为标注人员提供此控件的使用说明" maxLength={200} />
|
||||
</Form.Item>
|
||||
</Space>
|
||||
</Card>
|
||||
))}
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() =>
|
||||
add({
|
||||
fromName: "",
|
||||
toName: "",
|
||||
type: "Choices",
|
||||
required: false,
|
||||
})
|
||||
}
|
||||
block
|
||||
icon={<PlusOutlined />}
|
||||
>
|
||||
添加标签控件
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</Form.List>
|
||||
<TemplateConfigurationForm form={form} />
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user