diff --git a/src/api/spell-correct.ts b/src/api/spell-correct.ts new file mode 100644 index 0000000..77584e0 --- /dev/null +++ b/src/api/spell-correct.ts @@ -0,0 +1,48 @@ +export interface DifyRequest { + inputs: { + text_input: string + uploaded_files?: any[] + } + query: string + response_mode: string +} + +export interface SpellCorrectResult { + result_text: string +} + +export function uploadFile(file: File) { + const formData = new FormData() + formData.append('file', file) + + return fetch('https://copilot.sino-bridge.com/v1/files/upload', { + method: 'POST', + headers: { + 'Authorization': 'Bearer app-ygiEUNOOihWdBvSYKffUhdZg' + }, + body: formData + }).then(res => res.json()) +} + +export function detectAndCorrectSpelling(params: { + text_input: string + uploaded_files?: any[] +}) { + const requestBody: DifyRequest = { + inputs: { + text_input: params.text_input, + uploaded_files: params.uploaded_files + }, + 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-ygiEUNOOihWdBvSYKffUhdZg' + }, + body: JSON.stringify(requestBody) + }) +} diff --git a/src/pages/spell-correct/index.tsx b/src/pages/spell-correct/index.tsx new file mode 100644 index 0000000..fe068f0 --- /dev/null +++ b/src/pages/spell-correct/index.tsx @@ -0,0 +1,347 @@ +import { useState } from 'react' +import { + Button, + Card, + Form, + Input, + Upload, + message, + Space, + Typography, + Divider +} from 'antd' +import { UploadOutlined, CopyOutlined, ReloadOutlined, CheckOutlined } from '@ant-design/icons' +import { detectAndCorrectSpelling, uploadFile, SpellCorrectResult } from '@/api/spell-correct' + +const { Title, Text } = Typography +const { TextArea } = Input + +const SpellCorrect = () => { + const [form] = Form.useForm() + const [isGenerating, setIsGenerating] = useState(false) + const [originalText, setOriginalText] = useState('') + const [correctedText, setCorrectedText] = useState('') + const [uploadedFileData, setUploadedFileData] = useState(null) + + const handleFileUpload = async (file: File) => { + try { + const result = await uploadFile(file) + setUploadedFileData(result) + message.success('文件上传成功') + return false // 阻止默认上传行为 + } catch (error) { + console.error('文件上传失败:', error) + message.error('文件上传失败,请重试') + return false + } + } + + const handleGenerate = async () => { + try { + const formValues = await form.validateFields() + + // 验证用户输入文本 + if (!formValues.user_input?.trim()) { + message.warning('请输入需要检测错别字的文本') + return + } + + setIsGenerating(true) + setOriginalText(formValues.user_input) + setCorrectedText('') + + // 准备文件数据 + let filesList: any[] = [] + if (uploadedFileData) { + filesList = [ + { + ...uploadedFileData, + preview_url: '1', + transfer_method: 'local_file', + user: 'admin' + } + ] + } + + const response = await detectAndCorrectSpelling({ + text_input: formValues.user_input, + uploaded_files: filesList.length > 0 ? filesList : undefined + }) + + 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 = '' + + 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 + setCorrectedText(fullContent) + } else if (parsed.event === 'error') { + throw new Error(parsed.message || 'Dify API 返回错误') + } + } catch (parseError) { + console.warn('跳过无法解析的行:', trimmedLine) + } + } + } + } + } finally { + reader.releaseLock() + } + + if (!fullContent) { + message.warning('未获取到检测结果') + } + } catch (error) { + console.error('检测错误:', error) + message.error(error instanceof Error ? error.message : '检测失败,请稍后重试') + } finally { + setIsGenerating(false) + } + } + + const handleCopyResult = async () => { + if (!correctedText) { + message.warning('没有可复制的内容') + return + } + + try { + await navigator.clipboard.writeText(correctedText) + message.success('已复制到剪贴板') + } catch (error) { + console.error('复制失败:', error) + message.error('复制失败,请手动复制') + } + } + + const handleReset = () => { + form.resetFields() + setOriginalText('') + setCorrectedText('') + setUploadedFileData(null) + message.info('已重置表单') + } + + return ( +
+ {/* 页面标题区 */} +
+ + 错别字检测与修正工具 + +
+ + + {/* 参数输入区 */} + +
+ + 文本输入 + + } + rules={[ + { required: true, message: '请输入需要检测错别字的文本' }, + { max: 10000, message: '输入文本不能超过10000个字符' } + ]} + > +