背景
需要实现一个类似Select组件的tags模式的输入框,输入一个邮箱后,回车,会显示成tag样式,也可以删除每一个tag。
实现
技术栈:react-tag-input-component + Antd
目前Antd没有提供现成的组件,可以使用react-tag-input-component,回车生成tag的时候进行校验邮箱是否合法,并显示错误提示
在进行表单提交的时候,也可以对tags进行非空和合法性校验。
EmailsFormInput/index.tsx:
javascript">import { Form, FormItemProps } from 'antd';
import { forwardRef, useImperativeHandle, useState } from 'react';
import { TagsInput, TagsInputProps } from 'react-tag-input-component';
import styles from './index.module.less';
interface Props {
formItemProps: FormItemProps;
tagsInputProps: TagsInputProps;
}
const EmailErrorText = 'Please enter a valid email';
const EmailsEmptyErrorText = 'Please enter email';
const EmailReg = /^[\w.-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(,[\w.-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})*$/g;
export const EmailsFormInput = forwardRef(({ formItemProps, tagsInputProps }: Props, ref: any) => {
const [emailErrorTip, setEmailErrorTip] = useState<string>('');
const handleValidateEmail = (emails: string[]) => {
// 必填
if (!emails?.length) {
setEmailErrorTip(EmailsEmptyErrorText);
return false;
}
// 邮箱格式是否正确
if (!emails?.every((e) => new RegExp(EmailReg).test(e))) {
setEmailErrorTip(EmailErrorText);
return false;
}
return true;
};
useImperativeHandle(ref, () => ({
// 暴露对emails的校验方法
handleValidateEmail,
}));
return (
// validateStatus 和 help 用于错误提示
<Form.Item {...formItemProps} help={emailErrorTip} validateStatus="error" className={styles.tagInputFormItem}>
<div className={styles.tagInputContainer}>
<TagsInput
placeHolder="Input email and enter to add more"
classNames={{ tag: styles.tagContainer, input: styles.inputContainer }}
{...tagsInputProps}
onKeyUp={(e) => {
if (e.key === 'Backspace') {
// 删除操作的时候,需要清空错误提示
setEmailErrorTip('');
}
}}
beforeAddValidate={(tag: string) => {
// 添加tag前对输入的字符进行校验
if (tag && new RegExp(EmailReg).test(tag)) {
setEmailErrorTip('');
return true;
}
setEmailErrorTip(EmailErrorText);
return false;
}}
/>
</div>
</Form.Item>
);
});
EmailsFormInput/index.module.less: 修改ui以适应Antd的设计风格
.tagInputFormItem {
.tagInputContainer {
div:first-child {
padding: 1px 2px;
--rti-main: rgb(36, 126, 252);
border: 1px solid #ccc;
min-height: 54px;
display: flex;
justify-content: flex-start;
align-items: flex-start;
}
}
.tagContainer {
background-color: rgba(0, 0, 0, 0.06);
margin: 2px 4px 2px 0;
height: 24px;
button {
height: 10px;
width: 10px;
font-size: 10px;
color: rgba(0, 0, 0, 0.45);
}
}
.inputContainer {
height: 28px;
width: 40%;
margin-left: 8px;
color: rgba(0, 0, 0, 0.88);
}
}
使用
javascript"><EmailsFormInput
formItemProps={{
name: 'approver_list',
label: 'Approver(s)',
rules: [
{
required: true,
message: 'Approver',
},
],
}}
tagsInputProps={{ value: approvers, onChange: setApprovers }}
ref={approverEmailInputRef}
/>