Update from Vibe Studio

This commit is contained in:
Vibe Studio
2026-01-20 09:19:36 +00:00
parent 09becc5a6a
commit 63cdc32095

View File

@@ -1,73 +1,371 @@
import { Card, Row, Col, Typography, Space } from 'antd'
import { Link } from 'react-router-dom'
import { RobotOutlined, TranslationOutlined, FileTextOutlined, EditOutlined, SafetyCertificateOutlined } from '@ant-design/icons'
import { useState } from 'react'
import { Button, Card, Form, Input, message, Space, Typography } from 'antd'
import { reviewContract } from '@/api/contract-review'
const { Title, Paragraph } = Typography
const { Title, Paragraph, Text } = Typography
const cards = [
{
title: '测试页面 1',
description: 'Dify AI Agent 集成示例',
icon: <RobotOutlined style={{ fontSize: 32, color: '#1890ff' }} />,
link: '/test1'
},
{
title: '测试页面 2',
description: '功能开发中...',
icon: <FileTextOutlined style={{ fontSize: 32, color: '#52c41a' }} />,
link: '/test2'
},
{
title: '中英翻译器',
description: 'AI 驱动的翻译工具',
icon: <TranslationOutlined style={{ fontSize: 32, color: '#722ed1' }} />,
link: '/zh-en-translator'
},
{
title: '合同智能审核',
description: 'AI 驱动的合同智能审核系统,提供专业建议',
icon: <SafetyCertificateOutlined style={{ fontSize: 32, color: '#eb2f96' }} />,
link: '/contract-review'
},
{
title: '错别字检测',
description: '智能检测和纠正中文文本中的错别字',
icon: <EditOutlined style={{ fontSize: 32, color: '#fa8c16' }} />,
link: '/spell-check'
},
{
title: '错别字检测与修正系统',
description: '更智能的错别字检测与修正系统,支持分类和建议',
icon: <EditOutlined style={{ fontSize: 32, color: '#13c2c2' }} />,
link: '/spell-check-system'
}
]
const { TextArea } = Input
const HomePage: React.FC = () => {
const [form] = Form.useForm()
const [loading, setLoading] = useState(false)
const [reviewResult, setReviewResult] = useState('')
const [currentInput, setCurrentInput] = useState('')
const maxChars = 5000
// 简单的markdown处理函数
const formatMarkdown = (text: string) => {
if (!text) return []
const lines = text.split('\n')
const elements: React.ReactNode[] = []
let currentList: string[] = []
let inTable = false
let tableHeaders: string[] = []
let tableRows: string[][] = []
const flushList = () => {
if (currentList.length > 0) {
elements.push(
<ul key={elements.length} style={{ paddingLeft: '20px', margin: '12px 0' }}>
{currentList.map((item, index) => (
<li key={index} style={{ margin: '4px 0' }}>{item}</li>
))}
</ul>
)
currentList = []
}
}
const flushTable = () => {
if (tableHeaders.length > 0) {
elements.push(
<div key={elements.length} style={{ margin: '16px 0', overflowX: 'auto' }}>
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
<thead>
<tr>
{tableHeaders.map((header, index) => (
<th key={index} style={{
border: '1px solid #d9d9d9',
padding: '8px 12px',
backgroundColor: '#f5f5f5',
fontWeight: 'bold',
textAlign: 'left'
}}>
{header}
</th>
))}
</tr>
</thead>
<tbody>
{tableRows.map((row, rowIndex) => (
<tr key={rowIndex}>
{row.map((cell, cellIndex) => (
<td key={cellIndex} style={{
border: '1px solid #d9d9d9',
padding: '8px 12px'
}}>
{cell}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
)
tableHeaders = []
tableRows = []
inTable = false
}
}
lines.forEach((line, index) => {
const trimmedLine = line.trim()
// 处理标题
if (trimmedLine.startsWith('# ')) {
flushList()
flushTable()
elements.push(
<Title key={elements.length} level={1} style={{ color: '#1890ff', borderBottom: '1px solid #d9d9d9', paddingBottom: '8px', marginBottom: '16px' }}>
{trimmedLine.substring(2)}
</Title>
)
} else if (trimmedLine.startsWith('## ')) {
flushList()
flushTable()
elements.push(
<Title key={elements.length} level={2} style={{ color: '#1890ff', marginTop: '24px', marginBottom: '12px' }}>
{trimmedLine.substring(3)}
</Title>
)
} else if (trimmedLine.startsWith('### ')) {
flushList()
flushTable()
elements.push(
<Title key={elements.length} level={3} style={{ color: '#1890ff', marginTop: '20px', marginBottom: '10px' }}>
{trimmedLine.substring(4)}
</Title>
)
}
// 处理分割线
else if (trimmedLine === '---') {
flushList()
flushTable()
elements.push(
<hr key={elements.length} style={{ border: 'none', borderTop: '1px solid #d9d9d9', margin: '24px 0' }} />
)
}
// 处理列表项
else if (trimmedLine.startsWith('- ') || trimmedLine.startsWith('* ')) {
flushTable()
currentList.push(trimmedLine.substring(2))
}
// 处理表格
else if (trimmedLine.includes('|') && !inTable) {
flushList()
inTable = true
tableHeaders = trimmedLine.split('|').map(h => h.trim()).filter(h => h)
} else if (trimmedLine.includes('|') && inTable && !trimmedLine.includes('---')) {
const row = trimmedLine.split('|').map(c => c.trim()).filter(c => c)
if (row.length > 0) {
tableRows.push(row)
}
} else if (trimmedLine.includes('|') && inTable && trimmedLine.includes('---')) {
// 跳过分隔行
}
// 处理普通段落
else if (trimmedLine) {
flushList()
flushTable()
// 处理加粗文本
const boldRegex = /\*\*(.*?)\*\*/g
const parts = trimmedLine.split(boldRegex)
if (parts.length > 1) {
elements.push(
<Paragraph key={elements.length} style={{ margin: '12px 0' }}>
{parts.map((part, partIndex) =>
partIndex % 2 === 1 ?
<Text key={partIndex} strong style={{ color: '#262626' }}>{part}</Text> :
part
)}
</Paragraph>
)
} else {
elements.push(
<Paragraph key={elements.length} style={{ margin: '12px 0', lineHeight: '1.6' }}>
{trimmedLine}
</Paragraph>
)
}
}
})
// 清理剩余的内容
flushList()
flushTable()
return elements
}
const handleReview = async () => {
try {
const contract_text = form.getFieldValue('contract_text') || ''
if (!contract_text.trim()) {
message.warning('请输入合同片段内容')
return
}
setLoading(true)
setReviewResult('')
const response = await reviewContract(contract_text)
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('审核完成')
setLoading(false)
return
}
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
setReviewResult(fullContent)
} else if (parsed.event === 'error') {
throw new Error(parsed.message || 'Dify API 返回错误')
}
} catch (parseError) {
console.warn('跳过无法解析的行:', trimmedLine)
}
}
}
}
} finally {
reader.releaseLock()
}
if (fullContent) {
message.success('审核完成')
} else {
throw new Error('未收到审核结果')
}
} catch (error) {
console.error('审核错误:', error)
message.error(error instanceof Error ? error.message : '审核失败,请稍后重试')
} finally {
setLoading(false)
}
}
const handleReset = () => {
form.resetFields()
setReviewResult('')
setCurrentInput('')
message.info('已重置内容')
}
const handleReReview = () => {
handleReview()
}
return (
<div style={{ padding: 24 }}>
<Title level={2}>使 AI </Title>
<Paragraph style={{ marginBottom: 32 }}>
使访
<div style={{ maxWidth: '1200px', margin: '0 auto', padding: '24px' }}>
<Title level={2} style={{ textAlign: 'center', marginBottom: '30px' }}></Title>
<Paragraph style={{ textAlign: 'center', marginBottom: '40px', fontSize: '16px' }}>
AI技术对合同片段进行智能审核
</Paragraph>
<Row gutter={[16, 16]}>
{cards.map((card, index) => (
<Col xs={24} sm={12} md={8} key={index}>
<Link to={card.link}>
<Card hoverable style={{ height: '100%' }}>
<Space direction='vertical' align='center' style={{ width: '100%' }}>
{card.icon}
<Title level={4} style={{ marginBottom: 0 }}>{card.title}</Title>
<Paragraph type='secondary' style={{ marginBottom: 0 }}>
{card.description}
</Paragraph>
</Space>
<Space direction="vertical" style={{ width: '100%' }} size="large">
{/* 参数输入区 */}
<Card title="参数输入区">
<Form form={form} layout="vertical">
<Form.Item
name="contract_text"
label={
<div style={{ display: 'flex', justifyContent: 'space-between', width: '100%' }}>
<span></span>
<span style={{ color: currentInput.length > maxChars ? '#ff4d4f' : '#999' }}>
{currentInput.length} / {maxChars}
</span>
</div>
}
rules={[
{ required: true, message: '请输入合同片段内容' },
{ max: maxChars, message: `输入内容不能超过${maxChars}个字符` }
]}
>
<TextArea
placeholder="请输入需要审核的合同片段内容最多5000个字符"
rows={8}
value={currentInput}
onChange={(e) => setCurrentInput(e.target.value)}
showCount
maxLength={maxChars}
style={{ fontSize: '14px', fontFamily: 'monospace' }}
/>
</Form.Item>
</Form>
</Card>
</Link>
</Col>
))}
</Row>
{/* 操作按钮区 */}
<Space style={{ width: '100%', justifyContent: 'center' }} size="middle">
<Button
type="primary"
size="large"
onClick={handleReview}
loading={loading}
style={{ minWidth: '120px', height: '40px', fontSize: '16px' }}
>
</Button>
{reviewResult && (
<Button
size="large"
onClick={handleReReview}
disabled={loading}
style={{ minWidth: '120px', height: '40px', fontSize: '16px' }}
>
</Button>
)}
<Button
size="large"
onClick={handleReset}
disabled={loading}
style={{ minWidth: '120px', height: '40px', fontSize: '16px' }}
>
</Button>
</Space>
{/* 内容展示区 */}
<Card title="审核结果">
<div
style={{
backgroundColor: '#fafafa',
border: '1px solid #d9d9d9',
borderRadius: '4px',
padding: '15px',
minHeight: '300px',
maxHeight: '500px',
overflowY: 'auto',
fontSize: '14px',
lineHeight: '1.6'
}}
>
{loading ? (
<div style={{ textAlign: 'center', color: '#999', padding: '20px' }}>
...
</div>
) : reviewResult ? (
<div>
{formatMarkdown(reviewResult)}
</div>
) : (
<div style={{ textAlign: 'center', color: '#999' }}>
</div>
)}
</div>
</Card>
</Space>
</div>
)
}