feat: 重构新的前端,增加手机端的设计样例

alphaFuthures
Lxy 1 week ago
parent 3ed0ce1613
commit d629ada5b6

@ -1,20 +1,25 @@
:root {
--bg-primary: #0d0f14;
--bg-secondary: #151820;
--bg-card: #1a1d28;
--bg-card-hover: #222633;
--border-color: #2a2d3a;
--text-primary: #e8eaed;
--text-secondary: #9aa0ab;
--text-muted: #6b7280;
--green: #22c55e;
--green-bg: rgba(34, 197, 94, 0.15);
--green-border: rgba(34, 197, 94, 0.3);
--red: #ef4444;
--red-bg: rgba(239, 68, 68, 0.15);
--blue: #3b82f6;
--purple: #8b5cf6;
--orange: #f59e0b;
--bg-primary: #F5F5F7;
--bg-secondary: #FFFFFF;
--bg-card: #FFFFFF;
--bg-card-hover: #F8F8FA;
--border-color: #E5E5EA;
--text-primary: #1D1D1F;
--text-secondary: #6E6E73;
--text-muted: #8E8E93;
--apple-blue: #007AFF;
--apple-blue-hover: #0066D6;
--apple-blue-bg: rgba(0, 122, 255, 0.1);
--apple-green: #34C759;
--apple-green-bg: rgba(52, 199, 89, 0.1);
--apple-green-border: rgba(52, 199, 89, 0.3);
--apple-red: #FF3B30;
--apple-red-bg: rgba(255, 59, 48, 0.1);
--apple-orange: #FF9500;
--apple-purple: #AF52DE;
--card-shadow: 0 4px 12px rgba(0, 0, 0, 0.06);
--card-shadow-hover: 0 8px 24px rgba(0, 0, 0, 0.1);
--input-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}
* {
@ -24,10 +29,12 @@
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.app-container {
@ -40,9 +47,14 @@ body {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 24px;
background: var(--bg-secondary);
border-bottom: 1px solid var(--border-color);
padding: 16px 32px;
background: rgba(255, 255, 255, 0.72);
backdrop-filter: saturate(180%) blur(20px);
-webkit-backdrop-filter: saturate(180%) blur(20px);
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
position: sticky;
top: 0;
z-index: 100;
}
.nav-left {
@ -52,29 +64,30 @@ body {
}
.back-link {
color: var(--text-secondary);
color: var(--apple-blue);
text-decoration: none;
font-size: 14px;
font-size: 15px;
display: flex;
align-items: center;
gap: 6px;
transition: color 0.2s;
transition: opacity 0.2s;
}
.back-link:hover {
color: var(--text-primary);
opacity: 0.7;
}
.page-title {
display: flex;
align-items: center;
gap: 10px;
font-size: 16px;
font-size: 17px;
font-weight: 600;
color: var(--text-primary);
}
.page-title i {
color: var(--green);
.page-title svg {
color: var(--apple-blue);
}
.nav-right {
@ -84,22 +97,28 @@ body {
}
.nav-icon-btn {
color: var(--text-secondary);
color: var(--apple-blue);
text-decoration: none;
font-size: 16px;
padding: 6px;
border-radius: 6px;
padding: 8px;
border-radius: 10px;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
}
.nav-icon-btn:hover {
color: var(--text-primary);
background: var(--bg-card);
background: var(--apple-blue-bg);
}
.nav-icon-btn svg {
width: 18px;
height: 18px;
}
.main-content {
flex: 1;
padding: 24px;
padding: 32px 24px;
max-width: 900px;
margin: 0 auto;
width: 100%;
@ -108,22 +127,28 @@ body {
.config-container {
display: flex;
flex-direction: column;
gap: 20px;
gap: 24px;
}
.config-card {
background: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 20px;
border-radius: 20px;
padding: 24px;
box-shadow: var(--card-shadow);
transition: box-shadow 0.3s ease;
}
.config-card:hover {
box-shadow: var(--card-shadow-hover);
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
padding-bottom: 12px;
margin-bottom: 20px;
padding-bottom: 16px;
border-bottom: 1px solid var(--border-color);
}
@ -131,87 +156,99 @@ body {
display: flex;
align-items: center;
gap: 10px;
font-size: 16px;
font-size: 17px;
font-weight: 600;
color: var(--text-primary);
}
.card-header h3 i {
color: var(--green);
.card-header h3 svg {
color: var(--apple-blue);
}
/* 提供商网格 */
.provider-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
gap: 12px;
gap: 16px;
}
.provider-card {
padding: 16px;
padding: 20px 16px;
background: var(--bg-secondary);
border: 2px solid var(--border-color);
border-radius: 10px;
border-radius: 16px;
text-align: center;
cursor: pointer;
transition: all 0.2s;
transition: all 0.3s cubic-bezier(0.25, 0.1, 0.25, 1);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
.provider-card:hover {
border-color: var(--text-muted);
background: var(--bg-card-hover);
border-color: var(--apple-blue);
background: var(--apple-blue-bg);
box-shadow: 0 4px 16px rgba(0, 122, 255, 0.12);
transform: translateY(-2px);
}
.provider-card.active {
border-color: var(--green);
background: var(--green-bg);
border-color: var(--apple-blue);
background: var(--apple-blue-bg);
box-shadow: 0 4px 16px rgba(0, 122, 255, 0.15);
}
.provider-card i {
font-size: 28px;
margin-bottom: 8px;
.provider-card svg {
margin-bottom: 10px;
color: var(--text-secondary);
transition: color 0.3s;
}
.provider-card.active i {
color: var(--green);
.provider-card.active svg {
color: var(--apple-blue);
}
.provider-card .provider-name {
font-size: 13px;
font-size: 14px;
font-weight: 500;
color: var(--text-primary);
}
/* 表单 */
.form-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
gap: 20px;
}
.form-group {
display: flex;
flex-direction: column;
gap: 6px;
gap: 8px;
}
.form-group label {
font-size: 13px;
font-weight: 500;
color: var(--text-secondary);
}
.form-control {
padding: 10px 14px;
background: var(--bg-secondary);
padding: 12px 16px;
background: var(--bg-primary);
border: 1px solid var(--border-color);
border-radius: 8px;
border-radius: 12px;
color: var(--text-primary);
font-size: 14px;
font-size: 15px;
outline: none;
transition: border-color 0.2s;
transition: all 0.2s;
box-shadow: var(--input-shadow);
}
.form-control:focus {
border-color: var(--green);
border-color: var(--apple-blue);
box-shadow: 0 0 0 4px var(--apple-blue-bg);
background: var(--bg-secondary);
}
.form-control::placeholder {
color: var(--text-muted);
}
.input-with-toggle {
@ -222,95 +259,137 @@ body {
.input-with-toggle .form-control {
flex: 1;
padding-right: 40px;
padding-right: 44px;
}
.toggle-visibility {
position: absolute;
right: 10px;
right: 12px;
background: none;
border: none;
color: var(--text-muted);
cursor: pointer;
padding: 4px;
padding: 6px;
border-radius: 8px;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
}
.toggle-visibility:hover {
color: var(--apple-blue);
background: var(--apple-blue-bg);
}
.toggle-visibility svg {
width: 18px;
height: 18px;
}
.form-range {
width: 100%;
height: 6px;
border-radius: 3px;
background: var(--bg-secondary);
background: var(--border-color);
outline: none;
-webkit-appearance: none;
appearance: none;
}
.form-range::-webkit-slider-thumb {
-webkit-appearance: none;
width: 18px;
height: 18px;
appearance: none;
width: 22px;
height: 22px;
border-radius: 50%;
background: var(--green);
background: var(--apple-blue);
cursor: pointer;
box-shadow: 0 2px 8px rgba(0, 122, 255, 0.3);
transition: all 0.2s;
}
.form-range::-webkit-slider-thumb:hover {
transform: scale(1.1);
box-shadow: 0 4px 12px rgba(0, 122, 255, 0.4);
}
.form-range::-moz-range-thumb {
width: 22px;
height: 22px;
border-radius: 50%;
background: var(--apple-blue);
cursor: pointer;
border: none;
box-shadow: 0 2px 8px rgba(0, 122, 255, 0.3);
}
.range-labels {
display: flex;
justify-content: space-between;
font-size: 11px;
font-size: 12px;
color: var(--text-muted);
margin-top: 4px;
}
.form-actions {
display: flex;
align-items: center;
gap: 12px;
margin-top: 16px;
margin-top: 20px;
}
.test-result {
font-size: 13px;
font-size: 14px;
font-weight: 500;
}
.test-result.success {
color: var(--green);
color: var(--apple-green);
}
.test-result.error {
color: var(--red);
color: var(--apple-red);
}
/* 当前启用的AI卡片 */
.active-ai-card {
border-color: var(--green-border);
background: var(--green-bg);
border-color: var(--apple-green-border);
background: linear-gradient(135deg, var(--apple-green-bg) 0%, var(--bg-secondary) 100%);
}
.active-ai-card .card-header h3 {
color: var(--green);
color: var(--apple-green);
}
.active-ai-card .card-header i {
color: var(--green);
.active-ai-card .card-header svg {
color: var(--apple-green);
}
.active-ai-display {
display: flex;
align-items: center;
gap: 16px;
padding: 12px;
padding: 16px;
background: var(--bg-secondary);
border-radius: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
.active-ai-icon {
width: 48px;
height: 48px;
width: 52px;
height: 52px;
display: flex;
align-items: center;
justify-content: center;
background: rgba(34, 197, 94, 0.15);
border: 1px solid var(--green-border);
border-radius: 12px;
color: var(--green);
font-size: 24px;
background: var(--apple-blue-bg);
border: 1px solid rgba(0, 122, 255, 0.2);
border-radius: 14px;
color: var(--apple-blue);
}
.active-ai-icon svg {
width: 26px;
height: 26px;
}
.active-ai-info {
@ -320,28 +399,27 @@ body {
}
.active-ai-name {
font-size: 16px;
font-size: 17px;
font-weight: 600;
color: var(--text-primary);
}
.active-ai-model {
font-size: 13px;
font-size: 14px;
color: var(--text-secondary);
}
/* 设置列表 */
.settings-list {
display: flex;
flex-direction: column;
gap: 16px;
gap: 0;
}
.setting-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 0;
padding: 16px 0;
border-bottom: 1px solid var(--border-color);
}
@ -352,24 +430,24 @@ body {
.setting-info {
display: flex;
flex-direction: column;
gap: 2px;
gap: 4px;
}
.setting-name {
font-size: 14px;
font-size: 15px;
font-weight: 500;
color: var(--text-primary);
}
.setting-desc {
font-size: 12px;
font-size: 13px;
color: var(--text-muted);
}
/* 开关 */
.switch {
position: relative;
width: 48px;
height: 26px;
width: 51px;
height: 31px;
}
.switch input {
@ -385,49 +463,52 @@ body {
left: 0;
right: 0;
bottom: 0;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 26px;
transition: 0.3s;
background: var(--border-color);
border-radius: 31px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.slider:before {
position: absolute;
content: "";
height: 20px;
width: 20px;
height: 27px;
width: 27px;
left: 2px;
bottom: 2px;
background: var(--text-muted);
background: white;
border-radius: 50%;
transition: 0.3s;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
input:checked + .slider {
background: var(--green-bg);
border-color: var(--green-border);
background: var(--apple-green);
}
input:checked + .slider:before {
transform: translateX(22px);
background: var(--green);
transform: translateX(20px);
}
/* 模型列表 */
.models-list {
display: flex;
flex-direction: column;
gap: 10px;
gap: 12px;
}
.model-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
background: var(--bg-secondary);
padding: 16px;
background: var(--bg-primary);
border: 1px solid var(--border-color);
border-radius: 8px;
border-radius: 14px;
transition: all 0.2s;
}
.model-item:hover {
background: var(--bg-secondary);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
.model-info {
@ -443,8 +524,8 @@ input:checked + .slider:before {
}
.model-status.active {
background: var(--green);
box-shadow: 0 0 8px var(--green);
background: var(--apple-green);
box-shadow: 0 0 8px var(--apple-green);
}
.model-status.inactive {
@ -452,109 +533,181 @@ input:checked + .slider:before {
}
.model-name {
font-size: 14px;
font-size: 15px;
font-weight: 500;
color: var(--text-primary);
}
.model-provider {
font-size: 12px;
font-size: 13px;
color: var(--text-muted);
}
.model-actions {
display: flex;
gap: 8px;
align-items: center;
}
.model-actions button {
padding: 6px 12px;
font-size: 12px;
border-radius: 6px;
.model-actions button,
.btn-set-active,
.btn-delete {
padding: 8px 14px;
font-size: 13px;
font-weight: 500;
border-radius: 10px;
border: none;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
}
.btn-set-active {
background: var(--green-bg);
color: var(--green);
border: 1px solid var(--green-border);
background: var(--apple-blue-bg);
color: var(--apple-blue);
}
.btn-set-active:hover {
background: var(--green);
background: var(--apple-blue);
color: white;
}
.btn-delete {
background: var(--red-bg);
color: var(--red);
border: 1px solid var(--red-border);
background: var(--apple-red-bg);
color: var(--apple-red);
}
.btn-delete:hover {
background: var(--red);
background: var(--apple-red);
color: white;
}
/* 按钮 */
.btn-delete svg {
width: 14px;
height: 14px;
}
.active-badge {
font-size: 13px;
color: var(--apple-green);
font-weight: 500;
padding: 8px 14px;
background: var(--apple-green-bg);
border-radius: 10px;
}
.btn {
padding: 10px 20px;
border-radius: 8px;
font-size: 14px;
padding: 12px 24px;
border-radius: 12px;
font-size: 15px;
font-weight: 500;
cursor: pointer;
border: none;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
transition: all 0.2s;
-webkit-font-smoothing: antialiased;
}
.btn svg {
width: 16px;
height: 16px;
}
.btn-sm {
padding: 6px 12px;
font-size: 12px;
padding: 8px 14px;
font-size: 13px;
border-radius: 10px;
}
.btn-sm svg {
width: 14px;
height: 14px;
}
.btn-lg {
padding: 12px 28px;
font-size: 15px;
padding: 14px 32px;
font-size: 16px;
border-radius: 14px;
}
.btn-lg svg {
width: 18px;
height: 18px;
}
.btn-primary {
background: var(--green);
background: var(--apple-blue);
color: white;
box-shadow: 0 2px 8px rgba(0, 122, 255, 0.25);
}
.btn-primary:hover {
background: #16a34a;
background: var(--apple-blue-hover);
box-shadow: 0 4px 12px rgba(0, 122, 255, 0.35);
transform: translateY(-1px);
}
.btn-primary:active {
transform: translateY(0);
}
.btn-secondary {
background: var(--bg-secondary);
color: var(--text-primary);
background: var(--bg-primary);
color: var(--apple-blue);
border: 1px solid var(--border-color);
}
.btn-secondary:hover {
background: var(--bg-card-hover);
background: var(--apple-blue-bg);
border-color: var(--apple-blue);
}
.save-actions {
display: flex;
justify-content: center;
gap: 16px;
padding: 20px 0;
padding: 24px 0;
}
.empty-state {
text-align: center;
padding: 32px;
color: var(--text-muted);
font-size: 14px;
}
/* 响应式 */
@media (max-width: 768px) {
.top-nav {
padding: 14px 20px;
}
.main-content {
padding: 24px 16px;
}
.config-card {
padding: 20px;
border-radius: 16px;
}
.form-grid {
grid-template-columns: 1fr;
gap: 16px;
}
.provider-grid {
grid-template-columns: repeat(3, 1fr);
gap: 12px;
}
.provider-card {
padding: 16px 12px;
}
.save-actions {
@ -565,4 +718,29 @@ input:checked + .slider:before {
width: 100%;
justify-content: center;
}
.model-item {
flex-direction: column;
align-items: flex-start;
gap: 12px;
}
.model-actions {
width: 100%;
}
.model-actions button {
flex: 1;
}
}
@media (max-width: 480px) {
.provider-grid {
grid-template-columns: repeat(2, 1fr);
}
.active-ai-display {
flex-direction: column;
text-align: center;
}
}

@ -5,23 +5,32 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI模型配置 - 期货智析</title>
<link rel="stylesheet" href="/static/ai_config.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</head>
<body>
<div class="app-container">
<header class="top-nav">
<div class="nav-left">
<a href="/static/futures_analysis.html" class="back-link">
<i class="fas fa-arrow-left"></i> 返回期货智析
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 12L6 8L10 4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
返回期货智析
</a>
<div class="page-title">
<i class="fas fa-robot"></i>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="3" y="6" width="14" height="10" rx="2" stroke="currentColor" stroke-width="1.5"/>
<circle cx="8" cy="11" r="1.5" fill="currentColor"/>
<circle cx="12" cy="11" r="1.5" fill="currentColor"/>
<path d="M7 6V4C7 2.89543 7.89543 2 9 2H11C12.1046 2 13 2.89543 13 4V6" stroke="currentColor" stroke-width="1.5"/>
</svg>
<span>AI模型配置</span>
</div>
</div>
<div class="nav-right">
<a href="/" class="nav-icon-btn" title="返回首页">
<i class="fas fa-home"></i>
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 7.5L9 1.5L15 7.5V15C15 15.5523 14.5523 16 14 16H10V11H8V16H4C3.44772 16 3 15.5523 3 15V7.5Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</a>
</div>
</header>
@ -31,11 +40,22 @@
<!-- 当前启用的AI -->
<div class="config-card active-ai-card" id="active-ai-card" style="display:none;">
<div class="card-header">
<h3><i class="fas fa-check-circle"></i> 当前启用</h3>
<h3>
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="9" cy="9" r="8" stroke="currentColor" stroke-width="1.5"/>
<path d="M5.5 9L7.5 11L12.5 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
当前启用
</h3>
</div>
<div class="active-ai-display">
<div class="active-ai-icon" id="active-ai-icon">
<i class="fas fa-robot"></i>
<svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="4" y="8" width="18" height="12" rx="2" stroke="currentColor" stroke-width="1.5"/>
<circle cx="10" cy="14" r="1.5" fill="currentColor"/>
<circle cx="16" cy="14" r="1.5" fill="currentColor"/>
<path d="M9 8V6C9 4.89543 9.89543 4 11 4H15C16.1046 4 17 4.89543 17 6V8" stroke="currentColor" stroke-width="1.5"/>
</svg>
</div>
<div class="active-ai-info">
<div class="active-ai-name" id="active-ai-name">--</div>
@ -47,7 +67,12 @@
<!-- AI提供商选择 -->
<div class="config-card">
<div class="card-header">
<h3><i class="fas fa-cloud"></i> AI提供商</h3>
<h3>
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 12C2.34315 12 1 10.6569 1 9C1 7.51487 2.08333 6.28542 3.5 6.03687C4.10687 3.57091 6.33434 1.75 9 1.75C12.1756 1.75 14.75 4.32436 14.75 7.5C14.75 7.61229 14.7466 7.7238 14.7401 7.83428C16.0567 8.04571 17 9.20887 17 10.5C17 12.1569 15.6569 13.5 14 13.5H4Z" stroke="currentColor" stroke-width="1.5"/>
</svg>
AI提供商
</h3>
</div>
<div class="provider-grid" id="provider-grid">
<!-- 动态生成 -->
@ -57,7 +82,15 @@
<!-- API配置 -->
<div class="config-card">
<div class="card-header">
<h3><i class="fas fa-key"></i> API配置</h3>
<h3>
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="2" y="5" width="14" height="9" rx="2" stroke="currentColor" stroke-width="1.5"/>
<path d="M5 5V4C5 2.34315 6.34315 1 8 1H10C11.6569 1 13 2.34315 13 4V5" stroke="currentColor" stroke-width="1.5"/>
<circle cx="9" cy="9.5" r="1.5" fill="currentColor"/>
<path d="M9 11V12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>
API配置
</h3>
</div>
<div class="form-grid">
<div class="form-group">
@ -79,7 +112,10 @@
<div class="input-with-toggle">
<input type="password" id="api-key" class="form-control" placeholder="sk-...">
<button type="button" class="toggle-visibility" onclick="toggleApiKeyVisibility()">
<i class="fas fa-eye"></i>
<svg class="eye-icon" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 9C1 9 4 3 9 3C14 3 17 9 17 9C17 9 14 15 9 15C4 15 1 9 1 9Z" stroke="currentColor" stroke-width="1.5"/>
<circle cx="9" cy="9" r="2.5" stroke="currentColor" stroke-width="1.5"/>
</svg>
</button>
</div>
</div>
@ -102,7 +138,12 @@
</div>
<div class="form-actions">
<button class="btn btn-secondary" onclick="testConnection()">
<i class="fas fa-plug"></i> 测试连接
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 2L2 6L6 10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10 2L14 6L10 10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2 6H14" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>
测试连接
</button>
<span id="test-result" class="test-result"></span>
</div>
@ -111,7 +152,17 @@
<!-- 模型参数 -->
<div class="config-card">
<div class="card-header">
<h3><i class="fas fa-sliders-h"></i> 模型参数</h3>
<h3>
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 5H16" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M2 9H16" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M2 13H16" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<circle cx="6" cy="5" r="2" fill="currentColor"/>
<circle cx="12" cy="9" r="2" fill="currentColor"/>
<circle cx="8" cy="13" r="2" fill="currentColor"/>
</svg>
模型参数
</h3>
</div>
<div class="form-grid">
<div class="form-group">
@ -132,7 +183,13 @@
<!-- 分析设置 -->
<div class="config-card">
<div class="card-header">
<h3><i class="fas fa-cogs"></i> 分析设置</h3>
<h3>
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="7" cy="7" r="5" stroke="currentColor" stroke-width="1.5"/>
<path d="M10.5 10.5L15 15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>
分析设置
</h3>
</div>
<div class="settings-list">
<div class="setting-item">
@ -183,9 +240,21 @@
<!-- 已保存的模型 -->
<div class="config-card">
<div class="card-header">
<h3><i class="fas fa-database"></i> 已保存的模型</h3>
<h3>
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<ellipse cx="9" cy="4" rx="7" ry="2.5" stroke="currentColor" stroke-width="1.5"/>
<path d="M2 4V7C2 8.65685 5.13401 10 9 10C12.866 10 16 8.65685 16 7V4" stroke="currentColor" stroke-width="1.5"/>
<path d="M2 7V10C2 11.6569 5.13401 13 9 13C12.866 13 16 11.6569 16 10V7" stroke="currentColor" stroke-width="1.5"/>
<path d="M2 10V13C2 14.6569 5.13401 16 9 16C12.866 16 16 14.6569 16 13V10" stroke="currentColor" stroke-width="1.5"/>
</svg>
已保存的模型
</h3>
<button class="btn btn-primary btn-sm" onclick="addNewModel()">
<i class="fas fa-plus"></i> 添加模型
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 1V13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M1 7H13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>
添加模型
</button>
</div>
<div class="models-list" id="models-list">
@ -196,10 +265,19 @@
<!-- 保存按钮 -->
<div class="save-actions">
<button class="btn btn-primary btn-lg" onclick="saveConfig()">
<i class="fas fa-save"></i> 保存配置
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.5 16H3.5C2.67157 16 2 15.3284 2 14.5V3.5C2 2.67157 2.67157 2 3.5 2H11.5L16 6.5V14.5C16 15.3284 15.3284 16 14.5 16Z" stroke="currentColor" stroke-width="1.5"/>
<path d="M11 16V11H7V16" stroke="currentColor" stroke-width="1.5"/>
<path d="M2 2H11V7H2V2Z" stroke="currentColor" stroke-width="1.5"/>
</svg>
保存配置
</button>
<button class="btn btn-secondary btn-lg" onclick="loadConfig()">
<i class="fas fa-sync"></i> 重新加载
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 1V5H5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3.51 8.99C3.90192 10.279 4.64537 11.4272 5.65284 12.2985C6.6603 13.1698 7.88941 13.7278 9.19961 13.9085C10.5098 14.0892 11.8475 13.8851 13.0663 13.3185C14.2852 12.7518 15.3381 11.8446 16.1054 10.6995C16.8727 9.55439 17.3233 8.21691 17.4085 6.83162C17.4938 5.44634 17.2104 4.06538 16.5907 2.84419C15.9711 1.62299 15.0404 0.61021 13.8981 0.913653C12.7558 1.2171 11.8753 2.29247 11.5 3.5L1 5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
重新加载
</button>
</div>
</div>

@ -35,34 +35,91 @@ async function loadProviders() {
function getDefaultProviders() {
return [
{ id: 'openai', name: 'OpenAI', icon: 'fas fa-brain' },
{ id: 'anthropic', name: 'Claude', icon: 'fas fa-robot' },
{ id: 'google', name: 'Gemini', icon: 'fas fa-gem' },
{ id: 'aliyun', name: '通义千问', icon: 'fas fa-cloud' },
{ id: 'aliyun_coding', name: '通义灵码', icon: 'fas fa-code' },
{ id: 'bailian', name: '阿里百炼', icon: 'fas fa-flask' },
{ id: 'baidu', name: '文心一言', icon: 'fas fa-comments' },
{ id: 'zhipu', name: '智谱清言', icon: 'fas fa-lightbulb' }
{ id: 'openai', name: 'OpenAI', icon: 'brain' },
{ id: 'anthropic', name: 'Claude', icon: 'robot' },
{ id: 'google', name: 'Gemini', icon: 'gem' },
{ id: 'aliyun', name: '通义千问', icon: 'cloud' },
{ id: 'aliyun_coding', name: '通义灵码', icon: 'code' },
{ id: 'bailian', name: '阿里百炼', icon: 'flask' },
{ id: 'baidu', name: '文心一言', icon: 'comments' },
{ id: 'zhipu', name: '智谱清言', icon: 'lightbulb' }
];
}
function getProviderSVG(iconName) {
const svgMap = {
'brain': `<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 4C11.5 4 8 7.5 8 12C8 14.5 9.5 16.5 11 18V24C11 26 13 28 16 28C19 28 21 26 21 24V18C22.5 16.5 24 14.5 24 12C24 7.5 20.5 4 16 4Z" stroke="currentColor" stroke-width="2"/>
<path d="M13 28V30H19V28" stroke="currentColor" stroke-width="2"/>
<circle cx="13" cy="12" r="1.5" fill="currentColor"/>
<circle cx="19" cy="12" r="1.5" fill="currentColor"/>
</svg>`,
'robot': `<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="6" y="10" width="20" height="14" rx="3" stroke="currentColor" stroke-width="2"/>
<circle cx="13" cy="17" r="2" fill="currentColor"/>
<circle cx="19" cy="17" r="2" fill="currentColor"/>
<path d="M10 6V4C10 2.89543 10.8954 2 12 2H20C21.1046 2 22 2.89543 22 4V6" stroke="currentColor" stroke-width="2"/>
<path d="M12 24H20" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>`,
'gem': `<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 4L28 12L16 28L4 12L16 4Z" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
<path d="M4 12H28" stroke="currentColor" stroke-width="2"/>
<path d="M16 4L12 12L16 28L20 12L16 4Z" stroke="currentColor" stroke-width="2"/>
</svg>`,
'cloud': `<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 22C5.79086 22 4 20.2091 4 18C4 15.9584 5.53246 14.2884 7.5 14.0284C8.16974 10.6833 11.1509 8.16667 14.6667 8.16667C18.8756 8.16667 22.25 11.591 22.25 15.75C22.25 15.9028 22.2457 16.0549 22.2373 16.2059C24.0427 16.5125 25.4167 18.1191 25.4167 20.0833C25.4167 22.4167 23.5 24.5 21.25 24.5H8Z" stroke="currentColor" stroke-width="2"/>
</svg>`,
'code': `<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 10L4 16L10 22" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M22 10L28 16L22 22" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M18 6L14 26" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>`,
'flask': `<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 4H20V12L26 24C26.5 25 26 26 25 27C24 28 23 28 22 28H10C9 28 8 27 7 26C6 25 5.5 24 6 23L12 12V4Z" stroke="currentColor" stroke-width="2"/>
<path d="M10 4H22" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<path d="M8 22H24" stroke="currentColor" stroke-width="2"/>
</svg>`,
'comments': `<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 6C4 4.89543 4.89543 4 6 4H26C27.1046 4 28 4.89543 28 6V18C28 19.1046 27.1046 20 26 20H10L6 24V20C4.89543 20 4 19.1046 4 18V6Z" stroke="currentColor" stroke-width="2"/>
<path d="M10 10H22" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<path d="M10 14H18" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>`,
'lightbulb': `<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 4C10.4772 4 6 8.47715 6 14C6 17.5 8 20.5 11 22V25C11 26.1046 11.8954 27 13 27H19C20.1046 27 21 26.1046 21 25V22C24 20.5 26 17.5 26 14C26 8.47715 21.5228 4 16 4Z" stroke="currentColor" stroke-width="2"/>
<path d="M13 27V29H19V27" stroke="currentColor" stroke-width="2"/>
</svg>`,
'cog': `<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="16" cy="16" r="4" stroke="currentColor" stroke-width="2"/>
<path d="M16 4V8" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<path d="M16 24V28" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<path d="M4 16H8" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<path d="M24 16H28" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<path d="M7.05029 7.05029L9.87873 9.87873" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<path d="M22.1213 22.1213L24.9497 24.9497" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<path d="M7.05029 24.9497L9.87873 22.1213" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<path d="M22.1213 9.87873L24.9497 7.05029" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>`
};
return svgMap[iconName] || svgMap['cog'];
}
function renderProviders(providers) {
const grid = document.getElementById('provider-grid');
const iconMap = {
'openai': 'fas fa-brain',
'anthropic': 'fas fa-robot',
'google': 'fas fa-gem',
'aliyun': 'fas fa-cloud',
'aliyun_coding': 'fas fa-code',
'bailian': 'fas fa-flask',
'baidu': 'fas fa-comments',
'zhipu': 'fas fa-lightbulb',
'custom': 'fas fa-cog'
'openai': 'brain',
'anthropic': 'robot',
'google': 'gem',
'aliyun': 'cloud',
'aliyun_coding': 'code',
'bailian': 'flask',
'baidu': 'comments',
'zhipu': 'lightbulb',
'custom': 'cog'
};
grid.innerHTML = providers.map(p => `
<div class="provider-card ${p.id === selectedProvider ? 'active' : ''}" data-provider="${p.id}">
<i class="${iconMap[p.id] || 'fas fa-cog'}"></i>
${getProviderSVG(iconMap[p.id] || 'cog')}
<div class="provider-name">${p.name}</div>
</div>
`).join('');
@ -135,14 +192,12 @@ function populateForm(config) {
document.getElementById('temp-value').textContent = activeModel.temperature || 0.7;
document.getElementById('max-tokens').value = activeModel.max_tokens || 2000;
// 选中当前启用的提供商卡片
const providerCard = document.querySelector(`.provider-card[data-provider="${activeModel.provider}"]`);
if (providerCard) {
document.querySelectorAll('.provider-card').forEach(c => c.classList.remove('active'));
providerCard.classList.add('active');
}
// 更新模型下拉框
updateProviderModels();
}
@ -172,17 +227,16 @@ function renderModelsList(models) {
document.getElementById('active-ai-model').textContent = activeModel.model_name || activeModel.model_id || '--';
const iconMap = {
'openai': 'fab fa-openai',
'anthropic': 'fas fa-robot',
'google': 'fab fa-google',
'aliyun': 'fas fa-cloud',
'aliyun_coding': 'fas fa-code',
'bailian': 'fas fa-flask',
'baidu': 'fas fa-comments',
'zhipu': 'fas fa-lightbulb'
'openai': 'brain',
'anthropic': 'robot',
'google': 'gem',
'aliyun': 'cloud',
'aliyun_coding': 'code',
'bailian': 'flask',
'baidu': 'comments',
'zhipu': 'lightbulb'
};
const icon = document.getElementById('active-ai-icon').querySelector('i');
icon.className = iconMap[activeModel.provider] || 'fas fa-robot';
document.getElementById('active-ai-icon').innerHTML = getProviderSVG(iconMap[activeModel.provider] || 'robot');
} else {
activeCard.style.display = 'none';
}
@ -198,7 +252,13 @@ function renderModelsList(models) {
</div>
<div class="model-actions">
${!model.enabled ? `<button class="btn-set-active" onclick="setActiveModel(${index})">设为默认</button>` : '<span class="active-badge">当前启用</span>'}
<button class="btn-delete" onclick="deleteModel(${index})"><i class="fas fa-trash"></i></button>
<button class="btn-delete" onclick="deleteModel(${index})">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 3H12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M5 3V2C5 1.44772 5.44772 1 6 1H8C8.55228 1 9 1.44772 9 2V3" stroke="currentColor" stroke-width="1.5"/>
<path d="M3 3L3.5 12C3.5 12.5523 3.94772 13 4.5 13H9.5C10.0523 13 10.5 12.5523 10.5 12L11 3" stroke="currentColor" stroke-width="1.5"/>
</svg>
</button>
</div>
</div>
`).join('');
@ -220,13 +280,20 @@ function getProviderName(apiBase) {
function toggleApiKeyVisibility() {
const input = document.getElementById('api-key');
const icon = document.querySelector('.toggle-visibility i');
const svgIcon = document.querySelector('.toggle-visibility .eye-icon');
if (input.type === 'password') {
input.type = 'text';
icon.className = 'fas fa-eye-slash';
svgIcon.innerHTML = `
<path d="M3 3L15 15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M11.4141 6.41406C12.8406 7.51393 13.9453 8.93657 14.6605 10.5658C14.6957 10.6462 14.7012 10.7373 14.6761 10.8214C14.2061 12.3998 13.2893 13.8157 12.0285 14.921C10.7677 16.0263 9.20885 16.7814 7.53077 17.0963C5.85269 17.4113 4.11977 17.2734 2.50625 16.7004C1.69972 16.4166 0.95692 16.0016 0.3125 15.475" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M1 8.5C1 8.5 4 2.5 8.5 2.5C10.5 2.5 12.2667 3.5 13.6667 4.83333" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
`;
} else {
input.type = 'password';
icon.className = 'fas fa-eye';
svgIcon.innerHTML = `
<path d="M1 9C1 9 4 3 9 3C14 3 17 9 17 9C17 9 14 15 9 15C4 15 1 9 1 9Z" stroke="currentColor" stroke-width="1.5"/>
<circle cx="9" cy="9" r="2.5" stroke="currentColor" stroke-width="1.5"/>
`;
}
}

File diff suppressed because it is too large Load Diff

@ -5,231 +5,418 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>期货智析 - 智能期货期权分析系统</title>
<link rel="stylesheet" href="/static/futures_analysis.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--bg-page: #F5F5F7;
--bg-card: #FFFFFF;
--text-primary: #1D1D1F;
--text-secondary: #6E6E73;
--text-tertiary: #86868B;
--color-up: #FF3B30;
--color-down: #34C759;
--color-neutral: #FF9F0A;
--color-brand: #007AFF;
--color-ai: #AF52DE;
--shadow-sm: 0 1px 3px rgba(0,0,0,0.04);
--shadow-md: 0 4px 12px rgba(0,0,0,0.06);
--shadow-lg: 0 8px 24px rgba(0,0,0,0.08);
--radius-card: 20px;
--radius-btn: 12px;
--radius-pill: 9999px;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", "PingFang SC", sans-serif;
background-color: var(--bg-page);
color: var(--text-primary);
-webkit-font-smoothing: antialiased;
padding-bottom: 40px;
}
.header {
position: sticky;
top: 0;
z-index: 100;
height: 64px;
background: rgba(255, 255, 255, 0.8);
backdrop-filter: saturate(180%) blur(20px);
-webkit-backdrop-filter: saturate(180%) blur(20px);
border-bottom: 1px solid rgba(0,0,0,0.05);
display: flex;
align-items: center;
padding: 0 32px;
}
.logo { font-size: 18px; font-weight: 600; letter-spacing: -0.02em; margin-right: 40px; }
.nav-items { display: flex; gap: 24px; flex: 1; }
.nav-item { font-size: 14px; color: var(--text-secondary); cursor: pointer; transition: color 0.2s; text-decoration: none; }
.nav-item:hover { color: var(--text-primary); }
.nav-item.active { color: var(--color-brand); font-weight: 500; }
.live-badge { font-size: 11px; color: var(--color-down); display: flex; align-items: center; gap: 4px; }
.dot { width: 6px; height: 6px; background: var(--color-down); border-radius: 50%; }
.container { max-width: 1200px; margin: 0 auto; padding: 24px 32px; }
.toolbar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; flex-wrap: wrap; gap: 16px; }
.search-box {
background: #FFFFFF;
border: none;
border-radius: var(--radius-btn);
padding: 10px 16px;
font-size: 14px;
width: 300px;
box-shadow: var(--shadow-sm);
outline: none;
transition: box-shadow 0.2s;
}
.search-box:focus { box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.2); }
.pills { display: flex; gap: 8px; }
.pill {
padding: 6px 16px;
border-radius: var(--radius-pill);
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
background: #FFFFFF;
color: var(--text-secondary);
box-shadow: var(--shadow-sm);
border: none;
}
.pill:hover { background: #F5F5F7; }
.pill.active { background: var(--color-brand); color: #FFFFFF; box-shadow: 0 4px 10px rgba(0,122,255,0.3); }
.stats-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 32px; }
.stat-card {
background: var(--bg-card);
border-radius: 16px;
padding: 16px 20px;
display: flex;
align-items: center;
gap: 16px;
box-shadow: var(--shadow-sm);
transition: transform 0.2s, box-shadow 0.2s;
}
.stat-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
.stat-icon { width: 40px; height: 40px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 18px; }
.stat-icon.blue { background: rgba(0,122,255,0.1); color: var(--color-brand); }
.stat-icon.red { background: rgba(255,59,48,0.1); color: var(--color-up); }
.stat-icon.green { background: rgba(52,199,89,0.1); color: var(--color-down); }
.stat-icon.orange { background: rgba(255,159,10,0.1); color: var(--color-neutral); }
.stat-val { font-size: 24px; font-weight: 700; line-height: 1.2; }
.stat-label { font-size: 13px; color: var(--text-secondary); }
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 20px; }
.card {
background: var(--bg-card);
border-radius: var(--radius-card);
padding: 24px;
box-shadow: var(--shadow-md);
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
position: relative;
overflow: hidden;
cursor: pointer;
}
.card:hover { transform: translateY(-4px); box-shadow: var(--shadow-lg); }
.card-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 16px; }
.code-box { width: 36px; height: 36px; background: #F5F5F7; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 600; color: var(--text-tertiary); margin-bottom: 8px; }
.price-area { text-align: right; }
.price { font-size: 28px; font-weight: 700; letter-spacing: -0.02em; line-height: 1; }
.price.up { color: var(--color-up); }
.price.down { color: var(--color-down); }
.change { font-size: 13px; font-weight: 600; margin-top: 4px; }
.change.up { color: var(--color-up); }
.change.down { color: var(--color-down); }
.action-pill {
display: inline-block;
padding: 4px 12px;
border-radius: var(--radius-pill);
font-size: 12px;
font-weight: 600;
margin-bottom: 16px;
}
.action-pill.do-more { background: rgba(52,199,89,0.1); color: var(--color-down); }
.action-pill.watch { background: rgba(255,159,10,0.1); color: var(--color-neutral); }
.metrics { display: flex; gap: 24px; margin-bottom: 16px; }
.metric { flex: 1; }
.metric-label { font-size: 11px; color: var(--text-tertiary); margin-bottom: 4px; }
.metric-val { font-size: 14px; font-weight: 600; }
.bar-bg { height: 4px; background: #F5F5F7; border-radius: 2px; margin-top: 6px; overflow: hidden; }
.bar-fill { height: 100%; border-radius: 2px; }
.timeframes { display: flex; gap: 8px; margin-bottom: 16px; }
.tf { padding: 4px 10px; border-radius: 8px; font-size: 11px; color: var(--text-tertiary); background: #F5F5F7; cursor: pointer; }
.tf.active { background: #E8F2FF; color: var(--color-brand); font-weight: 500; }
.card-footer { display: flex; justify-content: space-between; align-items: center; border-top: 1px solid #F5F5F7; padding-top: 12px; font-size: 12px; }
.support-resist { color: var(--text-tertiary); }
.support-resist span { margin-right: 8px; }
.support-resist .red { color: var(--color-up); }
.support-resist .green { color: var(--color-down); }
.link { color: var(--color-brand); font-weight: 500; text-decoration: none; }
.link:hover { text-decoration: underline; }
/* 详情视图样式 */
.view { display: none; }
.view.active { display: block; }
.detail-actions { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; }
.back-btn, .refresh-btn {
padding: 8px 16px;
border-radius: var(--radius-btn);
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
background: #FFFFFF;
border: none;
box-shadow: var(--shadow-sm);
color: var(--text-secondary);
}
.back-btn:hover, .refresh-btn:hover { background: #F5F5F7; color: var(--text-primary); }
.detail-header {
background: var(--bg-card);
border-radius: var(--radius-card);
padding: 24px;
box-shadow: var(--shadow-md);
margin-bottom: 20px;
}
.header-left { display: flex; align-items: center; gap: 28px; }
.symbol-name { font-size: 28px; font-weight: 700; letter-spacing: -0.5px; }
.symbol-code { font-size: 14px; color: var(--text-tertiary); padding: 4px 12px; background: #F5F5F7; border-radius: 8px; }
.price-main { display: flex; flex-direction: column; align-items: flex-end; }
.detail-price { font-size: 36px; font-weight: 700; letter-spacing: -1px; }
.detail-price.up { color: var(--color-up); }
.detail-price.down { color: var(--color-down); }
.detail-change { font-size: 16px; font-weight: 600; }
.detail-change.up { color: var(--color-up); }
.detail-change.down { color: var(--color-down); }
.quote-grid { display: flex; gap: 32px; margin-top: 16px; }
.quote-item { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.quote-label { font-size: 12px; color: var(--text-tertiary); text-transform: uppercase; font-weight: 600; }
.quote-value { font-size: 16px; font-weight: 600; }
.period-bar { display: flex; align-items: center; gap: 16px; margin-bottom: 20px; }
.period-btns { display: flex; gap: 6px; background: #FFFFFF; padding: 4px; border-radius: 12px; box-shadow: var(--shadow-sm); }
.period-btn {
padding: 8px 20px;
border-radius: 8px;
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
background: transparent;
border: none;
color: var(--text-secondary);
}
.period-btn:hover { background: #F5F5F7; color: var(--text-primary); }
.period-btn.active { background: var(--color-brand); color: #FFFFFF; }
.detail-body { display: grid; grid-template-columns: 1fr 400px; gap: 20px; }
.chart-container, .history-container, .panel-card {
background: var(--bg-card);
border-radius: var(--radius-card);
padding: 24px;
box-shadow: var(--shadow-md);
}
.kline-chart { width: 100%; height: 480px; }
/* 模态框样式 */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.4);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
display: none;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-overlay.active { display: flex; }
.modal-content {
background: #FFFFFF;
border-radius: 24px;
width: 90%;
max-width: 640px;
max-height: 80vh;
overflow: hidden;
box-shadow: var(--shadow-lg);
}
.modal-large { max-width: 960px; }
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24px 28px;
border-bottom: 1px solid #F5F5F7;
}
.modal-header h3 { font-size: 18px; font-weight: 600; }
.modal-close {
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
background: none;
border: none;
cursor: pointer;
border-radius: 10px;
font-size: 18px;
color: var(--text-secondary);
}
.modal-close:hover { background: #F5F5F7; }
.modal-body { padding: 28px; overflow-y: auto; max-height: 70vh; }
/* Toast 样式 */
.toast-container { position: fixed; top: 88px; right: 32px; z-index: 2000; display: flex; flex-direction: column; gap: 12px; }
.toast {
display: flex;
align-items: center;
gap: 12px;
padding: 16px 22px;
background: #FFFFFF;
border-radius: 16px;
box-shadow: var(--shadow-lg);
min-width: 300px;
max-width: 420px;
}
.toast.success { border-left: 4px solid var(--color-down); }
.toast.error { border-left: 4px solid var(--color-up); }
.toast.warning { border-left: 4px solid var(--color-neutral); }
.toast.info { border-left: 4px solid var(--color-brand); }
.toast-title { font-size: 14px; font-weight: 600; }
.toast-message { font-size: 13px; color: var(--text-secondary); }
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@media (max-width: 1200px) { .detail-body { grid-template-columns: 1fr; } }
@media (max-width: 768px) {
.stats-row { grid-template-columns: repeat(2, 1fr); }
.grid { grid-template-columns: 1fr; }
.header { padding: 0 16px; }
.container { padding: 16px; }
}
</style>
</head>
<body class="theme-minimal">
<div class="bg-grid"></div>
<div class="bg-glow"></div>
<div class="app-container">
<!-- 顶部导航 -->
<header class="top-nav">
<div class="nav-left">
<div class="logo">
<div class="logo-icon">
<i class="fas fa-chart-line"></i>
<div class="logo-pulse"></div>
</div>
<div class="logo-text">
<span class="logo-title">期货智析</span>
<span class="logo-subtitle">FUTURES INTELLIGENCE</span>
</div>
</div>
</div>
<nav class="nav-center">
<a href="#" class="nav-item active" data-page="analysis">
<span class="nav-icon"><i class="fas fa-microchip"></i></span>
<span>品种分析</span>
</a>
<a href="#" class="nav-item" data-page="watched">
<span class="nav-icon"><i class="fas fa-star"></i></span>
<span>自选</span>
</a>
<a href="#" class="nav-item" data-page="market">
<span class="nav-icon"><i class="fas fa-globe"></i></span>
<span>市场概览</span>
</a>
<a href="#" class="nav-item" data-page="risk">
<span class="nav-icon"><i class="fas fa-shield-alt"></i></span>
<span>风险预警</span>
</a>
</nav>
<div class="nav-right">
<div class="system-status">
<span class="status-dot"></span>
<span class="status-text">LIVE</span>
</div>
<div class="datetime" id="current-time">--</div>
<a href="/ai-config" class="nav-btn" title="AI配置">
<i class="fas fa-brain"></i>
</a>
<button class="nav-btn theme-toggle" id="theme-toggle" title="切换主题">
<i class="fas fa-moon"></i>
</button>
<a href="/" class="nav-btn" title="返回首页">
<i class="fas fa-th-large"></i>
</a>
</div>
</header>
<body>
<header class="header">
<div class="logo">◈ 期货智析</div>
<div class="nav-items">
<a href="#" class="nav-item active" data-page="analysis">品种分析</a>
<a href="#" class="nav-item" data-page="watched">自选</a>
<a href="#" class="nav-item" data-page="market">市场概览</a>
<a href="#" class="nav-item" data-page="risk">风险预警</a>
</div>
<div class="live-badge">
<div class="dot"></div> LIVE
<span id="current-time" style="margin-left: 8px; font-size: 11px; color: var(--text-tertiary); font-variant-numeric: tabular-nums;"></span>
</div>
</header>
<div class="container">
<!-- Toast 提示容器 -->
<div class="toast-container" id="toast-container"></div>
<!-- 主内容区 -->
<main class="main-content">
<!-- 品种列表视图 -->
<div id="list-view" class="view active">
<!-- 搜索栏 -->
<div class="search-section">
<div class="search-box">
<i class="fas fa-search"></i>
<input type="text" id="search-input" placeholder="搜索品种名称或代码...">
<kbd>⌘K</kbd>
</div>
<div class="view-controls">
<button class="view-btn active" data-view="grid" title="网格视图">
<i class="fas fa-grid-2"></i>
<span>网格</span>
</button>
<button class="view-btn" data-view="list" title="列表视图">
<i class="fas fa-list"></i>
<span>列表</span>
</button>
</div>
<!-- 品种列表视图 -->
<div id="list-view" class="view active">
<!-- Toolbar -->
<div class="toolbar">
<input type="text" class="search-box" id="search-input" placeholder="🔍 搜索品种名称或代码...">
<div class="pills">
<button class="pill active" data-category="all">全部 <span id="count-all">32</span></button>
<button class="pill" data-category="watched">自选 <span id="count-watched">0</span></button>
<button class="pill" data-category="energy">能源</button>
<button class="pill" data-category="metal">金属</button>
<button class="pill" data-category="agriculture">农产品</button>
<button class="pill" data-category="finance">金融</button>
</div>
</div>
<!-- 筛选栏 -->
<div class="filter-bar">
<div class="filter-tabs">
<button class="filter-tab active" data-category="all">
<span>全部</span>
<span class="filter-count" id="count-all">0</span>
</button>
<button class="filter-tab" data-category="watched">
<i class="fas fa-star"></i>
<span>自选</span>
<span class="filter-count" id="count-watched">0</span>
</button>
<button class="filter-tab" data-category="energy">
<i class="fas fa-fire"></i>
<span>能源</span>
</button>
<button class="filter-tab" data-category="metal">
<i class="fas fa-cube"></i>
<span>金属</span>
</button>
<button class="filter-tab" data-category="agriculture">
<i class="fas fa-seedling"></i>
<span>农产品</span>
</button>
<button class="filter-tab" data-category="finance">
<i class="fas fa-chart-pie"></i>
<span>金融</span>
</button>
</div>
<div class="filter-actions">
<button class="refresh-all-btn" id="refresh-all-btn" title="刷新全部品种">
<i class="fas fa-sync-alt"></i>
<span>刷新全部</span>
</button>
<button class="ai-analyze-all-btn" id="ai-analyze-all-btn" title="AI分析全部品种">
<i class="fas fa-brain"></i>
<span>全部分析</span>
</button>
<div class="sort-select">
<select id="sort-select">
<option value="trend_score">趋势评分</option>
<option value="success_rate">成功率</option>
<option value="change_pct">涨跌幅</option>
<option value="name">名称</option>
</select>
<!-- Stats -->
<div class="stats-row">
<div class="stat-card" onclick="filterByTrend('all')" style="cursor: pointer;" title="显示全部">
<div class="stat-icon blue"></div>
<div><div class="stat-val" id="total-count">0</div><div class="stat-label">监控品种</div></div>
</div>
<div class="stat-card" onclick="filterByTrend('up')" style="cursor: pointer;" title="筛选上涨趋势">
<div class="stat-icon green"></div>
<div><div class="stat-val" id="up-count">0</div><div class="stat-label">上涨趋势</div></div>
</div>
<div class="stat-card" onclick="filterByTrend('down')" style="cursor: pointer;" title="筛选下跌趋势">
<div class="stat-icon red"></div>
<div><div class="stat-val" id="down-count">0</div><div class="stat-label">下跌趋势</div></div>
</div>
<!-- 统计概览 -->
<div class="stats-overview">
<div class="stat-card" onclick="filterByTrend('all')" style="cursor: pointer;" title="显示全部">
<div class="stat-icon"><i class="fas fa-layer-group"></i></div>
<div class="stat-info">
<span class="stat-value" id="total-count">0</span>
<span class="stat-label">监控品种</span>
</div>
</div>
<div class="stat-card up" onclick="filterByTrend('up')" style="cursor: pointer;" title="筛选上涨趋势">
<div class="stat-icon"><i class="fas fa-arrow-trend-up"></i></div>
<div class="stat-info">
<span class="stat-value" id="up-count">0</span>
<span class="stat-label">上涨趋势</span>
</div>
</div>
<div class="stat-card down" onclick="filterByTrend('down')" style="cursor: pointer;" title="筛选下跌趋势">
<div class="stat-icon"><i class="fas fa-arrow-trend-down"></i></div>
<div class="stat-info">
<span class="stat-value" id="down-count">0</span>
<span class="stat-label">下跌趋势</span>
</div>
</div>
<div class="stat-card neutral" onclick="filterByTrend('neutral')" style="cursor: pointer;" title="筛选震荡整理">
<div class="stat-icon"><i class="fas fa-arrows-left-right"></i></div>
<div class="stat-info">
<span class="stat-value" id="neutral-count">0</span>
<span class="stat-label">震荡整理</span>
</div>
</div>
<div class="stat-card" onclick="filterByTrend('neutral')" style="cursor: pointer;" title="筛选震荡整理">
<div class="stat-icon orange"></div>
<div><div class="stat-val" id="neutral-count">0</div><div class="stat-label">震荡整理</div></div>
</div>
</div>
<!-- 品种卡片网格 -->
<div id="futures-grid" class="futures-grid">
<!-- 动态生成 -->
</div>
<!-- Grid -->
<div id="futures-grid" class="grid">
<!-- 动态生成 -->
</div>
</div>
<!-- 详情分析视图 -->
<div id="detail-view" class="view">
<!-- 返回按钮 -->
<div class="detail-actions">
<button class="back-btn" id="back-btn">
<i class="fas fa-arrow-left"></i>
<span>返回</span>
</button>
<button class="refresh-btn" id="refresh-symbol-btn" title="刷新合约数据">
<i class="fas fa-sync-alt"></i>
<span>刷新数据</span>
</button>
<button class="back-btn" id="back-btn">← 返回</button>
<button class="refresh-btn" id="refresh-symbol-btn">刷新数据</button>
</div>
<!-- 品种标题区 -->
<div class="detail-header">
<div class="header-left">
<div class="symbol-info">
<div>
<span class="symbol-name" id="detail-name">--</span>
<span class="symbol-code" id="detail-symbol">--</span>
</div>
<div class="price-main">
<span class="price-value" id="detail-price">--</span>
<span class="price-change" id="detail-change">--</span>
<div class="price-levels">
<span class="level-tag resistance" id="detail-r1">R1: --</span>
<span class="level-tag support" id="detail-s1">S1: --</span>
</div>
<span class="detail-price" id="detail-price">--</span>
<span class="detail-change" id="detail-change">--</span>
</div>
</div>
<div class="header-right">
<div class="quote-grid">
<div class="quote-item">
<span class="quote-label">开盘</span>
<span class="quote-value" id="detail-open">--</span>
</div>
<div class="quote-item">
<span class="quote-label">最高</span>
<span class="quote-value up" id="detail-high">--</span>
</div>
<div class="quote-item">
<span class="quote-label">最低</span>
<span class="quote-value down" id="detail-low">--</span>
</div>
<div class="quote-item">
<span class="quote-label">成交量</span>
<span class="quote-value" id="detail-volume">--</span>
</div>
<div class="quote-grid">
<div class="quote-item">
<span class="quote-label">开盘</span>
<span class="quote-value" id="detail-open">--</span>
</div>
<div class="quote-item">
<span class="quote-label">最高</span>
<span class="quote-value" id="detail-high">--</span>
</div>
<div class="quote-item">
<span class="quote-label">最低</span>
<span class="quote-value" id="detail-low">--</span>
</div>
<div class="quote-item">
<span class="quote-label">成交量</span>
<span class="quote-value" id="detail-volume">--</span>
</div>
</div>
</div>
<!-- 周期选择 -->
<div class="period-bar">
<span class="period-label"><i class="fas fa-clock"></i> 周期</span>
<span style="font-size: 14px; color: var(--text-secondary); font-weight: 500;">周期</span>
<div class="period-btns">
<button class="period-btn" data-period="5">5M</button>
<button class="period-btn active" data-period="15">15M</button>
@ -238,145 +425,107 @@
</div>
</div>
<!-- 图表和侧边栏 -->
<div class="detail-body">
<div class="chart-section">
<!-- K线图表区 -->
<div class="chart-container">
<div class="chart-header">
<span class="chart-title">K线图</span>
<div class="chart-legend">
<span class="legend-item"><span class="legend-dot ma5"></span>MA5</span>
<span class="legend-item"><span class="legend-dot ma10"></span>MA10</span>
<span class="legend-item"><span class="legend-dot ma20"></span>MA20</span>
</div>
</div>
<div id="kline-chart" class="kline-chart"></div>
</div>
<!-- 历史分析记录 -->
<div class="history-container">
<div class="history-header">
<i class="fas fa-clock-rotate-left"></i>
<span>历史分析记录</span>
</div>
<div class="history-list" id="history-list">
<!-- 动态生成 -->
</div>
<div style="font-size: 17px; font-weight: 600; margin-bottom: 16px;">历史分析记录</div>
<div class="history-list" id="history-list"></div>
</div>
</div>
<!-- 右侧分析面板 -->
<div class="analysis-sidebar">
<!-- AI智能分析 -->
<div class="panel-card ai-analysis-card" id="ai-analysis-panel">
<div class="panel-header">
<i class="fas fa-brain"></i>
<span>AI 思维分析</span>
<div class="panel-header-actions">
<button class="ai-analyze-btn" id="ai-analyze-btn" onclick="runAIAnalysis()" title="执行AI分析">
<i class="fas fa-play"></i>
<span>智能分析</span>
</button>
</div>
<div class="panel-card" id="ai-analysis-panel">
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px;">
<span style="font-size: 15px; font-weight: 600;">AI 思维分析</span>
<button class="back-btn" id="ai-analyze-btn" onclick="runAIAnalysis()" style="padding: 6px 12px; font-size: 13px;">智能分析</button>
</div>
<div class="ai-analysis-content" id="ai-analysis-content">
<div class="ai-analysis-placeholder">
<i class="fas fa-brain"></i>
<div style="text-align: center; padding: 28px; color: var(--text-tertiary);">
<p>点击"智能分析"按钮获取AI分析结果</p>
</div>
</div>
</div>
<!-- 技术指标 -->
<div class="panel-card indicators-card">
<div class="panel-header">
<i class="fas fa-wave-pulse"></i>
<span>技术指标</span>
</div>
<div class="indicators-grid">
<div class="indicator-cell">
<span class="indicator-label">MACD</span>
<span class="indicator-value" id="macd-signal">--</span>
<span class="indicator-detail" id="macd-detail">--</span>
<div class="panel-card">
<div style="font-size: 15px; font-weight: 600; margin-bottom: 16px;">技术指标</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px;">
<div style="padding: 14px; background: #F5F5F7; border-radius: 12px;">
<div style="font-size: 12px; color: var(--text-tertiary); font-weight: 600;">MACD</div>
<div style="font-size: 16px; font-weight: 700;" id="macd-signal">--</div>
<div style="font-size: 12px; color: var(--text-secondary);" id="macd-detail">--</div>
</div>
<div class="indicator-cell">
<span class="indicator-label">RSI</span>
<span class="indicator-value" id="rsi-value">--</span>
<span class="indicator-detail" id="rsi-status">--</span>
<div style="padding: 14px; background: #F5F5F7; border-radius: 12px;">
<div style="font-size: 12px; color: var(--text-tertiary); font-weight: 600;">RSI</div>
<div style="font-size: 16px; font-weight: 700;" id="rsi-value">--</div>
<div style="font-size: 12px; color: var(--text-secondary);" id="rsi-status">--</div>
</div>
<div class="indicator-cell">
<span class="indicator-label">BOLL</span>
<span class="indicator-value" id="boll-signal">--</span>
<span class="indicator-detail" id="boll-detail">--</span>
<div style="padding: 14px; background: #F5F5F7; border-radius: 12px;">
<div style="font-size: 12px; color: var(--text-tertiary); font-weight: 600;">BOLL</div>
<div style="font-size: 16px; font-weight: 700;" id="boll-signal">--</div>
<div style="font-size: 12px; color: var(--text-secondary);" id="boll-detail">--</div>
</div>
<div class="indicator-cell">
<span class="indicator-label">KDJ</span>
<span class="indicator-value" id="kdj-signal">--</span>
<span class="indicator-detail" id="kdj-detail">--</span>
<div style="padding: 14px; background: #F5F5F7; border-radius: 12px;">
<div style="font-size: 12px; color: var(--text-tertiary); font-weight: 600;">KDJ</div>
<div style="font-size: 16px; font-weight: 700;" id="kdj-signal">--</div>
<div style="font-size: 12px; color: var(--text-secondary);" id="kdj-detail">--</div>
</div>
</div>
</div>
<!-- 关键点位 -->
<div class="panel-card levels-card">
<div class="panel-header">
<i class="fas fa-crosshairs"></i>
<span>关键点位</span>
</div>
<div class="levels-container">
<div class="level-group resistance">
<span class="level-group-label">压力</span>
<div class="level-item" id="resistance-1"><span>R1</span><span>--</span></div>
<div class="level-item" id="resistance-2"><span>R2</span><span>--</span></div>
<div class="panel-card">
<div style="font-size: 15px; font-weight: 600; margin-bottom: 16px;">关键点位</div>
<div style="display: flex; flex-direction: column; gap: 10px;">
<div style="font-size: 12px; font-weight: 600; color: var(--color-up); text-transform: uppercase;">压力</div>
<div style="display: flex; justify-content: space-between; padding: 8px 12px; background: #F5F5F7; border-radius: 10px; font-size: 13px;">
<span style="color: var(--text-secondary);">R1</span>
<span style="font-weight: 600;" id="resistance-1">--</span>
</div>
<div style="display: flex; justify-content: space-between; padding: 8px 12px; background: #F5F5F7; border-radius: 10px; font-size: 13px;">
<span style="color: var(--text-secondary);">R2</span>
<span style="font-weight: 600;" id="resistance-2">--</span>
</div>
<div style="height: 1px; background: #F5F5F7;"></div>
<div style="display: flex; justify-content: space-between; padding: 10px 14px; background: rgba(175,82,222,0.1); border-radius: 12px;">
<span style="color: var(--color-ai); font-weight: 600;">中枢 (PP)</span>
<span style="color: var(--color-ai); font-size: 16px; font-weight: 700;" id="pivot-point">--</span>
</div>
<div class="level-divider"></div>
<div class="level-item pivot-point" id="pivot-point">
<span>中枢 (PP)</span>
<span>--</span>
<div style="height: 1px; background: #F5F5F7;"></div>
<div style="font-size: 12px; font-weight: 600; color: var(--color-down); text-transform: uppercase;">支撑</div>
<div style="display: flex; justify-content: space-between; padding: 8px 12px; background: #F5F5F7; border-radius: 10px; font-size: 13px;">
<span style="color: var(--text-secondary);">S1</span>
<span style="font-weight: 600;" id="support-1">--</span>
</div>
<div class="level-divider"></div>
<div class="level-group support">
<span class="level-group-label">支撑</span>
<div class="level-item" id="support-1"><span>S1</span><span>--</span></div>
<div class="level-item" id="support-2"><span>S2</span><span>--</span></div>
<div style="display: flex; justify-content: space-between; padding: 8px 12px; background: #F5F5F7; border-radius: 10px; font-size: 13px;">
<span style="color: var(--text-secondary);">S2</span>
<span style="font-weight: 600;" id="support-2">--</span>
</div>
</div>
</div>
<!-- 多周期趋势 -->
<div class="panel-card trends-card">
<div class="panel-header">
<i class="fas fa-timeline"></i>
<span>多周期趋势</span>
</div>
<div class="trends-container" id="period-trends">
<!-- 动态生成 -->
</div>
<div class="panel-card">
<div style="font-size: 15px; font-weight: 600; margin-bottom: 16px;">多周期趋势</div>
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;" id="period-trends"></div>
</div>
<!-- 情景预案 -->
<div class="panel-card scenario-card" id="scenario-panel" style="display:none;">
<div class="panel-header">
<i class="fas fa-chess"></i>
<span>情景预案</span>
</div>
<div class="scenario-container" id="scenario-plans">
<!-- 动态生成 -->
</div>
<div class="panel-card" id="scenario-panel" style="display:none;">
<div style="font-size: 15px; font-weight: 600; margin-bottom: 16px;">情景预案</div>
<div style="display: flex; flex-direction: column; gap: 12px;" id="scenario-plans"></div>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
<!-- AI分析详情对话框 -->
<div class="modal-overlay" id="ai-analysis-modal">
<div class="modal-content modal-large">
<div class="modal-header">
<h3><i class="fas fa-brain"></i> AI 四维联合分析报告</h3>
<button class="modal-close" onclick="closeModal('ai-analysis-modal')"><i class="fas fa-xmark"></i></button>
<h3>AI 四维联合分析报告</h3>
<button class="modal-close" onclick="closeModal('ai-analysis-modal')"></button>
</div>
<div class="modal-body" id="ai-analysis-modal-body">
<!-- 动态生成 -->
@ -388,8 +537,8 @@
<div class="modal-overlay" id="suggestion-modal">
<div class="modal-content">
<div class="modal-header">
<h3><i class="fas fa-robot"></i> AI 交易建议详情</h3>
<button class="modal-close" onclick="closeModal('suggestion-modal')"><i class="fas fa-xmark"></i></button>
<h3>AI 交易建议详情</h3>
<button class="modal-close" onclick="closeModal('suggestion-modal')"></button>
</div>
<div class="modal-body" id="suggestion-modal-body">
<!-- 动态生成 -->
@ -401,8 +550,8 @@
<div class="modal-overlay" id="history-modal">
<div class="modal-content">
<div class="modal-header">
<h3><i class="fas fa-clock-rotate-left"></i> 分析记录详情</h3>
<button class="modal-close" onclick="closeModal('history-modal')"><i class="fas fa-xmark"></i></button>
<h3>分析记录详情</h3>
<button class="modal-close" onclick="closeModal('history-modal')"></button>
</div>
<div class="modal-body" id="history-modal-body">
<!-- 动态生成 -->

@ -60,28 +60,20 @@ function updateTime() {
}
function initEventListeners() {
document.getElementById('back-btn').addEventListener('click', showListView);
const backBtn = document.getElementById('back-btn');
if (backBtn) backBtn.addEventListener('click', showListView);
document.getElementById('theme-toggle').addEventListener('click', toggleTheme);
const themeToggle = document.getElementById('theme-toggle');
if (themeToggle) themeToggle.addEventListener('click', toggleTheme);
// AI交易建议卡片点击事件已隐藏暂时注释
// const suggestionCard = document.getElementById('suggestion-card');
// if (suggestionCard) {
// suggestionCard.addEventListener('click', function() {
// if (currentDetailData) {
// showSuggestionModal(currentDetailData);
// }
// });
// }
const refreshAllBtn = document.getElementById('refresh-all-btn');
if (refreshAllBtn) refreshAllBtn.addEventListener('click', refreshAllSymbols);
// 刷新全部按钮
document.getElementById('refresh-all-btn').addEventListener('click', refreshAllSymbols);
const aiAnalyzeAllBtn = document.getElementById('ai-analyze-all-btn');
if (aiAnalyzeAllBtn) aiAnalyzeAllBtn.addEventListener('click', analyzeAllSymbols);
// 全部AI分析按钮
document.getElementById('ai-analyze-all-btn').addEventListener('click', analyzeAllSymbols);
// 详情页刷新按钮
document.getElementById('refresh-symbol-btn').addEventListener('click', function() {
const refreshSymbolBtn = document.getElementById('refresh-symbol-btn');
if (refreshSymbolBtn) refreshSymbolBtn.addEventListener('click', function() {
if (currentSymbol) {
refreshSingleSymbol(currentSymbol);
}
@ -105,19 +97,23 @@ function initEventListeners() {
});
});
document.getElementById('search-input').addEventListener('input', function() {
const searchInput = document.getElementById('search-input');
if (searchInput) searchInput.addEventListener('input', function() {
filterFuturesList(this.value);
});
document.querySelectorAll('.filter-tab').forEach(tab => {
tab.addEventListener('click', function() {
document.querySelectorAll('.filter-tab').forEach(t => t.classList.remove('active'));
this.classList.add('active');
filterByCategory(this.dataset.category);
});
document.querySelectorAll('.pill').forEach(tab => {
if (tab.dataset.category) {
tab.addEventListener('click', function() {
document.querySelectorAll('.pill').forEach(t => t.classList.remove('active'));
this.classList.add('active');
filterByCategory(this.dataset.category);
});
}
});
document.getElementById('sort-select').addEventListener('change', function() {
const sortSelect = document.getElementById('sort-select');
if (sortSelect) sortSelect.addEventListener('change', function() {
sortFuturesList(this.value);
});
@ -157,10 +153,13 @@ async function showDetailView(symbol) {
}
async function loadHistoryListForAnalysis(symbol) {
console.log(`[AI分析] 开始加载 ${symbol} 的历史分析记录...`);
try {
const response = await fetch(`${API_BASE}/ai-analysis/${symbol}/history?limit=20`);
const data = await response.json();
console.log(`[AI分析] API响应:`, data);
if (data.success) {
renderHistoryList(data.data);
@ -168,13 +167,18 @@ async function loadHistoryListForAnalysis(symbol) {
const today = new Date();
const todayStr = today.toISOString().split('T')[0];
console.log(`[AI分析] 查找日期: ${todayStr}`);
console.log(`[AI分析] 历史记录数量: ${data.data?.length || 0}`);
let todayRecord = null;
if (data.data && data.data.length > 0) {
for (const record of data.data) {
const recordDate = new Date(record.analysis_time);
const recordDateStr = recordDate.toISOString().split('T')[0];
console.log(`[AI分析] 检查记录: ${record.analysis_time} -> ${recordDateStr}`);
if (recordDateStr === todayStr) {
todayRecord = record;
console.log(`[AI分析] ✓ 找到今天的记录!`);
break;
}
}
@ -182,7 +186,7 @@ async function loadHistoryListForAnalysis(symbol) {
// 4. 根据是否有今天的记录进行不同处理
if (todayRecord) {
console.log(`找到今天的分析记录: ${symbol} (${todayRecord.analysis_time})`);
console.log(`[AI分析] 显示AI分析结果...`);
currentAIAnalysis = {
id: todayRecord.id,
symbol: todayRecord.symbol,
@ -192,26 +196,33 @@ async function loadHistoryListForAnalysis(symbol) {
displayAIAnalysisResult(currentAIAnalysis);
syncAIToPanels(todayRecord.analysis_data);
} else {
console.log(`没有找到今天的分析记录: ${symbol}`);
console.log(`[AI分析] 没有找到今天的分析记录,显示占位符`);
showAIAnalysisPlaceholder();
}
} else {
console.log(`[AI分析] API返回失败显示占位符`);
showAIAnalysisPlaceholder();
}
} catch (error) {
console.error('加载历史记录失败:', error);
console.error('[AI分析] 加载历史记录失败:', error);
showAIAnalysisPlaceholder();
}
}
function showAIAnalysisPlaceholder() {
console.log('[AI分析] 显示占位符...');
const content = document.getElementById('ai-analysis-content');
console.log('[AI分析] ai-analysis-content元素:', content);
if (!content) {
console.error('[AI分析] 错误: 找不到ai-analysis-content元素!');
return;
}
content.innerHTML = `
<div class="ai-analysis-placeholder">
<i class="fas fa-brain"></i>
<div style="text-align: center; padding: 28px; color: var(--text-tertiary);">
<p>点击"智能分析"按钮获取AI分析结果</p>
</div>
`;
console.log('[AI分析] 占位符已显示');
}
async function loadWatchedSymbols() {
@ -462,64 +473,69 @@ function renderFuturesGrid(data) {
}
grid.innerHTML = data.map(item => {
const isWatched = watchedSymbols.includes(item.symbol);
const hasAI = item.hasAIAnalysis;
const code = item.symbol.replace(/[0-9]/g, '').substring(0, 2);
const changeIcon = item.change >= 0 ? '↑' : '↓';
const changeSign = item.change >= 0 ? '+' : '';
const pctSign = item.changePct >= 0 ? '+' : '';
let actionPillClass = 'watch';
let actionPillText = '观望';
if (item.suggestion?.includes('做多') || item.suggestion?.includes('试多')) {
actionPillClass = 'do-more';
actionPillText = '做多';
} else if (item.suggestion?.includes('做空') || item.suggestion?.includes('试空')) {
actionPillClass = 'do-more';
actionPillText = '做空';
}
const priceClass = item.change >= 0 ? 'up' : 'down';
const changeClass = item.change >= 0 ? 'up' : 'down';
const successRate = item.successRate || 0;
const trendScore = item.trendScore || 0;
const successColor = successRate >= 70 ? 'var(--color-down)' : successRate >= 60 ? 'var(--color-neutral)' : 'var(--color-up)';
const trendColor = trendScore >= 70 ? 'var(--color-down)' : trendScore >= 50 ? 'var(--color-neutral)' : 'var(--color-up)';
const periods = item.periods || {};
const resistance = item.resistance ? formatNumber(item.resistance) : '--';
const support = item.support ? formatNumber(item.support) : '--';
return `
<div class="futures-card ${!hasAI ? 'no-ai-data' : ''}" onclick="showDetailView('${item.symbol}')">
<div class="card-top">
<div class="card-symbol">
<div class="symbol-tag">${item.symbol.replace(/[0-9]/g, '').substring(0, 2)}</div>
<div>
<div class="card-name">${item.name}</div>
<div class="card-code">${item.symbol}</div>
</div>
<div class="card" onclick="showDetailView('${item.symbol}')">
<div class="card-header">
<div>
<div class="code-box">${code}</div>
<div style="font-weight:600;">${item.name}</div>
<div style="font-size:12px; color:var(--text-tertiary);">${item.symbol}</div>
</div>
<div class="card-price">
<div class="price-value ${item.change >= 0 ? 'up' : 'down'}">¥${formatNumber(item.price)}</div>
<div class="price-change ${item.change >= 0 ? 'up' : 'down'}">
<i class="fas fa-arrow-${item.change >= 0 ? 'up' : 'down'}"></i>
${item.change >= 0 ? '+' : ''}${formatNumber(item.change)} (${item.changePct >= 0 ? '+' : ''}${item.changePct.toFixed(2)}%)
</div>
<div class="price-area">
<div class="price ${priceClass}">¥${formatNumber(item.price)}</div>
<div class="change ${changeClass}">${changeIcon} ${changeSign}${formatNumber(item.change)} (${pctSign}${item.changePct.toFixed(2)}%)</div>
</div>
</div>
<span class="suggestion-badge ${hasAI ? item.suggestionType : 'neutral'}" id="suggestion-${item.symbol}">${hasAI ? item.suggestion : '--'}</span>
<div class="card-metrics">
<div class="metric-item">
<span class="metric-label">成功率</span>
<div class="metric-bar"><div class="metric-fill ${item.successRate >= 70 ? 'up' : item.successRate >= 60 ? 'orange' : 'down'}" style="width: ${item.successRate}%"></div></div>
<span class="metric-value ${item.successRate >= 70 ? 'up' : item.successRate >= 60 ? '' : 'down'}">${item.successRate}%</span>
<div class="action-pill ${actionPillClass}">${actionPillText}</div>
<div class="metrics">
<div class="metric">
<div class="metric-label">成功率 ${successRate}%</div>
<div class="bar-bg"><div class="bar-fill" style="width:${successRate}%; background:${successColor};"></div></div>
</div>
<div class="metric-item">
<span class="metric-label">趋势评分</span>
<div class="metric-bar"><div class="metric-fill ${item.trendScore >= 70 ? 'up' : item.trendScore >= 50 ? 'orange' : 'down'}" style="width: ${item.trendScore}%"></div></div>
<span class="metric-value ${item.trendScore >= 70 ? 'up' : item.trendScore >= 50 ? '' : 'down'}">${item.trendScore}</span>
<div class="metric">
<div class="metric-label">趋势评分 ${trendScore}</div>
<div class="bar-bg"><div class="bar-fill" style="width:${trendScore}%; background:${trendColor};"></div></div>
</div>
</div>
<div class="period-trends" id="period-trends-${item.symbol}">
<span class="period-tag ${hasAI ? (item.periods['5'] || 'neutral') : 'neutral'}" id="period-5-${item.symbol}">5M</span>
<span class="period-tag ${hasAI ? (item.periods['15'] || 'neutral') : 'neutral'}" id="period-15-${item.symbol}">15M</span>
<span class="period-tag ${hasAI ? (item.periods['30'] || 'neutral') : 'neutral'}" id="period-30-${item.symbol}">30M</span>
<span class="period-tag ${hasAI ? (item.periods['60'] || 'neutral') : 'neutral'}" id="period-60-${item.symbol}">1H</span>
<div class="timeframes">
<div class="tf ${periods['5'] === 'up' ? 'active' : ''}">5M</div>
<div class="tf ${periods['15'] === 'up' ? 'active' : ''}">15M</div>
<div class="tf ${periods['30'] === 'up' ? 'active' : ''}">30M</div>
<div class="tf ${periods['60'] === 'up' ? 'active' : ''}">1H</div>
</div>
${!hasAI ? `<div class="ai-hint"><i class="fas fa-info-circle"></i> 请先进行AI分析</div>` : ''}
<div class="card-footer">
<div class="key-levels">
<span><span class="label">压力</span> <span class="down" id="resistance-${item.symbol}">${hasAI ? formatNumber(item.resistance) : '--'}</span></span>
<span><span class="label">支撑</span> <span class="up" id="support-${item.symbol}">${hasAI ? formatNumber(item.support) : '--'}</span></span>
</div>
<div style="display:flex;align-items:center;gap:8px;">
<button class="card-refresh-btn" onclick="event.stopPropagation(); refreshSingleSymbol('${item.symbol}', this)" title="刷新数据">
<i class="fas fa-sync-alt"></i>
</button>
<button class="card-ai-btn" onclick="event.stopPropagation(); analyzeSingleSymbol('${item.symbol}', '${item.name}', this)" title="AI分析">
<i class="fas fa-brain"></i>
</button>
<button class="watch-btn ${isWatched ? 'active' : ''}" onclick="toggleWatch('${item.symbol}', '${item.name}', event)" title="${isWatched ? '取消自选' : '加入自选'}">
<i class="fas fa-star"></i>
</button>
<span class="detail-link"><i class="fas fa-arrow-right"></i> </span>
<div class="support-resist">
<span>压力 <b class="red">${resistance}</b></span>
<span>支撑 <b class="green">${support}</b></span>
</div>
<a href="#" class="link" onclick="event.stopPropagation(); showDetailView('${item.symbol}'); return false;">详情 </a>
</div>
</div>
`}).join('');
@ -552,12 +568,24 @@ function updateStats(data) {
d.suggestion?.includes('观望')
).length;
document.getElementById('total-count').textContent = total;
document.getElementById('up-count').textContent = upCount;
document.getElementById('down-count').textContent = downCount;
document.getElementById('neutral-count').textContent = neutralCount;
document.getElementById('count-all').textContent = total;
document.getElementById('count-watched').textContent = watchedSymbols.length;
// 安全检查确保元素存在再设置textContent
const totalCountEl = document.getElementById('total-count');
if (totalCountEl) totalCountEl.textContent = total;
const upCountEl = document.getElementById('up-count');
if (upCountEl) upCountEl.textContent = upCount;
const downCountEl = document.getElementById('down-count');
if (downCountEl) downCountEl.textContent = downCount;
const neutralCountEl = document.getElementById('neutral-count');
if (neutralCountEl) neutralCountEl.textContent = neutralCount;
const countAllEl = document.getElementById('count-all');
if (countAllEl) countAllEl.textContent = total;
const countWatchedEl = document.getElementById('count-watched');
if (countWatchedEl) countWatchedEl.textContent = watchedSymbols.length;
}
function filterByTrend(trend) {
@ -636,60 +664,70 @@ async function loadFuturesDetail(symbol) {
}
function updateDetailView(data) {
document.getElementById('detail-name').textContent = data.name || '--';
document.getElementById('detail-symbol').textContent = data.symbol || '--';
const nameEl = document.getElementById('detail-name');
if (nameEl) nameEl.textContent = data.name || '--';
const symbolEl = document.getElementById('detail-symbol');
if (symbolEl) symbolEl.textContent = data.symbol || '--';
const priceEl = document.getElementById('detail-price');
priceEl.textContent = '¥' + formatNumber(data.price);
priceEl.className = 'price-value ' + (data.change >= 0 ? 'up' : 'down');
if (priceEl) {
priceEl.textContent = '¥' + formatNumber(data.price);
priceEl.className = 'price-value ' + (data.change >= 0 ? 'up' : 'down');
}
const changeEl = document.getElementById('detail-change');
changeEl.className = 'price-change ' + (data.change >= 0 ? 'up' : 'down');
changeEl.innerHTML = `<i class="fas fa-arrow-${data.change >= 0 ? 'up' : 'down'}"></i> ${data.change >= 0 ? '+' : ''}${formatNumber(data.change)} (${data.changePct >= 0 ? '+' : ''}${data.changePct.toFixed(2)}%)`;
if (changeEl) {
changeEl.className = 'price-change ' + (data.change >= 0 ? 'up' : 'down');
changeEl.innerHTML = `${data.change >= 0 ? '↑' : '↓'} ${data.change >= 0 ? '+' : ''}${formatNumber(data.change)} (${data.changePct >= 0 ? '+' : ''}${data.changePct.toFixed(2)}%)`;
}
document.getElementById('detail-open').textContent = formatNumber(data.open);
document.getElementById('detail-high').textContent = formatNumber(data.high);
document.getElementById('detail-low').textContent = formatNumber(data.low);
document.getElementById('detail-volume').textContent = formatNumber(data.volume);
const openEl = document.getElementById('detail-open');
if (openEl) openEl.textContent = formatNumber(data.open);
const r1 = data.resistances ? data.resistances[0] : data.resistance;
const s1 = data.supports ? data.supports[0] : data.support;
const highEl = document.getElementById('detail-high');
if (highEl) highEl.textContent = formatNumber(data.high);
document.getElementById('detail-r1').textContent = `R1: ${formatNumber(r1)} (${calcPriceChangePercent(data.price, r1)})`;
document.getElementById('detail-s1').textContent = `S1: ${formatNumber(s1)} (${calcPriceChangePercent(data.price, s1)})`;
const lowEl = document.getElementById('detail-low');
if (lowEl) lowEl.textContent = formatNumber(data.low);
const badge = document.getElementById('suggestion-badge');
badge.textContent = data.suggestion || '--';
badge.className = 'suggestion-badge ' + (data.suggestionType || 'neutral');
const volumeEl = document.getElementById('detail-volume');
if (volumeEl) volumeEl.textContent = formatNumber(data.volume);
document.getElementById('suggestion-reason').textContent = data.suggestionReason || '--';
document.getElementById('entry-price').textContent = formatNumber(data.entryPrice);
document.getElementById('target-price').textContent = formatNumber(data.targetPrice);
document.getElementById('stop-loss').textContent = formatNumber(data.stopLoss);
document.getElementById('risk-level').textContent = data.riskLevel || '--';
// 注意: detail-r1 和 detail-s1 在新HTML中不存在已移除
// 注意: suggestion-badge, suggestion-reason, entry-price, target-price, stop-loss, risk-level 在新HTML中不存在已移除
if (data.macd) {
document.getElementById('macd-signal').textContent = data.macd.signal;
document.getElementById('macd-detail').textContent = data.macd.detail;
const macdSignalEl = document.getElementById('macd-signal');
if (macdSignalEl) macdSignalEl.textContent = data.macd.signal;
const macdDetailEl = document.getElementById('macd-detail');
if (macdDetailEl) macdDetailEl.textContent = data.macd.detail;
}
if (data.rsi) {
document.getElementById('rsi-value').textContent = data.rsi.value;
document.getElementById('rsi-status').textContent = data.rsi.status;
const rsiValueEl = document.getElementById('rsi-value');
if (rsiValueEl) rsiValueEl.textContent = data.rsi.value;
const rsiStatusEl = document.getElementById('rsi-status');
if (rsiStatusEl) rsiStatusEl.textContent = data.rsi.status;
}
if (data.boll) {
document.getElementById('boll-signal').textContent = data.boll.signal;
document.getElementById('boll-detail').textContent = data.boll.detail;
const bollSignalEl = document.getElementById('boll-signal');
if (bollSignalEl) bollSignalEl.textContent = data.boll.signal;
const bollDetailEl = document.getElementById('boll-detail');
if (bollDetailEl) bollDetailEl.textContent = data.boll.detail;
}
if (data.kdj) {
document.getElementById('kdj-signal').textContent = data.kdj.signal;
document.getElementById('kdj-detail').textContent = data.kdj.detail;
const kdjSignalEl = document.getElementById('kdj-signal');
if (kdjSignalEl) kdjSignalEl.textContent = data.kdj.signal;
const kdjDetailEl = document.getElementById('kdj-detail');
if (kdjDetailEl) kdjDetailEl.textContent = data.kdj.detail;
}
if (data.resistances) {
for (let i = 0; i < 2; i++) {
const el = document.getElementById(`resistance-${i + 1}`);
if (el) {
el.querySelector('span:last-child').textContent = formatNumber(data.resistances[i]);
el.textContent = formatNumber(data.resistances[i]);
}
}
}
@ -697,36 +735,32 @@ function updateDetailView(data) {
for (let i = 0; i < 2; i++) {
const el = document.getElementById(`support-${i + 1}`);
if (el) {
el.querySelector('span:last-child').textContent = formatNumber(data.supports[i]);
el.textContent = formatNumber(data.supports[i]);
}
}
}
if (data.pivotPoint) {
document.getElementById('pivot-point').querySelector('span:last-child').textContent = formatNumber(data.pivotPoint);
const ppEl = document.getElementById('pivot-point');
if (ppEl) ppEl.textContent = formatNumber(data.pivotPoint);
}
if (data.periodConsistency) {
const container = document.getElementById('period-trends');
const periodNames = { '5': '5分钟', '15': '15分钟', '30': '30分钟', '60': '60分钟' };
container.innerHTML = Object.entries(data.periodConsistency).map(([period, trend]) => `
<div class="trend-row">
<span class="trend-period">${periodNames[period]}</span>
<span class="trend-badge ${trend}">
${trend === 'up' ? '上涨' : trend === 'down' ? '下跌' : '震荡'}
</span>
</div>
`).join('');
if (container) {
const periodNames = { '5': '5分钟', '15': '15分钟', '30': '30分钟', '60': '60分钟' };
container.innerHTML = Object.entries(data.periodConsistency).map(([period, trend]) => `
<div class="trend-row">
<span class="trend-period">${periodNames[period]}</span>
<span class="trend-badge ${trend}">
${trend === 'up' ? '上涨' : trend === 'down' ? '下跌' : '震荡'}
</span>
</div>
`).join('');
}
}
if (data.trendScore !== undefined) {
document.getElementById('trend-score').textContent = data.trendScore;
const circle = document.getElementById('score-fill');
const circumference = 2 * Math.PI * 45;
const offset = circumference - (data.trendScore / 100) * circumference;
circle.style.strokeDasharray = circumference;
circle.style.strokeDashoffset = offset;
}
// 注意: trend-score 和 score-fill 在新HTML中不存在已移除
}
async function loadHistoryList(symbol) {
@ -761,8 +795,7 @@ async function loadAIAnalysis() {
} else {
console.log(`合约 ${currentSymbol} 无分析结果`);
content.innerHTML = `
<div class="ai-analysis-placeholder">
<i class="fas fa-brain"></i>
<div style="text-align: center; padding: 28px; color: var(--text-tertiary);">
<p>点击"智能分析"按钮获取AI分析结果</p>
</div>
`;
@ -770,8 +803,7 @@ async function loadAIAnalysis() {
} catch (error) {
console.error(`加载合约 ${currentSymbol} AI分析失败:`, error);
content.innerHTML = `
<div class="ai-analysis-placeholder">
<i class="fas fa-brain"></i>
<div style="text-align: center; padding: 28px; color: var(--text-tertiary);">
<p>点击"智能分析"按钮获取AI分析结果</p>
</div>
`;
@ -1681,11 +1713,11 @@ async function runAIAnalysis(forceRefresh = false) {
const content = document.getElementById('ai-analysis-content');
btn.disabled = true;
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i><span>分析中...</span>';
btn.textContent = '分析中...';
content.innerHTML = `
<div class="ai-analysis-loading">
<i class="fas fa-brain"></i>
<div style="display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 28px; gap: 14px;">
<div style="font-size: 32px; color: var(--color-ai); animation: spin 1.5s linear infinite;"></div>
<span>AI正在分析中...</span>
</div>
`;
@ -1700,8 +1732,7 @@ async function runAIAnalysis(forceRefresh = false) {
showToast('success', '分析完成', `${currentSymbol} AI分析已完成`);
} else {
content.innerHTML = `
<div class="ai-analysis-placeholder">
<i class="fas fa-exclamation-triangle"></i>
<div style="text-align: center; padding: 28px; color: var(--text-tertiary);">
<p>${data.error || 'AI分析失败请稍后重试'}</p>
</div>
`;
@ -1710,26 +1741,33 @@ async function runAIAnalysis(forceRefresh = false) {
} catch (error) {
console.error('AI分析请求失败:', error);
content.innerHTML = `
<div class="ai-analysis-placeholder">
<i class="fas fa-exclamation-triangle"></i>
<div style="text-align: center; padding: 28px; color: var(--text-tertiary);">
<p>网络错误请检查网络连接</p>
</div>
`;
showToast('error', '请求失败', '网络错误,请稍后重试');
} finally {
btn.disabled = false;
btn.innerHTML = '<i class="fas fa-play"></i><span>智能分析</span>';
btn.textContent = '智能分析';
}
}
function displayAIAnalysisResult(data) {
console.log('[AI分析] 开始显示AI分析结果...', data);
const content = document.getElementById('ai-analysis-content');
console.log('[AI分析] ai-analysis-content元素:', content);
if (!content) {
console.error('[AI分析] 错误: 找不到ai-analysis-content元素!');
return;
}
const result = data.result;
const timestamp = new Date(data.analysis_time).toLocaleString('zh-CN');
console.log('[AI分析] 分析结果数据:', result);
const direction = result.trading_suggestion?.direction || '观望';
const directionClass = direction === '做多' ? 'long' : direction === '做空' ? 'short' : 'neutral';
const directionIcon = direction === '做多' ? 'fa-arrow-up' : direction === '做空' ? 'fa-arrow-down' : 'fa-arrows-left-right';
const directionIcon = direction === '做多' ? '↑' : direction === '做空' ? '↓' : '↔';
const confidence = result.trading_suggestion?.confidence || 0;
const entryMin = result.trading_suggestion?.entry_range?.min || '--';
@ -1737,13 +1775,14 @@ function displayAIAnalysisResult(data) {
const stopLoss = result.trading_suggestion?.stop_loss || '--';
const positionSize = result.trading_suggestion?.position_size || '--';
console.log('[AI分析] 生成HTML模板...');
content.innerHTML = `
<div class="ai-analysis-result">
<div class="ai-summary">${result.summary || '暂无总结'}</div>
<div class="ai-suggestion-row">
<div class="ai-suggestion-direction ${directionClass}">
<i class="fas ${directionIcon}"></i>
<span style="font-size:18px;font-weight:700;">${directionIcon}</span>
<span style="font-weight:600;">${direction}</span>
</div>
<div class="ai-confidence">
@ -1775,16 +1814,19 @@ function displayAIAnalysisResult(data) {
</div>
<div class="ai-timestamp">
<i class="fas fa-clock"></i> : ${timestamp}
<span>🕐</span> : ${timestamp}
<button class="ai-detail-btn" onclick="showAIDetailModal()" style="margin-left: 8px; background: none; border: 1px solid var(--border-color); padding: 4px 12px; border-radius: 4px; color: var(--cyan); cursor: pointer; font-size: 11px;">
查看详情 <i class="fas fa-external-link-alt"></i>
查看详情
</button>
</div>
</div>
`;
console.log('[AI分析] HTML已设置到DOM');
// 同步AI分析数据到主面板各个卡片
syncAIToPanels(result);
console.log('[AI分析] AI分析结果显示完成');
}
function syncAIToPanels(result) {
@ -1814,23 +1856,23 @@ function syncAIToPanels(result) {
// 3. 同步到关键点位卡片
if (pivotPoints.r1) {
const r1El = document.getElementById('resistance-1');
if (r1El) r1El.querySelector('span:last-child').textContent = pivotPoints.r1;
if (r1El) r1El.textContent = pivotPoints.r1;
}
if (pivotPoints.r2) {
const r2El = document.getElementById('resistance-2');
if (r2El) r2El.querySelector('span:last-child').textContent = pivotPoints.r2;
if (r2El) r2El.textContent = pivotPoints.r2;
}
if (pivotPoints.pp) {
const ppEl = document.getElementById('pivot-point');
if (ppEl) ppEl.querySelector('span:last-child').textContent = pivotPoints.pp;
if (ppEl) ppEl.textContent = pivotPoints.pp;
}
if (pivotPoints.s1) {
const s1El = document.getElementById('support-1');
if (s1El) s1El.querySelector('span:last-child').textContent = pivotPoints.s1;
if (s1El) s1El.textContent = pivotPoints.s1;
}
if (pivotPoints.s2) {
const s2El = document.getElementById('support-2');
if (s2El) s2El.querySelector('span:last-child').textContent = pivotPoints.s2;
if (s2El) s2El.textContent = pivotPoints.s2;
}
// 4. 同步到多周期趋势卡片

@ -0,0 +1,462 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>期货智析 - Web Prototype (Apple Style)</title>
<style>
:root {
/* v3.0 Apple Style Colors */
--bg-page: #F5F5F7;
--bg-card: #FFFFFF;
--text-primary: #1D1D1F;
--text-secondary: #6E6E73;
--text-tertiary: #86868B;
--color-up: #FF3B30;
--color-down: #34C759;
--color-neutral: #FF9F0A;
--color-brand: #007AFF;
--color-ai: #AF52DE;
/* Shadows */
--shadow-sm: 0 1px 3px rgba(0,0,0,0.04);
--shadow-md: 0 4px 12px rgba(0,0,0,0.06);
--shadow-lg: 0 8px 24px rgba(0,0,0,0.08);
/* Radius */
--radius-card: 20px;
--radius-btn: 12px;
--radius-pill: 9999px;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", "PingFang SC", sans-serif;
background-color: var(--bg-page);
color: var(--text-primary);
-webkit-font-smoothing: antialiased;
padding-bottom: 40px;
}
/* Header - Glassmorphism */
.header {
position: sticky;
top: 0;
z-index: 100;
height: 64px;
background: rgba(255, 255, 255, 0.8);
backdrop-filter: saturate(180%) blur(20px);
-webkit-backdrop-filter: saturate(180%) blur(20px);
border-bottom: 1px solid rgba(0,0,0,0.05);
display: flex;
align-items: center;
padding: 0 32px;
}
.logo { font-size: 18px; font-weight: 600; letter-spacing: -0.02em; margin-right: 40px; }
.nav-items { display: flex; gap: 24px; flex: 1; }
.nav-item { font-size: 14px; color: var(--text-secondary); cursor: pointer; transition: color 0.2s; }
.nav-item:hover { color: var(--text-primary); }
.nav-item.active { color: var(--color-brand); font-weight: 500; }
.live-badge { font-size: 11px; color: var(--color-down); display: flex; align-items: center; gap: 4px; }
.dot { width: 6px; height: 6px; background: var(--color-down); border-radius: 50%; }
/* Main Container */
.container { max-width: 1200px; margin: 0 auto; padding: 24px 32px; }
/* Search & Filter */
.toolbar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; flex-wrap: wrap; gap: 16px; }
.search-box {
background: #FFFFFF;
border: none;
border-radius: var(--radius-btn);
padding: 10px 16px;
font-size: 14px;
width: 300px;
box-shadow: var(--shadow-sm);
outline: none;
transition: box-shadow 0.2s;
}
.search-box:focus { box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.2); }
.pills { display: flex; gap: 8px; }
.pill {
padding: 6px 16px;
border-radius: var(--radius-pill);
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
background: #FFFFFF;
color: var(--text-secondary);
box-shadow: var(--shadow-sm);
}
.pill:hover { background: #F5F5F7; }
.pill.active { background: var(--color-brand); color: #FFFFFF; box-shadow: 0 4px 10px rgba(0,122,255,0.3); }
/* Stats Row */
.stats-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 32px; }
.stat-card {
background: var(--bg-card);
border-radius: 16px;
padding: 16px 20px;
display: flex;
align-items: center;
gap: 16px;
box-shadow: var(--shadow-sm);
transition: transform 0.2s, box-shadow 0.2s;
}
.stat-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
.stat-icon { width: 40px; height: 40px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 18px; }
.stat-icon.blue { background: rgba(0,122,255,0.1); color: var(--color-brand); }
.stat-icon.red { background: rgba(255,59,48,0.1); color: var(--color-up); }
.stat-icon.green { background: rgba(52,199,89,0.1); color: var(--color-down); }
.stat-icon.orange { background: rgba(255,159,10,0.1); color: var(--color-neutral); }
.stat-val { font-size: 24px; font-weight: 700; line-height: 1.2; }
.stat-label { font-size: 13px; color: var(--text-secondary); }
/* Grid */
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 20px; }
/* Card */
.card {
background: var(--bg-card);
border-radius: var(--radius-card);
padding: 24px;
box-shadow: var(--shadow-md);
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
position: relative;
overflow: hidden;
cursor: pointer;
}
.card:hover { transform: translateY(-4px); box-shadow: var(--shadow-lg); }
.card-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 16px; }
.code-box { width: 36px; height: 36px; background: #F5F5F7; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 600; color: var(--text-tertiary); margin-bottom: 8px; }
.price-area { text-align: right; }
.price { font-size: 28px; font-weight: 700; letter-spacing: -0.02em; line-height: 1; }
.price.up { color: var(--color-up); }
.price.down { color: var(--color-down); }
.change { font-size: 13px; font-weight: 600; margin-top: 4px; }
.change.up { color: var(--color-up); }
.change.down { color: var(--color-down); }
.action-pill {
display: inline-block;
padding: 4px 12px;
border-radius: var(--radius-pill);
font-size: 12px;
font-weight: 600;
margin-bottom: 16px;
}
.action-pill.do-more { background: rgba(52,199,89,0.1); color: var(--color-down); }
.action-pill.watch { background: rgba(255,159,10,0.1); color: var(--color-neutral); }
.metrics { display: flex; gap: 24px; margin-bottom: 16px; }
.metric { flex: 1; }
.metric-label { font-size: 11px; color: var(--text-tertiary); margin-bottom: 4px; }
.metric-val { font-size: 14px; font-weight: 600; }
.bar-bg { height: 4px; background: #F5F5F7; border-radius: 2px; margin-top: 6px; overflow: hidden; }
.bar-fill { height: 100%; border-radius: 2px; }
.timeframes { display: flex; gap: 8px; margin-bottom: 16px; }
.tf { padding: 4px 10px; border-radius: 8px; font-size: 11px; color: var(--text-tertiary); background: #F5F5F7; cursor: pointer; }
.tf.active { background: #E8F2FF; color: var(--color-brand); font-weight: 500; }
.card-footer { display: flex; justify-content: space-between; align-items: center; border-top: 1px solid #F5F5F7; padding-top: 12px; font-size: 12px; }
.support-resist { color: var(--text-tertiary); }
.support-resist span { margin-right: 8px; }
.support-resist .red { color: var(--color-up); }
.support-resist .green { color: var(--color-down); }
.link { color: var(--color-brand); font-weight: 500; text-decoration: none; }
.link:hover { text-decoration: underline; }
</style>
</head>
<body>
<header class="header">
<div class="logo">◈ 期货智析</div>
<div class="nav-items">
<div class="nav-item active">品种分析</div>
<div class="nav-item">自选</div>
<div class="nav-item">市场概览</div>
<div class="nav-item">风险预警</div>
</div>
<div class="live-badge">
<div class="dot"></div> LIVE
</div>
</header>
<div class="container">
<!-- Toolbar -->
<div class="toolbar">
<input type="text" class="search-box" placeholder="🔍 搜索品种名称或代码...">
<div class="pills">
<div class="pill active">全部 32</div>
<div class="pill">能源</div>
<div class="pill">金属</div>
<div class="pill">农产品</div>
<div class="pill">金融</div>
</div>
</div>
<!-- Stats -->
<div class="stats-row">
<div class="stat-card">
<div class="stat-icon blue"></div>
<div><div class="stat-val">32</div><div class="stat-label">监控品种</div></div>
</div>
<div class="stat-card">
<div class="stat-icon green"></div>
<div><div class="stat-val">7</div><div class="stat-label">上涨趋势</div></div>
</div>
<div class="stat-card">
<div class="stat-icon red"></div>
<div><div class="stat-val">10</div><div class="stat-label">下跌趋势</div></div>
</div>
<div class="stat-card">
<div class="stat-icon orange"></div>
<div><div class="stat-val">15</div><div class="stat-label">震荡整理</div></div>
</div>
</div>
<!-- Grid -->
<div class="grid">
<!-- Card 1: Crude Oil -->
<div class="card">
<div class="card-header">
<div>
<div class="code-box">SC</div>
<div style="font-weight:600;">原油</div>
<div style="font-size:12px; color:var(--text-tertiary);">SC2606</div>
</div>
<div class="price-area">
<div class="price down">¥644.5</div>
<div class="change down">↓ -13.3 (-2.02%)</div>
</div>
</div>
<div class="action-pill watch">观望</div>
<div class="metrics">
<div class="metric">
<div class="metric-label">成功率 60%</div>
<div class="bar-bg"><div class="bar-fill" style="width:60%; background:var(--color-neutral);"></div></div>
</div>
<div class="metric">
<div class="metric-label">趋势评分 30</div>
<div class="bar-bg"><div class="bar-fill" style="width:30%; background:var(--color-up);"></div></div>
</div>
</div>
<div class="timeframes">
<div class="tf">5M</div>
<div class="tf active">15M</div>
<div class="tf">30M</div>
<div class="tf">1H</div>
</div>
<div class="card-footer">
<div class="support-resist">
<span>压力 <b class="red">642.4</b></span>
<span>支撑 <b class="green">631.7</b></span>
</div>
<a href="#" class="link">详情 →</a>
</div>
</div>
<!-- Card 2: Fuel Oil -->
<div class="card">
<div class="card-header">
<div>
<div class="code-box">FU</div>
<div style="font-weight:600;">燃油</div>
<div style="font-size:12px; color:var(--text-tertiary);">FU2606</div>
</div>
<div class="price-area">
<div class="price up">¥4,680</div>
<div class="change up">↑ +72 (+1.56%)</div>
</div>
</div>
<div class="action-pill do-more">做多</div>
<div class="metrics">
<div class="metric">
<div class="metric-label">成功率 85%</div>
<div class="bar-bg"><div class="bar-fill" style="width:85%; background:var(--color-down);"></div></div>
</div>
<div class="metric">
<div class="metric-label">趋势评分 65</div>
<div class="bar-bg"><div class="bar-fill" style="width:65%; background:var(--color-down);"></div></div>
</div>
</div>
<div class="timeframes">
<div class="tf">5M</div>
<div class="tf active">15M</div>
<div class="tf">30M</div>
<div class="tf">1H</div>
</div>
<div class="card-footer">
<div class="support-resist">
<span>压力 <b class="red">4,600</b></span>
<span>支撑 <b class="green">4,530</b></span>
</div>
<a href="#" class="link">详情 →</a>
</div>
</div>
<!-- Card 3: Silver -->
<div class="card">
<div class="card-header">
<div>
<div class="code-box">AG</div>
<div style="font-weight:600;">沪银</div>
<div style="font-size:12px; color:var(--text-tertiary);">AG2606</div>
</div>
<div class="price-area">
<div class="price up">¥18,643</div>
<div class="change up">↑ +413 (+2.27%)</div>
</div>
</div>
<div class="action-pill do-more">做多 (短线反弹)</div>
<div class="metrics">
<div class="metric">
<div class="metric-label">成功率 60%</div>
<div class="bar-bg"><div class="bar-fill" style="width:60%; background:var(--color-neutral);"></div></div>
</div>
<div class="metric">
<div class="metric-label">趋势评分 60</div>
<div class="bar-bg"><div class="bar-fill" style="width:60%; background:var(--color-neutral);"></div></div>
</div>
</div>
<div class="timeframes">
<div class="tf">5M</div>
<div class="tf active">15M</div>
<div class="tf">30M</div>
<div class="tf">1H</div>
</div>
<div class="card-footer">
<div class="support-resist">
<span>压力 <b class="red">18,685</b></span>
<span>支撑 <b class="green">18,584</b></span>
</div>
<a href="#" class="link">详情 →</a>
</div>
</div>
<!-- Card 4: Gold -->
<div class="card">
<div class="card-header">
<div>
<div class="code-box">AU</div>
<div style="font-weight:600;">沪金</div>
<div style="font-size:12px; color:var(--text-tertiary);">AU2606</div>
</div>
<div class="price-area">
<div class="price up">¥993.34</div>
<div class="change up">↑ +4.36 (+0.44%)</div>
</div>
</div>
<div class="action-pill watch">观望</div>
<div class="metrics">
<div class="metric">
<div class="metric-label">成功率 60%</div>
<div class="bar-bg"><div class="bar-fill" style="width:60%; background:var(--color-neutral);"></div></div>
</div>
<div class="metric">
<div class="metric-label">趋势评分 40</div>
<div class="bar-bg"><div class="bar-fill" style="width:40%; background:var(--color-up);"></div></div>
</div>
</div>
<div class="timeframes">
<div class="tf">5M</div>
<div class="tf active">15M</div>
<div class="tf">30M</div>
<div class="tf">1H</div>
</div>
<div class="card-footer">
<div class="support-resist">
<span>压力 <b class="red">993</b></span>
<span>支撑 <b class="green">989.5</b></span>
</div>
<a href="#" class="link">详情 →</a>
</div>
</div>
<!-- Card 5: Copper -->
<div class="card">
<div class="card-header">
<div>
<div class="code-box">CU</div>
<div style="font-weight:600;">沪铜</div>
<div style="font-size:12px; color:var(--text-tertiary);">CU2606</div>
</div>
<div class="price-area">
<div class="price up">¥104,770</div>
<div class="change up">↑ +1,090 (+1.05%)</div>
</div>
</div>
<div class="action-pill do-more">做多</div>
<div class="metrics">
<div class="metric">
<div class="metric-label">成功率 85%</div>
<div class="bar-bg"><div class="bar-fill" style="width:85%; background:var(--color-down);"></div></div>
</div>
<div class="metric">
<div class="metric-label">趋势评分 65</div>
<div class="bar-bg"><div class="bar-fill" style="width:65%; background:var(--color-down);"></div></div>
</div>
</div>
<div class="timeframes">
<div class="tf">5M</div>
<div class="tf active">15M</div>
<div class="tf">30M</div>
<div class="tf">1H</div>
</div>
<div class="card-footer">
<div class="support-resist">
<span>压力 <b class="red">105,250</b></span>
<span>支撑 <b class="green">104,450</b></span>
</div>
<a href="#" class="link">详情 →</a>
</div>
</div>
<!-- Card 6: Nickel -->
<div class="card">
<div class="card-header">
<div>
<div class="code-box">NI</div>
<div style="font-weight:600;">沪镍</div>
<div style="font-size:12px; color:var(--text-tertiary);">NI2606</div>
</div>
<div class="price-area">
<div class="price down">¥143,330</div>
<div class="change down">↓ -440 (-0.31%)</div>
</div>
</div>
<div class="action-pill watch">观望</div>
<div class="metrics">
<div class="metric">
<div class="metric-label">成功率 60%</div>
<div class="bar-bg"><div class="bar-fill" style="width:60%; background:var(--color-neutral);"></div></div>
</div>
<div class="metric">
<div class="metric-label">趋势评分 45</div>
<div class="bar-bg"><div class="bar-fill" style="width:45%; background:var(--color-up);"></div></div>
</div>
</div>
<div class="timeframes">
<div class="tf">5M</div>
<div class="tf active">15M</div>
<div class="tf">30M</div>
<div class="tf">1H</div>
</div>
<div class="card-footer">
<div class="support-resist">
<span>压力 <b class="red">144,550</b></span>
<span>支撑 <b class="green">143,940</b></span>
</div>
<a href="#" class="link">详情 →</a>
</div>
</div>
</div>
</div>
</body>
</html>

@ -0,0 +1,352 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>期货智析 - 品种详情页 (Apple Style)</title>
<style>
:root {
--bg-page: #F5F5F7;
--bg-card: #FFFFFF;
--text-primary: #1D1D1F;
--text-secondary: #6E6E73;
--text-tertiary: #86868B;
--border-light: rgba(0,0,0,0.05);
/* China Market: Red=Up, Green=Down */
--color-up: #FF3B30;
--color-down: #34C759;
--color-brand: #007AFF;
--color-ai: #AF52DE;
--color-ai-bg: rgba(175, 82, 222, 0.08);
--shadow-sm: 0 1px 3px rgba(0,0,0,0.04);
--shadow-md: 0 4px 12px rgba(0,0,0,0.06);
--radius-card: 16px;
--radius-btn: 8px;
--radius-pill: 9999px;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", "PingFang SC", sans-serif;
background-color: var(--bg-page);
color: var(--text-primary);
padding-bottom: 40px;
}
/* Header */
.header {
height: 64px;
background: rgba(255,255,255,0.8);
backdrop-filter: blur(20px);
border-bottom: 1px solid var(--border-light);
display: flex;
align-items: center;
padding: 0 24px;
position: sticky;
top: 0;
z-index: 100;
}
.back-btn {
font-size: 14px;
color: var(--color-brand);
font-weight: 500;
cursor: pointer;
margin-right: 20px;
display: flex;
align-items: center;
gap: 4px;
}
.header-title { font-size: 18px; font-weight: 600; }
.header-actions { margin-left: auto; display: flex; gap: 12px; }
.icon-btn { width: 32px; height: 32px; border-radius: 50%; background: #F5F5F7; display: flex; align-items: center; justify-content: center; cursor: pointer; color: var(--text-secondary); }
/* Main Content */
.container { max-width: 1200px; margin: 0 auto; padding: 24px; display: grid; grid-template-columns: 1fr 340px; gap: 24px; }
/* Info Bar */
.info-bar {
grid-column: 1 / -1;
background: var(--bg-card);
border-radius: var(--radius-card);
padding: 20px 24px;
box-shadow: var(--shadow-sm);
display: flex;
justify-content: space-between;
align-items: center;
}
.info-left { display: flex; align-items: baseline; gap: 16px; }
.symbol { font-size: 20px; font-weight: 700; }
.contract { font-size: 13px; color: var(--text-tertiary); background: #F5F5F7; padding: 4px 8px; border-radius: 6px; }
.price-main { font-size: 32px; font-weight: 700; letter-spacing: -0.02em; }
.price-up { color: var(--color-up); }
.price-down { color: var(--color-down); }
.price-change { font-size: 15px; font-weight: 600; margin-left: 8px; }
.ohlc { display: flex; gap: 24px; font-size: 13px; color: var(--text-secondary); }
.ohlc span b { font-weight: 600; color: var(--text-primary); margin-left: 4px; }
/* Chart Area */
.chart-section {
background: var(--bg-card);
border-radius: var(--radius-card);
padding: 20px;
box-shadow: var(--shadow-md);
}
.chart-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; }
.timeframes { display: flex; gap: 4px; background: #F5F5F7; padding: 4px; border-radius: 8px; }
.tf-btn { padding: 4px 10px; border-radius: 6px; font-size: 12px; font-weight: 500; color: var(--text-tertiary); cursor: pointer; }
.tf-btn.active { background: #FFFFFF; color: var(--text-primary); box-shadow: 0 1px 2px rgba(0,0,0,0.1); }
.chart-placeholder {
height: 360px;
background: #FAFAFA;
border-radius: 12px;
position: relative;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-tertiary);
font-size: 14px;
}
/* Simulated Chart Grid */
.grid-line { position: absolute; background: #F0F0F0; }
.grid-h { width: 100%; height: 1px; }
.grid-v { height: 100%; width: 1px; }
/* Side Panel */
.side-panel { display: flex; flex-direction: column; gap: 20px; }
.panel-card {
background: var(--bg-card);
border-radius: var(--radius-card);
padding: 20px;
box-shadow: var(--shadow-sm);
transition: transform 0.2s;
}
.panel-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
.card-title {
font-size: 15px;
font-weight: 600;
margin-bottom: 16px;
display: flex;
align-items: center;
justify-content: space-between;
}
.ai-tag { font-size: 10px; background: var(--color-ai-bg); color: var(--color-ai); padding: 2px 6px; border-radius: 4px; }
.analysis-text {
font-size: 13px;
color: var(--text-secondary);
line-height: 1.6;
margin-bottom: 16px;
background: #FAFAFA;
padding: 12px;
border-radius: 8px;
border-left: 3px solid var(--color-ai);
}
.data-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 12px; }
.data-item { display: flex; justify-content: space-between; font-size: 12px; }
.label { color: var(--text-tertiary); }
.val { font-weight: 600; }
.indicator-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
.indicator-box { background: #F5F5F7; padding: 12px; border-radius: 10px; }
.ind-name { font-size: 11px; color: var(--text-tertiary); margin-bottom: 4px; font-weight: 500; }
.ind-val { font-size: 14px; font-weight: 700; }
.pill-row { display: flex; gap: 8px; margin-top: 12px; }
.status-pill { padding: 6px 12px; border-radius: 20px; font-size: 12px; font-weight: 500; background: #F5F5F7; color: var(--text-secondary); }
.status-pill.red { background: rgba(255,59,48,0.08); color: var(--color-up); }
.status-pill.green { background: rgba(52,199,89,0.08); color: var(--color-down); }
.status-pill.orange { background: rgba(255,159,10,0.08); color: var(--color-neutral); }
/* Key Points */
.kp-row { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #F5F5F7; font-size: 13px; }
.kp-row:last-child { border-bottom: none; }
.kp-tag { font-weight: 600; color: var(--text-secondary); }
.kp-val { font-weight: 700; }
/* History */
.history-bar {
grid-column: 1 / -1;
background: var(--bg-card);
border-radius: var(--radius-card);
padding: 16px 24px;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: var(--shadow-sm);
}
.hist-title { font-size: 14px; font-weight: 600; color: var(--text-secondary); }
.hist-link { font-size: 13px; color: var(--color-brand); font-weight: 500; cursor: pointer; }
</style>
</head>
<body>
<header class="header">
<div class="back-btn">← 返回</div>
<div class="header-title">原油 SC2606</div>
<div class="header-actions">
<div class="icon-btn">🔔</div>
<div class="icon-btn"></div>
</div>
</header>
<div class="container">
<!-- Info Bar -->
<div class="info-bar">
<div class="info-left">
<span class="symbol">SC2606</span>
<span class="contract">SC</span>
<div>
<span class="price-main price-down">644.5</span>
<span class="price-change price-down">↓ -13.3 (-2.02%)</span>
</div>
</div>
<div class="ohlc">
<span><b>657.8</b></span>
<span><b class="price-up">664.8</b></span>
<span><b class="price-down">630.2</b></span>
<span><b>1,668,293</b></span>
</div>
</div>
<!-- Chart Section -->
<div class="chart-section">
<div class="chart-header">
<div class="timeframes">
<div class="tf-btn">分时</div>
<div class="tf-btn">5M</div>
<div class="tf-btn active">15M</div>
<div class="tf-btn">30M</div>
<div class="tf-btn">1H</div>
</div>
<div style="font-size:12px; color:var(--text-tertiary);">MA5: 652.1 MA10: 655.4 MA20: 660.2</div>
</div>
<div class="chart-placeholder">
<!-- Grid Lines -->
<div class="grid-line grid-h" style="top: 20%;"></div>
<div class="grid-line grid-h" style="top: 40%;"></div>
<div class="grid-line grid-h" style="top: 60%;"></div>
<div class="grid-line grid-h" style="top: 80%;"></div>
<div class="grid-line grid-v" style="left: 20%;"></div>
<div class="grid-line grid-v" style="left: 40%;"></div>
<div class="grid-line grid-v" style="left: 60%;"></div>
<div class="grid-line grid-v" style="left: 80%;"></div>
<!-- Simulated Candles via CSS/SVG is complex, using text placeholder for prototype -->
<div style="text-align:center;">
<div style="font-size:48px; color:var(--color-down); opacity:0.1; margin-bottom:10px;">📉</div>
<div>K 线图区域 (ECharts / TradingView)</div>
<div style="font-size:12px; margin-top:4px;">15 分钟周期 | 2026-05-23</div>
</div>
</div>
</div>
<!-- Side Panel -->
<div class="side-panel">
<!-- AI Analysis -->
<div class="panel-card">
<div class="card-title">
<span> AI 思维分析</span>
<span class="ai-tag">智能</span>
</div>
<div class="analysis-text">
SC2606 呈多周期空头排列,均线全面压制且量能极度萎缩,短期 30/60 分钟 KDJ 进入超卖区但无底背离与放量配合,整体处于缩量阴跌格局。
</div>
<div class="data-grid">
<div class="data-item"><span class="label">建议方向</span><span class="val price-down">观望</span></div>
<div class="data-item"><span class="label">置信度</span><span class="val">30%</span></div>
<div class="data-item"><span class="label">入场区间</span><span class="val">640.5 - 642.5</span></div>
<div class="data-item"><span class="label">止损位</span><span class="val price-up">645.5</span></div>
</div>
</div>
<!-- Indicators -->
<div class="panel-card">
<div class="card-title"> 技术指标</div>
<div class="indicator-grid">
<div class="indicator-box">
<div class="ind-name">MACD</div>
<div class="ind-val price-down">Down</div>
<div style="font-size:10px; color:var(--text-tertiary);">零轴下</div>
</div>
<div class="indicator-box">
<div class="ind-name">KDJ</div>
<div class="ind-val" style="color:var(--color-neutral);">超卖</div>
<div style="font-size:10px; color:var(--text-tertiary);">未金叉</div>
</div>
<div class="indicator-box">
<div class="ind-name">RSI</div>
<div class="ind-val">--</div>
<div style="font-size:10px; color:var(--text-tertiary);">--</div>
</div>
<div class="indicator-box">
<div class="ind-name">BOLL</div>
<div class="ind-val">--</div>
<div style="font-size:10px; color:var(--text-tertiary);">--</div>
</div>
</div>
</div>
<!-- Key Points -->
<div class="panel-card">
<div class="card-title">📍 关键点位</div>
<div class="kp-row">
<span class="kp-tag">R1 (阻力)</span>
<span class="kp-val price-up">642.4</span>
</div>
<div class="kp-row">
<span class="kp-tag">R2 (阻力)</span>
<span class="kp-val price-up">648.8</span>
</div>
<div class="kp-row" style="background: #F5F5F7; margin: 4px -12px; padding: 8px 12px; border-radius: 6px;">
<span class="kp-tag" style="color:var(--color-brand);">PP (中枢)</span>
<span class="kp-val" style="color:var(--color-brand);">638.1</span>
</div>
<div class="kp-row">
<span class="kp-tag">S1 (支撑)</span>
<span class="kp-val price-down">631.7</span>
</div>
<div class="kp-row">
<span class="kp-tag">S2 (支撑)</span>
<span class="kp-val price-down">627.4</span>
</div>
</div>
<!-- Multi-TF -->
<div class="panel-card">
<div class="card-title">🕒 多周期趋势</div>
<div class="pill-row">
<div class="status-pill red">60M 偏空</div>
<div class="status-pill orange">30M 震荡</div>
<div class="status-pill red">15M 偏空</div>
<div class="status-pill orange">5M 震荡</div>
</div>
</div>
</div>
<!-- History Bar -->
<div class="history-bar">
<div>
<span class="hist-title">🕰 历史分析记录</span>
<span style="font-size:12px; color:var(--text-tertiary); margin-left:12px;">SC2606 呈多周期空头排列...</span>
</div>
<span class="hist-link">查看全部 →</span>
</div>
</div>
</body>
</html>

@ -0,0 +1,340 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>期货智析 - AI 分析报告 (Apple Style)</title>
<style>
:root {
--bg-page: #F5F5F7;
--bg-card: #FFFFFF;
--text-primary: #1D1D1F;
--text-secondary: #6E6E73;
--text-tertiary: #86868B;
--color-up: #FF3B30;
--color-down: #34C759;
--color-neutral: #FF9F0A;
--color-brand: #007AFF;
--color-ai: #AF52DE;
--shadow-modal: 0 24px 80px rgba(0,0,0,0.15);
--radius-modal: 24px;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", "PingFang SC", sans-serif;
background-color: var(--bg-page);
color: var(--text-primary);
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
/* Background Simulation */
.bg-blur {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><rect fill="%23F5F5F7" width="100" height="100"/><rect fill="%23E5E5EA" x="10" y="10" width="80" height="80" rx="16"/></svg>');
filter: blur(10px) brightness(0.95);
z-index: 0;
}
/* Modal */
.modal {
position: relative;
z-index: 10;
width: 560px;
max-height: 85vh;
background: var(--bg-card);
border-radius: var(--radius-modal);
box-shadow: var(--shadow-modal);
display: flex;
flex-direction: column;
overflow: hidden;
}
.modal-header {
padding: 20px 24px;
border-bottom: 1px solid rgba(0,0,0,0.05);
display: flex;
justify-content: space-between;
align-items: center;
background: rgba(255,255,255,0.95);
}
.modal-title { font-size: 18px; font-weight: 700; display: flex; align-items: center; gap: 8px; }
.modal-date { font-size: 12px; color: var(--text-tertiary); font-weight: 400; }
.close-btn { width: 28px; height: 28px; background: #F5F5F7; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; color: var(--text-secondary); font-size: 14px; }
.modal-body {
padding: 24px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 24px;
}
.modal-body::-webkit-scrollbar { width: 6px; }
.modal-body::-webkit-scrollbar-thumb { background: #E5E5EA; border-radius: 3px; }
/* Quote */
.quote {
background: rgba(175, 82, 222, 0.06);
border-left: 4px solid var(--color-ai);
padding: 16px;
border-radius: 0 12px 12px 0;
font-size: 14px;
color: var(--text-secondary);
line-height: 1.6;
}
/* Section */
.section-title {
font-size: 15px;
font-weight: 700;
color: var(--color-ai);
display: flex;
align-items: center;
gap: 6px;
margin-bottom: 12px;
}
/* Table */
.table-container {
border: 1px solid rgba(0,0,0,0.05);
border-radius: 12px;
overflow: hidden;
}
table { width: 100%; border-collapse: collapse; font-size: 13px; }
th { background: #F5F5F7; text-align: left; padding: 10px 12px; font-weight: 600; color: var(--text-secondary); }
td { padding: 12px; border-top: 1px solid rgba(0,0,0,0.05); color: var(--text-primary); }
tr:last-child td { border-bottom: none; }
.status-badge {
display: inline-block;
padding: 2px 6px;
border-radius: 4px;
font-size: 11px;
font-weight: 600;
}
.status-down { background: rgba(52,199,89,0.1); color: var(--color-down); }
.status-up { background: rgba(255,59,48,0.1); color: var(--color-up); }
.status-neutral { background: rgba(142,142,147,0.1); color: var(--text-secondary); }
/* Stats Row */
.stats-row {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
}
.stat-box {
background: #F5F5F7;
padding: 12px;
border-radius: 12px;
text-align: center;
}
.stat-label { font-size: 11px; color: var(--text-tertiary); margin-bottom: 4px; }
.stat-val { font-size: 16px; font-weight: 700; }
.stat-val.red { color: var(--color-up); }
.stat-val.blue { color: var(--color-brand); }
/* KDJ Grid */
.kdj-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.kdj-card {
background: #F5F5F7;
padding: 12px;
border-radius: 10px;
}
.kdj-label { font-size: 11px; color: var(--text-tertiary); font-weight: 500; margin-bottom: 4px; }
.kdj-text { font-size: 13px; font-weight: 600; }
/* Key Points */
.kp-row {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px solid #F5F5F7;
}
.kp-row:last-child { border-bottom: none; }
.kp-tag { font-weight: 600; color: var(--text-secondary); font-size: 13px; }
.kp-val { font-weight: 700; font-size: 15px; }
/* Risk */
.risk-item {
display: flex;
gap: 8px;
font-size: 12px;
color: var(--text-secondary);
line-height: 1.5;
margin-bottom: 8px;
}
.risk-icon { color: var(--color-neutral); margin-top: 2px; }
</style>
</head>
<body>
<div class="bg-blur"></div>
<div class="modal">
<div class="modal-header">
<div class="modal-title">
<span style="font-size:18px;">📄</span> SC2606 AI 分析报告
</div>
<div class="modal-date">2026/5/23 18:04</div>
<div class="close-btn"></div>
</div>
<div class="modal-body">
<!-- Quote -->
<div class="quote">
"SC2606 呈多周期空头排列,均线全面压制且量能极度萎缩,短期 30/60 分钟 KDJ 进入超卖区但无底背离与放量配合,整体处于缩量阴跌格局。建议观望或极轻仓逢高试空。"
</div>
<!-- Table -->
<div>
<div class="section-title">🔵 AI 思维分析</div>
<div class="table-container">
<table>
<thead>
<tr>
<th>周期</th>
<th>MACD</th>
<th>成交量</th>
<th>KDJ 状态</th>
<th>结论</th>
</tr>
</thead>
<tbody>
<tr>
<td><b>60 分钟</b></td>
<td><span class="status-badge status-down">Down</span></td>
<td>极度缩量</td>
<td><span class="status-badge status-down">超卖</span></td>
<td>空头趋势明确,下跌动能衰竭但无资金承接。</td>
</tr>
<tr>
<td><b>30 分钟</b></td>
<td><span class="status-badge status-down">Down</span></td>
<td>缩量</td>
<td><span class="status-badge status-down">超卖</span></td>
<td>超卖引发技术性反弹预期,但缺乏量能验证。</td>
</tr>
<tr>
<td><b>15 分钟</b></td>
<td><span class="status-badge status-down">Down</span></td>
<td>缩量</td>
<td><span class="status-badge status-neutral">中性偏弱</span></td>
<td>反弹力度羸弱,受制于 MA10/20 压制。</td>
</tr>
<tr>
<td><b>5 分钟</b></td>
<td><span class="status-badge status-neutral">Neutral</span></td>
<td>极度缩量</td>
<td><span class="status-badge status-neutral">中性</span></td>
<td>微观结构处于无序震荡,等待放量选择方向。</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Stats -->
<div class="stats-row">
<div class="stat-box">
<div class="stat-label">入场区间</div>
<div class="stat-val">640.5-642.5</div>
</div>
<div class="stat-box">
<div class="stat-label">止损位</div>
<div class="stat-val red">645.5</div>
</div>
<div class="stat-box">
<div class="stat-label">建议仓位</div>
<div class="stat-val">轻仓</div>
</div>
<div class="stat-box">
<div class="stat-label">纪律评分</div>
<div class="stat-val blue">4/11</div>
</div>
</div>
<!-- KDJ -->
<div>
<div class="section-title">🟣 KDJ 诊断</div>
<div class="kdj-grid">
<div class="kdj-card">
<div class="kdj-label">当前状态</div>
<div class="kdj-text">30/60 分钟超卖区</div>
</div>
<div class="kdj-card">
<div class="kdj-label">背离情况</div>
<div class="kdj-text">无底背离 (价格新低 KDJ 未新低)</div>
</div>
<div class="kdj-card">
<div class="kdj-label">钝化</div>
<div class="kdj-text"></div>
</div>
<div class="kdj-card">
<div class="kdj-label">建议</div>
<div class="kdj-text" style="font-size:11px; font-weight:400; line-height:1.4;">超卖区仅提示反弹概率,非反转信号。严禁左侧接飞刀。</div>
</div>
</div>
</div>
<!-- Key Points -->
<div>
<div class="section-title">📍 关键点位</div>
<div style="display:flex; gap:8px;">
<div class="stat-box" style="flex:1;">
<div class="stat-label">R2</div>
<div class="stat-val red">648.8</div>
</div>
<div class="stat-box" style="flex:1;">
<div class="stat-label">R1</div>
<div class="stat-val red">642.4</div>
</div>
<div class="stat-box" style="flex:1; background:rgba(0,122,255,0.08);">
<div class="stat-label" style="color:var(--color-brand);">PP</div>
<div class="stat-val blue">638.1</div>
</div>
<div class="stat-box" style="flex:1;">
<div class="stat-label">S1</div>
<div class="stat-val" style="color:var(--color-down);">631.7</div>
</div>
<div class="stat-box" style="flex:1;">
<div class="stat-label">S2</div>
<div class="stat-val" style="color:var(--color-down);">627.4</div>
</div>
</div>
</div>
<!-- Risk -->
<div>
<div class="section-title">⚠️ 风险提示</div>
<div class="risk-item">
<span class="risk-icon"></span>
<span>技术指标具有滞后性,历史表现不代表未来。</span>
</div>
<div class="risk-item">
<span class="risk-icon"></span>
<span>需结合基本面和市场情绪综合判断。</span>
</div>
<div class="risk-item">
<span class="risk-icon"></span>
<span>SC2606 为远月合约,流动性极差,买卖价差大,实际交易滑点可能远超技术测算。</span>
</div>
</div>
</div>
</div>
</body>
</html>

@ -0,0 +1,280 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>期货智析 - Mobile Detail (Apple Style)</title>
<style>
:root {
--bg-page: #F5F5F7;
--bg-card: #FFFFFF;
--text-primary: #1D1D1F;
--text-secondary: #6E6E73;
--text-tertiary: #86868B;
--color-up: #FF3B30;
--color-down: #34C759;
--color-brand: #007AFF;
--color-ai: #AF52DE;
--shadow-sm: 0 1px 3px rgba(0,0,0,0.04);
--shadow-md: 0 4px 12px rgba(0,0,0,0.06);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", "PingFang SC", sans-serif;
background-color: #E5E5E5;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
}
.phone {
width: 390px;
height: 844px;
background: var(--bg-page);
border-radius: 40px;
box-shadow: 0 20px 60px rgba(0,0,0,0.2);
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
/* Status Bar */
.status-bar { height: 44px; display: flex; justify-content: space-between; align-items: center; padding: 0 24px; font-size: 14px; font-weight: 600; color: var(--text-primary); background: var(--bg-page); z-index: 10; }
/* Nav Bar */
.nav-bar { height: 44px; padding: 0 16px; display: flex; align-items: center; justify-content: space-between; background: var(--bg-page); position: sticky; top: 0; z-index: 10; }
.nav-back { font-size: 15px; color: var(--color-brand); font-weight: 500; display: flex; align-items: center; gap: 2px; }
.nav-title { font-size: 16px; font-weight: 600; position: absolute; left: 50%; transform: translateX(-50%); }
.nav-actions { display: flex; gap: 16px; font-size: 16px; color: var(--color-brand); }
/* Content Area */
.content {
flex: 1;
overflow-y: auto;
padding-bottom: 20px;
}
.content::-webkit-scrollbar { display: none; }
/* Ticker Header */
.ticker {
padding: 16px;
background: var(--bg-card);
margin: 0 0 12px 0;
box-shadow: var(--shadow-sm);
}
.price-main { font-size: 34px; font-weight: 700; letter-spacing: -0.02em; line-height: 1; }
.price-up { color: var(--color-up); }
.price-down { color: var(--color-down); }
.change-row { display: flex; align-items: center; gap: 8px; margin-top: 6px; font-size: 14px; font-weight: 600; }
.ohlc-row { display: flex; gap: 16px; margin-top: 12px; font-size: 12px; color: var(--text-tertiary); border-top: 1px solid #F5F5F7; padding-top: 10px; }
.ohlc-item b { color: var(--text-primary); font-weight: 600; margin-left: 4px; }
/* Chart */
.chart-area {
height: 260px;
background: var(--bg-card);
margin: 0 0 12px 0;
position: relative;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-tertiary);
font-size: 14px;
}
.chart-toolbar {
position: absolute;
top: 12px;
left: 12px;
display: flex;
gap: 8px;
}
.tf-btn { padding: 4px 8px; background: rgba(255,255,255,0.8); border-radius: 6px; font-size: 11px; font-weight: 500; color: var(--text-secondary); }
.tf-btn.active { background: var(--color-brand); color: #fff; }
/* Tabs */
.tabs {
display: flex;
background: #F5F5F7;
margin: 0 16px 12px 16px;
border-radius: 8px;
padding: 2px;
}
.tab {
flex: 1;
text-align: center;
padding: 6px 0;
font-size: 13px;
font-weight: 500;
color: var(--text-secondary);
border-radius: 6px;
cursor: pointer;
}
.tab.active { background: #FFFFFF; color: var(--text-primary); font-weight: 600; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
/* Cards */
.card {
background: var(--bg-card);
margin: 0 16px 16px 16px;
padding: 16px;
border-radius: 16px;
box-shadow: var(--shadow-sm);
}
.card-title { font-size: 15px; font-weight: 700; margin-bottom: 12px; display: flex; justify-content: space-between; align-items: center; }
.ai-tag { font-size: 10px; background: rgba(175, 82, 222, 0.1); color: var(--color-ai); padding: 2px 6px; border-radius: 4px; font-weight: 600; }
.analysis-text { font-size: 13px; color: var(--text-secondary); line-height: 1.5; background: #FAFAFA; padding: 12px; border-radius: 8px; border-left: 3px solid var(--color-ai); margin-bottom: 12px; }
.data-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.data-item { display: flex; justify-content: space-between; font-size: 13px; }
.label { color: var(--text-tertiary); }
.val { font-weight: 600; }
/* Indicators Grid */
.ind-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.ind-box { background: #F5F5F7; padding: 12px; border-radius: 10px; text-align: center; }
.ind-name { font-size: 11px; color: var(--text-tertiary); margin-bottom: 4px; }
.ind-val { font-size: 14px; font-weight: 700; }
/* Key Points */
.kp-row { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #F5F5F7; font-size: 13px; }
.kp-row:last-child { border-bottom: none; }
.kp-tag { font-weight: 600; color: var(--text-secondary); }
.kp-val { font-weight: 700; }
/* History */
.history-card { margin: 0 16px 40px 16px; padding: 16px; background: var(--bg-card); border-radius: 16px; box-shadow: var(--shadow-sm); display: flex; justify-content: space-between; align-items: center; }
.hist-label { font-size: 14px; font-weight: 600; }
.hist-link { font-size: 13px; color: var(--color-brand); font-weight: 500; }
</style>
</head>
<body>
<div class="phone">
<div class="status-bar">
<span>9:41</span>
<span></span>
</div>
<div class="nav-bar">
<div class="nav-back"></div>
<div class="nav-title">原油 SC2606</div>
<div class="nav-actions">
<span>🔔</span>
<span></span>
</div>
</div>
<div class="content">
<!-- Ticker -->
<div class="ticker">
<div class="price-main price-down">644.5</div>
<div class="change-row">
<span class="price-down">↓ 13.3</span>
<span class="price-down" style="background:rgba(52,199,89,0.1); padding:2px 6px; border-radius:4px;">-2.02%</span>
</div>
<div class="ohlc-row">
<div class="ohlc-item"><b>657.8</b></div>
<div class="ohlc-item"><b class="price-up">664.8</b></div>
<div class="ohlc-item"><b class="price-down">630.2</b></div>
<div class="ohlc-item"><b>1.6M</b></div>
</div>
</div>
<!-- Chart -->
<div class="chart-area">
<div class="chart-toolbar">
<div class="tf-btn">5M</div>
<div class="tf-btn active">15M</div>
<div class="tf-btn">1H</div>
<div class="tf-btn"></div>
</div>
<div style="text-align:center;">
<div style="font-size:40px; margin-bottom:8px;"></div>
<div>K 线图区域</div>
</div>
</div>
<!-- Tabs -->
<div class="tabs">
<div class="tab active">AI 分析</div>
<div class="tab">指标</div>
<div class="tab">点位</div>
</div>
<!-- AI Card -->
<div class="card">
<div class="card-title">
<span>🧠 思维分析</span>
<span class="ai-tag">智能</span>
</div>
<div class="analysis-text">
SC2606 呈多周期空头排列,均线全面压制且量能极度萎缩。建议观望或极轻仓逢高试空。
</div>
<div class="data-grid">
<div class="data-item"><span class="label">置信度</span><span class="val">30%</span></div>
<div class="data-item"><span class="label">止损位</span><span class="val price-up">645.5</span></div>
<div class="data-item"><span class="label">入场</span><span class="val">640.5-642.5</span></div>
<div class="data-item"><span class="label">仓位</span><span class="val">轻仓</span></div>
</div>
</div>
<!-- Indicators Card -->
<div class="card">
<div class="card-title">📊 技术指标</div>
<div class="ind-grid">
<div class="ind-box">
<div class="ind-name">MACD</div>
<div class="ind-val price-down">Down</div>
</div>
<div class="ind-box">
<div class="ind-name">KDJ</div>
<div class="ind-val" style="color:var(--color-up);">超卖</div>
</div>
<div class="ind-box">
<div class="ind-name">RSI</div>
<div class="ind-val">--</div>
</div>
<div class="ind-box">
<div class="ind-name">BOLL</div>
<div class="ind-val">--</div>
</div>
</div>
</div>
<!-- Points Card -->
<div class="card">
<div class="card-title">📍 关键点位</div>
<div class="kp-row">
<span class="kp-tag">R1</span>
<span class="kp-val price-up">642.4</span>
</div>
<div class="kp-row">
<span class="kp-tag">PP (中枢)</span>
<span class="kp-val" style="color:var(--color-brand);">638.1</span>
</div>
<div class="kp-row">
<span class="kp-tag">S1</span>
<span class="kp-val price-down">631.7</span>
</div>
</div>
<!-- History -->
<div class="history-card">
<div class="hist-label">🕰 历史分析记录</div>
<div class="hist-link">查看 ></div>
</div>
</div>
</div>
</body>
</html>

@ -0,0 +1,369 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>期货智析 - Mobile Prototype (Apple Style)</title>
<style>
:root {
/* v3.0 Apple Style Colors */
--bg-page: #F5F5F7;
--bg-card: #FFFFFF;
--text-primary: #1D1D1F;
--text-secondary: #6E6E73;
--text-tertiary: #86868B;
--color-up: #FF3B30;
--color-down: #34C759;
--color-neutral: #FF9F0A;
--color-brand: #007AFF;
/* Shadows */
--shadow-sm: 0 1px 3px rgba(0,0,0,0.04);
--shadow-md: 0 4px 12px rgba(0,0,0,0.06);
/* Radius */
--radius-card: 16px;
--radius-btn: 12px;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", "PingFang SC", sans-serif;
background-color: #E5E5E5; /* Darker bg to show phone */
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
}
.phone {
width: 390px;
height: 844px;
background: var(--bg-page);
border-radius: 40px;
box-shadow: 0 20px 60px rgba(0,0,0,0.2);
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
/* Status Bar */
.status-bar {
height: 44px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 24px;
font-size: 14px;
font-weight: 600;
color: var(--text-primary);
background: var(--bg-page);
}
/* Nav Bar */
.nav-bar {
height: 56px;
padding: 0 20px;
display: flex;
align-items: center;
justify-content: space-between;
background: var(--bg-page);
position: sticky;
top: 0;
z-index: 10;
}
.nav-title { font-size: 20px; font-weight: 700; }
.nav-icons { display: flex; gap: 16px; }
.icon-btn { width: 32px; height: 32px; background: #FFFFFF; border-radius: 50%; display: flex; align-items: center; justify-content: center; box-shadow: var(--shadow-sm); font-size: 14px; cursor: pointer;}
/* Content Scroll */
.content {
flex: 1;
overflow-y: auto;
padding: 0 20px 100px 20px; /* Padding bottom for tab bar */
}
.content::-webkit-scrollbar { display: none; }
/* Filter Pills */
.filter-row {
display: flex;
gap: 8px;
overflow-x: auto;
padding-bottom: 12px;
margin-bottom: 12px;
}
.filter-row::-webkit-scrollbar { display: none; }
.f-pill {
padding: 6px 14px;
background: #FFFFFF;
border-radius: 20px;
font-size: 13px;
font-weight: 500;
color: var(--text-secondary);
white-space: nowrap;
box-shadow: var(--shadow-sm);
}
.f-pill.active { background: var(--color-brand); color: #FFFFFF; box-shadow: 0 4px 10px rgba(0,122,255,0.3); }
/* Stats Mini */
.stats-mini {
display: flex;
gap: 12px;
margin-bottom: 20px;
}
.s-card {
flex: 1;
background: #FFFFFF;
border-radius: 12px;
padding: 12px;
text-align: center;
box-shadow: var(--shadow-sm);
}
.s-val { font-size: 18px; font-weight: 700; color: var(--text-primary); }
.s-label { font-size: 10px; color: var(--text-tertiary); margin-top: 2px; }
.s-card.up .s-val { color: var(--color-down); }
.s-card.down .s-val { color: var(--color-up); }
/* List Cards */
.card {
background: var(--bg-card);
border-radius: var(--radius-card);
padding: 16px;
margin-bottom: 16px;
box-shadow: var(--shadow-md);
display: flex;
flex-direction: column;
gap: 12px;
}
.card-top { display: flex; justify-content: space-between; align-items: flex-start; }
.code-badge { background: #F5F5F7; padding: 4px 8px; border-radius: 6px; font-size: 11px; font-weight: 600; color: var(--text-tertiary); margin-bottom: 4px; display: inline-block; }
.name { font-size: 16px; font-weight: 600; }
.price-col { text-align: right; }
.price { font-size: 22px; font-weight: 700; letter-spacing: -0.02em; }
.price.up { color: var(--color-up); }
.price.down { color: var(--color-down); }
.change { font-size: 12px; font-weight: 600; }
.change.up { color: var(--color-up); }
.change.down { color: var(--color-down); }
.action-row { display: flex; justify-content: space-between; align-items: center; }
.action-tag { padding: 4px 10px; border-radius: 12px; font-size: 11px; font-weight: 600; }
.tag-watch { background: rgba(255,159,10,0.1); color: var(--color-neutral); }
.tag-do { background: rgba(52,199,89,0.1); color: var(--color-down); }
.score-bar { height: 3px; background: #F5F5F7; border-radius: 2px; width: 80px; overflow: hidden; }
.score-fill { height: 100%; border-radius: 2px; }
.card-meta { display: flex; justify-content: space-between; font-size: 12px; color: var(--text-tertiary); border-top: 1px solid #F5F5F7; padding-top: 10px; }
.meta-item span { color: var(--text-primary); font-weight: 600; }
.meta-item .red { color: var(--color-up); }
.meta-item .green { color: var(--color-down); }
/* Tab Bar */
.tab-bar {
height: 83px;
background: rgba(255,255,255,0.9);
backdrop-filter: blur(20px);
border-top: 1px solid rgba(0,0,0,0.05);
display: flex;
justify-content: space-around;
align-items: flex-start;
padding-top: 12px;
position: absolute;
bottom: 0;
width: 100%;
}
.tab-item { display: flex; flex-direction: column; align-items: center; gap: 4px; cursor: pointer; }
.tab-icon { width: 24px; height: 24px; background: #E5E5EA; border-radius: 6px; }
.tab-icon.active { background: var(--color-brand); }
.tab-label { font-size: 10px; color: var(--text-tertiary); font-weight: 500; }
.tab-label.active { color: var(--color-brand); }
</style>
</head>
<body>
<div class="phone">
<!-- Status Bar -->
<div class="status-bar">
<span>9:41</span>
<span>🔋</span>
</div>
<!-- Nav Bar -->
<div class="nav-bar">
<div class="nav-title">期货智析</div>
<div class="nav-icons">
<div class="icon-btn">🔍</div>
<div class="icon-btn"></div>
</div>
</div>
<!-- Content -->
<div class="content">
<!-- Filters -->
<div class="filter-row">
<div class="f-pill active">全部</div>
<div class="f-pill">自选</div>
<div class="f-pill">能源</div>
<div class="f-pill">金属</div>
<div class="f-pill">农产品</div>
</div>
<!-- Stats -->
<div class="stats-mini">
<div class="s-card"><div class="s-val">32</div><div class="s-label">监控</div></div>
<div class="s-card up"><div class="s-val">7</div><div class="s-label">上涨</div></div>
<div class="s-card down"><div class="s-val">10</div><div class="s-label">下跌</div></div>
<div class="s-card"><div class="s-val">15</div><div class="s-label">震荡</div></div>
</div>
<!-- List -->
<div class="card">
<div class="card-top">
<div>
<div class="code-badge">SC 原油</div>
<div class="name">SC2606</div>
</div>
<div class="price-col">
<div class="price down">644.5</div>
<div class="change down">↓ 2.02%</div>
</div>
</div>
<div class="action-row">
<div class="action-tag tag-watch">观望</div>
<div style="display:flex; align-items:center; gap:6px;">
<span style="font-size:11px; color:var(--text-tertiary);">评分</span>
<div class="score-bar"><div class="score-fill" style="width:30%; background:var(--color-up);"></div></div>
</div>
</div>
<div class="card-meta">
<div class="meta-item">压力 <span class="red">642.4</span></div>
<div class="meta-item">支撑 <span class="green">631.7</span></div>
</div>
</div>
<div class="card">
<div class="card-top">
<div>
<div class="code-badge">FU 燃油</div>
<div class="name">FU2606</div>
</div>
<div class="price-col">
<div class="price up">4,680</div>
<div class="change up">↑ 1.56%</div>
</div>
</div>
<div class="action-row">
<div class="action-tag tag-do">做多</div>
<div style="display:flex; align-items:center; gap:6px;">
<span style="font-size:11px; color:var(--text-tertiary);">评分</span>
<div class="score-bar"><div class="score-fill" style="width:65%; background:var(--color-down);"></div></div>
</div>
</div>
<div class="card-meta">
<div class="meta-item">压力 <span class="red">4,600</span></div>
<div class="meta-item">支撑 <span class="green">4,530</span></div>
</div>
</div>
<div class="card">
<div class="card-top">
<div>
<div class="code-badge">AG 沪银</div>
<div class="name">AG2606</div>
</div>
<div class="price-col">
<div class="price up">18,643</div>
<div class="change up">↑ 2.27%</div>
</div>
</div>
<div class="action-row">
<div class="action-tag tag-do">做多 (反弹)</div>
<div style="display:flex; align-items:center; gap:6px;">
<span style="font-size:11px; color:var(--text-tertiary);">评分</span>
<div class="score-bar"><div class="score-fill" style="width:60%; background:var(--color-neutral);"></div></div>
</div>
</div>
<div class="card-meta">
<div class="meta-item">压力 <span class="red">18,685</span></div>
<div class="meta-item">支撑 <span class="green">18,584</span></div>
</div>
</div>
<div class="card">
<div class="card-top">
<div>
<div class="code-badge">AU 沪金</div>
<div class="name">AU2606</div>
</div>
<div class="price-col">
<div class="price up">993.34</div>
<div class="change up">↑ 0.44%</div>
</div>
</div>
<div class="action-row">
<div class="action-tag tag-watch">观望</div>
<div style="display:flex; align-items:center; gap:6px;">
<span style="font-size:11px; color:var(--text-tertiary);">评分</span>
<div class="score-bar"><div class="score-fill" style="width:40%; background:var(--color-up);"></div></div>
</div>
</div>
<div class="card-meta">
<div class="meta-item">压力 <span class="red">993</span></div>
<div class="meta-item">支撑 <span class="green">989.5</span></div>
</div>
</div>
<div class="card">
<div class="card-top">
<div>
<div class="code-badge">CU 沪铜</div>
<div class="name">CU2606</div>
</div>
<div class="price-col">
<div class="price up">104,770</div>
<div class="change up">↑ 1.05%</div>
</div>
</div>
<div class="action-row">
<div class="action-tag tag-do">做多</div>
<div style="display:flex; align-items:center; gap:6px;">
<span style="font-size:11px; color:var(--text-tertiary);">评分</span>
<div class="score-bar"><div class="score-fill" style="width:65%; background:var(--color-down);"></div></div>
</div>
</div>
<div class="card-meta">
<div class="meta-item">压力 <span class="red">105,250</span></div>
<div class="meta-item">支撑 <span class="green">104,450</span></div>
</div>
</div>
</div>
<!-- Tab Bar -->
<div class="tab-bar">
<div class="tab-item">
<div class="tab-icon active"></div>
<div class="tab-label active">首页</div>
</div>
<div class="tab-item">
<div class="tab-icon"></div>
<div class="tab-label">自选</div>
</div>
<div class="tab-item">
<div class="tab-icon"></div>
<div class="tab-label">分析</div>
</div>
<div class="tab-item">
<div class="tab-icon"></div>
<div class="tab-label">我的</div>
</div>
</div>
</div>
</body>
</html>

@ -0,0 +1,247 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>期货智析 - Mobile Report (Apple Style)</title>
<style>
:root {
--bg-page: #F5F5F7;
--bg-card: #FFFFFF;
--text-primary: #1D1D1F;
--text-secondary: #6E6E73;
--text-tertiary: #86868B;
--color-up: #FF3B30;
--color-down: #34C759;
--color-brand: #007AFF;
--color-ai: #AF52DE;
--shadow-sheet: 0 -10px 40px rgba(0,0,0,0.1);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", "PingFang SC", sans-serif;
background-color: #E5E5E5;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
}
.phone {
width: 390px;
height: 844px;
background: var(--bg-page);
border-radius: 40px;
box-shadow: 0 20px 60px rgba(0,0,0,0.2);
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
/* Background Content (Blurred) */
.bg-content {
padding: 60px 20px;
filter: blur(4px) brightness(0.9);
pointer-events: none;
user-select: none;
}
.skeleton { background: #D1D1D6; border-radius: 8px; margin-bottom: 16px; }
.sk-title { height: 30px; width: 60%; margin-bottom: 24px; }
.sk-card { height: 150px; width: 100%; margin-bottom: 16px; }
.sk-text { height: 12px; width: 100%; margin-bottom: 8px; }
.sk-text.short { width: 40%; }
/* Modal Overlay */
.overlay {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0,0,0,0.3);
z-index: 10;
}
/* Bottom Sheet */
.sheet {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: var(--bg-card);
border-radius: 24px 24px 0 0;
box-shadow: var(--shadow-sheet);
z-index: 20;
max-height: 85%;
display: flex;
flex-direction: column;
animation: slideUp 0.3s ease-out;
}
@keyframes slideUp {
from { transform: translateY(100%); }
to { transform: translateY(0); }
}
/* Handle */
.handle-bar {
width: 40px;
height: 5px;
background: #E5E5EA;
border-radius: 3px;
margin: 12px auto 0 auto;
}
/* Header */
.sheet-header {
padding: 16px 20px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid rgba(0,0,0,0.05);
}
.sheet-title { font-size: 18px; font-weight: 700; display: flex; align-items: center; gap: 8px; }
.close-btn { width: 24px; height: 24px; background: #F5F5F7; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: var(--text-secondary); font-size: 12px; }
/* Body */
.sheet-body {
padding: 20px;
overflow-y: auto;
}
.sheet-body::-webkit-scrollbar { display: none; }
.quote {
background: rgba(175, 82, 222, 0.08);
border-left: 3px solid var(--color-ai);
padding: 12px;
border-radius: 0 8px 8px 0;
font-size: 13px;
color: var(--text-secondary);
line-height: 1.5;
margin-bottom: 20px;
}
.section-title { font-size: 15px; font-weight: 700; color: var(--color-ai); margin-bottom: 10px; display: flex; align-items: center; gap: 6px; }
/* Table */
.table-wrap {
background: #F5F5F7;
border-radius: 12px;
padding: 2px;
margin-bottom: 20px;
}
.row {
display: flex;
justify-content: space-between;
padding: 10px 12px;
border-bottom: 1px solid #E5E5EA;
font-size: 12px;
}
.row:last-child { border-bottom: none; }
.row-label { color: var(--text-tertiary); font-weight: 500; }
.row-val { font-weight: 700; }
.tag { padding: 2px 6px; border-radius: 4px; font-size: 10px; font-weight: 600; }
.tag-down { background: rgba(52,199,89,0.15); color: var(--color-down); }
.tag-up { background: rgba(255,59,48,0.15); color: var(--color-up); }
/* Stats Grid */
.stats-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; margin-bottom: 20px; }
.s-box { background: #F5F5F7; padding: 12px; border-radius: 10px; text-align: center; }
.s-label { font-size: 11px; color: var(--text-tertiary); margin-bottom: 4px; }
.s-val { font-size: 16px; font-weight: 700; }
/* Risk */
.risk-list { display: flex; flex-direction: column; gap: 8px; }
.risk-item { display: flex; gap: 8px; font-size: 12px; color: var(--text-secondary); line-height: 1.4; }
.risk-icon { color: #FF9500; font-size: 14px; }
</style>
</head>
<body>
<div class="phone">
<!-- Blurred Background -->
<div class="bg-content">
<div class="skeleton sk-title"></div>
<div class="skeleton sk-card"></div>
<div class="skeleton sk-card"></div>
<div class="skeleton sk-text"></div>
<div class="skeleton sk-text short"></div>
</div>
<!-- Overlay -->
<div class="overlay"></div>
<!-- Bottom Sheet -->
<div class="sheet">
<div class="handle-bar"></div>
<div class="sheet-header">
<div class="sheet-title">
<span></span> AI 分析报告
</div>
<div class="close-btn"></div>
</div>
<div class="sheet-body">
<div class="quote">
"SC2606 呈多周期空头排列,均线全面压制且量能极度萎缩。建议观望或极轻仓逢高试空。"
</div>
<div class="section-title">🔵 思维分析</div>
<div class="table-wrap">
<div class="row">
<span class="row-label">60M 周期</span>
<span class="row-val"><span class="tag tag-down">超卖</span> 空头趋势明确</span>
</div>
<div class="row">
<span class="row-label">30M 周期</span>
<span class="row-val"><span class="tag tag-down">超卖</span> 缺乏量能验证</span>
</div>
<div class="row">
<span class="row-label">15M 周期</span>
<span class="row-val"><span class="tag" style="background:#E5E5EA; color:#6E6E73;">中性</span> 受均线压制</span>
</div>
</div>
<div class="stats-grid">
<div class="s-box">
<div class="s-label">入场区间</div>
<div class="s-val">640.5-642.5</div>
</div>
<div class="s-box">
<div class="s-label">止损位</div>
<div class="s-val" style="color:var(--color-up);">645.5</div>
</div>
<div class="s-box">
<div class="s-label">仓位</div>
<div class="s-val">轻仓</div>
</div>
<div class="s-box">
<div class="s-label">评分</div>
<div class="s-val" style="color:var(--color-brand);">4/11</div>
</div>
</div>
<div class="section-title">⚠️ 风险提示</div>
<div class="risk-list">
<div class="risk-item">
<span class="risk-icon"></span>
<span>技术指标具有滞后性,历史表现不代表未来。</span>
</div>
<div class="risk-item">
<span class="risk-icon"></span>
<span>SC2606 为远月合约,流动性极差,注意滑点风险。</span>
</div>
</div>
<div style="height: 40px;"></div> <!-- Bottom padding for scroll -->
</div>
</div>
</div>
</body>
</html>

@ -0,0 +1,242 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>期货智析 - Tablet Detail</title>
<style>
:root {
--bg-page: #F5F5F7;
--bg-card: #FFFFFF;
--text-primary: #1D1D1F;
--text-secondary: #6E6E73;
--text-tertiary: #86868B;
--color-up: #FF3B30;
--color-down: #34C759;
--color-brand: #007AFF;
--color-ai: #AF52DE;
--shadow-sm: 0 1px 3px rgba(0,0,0,0.04);
--shadow-md: 0 4px 12px rgba(0,0,0,0.06);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", "PingFang SC", sans-serif;
background-color: #E5E5E5;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
}
.tablet {
width: 1024px;
height: 640px;
background: var(--bg-page);
border-radius: 24px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
border: 8px solid #FFFFFF;
}
/* Header */
.header {
height: 56px;
background: rgba(255,255,255,0.8);
backdrop-filter: blur(20px);
border-bottom: 1px solid rgba(0,0,0,0.05);
display: flex;
align-items: center;
padding: 0 20px;
justify-content: space-between;
}
.back-btn { font-size: 14px; color: var(--color-brand); font-weight: 500; display: flex; align-items: center; gap: 4px; }
.title { font-size: 16px; font-weight: 600; position: absolute; left: 50%; transform: translateX(-50%); }
.actions { display: flex; gap: 12px; font-size: 16px; color: var(--color-brand); }
/* Content Layout */
.content {
flex: 1;
display: grid;
grid-template-columns: 1.2fr 1fr; /* Left Chart, Right Data */
gap: 16px;
padding: 16px 20px;
overflow: hidden;
}
/* Left Column (Chart) */
.chart-section {
background: var(--bg-card);
border-radius: 16px;
box-shadow: var(--shadow-sm);
display: flex;
flex-direction: column;
overflow: hidden;
}
.chart-header {
padding: 16px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #F5F5F7;
}
.price-display { display: flex; align-items: baseline; gap: 12px; }
.price-main { font-size: 28px; font-weight: 700; color: var(--color-down); }
.price-change { font-size: 14px; font-weight: 600; color: var(--color-down); background: rgba(52,199,89,0.1); padding: 2px 6px; border-radius: 4px; }
.timeframes { display: flex; gap: 4px; background: #F5F5F7; padding: 4px; border-radius: 8px; }
.tf { padding: 4px 10px; border-radius: 6px; font-size: 12px; font-weight: 500; color: var(--text-tertiary); cursor: pointer; }
.tf.active { background: #FFFFFF; color: var(--text-primary); box-shadow: 0 1px 2px rgba(0,0,0,0.1); }
.chart-area {
flex: 1;
background: #FAFAFA;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-tertiary);
font-size: 14px;
}
/* Right Column (Panels) */
.panels {
display: flex;
flex-direction: column;
gap: 16px;
overflow-y: auto;
padding-right: 4px;
}
.panels::-webkit-scrollbar { width: 4px; }
.panels::-webkit-scrollbar-thumb { background: #E5E5EA; border-radius: 2px; }
.panel-card {
background: var(--bg-card);
border-radius: 16px;
padding: 16px;
box-shadow: var(--shadow-sm);
}
.panel-title { font-size: 14px; font-weight: 700; margin-bottom: 12px; display: flex; justify-content: space-between; align-items: center; }
.ai-tag { font-size: 10px; background: rgba(175, 82, 222, 0.1); color: var(--color-ai); padding: 2px 6px; border-radius: 4px; font-weight: 600; }
.analysis-text { font-size: 12px; color: var(--text-secondary); line-height: 1.5; background: #FAFAFA; padding: 10px; border-radius: 8px; border-left: 3px solid var(--color-ai); margin-bottom: 12px; }
.data-row { display: flex; justify-content: space-between; font-size: 12px; margin-bottom: 8px; }
.data-label { color: var(--text-tertiary); }
.data-val { font-weight: 600; }
.ind-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.ind-box { background: #F5F5F7; padding: 10px; border-radius: 8px; text-align: center; }
.ind-name { font-size: 10px; color: var(--text-tertiary); margin-bottom: 2px; }
.ind-val { font-size: 12px; font-weight: 700; }
.kp-row { display: flex; justify-content: space-between; padding: 6px 0; border-bottom: 1px solid #F5F5F7; font-size: 12px; }
.kp-row:last-child { border-bottom: none; }
.kp-tag { font-weight: 600; color: var(--text-secondary); }
.kp-val { font-weight: 700; }
/* History Bar */
.history-bar {
height: 48px;
background: var(--bg-card);
border-top: 1px solid rgba(0,0,0,0.05);
display: flex;
align-items: center;
padding: 0 20px;
justify-content: space-between;
font-size: 13px;
color: var(--text-secondary);
}
.link { color: var(--color-brand); font-weight: 500; }
</style>
</head>
<body>
<div class="tablet">
<div class="header">
<div class="back-btn">← 返回</div>
<div class="title">原油 SC2606</div>
<div class="actions">
<span>🔔</span>
<span></span>
</div>
</div>
<div class="content">
<!-- Left Chart -->
<div class="chart-section">
<div class="chart-header">
<div class="price-display">
<span class="price-main">644.5</span>
<span class="price-change">↓ -2.02%</span>
</div>
<div class="timeframes">
<div class="tf">5M</div>
<div class="tf active">15M</div>
<div class="tf">30M</div>
<div class="tf">1H</div>
</div>
</div>
<div class="chart-area">
<div style="text-align:center;">
<div style="font-size:40px; margin-bottom:8px;"></div>
<div>K 线图区域</div>
<div style="font-size:11px; margin-top:4px; color:#86868B;">MA5: 652 | MA10: 655 | MA20: 660</div>
</div>
</div>
</div>
<!-- Right Panels -->
<div class="panels">
<!-- AI -->
<div class="panel-card">
<div class="panel-title"><span>🧠 AI 分析</span><span class="ai-tag">智能</span></div>
<div class="analysis-text">SC2606 呈多周期空头排列,建议观望或极轻仓逢高试空。</div>
<div class="data-row"><span class="data-label">置信度</span><span class="data-val">30%</span></div>
<div class="data-row"><span class="data-label">止损位</span><span class="data-val" style="color:var(--color-up);">645.5</span></div>
</div>
<!-- Indicators -->
<div class="panel-card">
<div class="panel-title">📊 技术指标</div>
<div class="ind-grid">
<div class="ind-box"><div class="ind-name">MACD</div><div class="ind-val" style="color:var(--color-down);">Down</div></div>
<div class="ind-box"><div class="ind-name">KDJ</div><div class="ind-val" style="color:var(--color-up);">超卖</div></div>
<div class="ind-box"><div class="ind-name">RSI</div><div class="ind-val">--</div></div>
<div class="ind-box"><div class="ind-name">BOLL</div><div class="ind-val">--</div></div>
</div>
</div>
<!-- Key Points -->
<div class="panel-card">
<div class="panel-title"> 关键点位</div>
<div class="kp-row"><span class="kp-tag">R1</span><span class="kp-val" style="color:var(--color-up);">642.4</span></div>
<div class="kp-row"><span class="kp-tag">PP</span><span class="kp-val" style="color:var(--color-brand);">638.1</span></div>
<div class="kp-row"><span class="kp-tag">S1</span><span class="kp-val" style="color:var(--color-down);">631.7</span></div>
</div>
<!-- Trend -->
<div class="panel-card">
<div class="panel-title">🕒 多周期</div>
<div style="display:flex; gap:6px; flex-wrap:wrap;">
<span style="padding:4px 8px; background:rgba(255,59,48,0.1); color:var(--color-up); border-radius:4px; font-size:11px; font-weight:500;">60M 偏空</span>
<span style="padding:4px 8px; background:rgba(255,159,10,0.1); color:var(--color-neutral); border-radius:4px; font-size:11px; font-weight:500;">30M 震荡</span>
<span style="padding:4px 8px; background:rgba(255,59,48,0.1); color:var(--color-up); border-radius:4px; font-size:11px; font-weight:500;">15M 偏空</span>
</div>
</div>
</div>
</div>
<div class="history-bar">
<span>🕰 历史分析记录SC2606 呈多周期空头排列...</span>
<span class="link">查看全部 →</span>
</div>
</div>
</body>
</html>

@ -0,0 +1,348 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>期货智析 - Tablet List (Compact 3-Col)</title>
<style>
:root {
--bg-page: #F5F5F7;
--bg-card: #FFFFFF;
--text-primary: #1D1D1F;
--text-secondary: #6E6E73;
--text-tertiary: #86868B;
--color-up: #FF3B30;
--color-down: #34C759;
--color-neutral: #FF9F0A;
--color-brand: #007AFF;
--shadow-sm: 0 1px 3px rgba(0,0,0,0.04);
--shadow-md: 0 4px 12px rgba(0,0,0,0.06);
--radius-card: 16px; /* Slightly smaller radius for compact */
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", "PingFang SC", sans-serif;
background-color: #E5E5E5;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
}
.tablet {
width: 1024px;
height: 640px;
background: var(--bg-page);
border-radius: 24px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
border: 8px solid #FFFFFF;
}
/* Header */
.header {
height: 56px;
background: rgba(255,255,255,0.8);
backdrop-filter: blur(20px);
border-bottom: 1px solid rgba(0,0,0,0.05);
display: flex;
align-items: center;
padding: 0 20px;
justify-content: space-between;
}
.logo { font-size: 16px; font-weight: 700; }
.search-bar {
background: #F5F5F7;
border-radius: 8px;
padding: 6px 12px;
width: 300px;
font-size: 13px;
color: var(--text-tertiary);
display: flex;
align-items: center;
gap: 8px;
}
.user-avatar { width: 32px; height: 32px; background: #E5E5EA; border-radius: 50%; }
/* Content */
.content {
flex: 1;
padding: 16px 20px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 16px;
}
/* Stats Row (Compact) */
.stats-row {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
}
.stat-card {
background: var(--bg-card);
border-radius: 12px;
padding: 12px;
display: flex;
align-items: center;
gap: 10px;
box-shadow: var(--shadow-sm);
}
.stat-icon {
width: 32px; height: 32px;
border-radius: 8px;
display: flex; align-items: center; justify-content: center;
font-size: 14px;
}
.stat-icon.blue { background: rgba(0,122,255,0.1); color: var(--color-brand); }
.stat-icon.green { background: rgba(52,199,89,0.1); color: var(--color-down); }
.stat-icon.red { background: rgba(255,59,48,0.1); color: var(--color-up); }
.stat-icon.orange { background: rgba(255,159,10,0.1); color: var(--color-neutral); }
.stat-val { font-size: 18px; font-weight: 700; line-height: 1.2; }
.stat-label { font-size: 11px; color: var(--text-secondary); }
/* Filters */
.filters { display: flex; gap: 8px; }
.pill {
padding: 6px 14px;
border-radius: 16px;
font-size: 12px;
font-weight: 500;
background: #FFFFFF;
color: var(--text-secondary);
box-shadow: var(--shadow-sm);
}
.pill.active { background: var(--color-brand); color: #FFFFFF; }
/* Grid (3 Columns) */
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
}
/* Compact Card */
.card {
background: var(--bg-card);
border-radius: var(--radius-card);
padding: 16px;
box-shadow: var(--shadow-sm);
transition: transform 0.2s, box-shadow 0.2s;
cursor: pointer;
display: flex;
flex-direction: column;
gap: 12px;
}
.card:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
.card-header { display: flex; justify-content: space-between; align-items: flex-start; }
.code-box {
width: 32px; height: 32px;
background: #F5F5F7;
border-radius: 8px;
display: flex; align-items: center; justify-content: center;
font-size: 11px; font-weight: 600; color: var(--text-tertiary);
}
.price-area { text-align: right; }
.price { font-size: 20px; font-weight: 700; letter-spacing: -0.01em; line-height: 1; }
.price.up { color: var(--color-up); }
.price.down { color: var(--color-down); }
.change { font-size: 11px; font-weight: 600; margin-top: 2px; }
.change.up { color: var(--color-up); }
.change.down { color: var(--color-down); }
.action-pill {
display: inline-block;
padding: 4px 10px;
border-radius: 12px;
font-size: 10px;
font-weight: 600;
width: fit-content;
}
.action-pill.do-more { background: rgba(52,199,89,0.1); color: var(--color-down); }
.action-pill.watch { background: rgba(255,159,10,0.1); color: var(--color-neutral); }
.metrics { display: flex; gap: 12px; }
.metric { flex: 1; }
.metric-label { font-size: 10px; color: var(--text-tertiary); margin-bottom: 4px; display: flex; justify-content: space-between; }
.bar-bg { height: 3px; background: #F5F5F7; border-radius: 2px; overflow: hidden; }
.bar-fill { height: 100%; border-radius: 2px; }
.timeframes { display: flex; gap: 6px; }
.tf { padding: 3px 8px; border-radius: 6px; font-size: 10px; color: var(--text-tertiary); background: #F5F5F7; }
.tf.active { background: #E8F2FF; color: var(--color-brand); font-weight: 500; }
.card-footer { display: flex; justify-content: space-between; align-items: center; border-top: 1px solid #F5F5F7; padding-top: 10px; font-size: 11px; }
.support-resist { color: var(--text-tertiary); }
.support-resist span { margin-right: 8px; }
.support-resist .red { color: var(--color-up); }
.support-resist .green { color: var(--color-down); }
.link { color: var(--color-brand); font-weight: 500; text-decoration: none; }
/* Bottom Nav */
.nav-bar {
height: 64px;
background: rgba(255,255,255,0.95);
border-top: 1px solid rgba(0,0,0,0.05);
display: flex;
justify-content: space-around;
align-items: center;
}
.nav-item { display: flex; flex-direction: column; align-items: center; gap: 2px; color: var(--text-tertiary); font-size: 10px; }
.nav-item.active { color: var(--color-brand); }
.nav-icon { width: 20px; height: 20px; background: #E5E5EA; border-radius: 4px; }
.nav-item.active .nav-icon { background: var(--color-brand); }
.fab {
width: 48px; height: 48px;
background: var(--color-brand);
border-radius: 14px;
display: flex; align-items: center; justify-content: center;
color: white; font-size: 20px;
box-shadow: 0 4px 12px rgba(0,122,255,0.3);
margin-top: -24px;
}
</style>
</head>
<body>
<div class="tablet">
<div class="header">
<div class="logo">◈ 期货智析</div>
<div class="search-bar">🔍 搜索品种...</div>
<div class="user-avatar"></div>
</div>
<div class="content">
<div class="stats-row">
<div class="stat-card">
<div class="stat-icon blue"></div>
<div><div class="stat-val">32</div><div class="stat-label">监控</div></div>
</div>
<div class="stat-card">
<div class="stat-icon green"></div>
<div><div class="stat-val">7</div><div class="stat-label">上涨</div></div>
</div>
<div class="stat-card">
<div class="stat-icon red"></div>
<div><div class="stat-val">10</div><div class="stat-label">下跌</div></div>
</div>
<div class="stat-card">
<div class="stat-icon orange"></div>
<div><div class="stat-val">15</div><div class="stat-label">震荡</div></div>
</div>
</div>
<div class="filters">
<div class="pill active">全部</div>
<div class="pill">能源</div>
<div class="pill">金属</div>
<div class="pill">农产品</div>
</div>
<div class="grid">
<!-- 6 Cards -->
<div class="card">
<div class="card-header">
<div class="code-box">SC</div>
<div class="price-area"><div class="price down">644.5</div><div class="change down">↓ 2.02%</div></div>
</div>
<div class="action-pill watch">观望</div>
<div class="metrics">
<div class="metric"><div class="metric-label">成功率 <b>60%</b></div><div class="bar-bg"><div class="bar-fill" style="width:60%; background:var(--color-neutral);"></div></div></div>
<div class="metric"><div class="metric-label">评分 <b>30</b></div><div class="bar-bg"><div class="bar-fill" style="width:30%; background:var(--color-up);"></div></div></div>
</div>
<div class="timeframes"><div class="tf">5M</div><div class="tf active">15M</div><div class="tf">30M</div></div>
<div class="card-footer"><div class="support-resist"><span><b class="red">642</b></span><span><b class="green">631</b></span></div><a href="#" class="link">详情 →</a></div>
</div>
<div class="card">
<div class="card-header">
<div class="code-box">FU</div>
<div class="price-area"><div class="price up">4,680</div><div class="change up">↑ 1.56%</div></div>
</div>
<div class="action-pill do-more">做多</div>
<div class="metrics">
<div class="metric"><div class="metric-label">成功率 <b>85%</b></div><div class="bar-bg"><div class="bar-fill" style="width:85%; background:var(--color-down);"></div></div></div>
<div class="metric"><div class="metric-label">评分 <b>65</b></div><div class="bar-bg"><div class="bar-fill" style="width:65%; background:var(--color-down);"></div></div></div>
</div>
<div class="timeframes"><div class="tf">5M</div><div class="tf active">15M</div><div class="tf">30M</div></div>
<div class="card-footer"><div class="support-resist"><span><b class="red">4.6k</b></span><span><b class="green">4.5k</b></span></div><a href="#" class="link">详情 →</a></div>
</div>
<div class="card">
<div class="card-header">
<div class="code-box">AG</div>
<div class="price-area"><div class="price up">18,643</div><div class="change up">↑ 2.27%</div></div>
</div>
<div class="action-pill do-more">做多</div>
<div class="metrics">
<div class="metric"><div class="metric-label">成功率 <b>60%</b></div><div class="bar-bg"><div class="bar-fill" style="width:60%; background:var(--color-neutral);"></div></div></div>
<div class="metric"><div class="metric-label">评分 <b>60</b></div><div class="bar-bg"><div class="bar-fill" style="width:60%; background:var(--color-neutral);"></div></div></div>
</div>
<div class="timeframes"><div class="tf">5M</div><div class="tf active">15M</div><div class="tf">30M</div></div>
<div class="card-footer"><div class="support-resist"><span><b class="red">18.6k</b></span><span><b class="green">18.5k</b></span></div><a href="#" class="link">详情 →</a></div>
</div>
<div class="card">
<div class="card-header">
<div class="code-box">AU</div>
<div class="price-area"><div class="price up">993.34</div><div class="change up">↑ 0.44%</div></div>
</div>
<div class="action-pill watch">观望</div>
<div class="metrics">
<div class="metric"><div class="metric-label">成功率 <b>60%</b></div><div class="bar-bg"><div class="bar-fill" style="width:60%; background:var(--color-neutral);"></div></div></div>
<div class="metric"><div class="metric-label">评分 <b>40</b></div><div class="bar-bg"><div class="bar-fill" style="width:40%; background:var(--color-up);"></div></div></div>
</div>
<div class="timeframes"><div class="tf">5M</div><div class="tf active">15M</div><div class="tf">30M</div></div>
<div class="card-footer"><div class="support-resist"><span><b class="red">993</b></span><span><b class="green">989</b></span></div><a href="#" class="link">详情 →</a></div>
</div>
<div class="card">
<div class="card-header">
<div class="code-box">CU</div>
<div class="price-area"><div class="price up">104,770</div><div class="change up">↑ 1.05%</div></div>
</div>
<div class="action-pill do-more">做多</div>
<div class="metrics">
<div class="metric"><div class="metric-label">成功率 <b>85%</b></div><div class="bar-bg"><div class="bar-fill" style="width:85%; background:var(--color-down);"></div></div></div>
<div class="metric"><div class="metric-label">评分 <b>65</b></div><div class="bar-bg"><div class="bar-fill" style="width:65%; background:var(--color-down);"></div></div></div>
</div>
<div class="timeframes"><div class="tf">5M</div><div class="tf active">15M</div><div class="tf">30M</div></div>
<div class="card-footer"><div class="support-resist"><span><b class="red">105k</b></span><span><b class="green">104k</b></span></div><a href="#" class="link">详情 →</a></div>
</div>
<div class="card">
<div class="card-header">
<div class="code-box">NI</div>
<div class="price-area"><div class="price down">143,330</div><div class="change down">↓ 0.31%</div></div>
</div>
<div class="action-pill watch">观望</div>
<div class="metrics">
<div class="metric"><div class="metric-label">成功率 <b>60%</b></div><div class="bar-bg"><div class="bar-fill" style="width:60%; background:var(--color-neutral);"></div></div></div>
<div class="metric"><div class="metric-label">评分 <b>45</b></div><div class="bar-bg"><div class="bar-fill" style="width:45%; background:var(--color-up);"></div></div></div>
</div>
<div class="timeframes"><div class="tf">5M</div><div class="tf active">15M</div><div class="tf">30M</div></div>
<div class="card-footer"><div class="support-resist"><span><b class="red">144k</b></span><span><b class="green">143k</b></span></div><a href="#" class="link">详情 →</a></div>
</div>
</div>
</div>
<div class="nav-bar">
<div class="nav-item active"><div class="nav-icon"></div><span>首页</span></div>
<div class="nav-item"><div class="nav-icon"></div><span>自选</span></div>
<div class="fab">+</div>
<div class="nav-item"><div class="nav-icon"></div><span>分析</span></div>
<div class="nav-item"><div class="nav-icon"></div><span>我的</span></div>
</div>
</div>
</body>
</html>

@ -0,0 +1,209 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>期货智析 - Tablet Report (Right Drawer)</title>
<style>
:root {
--bg-page: #F5F5F7;
--bg-card: #FFFFFF;
--text-primary: #1D1D1F;
--text-secondary: #6E6E73;
--text-tertiary: #86868B;
--color-up: #FF3B30;
--color-down: #34C759;
--color-brand: #007AFF;
--color-ai: #AF52DE;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", "PingFang SC", sans-serif;
background-color: #E5E5E5;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
}
.tablet {
width: 1024px;
height: 640px;
background: var(--bg-page);
border-radius: 24px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
border: 8px solid #FFFFFF;
}
/* Background (Blurred) */
.bg-content {
padding: 60px 20px;
filter: blur(4px) brightness(0.9);
pointer-events: none;
}
.skeleton { background: #D1D1D6; border-radius: 8px; margin-bottom: 16px; }
.sk-title { height: 30px; width: 40%; margin-bottom: 24px; }
.sk-card { height: 120px; width: 100%; margin-bottom: 16px; }
.sk-text { height: 12px; width: 100%; margin-bottom: 8px; }
/* Right Drawer */
.drawer {
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 450px;
background: var(--bg-card);
box-shadow: -10px 0 40px rgba(0,0,0,0.1);
z-index: 20;
display: flex;
flex-direction: column;
animation: slideLeft 0.3s ease-out;
}
@keyframes slideLeft {
from { transform: translateX(100%); }
to { transform: translateX(0); }
}
.drawer-header {
padding: 20px;
border-bottom: 1px solid rgba(0,0,0,0.05);
display: flex;
justify-content: space-between;
align-items: center;
}
.drawer-title { font-size: 18px; font-weight: 700; display: flex; align-items: center; gap: 8px; }
.close-btn { width: 28px; height: 28px; background: #F5F5F7; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: var(--text-secondary); font-size: 14px; cursor: pointer; }
.drawer-body {
padding: 20px;
overflow-y: auto;
flex: 1;
}
.drawer-body::-webkit-scrollbar { width: 4px; }
.drawer-body::-webkit-scrollbar-thumb { background: #E5E5EA; border-radius: 2px; }
.quote {
background: rgba(175, 82, 222, 0.08);
border-left: 3px solid var(--color-ai);
padding: 12px;
border-radius: 0 8px 8px 0;
font-size: 13px;
color: var(--text-secondary);
line-height: 1.5;
margin-bottom: 20px;
}
.section-title { font-size: 14px; font-weight: 700; color: var(--color-ai); margin-bottom: 10px; display: flex; align-items: center; gap: 6px; }
/* Table */
.table-wrap { background: #F5F5F7; border-radius: 10px; padding: 2px; margin-bottom: 20px; }
.row { display: flex; justify-content: space-between; padding: 8px 12px; border-bottom: 1px solid #E5E5EA; font-size: 12px; }
.row:last-child { border-bottom: none; }
.row-label { color: var(--text-tertiary); font-weight: 500; }
.row-val { font-weight: 700; }
.tag { padding: 2px 6px; border-radius: 4px; font-size: 10px; font-weight: 600; }
.tag-down { background: rgba(52,199,89,0.15); color: var(--color-down); }
.tag-up { background: rgba(255,59,48,0.15); color: var(--color-up); }
/* Stats Grid */
.stats-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; margin-bottom: 20px; }
.s-box { background: #F5F5F7; padding: 12px; border-radius: 10px; text-align: center; }
.s-label { font-size: 11px; color: var(--text-tertiary); margin-bottom: 4px; }
.s-val { font-size: 16px; font-weight: 700; }
/* Risk */
.risk-list { display: flex; flex-direction: column; gap: 8px; }
.risk-item { display: flex; gap: 8px; font-size: 12px; color: var(--text-secondary); line-height: 1.4; }
.risk-icon { color: #FF9500; font-size: 14px; }
</style>
</head>
<body>
<div class="tablet">
<!-- Blurred Background -->
<div class="bg-content">
<div class="skeleton sk-title"></div>
<div class="skeleton sk-card"></div>
<div class="skeleton sk-card"></div>
<div class="skeleton sk-text"></div>
<div class="skeleton sk-text"></div>
</div>
<!-- Right Drawer -->
<div class="drawer">
<div class="drawer-header">
<div class="drawer-title">
<span></span> AI 分析报告
</div>
<div class="close-btn"></div>
</div>
<div class="drawer-body">
<div class="quote">
"SC2606 呈多周期空头排列,均线全面压制且量能极度萎缩。建议观望或极轻仓逢高试空。"
</div>
<div class="section-title">🔵 思维分析</div>
<div class="table-wrap">
<div class="row">
<span class="row-label">60M 周期</span>
<span class="row-val"><span class="tag tag-down">超卖</span> 空头趋势明确</span>
</div>
<div class="row">
<span class="row-label">30M 周期</span>
<span class="row-val"><span class="tag tag-down">超卖</span> 缺乏量能验证</span>
</div>
<div class="row">
<span class="row-label">15M 周期</span>
<span class="row-val"><span class="tag" style="background:#E5E5EA; color:#6E6E73;">中性</span> 受均线压制</span>
</div>
</div>
<div class="stats-grid">
<div class="s-box">
<div class="s-label">入场区间</div>
<div class="s-val">640.5-642.5</div>
</div>
<div class="s-box">
<div class="s-label">止损位</div>
<div class="s-val" style="color:var(--color-up);">645.5</div>
</div>
<div class="s-box">
<div class="s-label">仓位</div>
<div class="s-val">轻仓</div>
</div>
<div class="s-box">
<div class="s-label">评分</div>
<div class="s-val" style="color:var(--color-brand);">4/11</div>
</div>
</div>
<div class="section-title">⚠️ 风险提示</div>
<div class="risk-list">
<div class="risk-item">
<span class="risk-icon"></span>
<span>技术指标具有滞后性,历史表现不代表未来。</span>
</div>
<div class="risk-item">
<span class="risk-icon"></span>
<span>SC2606 为远月合约,流动性极差,注意滑点风险。</span>
</div>
</div>
<div style="height: 40px;"></div>
</div>
</div>
</div>
</body>
</html>

@ -0,0 +1,351 @@
# 期货智析 — UI 设计规范 v2.0 (Dark Terminal)
> **方案 A专业交易终端风格**
> 基于深色模式重新设计,参考 Bloomberg Terminal、TradingView Pro 等专业交易工具。
> 核心目标:减少视觉疲劳、突出数据本身、增强专业感。
---
## 1. 设计原则
| 原则 | 说明 |
|------|------|
| **深色优先** | 全局深色背景,降低长时间盯盘的视觉疲劳 |
| **数据高亮** | 关键价格、涨跌幅使用高对比色,在深色背景上更醒目 |
| **硬朗专业** | 减小圆角6px减少柔和阴影使用边框和分隔线划分区域 |
| **蓝色品牌** | 品牌色从紫色改为蓝色,更符合金融/科技专业工具的调性 |
| **信息分层** | 利用背景色阶(`#0D1117` → `#161B22``#21262D`)构建清晰的层级 |
---
## 2. 色彩系统
### 2.1 背景色阶(核心)
| 名称 | HEX | 用途 |
|------|-----|------|
| Page BG | `#0D1117` | 页面最底层背景 |
| Card BG | `#161B22` | 卡片、面板背景 |
| Hover BG | `#21262D` | 卡片 Hover 态、次级区块 |
| Input BG | `#010409` | 输入框、深色嵌套区域 |
| Border | `#30363D` | 卡片边框、分割线 |
| Border-Light | `#484F58` | 次要边框、表格线 |
### 2.2 文字色阶
| 名称 | HEX | 用途 |
|------|-----|------|
| Text Primary | `#C9D1D9` | 主标题、重要数值 |
| Text Secondary | `#8B949E` | 副标题、标签、辅助说明 |
| Text Tertiary | `#6E7681` | 禁用态、占位符、时间戳 |
| Text Inverse | `#FFFFFF` | 深色按钮上的文字 |
### 2.3 功能色Dark Mode 适配)
> 相比浅色模式,深色模式下的功能色需要提高亮度以保证对比度。
| 名称 | HEX | 用途 |
|------|-----|------|
| 上涨 | `#F85149` | 价格上涨、做多信号 |
| 上涨-淡 | `rgba(248, 81, 73, 0.15)` | 上涨背景遮罩 |
| 下跌 | `#3FB950` | 价格下跌、做空信号 |
| 下跌-淡 | `rgba(63, 185, 80, 0.15)` | 下跌背景遮罩 |
| 观望/中性 | `#D29922` | 观望信号、震荡状态 |
| 观望-淡 | `rgba(210, 153, 34, 0.15)` | 观望背景遮罩 |
### 2.4 品牌色(蓝色系)
| 名称 | HEX | 用途 |
|------|-----|------|
| Brand Primary | `#58A6FF` | 导航高亮、主按钮、选中态、链接 |
| Brand Secondary | `#1F6FEB` | 按钮 Hover、强调 |
| Brand Pale | `rgba(88, 166, 255, 0.15)` | 选中背景、轻量强调 |
| Brand Border | `#58A6FF` | 选中边框 |
### 2.5 AI / 智能色系
> 保持紫色系以区分 AI 内容,但在深色模式下调整饱和度。
| 名称 | HEX | 用途 |
|------|-----|------|
| AI Primary | `#A371F7` | AI 模块标题、智能按钮 |
| AI BG | `rgba(163, 113, 247, 0.1)` | AI 分析区域背景 |
| AI Border | `#A371F7` | AI 模块边框 |
### 2.6 色调样例板
```
┌──────────────────────────────────────────────────────────────┐
│ 背景色阶(深 → 浅) │
│ ██████ #0D1117 ██████ #161B22 ██████ #21262D
│ ██████ #30363D ██████ #484F58
├──────────────────────────────────────────────────────────────┤
│ 文字色阶(亮 → 暗) │
│ ██████ #C9D1D9 ██████ #8B949E ██████ #6E7681
│ ██████ #FFFFFF
├──────────────────────────────────────────────────────────────┤
│ 功能色(高亮) │
│ ██████ #F85149 ██████ #3FB950 ██████ #D29922
──────────────────────────────────────────────────────────────┤
│ 品牌色(蓝) & AI色
│ ██████ #58A6FF ██████ #1F6FEB ██████ #A371F7
└──────────────────────────────────────────────────────────────┘
```
### 2.7 CSS 变量定义
```css
:root {
/* 背景 */
--bg-page: #0D1117;
--bg-card: #161B22;
--bg-hover: #21262D;
--bg-input: #010409;
--border-default: #30363D;
--border-light: #484F58;
/* 文字 */
--text-primary: #C9D1D9;
--text-secondary: #8B949E;
--text-tertiary: #6E7681;
--text-inverse: #FFFFFF;
/* 功能色 */
--color-up: #F85149;
--color-up-bg: rgba(248, 81, 73, 0.15);
--color-down: #3FB950;
--color-down-bg: rgba(63, 185, 80, 0.15);
--color-neutral: #D29922;
--color-neutral-bg: rgba(210, 153, 34, 0.15);
/* 品牌色 */
--color-brand: #58A6FF;
--color-brand-hover: #1F6FEB;
--color-brand-bg: rgba(88, 166, 255, 0.15);
/* AI色 */
--color-ai: #A371F7;
--color-ai-bg: rgba(163, 113, 247, 0.1);
}
```
---
## 3. 字体系统
### 3.1 字体族
```css
font-family:
'SF Mono', 'Fira Code', 'Roboto Mono',
'PingFang SC', 'Microsoft YaHei',
monospace, sans-serif;
```
> **注**:金融终端推荐使用等宽字体显示数字和代码,增强专业感。
### 3.2 字号阶梯
| 层级 | 字号 | 字重 | 行高 | 用途 |
|------|------|------|------|------|
| H1 | 20px | 600 | 28px | 页面主标题(深色下不宜过大) |
| H2 | 16px | 600 | 24px | 模块标题 |
| H3 | 14px | 600 | 20px | 卡片标题 |
| Body | 13px | 400 | 20px | 正文内容 |
| Body-S | 12px | 400 | 18px | 辅助说明 |
| Caption | 11px | 400 | 16px | 极小标注 |
| Num-L | 24px | 700 | — | 卡片价格(改用 24px 更紧凑) |
| Num-M | 18px | 700 | — | 详情页价格 |
| Num-S | 12px | 600 | — | 辅助数值 |
---
## 4. 间距与圆角
### 4.1 间距(紧凑化)
深色模式下,适当减小间距可以增加信息密度感。
| Token | 值 | 用途 |
|-------|-----|------|
| `space-xs` | 4px | 图标与文字 |
| `space-sm` | 8px | 卡片内元素 |
| `space-md` | 12px | 卡片间距 |
| `space-lg` | 16px | 区块间距 |
| `space-xl` | 24px | 页面级间距 |
### 4.2 圆角(硬朗化)
| Token | 值 | 用途 |
|-------|-----|------|
| `radius-sm` | 4px | 小按钮、标签 |
| `radius-md` | 6px | 卡片、面板、输入框 |
| `radius-lg` | 8px | 弹窗 |
| `radius-full` | 9999px | Pill 标签 |
---
## 5. 组件规范
### 5.1 顶部导航栏
```
┌────────────────────────────────────────────────────────────────────┐
│ 期货智析 [品种分析] 自选 市场概览 风险预警 [●LIVE] │
└────────────────────────────────────────────────────────────────────┘
```
| 元素 | 规范 |
|------|------|
| 高度 | 48px更紧凑 |
| 背景 | `#0D1117` + 底边框 `#30363D` |
| Logo | 图标 + 文字"期货智析",字号 14px字重 600颜色 `#C9D1D9` |
| 导航项 | 选中态:蓝色文字 `#58A6FF` + 下划线;未选中:灰色 `#8B949E` |
| LIVE 徽章 | 绿色圆点 `#3FB950` + "LIVE"文字,字号 10px |
### 5.2 搜索与筛选栏
```
┌────────────────────────────────────────────────────────────────────┐
│ 🔍 搜索品种... [●网格] [○列表] │
│ [●全部] [☆自选] [能源] [金属] [农产品] [刷新] [分析] [▼] │
└────────────────────────────────────────────────────────────────────┘
```
| 元素 | 规范 |
|------|------|
| 搜索框 | 高度 32px圆角 6px背景 `#010409`,边框 `#30363D`,文字 `#C9D1D9` |
| 视图切换 | 图标按钮,选中态蓝色 `#58A6FF` |
| 分类 Pill | 高度 24px圆角 4px选中态蓝色背景 `rgba(88,166,255,0.15)` + 蓝色文字 |
| 操作按钮 | "刷新"蓝色文字,"分析"蓝色实心按钮 |
### 5.3 统计概览卡片
```
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ ≡ 32 │ │ ↗ 7 │ │ ↘ 10 │ │ ↔ 15 │
│ 监控品种 │ │ 上涨趋势 │ │ 下跌趋势 │ │ 震荡整理 │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
```
| 元素 | 规范 |
|------|------|
| 背景 | `#161B22`,边框 `#30363D` |
| 数值 | 20px/700`#C9D1D9` |
| 标签 | 12px/400`#8B949E` |
| 图标 | 16×16px颜色对应状态涨红/跌绿/震荡橙) |
### 5.4 品种卡片(核心组件 - Dark
```
┌──────────────────────────────────────────────────────────┐
│ SC 原油 ¥644.5 │
│ SC2606 ↓ -13.3 (-2.02%) │
├──────────────────────────────────────────────────────────┤
│ [观望] │
│ 成功率 ━━━━━━ 60% 趋势评分 30 │
│ [5M] [15M] [30M] [1H] │
│ 压力 642.4 支撑 631.7 [↻] [🔔] [★] → │
└──────────────────────────────────────────────────────────┘
```
| 元素 | 规范 |
|------|------|
| 卡片背景 | `#161B22`,边框 `#30363D`Hover 时边框变 `#58A6FF` |
| 品种标识 | 32×32px圆角 4px背景 `#21262D`,文字 `#8B949E` |
| 价格 | 24px/700`#C9D1D9`(默认),涨跌时变红/绿 |
| 涨跌幅 | 12px/600`#F85149` / 绿 `#3FB950` |
| 操作建议 | Pill 标签,高度 20px圆角 4px背景用半透明色`rgba(210,153,34,0.15)`),文字用纯色 |
| 进度条 | 高度 2px更细颜色对应状态 |
| 周期 Pill | 高度 20px圆角 4px选中态蓝色边框 + 文字 |
| 底部工具 | 图标颜色 `#8B949E`Hover 变 `#C9D1D9` |
### 5.5 K 线图表区Dark
| 元素 | 规范 |
|------|------|
| 图表背景 | `#0D1117`(与页面背景一致,无边框感) |
| 网格线 | `#21262D`(极淡) |
| 蜡烛图 | 阳线 `#F85149`(空心或实心),阴线 `#3FB950` |
| 均线 | MA5 `#D29922`MA10 `#58A6FF`MA20 `#A371F7` |
| 成交量 | 阳线量柱 `#F85149` (40% 透明度),阴线量柱 `#3FB950` (40% 透明度) |
| 坐标轴 | 文字 `#6E7681` |
### 5.6 右侧面板Dark
```
┌────────────────────────────────
│ 🔊 AI 思维分析 [智能分析] │
├────────────────────────────────┤
│ SC2606 呈多周期空头排列... │
├────────────────────────────────┤
│ 入场区间 640.5-642.5 │
│ 止损位 645.5 │
└────────────────────────────────
```
| 元素 | 规范 |
|------|------|
| 面板背景 | `#161B22`,边框 `#30363D` |
| 标题 | 14px/600`#C9D1D9` |
| AI 按钮 | 紫色渐变背景或紫色文字 |
| 数据行 | 标签 `#6E7681`,数值 `#C9D1D9` |
| 分隔线 | `#30363D` |
### 5.7 弹窗Dark
| 元素 | 规范 |
|------|------|
| 遮罩 | `rgba(0,0,0,0.7)`(更深) |
| 弹窗背景 | `#161B22`,边框 `#30363D` |
| 标题栏 | 背景 `#0D1117`,底边框 `#30363D` |
| 表格头 | 背景 `#0D1117`,文字 `#8B949E` |
| 表格行 | 背景 `#161B22`Hover `#21262D` |
| 风险提示 | 左边框 `#D29922`,背景 `rgba(210,153,34,0.1)` |
---
## 6. 交互规范
### 6.1 状态反馈
| 交互 | 反馈方式 |
|------|---------|
| 卡片 Hover | 背景变 `#21262D`,边框变 `#58A6FF` |
| 按钮 Hover | 背景亮度增加 10% |
| 按钮 Active | 背景亮度降低 10% |
| 数据刷新 | 骨架屏(`#21262D` 脉冲动画) |
### 6.2 滚动条
| 元素 | 规范 |
|------|------|
| 轨道 | `#0D1117` |
| 滑块 | `#30363D`,圆角 4px |
| 滑块 Hover | `#484F58` |
---
## 7. 暗色模式专属优化
1. **对比度检查**:所有文字与背景对比度至少 4.5:1WCAG AA
2. **避免纯黑纯白**:不使用 `#000000``#FFFFFF` 作为大面积背景/文字,减少眩光。
3. **阴影替代**:深色模式下阴影不可见,改用边框颜色变化来体现层级和选中态。
4. **图片适配**:如有图标/图片,增加 10% 亮度或添加半透明遮罩使其融入深色背景。
---
## 8. 开发资源清单
| 资源 | 说明 |
|------|------|
| CSS 变量 | 已提供 `:root` 定义 |
| 字体 | 推荐 `SF Mono` / `Fira Code` 用于数字 |
| 图标 | 推荐 `Lucide``Phosphor Icons`(线性风格,适合深色) |
| 图表 | `Lightweight Charts`(原生支持深色主题)或 `ECharts` |
| 组件库 | 建议基于 `Ant Design Dark Theme``Radix UI` 定制 |
---
*文档版本: v2.0 (Dark Terminal) | 最后更新: 2026-05-23*
*基于方案 A专业交易终端风格*
Loading…
Cancel
Save