需求效果功能如圖:

代碼:
const [relateList, setRelateList] = useState(getPath(initInfo, 'relateLinkList', [] || []));
這個是因為此頁面還實現了編輯功能,需要渲染初始數據,此時需要監聽一個已有的列表,進行刪除時候的刪除處理,否則初始化后刪除會出問題
<Form.Item
label="關聯鏈接"
vvspan={24}
labelCol={{ md: 3 }}
wrapperCol={{ md: 21 }}
style={{ marginBottom: '0' }}
>
<span
style={{
color: urlList.length < 20 ? '#FFA22D' : 'rgba(0, 0, 0, 0.25)',
cursor: 'pointer'
}}
onClick={add}
>
添加鏈接
</span>
</Form.Item>
<Col vvspan={24} offset={3} className={styles.relateLinkList}>
{urlList.map(k => (
<Form.Item vvspan={24} labelCol={{ md: 3 }} wrapperCol={{ md: 21 }}>
<div key={k} style={{ position: 'relative' }}>
{getFieldDecorator(`relateLinkList[${k}]`, {
validateTrigger: ['onBlur'],
rules: [
{
min: 1,
max: 2000,
message: '不可超過2000字'
},
{
pattern: /^(ht|f)tps?:\/\/(([a-zA-Z0-9_-])+(\.)?)*(:\d+)?(\/((\.)?(\?)?=?&?([a-zA-Z0-9_-]|#|%)(\?)?)*)*$/i,
message: '請輸入有效網址!'
}
],
initialValue: getPath(relateList, `${k - 1}`, '')
})(
<Input
style={{ width: '100%', paddingRight: '30px', boxSizing: 'border-box' }}
/>
)}
<i
className="icon iconfont iconshanchu"
style={{
position: 'absolute',
right: '12px',
top: '0',
cursor: 'pointer',
color: '#333333'
}}
onClick={() => remove(k)}
/>
</div>
</Form.Item>
))}
</Col>
備注:這里遍歷重新添加form.item的原因是加了網址校驗,如果只遍歷getFieldDecorator,校驗只校驗第一條數據
方法:
const remove = k => {
setUrlList(urlList.filter(key => key !== k));
setRelateList(relateList.filter((_, index) => index !== k - 1));
};
const add = () => {
const keys = form.getFieldValue('relateLinkList');
if (urlList.length < 20) {
if ((keys && keys[keys.length - 1]) || keys === undefined) {
const nextKeys = urlList.concat(urlList.length + 1);
setUrlList(nextKeys);
}
} else {
message.error('上限20個');
}
};
數據提交的校驗:
const newLinkList = values.relateLinkList && values.relateLinkList.filter(item => !!item);
const params = {
...values,
id: 0,
relateLinkList: newLinkList,
};
const result = await launchTask(params);
if (result.code === 10000) {
message.success(result.msg);
router.goBack();
pageTabUtil.closeTab('/work/task/addTask');
} else {
message.error(result.msg);
}
-------------------------------------接上文警告線-----------------------------------------------上面的實現,發生了一個意想不到的問題:如果有初始值,那么增刪都會出現bug----------------------------
因此重新封裝了組件:
👇表單項代碼:
<Form.Item
label="關聯鏈接"
vvspan={24}
labelCol={{ md: 3 }}
wrapperCol={{ md: 21 }}
style={{ marginBottom: '0' }}
>
{getFieldDecorator('relateLinkList', {
validateTrigger: ['onChange', 'onBlur'],
rules: [
{
validator(_, value, callback) {
value.some(item => {
const errorMessage = linkRef.current.getValidateError(item.link);
if (errorMessage) {
callback(errorMessage);
return true;
}
return false;
});
callback();
}
}
],
initialValue: relativeLinkData
})(<RelativeLink childRef={linkRef} />)}
</Form.Item>
拿到的數據初始化:
const relativeLinkData = (initInfo.relateLinkList || []).map((item, index) => ({ key: index + 1, link: item }));
封裝的組件:
import React from 'react';
import { message, Input } from 'antd';
import cn from 'classnames';
import styles from './RelativeLink.less';
let id;
const validators = [
{
validator: link => link && link.length <= 2000,
message: '不可超過2000字'
},
{
validator: link =>
/^(ht|f)tps?:\/\/(([a-zA-Z0-9_-])+(\.)?)*(:\d+)?(\/((\.)?(\?)?=?&?([a-zA-Z0-9_-]|#|%|\)|\()(\?)?)*)*$/i.test(
link
),
message: '請輸入有效網址'
}
];
const getValidateError = link => {
let msg;
if (!link) {
return msg;
}
validators.some(item => {
if (!item.validator(link)) {
msg = item.message;
return true;
}
return false;
});
return msg;
};
const RelativeLink = ({ value, onChange, childRef }) => {
const handleAdd = () => {
if (value && value.length < 20) {
if (!id) id = value.length || 0;
id += 1;
onChange([...value, { key: id }]);
} else {
message.error('上限20個');
}
};
const handleOnChange = (e, key) => {
onChange(
value.map(it => {
if (it.key === key) {
return { ...it, link: e.target.value };
}
return it;
})
);
};
const handleDelete = key => {
onChange(value.filter(it => it.key !== key));
};
React.useImperativeHandle(childRef, () => ({
getValidateError
}));
return (
<>
<span
style={{
color: value.length < 20 ? '#FFA22D' : 'rgba(0, 0, 0, 0.25)',
cursor: 'pointer'
}}
onClick={handleAdd}
>
添加鏈接
</span>
{value.map(item => (
<div
key={item.key}
style={{ position: 'relative' }}
className={cn({
[styles.success]: !getValidateError(item.link)
})}
>
<Input
style={{ width: '100%', paddingRight: '30px', boxSizing: 'border-box' }}
value={item.link}
onChange={e => handleOnChange(e, item.key)}
/>
<i
className="icon iconfont iconshanchu"
style={{
position: 'absolute',
right: '12px',
top: '0',
cursor: 'pointer',
color: '#333333'
}}
onClick={() => handleDelete(item.key)}
/>
</div>
))}
</>
);
};
export default RelativeLink;
引入頁面:
import { RelativeLink } from './components';
const linkRef = React.useRef();
提交時候的數據處理為網址數組:
const newLinkList =
values.relateLinkList &&
values.relateLinkList.filter(item => item.link && item.link.length > 0).map(item => item.link);
實際效果【當添加后沒輸入則不校驗,否則必須小於2000字且要求網址格式正確】
拿到的初始數據:

渲染效果:

隨意增刪,也不會出現渲染問題

