180 lines
8.0 KiB
TypeScript
180 lines
8.0 KiB
TypeScript
import React, {useMemo} from "react";
|
|
import {RightOutlined} from "@ant-design/icons";
|
|
import {Popover, Space} from "antd";
|
|
import {css} from "@emotion/css";
|
|
import {DropdownMenu} from "@/components/popover/dropdown-menu";
|
|
import {
|
|
ProofreadStateEnum,
|
|
ProofreadTypeEnum,
|
|
TCorrectedContent,
|
|
} from "@/service/types/document";
|
|
import {showToast} from "@/components/messages/Modal.tsx";
|
|
import {whitelist} from "@/service/api/lexicon.ts";
|
|
import {useSetState} from "ahooks";
|
|
import {
|
|
IconAccept,
|
|
IconDotMore2,
|
|
IconIgnore,
|
|
IconPlus,
|
|
IconReview,
|
|
SvgIcon
|
|
} from "./icons.tsx";
|
|
|
|
|
|
export type ActionKey = 'click' | 'accept' | 'acceptAll' | 'ignore' | 'ignoreAll' | 'addToLexicon' | 'review' | 'redo'
|
|
type ProofreadItemProps = {
|
|
selected?: boolean;
|
|
className?: string | undefined;
|
|
previewMode?: boolean;
|
|
last?: boolean;
|
|
onAction: (key: ActionKey) => void
|
|
it: TCorrectedContent
|
|
index?: number;
|
|
}
|
|
|
|
const ActionToolTip = (props: {open?:boolean; text: string; children: React.ReactNode }) => (
|
|
<Popover
|
|
open={props.open}
|
|
overlayClassName="black-bg"
|
|
content={props.text}
|
|
>{props.children}</Popover>)
|
|
|
|
function getDescription(it: TCorrectedContent) {
|
|
// 获取类型数据
|
|
// const typeData = getTypeData(it.type)!;
|
|
|
|
const str = [`【${it.typeData?.text}】`]
|
|
if (it.typeData?.type === 'black' || it.type == ProofreadTypeEnum.sensitive) {
|
|
str.push('请复核',it.tag == 'r' && !/^[*]+$/.test(it.text) ?`,将选用"${it.text}"`:'')
|
|
} else if (it.tag == 'r') {
|
|
str.push(`建议选用"${it.text}"`)
|
|
} else {
|
|
str.push('建议' + (it.tag == 'd' ? '删除' : '新增'))
|
|
}
|
|
return str.join('');
|
|
// return typeData?.text ? typeData?.text + ',' : ''}{props.it.tag == 'r' ? `选用"${props.it.text}"` : (
|
|
// it.tag == 'd' ? '删除' : '新增'
|
|
}
|
|
|
|
export const ProofreadItem: React.FC<ProofreadItemProps> = (props) => {
|
|
|
|
const requireReview = useMemo(() => {
|
|
// 敏感词或者要拦截的黑名单
|
|
return props.it.type == ProofreadTypeEnum.sensitive || props.it.type == ProofreadTypeEnum.blackWordBlock || (
|
|
props.it.type == ProofreadTypeEnum.blackWord
|
|
&& props.it.tag == 'r'
|
|
&& /^[*]+$/.test(props.it.text)
|
|
);
|
|
}, [props.it])
|
|
|
|
// 当前类别颜色
|
|
const colorStyle = css`--proofread-color: ${props.it.typeData?.color || '#000000'}`
|
|
const [state, setState] = useSetState({
|
|
addToLexiconVisible: true
|
|
})
|
|
|
|
// 添加到词库
|
|
const handleAddToLexicon = () => {
|
|
whitelist.create({
|
|
word: props.it.origin
|
|
}).then(() => {
|
|
showToast('加入白名单成功', 'success');
|
|
// 隐藏添加按钮
|
|
setState({
|
|
addToLexiconVisible: false
|
|
})
|
|
}).catch((e) => {
|
|
console.log(e);
|
|
showToast('加入白名单失败', 'warning')
|
|
})
|
|
}
|
|
// const handleUndo = async () => {
|
|
// showToast('撤销成功')
|
|
// }
|
|
const AddToLexiconButton = ({disabled}:{disabled:boolean}) => {
|
|
const AddButton = <button disabled={disabled} className="btn btn-add-to-lexicon" onClick={handleAddToLexicon}>
|
|
<SvgIcon component={IconPlus}/>
|
|
</button>;
|
|
return (props.selected && state.addToLexiconVisible && props.it.type != ProofreadTypeEnum.blackWord) ?
|
|
disabled ? AddButton : (<ActionToolTip text="加入白名单">
|
|
{AddButton}
|
|
</ActionToolTip>) : null
|
|
}
|
|
return (<>
|
|
<div
|
|
onClick={() => props.onAction('click')}
|
|
className={`proofread-item ${props.it.isAccept == ProofreadStateEnum.Default?'':'processed'} proofread-action-${props.it.action} proofread-item-${props.it.id} ${props.className} ${colorStyle} item ${props.selected ? 'select' : ''}`}>
|
|
<div className="info">
|
|
<div className="text d-flex align-center">
|
|
<span className="origin">{
|
|
props.it.tag == 'i' ? props.it.text : (
|
|
props.it.origin == ' ' ? '空格' : props.it.origin
|
|
)
|
|
}</span>
|
|
{
|
|
props.it.type == ProofreadTypeEnum.blackWord ?
|
|
<span className="action-text">黑名单</span> : (props.it.typeData?.type == 'sensitive' ?
|
|
<span className="action-text">敏感词</span> : (props.it.tag == 'r'
|
|
? <>
|
|
<span className="arrow"><RightOutlined/></span>
|
|
<span className="replaced">{props.it.text}</span>
|
|
</>
|
|
: <span className="action-text">{props.it.tag == 'd' ? '删除' : '新增'}</span>))
|
|
|
|
}
|
|
</div>
|
|
<div className="description">
|
|
{getDescription(props.it)}
|
|
</div>
|
|
</div>
|
|
{!props.previewMode && <div className="action align-center">
|
|
{props.selected && (props.it.isAccept == ProofreadStateEnum.Default) && <Space align={'center'}>
|
|
{!requireReview && <ActionToolTip text="采纳">
|
|
<button className="btn btn-accept" onClick={() => props.onAction('accept')}>
|
|
<SvgIcon component={IconAccept}/>
|
|
</button>
|
|
</ActionToolTip>}
|
|
{requireReview && <ActionToolTip text="复核">
|
|
<button className="btn btn-review" onClick={() => props.onAction('review')}>
|
|
<SvgIcon component={IconReview}/>
|
|
</button>
|
|
</ActionToolTip>}
|
|
<ActionToolTip text="忽略">
|
|
<button className="btn btn-odd btn-ignore" onClick={() => props.onAction('ignore')}>
|
|
<SvgIcon component={IconIgnore}/>
|
|
</button>
|
|
</ActionToolTip>
|
|
<AddToLexiconButton disabled={requireReview}/>
|
|
<DropdownMenu
|
|
theme="dark"
|
|
items={!requireReview ? [
|
|
{key: 'acceptAll', label: '采纳全部相同结果', onClick: () => props.onAction('acceptAll')},
|
|
{type: 'divider'},
|
|
{key: 'accept', label: '忽略全部相同结果', onClick: () => props.onAction('ignoreAll')},
|
|
] : [{key: 'accept', label: '忽略全部相同结果', onClick: () => props.onAction('ignoreAll')}]}
|
|
>
|
|
<div className="more-action"><SvgIcon component={IconDotMore2} size={12}/></div>
|
|
</DropdownMenu>
|
|
</Space>}
|
|
{props.it.isAccept != ProofreadStateEnum.Default &&
|
|
<div className="processed-wrapper align-center">
|
|
{props.it.isAccept == ProofreadStateEnum.Accept && <div className="state-accept align-center">
|
|
<SvgIcon component={IconAccept}/>
|
|
<span className="ml-2">已采纳</span>
|
|
</div>}
|
|
{props.it.isAccept == ProofreadStateEnum.Review &&
|
|
<div className="state-accept state-review align-center">
|
|
<SvgIcon component={IconReview}/>
|
|
<span className="ml-2">已复核</span>
|
|
</div>}
|
|
{props.it.isAccept == ProofreadStateEnum.Ignore && <div className="state-ignore align-center">
|
|
<SvgIcon component={IconIgnore}/>
|
|
<span className="ml-2">已忽略</span>
|
|
</div>}
|
|
</div>}
|
|
</div>}
|
|
</div>
|
|
{props.selected && !props.last && <div className={'selected-line'}></div>}
|
|
</>)
|
|
}
|