Update from Vibe Studio

This commit is contained in:
Vibe Studio
2026-01-22 09:56:47 +00:00
parent 976704f9ec
commit 65e5656fd3
3 changed files with 363 additions and 0 deletions

View File

@@ -0,0 +1,64 @@
export interface ContentGenerationRequest {
inputs: {
user_input: string
files?: any[]
}
query: string
response_mode: string
preview_url?: string
transfer_method?: string
user?: string
}
export interface DifyResponse {
message: {
content: string
}
}
/**
* 生成AI内容
* @param userInput 用户输入的文本
* @param files 可选的文件数组
* @returns Promise<DifyResponse>
*/
export function generateContent(userInput: string, files?: any[]) {
const requestBody: ContentGenerationRequest = {
inputs: {
user_input: userInput,
files: files || []
},
query: '1',
response_mode: 'streaming',
preview_url: files && files.length > 0 ? '1' : undefined,
transfer_method: files && files.length > 0 ? 'local_file' : undefined,
user: files && files.length > 0 ? 'admin' : undefined
}
return fetch('https://copilot.sino-bridge.com/v1/chat-messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer app-YG1qx3qGCPsgqMIyBr8tCmJ7'
},
body: JSON.stringify(requestBody)
})
}
/**
* 上传文件到Dify平台
* @param file 文件对象
* @returns Promise<any>
*/
export function uploadFile(file: File) {
const formData = new FormData()
formData.append('file', file)
return fetch('https://copilot.sino-bridge.com/v1/files/upload', {
method: 'POST',
headers: {
'Authorization': 'Bearer app-YG1qx3qGCPsgqMIyBr8tCmJ7'
},
body: formData
})
}

View File

@@ -0,0 +1,295 @@
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

View File

@@ -30,6 +30,10 @@ const router: RouteObject[] = [
children: [
{
path: '/',
element: LazyLoad(lazy(() => import('@/pages/ai-content-generator')))
},
{
path: '/home',
element: LazyLoad(lazy(() => import('@/pages/home')))
},
{