由于antd pro 下载下面的页面默认是函数组件的不能使用refs信息直接获取子组件的信息
react中提供的reactHook可以帮助我们获取到子组件的内容
下载之后看到的就是一个如图的页面,页面分为输入框和table,适用于开发一对多关系的业务,例如订单中的商品信息,一个订单包含多个商品
接下来的这个问题就是,我在点击新增table中的数据时候,始终提交的都是页面中之前的初始化数据,没有办法获得最新的数据,这个问题困扰了我2天,最终我发现这里面包含了2个参数,一个value,和一个onChange
initialValues={{ params: tableData }}
const TableForm: FC<TableFormProps> = ({ value, onChange }) => {
...
}
在TableForm组件中加入一个副作用,作用于是data发生变更时候
data在都不顶部有声明
useEffect(() => {
//发生改变就传传递这个给调用的组件
onChange && onChange(data);
}, [data])
最后我把这2个完整的函数组件贴出来供大家参考
index.tsx
import { CloseCircleOutlined } from '@ant-design/icons';
import { Button, Card, Col, DatePicker, Form, Input, Popover, Row, Select } 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';
import { connect, Dispatch } from 'dva';
type InternalNamePath = (string | number)[];
const { Option } = Select;
const fieldLabels = {
jobName: '定时任务编码',
functionId: '执行功能',
triggerType: '触发类型',
description: '定时任务描述',
};
const tableData: any = [
{
key: `NEW_TEMP_ID_1`,
paramName: 'siteCode',
paramValue: 'LMS0001',
paramType: 'string',
paramIndex: 1,
description: '服务平台',
}
];
interface JobDefineAddProps {
dispatch: Dispatch<any>;
submitting: boolean;
}
interface ErrorField {
name: InternalNamePath;
errors: string[];
}
const JobDefineAdd: FC<JobDefineAddProps> = ({
submitting,
dispatch,
}) => {
const [form] = Form.useForm();
const [error, setError] = useState<ErrorField[]>([]);
const [dataForm, setDataForm] = useState();
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([]);
console.log('执行了这里');
console.log('values', values);
dispatch({
type: 'jobAndJobDefineAdd/submitAdvancedForm',
payload: values,
});
};
const onFinishFailed = (errorInfo: any) => {
setError(errorInfo.errorFields);
};
return (
<Form
form={form}
layout="vertical"
hideRequiredMark
initialValues={{ params: tableData }}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
//fields={fields}
onFieldsChange={(changedFields, allFields) => {
//onChange(allFields);
console.log(changedFields);
console.log(allFields);
}}
>
<PageHeaderWrapper content="此页面用于定义定时任务的已经定时任务的参数信息">
<Card title="定时任务定义" className={styles.card} bordered={false}>
<Row gutter={16}>
<Col lg={6} md={12} sm={24}>
<Form.Item
label={fieldLabels.jobName}
name="jobName"
rules={[{ required: true, message: '请输入定时任务编码' }]}
>
<Input placeholder="请输入定时任务编码" />
</Form.Item>
</Col>
<Col xl={{ span: 6, offset: 2 }} lg={{ span: 10 }} md={{ span: 24 }} sm={24}>
<Form.Item
label={fieldLabels.functionId}
name="functionId"
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: 10 }} md={{ span: 24 }} sm={24}>
<Form.Item
label={fieldLabels.triggerType}
name="triggerType"
rules={[{ required: true, message: '请选择JOB类型' }]}
>
<Select placeholder="请请选择JOB类型">
<Option value="job">JAVA</Option>
<Option value="procedure">存储过程</Option>
</Select>
</Form.Item>
</Col>
</Row>
<Row gutter={16}>
<Col lg={6} md={12} sm={24}>
<Form.Item
label={fieldLabels.description}
name="description"
rules={[{ required: true, message: '定时任务描述' }]}
>
<Input placeholder="请输入定时任务描述" />
</Form.Item>
</Col>
</Row>
</Card>
<Card title="定时任务参数" bordered={false}>
<Form.Item name="params">
<TableForm onChange={(datas: any) => {
console.log(datas);
//setDataForm(datas)
}
} />
</Form.Item>
</Card>
</PageHeaderWrapper>
<FooterToolbar>
{getErrorInfo(error)}
<Button type="primary" onClick={() => {
console.log("dataForm的信息为", dataForm);
form?.submit()
}} loading={submitting}>
提交
</Button>
</FooterToolbar>
</Form>
);
};
export default connect(({ loading }: { loading: { effects: { [key: string]: boolean } } }) => ({
submitting: loading.effects['jobAndJobDefineAdd/submitAdvancedForm'],
}))(JobDefineAdd);
import { PlusOutlined } from '@ant-design/icons';
import { Button, Divider, Input, Popconfirm, Table, message } from 'antd';
import React, { FC, useState, useEffect } from 'react';
import styles from '../style.less';
interface TableFormDateType {
key: string;
workId?: string;
name?: string;
department?: string;
isNew?: boolean;
editable?: boolean;
}
interface TableFormProps {
value?: TableFormDateType[];
onChange?: (value: TableFormDateType[]) => void;
}
const TableForm: FC<TableFormProps> = ({ value, onChange }) => {
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 = () => {
const newData = data?.map((item) => ({ ...item })) || [];
newData.push({
key: `NEW_TEMP_ID_${index}`,
paramName: '',
paramValue: '',
paramType: '',
paramIndex: `${index + 1}`,
description: '',
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);
console.info(target);
if (!target.paramName || !target.paramValue || !target.paramType) {
message.error('请填写完整JOB变量');
(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);
}
};
useEffect(() => {
//一家在就传传递这个给调用的组件
onChange && onChange(data);
}, [data])
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: 'paramIndex',
key: 'paramIndex',
width: '8%',
render: (text: string, record: TableFormDateType) => {
if (record.editable) {
return (
<Input
value={text}
autoFocus
onChange={(e) => handleFieldChange(e, 'paramIndex', record.key)}
onKeyPress={(e) => handleKeyPress(e, record.key)}
placeholder="paramIndex"
/>
);
}
return text;
},
},
{
title: '变量名称',
dataIndex: 'paramName',
key: 'paramName',
width: '20%',
render: (text: string, record: TableFormDateType) => {
if (record.editable) {
return (
<Input
value={text}
autoFocus
onChange={(e) => handleFieldChange(e, 'paramName', record.key)}
onKeyPress={(e) => handleKeyPress(e, record.key)}
placeholder="变量名称"
/>
);
}
return text;
},
},
{
title: '值',
dataIndex: 'paramValue',
key: 'paramValue',
width: '20%',
render: (text: string, record: TableFormDateType) => {
if (record.editable) {
return (
<Input
value={text}
onChange={(e) => handleFieldChange(e, 'paramValue', record.key)}
onKeyPress={(e) => handleKeyPress(e, record.key)}
placeholder="请输入变量值"
/>
);
}
return text;
},
},
{
title: '变量类型',
dataIndex: 'paramType',
key: 'paramType',
width: '10%',
render: (text: string, record: TableFormDateType) => {
if (record.editable) {
return (
<Input
value={text}
onChange={(e) => handleFieldChange(e, 'paramType', record.key)}
onKeyPress={(e) => handleKeyPress(e, record.key)}
placeholder="请选择变量类型"
/>
);
}
return text;
},
},
{
title: '变量描述',
dataIndex: 'description',
key: 'description',
width: '20%',
render: (text: string, record: TableFormDateType) => {
if (record.editable) {
return (
<Input
value={text}
onChange={(e) => handleFieldChange(e, 'description', 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;