Update from Vibe Studio
This commit is contained in:
356
src/pages/spell-check-system/index.tsx
Normal file
356
src/pages/spell-check-system/index.tsx
Normal file
@@ -0,0 +1,356 @@
|
||||
import { useState } from 'react'
|
||||
import { Button, Card, Form, Input, Table, Switch, Space, message } from 'antd'
|
||||
import { detectSpellingErrors, SpellCheckResult } from '@/api/spell-check-system'
|
||||
|
||||
interface FeedbackItem extends SpellCheckResult {
|
||||
id: string
|
||||
status: 'pending' | 'confirmed' | 'rejected'
|
||||
}
|
||||
|
||||
const SpellCheckSystem = () => {
|
||||
const [form] = Form.useForm()
|
||||
const [isDetecting, setIsDetecting] = useState(false)
|
||||
const [detectionResults, setDetectionResults] = useState<FeedbackItem[]>([])
|
||||
const [feedbackData, setFeedbackData] = useState<{
|
||||
confirmed: string[]
|
||||
rejected: string[]
|
||||
}>({
|
||||
confirmed: [],
|
||||
rejected: []
|
||||
})
|
||||
|
||||
const handleDetect = async () => {
|
||||
try {
|
||||
const formValues = await form.validateFields()
|
||||
|
||||
// 验证用户输入文本
|
||||
if (!formValues.text_input?.trim()) {
|
||||
message.warning('请输入需要检测的文本内容')
|
||||
return
|
||||
}
|
||||
|
||||
setIsDetecting(true)
|
||||
setDetectionResults([])
|
||||
|
||||
const response = await detectSpellingErrors({
|
||||
text_input: formValues.text_input,
|
||||
enable_error_category: formValues.enable_error_category || false,
|
||||
enable_correction_suggestion: formValues.enable_correction_suggestion !== false,
|
||||
enable_batch_processing: formValues.enable_batch_processing || false
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text()
|
||||
throw new Error(`检测请求失败: ${response.status} ${errorText}`)
|
||||
}
|
||||
|
||||
if (!response.body) {
|
||||
throw new Error('响应体为空')
|
||||
}
|
||||
|
||||
const reader = response.body.getReader()
|
||||
const decoder = new TextDecoder('utf-8')
|
||||
let buffer = ''
|
||||
let fullContent = ''
|
||||
let results: SpellCheckResult[] = []
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) break
|
||||
|
||||
buffer += decoder.decode(value, { stream: true })
|
||||
const lines = buffer.split('\n')
|
||||
buffer = lines.pop() || ''
|
||||
|
||||
for (const line of lines) {
|
||||
const trimmedLine = line.trim()
|
||||
if (!trimmedLine || trimmedLine === 'data: [DONE]') {
|
||||
if (trimmedLine === 'data: [DONE]') {
|
||||
message.success('检测完成')
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if (trimmedLine.startsWith('data: ')) {
|
||||
try {
|
||||
const data = trimmedLine.slice(6)
|
||||
const parsed = JSON.parse(data)
|
||||
|
||||
if (parsed.event === 'message' && parsed.answer) {
|
||||
fullContent += parsed.answer
|
||||
// 尝试解析JSON结果
|
||||
try {
|
||||
results = JSON.parse(fullContent)
|
||||
const feedbackItems: FeedbackItem[] = results.map((item, index) => ({
|
||||
...item,
|
||||
id: `item-${index}`,
|
||||
status: 'pending' as const
|
||||
}))
|
||||
setDetectionResults(feedbackItems)
|
||||
} catch (e) {
|
||||
// 解析失败,继续等待
|
||||
}
|
||||
} else if (parsed.event === 'error') {
|
||||
throw new Error(parsed.message || 'Dify API 返回错误')
|
||||
}
|
||||
} catch (parseError) {
|
||||
console.warn('跳过无法解析的行:', trimmedLine)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
reader.releaseLock()
|
||||
}
|
||||
|
||||
if (results.length === 0 && fullContent) {
|
||||
try {
|
||||
results = JSON.parse(fullContent)
|
||||
const feedbackItems: FeedbackItem[] = results.map((item, index) => ({
|
||||
...item,
|
||||
id: `item-${index}`,
|
||||
status: 'pending' as const
|
||||
}))
|
||||
setDetectionResults(feedbackItems)
|
||||
} catch (e) {
|
||||
message.warning('检测完成,但结果格式异常')
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('检测错误:', error)
|
||||
message.error(error instanceof Error ? error.message : '检测失败,请稍后重试')
|
||||
} finally {
|
||||
setIsDetecting(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleFeedback = (id: string, status: 'confirmed' | 'rejected') => {
|
||||
setDetectionResults(prev =>
|
||||
prev.map(item =>
|
||||
item.id === id ? { ...item, status } : item
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const handleSubmitFeedback = () => {
|
||||
const confirmed: string[] = []
|
||||
const rejected: string[] = []
|
||||
|
||||
detectionResults.forEach(item => {
|
||||
if (item.status === 'confirmed') {
|
||||
confirmed.push(item.suggestion)
|
||||
} else if (item.status === 'rejected') {
|
||||
rejected.push(item.error_word)
|
||||
}
|
||||
})
|
||||
|
||||
const feedback = { confirmed, rejected }
|
||||
|
||||
// 存储到本地存储
|
||||
localStorage.setItem('spell_check_feedback', JSON.stringify(feedback))
|
||||
setFeedbackData(feedback)
|
||||
|
||||
message.success('反馈数据已提交并保存到本地存储')
|
||||
}
|
||||
|
||||
const renderFeedbackButtons = (record: FeedbackItem) => {
|
||||
if (record.status !== 'pending') {
|
||||
return (
|
||||
<span style={{ color: record.status === 'confirmed' ? '#52c41a' : '#ff4d4f', fontWeight: 'bold' }}>
|
||||
{record.status === 'confirmed' ? '已确认' : '已否决'}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Space size="small">
|
||||
<Button
|
||||
size="small"
|
||||
type="primary"
|
||||
onClick={() => handleFeedback(record.id, 'confirmed')}
|
||||
style={{ fontSize: '12px', height: '24px' }}
|
||||
>
|
||||
确认
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
danger
|
||||
onClick={() => handleFeedback(record.id, 'rejected')}
|
||||
style={{ fontSize: '12px', height: '24px' }}
|
||||
>
|
||||
否决
|
||||
</Button>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '错误词',
|
||||
dataIndex: 'error_word',
|
||||
key: 'error_word',
|
||||
width: 120,
|
||||
render: (text: string) => (
|
||||
<span style={{ color: '#ff4d4f', fontWeight: 'bold' }}>{text}</span>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '上下文',
|
||||
dataIndex: 'context',
|
||||
key: 'context',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '建议词',
|
||||
dataIndex: 'suggestion',
|
||||
key: 'suggestion',
|
||||
width: 120,
|
||||
render: (text: string) => (
|
||||
<span style={{ color: '#52c41a', fontWeight: 'bold' }}>{text}</span>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '错误类型',
|
||||
dataIndex: 'error_type',
|
||||
key: 'error_type',
|
||||
width: 120,
|
||||
render: (text: string) => text || '-'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 150,
|
||||
render: (_: any, record: FeedbackItem) => renderFeedbackButtons(record)
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<div style={{ maxWidth: '1000px', margin: '0 auto', padding: '20px' }}>
|
||||
{/* 页面标题区 */}
|
||||
<div style={{ textAlign: 'center', marginBottom: '40px' }}>
|
||||
<h1 style={{ color: '#1890ff', fontSize: '32px', fontWeight: 'bold', margin: 0 }}>
|
||||
错别字检测与修正系统
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<Space direction="vertical" style={{ width: '100%' }} size="large">
|
||||
{/* 参数输入区 */}
|
||||
<Card title="参数输入区">
|
||||
<Form form={form} layout="vertical" initialValues={{
|
||||
enable_error_category: false,
|
||||
enable_correction_suggestion: true,
|
||||
enable_batch_processing: false
|
||||
}}>
|
||||
<Form.Item
|
||||
name="text_input"
|
||||
label="待检测文本"
|
||||
rules={[
|
||||
{ required: true, message: '请输入需要检测的文本内容' }
|
||||
]}
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={6}
|
||||
placeholder="请输入需要检测错别字的文本内容..."
|
||||
style={{ fontSize: '14px' }}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<div style={{ display: 'flex', gap: '24px', flexWrap: 'wrap', marginTop: '16px' }}>
|
||||
<Form.Item
|
||||
name="enable_error_category"
|
||||
label="是否启用分类"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="enable_correction_suggestion"
|
||||
label="是否启用建议"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="enable_batch_processing"
|
||||
label="是否启用批量处理"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Form>
|
||||
</Card>
|
||||
|
||||
{/* 操作按钮区 */}
|
||||
<div style={{ display: 'flex', justifyContent: 'center', gap: '16px' }}>
|
||||
<Button
|
||||
type="primary"
|
||||
size="large"
|
||||
onClick={handleDetect}
|
||||
loading={isDetecting}
|
||||
style={{ minWidth: '120px', height: '40px', fontSize: '16px' }}
|
||||
>
|
||||
检测
|
||||
</Button>
|
||||
<Button
|
||||
size="large"
|
||||
onClick={() => {
|
||||
form.resetFields()
|
||||
setDetectionResults([])
|
||||
setFeedbackData({ confirmed: [], rejected: [] })
|
||||
message.info('已重置表单')
|
||||
}}
|
||||
disabled={isDetecting}
|
||||
style={{ minWidth: '120px', height: '40px', fontSize: '16px' }}
|
||||
>
|
||||
重置
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 内容展示区 */}
|
||||
<Card
|
||||
title="错别字检测结果"
|
||||
style={{
|
||||
display: detectionResults.length > 0 || isDetecting ? 'block' : 'none'
|
||||
}}
|
||||
>
|
||||
{isDetecting ? (
|
||||
<div style={{ textAlign: 'center', color: '#999', padding: '40px' }}>
|
||||
正在检测中,请稍候...
|
||||
</div>
|
||||
) : detectionResults.length > 0 ? (
|
||||
<>
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={detectionResults}
|
||||
rowKey="id"
|
||||
pagination={{ pageSize: 10 }}
|
||||
scroll={{ y: 400 }}
|
||||
/>
|
||||
<div style={{ marginTop: '16px', textAlign: 'center' }}>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={handleSubmitFeedback}
|
||||
disabled={detectionResults.every(item => item.status === 'pending')}
|
||||
style={{ minWidth: '140px', height: '36px', fontSize: '14px' }}
|
||||
>
|
||||
提交反馈
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div style={{ textAlign: 'center', color: '#999' }}>
|
||||
错别字检测结果将在这里显示
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</Space>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SpellCheckSystem
|
||||
Reference in New Issue
Block a user