import React, { useMemo, useState } from 'react'; import { Modal, Button, Card, Statistic, Row, Col, message, theme } from 'antd'; import { CheckOutlined, CloseOutlined, SwapOutlined } from '@ant-design/icons'; import ReactDiffViewer from 'react-diff-viewer-continued'; import { useThemeMode } from '../theme/useThemeMode'; interface ChapterContentComparisonProps { visible: boolean; onClose: () => void; chapterId: string; chapterTitle: string; originalContent: string; newContent: string; wordCount: number; onApply: () => void; onDiscard: () => void; } const ChapterContentComparison: React.FC = ({ visible, onClose, chapterId, chapterTitle, originalContent, newContent, wordCount, onApply, onDiscard }) => { const { token } = theme.useToken(); const { resolvedMode } = useThemeMode(); const [applying, setApplying] = useState(false); const [viewMode, setViewMode] = useState<'split' | 'unified'>('split'); const [modal, contextHolder] = Modal.useModal(); const originalWordCount = originalContent.length; const wordCountDiff = wordCount - originalWordCount; const wordCountDiffPercent = ((wordCountDiff / originalWordCount) * 100).toFixed(1); const isDarkMode = resolvedMode === 'dark'; const diffViewerStyles = useMemo(() => ({ variables: { light: { diffViewerBackground: token.colorBgContainer, diffViewerColor: token.colorText, diffViewerTitleBackground: token.colorBgElevated, diffViewerTitleColor: token.colorTextHeading, addedBackground: token.colorSuccessBg, addedColor: token.colorText, removedBackground: token.colorErrorBg, removedColor: token.colorText, wordAddedBackground: token.colorSuccessBorder, wordRemovedBackground: token.colorErrorBorder, addedGutterBackground: token.colorSuccessBg, removedGutterBackground: token.colorErrorBg, gutterBackground: token.colorFillQuaternary, gutterBackgroundDark: token.colorFillTertiary, highlightBackground: token.colorWarningBg, highlightGutterBackground: token.colorWarningBorder, }, dark: { diffViewerBackground: token.colorBgContainer, diffViewerColor: token.colorText, diffViewerTitleBackground: token.colorBgElevated, diffViewerTitleColor: token.colorTextHeading, addedBackground: 'rgba(82, 196, 26, 0.16)', addedColor: token.colorText, removedBackground: 'rgba(255, 77, 79, 0.16)', removedColor: token.colorText, wordAddedBackground: 'rgba(82, 196, 26, 0.3)', wordRemovedBackground: 'rgba(255, 77, 79, 0.3)', addedGutterBackground: 'rgba(82, 196, 26, 0.12)', removedGutterBackground: 'rgba(255, 77, 79, 0.12)', gutterBackground: token.colorFillQuaternary, gutterBackgroundDark: token.colorFillSecondary, highlightBackground: 'rgba(250, 173, 20, 0.18)', highlightGutterBackground: 'rgba(250, 173, 20, 0.28)', }, }, line: { padding: '10px 2px', fontSize: '14px', lineHeight: '20px', whiteSpace: 'pre-wrap' as const, wordBreak: 'break-word' as const, }, }), [token]); const handleApply = async () => { setApplying(true); try { const response = await fetch(`/api/chapters/${chapterId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ content: newContent }) }); if (!response.ok) { throw new Error('应用新内容失败'); } message.success('新内容已应用!'); // 先调用 onApply 通知父组件刷新 onApply(); // 延迟触发章节分析,给父组件时间刷新 setTimeout(async () => { try { const analysisResponse = await fetch(`/api/chapters/${chapterId}/analyze`, { method: 'POST', headers: { 'Content-Type': 'application/json', } }); if (analysisResponse.ok) { message.success('章节分析已开始,请稍后查看结果'); } else { message.warning('章节分析触发失败,您可以手动触发分析'); } } catch (analysisError) { console.error('触发分析失败:', analysisError); message.warning('章节分析触发失败,您可以手动触发分析'); } }, 500); onClose(); } catch (error: unknown) { const err = error as Error; message.error(err.message || '应用失败'); } finally { setApplying(false); } }; const handleDiscard = () => { modal.confirm({ title: '确认放弃', content: '确定要放弃新生成的内容吗?此操作不可恢复。', centered: true, okText: '确定放弃', cancelText: '取消', okButtonProps: { danger: true }, onOk: () => { onDiscard(); onClose(); message.info('已放弃新内容'); } }); }; return ( <> {contextHolder} } onClick={handleDiscard} > 放弃新内容 , , ]} > {/* 统计信息 */} 0 ? 'var(--color-success)' : 'var(--color-error)' }} prefix={wordCountDiff > 0 ? '+' : ''} /> 0 ? '+' : ''} /> {/* 内容对比 */}
); }; export default ChapterContentComparison;