refactor: 提取描述框和表单项标签组件,简化代码结构
This commit is contained in:
@@ -291,7 +291,6 @@ const getResponseRenderer = (
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
// AI响应框主组件
|
|
||||||
const AIResponseBox = React.memo(function AIResponseBox({
|
const AIResponseBox = React.memo(function AIResponseBox({
|
||||||
value,
|
value,
|
||||||
isLastResponseValue,
|
isLastResponseValue,
|
||||||
|
|||||||
@@ -27,6 +27,41 @@ export type SelectOptionsComponentPropsType = {
|
|||||||
variant?: string;
|
variant?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DescriptionBox = React.memo(function DescriptionBox({
|
||||||
|
description
|
||||||
|
}: {
|
||||||
|
description?: string;
|
||||||
|
}) {
|
||||||
|
if (!description) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
mb={4}
|
||||||
|
p={4}
|
||||||
|
border="1px solid"
|
||||||
|
borderColor="blue.200"
|
||||||
|
bg="blue.50"
|
||||||
|
borderRadius="md"
|
||||||
|
boxShadow="sm"
|
||||||
|
textAlign="center"
|
||||||
|
>
|
||||||
|
<Markdown source={description} />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const inputBaseStyle = {
|
||||||
|
bg: 'white',
|
||||||
|
borderWidth: '1px',
|
||||||
|
borderColor: 'gray.300',
|
||||||
|
_hover: { borderColor: 'gray.400' },
|
||||||
|
_focus: {
|
||||||
|
borderColor: 'primary.500',
|
||||||
|
boxShadow: '0 0 0 1px var(--chakra-colors-primary-500)'
|
||||||
|
},
|
||||||
|
borderRadius: 'md'
|
||||||
|
};
|
||||||
|
|
||||||
export const SelectOptionsComponent = React.memo(function SelectOptionsComponent({
|
export const SelectOptionsComponent = React.memo(function SelectOptionsComponent({
|
||||||
options = [],
|
options = [],
|
||||||
description,
|
description,
|
||||||
@@ -37,20 +72,7 @@ export const SelectOptionsComponent = React.memo(function SelectOptionsComponent
|
|||||||
}: SelectOptionsComponentPropsType) {
|
}: SelectOptionsComponentPropsType) {
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
{description && (
|
<DescriptionBox description={description} />
|
||||||
<Box
|
|
||||||
mb={4}
|
|
||||||
p={4}
|
|
||||||
border="1px solid"
|
|
||||||
borderColor="blue.200"
|
|
||||||
bg="blue.50"
|
|
||||||
borderRadius="md"
|
|
||||||
boxShadow="sm"
|
|
||||||
textAlign="center"
|
|
||||||
>
|
|
||||||
<Markdown source={description} />
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
<Flex flexDirection={'column'} gap={3} maxW={'400px'} mx="auto">
|
<Flex flexDirection={'column'} gap={3} maxW={'400px'} mx="auto">
|
||||||
{options.map((option: SelectOptionType) => {
|
{options.map((option: SelectOptionType) => {
|
||||||
const selected = option.value === selectedValue;
|
const selected = option.value === selectedValue;
|
||||||
@@ -118,12 +140,133 @@ export type FormInputComponentProps = {
|
|||||||
onSubmit?: (data: Record<string, any>) => void;
|
onSubmit?: (data: Record<string, any>) => void;
|
||||||
isDisabled?: boolean;
|
isDisabled?: boolean;
|
||||||
defaultValues?: Record<string, any>;
|
defaultValues?: Record<string, any>;
|
||||||
submitButtonText?: 'common:Submit' | string; // 使用联合类型指定特定的i18n键名
|
submitButtonText?: 'common:Submit' | string;
|
||||||
showSubmitButton?: boolean;
|
showSubmitButton?: boolean;
|
||||||
submitButtonIcon?: IconNameType;
|
submitButtonIcon?: IconNameType;
|
||||||
isCompact?: boolean;
|
isCompact?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 表单项标签组件
|
||||||
|
const FormItemLabel = React.memo(function FormItemLabel({
|
||||||
|
label,
|
||||||
|
required,
|
||||||
|
description
|
||||||
|
}: {
|
||||||
|
label: string;
|
||||||
|
required?: boolean;
|
||||||
|
description?: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Flex mb={2} alignItems={'center'}>
|
||||||
|
<FormLabel required={required} mb={0} fontWeight="medium" color="gray.700">
|
||||||
|
{label}
|
||||||
|
</FormLabel>
|
||||||
|
{description && <QuestionTip ml={1} label={description} />}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 渲染不同类型的表单输入项
|
||||||
|
const renderFormInput = (
|
||||||
|
input: FormItemType,
|
||||||
|
register: any,
|
||||||
|
control: any,
|
||||||
|
setValue: any,
|
||||||
|
isDisabled: boolean
|
||||||
|
) => {
|
||||||
|
const { type, label, required, maxLength, min, max, defaultValue, list } = input;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case FlowNodeInputTypeEnum.input:
|
||||||
|
return (
|
||||||
|
<MyTextarea
|
||||||
|
isDisabled={isDisabled}
|
||||||
|
{...register(label, { required })}
|
||||||
|
{...inputBaseStyle}
|
||||||
|
autoHeight
|
||||||
|
minH={40}
|
||||||
|
maxH={100}
|
||||||
|
p={3}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
case FlowNodeInputTypeEnum.textarea:
|
||||||
|
return (
|
||||||
|
<Textarea
|
||||||
|
isDisabled={isDisabled}
|
||||||
|
{...register(label, { required })}
|
||||||
|
{...inputBaseStyle}
|
||||||
|
rows={5}
|
||||||
|
maxLength={maxLength || 4000}
|
||||||
|
p={3}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
case FlowNodeInputTypeEnum.numberInput:
|
||||||
|
return (
|
||||||
|
<Box position="relative">
|
||||||
|
<MyNumberInput
|
||||||
|
min={min}
|
||||||
|
max={max}
|
||||||
|
defaultValue={defaultValue}
|
||||||
|
isDisabled={isDisabled}
|
||||||
|
{...inputBaseStyle}
|
||||||
|
register={register}
|
||||||
|
name={label}
|
||||||
|
isRequired={required}
|
||||||
|
sx={{
|
||||||
|
'& input': {
|
||||||
|
width: '100%',
|
||||||
|
height: '40px',
|
||||||
|
px: 3,
|
||||||
|
borderRadius: 'md',
|
||||||
|
border: 'none',
|
||||||
|
_focus: { outline: 'none' }
|
||||||
|
},
|
||||||
|
'& button': {
|
||||||
|
border: 'none',
|
||||||
|
bg: 'transparent',
|
||||||
|
color: 'gray.500'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
|
||||||
|
case FlowNodeInputTypeEnum.select:
|
||||||
|
return (
|
||||||
|
<Controller
|
||||||
|
key={label}
|
||||||
|
control={control}
|
||||||
|
name={label}
|
||||||
|
rules={{ required }}
|
||||||
|
render={({ field: { ref, value } }) => {
|
||||||
|
if (!list) return <></>;
|
||||||
|
return (
|
||||||
|
<MySelect
|
||||||
|
ref={ref}
|
||||||
|
width={'100%'}
|
||||||
|
variant="outline"
|
||||||
|
borderColor="gray.300"
|
||||||
|
borderRadius="md"
|
||||||
|
height="40px"
|
||||||
|
bg="white"
|
||||||
|
_hover={{ borderColor: 'gray.400' }}
|
||||||
|
list={list}
|
||||||
|
value={value}
|
||||||
|
isDisabled={isDisabled}
|
||||||
|
onChange={(e) => setValue(label, e)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const FormInputComponent = React.memo(function FormInputComponent({
|
export const FormInputComponent = React.memo(function FormInputComponent({
|
||||||
inputForm = [],
|
inputForm = [],
|
||||||
description,
|
description,
|
||||||
@@ -151,20 +294,7 @@ export const FormInputComponent = React.memo(function FormInputComponent({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
{description && (
|
<DescriptionBox description={description} />
|
||||||
<Box
|
|
||||||
mb={4}
|
|
||||||
p={4}
|
|
||||||
border="1px solid"
|
|
||||||
borderColor="blue.200"
|
|
||||||
bg="blue.50"
|
|
||||||
borderRadius="md"
|
|
||||||
boxShadow="sm"
|
|
||||||
textAlign="center"
|
|
||||||
>
|
|
||||||
<Markdown source={description} />
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
<Box
|
<Box
|
||||||
as="form"
|
as="form"
|
||||||
onSubmit={handleSubmit(handleFormSubmit)}
|
onSubmit={handleSubmit(handleFormSubmit)}
|
||||||
@@ -176,114 +306,12 @@ export const FormInputComponent = React.memo(function FormInputComponent({
|
|||||||
<Flex flexDirection={'column'} gap={5} w={'100%'}>
|
<Flex flexDirection={'column'} gap={5} w={'100%'}>
|
||||||
{inputForm.map((input: FormItemType) => (
|
{inputForm.map((input: FormItemType) => (
|
||||||
<Box key={input.label} mb={2}>
|
<Box key={input.label} mb={2}>
|
||||||
<Flex mb={2} alignItems={'center'}>
|
<FormItemLabel
|
||||||
<FormLabel required={input.required} mb={0} fontWeight="medium" color="gray.700">
|
label={input.label}
|
||||||
{input.label}
|
required={input.required}
|
||||||
</FormLabel>
|
description={input.description}
|
||||||
{input.description && <QuestionTip ml={1} label={input.description} />}
|
/>
|
||||||
</Flex>
|
{renderFormInput(input, register, control, setValue, isDisabled)}
|
||||||
{input.type === FlowNodeInputTypeEnum.input && (
|
|
||||||
<MyTextarea
|
|
||||||
isDisabled={isDisabled}
|
|
||||||
{...register(input.label, {
|
|
||||||
required: input.required
|
|
||||||
})}
|
|
||||||
bg={'white'}
|
|
||||||
borderWidth="1px"
|
|
||||||
borderColor="gray.300"
|
|
||||||
_hover={{ borderColor: 'gray.400' }}
|
|
||||||
_focus={{
|
|
||||||
borderColor: 'primary.500',
|
|
||||||
boxShadow: '0 0 0 1px var(--chakra-colors-primary-500)'
|
|
||||||
}}
|
|
||||||
autoHeight
|
|
||||||
minH={40}
|
|
||||||
maxH={100}
|
|
||||||
borderRadius="md"
|
|
||||||
p={3}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{input.type === FlowNodeInputTypeEnum.textarea && (
|
|
||||||
<Textarea
|
|
||||||
isDisabled={isDisabled}
|
|
||||||
bg={'white'}
|
|
||||||
borderWidth="1px"
|
|
||||||
borderColor="gray.300"
|
|
||||||
_hover={{ borderColor: 'gray.400' }}
|
|
||||||
_focus={{
|
|
||||||
borderColor: 'primary.500',
|
|
||||||
boxShadow: '0 0 0 1px var(--chakra-colors-primary-500)'
|
|
||||||
}}
|
|
||||||
{...register(input.label, {
|
|
||||||
required: input.required
|
|
||||||
})}
|
|
||||||
rows={5}
|
|
||||||
maxLength={input.maxLength || 4000}
|
|
||||||
borderRadius="md"
|
|
||||||
p={3}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{input.type === FlowNodeInputTypeEnum.numberInput && (
|
|
||||||
<Box position="relative">
|
|
||||||
<MyNumberInput
|
|
||||||
min={input.min}
|
|
||||||
max={input.max}
|
|
||||||
defaultValue={input.defaultValue}
|
|
||||||
isDisabled={isDisabled}
|
|
||||||
bg={'white'}
|
|
||||||
borderWidth="1px"
|
|
||||||
borderRadius="md"
|
|
||||||
borderColor="gray.300"
|
|
||||||
_hover={{ borderColor: 'gray.400' }}
|
|
||||||
_focus={{ borderColor: 'primary.500' }}
|
|
||||||
register={register}
|
|
||||||
name={input.label}
|
|
||||||
isRequired={input.required}
|
|
||||||
sx={{
|
|
||||||
'& input': {
|
|
||||||
width: '100%',
|
|
||||||
height: '40px',
|
|
||||||
px: 3,
|
|
||||||
borderRadius: 'md',
|
|
||||||
border: 'none',
|
|
||||||
_focus: { outline: 'none' }
|
|
||||||
},
|
|
||||||
'& button': {
|
|
||||||
border: 'none',
|
|
||||||
bg: 'transparent',
|
|
||||||
color: 'gray.500'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
{input.type === FlowNodeInputTypeEnum.select && (
|
|
||||||
<Controller
|
|
||||||
key={input.label}
|
|
||||||
control={control}
|
|
||||||
name={input.label}
|
|
||||||
rules={{ required: input.required }}
|
|
||||||
render={({ field: { ref, value } }) => {
|
|
||||||
if (!input.list) return <></>;
|
|
||||||
return (
|
|
||||||
<MySelect
|
|
||||||
ref={ref}
|
|
||||||
width={'100%'}
|
|
||||||
variant="outline"
|
|
||||||
borderColor="gray.300"
|
|
||||||
borderRadius="md"
|
|
||||||
height="40px"
|
|
||||||
bg="white"
|
|
||||||
_hover={{ borderColor: 'gray.400' }}
|
|
||||||
list={input.list}
|
|
||||||
value={value}
|
|
||||||
isDisabled={isDisabled}
|
|
||||||
onChange={(e) => setValue(input.label, e)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user