老规矩,话不多说,直接上实现方式
注意:本帅用的版本 “antd”: “4.16.13”,
前提代码如下
const generateUUID = () => {
let d = new Date().getTime();
const uuid = 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, (c: any) => {
const r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c == 'x' ? r : (r & 0x3) | 0x8).toString(16);
});
return uuid;
};
const arrs = [];
for (let i = 20; i--; ) {
const uuid = generateUUID();
arrs.push({
label: uuid,
name: uuid,
key: uuid,
});
}
const [state, setstate] = useState([]);
// setstate(arrs);
useEffect(() => {
setTimeout(() => {
console.log(arrs, 'arrs');
setstate(arrs);
}, 200);
}, []);
//首先给Form添加一个属性 scrollToFirstError={true}
//然后给button按钮添加属性 htmlType="submit" 即可
//代码如下
<Form form={form} name="basic" scrollToFirstError={true}>
<Form.Item
id="sadfasf"
label="asdfa"
name="sadfasf"
rules={[{ required: true, message: "Please input your password!" }]}
>
<Input.Password />
</Form.Item>
<Form.Item
id="aaaa"
label="aaaa"
name="aaaa"
rules={[{ required: true, message: "Please input your password!" }]}
>
<Input.Password />
</Form.Item>
{state.map((item) => {
return (
<Form.Item
key={item.key}
label={item?.label}
name={item?.name}
rules={[{ required: false, message: "Please input your password!" }]}
>
<Input.Password />
</Form.Item>
);
})}
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form>;
//失败就会触发该方法
//代码如下:
<Form
form={form}
name="basic"
onFinishFailed={() => {
//关键代码
const errorList = (document as any).querySelectorAll(".ant-form-item-has-error");
//由于校验失败ant会自动给失败表单项添加类名,直接获取即可
errorList[0].scrollIntoView({
block: "center",
behavior: "smooth",
});
}}
//注意:由于dom更新异步,可能会获取不到,可以在setTimeout()中操作,vue的使用nextTick()即可
>
<Form.Item
id="sadfasf"
label="asdfa"
name="sadfasf"
rules={[{ required: true, message: "Please input your password!" }]}
>
<Input.Password />
</Form.Item>
<Form.Item
id="aaaa"
label="aaaa"
name="aaaa"
rules={[{ required: true, message: "Please input your password!" }]}
>
<Input.Password />
</Form.Item>
{state.map((item) => {
return (
<Form.Item
key={item.key}
label={item?.label}
name={item?.name}
rules={[{ required: false, message: "Please input your password!" }]}
>
<Input.Password />
</Form.Item>
);
})}
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form>;
很多情况下,表单项是通过接口获取渲染的,页面布局可能是自定义的,或嵌套多层div
//类似这种嵌套多层盒子,可能就会失效
{state.map((item) => {
return (
<div>
<div>
<div>
<span></span>
<div>
<Form.Item
key={item.key}
label={item?.label}
name={item?.name}
rules={[{ required: false, message: 'Please input your password!' }]}
>
<Input.Password />
</Form.Item>
</div>
</div>
</div>
</div>
);
})}
//这时候必须要改盒子绑定一定id才会生效
<Form form={form} name="basic" scrollToFirstError={true}>
<Form.Item
id="sadfasf"
label="asdfa"
name="sadfasf"
rules={[{ required: true, message: "Please input your password!" }]}
>
<Input.Password />
</Form.Item>
<Form.Item
id="aaaa"
label="aaaa"
name="aaaa"
rules={[{ required: true, message: "Please input your password!" }]}
>
<Input.Password />
</Form.Item>
{state.map((item) => {
return (
<div id={item.name}>
<div>
<div>
<span></span>
<div>
<Form.Item
key={item.key}
label={item?.label}
name={item?.name}
rules={[{ required: false, message: 'Please input your password!' }]}
>
<Input.Password />
</Form.Item>
</div>
</div>
</div>
</div>
);
})}
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form>;
可能有个需求,切换对应的tabs,获取相应的表单列表,那么问题就来了,必填项校验失败后,自动滚到到第一个必填项只在第一个tab中生效,其他的不生效。所以我目前是通过销毁隐藏的dom来解决(destroyInactiveTabPane)
完整代码如下
<Tabs destroyInactiveTabPane defaultActiveKey="1" onChange={onChange} tabPosition="left">
<TabPane tab="Tab 1" key="1">
<div className={styles.con} id="crfDetailContent">
<Form
form={form}
name="basic"
labelCol={{ span: 8 }}
wrapperCol={{ span: 16 }}
onFinishFailed={() => {
//注意,用方式一也是可以的---scrollToFirstError
let str = 'sadfasf';
const errorDom: any = document.getElementById(str);
errorDom.scrollIntoView({
block: 'center',
behavior: 'smooth',
});
}}
>
{console.log('state', state)}
<hr style={{ marginTop: 30 }} />
<div>
<div>
<div id="sadfasf">
<Form.Item
id="sadfasf"
label="asdfa"
name="sadfasf"
rules={[{ required: true, message: 'Please input your password!' }]}
>
<Input.Password />
</Form.Item>
</div>
</div>
</div>
<div>
<Form.Item
id="aaaa"
label="aaaa"
name="aaaa"
rules={[{ required: true, message: 'Please input your password!' }]}
>
<Input.Password />
</Form.Item>
</div>
{state.map((item) => {
return (
<div>
<div>
<div>
<span></span>
<div>
<Form.Item
key={item.key}
label={item?.label}
name={item?.name}
rules={[{ required: false, message: 'Please input your password!' }]}
>
<Input.Password />
</Form.Item>
</div>
</div>
</div>
</div>
);
})}
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form>
</div>
</TabPane>
<TabPane tab="Tab 2" key="2">
<div className={styles.con} id="crfDetailContent">
<Form
form={form}
name="basic"
labelCol={{ span: 8 }}
wrapperCol={{ span: 16 }}
// onFinish={onFinish}
onFinishFailed={() => {
let str = 'sadfasf';
const errorDom: any = document.getElementById(str);
errorDom.scrollIntoView({
block: 'center',
behavior: 'smooth',
});
}}
>
<hr style={{ marginTop: 30 }} />
<div>
<div>
<div id="sadfasf">
<Form.Item
id="sadfasf"
label="asdfa"
name="sadfasf"
rules={[{ required: true, message: 'Please input your password!' }]}
>
<Input.Password />
</Form.Item>
</div>
</div>
</div>
<div>
<Form.Item
id="aaaa"
label="aaaa"
name="aaaa"
rules={[{ required: true, message: 'Please input your password!' }]}
>
<Input.Password />
</Form.Item>
</div>
{state.map((item) => {
return (
<div>
<div>
<div>
<span></span>
<div>
<Form.Item
key={item.key}
label={item?.label}
name={item?.name}
rules={[{ required: false, message: 'Please input your password!' }]}
>
<Input.Password />
</Form.Item>
</div>
</div>
</div>
</div>
);
})}
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form>
</div>
</TabPane>
<TabPane tab="Tab 3" key="3">
Content of Tab Pane 3
</TabPane>