Files
copilot-toolbox-templatewe/src/pages/ai-content-generator/index.tsx
2026-01-22 09:56:47 +00:00

295 lines
8.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState } from 'react'
import {
Card,
Form,
Input,
Button,
Typography,
Space,
Upload,
message,
Spin
} from 'antd'
import {
RocketOutlined,
ReloadOutlined,
UploadOutlined
} from '@ant-design/icons'
import type { UploadFile, UploadProps } from 'antd'
const { Title, Text } = Typography
const { TextArea } = Input
const AIContentGenerator: React.FC = () => {
const [form] = Form.useForm()
const [generatedResult, setGeneratedResult] = useState<string>('')
const [isGenerating, setIsGenerating] = useState<boolean>(false)
const [fileList, setFileList] = useState<UploadFile[]>([])
// 处理文件上传
const handleUpload: UploadProps['customRequest'] = async ({ file, onSuccess, onError }) => {
try {
const fileObj = file as File
const response = await fetch('https://copilot.sino-bridge.com/v1/files/upload', {
method: 'POST',
headers: {
'Authorization': 'Bearer app-YG1qx3qGCPsgqMIyBr8tCmJ7'
},
body: (() => {
const formData = new FormData()
formData.append('file', fileObj)
return formData
})()
})
if (response.ok) {
const data = await response.json()
message.success('文件上传成功')
onSuccess && onSuccess(data, {} as any)
} else {
throw new Error('文件上传失败')
}
} catch (error) {
console.error('文件上传错误:', error)
message.error('文件上传失败')
onError && onError(error as any)
}
}
// 处理文件变化
const handleFileChange: UploadProps['onChange'] = (info) => {
let newFileList = [...info.fileList]
// 限制上传文件数量为 3 个
newFileList = newFileList.slice(-3)
setFileList(newFileList)
}
// 生成内容
const handleGenerate = async () => {
try {
const values = await form.validateFields()
const userInput = values.user_input?.trim()
if (!userInput) {
message.warning('请输入需要生成内容的描述')
return
}
setIsGenerating(true)
setGeneratedResult('')
// 准备文件数据
const files = fileList.map(file => {
if (file.response) {
return file.response
}
return null
}).filter(Boolean)
const requestBody = {
inputs: {
user_input: userInput,
files: files
},
query: '1',
response_mode: 'streaming',
...(files.length > 0 && {
preview_url: '1',
transfer_method: 'local_file',
user: 'admin'
})
}
const response = await fetch('https://copilot.sino-bridge.com/v1/chat-messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer app-YG1qx3qGCPsgqMIyBr8tCmJ7'
},
body: JSON.stringify(requestBody)
})
if (!response.ok) {
throw new Error(`请求失败: ${response.status}`)
}
const reader = response.body?.getReader()
if (!reader) {
throw new Error('无法读取响应流')
}
const decoder = new TextDecoder()
let content = ''
while (true) {
const { done, value } = await reader.read()
if (done) break
const chunk = decoder.decode(value, { stream: true })
const lines = chunk.split('\n')
for (const line of lines) {
if (line.startsWith('data: ')) {
const dataStr = line.slice(6)
if (dataStr === '[DONE]') {
continue
}
try {
const data = JSON.parse(dataStr)
if (data.event === 'message' && data.answer) {
content += data.answer
setGeneratedResult(content)
}
} catch (e) {
// 忽略解析错误的行
}
}
}
}
message.success('内容生成完成')
} catch (error) {
console.error('生成内容错误:', error)
message.error('生成内容失败,请稍后重试')
} finally {
setIsGenerating(false)
}
}
// 清空重置
const handleReset = () => {
form.resetFields()
setGeneratedResult('')
setFileList([])
}
return (
<div style={{
minHeight: '100vh',
backgroundColor: '#ffffff',
padding: '24px',
display: 'flex',
flexDirection: 'column',
alignItems: 'center'
}}>
{/* 页面标题区 */}
<div style={{ marginBottom: 32, textAlign: 'center' }}>
<Title level={2} style={{ color: '#1890ff', marginBottom: 8 }}>
AI内容生成工具
</Title>
<Text type='secondary'>
AI将为您生成高质量内容
</Text>
</div>
<div style={{
width: '100%',
maxWidth: '800px',
display: 'flex',
flexDirection: 'column',
gap: 24
}}>
{/* 参数输入区 */}
<Card title='内容描述' style={{ width: '100%' }}>
<Form form={form} layout='vertical'>
<Form.Item
name='user_input'
rules={[{ required: true, message: '请输入您需要生成内容的描述' }]}
>
<TextArea
placeholder='请输入您需要生成内容的描述...'
rows={4}
style={{ fontSize: 14 }}
/>
</Form.Item>
{/* 文件上传区 */}
<Form.Item label='附件(可选)'>
<Upload
multiple
fileList={fileList}
onChange={handleFileChange}
customRequest={handleUpload}
beforeUpload={() => false} // 阻止自动上传
onRemove={() => {
setFileList([])
}}
>
<Button icon={<UploadOutlined />}></Button>
</Upload>
<Text type='secondary' style={{ fontSize: 12, display: 'block', marginTop: 4 }}>
AI将结合文件内容生成更精准的内容
</Text>
</Form.Item>
{/* 操作按钮区 */}
<Form.Item>
<Space size='middle'>
<Button
type='primary'
icon={<RocketOutlined />}
onClick={handleGenerate}
loading={isGenerating}
size='large'
>
{isGenerating ? '生成中...' : '生成内容'}
</Button>
<Button
icon={<ReloadOutlined />}
onClick={handleReset}
disabled={isGenerating}
size='large'
>
</Button>
</Space>
</Form.Item>
</Form>
</Card>
{/* 内容展示区 */}
<Card title='生成结果' style={{ width: '100%', minHeight: '200px' }}>
{isGenerating ? (
<div style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
minHeight: '150px',
flexDirection: 'column',
gap: 16
}}>
<Spin size='large' />
<Text type='secondary'>AI正在生成内容...</Text>
</div>
) : generatedResult ? (
<div style={{
padding: '16px',
backgroundColor: '#fafafa',
borderRadius: '6px',
whiteSpace: 'pre-wrap',
wordBreak: 'break-word',
minHeight: '100px'
}}>
{generatedResult}
</div>
) : (
<div style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
minHeight: '100px',
color: '#999',
fontStyle: 'italic'
}}>
{generatedResult === '' ? '生成的内容将在这里显示...' : ''}
</div>
)}
</Card>
</div>
</div>
)
}
export default AIContentGenerator