Update from Vibe Studio
This commit is contained in:
46
src/api/spell-check-system.ts
Normal file
46
src/api/spell-check-system.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
export interface SpellCheckSystemRequest {
|
||||
text_input: string
|
||||
enable_error_category: boolean
|
||||
enable_correction_suggestion: boolean
|
||||
enable_batch_processing: boolean
|
||||
}
|
||||
|
||||
export interface DifyRequest {
|
||||
inputs: {
|
||||
text_input: string
|
||||
enable_error_category: boolean
|
||||
enable_correction_suggestion: boolean
|
||||
enable_batch_processing: boolean
|
||||
}
|
||||
query: string
|
||||
response_mode: string
|
||||
}
|
||||
|
||||
export interface SpellCheckResult {
|
||||
error_word: string
|
||||
context: string
|
||||
suggestion: string
|
||||
error_type?: string
|
||||
}
|
||||
|
||||
export function detectSpellingErrors(params: SpellCheckSystemRequest) {
|
||||
const requestBody: DifyRequest = {
|
||||
inputs: {
|
||||
text_input: params.text_input,
|
||||
enable_error_category: params.enable_error_category,
|
||||
enable_correction_suggestion: params.enable_correction_suggestion,
|
||||
enable_batch_processing: params.enable_batch_processing
|
||||
},
|
||||
query: '1',
|
||||
response_mode: 'streaming'
|
||||
}
|
||||
|
||||
return fetch('https://copilot.sino-bridge.com/v1/chat-messages', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer app-YOaqLN0ZBsMozXCoYJqogwiA'
|
||||
},
|
||||
body: JSON.stringify(requestBody)
|
||||
})
|
||||
}
|
||||
@@ -28,6 +28,12 @@ const cards = [
|
||||
description: '智能检测和纠正中文文本中的错别字',
|
||||
icon: <EditOutlined style={{ fontSize: 32, color: '#fa8c16' }} />,
|
||||
link: '/spell-check'
|
||||
},
|
||||
{
|
||||
title: '错别字检测与修正系统',
|
||||
description: '更智能的错别字检测与修正系统,支持分类和建议',
|
||||
icon: <EditOutlined style={{ fontSize: 32, color: '#13c2c2' }} />,
|
||||
link: '/spell-check-system'
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
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
|
||||
@@ -48,6 +48,10 @@ const router: RouteObject[] = [
|
||||
path: '/spell-check',
|
||||
element: LazyLoad(lazy(() => import('@/pages/spell-check')))
|
||||
},
|
||||
{
|
||||
path: '/spell-check-system',
|
||||
element: LazyLoad(lazy(() => import('@/pages/spell-check-system')))
|
||||
},
|
||||
{
|
||||
path: '/404',
|
||||
element: <>404</>
|
||||
|
||||
127
错别字检测系统使用说明.md
Normal file
127
错别字检测系统使用说明.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# 错别字检测与修正系统 - 使用说明
|
||||
|
||||
## 系统概述
|
||||
|
||||
错别字检测与修正系统是一个基于AI技术的智能文本校对工具,能够自动识别中文文本中的错别字并提供修正建议。
|
||||
|
||||
## 功能特点
|
||||
|
||||
### 核心功能
|
||||
- **智能检测**:自动识别文本中的错别字
|
||||
- **上下文分析**:提供错误词在原文中的上下文信息
|
||||
- **修正建议**:为每个错别字提供准确的修正建议
|
||||
- **错误分类**:可选择启用错别字类型分类(如:同音字、形近字等)
|
||||
- **用户反馈**:支持用户对修正建议进行确认或否决
|
||||
- **批量处理**:支持多段文本的批量检测
|
||||
|
||||
### 交互功能
|
||||
- **实时反馈**:用户可以逐条确认或否决检测结果
|
||||
- **结果统计**:自动统计确认和否决的反馈数据
|
||||
- **本地存储**:反馈数据自动保存到本地存储
|
||||
|
||||
## 页面结构
|
||||
|
||||
### 1. 页面标题区
|
||||
- 居中显示蓝色标题"错别字检测与修正系统"
|
||||
|
||||
### 2. 参数输入区
|
||||
- **待检测文本**:大型文本输入框,支持多段文本
|
||||
- **配置选项**:
|
||||
- 是否启用分类:控制是否显示错别字类型
|
||||
- 是否启用建议:控制是否提供修正建议(默认启用)
|
||||
- 是否启用批量处理:控制是否支持多段文本处理
|
||||
|
||||
### 3. 操作按钮区
|
||||
- **检测按钮**:触发错别字检测流程
|
||||
- **重置按钮**:清空表单和结果
|
||||
|
||||
### 4. 内容展示区
|
||||
- **检测结果表格**:展示所有检测到的错别字
|
||||
- **表格列**:
|
||||
- 错误词:显示识别到的错别字(红色高亮)
|
||||
- 上下文:显示错别字在原文中的位置和上下文
|
||||
- 建议词:显示系统提供的修正建议(绿色高亮)
|
||||
- 错误类型:显示错别字分类(如适用)
|
||||
- 操作:提供确认/否决按钮供用户反馈
|
||||
- **反馈提交按钮**:提交用户反馈数据
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 基本流程
|
||||
|
||||
1. **输入文本**
|
||||
- 在"待检测文本"区域输入需要检测的文本内容
|
||||
- 文本支持中文,可包含多段内容
|
||||
|
||||
2. **配置选项**
|
||||
- 根据需要开启/关闭各项功能开关
|
||||
- 默认启用"是否启用建议"
|
||||
|
||||
3. **开始检测**
|
||||
- 点击"检测"按钮
|
||||
- 系统将发送请求到Dify AI平台
|
||||
- 等待流式响应返回检测结果
|
||||
|
||||
4. **查看结果**
|
||||
- 检测完成后,结果将显示在下方表格中
|
||||
- 表格显示错误词、上下文、建议词、错误类型等信息
|
||||
|
||||
5. **提交反馈**
|
||||
- 对每条检测结果点击"确认"或"否决"
|
||||
- 点击"提交反馈"按钮保存反馈数据
|
||||
- 数据将自动保存到本地存储
|
||||
|
||||
### API配置
|
||||
|
||||
- **请求地址**:https://copilot.sino-bridge.com/v1/chat-messages
|
||||
- **鉴权方式**:Bearer Token (app-YOaqLN0ZBsMozXCoYJqogwiA)
|
||||
- **请求方式**:POST
|
||||
- **响应模式**:流式 (streaming)
|
||||
|
||||
## 技术实现
|
||||
|
||||
### 前端技术栈
|
||||
- React 18 + TypeScript
|
||||
- Ant Design 5.x(UI组件库)
|
||||
- Tailwind CSS 4.x(样式框架)
|
||||
- React Router DOM 6.x(路由管理)
|
||||
|
||||
### 核心特性
|
||||
- **流式数据处理**:实时接收和解析Dify平台的流式响应
|
||||
- **响应式设计**:自适应不同屏幕尺寸
|
||||
- **类型安全**:完整的TypeScript类型定义
|
||||
- **模块化设计**:清晰的API和组件分离
|
||||
|
||||
## 文件结构
|
||||
|
||||
```
|
||||
src/
|
||||
├── api/
|
||||
│ └── spell-check-system.ts # API接口定义
|
||||
├── pages/
|
||||
│ └── spell-check-system/
|
||||
│ └── index.tsx # 主页面组件
|
||||
└── router/
|
||||
└── index.tsx # 路由配置
|
||||
```
|
||||
|
||||
## 访问路径
|
||||
|
||||
- 主页导航:`/` → 选择"错别字检测与修正系统"卡片
|
||||
- 直接访问:`/#/spell-check-system`
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **文本长度**:建议单次检测的文本长度不超过合理范围,以保证检测效果
|
||||
2. **网络连接**:需要稳定的网络连接以访问Dify AI服务
|
||||
3. **浏览器兼容性**:推荐使用现代浏览器(Chrome、Firefox、Safari、Edge)
|
||||
4. **数据存储**:用户反馈数据仅保存在本地浏览器存储中
|
||||
|
||||
## 更新日志
|
||||
|
||||
### v1.0.0
|
||||
- 初始版本发布
|
||||
- 实现基础的错别字检测功能
|
||||
- 支持流式数据处理
|
||||
- 添加用户反馈系统
|
||||
- 集成Dify AI平台
|
||||
Reference in New Issue
Block a user