Update from Vibe Studio
This commit is contained in:
14
package-lock.json
generated
14
package-lock.json
generated
@@ -509,6 +509,7 @@
|
|||||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.27.1",
|
||||||
"@babel/generator": "^7.28.5",
|
"@babel/generator": "^7.28.5",
|
||||||
@@ -883,6 +884,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@dnd-kit/core/-/core-6.3.1.tgz",
|
"resolved": "https://registry.npmmirror.com/@dnd-kit/core/-/core-6.3.1.tgz",
|
||||||
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
|
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dnd-kit/accessibility": "^3.1.1",
|
"@dnd-kit/accessibility": "^3.1.1",
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
@@ -2322,6 +2324,7 @@
|
|||||||
"integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==",
|
"integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.21.0"
|
"undici-types": "~6.21.0"
|
||||||
}
|
}
|
||||||
@@ -2337,6 +2340,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz",
|
||||||
"integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
|
"integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/prop-types": "*",
|
"@types/prop-types": "*",
|
||||||
"csstype": "^3.2.2"
|
"csstype": "^3.2.2"
|
||||||
@@ -2439,6 +2443,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/antd/-/antd-5.29.3.tgz",
|
"resolved": "https://registry.npmjs.org/antd/-/antd-5.29.3.tgz",
|
||||||
"integrity": "sha512-3DdbGCa9tWAJGcCJ6rzR8EJFsv2CtyEbkVabZE14pfgUHfCicWCj0/QzQVLDYg8CPfQk9BH7fHCoTXHTy7MP/A==",
|
"integrity": "sha512-3DdbGCa9tWAJGcCJ6rzR8EJFsv2CtyEbkVabZE14pfgUHfCicWCj0/QzQVLDYg8CPfQk9BH7fHCoTXHTy7MP/A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/colors": "^7.2.1",
|
"@ant-design/colors": "^7.2.1",
|
||||||
"@ant-design/cssinjs": "^1.23.0",
|
"@ant-design/cssinjs": "^1.23.0",
|
||||||
@@ -2559,6 +2564,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.9.0",
|
"baseline-browser-mapping": "^2.9.0",
|
||||||
"caniuse-lite": "^1.0.30001759",
|
"caniuse-lite": "^1.0.30001759",
|
||||||
@@ -2950,6 +2956,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/graphql/-/graphql-16.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/graphql/-/graphql-16.12.0.tgz",
|
||||||
"integrity": "sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==",
|
"integrity": "sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
|
"node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
|
||||||
}
|
}
|
||||||
@@ -3148,6 +3155,7 @@
|
|||||||
"integrity": "sha512-UKgI3/KON4u6ngSsnDADsUERqhZknsVZbnuzlRZXLQCmfC/MDld42fTydUE9B+Mla1AL6SJ/Pp6SlEFi/AVGfw==",
|
"integrity": "sha512-UKgI3/KON4u6ngSsnDADsUERqhZknsVZbnuzlRZXLQCmfC/MDld42fTydUE9B+Mla1AL6SJ/Pp6SlEFi/AVGfw==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"copy-anything": "^2.0.1",
|
"copy-anything": "^2.0.1",
|
||||||
"parse-node-version": "^1.0.1",
|
"parse-node-version": "^1.0.1",
|
||||||
@@ -4144,6 +4152,7 @@
|
|||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -4366,6 +4375,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-2.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-2.7.1.tgz",
|
||||||
"integrity": "sha512-vKeSifSJ6HoLaAB+B8aq/Qgm8a3dyxROzCtKNCsBQgiverpc4kWDQihoUwzUj+zNWJOykwSY4dNX3QrGwtVb9A==",
|
"integrity": "sha512-vKeSifSJ6HoLaAB+B8aq/Qgm8a3dyxROzCtKNCsBQgiverpc4kWDQihoUwzUj+zNWJOykwSY4dNX3QrGwtVb9A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.18.0",
|
"@babel/runtime": "^7.18.0",
|
||||||
"@rc-component/async-validator": "^5.0.3",
|
"@rc-component/async-validator": "^5.0.3",
|
||||||
@@ -4874,6 +4884,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.1.0"
|
"loose-envify": "^1.1.0"
|
||||||
},
|
},
|
||||||
@@ -4886,6 +4897,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||||
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.1.0",
|
"loose-envify": "^1.1.0",
|
||||||
"scheduler": "^0.23.2"
|
"scheduler": "^0.23.2"
|
||||||
@@ -5567,6 +5579,7 @@
|
|||||||
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
|
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"fdir": "^6.4.4",
|
"fdir": "^6.4.4",
|
||||||
@@ -5679,6 +5692,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
|
|||||||
40
src/api/spellCheck.ts
Normal file
40
src/api/spellCheck.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
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[]
|
||||||
|
}
|
||||||
|
query: string
|
||||||
|
response_mode: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function checkAndCorrectSpelling(
|
||||||
|
user_input_text: string,
|
||||||
|
auto_correct: boolean,
|
||||||
|
custom_vocab: string[]
|
||||||
|
) {
|
||||||
|
const requestBody: DifyRequest = {
|
||||||
|
inputs: {
|
||||||
|
user_input_text,
|
||||||
|
auto_correct,
|
||||||
|
custom_vocab
|
||||||
|
},
|
||||||
|
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-Dmsx84IAGk7rVCko5MWptmK3'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(requestBody)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Card, Row, Col, Typography, Space } from 'antd'
|
import { Card, Row, Col, Typography, Space } from 'antd'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { RobotOutlined, TranslationOutlined, FileTextOutlined } from '@ant-design/icons'
|
import { RobotOutlined, TranslationOutlined, FileTextOutlined, EditOutlined } from '@ant-design/icons'
|
||||||
|
|
||||||
const { Title, Paragraph } = Typography
|
const { Title, Paragraph } = Typography
|
||||||
|
|
||||||
@@ -22,6 +22,12 @@ const cards = [
|
|||||||
description: 'AI 驱动的翻译工具',
|
description: 'AI 驱动的翻译工具',
|
||||||
icon: <TranslationOutlined style={{ fontSize: 32, color: '#722ed1' }} />,
|
icon: <TranslationOutlined style={{ fontSize: 32, color: '#722ed1' }} />,
|
||||||
link: '/zh-en-translator'
|
link: '/zh-en-translator'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '错别字检测',
|
||||||
|
description: '智能检测和纠正中文文本中的错别字',
|
||||||
|
icon: <EditOutlined style={{ fontSize: 32, color: '#fa8c16' }} />,
|
||||||
|
link: '/spell-check'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
336
src/pages/spell-check/index.tsx
Normal file
336
src/pages/spell-check/index.tsx
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
import { useState } from 'react'
|
||||||
|
import { Button, Card, Form, Input, message, Space, Switch } from 'antd'
|
||||||
|
import { PageHeader } from '@ant-design/pro-components'
|
||||||
|
import { checkAndCorrectSpelling } from '@/api/spellCheck'
|
||||||
|
|
||||||
|
interface CorrectionResult {
|
||||||
|
original: string
|
||||||
|
corrected: string
|
||||||
|
position: number
|
||||||
|
suggestions?: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const SpellCheck = () => {
|
||||||
|
const [form] = Form.useForm()
|
||||||
|
const [isGenerating, setIsGenerating] = useState(false)
|
||||||
|
const [correctionResults, setCorrectionResults] = useState<CorrectionResult[]>([])
|
||||||
|
const [correctedText, setCorrectedText] = useState('')
|
||||||
|
const maxChars = 5000
|
||||||
|
|
||||||
|
const validateCustomVocab = (_: any, value: string) => {
|
||||||
|
if (!value) {
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
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()) {
|
||||||
|
message.warning('请输入需要检测错别字的文本')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证文本长度
|
||||||
|
if (formValues.user_input_text.length > maxChars) {
|
||||||
|
message.warning(`输入文本不能超过${maxChars}个字符`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsGenerating(true)
|
||||||
|
setCorrectionResults([])
|
||||||
|
setCorrectedText('')
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
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 = ''
|
||||||
|
let results: CorrectionResult[] = []
|
||||||
|
|
||||||
|
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
|
||||||
|
// 尝试解析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) {
|
||||||
|
// 解析失败,继续等待
|
||||||
|
}
|
||||||
|
} else if (parsed.event === 'error') {
|
||||||
|
throw new Error(parsed.message || 'Dify API 返回错误')
|
||||||
|
}
|
||||||
|
} catch (parseError) {
|
||||||
|
console.warn('跳过无法解析的行:', trimmedLine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} 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 : '检测失败,请稍后重试')
|
||||||
|
} finally {
|
||||||
|
setIsGenerating(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
form.resetFields()
|
||||||
|
setCorrectionResults([])
|
||||||
|
setCorrectedText('')
|
||||||
|
message.info('已重置表单')
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderCorrectionResults = () => {
|
||||||
|
if (isGenerating) {
|
||||||
|
return (
|
||||||
|
<div style={{ textAlign: 'center', color: '#999', padding: '20px' }}>
|
||||||
|
正在检测中,请稍候...
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 style={{ textAlign: 'center', marginBottom: '30px' }}>
|
||||||
|
<h1 style={{ color: '#1890ff', fontSize: '28px', fontWeight: 'bold', margin: 0 }}>
|
||||||
|
错别字检测与纠正系统
|
||||||
|
</h1>
|
||||||
|
</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 }]}
|
||||||
|
extra={
|
||||||
|
<div style={{ fontSize: '12px', color: '#666', marginTop: '4px' }}>
|
||||||
|
格式要求:合法的JSON数组,元素为字符串
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Input.TextArea
|
||||||
|
rows={4}
|
||||||
|
placeholder='请输入JSON格式词汇库,如:["正确词1", "正确词2"]'
|
||||||
|
style={{ fontSize: '14px' }}
|
||||||
|
/>
|
||||||
|
</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' }}
|
||||||
|
>
|
||||||
|
检测错别字
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="large"
|
||||||
|
onClick={handleReset}
|
||||||
|
disabled={isGenerating}
|
||||||
|
style={{ minWidth: '120px', height: '40px', fontSize: '16px' }}
|
||||||
|
>
|
||||||
|
重置
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Card title="检测结果">
|
||||||
|
<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}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SpellCheck
|
||||||
@@ -44,6 +44,10 @@ const router: RouteObject[] = [
|
|||||||
path: '/zh-en-translator',
|
path: '/zh-en-translator',
|
||||||
element: LazyLoad(lazy(() => import('@/pages/zh-en-translator')))
|
element: LazyLoad(lazy(() => import('@/pages/zh-en-translator')))
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/spell-check',
|
||||||
|
element: LazyLoad(lazy(() => import('@/pages/spell-check')))
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/404',
|
path: '/404',
|
||||||
element: <>404</>
|
element: <>404</>
|
||||||
|
|||||||
Reference in New Issue
Block a user