597 lines
18 KiB
HTML
597 lines
18 KiB
HTML
<!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> |