2025-03-21 13:09:08 +08:00

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>)
}