先上图,功能很简单,记录分享
之前在开发中,基本都是使用antd3.x 版本,冷不丁切换到4.x 版本,有些不适应
开始
功能看起来是很简单的,主要使用了Form、Form.List,其中使用了ahooks中的useDynamicList
对比antd3.x版本,4.x版本 form
- 取消了绑定函数,采用name(依然支持嵌套)
- 4,x修改表单不会再触发表单的更新,需要手动控制更新
- 4.x增加了Form.List,将一些逻辑封装到了一起
- ......
具体代码,可以查看github
show code
// 部分代码
const EditForm: React.FC<Props> = () => {
const [params, setParams] = useState({});
const [editStatus, setEditStatus] = useState<boolean>(false);
const { list, push, remove, getKey, resetList } = useDynamicList<ListItem>(
defaultList,
);
const [form] = Form.useForm();
// 提交
const submit = () => {
form.validateFields().then(values => {
setParams(values);
});
};
// 设置初值, 多条Form.List 需要动态设置初值
useEffect(() => {
if (editStatus) {
resetList(editData);
const grades: FormValues['grades'] = [];
editData.forEach(item => {
const teams: ListItem['teams'] = [];
item.teams.forEach(routerItem =>
teams.push({
...routerItem,
teamName: item.teamName,
teamPosition: item.teamPosition,
}),
);
grades.push({
class: item.class,
teams,
});
});
form.setFieldsValue({ grades: grades });
} else {
form.setFieldsValue({ grades: defaultList });
resetList(defaultList);
}
}, [editStatus]);
// 添加TabPane
const addTabPane = () => {
push({
title: '',
teams: [
{
axis: [{}],
},
],
});
// 给表单添加默认值
const values = form.getFieldValue('grades');
values.push({
teams: [
{
axis: [{}],
},
],
});
form.setFieldsValue({ grades: values });
};
const layout = {
labelCol: { span: 2 },
wrapperCol: { span: 14 },
};
return (
<>
<Button
style={{ marginBottom: 8 }}
type="primary"
onClick={() => setEditStatus(!editStatus)}
>
切换{editStatus ? '新增' : '编辑'}
</Button>
<Form {...layout} labelCol={{ span: 2 }} form={form}>
<Tabs
type="editable-card"
hideAdd
tabBarExtraContent={<Button onClick={addTabPane}>添加</Button>}
>
{list.map((_, index) => (
<TabPane
forceRender
key={`${getKey(index)}`}
closeIcon={<CloseOutlined onClick={() => remove(index)} />}
tab={`班级 ${getKey(index) + 1}`}
>
<Item
{...layout}
label="班级"
name={['grades', getKey(index), 'class']}
rules={[
{
required: true,
message: '请填写班级',
},
]}
>
<Input placeholder="请填写班级" />
</Item>
<FormList parentIndex={getKey(index)} />
</TabPane>
))}
</Tabs>
</Form>
<Button
type="primary"
style={{ marginTop: 8, width: '100%' }}
onClick={submit}
>
提交
</Button>
{Object.keys(params).length > 0 && (
<pre>{JSON.stringify(params, null, '\t')}</pre>
)}
</>
);
};
export default EditForm;
自定义FormList
const AxisFormList: React.FC<AxisFormListProps> = ({
parentIndex,
collapseIndex,
}) => {
return (
<List {...layout} name={[collapseIndex, 'axis']}>
{(fields, { add, remove }) => (
<>
{fields.map(routerItem => (
<Row style={{ width: '100%' }} key={routerItem.key}>
<Item
style={{ width: '40%' }}
rules={[
{
required: true,
message: '请选择',
},
({ getFieldValue }) => ({
validator(rule, value) {
// 自定义校验名称重复
if (!value) {
return Promise.resolve();
}
const axis = getFieldValue([
'grades',
parentIndex,
'teams',
collapseIndex,
'axis']).map((item: AxisItem) => item.row) as string[];
const index = axis.findIndex(item => item === value)
axis.splice(index, 1)
let infoPromise = Promise.resolve();
axis.forEach((checkItem: string) => {
if (checkItem === value) {
infoPromise = Promise.reject('排名称重复');
}
})
return infoPromise;
},
}),
]}
{...routerItemLayout}
label="排"
initialValue=""
fieldKey={[routerItem.fieldKey, 'row']}
name={[routerItem.fieldKey, 'row']}
>
<Select>
<Select.Option value="one">第一排</Select.Option>
<Select.Option value="two">第二排</Select.Option>
<Select.Option value="three">第三排</Select.Option>
</Select>
</Item>
<Item shouldUpdate noStyle>
{/* 4.x触发更新需要包一层Item */}
{({ getFieldValue }) => {
const getDefaultValue =
getFieldValue([
'grades',
parentIndex,
'teams',
collapseIndex,
'axis',
routerItem.fieldKey,
'row',
]) === 'three';
return (
<Item
style={{ width: '40%' }}
rules={[
{
required: !getDefaultValue && true,
message: '请填写',
},
]}
{...routerItemLayout}
label="列"
fieldKey={[routerItem.fieldKey, 'column']}
name={[routerItem.fieldKey, 'column']}
>
<Input
disabled={getDefaultValue}
/>
</Item>
);
}}
</Item>
<Item>
<DeleteOutlined onClick={() => remove(routerItem.name)} />
</Item>
</Row>
))}
<Button
type="dashed"
style={{ marginTop: 8, width: '50%' }}
onClick={() => add()}
>
添加
</Button>
</>
)}
</List>
);
};
const FormList: React.FC<Props> = ({ parentIndex }) => {
return (
<List {...layout} name={['grades', parentIndex, 'teams']}>
{(fields, { add, remove }) => (
<>
{fields.map((collapseItem, collapseIndex) => (
<Collapse
style={{ marginBottom: 24 }}
defaultActiveKey={[`${collapseItem.fieldKey}`]}
key={collapseItem.fieldKey}
>
<Panel
forceRender
extra={
<CloseOutlined onClick={() => remove(collapseItem.name)} />
}
header={
<Item
style={{
marginBottom: 0,
width: '50%',
display: 'inline-flex',
}}
rules={[
{
required: true,
message: '请填写小组名称',
},
]}
{...routerItemLayout}
label="小组名称"
name={[collapseItem.name, 'teamName']}
fieldKey={[collapseItem.fieldKey, 'teamName']}
>
<Input
placeholder="请填写小组名称"
onClick={e => e.stopPropagation()}
addonBefore="小组名称"
/>
</Item>
}
key={`${collapseItem.fieldKey}`}
>
<Item
rules={[
{
required: true,
message: '请填写小组位置',
},
]}
{...layout}
label="小组位置"
name={[collapseItem.name, 'teamPosition']}
fieldKey={[collapseItem.fieldKey, 'teamPosition']}
>
<Input placeholder="请填写小组位置" />
</Item>
<Item
style={{ position: 'absolute' }}
initialValue="NONE"
{...layout}
name={[collapseIndex, 'type']}
>
<Input hidden />
</Item>
<AxisFormList
parentIndex={parentIndex}
collapseIndex={collapseIndex}
/>
</Panel>
</Collapse>
))}
<Button
type="dashed"
style={{ marginTop: 8, width: '100%' }}
onClick={() =>
add({
axis: [{}],
})
}
>
添加
</Button>
</>
)}
</List>
);
};
export default FormList;