import { InfoCircleOutlined } from "@ant-design/icons";
import {
Button,
Checkbox,
Col,
DatePicker,
Divider,
Form,
Input,
InputNumber,
Radio,
Row,
Select,
Tooltip,
} from "antd";
import dayjs from "dayjs";
import { FORM_ITEM_RENDER_ENUM } from "../../enum/formItemRender";
const { TextArea } = Input;
const { RangePicker } = DatePicker;
/**
* 表单项渲染器组件
* @param {object} props - 组件属性
* @param {Array} props.options - 表单配置项数组
* @param {object} props.labelCol - label 栅格配置
* @param {number} props.gutter - 栅格间距
* @param {number} props.span - 默认栅格占据列数
* @param {boolean} props.collapse - 是否折叠(仅显示前3项)
* @param {boolean} props.useAutoGenerateRequired - 是否自动生成必填规则
* @param {object} props.initialValues - 初始值
*/
const FormItemsRenderer = ({
options,
labelCol,
gutter = 24,
span = 12,
collapse = false,
useAutoGenerateRequired = true,
initialValues,
}) => {
const form = Form.useFormInstance();
// 获取表单值,优先使用 initialValues
const getFormValues = () => {
const formValues = form.getFieldsValue();
// 如果表单值为空但有 initialValues,则使用 initialValues
if (Object.keys(formValues).length === 0 && initialValues) {
return initialValues;
}
return formValues;
};
// 获取传给组件的属性
const getComponentProps = (option) => {
return typeof option.componentProps === "function"
? option.componentProps(getFormValues())
: (option.componentProps || {});
};
// 获取 Form.List 独有的属性
const getFormListUniqueProps = (option) => {
const defaultProps = {
showAddButton: true,
showRemoveButton: true,
addButtonText: "添加",
removeButtonText: "删除",
options: [],
addDefaultValue: {},
addInsertIndex: undefined,
};
if (typeof option.formListUniqueProps === "function") {
return {
...defaultProps,
...option.formListUniqueProps(getFormValues()),
};
}
return {
...defaultProps,
...(option.formListUniqueProps || {}),
};
};
// 获取传给formItem的属性
const getFormItemProps = (option) => {
const formItemProps = typeof option.formItemProps === "function"
? option.formItemProps(getFormValues())
: (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 || "bianma",
labelKey: option?.itemsField?.labelKey || "name",
};
};
// 获取 required
const getRequired = (required) => {
// 支持动态计算 required
return typeof required === "function"
? required(getFormValues())
: (required ?? true);
};
// 获取验证规则
const getRules = (option) => {
if (option.render === FORM_ITEM_RENDER_ENUM.DIVIDER)
return [];
const rules = [];
/** @type {string | Function} */
const render = option.render || FORM_ITEM_RENDER_ENUM.INPUT;
switch (render) {
case FORM_ITEM_RENDER_ENUM.INPUT:
rules.push({ max: 50, message: "最多输入50字符" });
break;
case FORM_ITEM_RENDER_ENUM.TEXTAREA:
rules.push({ max: 500, message: "最多输入500字符" });
break;
case FORM_ITEM_RENDER_ENUM.INPUT_NUMBER:
case FORM_ITEM_RENDER_ENUM.NUMBER:
rules.push({ pattern: /^(\d+)(\.\d{1,2})?$/, message: "请输入正确的数字,最多保留两位小数" });
rules.push({
validator: (_, value) => {
if (value && Math.abs(Number.parseFloat(value)) > 999999999) {
return Promise.reject("输入数值超出安全范围");
}
return Promise.resolve();
},
});
break;
}
if (!useAutoGenerateRequired)
return option.rules ? (Array.isArray(option.rules) ? [...option.rules, ...rules] : [option.rules, ...rules]) : rules;
if (getRequired(option.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);
rules.push({ 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, ...rules] : [option.rules, ...rules]) : rules;
};
// 获取key
const getKey = (option) => {
return option.key || option.name;
};
// 使用 style 控制显示/隐藏
const getStyle = (index) => {
return collapse && index >= 3 ? { display: "none" } : undefined;
};
// 列数
const getCol = (option) => {
const itemSpan = option.render === FORM_ITEM_RENDER_ENUM.DIVIDER ? 24 : option.span ?? span;
const itemLabelCol = option.labelCol ?? (itemSpan === 24 ? { span: labelCol.span / 2 } : labelCol);
const itemWrapperCol = option.wrapperCol ?? { span: 24 - itemLabelCol.span };
return { span: itemSpan, labelCol: itemLabelCol, wrapperCol: itemWrapperCol };
};
// 获取 hidden
const getHidden = (hidden) => {
// 支持动态计算 hidden
return typeof hidden === "function"
? hidden(getFormValues())
: (hidden ?? false);
};
// 获取 listOptions
const getListOptions = (listOptions, field, fieldIndex) => {
return typeof listOptions === "function"
? listOptions(field, fieldIndex)
: (listOptions ?? []);
};
// 渲染表单控件
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 ;
case FORM_ITEM_RENDER_ENUM.TEXTAREA:
return ;
case FORM_ITEM_RENDER_ENUM.INPUT_NUMBER:
case FORM_ITEM_RENDER_ENUM.NUMBER:
return