Update from Vibe Studio
This commit is contained in:
2602
package-lock.json
generated
2602
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -18,18 +18,20 @@
|
|||||||
"less": "^4.2.1",
|
"less": "^4.2.1",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-router-dom": "^6.28.1"
|
"react-markdown": "^10.1.0",
|
||||||
|
"react-router-dom": "^6.28.1",
|
||||||
|
"remark-gfm": "^4.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/vite": "4.0.12",
|
"@tailwindcss/vite": "4.0.12",
|
||||||
"@types/node": "^22.10.5",
|
"@types/node": "^22.10.5",
|
||||||
"@types/react": "^18.3.18",
|
"@types/react": "^18.3.18",
|
||||||
"@types/react-dom": "^18.3.5",
|
"@types/react-dom": "^18.3.5",
|
||||||
"@vitejs/plugin-react": "^4.3.4",
|
"@vitejs/plugin-react": "^4.7.0",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"prettier-plugin-tailwindcss": "^0.1.7",
|
"prettier-plugin-tailwindcss": "^0.1.7",
|
||||||
"tailwindcss": "4.0.12",
|
"tailwindcss": "4.0.12",
|
||||||
"typescript": "~5.6.3",
|
"typescript": "~5.6.3",
|
||||||
"vite": "^6.0.7"
|
"vite": "^6.0.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
38
src/api/contract-review.ts
Normal file
38
src/api/contract-review.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
export interface ContractReviewRequest {
|
||||||
|
contract_text: string
|
||||||
|
knowledge_base: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DifyRequest {
|
||||||
|
inputs: {
|
||||||
|
contract_text: string
|
||||||
|
knowledge_base: string
|
||||||
|
}
|
||||||
|
query: string
|
||||||
|
response_mode: string
|
||||||
|
user: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送合同审核请求到Dify平台
|
||||||
|
*/
|
||||||
|
export function submitContractReview(contract_text: string, knowledge_base: string) {
|
||||||
|
const requestBody: DifyRequest = {
|
||||||
|
inputs: {
|
||||||
|
contract_text,
|
||||||
|
knowledge_base
|
||||||
|
},
|
||||||
|
query: '1',
|
||||||
|
response_mode: 'streaming',
|
||||||
|
user: 'contract-review-user'
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetch('https://copilot.sino-bridge.com/v1/chat-messages', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': 'Bearer app-rzQ05wbVNUcNaoE7nnzmBueE'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(requestBody)
|
||||||
|
})
|
||||||
|
}
|
||||||
292
src/pages/contract-review/index.tsx
Normal file
292
src/pages/contract-review/index.tsx
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
import { useState } from 'react'
|
||||||
|
import { Button, Card, Form, Input, message, Row, Col, Divider, Typography, Space } from 'antd'
|
||||||
|
import { FileTextOutlined, DownloadOutlined } from '@ant-design/icons'
|
||||||
|
import ReactMarkdown from 'react-markdown'
|
||||||
|
import remarkGfm from 'remark-gfm'
|
||||||
|
import { submitContractReview } from '@/api/contract-review'
|
||||||
|
|
||||||
|
const { Title, Paragraph } = Typography
|
||||||
|
|
||||||
|
const { TextArea } = Input
|
||||||
|
|
||||||
|
const ContractReview = () => {
|
||||||
|
const [form] = Form.useForm()
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
const [reviewResult, setReviewResult] = useState('')
|
||||||
|
const [currentContract, setCurrentContract] = useState('')
|
||||||
|
const maxChars = 10000
|
||||||
|
|
||||||
|
const handleReview = async () => {
|
||||||
|
try {
|
||||||
|
const contract_text = form.getFieldValue('contract_fragment') || ''
|
||||||
|
const knowledge_base = '通用合同审核规则' // 固定使用通用知识库
|
||||||
|
|
||||||
|
// 验证输入
|
||||||
|
if (!contract_text.trim()) {
|
||||||
|
message.warning('请输入需要审核的合同片段')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true)
|
||||||
|
setReviewResult('')
|
||||||
|
|
||||||
|
const response = await submitContractReview(contract_text, knowledge_base)
|
||||||
|
|
||||||
|
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 handleClear = () => {
|
||||||
|
form.resetFields()
|
||||||
|
setReviewResult('')
|
||||||
|
setCurrentContract('')
|
||||||
|
message.info('已清空内容')
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleExport = () => {
|
||||||
|
if (!reviewResult) {
|
||||||
|
message.warning('没有可导出的内容')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建下载链接
|
||||||
|
const element = document.createElement('a')
|
||||||
|
const file = new Blob([reviewResult], { type: 'text/plain;charset=utf-8' })
|
||||||
|
element.href = URL.createObjectURL(file)
|
||||||
|
element.download = `合同审核报告_${new Date().toLocaleString('zh-CN', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit'
|
||||||
|
}).replace(/\//g, '-')}.txt`
|
||||||
|
document.body.appendChild(element)
|
||||||
|
element.click()
|
||||||
|
document.body.removeChild(element)
|
||||||
|
message.success('导出成功')
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ maxWidth: '1200px', margin: '0 auto', padding: '20px' }}>
|
||||||
|
<Space direction="vertical" size="small" style={{ marginBottom: '30px', textAlign: 'center' }}>
|
||||||
|
<Title level={2} style={{ color: '#1890ff', marginBottom: '8px' }}>
|
||||||
|
合同智能审核
|
||||||
|
</Title>
|
||||||
|
<Paragraph style={{ color: '#666', fontSize: '14px', marginBottom: 0 }}>
|
||||||
|
AI驱动的合同智能审核工具,基于通用合同审核规则快速识别风险点并提供专业建议
|
||||||
|
</Paragraph>
|
||||||
|
</Space>
|
||||||
|
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '20px' }}>
|
||||||
|
{/* 参数输入区 */}
|
||||||
|
<Card title="参数输入区">
|
||||||
|
<Form form={form} layout="vertical">
|
||||||
|
<Form.Item
|
||||||
|
name="contract_fragment"
|
||||||
|
label={
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'space-between', width: '100%' }}>
|
||||||
|
<span>合同片段</span>
|
||||||
|
<span style={{ color: currentContract.length > maxChars ? '#ff4d4f' : '#999' }}>
|
||||||
|
{currentContract.length} / {maxChars}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
rules={[
|
||||||
|
{ required: true, message: '请输入需要审核的合同片段' },
|
||||||
|
{ max: maxChars, message: `输入内容不能超过${maxChars}个字符` }
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<TextArea
|
||||||
|
placeholder="请输入需要审核的合同片段内容(最多10000个字符)"
|
||||||
|
rows={10}
|
||||||
|
value={currentContract}
|
||||||
|
onChange={(e) => setCurrentContract(e.target.value)}
|
||||||
|
showCount
|
||||||
|
maxLength={maxChars}
|
||||||
|
style={{ fontSize: '14px' }}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
{/* 知识库选择已隐藏,默认使用通用合同审核规则 */}
|
||||||
|
<input type="hidden" name="knowledge_base" value="通用合同审核规则" />
|
||||||
|
</Form>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* 操作按钮区 */}
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'center', gap: '16px' }}>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
|
onClick={handleReview}
|
||||||
|
loading={loading}
|
||||||
|
icon={<FileTextOutlined />}
|
||||||
|
style={{ minWidth: '140px', height: '44px', fontSize: '16px' }}
|
||||||
|
>
|
||||||
|
开始审核
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="large"
|
||||||
|
onClick={handleClear}
|
||||||
|
disabled={loading}
|
||||||
|
style={{ minWidth: '120px', height: '44px', fontSize: '16px' }}
|
||||||
|
>
|
||||||
|
清空
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="large"
|
||||||
|
onClick={handleExport}
|
||||||
|
disabled={!reviewResult || loading}
|
||||||
|
icon={<DownloadOutlined />}
|
||||||
|
style={{ minWidth: '120px', height: '44px', fontSize: '16px' }}
|
||||||
|
>
|
||||||
|
导出报告
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 内容展示区 */}
|
||||||
|
<Card
|
||||||
|
title="审核结果"
|
||||||
|
extra={
|
||||||
|
reviewResult && (
|
||||||
|
<span style={{ color: '#52c41a', fontWeight: 'bold' }}>
|
||||||
|
审核完成
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
minHeight: '300px',
|
||||||
|
maxHeight: '600px',
|
||||||
|
overflowY: 'auto',
|
||||||
|
padding: '20px',
|
||||||
|
backgroundColor: '#fafafa',
|
||||||
|
border: '1px solid #d9d9d9',
|
||||||
|
borderRadius: '6px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<div style={{ textAlign: 'center', color: '#999', padding: '40px' }}>
|
||||||
|
<div style={{ fontSize: '16px', marginBottom: '8px' }}>🤖 AI 正在审核中...</div>
|
||||||
|
<div style={{ fontSize: '12px' }}>请稍候,审核过程可能需要几分钟时间</div>
|
||||||
|
</div>
|
||||||
|
) : reviewResult ? (
|
||||||
|
<ReactMarkdown
|
||||||
|
remarkPlugins={[remarkGfm]}
|
||||||
|
components={{
|
||||||
|
// 自定义样式
|
||||||
|
h1: ({ children }) => <h1 style={{ color: '#1890ff', fontSize: '20px', marginBottom: '16px', borderBottom: '2px solid #1890ff', paddingBottom: '8px' }}>{children}</h1>,
|
||||||
|
h2: ({ children }) => <h2 style={{ color: '#1890ff', fontSize: '18px', marginBottom: '12px', marginTop: '20px' }}>{children}</h2>,
|
||||||
|
h3: ({ children }) => <h3 style={{ color: '#333', fontSize: '16px', marginBottom: '10px', marginTop: '16px' }}>{children}</h3>,
|
||||||
|
p: ({ children }) => <p style={{ marginBottom: '12px', lineHeight: '1.6', color: '#333' }}>{children}</p>,
|
||||||
|
table: ({ children }) => (
|
||||||
|
<div style={{ overflowX: 'auto', marginBottom: '16px' }}>
|
||||||
|
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '14px' }}>{children}</table>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
th: ({ children }) => <th style={{ border: '1px solid #d9d9d9', padding: '8px 12px', backgroundColor: '#f5f5f5', fontWeight: 'bold', textAlign: 'left' }}>{children}</th>,
|
||||||
|
td: ({ children }) => <td style={{ border: '1px solid #d9d9d9', padding: '8px 12px' }}>{children}</td>,
|
||||||
|
ul: ({ children }) => <ul style={{ marginBottom: '12px', paddingLeft: '20px' }}>{children}</ul>,
|
||||||
|
ol: ({ children }) => <ol style={{ marginBottom: '12px', paddingLeft: '20px' }}>{children}</ol>,
|
||||||
|
li: ({ children }) => <li style={{ marginBottom: '4px', lineHeight: '1.6' }}>{children}</li>,
|
||||||
|
blockquote: ({ children }) => (
|
||||||
|
<blockquote style={{ borderLeft: '4px solid #1890ff', paddingLeft: '16px', marginLeft: 0, marginBottom: '12px', fontStyle: 'italic', backgroundColor: '#f0f9ff', padding: '12px 16px' }}>
|
||||||
|
{children}
|
||||||
|
</blockquote>
|
||||||
|
),
|
||||||
|
code: ({ children }) => (
|
||||||
|
<code style={{ backgroundColor: '#f5f5f5', padding: '2px 4px', borderRadius: '3px', fontSize: '13px', fontFamily: 'Monaco, Consolas, monospace' }}>
|
||||||
|
{children}
|
||||||
|
</code>
|
||||||
|
),
|
||||||
|
pre: ({ children }) => (
|
||||||
|
<pre style={{ backgroundColor: '#f5f5f5', padding: '12px', borderRadius: '6px', overflowX: 'auto', marginBottom: '12px' }}>
|
||||||
|
{children}
|
||||||
|
</pre>
|
||||||
|
),
|
||||||
|
strong: ({ children }) => <strong style={{ fontWeight: 'bold', color: '#1890ff' }}>{children}</strong>
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{reviewResult}
|
||||||
|
</ReactMarkdown>
|
||||||
|
) : (
|
||||||
|
<div style={{ textAlign: 'center', color: '#999', padding: '60px' }}>
|
||||||
|
<FileTextOutlined style={{ fontSize: '48px', marginBottom: '16px', display: 'block' }} />
|
||||||
|
<div>审核结果将在这里显示</div>
|
||||||
|
<div style={{ fontSize: '12px', marginTop: '8px' }}>
|
||||||
|
请输入合同片段,然后点击"开始审核"
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ContractReview
|
||||||
@@ -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, EditOutlined } from '@ant-design/icons'
|
import { RobotOutlined, TranslationOutlined, FileTextOutlined, EditOutlined, CheckCircleOutlined } from '@ant-design/icons'
|
||||||
|
|
||||||
const { Title, Paragraph } = Typography
|
const { Title, Paragraph } = Typography
|
||||||
|
|
||||||
@@ -34,6 +34,12 @@ const cards = [
|
|||||||
description: '更智能的错别字检测与修正系统,支持分类和建议',
|
description: '更智能的错别字检测与修正系统,支持分类和建议',
|
||||||
icon: <EditOutlined style={{ fontSize: 32, color: '#13c2c2' }} />,
|
icon: <EditOutlined style={{ fontSize: 32, color: '#13c2c2' }} />,
|
||||||
link: '/spell-check-system'
|
link: '/spell-check-system'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '合同智能审核',
|
||||||
|
description: 'AI驱动的合同智能审核工具,快速识别风险点并提供专业建议',
|
||||||
|
icon: <CheckCircleOutlined style={{ fontSize: 32, color: '#1890ff' }} />,
|
||||||
|
link: '/contract-review'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { Button, Card, Form, Input, message, Space } from 'antd'
|
import { Button, Card, Form, Input, message, Space, Typography } from 'antd'
|
||||||
import { PageHeader } from '@ant-design/pro-components'
|
|
||||||
import { translateChineseToEnglish } from '@/api/zh-en-translator'
|
import { translateChineseToEnglish } from '@/api/zh-en-translator'
|
||||||
|
|
||||||
|
const { Title, Paragraph } = Typography
|
||||||
|
|
||||||
const ZhEnTranslator = () => {
|
const ZhEnTranslator = () => {
|
||||||
const [form] = Form.useForm()
|
const [form] = Form.useForm()
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
@@ -115,11 +116,14 @@ const ZhEnTranslator = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ maxWidth: '900px', margin: '0 auto', padding: '20px' }}>
|
<div style={{ maxWidth: '900px', margin: '0 auto', padding: '20px' }}>
|
||||||
<PageHeader
|
<Space direction="vertical" size="small" style={{ marginBottom: '30px', textAlign: 'center' }}>
|
||||||
title="中文转英文翻译助手"
|
<Title level={2} style={{ color: '#1890ff', marginBottom: '8px' }}>
|
||||||
subTitle="专业的中文到英文文本翻译工具,支持流式输出,翻译结果地道自然"
|
中文转英文翻译助手
|
||||||
style={{ marginBottom: '30px' }}
|
</Title>
|
||||||
/>
|
<Paragraph style={{ color: '#666', fontSize: '14px', marginBottom: 0 }}>
|
||||||
|
专业的中文到英文文本翻译工具,支持流式输出,翻译结果地道自然
|
||||||
|
</Paragraph>
|
||||||
|
</Space>
|
||||||
|
|
||||||
<Space direction="vertical" style={{ width: '100%' }} size="large">
|
<Space direction="vertical" style={{ width: '100%' }} size="large">
|
||||||
<Card title="参数输入区">
|
<Card title="参数输入区">
|
||||||
|
|||||||
@@ -52,6 +52,10 @@ const router: RouteObject[] = [
|
|||||||
path: '/spell-check-system',
|
path: '/spell-check-system',
|
||||||
element: LazyLoad(lazy(() => import('@/pages/spell-check-system')))
|
element: LazyLoad(lazy(() => import('@/pages/spell-check-system')))
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/contract-review',
|
||||||
|
element: LazyLoad(lazy(() => import('@/pages/contract-review')))
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/404',
|
path: '/404',
|
||||||
element: <>404</>
|
element: <>404</>
|
||||||
|
|||||||
Reference in New Issue
Block a user