自创正则:/(http|https):\/\/(?![-])+[a-z0-9\u4e00-\u9fa5\-*.]((?![*])+[a-z0-9\u4e00-\u9fa5\-.])+((top|xyz|com|cn|edu)|(\:+(\d{4})))(?<![-])$/
子组件:
interface ValidOptions {
reg: any;
errorMsg: string;
}
interface MultipleInput {
defaultValue?: any;
isEditing?: false;
maxLength?: number;
placeholder?: string;
validOptions?: ValidOptions[];
label?: any;
onChange?(data: object): void;
}
export function MultipleInput({
defaultValue,
onChange,
isEditing,
maxLength,
placeholder,
label,
validOptions,
}: MultipleInput) {
const [uriData, setUriData] = useState(Array.isArray(defaultValue) ? defaultValue : []);
const [status, setStatus] = useState([]);
const [errorMsg, setErrorsMsg] = useState([]);
const addItem = () => {
if (uriData.length === 10) {
return;
}
setUriData([...uriData, undefined]);
};
const delItem = (index: number) => {
uriData.splice(index, 1);
setUriData([...uriData]);
status.splice(index, 1);
setStatus([...status]);
errorMsg.splice(index, 1);
setErrorsMsg([...errorMsg]);
onChange(uriData);
};
const validateUri = useCallback(
_.debounce((value: string, index: number) => {
let flag = true;
for (const item of validOptions) {
if (!item.reg.test(value)) {
flag = false;
errorMsg[index] = t(item?.errorMsg);
break;
}
}
status[index] = flag ? 'success' : 'error';
setStatus([...status]);
setErrorsMsg([...errorMsg]);
onChange(uriData);
return flag ? 'success' : 'error';
}, 300),
[uriData, status],
);
return (
<div className="ciam-mutiple-input">
<div className="app-ciam-form__item app-ciam-form-wrap">
<div className="app-ciam-form__label app-ciam-form-wrap__lable">
<label className="ciam-mutiple-input__lable">{label}</label>
</div>
<div className="app-ciam-form-wrap__item ciam-mutiple-input__content">
{isEditing ? (
<>
{uriData.map((item, index) => (
<Form.Item showStatusIcon={false} className="ciam-mutiple-input__item" status={status[index]}>
<Bubble error content={status[index] === 'error' ? errorMsg[index] : ''}>
<Input
size="l"
defaultValue={uriData[index]}
autoComplete="off"
placeholder={placeholder}
onChange={(value) => {
validateUri(value, index);
uriData[index] = value;
setUriData([...uriData]);
}}
value={item}
defaultValue={item}
maxLength={maxLength}
status={status}
/>
</Bubble>
<Text theme="label" className="icon-button" onClick={() => delItem(index)}>
<img src={COS_IMAGES.ICON_URI_DELETE} loading="eager" width={'14px'} />
<Button type="text" className="del-button">
{t('删除')}
</Button>
</Text>
</Form.Item>
))}
<Form.Item>
<Bubble error trigger="hover" content={uriData?.length === 10 ? t('最多可配置10条') : ''}>
<Text className={uriData.length < 10 ? 'icon-button' : 'icon-button disabled'} onClick={addItem}>
<img src={COS_IMAGES.ICON_URI_ADD} width={'14px'} />
<Text className="add-button" disabled={uriData?.length === 10}>
{t('添加')}
</Text>
</Text>
</Bubble>
</Form.Item>
</>
) : (
<>
{uriData?.length > 0 ? (
uriData.map((item) => (
<span style={{ lineHeight: '42px', display: 'block', wordBreak: 'break-all' }}>
{parseEmptyValue(item)}
</span>
))
) : (
<Form.Item label={getEmptySeparator()}></Form.Item>
)}
</>
)}
</div>
</div>
</div>
);
}
父组件:
export default function Cors({ record, isEdit }) {
const { control, handleSubmit, formState, reset } = useForm({ mode: 'onChange' });
const [isCardEdit, setIsCardEdit] = useState(false);
const [formInfo, setFormInfo] = useState({});
const [loading, setLoading] = useState(true);
async function onSubmit(values) {
const body = {
appId: record?.id,
urls: values.uriData,
};
const res = await applicationService.postUpdateSecurityDomain(body);
if (res?.httpStatus === 'OK') {
message.success({
content: t('保存成功'),
});
await refresh(record?.id);
setIsCardEdit(false);
}
}
const formReset = () => {
setTimeout(() => {
reset(null, {
keepDirty: false,
keepErrors: false,
keepIsSubmitted: false,
keepTouched: false,
keepIsValid: false,
keepSubmitCount: false,
});
}, 50);
};
const handleCancel = async () => {
setIsCardEdit(false);
formReset();
};
const refresh = async (appId) => {
if (!appId) {
return;
}
setLoading(true);
const res = await applicationService.querySecurityDomain({ appId });
if (res?.httpStatus === 'OK') {
await setFormInfo({ ...res?.data });
}
setLoading(false);
};
useEffect(() => {
refresh(record?.id);
}, []);
return (
<div className="application-cors">
<CorsHeader />
<Card>
<Card.Body
title={t('安全域配置')}
operation={
isEdit && !isCardEdit ? (
<Button
type="link"
onClick={(e) => {
setIsCardEdit(!isCardEdit);
e.stopPropagation();
e.preventDefault();
}}
>
{t('编辑')}
</Button>
) : (
''
)
}
>
{formInfo ? (
isCardEdit ? (
<form onSubmit={handleSubmit(onSubmit)}>
<Form>
<Controller
name="uriData"
control={control}
defaultValue={formInfo?.urls}
render={(input) => (
<MultipleInput
label={
<>
<span className="application-cors-badge">*</span>
<span>{t('安全域(CORS)')}</span>
</>
}
onChange={input.onChange}
isEditing={true}
defaultValue={formInfo?.urls}
validOptions={[{ reg: reg.url, errorMsg: t('域名输入错误') }]}
/>
)}
/>
</Form>
<Form.Action>
<Button type="primary" htmlType="submit" loading={formState.isSubmitting}>
{t('确定')}
</Button>
<Button type="primary" onClick={handleCancel}>
{t('取消')}
</Button>
</Form.Action>
</form>
) : (
<CorsView loading={loading} data={formInfo} />
)
) : (
<LoadingTip />
)}
</Card.Body>
</Card>
</div>
);
}