stock-ai-agent/frontend/index.html
2026-02-03 22:48:45 +08:00

185 lines
9.4 KiB
HTML
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.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>龙哥的 AI 金融智能体</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>AI金融智能体</span>
</div>
<div class="header-right">
<!-- Model Selector -->
<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>
<select v-model="selectedModel" @change="switchModel" class="model-select">
<option v-for="model in availableModels" :key="model.provider" :value="model.provider">
{{ model.name }}
</option>
</select>
</div>
<div class="status">
<div class="status-dot"></div>
<span>在线</span>
</div>
</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('NVDA基本面')">NVDA基本面</button>
</div>
</div>
<div class="welcome-footer">
<p>💬 输入股票名称或代码开始分析</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="输入股票名称或代码..."
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>
<!-- Author Info -->
<div class="author-info">
<span class="author-label">联系龙哥</span>
<span class="author-divider">|</span>
<span class="author-contact" @click="copyWechat" title="点击复制微信号">微信aaronlzhou</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>
</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>