Update from Vibe Studio

This commit is contained in:
Vibe Studio
2026-01-12 09:12:41 +00:00
parent a4605e311a
commit a6ae5199b0
29756 changed files with 2526222 additions and 278 deletions

104
src/api/financial-news.ts Normal file
View File

@@ -0,0 +1,104 @@
export interface DifyRequest {
inputs: Record<string, any>
query: string
response_mode: string
user?: string
}
export interface DifyResponse {
id: string
answer: string
created_at: number
}
export interface FinancialNewsResponse {
success: boolean
data?: {
content: string
message_id: string
}
error?: string
}
/**
* 获取财经新闻要点
* @param query 用户希望查询的内容
* @param user 用户标识
* @param onMessage 接收流式消息的回调函数
* @returns Promise<void>
*/
export async function getFinancialNews(
query: string,
user: string,
onMessage: (content: string, isDone: boolean) => void
): Promise<void> {
const url = 'https://copilot.sino-bridge.com/v1/chat-messages'
const token = 'app-OvYx7jfb3NT7JX2ig7ofOYqa'
const requestData: DifyRequest = {
inputs: {
query: query
},
query: '1',
response_mode: 'streaming',
user: user
}
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(requestData)
})
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
const reader = response.body?.getReader()
if (!reader) {
throw new Error('无法读取响应流')
}
const decoder = new TextDecoder()
let accumulatedContent = ''
while (true) {
const { done, value } = await reader.read()
if (done) break
const chunk = decoder.decode(value, { stream: true })
const lines = chunk.split('\n').filter(line => line.trim())
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6)
if (data === '[DONE]') {
onMessage(accumulatedContent, true)
return
}
try {
const parsed = JSON.parse(data) as DifyResponse
if (parsed.answer) {
accumulatedContent += parsed.answer
onMessage(accumulatedContent, false)
}
} catch (parseError) {
console.warn('解析数据时出错:', parseError)
}
}
}
}
onMessage(accumulatedContent, true)
} catch (error) {
console.error('获取财经新闻失败:', error)
throw new Error(error instanceof Error ? error.message : '未知错误')
}
}

View File

@@ -0,0 +1,249 @@
import { useState } from 'react'
import { Card, Button, Typography, Space, Spin, message, Input } from 'antd'
import { FileTextOutlined, ReloadOutlined, SearchOutlined } from '@ant-design/icons'
import ReactMarkdown from 'react-markdown'
import { getFinancialNews } from '@/api/financial-news'
const { Title, Paragraph } = Typography
const { TextArea } = Input
const FinancialNewsPage: React.FC = () => {
const [loading, setLoading] = useState(false)
const [content, setContent] = useState('')
const [isComplete, setIsComplete] = useState(false)
const [query, setQuery] = useState('')
const handleGenerate = async () => {
if (!query.trim()) {
message.warning('请输入您希望查询的内容')
return
}
setLoading(true)
setContent('')
setIsComplete(false)
// 生成用户标识,可以根据实际需求调整
const user = `user_${Date.now()}`
try {
await getFinancialNews(query, user, (newContent, done) => {
setContent(newContent)
if (done) {
setIsComplete(true)
setLoading(false)
}
})
} catch (error) {
console.error('生成失败:', error)
message.error('获取财经新闻要点失败,请稍后重试')
setLoading(false)
}
}
return (
<div className="min-h-screen bg-white" style={{ padding: '24px' }}>
<div className="max-w-4xl mx-auto">
{/* 页面标题区 */}
<div className="text-center mb-8">
<Space direction="vertical" size="middle">
<Title
level={2}
style={{
color: '#1890ff',
marginBottom: 0
}}
>
<FileTextOutlined style={{ marginRight: 8 }} />
</Title>
<Paragraph
style={{
fontSize: 16,
color: '#666',
marginBottom: 0
}}
>
</Paragraph>
</Space>
</div>
{/* 查询输入区 */}
<div className="mb-6">
<TextArea
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="请输入您希望查询的内容例如今日A股市场动态、科技板块走势、央行货币政策等"
autoSize={{ minRows: 2, maxRows: 4 }}
disabled={loading}
style={{
fontSize: '15px'
}}
/>
</div>
{/* 操作按钮区 */}
<div className="text-center mb-8">
<Button
type="primary"
size="large"
icon={loading ? <Spin size="small" /> : <SearchOutlined />}
onClick={handleGenerate}
loading={loading}
disabled={loading || !query.trim()}
style={{
height: '48px',
fontSize: '16px',
paddingLeft: '24px',
paddingRight: '24px'
}}
>
{loading ? '正在获取新闻要点...' : '获取新闻要点'}
</Button>
</div>
{/* 内容展示区 */}
<Card
style={{
minHeight: '400px',
maxHeight: '70vh',
overflowY: 'auto'
}}
bodyStyle={{
padding: '24px'
}}
>
{loading && !content ? (
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '200px'
}}
>
<Space direction="vertical" align="center">
<Spin size="large" />
<Paragraph type="secondary">
...
</Paragraph>
</Space>
</div>
) : content ? (
<div className="prose prose-sm max-w-none">
<ReactMarkdown
components={{
h2: ({ children }) => (
<h2
style={{
color: '#1890ff',
borderBottom: '2px solid #1890ff',
paddingBottom: '8px',
marginTop: '24px',
marginBottom: '16px'
}}
>
{children}
</h2>
),
h3: ({ children }) => (
<h3
style={{
color: '#333',
marginTop: '20px',
marginBottom: '12px'
}}
>
{children}
</h3>
),
p: ({ children }) => (
<p
style={{
lineHeight: '1.8',
marginBottom: '12px',
color: '#444'
}}
>
{children}
</p>
),
ul: ({ children }) => (
<ul
style={{
marginBottom: '16px',
paddingLeft: '20px'
}}
>
{children}
</ul>
),
li: ({ children }) => (
<li
style={{
lineHeight: '1.8',
marginBottom: '8px'
}}
>
{children}
</li>
),
strong: ({ children }) => (
<strong
style={{
color: '#1890ff',
fontWeight: 'bold'
}}
>
{children}
</strong>
),
em: ({ children }) => (
<em
style={{
color: '#666'
}}
>
{children}
</em>
)
}}
>
{content}
</ReactMarkdown>
{isComplete && (
<div
style={{
marginTop: '24px',
paddingTop: '16px',
borderTop: '1px solid #f0f0f0',
textAlign: 'center',
color: '#999',
fontSize: '14px'
}}
>
</div>
)}
</div>
) : (
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '200px',
color: '#999'
}}
>
</div>
)}
</Card>
</div>
</div>
)
}
export default FinancialNewsPage

View File

@@ -5,6 +5,12 @@ import { RobotOutlined, TranslationOutlined, FileTextOutlined } from '@ant-desig
const { Title, Paragraph } = Typography
const cards = [
{
title: '财经新闻要点获取',
description: 'AI 驱动的财经新闻聚合工具',
icon: <FileTextOutlined style={{ fontSize: 32, color: '#fa8c16' }} />,
link: '/financial-news'
},
{
title: '测试页面 1',
description: 'Dify AI Agent 集成示例',

View File

@@ -44,6 +44,10 @@ const router: RouteObject[] = [
path: '/zh-en-translator',
element: LazyLoad(lazy(() => import('@/pages/zh-en-translator')))
},
{
path: '/financial-news',
element: LazyLoad(lazy(() => import('@/pages/financial-news')))
},
{
path: '/404',
element: <>404</>