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