Update from Vibe Studio
This commit is contained in:
48
src/api/spell-correct.ts
Normal file
48
src/api/spell-correct.ts
Normal file
@@ -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)
|
||||||
|
})
|
||||||
|
}
|
||||||
347
src/pages/spell-correct/index.tsx
Normal file
347
src/pages/spell-correct/index.tsx
Normal file
@@ -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<any>(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 (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
maxWidth: '1000px',
|
||||||
|
margin: '0 auto',
|
||||||
|
padding: '20px',
|
||||||
|
minHeight: '100vh',
|
||||||
|
backgroundColor: '#ffffff'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* 页面标题区 */}
|
||||||
|
<div style={{ textAlign: 'center', marginBottom: '40px' }}>
|
||||||
|
<Title level={2} style={{ color: '#1890ff', marginBottom: 0 }}>
|
||||||
|
错别字检测与修正工具
|
||||||
|
</Title>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Space direction="vertical" style={{ width: '100%' }} size="large">
|
||||||
|
{/* 参数输入区 */}
|
||||||
|
<Card>
|
||||||
|
<Form form={form} layout="vertical">
|
||||||
|
<Form.Item
|
||||||
|
name="user_input"
|
||||||
|
label={
|
||||||
|
<Text strong style={{ fontSize: '16px' }}>
|
||||||
|
文本输入
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
|
rules={[
|
||||||
|
{ required: true, message: '请输入需要检测错别字的文本' },
|
||||||
|
{ max: 10000, message: '输入文本不能超过10000个字符' }
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<TextArea
|
||||||
|
rows={8}
|
||||||
|
placeholder="请输入需要检测错别字的中文文本..."
|
||||||
|
style={{ fontSize: '14px', fontFamily: '微软雅黑, 苹方, sans-serif' }}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name="upload_file"
|
||||||
|
label={
|
||||||
|
<Text strong style={{ fontSize: '16px' }}>
|
||||||
|
文件上传(可选)
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Upload
|
||||||
|
beforeUpload={handleFileUpload}
|
||||||
|
showUploadList={true}
|
||||||
|
accept=".txt,.doc,.docx,.pdf"
|
||||||
|
maxCount={1}
|
||||||
|
>
|
||||||
|
<Button icon={<UploadOutlined />}>选择文件</Button>
|
||||||
|
</Upload>
|
||||||
|
<div style={{ marginTop: '8px', fontSize: '12px', color: '#666' }}>
|
||||||
|
支持格式:TXT, DOC, DOCX, PDF
|
||||||
|
</div>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* 操作按钮区 */}
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'center', gap: '16px', flexWrap: 'wrap' }}>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
|
onClick={handleGenerate}
|
||||||
|
loading={isGenerating}
|
||||||
|
icon={<CheckOutlined />}
|
||||||
|
style={{ minWidth: '140px', height: '44px', fontSize: '16px' }}
|
||||||
|
>
|
||||||
|
检测修正
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="large"
|
||||||
|
onClick={handleCopyResult}
|
||||||
|
disabled={!correctedText}
|
||||||
|
icon={<CopyOutlined />}
|
||||||
|
style={{ minWidth: '140px', height: '44px', fontSize: '16px' }}
|
||||||
|
>
|
||||||
|
复制结果
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="large"
|
||||||
|
onClick={handleGenerate}
|
||||||
|
loading={isGenerating}
|
||||||
|
disabled={!originalText}
|
||||||
|
icon={<ReloadOutlined />}
|
||||||
|
style={{ minWidth: '140px', height: '44px', fontSize: '16px' }}
|
||||||
|
>
|
||||||
|
重新生成
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="large"
|
||||||
|
onClick={handleReset}
|
||||||
|
disabled={isGenerating}
|
||||||
|
style={{ minWidth: '140px', height: '44px', fontSize: '16px' }}
|
||||||
|
>
|
||||||
|
重置
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 内容展示区 */}
|
||||||
|
{(originalText || correctedText || isGenerating) && (
|
||||||
|
<Card title="检测结果对比">
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '24px' }}>
|
||||||
|
{/* 原始文本 */}
|
||||||
|
{originalText && (
|
||||||
|
<div>
|
||||||
|
<Divider orientation="left" style={{ margin: '0 0 16px 0' }}>
|
||||||
|
<Text strong style={{ fontSize: '16px', color: '#666' }}>
|
||||||
|
原始文本
|
||||||
|
</Text>
|
||||||
|
</Divider>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
minHeight: '120px',
|
||||||
|
padding: '15px',
|
||||||
|
backgroundColor: '#fafafa',
|
||||||
|
border: '1px solid #d9d9d9',
|
||||||
|
borderRadius: '6px',
|
||||||
|
whiteSpace: 'pre-wrap',
|
||||||
|
wordWrap: 'break-word',
|
||||||
|
fontSize: '14px',
|
||||||
|
lineHeight: '1.8',
|
||||||
|
fontFamily: '微软雅黑, 苹方, sans-serif'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{originalText}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 修正后文本 */}
|
||||||
|
<div>
|
||||||
|
<Divider orientation="left" style={{ margin: '0 0 16px 0' }}>
|
||||||
|
<Text strong style={{ fontSize: '16px', color: '#52c41a' }}>
|
||||||
|
修正后文本
|
||||||
|
</Text>
|
||||||
|
</Divider>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
minHeight: '150px',
|
||||||
|
padding: '15px',
|
||||||
|
backgroundColor: isGenerating ? '#fff' : '#f6ffed',
|
||||||
|
border: `1px solid ${isGenerating ? '#d9d9d9' : '#b7eb8f'}`,
|
||||||
|
borderRadius: '6px',
|
||||||
|
whiteSpace: 'pre-wrap',
|
||||||
|
wordWrap: 'break-word',
|
||||||
|
fontSize: '14px',
|
||||||
|
lineHeight: '1.8',
|
||||||
|
fontFamily: '微软雅黑, 苹方, sans-serif'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isGenerating ? (
|
||||||
|
<div style={{ textAlign: 'center', color: '#999', padding: '20px' }}>
|
||||||
|
正在检测修正中,请稍候...
|
||||||
|
</div>
|
||||||
|
) : correctedText ? (
|
||||||
|
<TextArea
|
||||||
|
value={correctedText}
|
||||||
|
onChange={(e) => setCorrectedText(e.target.value)}
|
||||||
|
bordered={false}
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
fontSize: '14px',
|
||||||
|
lineHeight: '1.8',
|
||||||
|
padding: 0
|
||||||
|
}}
|
||||||
|
autoSize={{ minRows: 6 }}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div style={{ textAlign: 'center', color: '#999' }}>
|
||||||
|
修正结果将在这里显示
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SpellCorrect
|
||||||
@@ -30,7 +30,7 @@ const router: RouteObject[] = [
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
element: LazyLoad(lazy(() => import('@/pages/home')))
|
element: LazyLoad(lazy(() => import('@/pages/spell-correct')))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/test1',
|
path: '/test1',
|
||||||
|
|||||||
Reference in New Issue
Block a user