Update from Vibe Studio

This commit is contained in:
Vibe Studio
2026-01-16 09:17:38 +00:00
parent 976704f9ec
commit 90e523b17e
2 changed files with 114 additions and 256 deletions

View File

@@ -1,29 +1,15 @@
export interface SpellCheckRequest {
user_input_text: string
auto_correct: boolean
custom_vocab: string[]
}
export interface DifyRequest {
inputs: {
user_input_text: string
auto_correct: boolean
custom_vocab: string[]
user_input: string
}
query: string
response_mode: string
}
export function checkAndCorrectSpelling(
user_input_text: string,
auto_correct: boolean,
custom_vocab: string[]
) {
export function checkAndCorrectSpelling(user_input: string) {
const requestBody: DifyRequest = {
inputs: {
user_input_text,
auto_correct,
custom_vocab
user_input
},
query: '1',
response_mode: 'streaming'
@@ -33,7 +19,7 @@ export function checkAndCorrectSpelling(
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer app-Dmsx84IAGk7rVCko5MWptmK3'
'Authorization': 'Bearer app-8LtwQRbmrDRpiGFZqVg4SK3q'
},
body: JSON.stringify(requestBody)
})

View File

@@ -1,75 +1,36 @@
import { useState } from 'react'
import { Button, Card, Form, Input, message, Space, Switch } from 'antd'
import { useState, useEffect, useRef } from 'react'
import { Button, Card, Form, Input, message, Typography } from 'antd'
import { checkAndCorrectSpelling } from '@/api/spellCheck'
interface CorrectionResult {
original: string
corrected: string
position: number
suggestions?: string[]
}
const { Title } = Typography
const SpellCheck = () => {
const [form] = Form.useForm()
const [isGenerating, setIsGenerating] = useState(false)
const [correctionResults, setCorrectionResults] = useState<CorrectionResult[]>([])
const [correctedText, setCorrectedText] = useState('')
const maxChars = 5000
const [generatedResult, setGeneratedResult] = useState('')
const textareaRef = useRef<any>(null)
const validateCustomVocab = (_: any, value: string) => {
if (!value) {
return Promise.resolve()
// 页面加载时自动聚焦
useEffect(() => {
if (textareaRef.current) {
textareaRef.current.focus()
}
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 handleGenerate = async () => {
try {
const formValues = await form.validateFields()
// 验证用户输入文本
if (!formValues.user_input_text?.trim()) {
if (!formValues.user_input?.trim()) {
message.warning('请输入需要检测错别字的文本')
return
}
// 验证文本长度
if (formValues.user_input_text.length > maxChars) {
message.warning(`输入文本不能超过${maxChars}个字符`)
return
}
setIsGenerating(true)
setCorrectionResults([])
setCorrectedText('')
setGeneratedResult('')
let customVocabArray: string[] = []
if (formValues.custom_vocab) {
try {
customVocabArray = JSON.parse(formValues.custom_vocab)
} catch (error) {
message.error('自定义词汇库格式错误')
setIsGenerating(false)
return
}
}
const response = await checkAndCorrectSpelling(
formValues.user_input_text,
formValues.auto_correct || false,
customVocabArray
)
const response = await checkAndCorrectSpelling(formValues.user_input)
if (!response.ok) {
const errorText = await response.text()
@@ -84,7 +45,6 @@ const SpellCheck = () => {
const decoder = new TextDecoder('utf-8')
let buffer = ''
let fullContent = ''
let results: CorrectionResult[] = []
try {
while (true) {
@@ -112,22 +72,7 @@ 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) {
// 解析失败,继续等待
}
setGeneratedResult(fullContent)
} else if (parsed.event === 'error') {
throw new Error(parsed.message || 'Dify API 返回错误')
}
@@ -140,24 +85,6 @@ const SpellCheck = () => {
} finally {
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('检测完成,但结果格式异常')
}
}
} catch (error) {
console.error('检测错误:', error)
message.error(error instanceof Error ? error.message : '检测失败,请稍后重试')
@@ -168,173 +95,118 @@ const SpellCheck = () => {
const handleReset = () => {
form.resetFields()
setCorrectionResults([])
setCorrectedText('')
setGeneratedResult('')
message.info('已重置表单')
if (textareaRef.current) {
textareaRef.current.focus()
}
}
const renderCorrectionResults = () => {
if (isGenerating) {
return (
<div style={{ textAlign: 'center', color: '#999', padding: '20px' }}>
...
</div>
)
const handleCopy = async () => {
try {
await navigator.clipboard.writeText(generatedResult)
message.success('复制成功')
} catch (error) {
message.error('复制失败')
}
if (correctionResults.length === 0) {
return (
<div style={{ textAlign: 'center', color: '#999' }}>
</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>
)
}
return (
<div style={{ maxWidth: '900px', margin: '0 auto', padding: '20px' }}>
<div className="spell-check-page" style={{ maxWidth: '800px', margin: '0 auto', padding: '20px' }}>
{/* 页面标题区 */}
<div style={{ textAlign: 'center', marginBottom: '40px' }}>
<h1 style={{ color: '#1890ff', fontSize: '32px', fontWeight: 'bold', margin: 0 }}>
</h1>
<div style={{ textAlign: 'center', marginBottom: '30px' }}>
<Title level={2} style={{ color: '#1890ff', margin: 0 }}>
</Title>
</div>
<Space direction="vertical" style={{ width: '100%' }} size="large">
{/* 参数输入区 */}
<Card title="参数输入区">
<Form form={form} layout="vertical">
<Form.Item
name="user_input_text"
label="用户输入文本"
rules={[
{ required: true, message: '请输入需要检测错别字的文本' },
{ max: maxChars, message: `输入文本不能超过${maxChars}个字符` }
]}
>
<Input.TextArea
rows={8}
placeholder="请输入需要检测错别字的中文文本..."
maxLength={maxChars}
showCount
style={{ fontSize: '14px' }}
/>
</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' }}>
<Button
type="primary"
size="large"
onClick={handleGenerate}
loading={isGenerating}
style={{ minWidth: '120px', height: '40px', fontSize: '16px' }}
{/* 参数输入区 */}
<Card style={{ marginBottom: '20px' }}>
<Form
form={form}
layout="vertical"
initialValues={{ user_input: '' }}
>
<Form.Item
name="user_input"
label="需要检测的文本"
rules={[
{ required: true, message: '请输入需要检测错别字的文本' }
]}
>
</Button>
<Button
size="large"
onClick={handleReset}
disabled={isGenerating}
style={{ minWidth: '120px', height: '40px', fontSize: '16px' }}
>
</Button>
</div>
<Input.TextArea
ref={textareaRef}
rows={8}
placeholder="请输入需要检测错别字的中文文本..."
style={{ fontSize: '14px' }}
/>
</Form.Item>
</Form>
</Card>
{/* 内容展示区 */}
<Card
title="检测结果"
{/* 操作按钮区 */}
<div style={{ display: 'flex', justifyContent: 'center', gap: '16px', marginBottom: '30px' }}>
<Button
type="primary"
size="large"
onClick={handleGenerate}
loading={isGenerating}
style={{ minWidth: '140px', height: '44px', fontSize: '16px' }}
>
</Button>
<Button
size="large"
onClick={handleReset}
disabled={isGenerating}
style={{ minWidth: '140px', height: '44px', fontSize: '16px' }}
>
</Button>
</div>
{/* 内容展示区 */}
<Card
title="检测结果"
style={{
display: generatedResult || isGenerating ? 'block' : 'none',
minHeight: '200px'
}}
>
<div
style={{
display: correctionResults.length > 0 || isGenerating ? 'block' : 'none'
minHeight: '150px',
maxHeight: '60vh',
overflowY: 'auto',
padding: '20px',
backgroundColor: '#fafafa',
border: '1px solid #e8e8e8',
borderRadius: '6px',
fontSize: '14px',
lineHeight: '1.8',
whiteSpace: 'pre-wrap'
}}
>
<div
style={{
minHeight: '150px',
maxHeight: '400px',
overflowY: 'auto',
padding: '15px',
backgroundColor: '#fafafa',
border: '1px solid #d9d9d9',
borderRadius: '4px'
}}
>
{renderCorrectionResults()}
</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}
{isGenerating && !generatedResult ? (
<div style={{ textAlign: 'center', color: '#999', padding: '40px 0' }}>
...
</div>
</Card>
)}
</Space>
) : generatedResult ? (
<div>
{generatedResult}
<div style={{ marginTop: '20px', textAlign: 'center' }}>
<Button onClick={handleCopy} size="small">
</Button>
</div>
</div>
) : (
<div style={{ textAlign: 'center', color: '#999' }}>
</div>
)}
</div>
</Card>
</div>
)
}