Update from Vibe Studio

This commit is contained in:
Vibe Studio
2026-02-04 07:56:24 +00:00
parent 976704f9ec
commit 4332632f15

View File

@@ -1,79 +1,59 @@
import { useState } from 'react'
import { Button, Card, Form, Input, message, Space, Switch } from 'antd'
import { checkAndCorrectSpelling } from '@/api/spellCheck'
import { useState, useCallback } from 'react'
import { Button, Card, Form, Input, message, Space, Typography } from 'antd'
import { CopyOutlined, ClearOutlined, CheckCircleOutlined } from '@ant-design/icons'
interface CorrectionResult {
original: string
corrected: string
position: number
suggestions?: string[]
const { Title, Text, Paragraph } = Typography
interface SpellCheckPageProps {
// Props definition if needed
}
const SpellCheck = () => {
const SpellCheckPage: React.FC<SpellCheckPageProps> = () => {
const [form] = Form.useForm()
const [isGenerating, setIsGenerating] = useState(false)
const [correctionResults, setCorrectionResults] = useState<CorrectionResult[]>([])
const [correctedText, setCorrectedText] = useState('')
const [isChecking, setIsChecking] = useState(false)
const [result, setResult] = useState<string>('')
const [originalText, setOriginalText] = useState<string>('')
const maxChars = 5000
const validateCustomVocab = (_: any, value: string) => {
if (!value) {
return Promise.resolve()
}
// 调用Dify API进行错别字校验
const handleCheck = useCallback(async () => {
try {
const parsed = JSON.parse(value)
if (!Array.isArray(parsed)) {
return Promise.reject(new Error('必须是JSON数组格式'))
}
if (!parsed.every(item => typeof item === 'string')) {
return Promise.reject(new Error('数组元素必须为字符串'))
}
return Promise.resolve()
} catch (error) {
return Promise.reject(new Error('JSON格式无效'))
}
}
const values = await form.validateFields(['text_to_check'])
const handleGenerate = async () => {
try {
const formValues = await form.validateFields()
// 验证用户输入文本
if (!formValues.user_input_text?.trim()) {
message.warning('请输入需要检测错别字的文本')
if (!values.text_to_check?.trim()) {
message.warning('请输入需要校验的文本内容')
return
}
// 验证文本长度
if (formValues.user_input_text.length > maxChars) {
if (values.text_to_check.length > maxChars) {
message.warning(`输入文本不能超过${maxChars}个字符`)
return
}
setIsGenerating(true)
setCorrectionResults([])
setCorrectedText('')
setIsChecking(true)
setResult('')
setOriginalText(values.text_to_check)
let customVocabArray: string[] = []
if (formValues.custom_vocab) {
try {
customVocabArray = JSON.parse(formValues.custom_vocab)
} catch (error) {
message.error('自定义词汇库格式错误')
setIsGenerating(false)
return
}
}
const API_URL = 'https://copilot.sino-bridge.com/v1/chat-messages'
const API_KEY = 'app-Ykwky9aKr95GBwm9FWFUg6ot'
const response = await checkAndCorrectSpelling(
formValues.user_input_text,
formValues.auto_correct || false,
customVocabArray
)
const response = await fetch(API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}`
},
body: JSON.stringify({
inputs: {
text_to_check: values.text_to_check
},
query: '1',
response_mode: 'streaming'
})
})
if (!response.ok) {
const errorText = await response.text()
throw new Error(`检测请求失败: ${response.status} ${errorText}`)
throw new Error(`API请求失败: ${response.status}`)
}
if (!response.body) {
@@ -84,7 +64,6 @@ const SpellCheck = () => {
const decoder = new TextDecoder('utf-8')
let buffer = ''
let fullContent = ''
let results: CorrectionResult[] = []
try {
while (true) {
@@ -99,7 +78,7 @@ const SpellCheck = () => {
const trimmedLine = line.trim()
if (!trimmedLine || trimmedLine === 'data: [DONE]') {
if (trimmedLine === 'data: [DONE]') {
message.success('检测完成')
message.success('校验完成')
break
}
continue
@@ -112,27 +91,12 @@ const SpellCheck = () => {
if (parsed.event === 'message' && parsed.answer) {
fullContent += parsed.answer
// 尝试解析JSON结果
try {
results = JSON.parse(fullContent)
setCorrectionResults(results)
if (formValues.auto_correct) {
let corrected = formValues.user_input_text
results.forEach(item => {
if (item.corrected && item.original) {
corrected = corrected.replace(item.original, item.corrected)
}
})
setCorrectedText(corrected)
}
} catch (e) {
// 解析失败,继续等待
}
setResult(fullContent)
} else if (parsed.event === 'error') {
throw new Error(parsed.message || 'Dify API 返回错误')
}
} catch (parseError) {
console.warn('跳过无法解析的行:', trimmedLine)
// 跳过无法解析的行
}
}
}
@@ -141,202 +105,156 @@ const SpellCheck = () => {
reader.releaseLock()
}
if (results.length === 0 && fullContent) {
try {
results = JSON.parse(fullContent)
setCorrectionResults(results)
if (formValues.auto_correct) {
let corrected = formValues.user_input_text
results.forEach(item => {
if (item.corrected && item.original) {
corrected = corrected.replace(item.original, item.corrected)
}
})
setCorrectedText(corrected)
}
} catch (e) {
message.warning('检测完成,但结果格式异常')
}
if (!fullContent) {
message.warning('未能获取到校验结果')
}
} catch (error) {
console.error('检测错误:', error)
message.error(error instanceof Error ? error.message : '检测失败,请稍后重试')
console.error('校验错误:', error)
message.error(error instanceof Error ? error.message : '校验失败,请稍后重试')
} finally {
setIsGenerating(false)
}
setIsChecking(false)
}
}, [form])
const handleReset = () => {
// 清空内容
const handleClear = useCallback(() => {
form.resetFields()
setCorrectionResults([])
setCorrectedText('')
message.info('已重置表单')
setResult('')
setOriginalText('')
message.info('已清空内容')
}, [form])
// 复制结果
const handleCopy = useCallback(async () => {
if (!result) {
message.warning('没有可复制的内容')
return
}
const renderCorrectionResults = () => {
if (isGenerating) {
try {
await navigator.clipboard.writeText(result)
message.success('已复制到剪贴板')
} catch (error) {
message.error('复制失败,请手动选择复制')
}
}, [result])
// 渲染校验结果
const renderResult = () => {
if (isChecking) {
return (
<div style={{ textAlign: 'center', color: '#999', padding: '20px' }}>
...
<div style={{ textAlign: 'center', padding: '40px 20px' }}>
<Text type='secondary'>...</Text>
</div>
)
}
if (correctionResults.length === 0) {
if (!result && !originalText) {
return (
<div style={{ textAlign: 'center', color: '#999' }}>
<div style={{ textAlign: 'center', padding: '40px 20px', color: '#999' }}>
<CheckCircleOutlined style={{ fontSize: 48, color: '#d9d9d9', marginBottom: 16 }} />
<br />
<Text type='secondary'></Text>
</div>
)
}
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
{correctionResults.map((item, index) => (
<div
key={index}
style={{
padding: '12px',
backgroundColor: '#f5f5f5',
borderRadius: '6px',
border: '1px solid #d9d9d9'
}}
>
<div style={{ marginBottom: '8px', fontWeight: 'bold' }}>
{item.position}
</div>
<div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
<span style={{ color: '#ff4d4f' }}>{item.original}</span>
<span></span>
<span style={{ color: '#52c41a' }}>{item.corrected}</span>
</div>
{item.suggestions && item.suggestions.length > 0 && (
<div style={{ marginTop: '8px', fontSize: '12px', color: '#666' }}>
{item.suggestions.join('、')}
</div>
)}
</div>
))}
<div style={{ whiteSpace: 'pre-wrap', wordWrap: 'break-word', lineHeight: 1.8 }}>
{result || ''}
</div>
)
}
return (
<div style={{ maxWidth: '900px', margin: '0 auto', padding: '20px' }}>
<div style={{ maxWidth: '800px', margin: '0 auto', padding: '40px 20px' }}>
{/* 页面标题区 */}
<div style={{ textAlign: 'center', marginBottom: '40px' }}>
<h1 style={{ color: '#1890ff', fontSize: '32px', fontWeight: 'bold', margin: 0 }}>
</h1>
<Title level={2} style={{ color: '#1890ff', marginBottom: 8 }}>
</Title>
<Text type='secondary'>
AI将自动检测并标记错别字
</Text>
</div>
<Space direction="vertical" style={{ width: '100%' }} size="large">
<Space direction='vertical' style={{ width: '100%' }} size='large'>
{/* 参数输入区 */}
<Card title="参数输入区">
<Form form={form} layout="vertical">
<Card style={{ borderRadius: 8 }}>
<Form form={form} layout='vertical'>
<Form.Item
name="user_input_text"
label="用户输入文本"
name='text_to_check'
rules={[
{ required: true, message: '请输入需要检测错别字的文本' },
{ required: true, message: '请输入需要校验的文本内容' },
{ max: maxChars, message: `输入文本不能超过${maxChars}个字符` }
]}
>
<Input.TextArea
rows={8}
placeholder="请输入需要检测错别字的中文文本..."
rows={6}
placeholder='请输入或粘贴需要校验的文本内容...'
maxLength={maxChars}
showCount
style={{ fontSize: '14px' }}
style={{ fontSize: 14, borderRadius: 6 }}
/>
</Form.Item>
<Form.Item name="auto_correct" label="是否自动替换" valuePropName="checked">
<Switch />
</Form.Item>
<Form.Item
name="custom_vocab"
label="自定义词汇库(可选)"
rules={[{ validator: validateCustomVocab }]}
>
<Input.TextArea
rows={4}
placeholder='请输入JSON格式词汇库["正确词1", "正确词2"]'
style={{ fontSize: '14px' }}
/>
<div className="form-helper-text" style={{ fontSize: '12px', color: '#666', marginTop: '4px' }}>
JSON数组
</div>
</Form.Item>
</Form>
</Card>
{/* 操作按钮区 */}
<div className="button-group" style={{ display: 'flex', justifyContent: 'center', gap: '16px' }}>
<div style={{ display: 'flex', justifyContent: 'center', gap: 16 }}>
<Button
type="primary"
size="large"
onClick={handleGenerate}
loading={isGenerating}
style={{ minWidth: '120px', height: '40px', fontSize: '16px' }}
type='primary'
size='large'
onClick={handleCheck}
loading={isChecking}
style={{ minWidth: 120, height: 44, borderRadius: 6 }}
>
</Button>
<Button
size="large"
onClick={handleReset}
disabled={isGenerating}
style={{ minWidth: '120px', height: '40px', fontSize: '16px' }}
size='large'
onClick={handleClear}
disabled={isChecking}
icon={<ClearOutlined />}
style={{ minWidth: 120, height: 44, borderRadius: 6 }}
>
</Button>
<Button
size='large'
onClick={handleCopy}
disabled={!result || isChecking}
icon={<CopyOutlined />}
style={{ minWidth: 120, height: 44, borderRadius: 6 }}
>
</Button>
</div>
{/* 内容展示区 */}
<Card
title="检测结果"
style={{
display: correctionResults.length > 0 || isGenerating ? 'block' : 'none'
}}
title='校验结果'
style={{ borderRadius: 8 }}
>
<div
style={{
minHeight: '150px',
maxHeight: '400px',
minHeight: 150,
maxHeight: '50vh',
overflowY: 'auto',
padding: '15px',
padding: 16,
backgroundColor: '#fafafa',
border: '1px solid #d9d9d9',
borderRadius: '4px'
borderRadius: 6
}}
>
{renderCorrectionResults()}
{renderResult()}
</div>
</Card>
{correctedText && (
<Card title="纠正后的文本">
<div
style={{
minHeight: '120px',
padding: '15px',
backgroundColor: '#f6ffed',
border: '1px solid #b7eb8f',
borderRadius: '4px',
whiteSpace: 'pre-wrap',
wordWrap: 'break-word',
fontSize: '14px',
lineHeight: '1.6'
}}
>
{correctedText}
</div>
</Card>
)}
</Space>
</div>
)
}
export default SpellCheck
export default SpellCheckPage