244 lines
10 KiB
TypeScript
244 lines
10 KiB
TypeScript
import {Button, Input, Modal, Select, Space} from "antd";
|
|
import React, {useState} from "react";
|
|
import {useBoolean} from "ahooks";
|
|
import {cloneDeep} from "lodash"
|
|
import {
|
|
BlackCreateActionType,
|
|
TBatchCreateError,
|
|
TCreateBatch
|
|
} from "@/service/types/lexicon.ts";
|
|
import {blacklist, whitelist} from "@/service/api/lexicon.ts";
|
|
import {showToast} from "../../../components/messages/Modal.tsx";
|
|
import {PlusOutlined} from "@ant-design/icons";
|
|
|
|
type SaveModalProps = {
|
|
visible?: boolean;
|
|
onClose?: (refresh?: boolean) => void;
|
|
type: 'white' | 'black'
|
|
}
|
|
|
|
type LexiconField = {
|
|
words: string | number;
|
|
type: string | number;
|
|
suggestion: string | number;
|
|
replace: string | number;
|
|
}
|
|
type LexiconFieldList = keyof LexiconField;
|
|
type FieldValue = {
|
|
value: string | number;
|
|
message?: string;
|
|
}
|
|
type LexiconFieldItem = {
|
|
[field in LexiconFieldList]: FieldValue
|
|
};
|
|
const defaultFieldRow = {
|
|
words: {value: '', message: undefined},
|
|
type: {value: 1, message: undefined},
|
|
suggestion: {value: BlackCreateActionType.Delete, message: undefined},
|
|
replace: {value: '', message: undefined},
|
|
}
|
|
const SuggestionList = [
|
|
{value: 1, label: '拦截'},
|
|
{value: 2, label: '替换'},
|
|
]
|
|
|
|
export const SaveModal: React.FC<SaveModalProps> = (props) => {
|
|
const [loading, {set: setLoading}] = useBoolean(false)
|
|
const [showResult, {setTrue}] = useBoolean(false);
|
|
|
|
const [fieldList, setFieldList] = useState<LexiconFieldItem[]>([cloneDeep(defaultFieldRow)])
|
|
const requireCheck = (row: LexiconFieldItem, key: string) => {
|
|
// @ts-ignore
|
|
const item = row[key] as FieldValue;
|
|
if (!item.value) {
|
|
item.message = '未填写'
|
|
return false;
|
|
}
|
|
if(key == 'replace' && item.value == row.words.value){
|
|
item.message = '拦截词与替换词相同';
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
const handleSubmit = () => {
|
|
const values: LexiconField[] = []
|
|
let hasError = false;
|
|
const params: TCreateBatch = {
|
|
words: [],
|
|
actionTypes: [],
|
|
replaceWords: [],
|
|
}
|
|
// 验证是否填写
|
|
fieldList.forEach(row => {
|
|
row.words.message = undefined;
|
|
row.replace.message = undefined;
|
|
if (!requireCheck(row, 'words')) hasError = true;
|
|
if (props.type === 'black' && row.suggestion.value === BlackCreateActionType.Replace && !requireCheck(row, 'replace')) hasError = true;
|
|
params.words.push(row.words.value as any);
|
|
if (props.type == 'black') {
|
|
params.actionTypes.push(row.suggestion.value as any);
|
|
params.replaceWords.push(row.replace.value as any);
|
|
}
|
|
values.push({
|
|
words: row.words.value,
|
|
type: row.type.value,
|
|
suggestion: row.suggestion.value,
|
|
replace: row.replace.value,
|
|
})
|
|
})
|
|
if (hasError) {
|
|
setFieldList(cloneDeep(fieldList));
|
|
return;
|
|
}
|
|
setFieldList(cloneDeep(fieldList));
|
|
// 执行保存
|
|
const save = props.type == 'white' ? whitelist.createBatch : blacklist.createBatch;
|
|
setLoading(true)
|
|
save(params).then(() => {
|
|
resetData()
|
|
showToast('添加成功','success');
|
|
props.onClose?.(true)
|
|
}).catch((e: BizError) => { //
|
|
if (e.data && e.code == 2) {
|
|
console.log('create e', e, e.data)
|
|
//校验失败
|
|
const data = e.data as TBatchCreateError[];
|
|
data.forEach(it => {
|
|
// 是否存在该词条
|
|
if (!fieldList[it.index]) return;
|
|
if (it.type !== 1) { // 拦截词
|
|
if (props.type == 'black') {
|
|
fieldList[it.index].replace.message = it.msg
|
|
}
|
|
} else {
|
|
fieldList[it.index].words.message = it.msg
|
|
}
|
|
})
|
|
setFieldList(cloneDeep(fieldList));
|
|
return;
|
|
}
|
|
showToast(e.message || '批量添加名单失败','warning')
|
|
}).finally(() => setLoading(false))
|
|
};
|
|
const resetData = () => {
|
|
setFieldList([
|
|
cloneDeep(defaultFieldRow)
|
|
])
|
|
}
|
|
const addItem = () => {
|
|
const item: LexiconFieldItem = cloneDeep(defaultFieldRow)
|
|
console.log('addItem', item)
|
|
setFieldList([
|
|
...fieldList,
|
|
item
|
|
])
|
|
}
|
|
const removeItem = (index: number) => {
|
|
if (fieldList.length == 1) return;
|
|
const list = cloneDeep(fieldList)
|
|
list.splice(index, 1)
|
|
setFieldList(list);
|
|
}
|
|
const setFieldValue = (key: string, index: number, value: any) => {
|
|
// @ts-ignore
|
|
const item = fieldList[index][key] as FieldValue;
|
|
item.value = value;
|
|
setFieldList(cloneDeep(fieldList));
|
|
}
|
|
|
|
return (<Modal
|
|
maskClosable={false}
|
|
open={props.visible}
|
|
className="modal-save-lexicon"
|
|
destroyOnClose={true}
|
|
onCancel={() => props.onClose?.()}
|
|
footer={null}
|
|
width={props.type == 'white' ? 700 : 850}
|
|
>
|
|
<div className={`save-modal-body save-modal-${props.type}`}>
|
|
<h2 className="text-center">添加{props.type == 'white' ? '白' : '黑'}名单词条</h2>
|
|
<div className="table-wrapper text-tip">
|
|
<div className="form-table">
|
|
<div className="header table-row">
|
|
<div className="item item-input item-words">{props.type == 'white' ? '放行词' : '拦截词'}</div>
|
|
<div className="item item-type">词条类型</div>
|
|
{props.type == 'black' && <>
|
|
<div className="item item-suggestion">操作建议</div>
|
|
<div className="item item-input item-replace">替换词</div>
|
|
</>}
|
|
<div className="item-operation"></div>
|
|
</div>
|
|
{fieldList.map((it, index) => (<div className="table-row" key={index}>
|
|
<div className="item item-input item-words">
|
|
<div className="form-item">
|
|
<Input
|
|
value={it.words.value}
|
|
onChange={e => {
|
|
setFieldValue('words', index, e.currentTarget.value)
|
|
}}
|
|
placeholder="请输入"
|
|
className={it.words.message ? 'has-error' : ''}
|
|
/>
|
|
</div>
|
|
<div className="validate-message">{it.words.message}</div>
|
|
</div>
|
|
<div className="item item-type">
|
|
<div className="form-item">
|
|
<Select
|
|
className="w-full"
|
|
onChange={value => setFieldValue('type', index, value)}
|
|
value={it.type.value}
|
|
options={[
|
|
{value: 1, label: '通用'},
|
|
]}
|
|
/>
|
|
</div>
|
|
<div className="validate-message">{it.type.message}</div>
|
|
</div>
|
|
{props.type === 'black' && <>
|
|
<div className="item item-suggestion">
|
|
<div className="form-item">
|
|
<Select
|
|
className="w-full"
|
|
onChange={value => setFieldValue('suggestion', index, value)}
|
|
value={it.suggestion.value}
|
|
options={SuggestionList}
|
|
/>
|
|
</div>
|
|
<div className="validate-message">{it.type.message}</div>
|
|
</div>
|
|
<div className="item item-input item-replace">
|
|
<div className="form-item">
|
|
<Input
|
|
disabled={it.suggestion.value != BlackCreateActionType.Replace}
|
|
value={it.replace.value}
|
|
onChange={e => setFieldValue('replace', index, e.currentTarget.value)}
|
|
placeholder={it.suggestion.value == 'rep' ? "请输入" : ''}
|
|
className={it.replace.message ? 'has-error' : ''}
|
|
/>
|
|
</div>
|
|
<div className="validate-message">{it.replace.message}</div>
|
|
</div>
|
|
</>
|
|
}
|
|
<div className="item-operation text-center">
|
|
{fieldList.length > 1 &&
|
|
<span className="pointer" onClick={() => removeItem(index)}>删除</span>}
|
|
</div>
|
|
</div>))}
|
|
</div>
|
|
<div className="text-right add-row">
|
|
<Button type="default" className="btn-default-border" icon={<PlusOutlined />} onClick={addItem}>再来一条 </Button>
|
|
</div>
|
|
|
|
<div className="text-center">
|
|
<Space size={30}>
|
|
<Button className="btn-grey" onClick={() => props.onClose?.()}>取消</Button>
|
|
<Button loading={loading} type="primary" onClick={handleSubmit}>保存</Button>
|
|
</Space>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Modal>)
|
|
}
|