stock-ai-agent/frontend/index.html
2026-02-10 00:04:22 +08:00

212 lines
11 KiB
HTML
Raw Permalink 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.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TradusAI 金融智能体</title>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<!-- Marked.js for Markdown rendering -->
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<!-- Styles -->
<link rel="stylesheet" href="/static/css/style.css?v=3">
</head>
<body>
<div id="app">
<!-- Main Container -->
<div class="container">
<!-- Header -->
<header class="header">
<div class="logo">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z" fill="currentColor"/>
</svg>
<span>TradusAI金融智能体</span>
</div>
<div class="header-right">
<!-- Model Display (只显示,不可切换) -->
<div class="model-selector" v-if="currentModel">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="3"/>
<path d="M12 1v6m0 6v6M5.64 5.64l4.24 4.24m4.24 4.24l4.24 4.24M1 12h6m6 0h6M5.64 18.36l4.24-4.24m4.24-4.24l4.24-4.24"/>
</svg>
<span class="model-name">{{ currentModel.name }}</span>
</div>
<!-- Logout Button -->
<button class="logout-btn" @click="logout" title="登出">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/>
<polyline points="16 17 21 12 16 7"/>
<line x1="21" y1="12" x2="9" y2="12"/>
</svg>
</button>
</div>
</header>
<!-- Chat Area -->
<div class="chat-container" ref="chatContainer">
<!-- Welcome Screen -->
<div v-if="messages.length === 0" class="welcome">
<div class="welcome-icon">
<svg width="60" height="60" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
</svg>
</div>
<h1>AI 金融智能体</h1>
<p class="welcome-subtitle">支持 A股 · 美股 · 港股 三大市场分析</p>
<div class="guide-section">
<div class="example-queries">
<button class="example-btn" @click="sendExample('分析贵州茅台')">分析贵州茅台</button>
<button class="example-btn" @click="sendExample('比亚迪怎么样')">比亚迪怎么样</button>
<button class="example-btn" @click="sendExample('分析特斯拉')">分析特斯拉</button>
<button class="example-btn" @click="sendExample('苹果股票')">苹果股票</button>
<button class="example-btn" @click="sendExample('港股腾讯')">港股腾讯</button>
<button class="example-btn" @click="sendExample('分析小米')">分析小米</button>
</div>
</div>
<div class="welcome-footer">
<p>💬 输入股票名称或代码,支持 A股/美股/港股</p>
</div>
</div>
<!-- Messages -->
<div v-else class="messages">
<div v-for="(msg, index) in messages" :key="index"
:class="['message', msg.role]">
<div class="message-content">
<div v-if="msg.role === 'user'" class="text">{{ msg.content }}</div>
<div v-else>
<div class="text markdown" v-html="renderMarkdown(msg.content)"></div>
<!-- Action Buttons for AI Messages (只在流式输出完成后显示) -->
<div v-if="!msg.streaming" class="message-actions">
<button class="action-btn" @click.stop="copyMessage(msg.content)" title="复制内容">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
</svg>
<span>复制</span>
</button>
<button class="action-btn" @click.stop="generateShareImage(msg.content, index)" title="生成分享图">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
<circle cx="8.5" cy="8.5" r="1.5"/>
<polyline points="21 15 16 10 5 21"/>
</svg>
<span>分享图</span>
</button>
</div>
<!-- Streaming Indicator (流式输出中显示) -->
<div v-if="msg.streaming" class="streaming-indicator">
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
</div>
<!-- Chart Display -->
<div v-if="msg.metadata && msg.metadata.type === 'chart'" class="chart-box">
<div :id="'chart-' + index" class="chart"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Input Area -->
<div class="input-container">
<div class="input-wrapper">
<textarea
v-model="userInput"
@keydown.enter.exact.prevent="sendMessage"
placeholder="输入股票名称或代码A股/美股/港股)..."
rows="1"
:disabled="loading"
ref="textarea"
></textarea>
<button
class="send-btn"
@click="sendMessage"
:disabled="loading || !userInput.trim()"
>
<svg v-if="!loading" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="22" y1="2" x2="11" y2="13"/>
<polygon points="22 2 15 22 11 13 2 9 22 2"/>
</svg>
<div v-else class="spinner"></div>
</button>
</div>
<!-- Footer Info -->
<div class="footer-info">
<span class="contact-link" @click="showContactModal = true">联系作者</span>
</div>
</div>
</div>
<!-- Image Modal -->
<div v-if="showImageModal" class="image-modal" @click="closeImageModal">
<div class="image-modal-content" @click.stop>
<button class="modal-close-btn" @click="closeImageModal" title="关闭">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18"/>
<line x1="6" y1="6" x2="18" y2="18"/>
</svg>
</button>
<img :src="modalImageUrl" alt="分享图" class="modal-image">
<p class="modal-hint">长按图片可保存到相册</p>
</div>
</div>
<!-- Contact Modal -->
<div v-if="showContactModal" class="contact-modal" @click="showContactModal = false">
<div class="contact-modal-content" @click.stop>
<button class="modal-close-btn" @click="showContactModal = false" title="关闭">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18"/>
<line x1="6" y1="6" x2="18" y2="18"/>
</svg>
</button>
<h3 class="contact-title">联系作者</h3>
<div class="contact-info">
<div class="contact-item">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
</svg>
<span>微信号:<strong>aaronlzhou</strong></span>
</div>
<button class="copy-btn" @click="copyWechat">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
</svg>
复制微信号
</button>
</div>
<p class="contact-hint">欢迎交流讨论股票分析和AI技术</p>
</div>
</div>
</div>
<!-- Vue 3 -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<!-- Lightweight Charts -->
<script src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.production.js"></script>
<!-- html2canvas for generating share images -->
<script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
<!-- App Script -->
<script src="/static/js/app.js?v=4"></script>
</body>
</html>