Update from Vibe Studio

This commit is contained in:
Vibe Studio
2026-01-22 01:39:49 +00:00
parent 976704f9ec
commit 085407ccd6
2 changed files with 347 additions and 0 deletions

View 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)
})
}

View 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