世界上最伟大的投资就是投资自己的教育
表单中自定义控件,经过 API 请求,自定义控件中无法显示值
罗志勃发布于2361 次阅读
拿 antd pro 的官网 advanced-form 为例,
![](https:/
改如何处理 自定义表单控件与 form 表单双向数据绑定问题??????????????????
部分代码如下:
表单
import { CloseCircleOutlined } from '@ant-design/icons';
import { Button, Card, Col, DatePicker, Form, Input, Popover, Row, Select, TimePicker } from 'antd';
import React, { FC, useState } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import { connect, Dispatch } from 'umi';
import TableForm from './components/TableForm';
import FooterToolbar from './components/FooterToolbar';
import styles from './style.less';
type InternalNamePath = (string | number)[];
const { Option } = Select;
const { RangePicker } = DatePicker;
const fieldLabels = {
name: '仓库名',
url: '仓库域名',
owner: '仓库管理员',
approver: '审批人',
dateRange: '生效日期',
type: '仓库类型',
name2: '任务名',
url2: '任务描述',
owner2: '执行人',
approver2: '责任人',
dateRange2: '生效日期',
type2: '任务类型',
};
/**
* 如果tableData数据通过接口请求后,进行赋值,列表中无法显示值
* ?????????????????? 改如何处理 自定义表单控件与form表单双向数据绑定问题
*/
const tableData = [
{
key: '1',
workId: '00001',
name: 'John Brown',
department: 'New York No. 1 Lake Park',
},
{
key: '2',
workId: '00002',
name: 'Jim Green',
department: 'London No. 1 Lake Park',
},
{
key: '3',
workId: '00003',
name: 'Joe Black',
department: 'Sidney No. 1 Lake Park',
},
];
interface AdvancedFormProps {
dispatch: Dispatch<any>;
submitting: boolean;
}
interface ErrorField {
name: InternalNamePath;
errors: string[];
}
const AdvancedForm: FC<AdvancedFormProps> = ({ submitting, dispatch }) : {
const [form] = Form.useForm();
const [error, setError] = useState<ErrorField[]>([]);
const getErrorInfo = (errors: ErrorField[]) : {
const errorCount = errors.filter((item) : item.errors.length > 0).length;
if (!errors || errorCount === 0) {
return null;
}
const scrollToField = (fieldKey: string) : {
const labelNode = document.querySelector(`label[for="${fieldKey}"]`);
if (labelNode) {
labelNode.scrollIntoView(true);
}
};
const errorList = errors.map((err) : {
if (!err || err.errors.length === 0) {
return null;
}
const key = err.name[0] as string;
return (
<li key={key} className={styles.errorListItem} onClick={() : scrollToField(key)}>
<CloseCircleOutlined className={styles.errorIcon} />
<div className={styles.errorMessage}>{err.errors[0]}</div>
<div className={styles.errorField}>{fieldLabels[key]}</div>
</li>
);
});
return (
<span className={styles.errorIcon}>
<Popover
title="表单校验信息"
content={errorList}
overlayClassName={styles.errorPopover}
trigger="click"
getPopupContainer={(trigger: HTMLElement) : {
if (trigger && trigger.parentNode) {
return trigger.parentNode as HTMLElement;
}
return trigger;
}}
>
<CloseCircleOutlined />
</Popover>
{errorCount}
</span>
);
};
const onFinish = (values: { [key: string]: any }) : {
setError([]);
dispatch({
type: 'formAndadvancedForm/submitAdvancedForm',
payload: values,
});
};
const onFinishFailed = (errorInfo: any) : {
console.log('Failed:', errorInfo);
setError(errorInfo.errorFields);
};
return (
<Form
form={form}
layout="vertical"
hideRequiredMark
initialValues={{ members: tableData }}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
>
<PageHeaderWrapper content="高级表单常见于一次性输入和提交大批量数据的场景。">
<Card title="仓库管理" className={styles.card} bordered={false}>
<Row gutter={16}>
<Col lg={6} md={12} sm={24}>
<Form.Item
label={fieldLabels.name}
name="name"
rules={[{ required: true, message: '请输入仓库名称' }]}
>
<Input placeholder="请输入仓库名称" />
</Form.Item>
</Col>
<Col xl={{ span: 6, offset: 2 }} lg={{ span: 8 }} md={{ span: 12 }} sm={24}>
<Form.Item
label={fieldLabels.url}
name="url"
rules={[{ required: true, message: '请选择' }]}
>
<Input
style={{ width: '100%' }}
addonBefore="http://"
addonAfter=".com"
placeholder="请输入"
/>
</Form.Item>
</Col>
<Col xl={{ span: 8, offset: 2 }} lg={{ span: 10 }} md={{ span: 24 }} sm={24}>
<Form.Item
label={fieldLabels.owner}
name="owner"
rules={[{ required: true, message: '请选择管理员' }]}
>
<Select placeholder="请选择管理员">
<Option value="xiao">付晓晓</Option>
<Option value="mao">周毛毛</Option>
</Select>
</Form.Item>
</Col>
</Row>
<Row gutter={16}>
<Col lg={6} md={12} sm={24}>
<Form.Item
label={fieldLabels.approver}
name="approver"
rules={[{ required: true, message: '请选择审批员' }]}
>
<Select placeholder="请选择审批员">
<Option value="xiao">付晓晓</Option>
<Option value="mao">周毛毛</Option>
</Select>
</Form.Item>
</Col>
<Col xl={{ span: 6, offset: 2 }} lg={{ span: 8 }} md={{ span: 12 }} sm={24}>
<Form.Item
label={fieldLabels.dateRange}
name="dateRange"
rules={[{ required: true, message: '请选择生效日期' }]}
>
<RangePicker placeholder={['开始日期', '结束日期']} style={{ width: '100%' }} />
</Form.Item>
</Col>
<Col xl={{ span: 8, offset: 2 }} lg={{ span: 10 }} md={{ span: 24 }} sm={24}>
<Form.Item
label={fieldLabels.type}
name="type"
rules={[{ required: true, message: '请选择仓库类型' }]}
>
<Select placeholder="请选择仓库类型">
<Option value="private">私密</Option>
<Option value="public">公开</Option>
</Select>
</Form.Item>
</Col>
</Row>
</Card>
<Card title="任务管理" className={styles.card} bordered={false}>
<Row gutter={16}>
<Col lg={6} md={12} sm={24}>
<Form.Item
label={fieldLabels.name2}
name="name2"
rules={[{ required: true, message: '请输入' }]}
>
<Input placeholder="请输入" />
</Form.Item>
</Col>
<Col xl={{ span: 6, offset: 2 }} lg={{ span: 8 }} md={{ span: 12 }} sm={24}>
<Form.Item
label={fieldLabels.url2}
name="url2"
rules={[{ required: true, message: '请选择' }]}
>
<Input placeholder="请输入" />
</Form.Item>
</Col>
<Col xl={{ span: 8, offset: 2 }} lg={{ span: 10 }} md={{ span: 24 }} sm={24}>
<Form.Item
label={fieldLabels.owner2}
name="owner2"
rules={[{ required: true, message: '请选择管理员' }]}
>
<Select placeholder="请选择管理员">
<Option value="xiao">付晓晓</Option>
<Option value="mao">周毛毛</Option>
</Select>
</Form.Item>
</Col>
</Row>
<Row gutter={16}>
<Col lg={6} md={12} sm={24}>
<Form.Item
label={fieldLabels.approver2}
name="approver2"
rules={[{ required: true, message: '请选择审批员' }]}
>
<Select placeholder="请选择审批员">
<Option value="xiao">付晓晓</Option>
<Option value="mao">周毛毛</Option>
</Select>
</Form.Item>
</Col>
<Col xl={{ span: 6, offset: 2 }} lg={{ span: 8 }} md={{ span: 12 }} sm={24}>
<Form.Item
label={fieldLabels.dateRange2}
name="dateRange2"
rules={[{ required: true, message: '请输入' }]}
>
<TimePicker
placeholder="提醒时间"
style={{ width: '100%' }}
getPopupContainer={(trigger) : {
if (trigger && trigger.parentNode) {
return trigger.parentNode as HTMLElement;
}
return trigger;
}}
/>
</Form.Item>
</Col>
<Col xl={{ span: 8, offset: 2 }} lg={{ span: 10 }} md={{ span: 24 }} sm={24}>
<Form.Item
label={fieldLabels.type2}
name="type2"
rules={[{ required: true, message: '请选择仓库类型' }]}
>
<Select placeholder="请选择仓库类型">
<Option value="private">私密</Option>
<Option value="public">公开</Option>
</Select>
</Form.Item>
</Col>
</Row>
</Card>
<Card title="成员管理" bordered={false}>
<Form.Item name="members">
<TableForm />
</Form.Item>
</Card>
</PageHeaderWrapper>
<FooterToolbar>
{getErrorInfo(error)}
<Button type="primary" onClick={() : form?.submit()} loading={submitting}>
提交
</Button>
</FooterToolbar>
</Form>
);
};
export default connect(({ loading }: { loading: { effects: { [key: string]: boolean } } }) : ({
submitting: loading.effects['formAndadvancedForm/submitAdvancedForm'],
}))(AdvancedForm);
自定义表单 TableForm 组件
import { PlusOutlined } from '@ant-design/icons';
import { Button, Divider, Input, Popconfirm, Table, message } from 'antd';
import React, { FC, useState } from 'react';
import styles from '../style.less';
interface TableFormDateType {
key: string;
workId?: string;
name?: string;
department?: string;
isNew?: boolean;
editable?: boolean;
}
/**
* 我发现网站编辑器 会把 TableFormProps interface 中箭头干掉
* interface TableFormProps {
* value?: TableFormDateType[];
* onChange?: (value: TableFormDateType[]) : void;
}
*/
interface TableFormProps {
value?: TableFormDateType[];
onChange?: (value: TableFormDateType[]) : void;
}
const TableForm: FC<TableFormProps> = ({ value, onChange }) : {
console.log("--------- values = ",value);
const [clickedCancel, setClickedCancel] = useState(false);
const [loading, setLoading] = useState(false);
const [index, setIndex] = useState(0);
const [cacheOriginData, setCacheOriginData] = useState({});
const [data, setData] = useState(value);
const getRowByKey = (key: string, newData?: TableFormDateType[]) :
(newData || data)?.filter((item) : item.key === key)[0];
const toggleEditable = (e: React.MouseEvent | React.KeyboardEvent, key: string) : {
e.preventDefault();
const newData = data?.map((item) : ({ ...item }));
const target = getRowByKey(key, newData);
if (target) {
// 进入编辑状态时保存原始数据
if (!target.editable) {
cacheOriginData[key] = { ...target };
setCacheOriginData(cacheOriginData);
}
target.editable = !target.editable;
setData(newData);
}
};
const newMember = () : {
console.log("------------ newMember ------------ ");
const newData = data?.map((item) : ({ ...item })) || [];
// eslint-disable-next-line no-unused-expressions
newData?.push({
key: `NEW_TEMP_ID_${index}`,
workId: '',
name: '',
department: '',
editable: true,
isNew: true,
});
setIndex(index + 1);
setData(newData);
};
const remove = (key: string) : {
const newData = data?.filter((item) : item.key !== key) as TableFormDateType[];
setData(newData);
if (onChange) {
onChange(newData);
}
};
const handleFieldChange = (
e: React.ChangeEvent<HTMLInputElement>,
fieldName: string,
key: string,
) : {
const newData = [...(data as TableFormDateType[])];
const target = getRowByKey(key, newData);
if (target) {
target[fieldName] = e.target.value;
setData(newData);
}
};
const saveRow = (e: React.MouseEvent | React.KeyboardEvent, key: string) : {
e.persist();
setLoading(true);
setTimeout(() : {
if (clickedCancel) {
setClickedCancel(false);
return;
}
const target = getRowByKey(key) || ({} as any);
if (!target.workId || !target.name || !target.department) {
message.error('请填写完整成员信息。');
(e.target as HTMLInputElement).focus();
setLoading(false);
return;
}
delete target.isNew;
toggleEditable(e, key);
if (onChange) {
onChange(data as TableFormDateType[]);
}
setLoading(false);
}, 500);
};
const handleKeyPress = (e: React.KeyboardEvent, key: string) : {
if (e.key === 'Enter') {
saveRow(e, key);
}
};
const cancel = (e: React.MouseEvent, key: string) : {
setClickedCancel(true);
e.preventDefault();
const newData = [...(data as TableFormDateType[])];
// 编辑前的原始数据
let cacheData = [];
cacheData = newData.map((item) : {
if (item.key === key) {
if (cacheOriginData[key]) {
const originItem = {
...item,
...cacheOriginData[key],
editable: false,
};
delete cacheOriginData[key];
setCacheOriginData(cacheOriginData);
return originItem;
}
}
return item;
});
setData(cacheData);
setClickedCancel(false);
};
const columns = [
{
title: '成员姓名',
dataIndex: 'name',
key: 'name',
width: '20%',
render: (text: string, record: TableFormDateType) : {
if (record.editable) {
return (
<Input
value={text}
autoFocus
onChange={(e) : handleFieldChange(e, 'name', record.key)}
onKeyPress={(e) : handleKeyPress(e, record.key)}
placeholder="成员姓名"
/>
);
}
return text;
},
},
{
title: '工号',
dataIndex: 'workId',
key: 'workId',
width: '20%',
render: (text: string, record: TableFormDateType) : {
if (record.editable) {
return (
<Input
value={text}
onChange={(e) : handleFieldChange(e, 'workId', record.key)}
onKeyPress={(e) : handleKeyPress(e, record.key)}
placeholder="工号"
/>
);
}
return text;
},
},
{
title: '所属部门',
dataIndex: 'department',
key: 'department',
width: '40%',
render: (text: string, record: TableFormDateType) : {
if (record.editable) {
return (
<Input
value={text}
onChange={(e) : handleFieldChange(e, 'department', record.key)}
onKeyPress={(e) : handleKeyPress(e, record.key)}
placeholder="所属部门"
/>
);
}
return text;
},
},
{
title: '操作',
key: 'action',
render: (text: string, record: TableFormDateType) : {
if (!!record.editable && loading) {
return null;
}
if (record.editable) {
if (record.isNew) {
return (
<span>
<a onClick={(e) : saveRow(e, record.key)}>添加</a>
<Divider type="vertical" />
<Popconfirm title="是否要删除此行?" onConfirm={() : remove(record.key)}>
<a>删除</a>
</Popconfirm>
</span>
);
}
return (
<span>
<a onClick={(e) : saveRow(e, record.key)}>保存</a>
<Divider type="vertical" />
<a onClick={(e) : cancel(e, record.key)}>取消</a>
</span>
);
}
return (
<span>
<a onClick={(e) : toggleEditable(e, record.key)}>编辑</a>
<Divider type="vertical" />
<Popconfirm title="是否要删除此行?" onConfirm={() : remove(record.key)}>
<a>删除</a>
</Popconfirm>
</span>
);
},
},
];
return (
<>
<Table<TableFormDateType>
loading={loading}
columns={columns}
dataSource={data}
pagination={false}
rowClassName={(record) : (record.editable ? styles.editable : '')}
/>
<Button
style={{ width: '100%', marginTop: 16, marginBottom: 8 }}
type="dashed"
onClick={newMember}
>
<PlusOutlined />
新增成员
</Button>
</>
);
};
export default TableForm;
本站文章均为原创内容,如需转载请注明出处,谢谢。
© 汕尾市求知科技有限公司 | Rails365 Gitlab | 知乎 | b 站 | csdn
粤公网安备 44152102000088号 | 粤ICP备19038915号
Top
1