react+antd form表单项动态添加数据的实现

需求效果功能如图:

 

 

代码:

    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字且要求网址格式正确】

拿到的初始数据:

 

 渲染效果:

 

 随意增删,也不会出现渲染问题

 

 

posted @ 2020-06-19 10:48  芝麻小仙女  阅读(16872)  评论(0编辑  收藏  举报