form.js组件
import React, { useState, useEffect, useImperativeHandle, forwardRef } from 'react';
import {
Modal,
Form,
Input,
Button,
Row,
Col,
Radio,
Select,
Cascader,
DatePicker,
InputNumber,
TreeSelect,
Switch,
Checkbox,
Slider,
Upload,
Alert,
Transfer,
} from 'antd';
import './form.less';
import { PlusOutlined, InboxOutlined } from '@ant-design/icons';
import { cacheData } from '@/utils';
const { RangePicker } = DatePicker;
const SearchForm = (props) => {
const [form] = Form.useForm();
let {
cRef,
defaultInitialValues, //form的initialValues值,只渲染一次
initialValues, //相当于form的setFieldsValue,只要改变就重新渲染
ColSpan,
FormItems = [],
formName,
layout,
tailLayout,
showModal,
modalData = { visible: true },
action,
hasLoding = true,
hasSearchBtn = true,
formProps = {},
modalProps = {},
} = props;
const [visible, setVisible] = useState(false);
const [confirmLoading, setConfirmLoading] = useState(false);
const [previewVisible, setPreviewVisible] = useState(false);
const [previewImage, setPreviewImage] = useState();
const [previewTitle, setPreviewTitle] = useState();
const [fileList, setFileList] = useState([]);
const [errorInfo, setErrorInfo] = useState();
const [formValues, setFormValues] = useState();
useImperativeHandle(cRef, () => ({
setVisible: (val) => {
handleShowModal(val);
},
setLoading: (val) => {
setConfirmLoading(val);
},
getFieldsValue: () => {
return formValues;
},
setFieldsValue: (values) => {
form.setFieldsValue(values);
},
resetFields: () => {
form.resetFields();
},
getForm: () => {
return form;
},
}));
useEffect(() => {
setVisible(modalData.visible);
}, [modalData.visible]);
useEffect(() => {
initialValues && props.onValuesChange && props.onValuesChange(initialValues, initialValues);
form.setFieldsValue(initialValues);
setFormValues(initialValues);
}, [initialValues]);
const onFinish = (values, action) => {
// 校验通过
let newvalues = { ...values };
FormItems &&
FormItems.map((item) => {
let oldValue = newvalues[item.name];
switch (item.type) {
case 'RangePicker':
if (item.name instanceof Array) {
oldValue = newvalues[item.name[0]][item.name[1]];
item.name.map((nameItem, index) => {
newvalues[nameItem] =
action == 'reset' || !oldValue
? undefined
: oldValue[index].format(item.format || 'YYYY-MM-DD');
});
} else {
newvalues[item.name] = [
oldValue[0].format(item.format || 'YYYY-MM-DD'),
oldValue[1].format(item.format || 'YYYY-MM-DD'),
];
}
break;
case 'DatePicker':
newvalues[item.name] = oldValue.format('YYYY-MM-DD');
break;
case 'Monthpicker':
newvalues[item.name] = oldValue.format('YYYY-MM');
break;
case 'RangeTimePicker':
newvalues[item.name] = [
oldValue[0].format('YYYY-MM-DD HH:mm:ss'),
oldValue[1].format('YYYY-MM-DD HH:mm:ss'),
];
break;
case 'TimePicker':
newvalues[item.name] = oldValue.format('HH:mm:ss');
break;
default:
break;
}
newvalues = {
...newvalues,
};
});
props.onFinish(newvalues, action);
};
const onFinishFailed = (errorInfo) => {
// 校验未通过
setErrorInfo(errorInfo);
props.onFinishFailed(errorInfo);
};
// upload组件方法
const getBase64 = (file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = (error) => reject(error);
});
};
const handleUploadModalCancel = () => setPreviewVisible(false);
const handleUploadPreview = async (file) => {
if (!file.url && !file.preview) {
file.preview = await getBase64(file.originFileObj);
}
setPreviewImage(file.url || file.preview);
setPreviewVisible(true);
setPreviewTitle(file.name || file.url.substring(file.url.lastIndexOf('/') + 1));
};
const handleUploadChange = ({ fileList }) => setFileList(fileList);
const normFile = (e) => {
if (Array.isArray(e)) {
return e;
}
return e && e.fileList;
};
// 模态框
const handleClick = () => {
props.handleClick && props.handleClick();
form.resetFields();
handleShowModal(true);
};
const handleShowModal = (status) => {
console.log('form handleShowModal', status);
props.handleShowModal && props.handleShowModal(status);
setVisible(status);
if (status == false) {
form.resetFields();
}
setConfirmLoading(false);
};
const handleOk = () => {
form.validateFields().then((values) => {
onFinish(values, action);
if (hasLoding) {
setConfirmLoading(true);
}
});
};
const onReset = () => {
const { showModal } = props;
handleShowModal(false);
if (!showModal) {
// 为模态框取消按钮的时候不做任何操作
const values = form.getFieldsValue();
onFinish(values, 'reset');
}
};
const getFields = (item) => {
let html = null;
switch (item.type) {
case 'Text':
if (item.render) {
html = item.render(initialValues);
} else {
html =item.name&&initialValues && initialValues[item.name];
}
html = item.suffix ? html + item.suffix : html; //suffix后缀
break;
case 'Input':
html = <Input placeholder="请输入" {...item.props} />;
break;
case 'InputPassword':
html = <Input.Password placeholder="请输入" {...item.props} />;
break;
case 'TextArea':
html = <Input.TextArea placeholder="请输入" autoSize={{ minRows: 4 }} {...item.props} />;
break;
case 'Select':
html = (
<Select placeholder="请选择" {...item.props}>
{item.data &&
item.data.map((innerItem, index) => (
<Select.Option
disabled={innerItem.available && innerItem.available === true ? true : false}
key={`mySelect${index}`}
value={innerItem[item.fieldNames ? item.fieldNames.value : 'value']}
>
{innerItem[item.fieldNames ? item.fieldNames.label : 'label']}
</Select.Option>
))}
</Select>
);
break;
case 'TreeSelect':
html = <TreeSelect placeholder="请选择" {...item.props} />;
break;
case 'Cascader':
html = (
<Cascader
placeholder="请选择"
{...item.props}
options={(item && item.props && item.props.options) || []}
/>
);
break;
case 'DatePicker':
html = <DatePicker placeholder="请选择" {...item.props} />;
break;
case 'RangePicker':
html = <RangePicker format={'YYYY/MM/DD'} {...item.props} />;
break;
case 'InputNumber':
html = <InputNumber placeholder="请输入" {...item.props} />;
break;
case 'Switch':
html = (
<Switch
defaultChecked={(initialValues && initialValues[item.name]) || false}
{...item.props}
/>
);
break;
case 'RadioButton':
html = (
<Radio.Group name="radiogroup">
<Row>
{item.data &&
item.data.map((innerItem, index) => {
return (
<Col span={item.colSpan} key={`myRadio${index}`}>
<Radio.Button
value={innerItem[item.fieldNames ? item.fieldNames.value : 'value']}
style={{ lineHeight: item.lineHeight ? item.lineHeight : '32px' }}
{...item.props}
>
{innerItem[item.fieldNames ? item.fieldNames.label : 'label']}
</Radio.Button>
</Col>
);
})}
</Row>
</Radio.Group>
);
break;
case 'Radio':
html = (
<Radio.Group name="radiogroup" {...item.props}>
<Row>
{item.data &&
item.data.map((innerItem, index) => {
return (
<Col span={item.colSpan} key={`myRadio${index}`}>
<Radio
value={innerItem[item.fieldNames ? item.fieldNames.value : 'value']}
style={{ lineHeight: item.lineHeight ? item.lineHeight : '32px' }}
{...item.props}
>
{innerItem[item.fieldNames ? item.fieldNames.label : 'label']}
</Radio>
</Col>
);
})}
</Row>
</Radio.Group>
);
break;
case 'Slider':
html = <Slider marks={item.data} />;
break;
case 'Checkbox':
html = (
<Checkbox.Group {...item.props}>
<Row>
{item.data &&
item.data.map((innerItem, index) => (
<Col span={item.colSpan} key={`myCheckbox${index}`}>
<Checkbox
value={innerItem[item.fieldNames ? item.fieldNames.value : 'value']}
style={{ lineHeight: item.lineHeight ? item.lineHeight : '32px' }}
>
{innerItem[item.fieldNames ? item.fieldNames.label : 'label']}
</Checkbox>
</Col>
))}
</Row>
</Checkbox.Group>
);
break;
case 'Upload':
html = (
<Upload
onPreview={this.handleUploadPreview}
onChange={this.handleUploadChange}
{...item.props}
>
{fileList.length >= item.maxLength ? null : (
<div>
<PlusOutlined />
<div className="ant-upload-text">Upload</div>
</div>
)}
</Upload>
);
break;
case 'Dragger':
html = (
<Upload.Dragger name="files" action="/upload.do">
<p className="ant-upload-drag-icon">
<InboxOutlined />
</p>
<p className="ant-upload-text">Click or drag file to this area to upload</p>
<p className="ant-upload-hint">Support for a single or bulk upload.</p>
</Upload.Dragger>
);
break;
case 'Transfer':
html = <Transfer {...item.props} />;
break;
default:
html = null;
}
return html;
};
const getForm = () => {
layout = layout || {
labelCol: { span: 7 },
wrapperCol: { span: 17 },
};
tailLayout = tailLayout || {
wrapperCol: { span: 24, offset: 0 },
};
ColSpan = ColSpan || 6;
const html = (
<Form
{...layout}
form={form}
name={formName}
{...formProps}
onFieldsChange={(changedFields, allFields) =>
props.onFieldsChange && props.onFieldsChange(changedFields, allFields, action)
}
onValuesChange={(changedValues, allValues) => {
setFormValues(allValues);
props.onValuesChange && props.onValuesChange(changedValues, allValues, action);
}}
onFinish={(values) => onFinish(values, action)}
onFinishFailed={onFinishFailed}
initialValues={defaultInitialValues}
>
<Row>
{FormItems.map((item, index) => {
if (!item.type) {
return;
}
if (item.type == 'Alert') {
return (
<Alert
key={`myForm${index}`}
message="This is a warning notice about copywriting"
description=""
type="warning"
showIcon
banner
style={
item.whiteBg
? { marginBottom: '16px', backgroundColor: 'white', padding: 0 }
: { marginBottom: '16px' }
}
{...item.props}
/>
);
}
if (item.type == 'ReactNode') {
return (
<div
style={item.style ? item.style : { width: '100%', marginBottom: '16px' }}
key={`myForm${index}`}
>
{item.children}
</div>
);
}
return (
<Col
span={ColSpan}
// className="flexCenter"
key={`myForm${index}`}
>
<Form.Item
label={item.label}
name={item.name}
rules={item.rules}
help={item.help || undefined}
extra={item.extra || ''}
>
{getFields(item, index)}
</Form.Item>
</Col>
);
})}
{!showModal && hasSearchBtn && (
<Col span={ColSpan}>
<Form.Item {...tailLayout}>
<Button
type="primary"
htmlType="submit"
style={
FormItems.length % (24 / ColSpan) == 0
? { marginRight: '8px' }
: { marginRight: '8px', marginLeft: '16px' }
}
>
查询
</Button>
<Button onClick={() => onReset('reset')}>重置</Button>
</Form.Item>
</Col>
)}
</Row>
</Form>
);
return html;
};
return (
<>
<>
{showModal && (
<div className="modalFormCmpBox" style={{ display: 'inline-block' }}>
{modalData.clickNode ? (
<span onClick={handleClick}>{modalData.clickNode}</span>
) : (
<Button type="primary" onClick={handleClick}>
{modalData.btnText}
</Button>
)}
<Modal
title={modalData.title}
maskClosable={false}
width={modalData.width || 480}
visible={visible}
destroyOnClose={true}
{...modalProps}
onCancel={() => onReset('cancle')}
footer={[
<Button key="submit" type="primary" loading={confirmLoading} onClick={handleOk}>
确定
</Button>,
<Button key="back" onClick={() => onReset('cancle')}>
取消
</Button>,
]}
>
{getForm()}
</Modal>
</div>
)}
</>
{!showModal && getForm()}
</>
);
};
export default forwardRef(SearchForm);