zy-react-library/components/FormBuilder/FormItemsRenderer.js

346 lines
11 KiB
JavaScript
Raw Normal View History

2025-10-22 14:43:42 +08:00
import { InfoCircleOutlined } from "@ant-design/icons";
import {
Checkbox,
Col,
DatePicker,
Divider,
Form,
Input,
InputNumber,
Radio,
Select,
Tooltip,
} from "antd";
import dayjs from "dayjs";
import { FORM_ITEM_RENDER_ENUM } from "../../enum/formItemRender";
const { TextArea } = Input;
const { RangePicker } = DatePicker;
/**
* 表单项渲染器组件
*/
const FormItemsRenderer = ({
options,
span = 12,
collapse = false,
useAutoGenerateRequired = true,
}) => {
const form = Form.useFormInstance();
// 获取传给组件的属性
const getComponentProps = (option) => {
return typeof option.componentProps === "function"
? option.componentProps(form.getFieldsValue())
: (option.componentProps || {});
};
// 获取传给formItem的属性
const getFormItemProps = (option) => {
const formItemProps = typeof option.formItemProps === "function"
? option.formItemProps(form.getFieldsValue())
: (option.formItemProps || {});
// 为日期组件添加特殊处理
if ([
FORM_ITEM_RENDER_ENUM.DATE,
FORM_ITEM_RENDER_ENUM.DATE_MONTH,
FORM_ITEM_RENDER_ENUM.DATE_YEAR,
FORM_ITEM_RENDER_ENUM.DATETIME,
].includes(option.render)) {
formItemProps.getValueFromEvent = (_, dateString) => dateString;
formItemProps.getValueProps = value => ({ value: value ? dayjs(value) : undefined });
}
// 为日期周组件添加特殊处理
if ([
FORM_ITEM_RENDER_ENUM.DATE_WEEK,
].includes(option.render)) {
formItemProps.getValueFromEvent = (_, dateString) => dateString;
formItemProps.getValueProps = value => ({ value: value ? dayjs(value, "YYYY-wo") : undefined });
}
// 为日期范围组件添加特殊处理
if ([
FORM_ITEM_RENDER_ENUM.DATE_RANGE,
FORM_ITEM_RENDER_ENUM.DATETIME_RANGE,
].includes(option.render)) {
formItemProps.getValueFromEvent = (_, dateString) => dateString;
formItemProps.getValueProps = value => ({ value: Array.isArray(value) ? value.map(v => v ? dayjs(v) : undefined) : undefined });
}
return formItemProps;
};
// 获取items里的value和label字段key
const getItemsFieldKey = (option) => {
return {
valueKey: option?.itemsField?.valueKey || "value",
labelKey: option?.itemsField?.labelKey || "label",
};
};
// 获取验证规则
const getRules = (option) => {
if (!useAutoGenerateRequired)
return option.rules ? (Array.isArray(option.rules) ? option.rules : [option.rules]) : [];
if (option.render === FORM_ITEM_RENDER_ENUM.DIVIDER)
return [];
// 支持动态计算 required
const required = typeof option.required === "function"
? option.required(form.getFieldsValue())
: (option.required || true);
if (required) {
const isBlurTrigger = !option.render || [
FORM_ITEM_RENDER_ENUM.INPUT,
FORM_ITEM_RENDER_ENUM.TEXTAREA,
FORM_ITEM_RENDER_ENUM.INPUT_NUMBER,
FORM_ITEM_RENDER_ENUM.NUMBER,
].includes(option.render);
const rules = [
{
required: true,
message: `${isBlurTrigger ? "请输入" : "请选择"}${option.label}`,
},
];
if (option.rules) {
if (Array.isArray(option.rules)) {
rules.push(...option.rules);
}
else {
rules.push(option.rules);
}
}
return rules;
}
return option.rules ? (Array.isArray(option.rules) ? option.rules : [option.rules]) : [];
};
// 渲染表单控件
const renderFormControl = (option) => {
const componentProps = getComponentProps(option);
const itemsFieldKey = getItemsFieldKey(option);
/** @type {string | Function} */
const render = option.render || FORM_ITEM_RENDER_ENUM.INPUT;
const placeholder = option.placeholder || `${render === FORM_ITEM_RENDER_ENUM.SELECT || render === FORM_ITEM_RENDER_ENUM.RADIO || render === FORM_ITEM_RENDER_ENUM.CHECKBOX ? "选择" : "输入"}${option.label}`;
switch (render) {
case FORM_ITEM_RENDER_ENUM.INPUT:
return <Input placeholder={placeholder} {...componentProps} />;
case FORM_ITEM_RENDER_ENUM.TEXTAREA:
return <TextArea placeholder={placeholder} rows={3} {...componentProps} />;
case FORM_ITEM_RENDER_ENUM.INPUT_NUMBER:
case FORM_ITEM_RENDER_ENUM.NUMBER:
return <InputNumber placeholder={placeholder} style={{ width: "100%" }} {...componentProps} />;
case FORM_ITEM_RENDER_ENUM.SELECT:
return (
<Select placeholder={placeholder} {...componentProps}>
{(option.items || []).map((item) => {
const value = item[itemsFieldKey.valueKey] ?? item.dictionariesId ?? item.id;
const label = item[itemsFieldKey.labelKey] ?? item.name;
return (
<Select.Option key={value} value={value}>
{label}
</Select.Option>
);
})}
</Select>
);
case FORM_ITEM_RENDER_ENUM.RADIO:
return (
<Radio.Group {...componentProps}>
{(option.items || []).map((item) => {
const value = item[itemsFieldKey.valueKey] ?? item.dictionariesId ?? item.id;
const label = item[itemsFieldKey.labelKey] ?? item.name;
return (
<Radio key={value} value={value}>
{label}
</Radio>
);
})}
</Radio.Group>
);
case FORM_ITEM_RENDER_ENUM.CHECKBOX:
return (
<Checkbox.Group {...componentProps}>
{(option.items || []).map((item) => {
const value = item[itemsFieldKey.valueKey] ?? item.dictionariesId ?? item.id;
const label = item[itemsFieldKey.labelKey] ?? item.name;
return (
<Checkbox key={value} value={value}>
{label}
</Checkbox>
);
})}
</Checkbox.Group>
);
case FORM_ITEM_RENDER_ENUM.DATE:
return <DatePicker placeholder={placeholder} format="YYYY-MM-DD" style={{ width: "100%" }} {...componentProps} />;
case FORM_ITEM_RENDER_ENUM.DATE_MONTH:
return <DatePicker picker="month" placeholder={placeholder} format="YYYY-MM" style={{ width: "100%" }} {...componentProps} />;
case FORM_ITEM_RENDER_ENUM.DATE_YEAR:
return <DatePicker picker="year" placeholder={placeholder} format="YYYY" style={{ width: "100%" }} {...componentProps} />;
case FORM_ITEM_RENDER_ENUM.DATE_WEEK:
return <DatePicker picker="week" placeholder={placeholder} format="YYYY-wo" style={{ width: "100%" }} {...componentProps} />;
case FORM_ITEM_RENDER_ENUM.DATE_RANGE:
return (
<RangePicker
placeholder={[`请选择开始${option.label}`, `请选择结束${option.label}`]}
format="YYYY-MM-DD"
style={{ width: "100%" }}
{...componentProps}
/>
);
case FORM_ITEM_RENDER_ENUM.DATETIME:
return (
<DatePicker
showTime
placeholder={placeholder}
format="YYYY-MM-DD HH:mm:ss"
style={{ width: "100%" }}
{...componentProps}
/>
);
case FORM_ITEM_RENDER_ENUM.DATETIME_RANGE:
return (
<RangePicker
showTime
placeholder={[`请选择开始${option.label}`, `请选择结束${option.label}`]}
format="YYYY-MM-DD HH:mm:ss"
style={{ width: "100%" }}
{...componentProps}
/>
);
case FORM_ITEM_RENDER_ENUM.DIVIDER:
return null;
default:
// 支持传入自定义组件
if (typeof render === "function") {
const CustomComponent = render;
return <CustomComponent {...componentProps} />;
}
return <Input placeholder={placeholder} {...componentProps} />;
}
};
// 渲染 label带提示
const renderLabel = (option) => {
if (!option.tip)
return option.label;
return (
<>
{option.label}
<Tooltip title={option.tip}>
<InfoCircleOutlined style={{ marginLeft: 4, fontSize: 12 }} />
</Tooltip>
</>
);
};
return (
<>
{options.map((option, index) => {
const itemSpan = option.render === FORM_ITEM_RENDER_ENUM.DIVIDER ? 24 : option.span ?? span;
// 使用 style 控制显示/隐藏
const style = collapse && index >= 3 ? { display: "none" } : undefined;
// 如果是分割线
if (option.render === FORM_ITEM_RENDER_ENUM.DIVIDER) {
return (
<Col key={option.name || index} span={itemSpan} style={style}>
<Divider orientation="left">{option.label}</Divider>
</Col>
);
}
// 如果配置了 shouldUpdate 或 dependencies使用 Form.Item 的联动机制
if (option.shouldUpdate || option.dependencies || option?.componentProps?.shouldUpdate || option?.componentProps?.dependencies) {
return (
option.customizeRender
? (renderFormControl(option))
: (
<Col key={option.name || index} span={itemSpan} style={style}>
<Form.Item
noStyle
shouldUpdate={option.shouldUpdate || option?.componentProps?.shouldUpdate}
dependencies={option.dependencies || option?.componentProps?.dependencies}
>
{(form) => {
// 支持动态计算 hidden
const hidden = typeof option.hidden === "function"
? option.hidden(form.getFieldsValue())
: (option.hidden || false);
if (hidden)
return null;
return (
<Form.Item
name={option.name}
label={renderLabel(option)}
rules={getRules(option)}
labelCol={option.labelCol}
wrapperCol={option.wrapperCol}
{...getFormItemProps(option)}
>
{renderFormControl(option)}
</Form.Item>
);
}}
</Form.Item>
</Col>
)
);
}
// 普通表单项(静态配置)
if (option.hidden)
return null;
return (
option.customizeRender
? (renderFormControl(option))
: (
<Col key={option.name || index} span={itemSpan} style={style}>
<Form.Item
name={option.name}
label={renderLabel(option)}
rules={getRules(option)}
labelCol={option.labelCol}
wrapperCol={option.wrapperCol}
{...getFormItemProps(option)}
>
{renderFormControl(option)}
</Form.Item>
</Col>
)
);
})}
</>
);
};
FormItemsRenderer.displayName = "FormItemsRenderer";
export default FormItemsRenderer;