Update from Vibe Studio
This commit is contained in:
31
src/api/spell-check-tool.ts
Normal file
31
src/api/spell-check-tool.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 错别字检测与修正工具 API
|
||||
* 调用 Dify 平台进行错别字检测与修正
|
||||
*/
|
||||
|
||||
export interface DifyRequest {
|
||||
inputs: {
|
||||
user_input: string
|
||||
}
|
||||
query: string
|
||||
response_mode: string
|
||||
}
|
||||
|
||||
export function checkSpellingWithDify(user_input: string) {
|
||||
const requestBody: DifyRequest = {
|
||||
inputs: {
|
||||
user_input
|
||||
},
|
||||
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-4SnGvNq4HPxFH4Ie8C0xo7ng'
|
||||
},
|
||||
body: JSON.stringify(requestBody)
|
||||
})
|
||||
}
|
||||
316
src/pages/spell-check-tool/index.tsx
Normal file
316
src/pages/spell-check-tool/index.tsx
Normal file
@@ -0,0 +1,316 @@
|
||||
import { useState } from 'react'
|
||||
import { Button, Card, Form, Input, Space, message, Typography } from 'antd'
|
||||
import { checkSpellingWithDify } from '@/api/spell-check-tool'
|
||||
|
||||
const { TextArea } = Input
|
||||
const { Title, Text } = Typography
|
||||
|
||||
const SpellCheckTool = () => {
|
||||
const [form] = Form.useForm()
|
||||
const [inputText, setInputText] = useState('')
|
||||
const [resultText, setResultText] = useState('')
|
||||
const [isProcessing, setIsProcessing] = useState(false)
|
||||
const [viewMode, setViewMode] = useState<'original' | 'corrected'>('original')
|
||||
const maxChars = 5000
|
||||
|
||||
const handleGenerate = async () => {
|
||||
try {
|
||||
const formValues = await form.validateFields()
|
||||
|
||||
// 验证输入内容
|
||||
if (!formValues.user_input?.trim()) {
|
||||
message.warning('请输入需要检测错别字的文本')
|
||||
return
|
||||
}
|
||||
|
||||
if (formValues.user_input.length > maxChars) {
|
||||
message.warning(`输入文本不能超过${maxChars}个字符`)
|
||||
return
|
||||
}
|
||||
|
||||
setIsProcessing(true)
|
||||
setResultText('')
|
||||
setViewMode('original')
|
||||
|
||||
const response = await checkSpellingWithDify(formValues.user_input)
|
||||
|
||||
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
|
||||
setResultText(fullContent)
|
||||
} else if (parsed.event === 'error') {
|
||||
throw new Error(parsed.message || 'Dify API 返回错误')
|
||||
}
|
||||
} catch (parseError) {
|
||||
console.warn('跳过无法解析的行:', trimmedLine)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
reader.releaseLock()
|
||||
}
|
||||
|
||||
if (fullContent) {
|
||||
setResultText(fullContent)
|
||||
setViewMode('corrected')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('检测错误:', error)
|
||||
message.error(error instanceof Error ? error.message : '检测失败,请稍后重试')
|
||||
} finally {
|
||||
setIsProcessing(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
form.resetFields()
|
||||
setInputText('')
|
||||
setResultText('')
|
||||
setIsProcessing(false)
|
||||
setViewMode('original')
|
||||
message.info('已清空内容')
|
||||
}
|
||||
|
||||
const handleCopyResult = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(resultText)
|
||||
message.success('已复制到剪贴板')
|
||||
} catch (error) {
|
||||
message.error('复制失败,请手动复制')
|
||||
}
|
||||
}
|
||||
|
||||
const renderContent = () => {
|
||||
if (isProcessing) {
|
||||
return (
|
||||
<div style={{ textAlign: 'center', padding: '40px' }}>
|
||||
<div style={{ marginBottom: '20px' }}>
|
||||
<div className="loading-spinner" style={{
|
||||
display: 'inline-block',
|
||||
width: '40px',
|
||||
height: '40px',
|
||||
border: '4px solid #f3f3f3',
|
||||
borderTop: '4px solid #1890ff',
|
||||
borderRadius: '50%',
|
||||
animation: 'spin 1s linear infinite'
|
||||
}} />
|
||||
</div>
|
||||
<Text type="secondary">正在检测中,请稍候...</Text>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (!resultText) {
|
||||
return (
|
||||
<div style={{ textAlign: 'center', color: '#999', padding: '40px' }}>
|
||||
请输入文本后点击'开始检测'按钮
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (viewMode === 'original') {
|
||||
return (
|
||||
<div style={{
|
||||
padding: '20px',
|
||||
backgroundColor: '#fafafa',
|
||||
border: '1px solid #d9d9d9',
|
||||
borderRadius: '4px',
|
||||
minHeight: '200px',
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordWrap: 'break-word',
|
||||
fontSize: '14px',
|
||||
lineHeight: '1.6'
|
||||
}}>
|
||||
{inputText}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// 显示修正后的文本,不同颜色标记修改内容
|
||||
return (
|
||||
<div style={{
|
||||
padding: '20px',
|
||||
backgroundColor: '#f6ffed',
|
||||
border: '1px solid #b7eb8f',
|
||||
borderRadius: '4px',
|
||||
minHeight: '200px',
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordWrap: 'break-word',
|
||||
fontSize: '14px',
|
||||
lineHeight: '1.6'
|
||||
}}>
|
||||
{resultText}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
maxWidth: '800px',
|
||||
margin: '0 auto',
|
||||
padding: '20px',
|
||||
minHeight: '100vh',
|
||||
backgroundColor: '#ffffff'
|
||||
}}>
|
||||
{/* 页面标题区 */}
|
||||
<div style={{ textAlign: 'center', marginBottom: '40px', paddingTop: '20px' }}>
|
||||
<Title level={2} style={{ color: '#1890ff', fontSize: '28px', fontWeight: 'bold', margin: 0 }}>
|
||||
错别字检测与修正工具
|
||||
</Title>
|
||||
</div>
|
||||
|
||||
<Space direction="vertical" style={{ width: '100%' }} size="large">
|
||||
{/* 参数输入区 */}
|
||||
<Card style={{ borderRadius: '8px' }}>
|
||||
<Form form={form} layout="vertical">
|
||||
<Form.Item
|
||||
name="user_input"
|
||||
label={
|
||||
<Text strong style={{ fontSize: '14px' }}>
|
||||
输入文本
|
||||
</Text>
|
||||
}
|
||||
rules={[
|
||||
{ required: true, message: '请输入需要检测错别字的文本' },
|
||||
{ max: maxChars, message: `输入文本不能超过${maxChars}个字符` }
|
||||
]}
|
||||
>
|
||||
<TextArea
|
||||
rows={8}
|
||||
placeholder="请输入需要检测错别字的文本内容..."
|
||||
maxLength={maxChars}
|
||||
showCount
|
||||
style={{
|
||||
fontSize: '14px',
|
||||
fontFamily: 'Microsoft YaHei, -apple-system, BlinkMacSystemFont, sans-serif'
|
||||
}}
|
||||
onChange={(e) => setInputText(e.target.value)}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Card>
|
||||
|
||||
{/* 操作按钮区 */}
|
||||
<div style={{ display: 'flex', justifyContent: 'center', gap: '16px', flexWrap: 'wrap' }}>
|
||||
<Button
|
||||
type="primary"
|
||||
size="large"
|
||||
onClick={handleGenerate}
|
||||
loading={isProcessing}
|
||||
style={{
|
||||
minWidth: '120px',
|
||||
height: '40px',
|
||||
fontSize: '16px',
|
||||
fontWeight: 500
|
||||
}}
|
||||
>
|
||||
开始检测
|
||||
</Button>
|
||||
<Button
|
||||
size="large"
|
||||
onClick={handleCopyResult}
|
||||
disabled={!resultText || isProcessing}
|
||||
style={{
|
||||
minWidth: '120px',
|
||||
height: '40px',
|
||||
fontSize: '16px'
|
||||
}}
|
||||
>
|
||||
复制结果
|
||||
</Button>
|
||||
<Button
|
||||
size="large"
|
||||
onClick={handleReset}
|
||||
disabled={isProcessing}
|
||||
style={{
|
||||
minWidth: '120px',
|
||||
height: '40px',
|
||||
fontSize: '16px'
|
||||
}}
|
||||
>
|
||||
清空内容
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 内容展示区 */}
|
||||
<Card
|
||||
title={
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<span style={{ fontWeight: 500 }}>检测结果</span>
|
||||
{resultText && !isProcessing && (
|
||||
<div style={{ display: 'flex', gap: '8px' }}>
|
||||
<Button
|
||||
size="small"
|
||||
type={viewMode === 'original' ? 'primary' : 'default'}
|
||||
onClick={() => setViewMode('original')}
|
||||
style={{ fontSize: '12px', height: '28px' }}
|
||||
>
|
||||
原文
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
type={viewMode === 'corrected' ? 'primary' : 'default'}
|
||||
onClick={() => setViewMode('corrected')}
|
||||
style={{ fontSize: '12px', height: '28px' }}
|
||||
>
|
||||
修正后
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
style={{ borderRadius: '8px' }}
|
||||
>
|
||||
{renderContent()}
|
||||
</Card>
|
||||
</Space>
|
||||
|
||||
<style>{`
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SpellCheckTool
|
||||
Reference in New Issue
Block a user