trading.ai/templates/dashboard_table.html
2025-08-17 21:27:42 +08:00

597 lines
18 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>AI选币系统 - 表格视图</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: #f8fafc;
color: #1e293b;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
}
.navbar {
background: white;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
padding: 1rem 0;
position: sticky;
top: 0;
z-index: 100;
}
.nav-content {
max-width: 1400px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 2rem;
}
.logo {
font-size: 1.5rem;
font-weight: 700;
color: #3b82f6;
display: flex;
align-items: center;
gap: 8px;
}
.refresh-btn {
background: #3b82f6;
color: white;
border: none;
padding: 10px 20px;
border-radius: 6px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s;
}
.refresh-btn:hover {
background: #2563eb;
}
.main-container {
max-width: 1400px;
margin: 0 auto;
padding: 2rem;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.stat-card {
background: white;
border-radius: 8px;
padding: 1.5rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
text-align: center;
}
.stat-icon {
font-size: 2rem;
color: #3b82f6;
margin-bottom: 0.5rem;
}
.stat-value {
font-size: 2rem;
font-weight: 700;
color: #1e293b;
margin-bottom: 0.25rem;
}
.stat-label {
color: #64748b;
font-size: 0.875rem;
}
.section-title {
font-size: 1.5rem;
font-weight: 600;
color: #1e293b;
margin-bottom: 1.5rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.time-group {
margin-bottom: 2rem;
}
.group-header {
background: #3b82f6;
color: white;
padding: 1rem;
border-radius: 8px 8px 0 0;
font-weight: 600;
font-size: 1.1rem;
}
.timeframe-subgroup {
margin-bottom: 1rem;
}
.subgroup-header {
background: #64748b;
color: white;
padding: 0.75rem 1rem;
font-weight: 500;
font-size: 0.95rem;
border-left: 4px solid #3b82f6;
}
.subgroup-header.timeframe-15m {
background: #ef4444;
border-left-color: #dc2626;
}
.subgroup-header.timeframe-1h {
background: #f59e0b;
border-left-color: #d97706;
}
.subgroup-header.timeframe-4h {
background: #10b981;
border-left-color: #059669;
}
.subgroup-header.timeframe-1d {
background: #8b5cf6;
border-left-color: #7c3aed;
}
.timeframe-subgroup:first-child .subgroup-header {
border-radius: 0;
}
.timeframe-subgroup:last-child .coins-table {
border-radius: 0 0 8px 8px;
}
.coins-table {
width: 100%;
background: white;
border-radius: 0 0 8px 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
overflow: hidden;
border: 1px solid #e2e8f0;
border-top: none;
}
.coins-table table {
width: 100%;
border-collapse: collapse;
font-size: 0.9rem;
}
.coins-table th,
.coins-table td {
padding: 0.75rem;
text-align: left;
border-bottom: 1px solid #f1f5f9;
vertical-align: middle;
}
.coins-table th {
background: #f8fafc;
font-weight: 600;
color: #475569;
border-bottom: 2px solid #e2e8f0;
}
.coins-table tbody tr:hover {
background: #f8fafc;
}
.coin-symbol-cell {
font-weight: 600;
color: #1e293b;
min-width: 80px;
}
.factors-cell {
text-align: center;
min-width: 80px;
}
.factors-badge {
padding: 4px 8px;
border-radius: 4px;
font-size: 0.8rem;
font-weight: 600;
display: inline-block;
color: white;
}
.factors-6 {
background: #7c3aed;
}
.factors-5 {
background: #10b981;
}
.factors-4 {
background: #10b981;
}
.factors-3 {
background: #3b82f6;
}
.timeframe-cell {
text-align: center;
min-width: 60px;
}
.timeframe-badge {
padding: 4px 8px;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 500;
background: #f1f5f9;
color: #475569;
border: 1px solid #e2e8f0;
}
.signal-type-cell {
text-align: center;
min-width: 60px;
}
.signal-long {
background: #dcfce7;
color: #15803d;
padding: 4px 8px;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 500;
}
.signal-short {
background: #fee2e2;
color: #b91c1c;
padding: 4px 8px;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 500;
}
.price-cell {
text-align: right;
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
min-width: 90px;
font-size: 0.85rem;
}
.price-entry {
font-weight: 600;
color: #1e293b;
}
.price-stop {
color: #ef4444;
}
.price-profit {
color: #10b981;
}
.strategy-cell {
text-align: center;
min-width: 80px;
}
.strategy-badge {
padding: 4px 8px;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 500;
color: white;
background: #64748b; /* 默认背景色 */
}
/* 使用JavaScript动态设置颜色这里先设置默认样式 */
.action-cell {
max-width: 200px;
font-size: 0.8rem;
line-height: 1.3;
}
.action-wait {
color: #d97706;
}
.action-ready {
color: #059669;
font-weight: 500;
}
.reason-cell {
max-width: 280px;
font-size: 0.8rem;
color: #64748b;
line-height: 1.3;
}
.time-cell {
font-size: 0.75rem;
color: #64748b;
min-width: 70px;
}
.no-data {
text-align: center;
padding: 3rem;
color: #64748b;
}
.no-data i {
font-size: 3rem;
margin-bottom: 1rem;
color: #cbd5e1;
}
.load-more-container {
text-align: center;
padding: 2rem;
}
.load-more-btn {
background: #3b82f6;
color: white;
border: none;
padding: 12px 24px;
border-radius: 6px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s;
}
.load-more-btn:hover {
background: #2563eb;
}
@media (max-width: 768px) {
.main-container {
padding: 1rem;
}
.coins-table {
font-size: 0.8rem;
}
.coins-table th,
.coins-table td {
padding: 0.5rem;
}
.reason-cell {
max-width: 150px;
}
.action-cell {
max-width: 120px;
}
}
</style>
</head>
<body>
<nav class="navbar">
<div class="nav-content">
<div class="logo">
<i class="fas fa-chart-line"></i>
AI选币系统
</div>
<button class="refresh-btn" onclick="refreshData()" id="refresh-btn">
<i class="fas fa-sync-alt"></i>
刷新数据
</button>
</div>
</nav>
<div class="main-container">
<!-- 统计信息 -->
<div class="stats-grid">
<div class="stat-card">
<div class="stat-icon"><i class="fas fa-coins"></i></div>
<div class="stat-value">{{ total_signals }}</div>
<div class="stat-label">总信号数</div>
</div>
<div class="stat-card">
<div class="stat-icon"><i class="fas fa-arrow-up" style="color: #10b981;"></i></div>
<div class="stat-value">{{ long_signals }}</div>
<div class="stat-label">多头信号</div>
</div>
<div class="stat-card">
<div class="stat-icon"><i class="fas fa-arrow-down" style="color: #ef4444;"></i></div>
<div class="stat-value">{{ short_signals }}</div>
<div class="stat-label">空头信号</div>
</div>
<div class="stat-card">
<div class="stat-icon"><i class="fas fa-clock"></i></div>
<div class="stat-value">{{ update_time }}</div>
<div class="stat-label">更新时间</div>
</div>
</div>
<!-- 选币结果表格 -->
<div id="selections-container">
{% if grouped_selections %}
{% for group_time, timeframe_groups in grouped_selections.items() %}
<div class="time-group">
<div class="group-header">
<i class="fas fa-calendar-alt"></i>
{{ group_time }}
{% set total_coins = timeframe_groups.values() | map('length') | sum %}
({{ total_coins }}个币种)
</div>
<!-- 时间级别分组 -->
{% for timeframe, selections in timeframe_groups.items() %}
<div class="timeframe-subgroup">
<div class="subgroup-header timeframe-{{ timeframe }}">
<i class="fas fa-clock"></i>
{{ timeframe }} 时间级别 ({{ selections|length }}个信号)
</div>
<div class="coins-table">
<table>
<thead>
<tr>
<th>币种</th>
<th>符合因子</th>
<th>信号类型</th>
<th>策略</th>
<th>入场价</th>
<th>止损价</th>
<th>止盈价</th>
<th>操作建议</th>
<th>选择理由</th>
<th>时间</th>
</tr>
</thead>
<tbody>
{% for selection in selections %}
<tr>
<td class="coin-symbol-cell">
{{ selection.symbol.replace('USDT', '') }}
</td>
<td class="factors-cell">
<span class="factors-badge factors-{{ selection.qualified_factors if selection.qualified_factors is not none else selection.score|round|int }}">
{{ selection.qualified_factors if selection.qualified_factors is not none else selection.score|round|int }}/6
</span>
</td>
<td class="signal-type-cell">
<span class="signal-{{ 'short' if selection.signal_type == 'SHORT' else 'long' }}">
{{ '做空' if selection.signal_type == 'SHORT' else '做多' }}
</span>
</td>
<td class="strategy-cell">
<span class="strategy-badge">
{{ selection.strategy_type }}
</span>
</td>
<td class="price-cell price-entry">
${{ "%.4f"|format(selection.entry_price) }}
</td>
<td class="price-cell price-stop">
${{ "%.4f"|format(selection.stop_loss) }}
</td>
<td class="price-cell price-profit">
${{ "%.4f"|format(selection.take_profit) }}
</td>
<td class="action-cell">
<span class="{{ 'action-ready' if '分批' in selection.action_suggestion else 'action-wait' }}">
{{ selection.action_suggestion }}
</span>
</td>
<td class="reason-cell">
{{ selection.reason }}
</td>
<td class="time-cell">
{{ selection.selection_time.split(' ')[1][:5] if selection.selection_time else '-' }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endfor %}
</div>
{% endfor %}
<!-- 加载更多按钮 -->
{% if total_count >= current_limit %}
<div class="load-more-container">
<button class="load-more-btn" onclick="loadMoreData()">
<i class="fas fa-arrow-down"></i>
加载更多
</button>
</div>
{% endif %}
{% else %}
<div class="no-data">
<div><i class="fas fa-chart-line"></i></div>
<h3>暂无选币数据</h3>
<p>请稍后刷新或运行选币程序</p>
</div>
{% endif %}
</div>
</div>
<script>
// 设置策略标签颜色
function setStrategyColors() {
document.querySelectorAll('.strategy-badge').forEach(badge => {
const text = badge.textContent.trim();
if (text.includes('极品信号')) {
badge.style.background = '#7c3aed'; // 紫色
} else if (text.includes('优质信号')) {
badge.style.background = '#10b981'; // 绿色
} else if (text.includes('标准信号')) {
badge.style.background = '#3b82f6'; // 蓝色
} else if (text.includes('基础信号')) {
badge.style.background = '#64748b'; // 灰色
}
});
}
// 页面加载完成后设置颜色
document.addEventListener('DOMContentLoaded', setStrategyColors);
async function refreshData() {
const btn = document.getElementById('refresh-btn');
const originalText = btn.innerHTML;
btn.disabled = true;
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 刷新中...';
try {
const response = await fetch('/api/refresh');
const result = await response.json();
if (result.success) {
location.reload();
} else {
alert('刷新失败: ' + result.message);
}
} catch (error) {
alert('刷新失败: ' + error.message);
} finally {
btn.disabled = false;
btn.innerHTML = originalText;
}
}
async function loadMoreData() {
// 实现加载更多逻辑
console.log('加载更多数据');
}
// 自动刷新
setInterval(() => {
const now = new Date();
if (now.getMinutes() % 30 === 0 && now.getSeconds() < 10) {
refreshData();
}
}, 5000);
</script>
</body>
</html>