Restructure into modules/ with single-process CTP and config/ layout.
Move business code under modules/, env template to config/, PM2 single qihuo process, and _legacy shims for old imports. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
{# Copyright (c) 2025-2026 马建军. All rights reserved. 专有软件,详见 LICENSE.zh-CN.txt #}
|
||||
{% extends "base.html" %}
|
||||
{% block title %}行情K线 - 国内期货 · 交易复盘系统{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class="card market-card">
|
||||
<h2>行情 K 线</h2>
|
||||
<form class="market-toolbar" id="market-form" onsubmit="return false;">
|
||||
<div class="symbol-wrap market-symbol-wrap">
|
||||
<input type="text" class="symbol-input" id="market-symbol-input" placeholder="点击选择主力合约,或输入搜索" autocomplete="off" value="{{ symbol }}">
|
||||
<input type="hidden" name="symbol" id="market-symbol-hidden" value="{{ symbol }}">
|
||||
<input type="hidden" name="symbol_name" id="market-symbol-name">
|
||||
<input type="hidden" name="market_code" id="market-market-code">
|
||||
<input type="hidden" name="sina_code" id="market-sina-code">
|
||||
<div class="symbol-dropdown"></div>
|
||||
<div class="symbol-selected" id="market-symbol-selected"></div>
|
||||
</div>
|
||||
<div class="market-period-tabs" id="market-period-tabs">
|
||||
{% for p in market_periods %}
|
||||
<button type="button" class="period-tab{% if p.key == period %} active{% endif %}" data-period="{{ p.key }}">{{ p.label }}</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<button type="button" class="btn-primary" id="market-load-btn">查看</button>
|
||||
</form>
|
||||
<div class="market-quote" id="market-quote">
|
||||
<span class="market-quote-name" id="market-quote-name">—</span>
|
||||
<span class="market-quote-price" id="market-quote-price">—</span>
|
||||
<span class="market-quote-prev text-muted" id="market-quote-prev"></span>
|
||||
<span class="market-quote-meta text-muted" id="market-quote-meta"></span>
|
||||
</div>
|
||||
<div class="market-chart-toolbar">
|
||||
<div class="market-chart-options">
|
||||
<label class="chart-opt"><input type="checkbox" id="chart-opt-prev-close">昨收线</label>
|
||||
<label class="chart-opt"><input type="checkbox" id="chart-opt-ma">EMA</label>
|
||||
<span class="chart-ema-periods" id="chart-ema-periods">
|
||||
<input type="number" class="chart-ema-input" id="chart-ema-fast" value="21" min="2" max="500" step="1" title="快线周期" aria-label="EMA快线周期">
|
||||
<span class="chart-ema-sep">/</span>
|
||||
<input type="number" class="chart-ema-input" id="chart-ema-slow" value="55" min="2" max="500" step="1" title="慢线周期" aria-label="EMA慢线周期">
|
||||
</span>
|
||||
<label class="chart-opt"><input type="checkbox" id="chart-opt-gap-day">间隔日</label>
|
||||
</div>
|
||||
<div class="market-chart-zoom">
|
||||
<button type="button" class="chart-zoom-btn" id="chart-zoom-in" title="放大">+</button>
|
||||
<button type="button" class="chart-zoom-btn" id="chart-zoom-out" title="缩小">-</button>
|
||||
<button type="button" class="chart-zoom-btn chart-zoom-reset" id="chart-zoom-reset">重置</button>
|
||||
</div>
|
||||
<span class="market-refresh-hint text-muted" id="market-refresh-hint"></span>
|
||||
</div>
|
||||
<div class="market-chart-auto-row">
|
||||
<button type="button" class="chart-auto-btn is-active" id="market-auto-btn" title="开启后自动跟随最新 K 线">自动</button>
|
||||
<span class="hint market-auto-hint">关闭后可自由拖动查看历史,刷新时只更新最新 K 线,不重置视图</span>
|
||||
</div>
|
||||
<div class="market-chart-wrap" id="market-chart-wrap">
|
||||
<div id="market-chart" class="market-chart" aria-label="K线图"></div>
|
||||
<div class="market-chart-empty" id="market-chart-empty">请选择合约并点击「查看」</div>
|
||||
<div class="market-chart-loading" id="market-chart-loading">连接中…</div>
|
||||
</div>
|
||||
<p class="hint">图表引擎:TradingView Lightweight Charts(红跌绿涨)。K 线与报价均使用<strong>新浪</strong>数据。滚轮缩放、拖拽平移;关闭「自动」后拖动查看历史时,推送更新不会重置画面。</p>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.market-card{overflow:visible}
|
||||
.market-card h2{margin-bottom:.75rem}
|
||||
.market-toolbar{
|
||||
display:flex;flex-wrap:wrap;gap:.65rem;align-items:flex-end;
|
||||
margin-bottom:.75rem;position:relative;z-index:2;
|
||||
min-height:2.5rem;
|
||||
}
|
||||
.market-symbol-wrap{flex:1;min-width:200px;max-width:360px;z-index:3}
|
||||
.market-symbol-wrap .symbol-dropdown{max-height:min(70vh,420px)}
|
||||
.market-symbol-wrap .symbol-selected{display:none}
|
||||
.market-period-tabs{display:flex;flex-wrap:wrap;gap:.35rem;align-items:center}
|
||||
.period-tab{
|
||||
padding:.4rem .65rem;border-radius:999px;
|
||||
border:1px solid var(--input-border);
|
||||
background:var(--toggle-bg);color:var(--text-muted);
|
||||
font-size:.78rem;cursor:pointer;width:auto;white-space:nowrap;
|
||||
}
|
||||
.period-tab:hover{border-color:var(--accent);color:var(--accent)}
|
||||
.period-tab.active{
|
||||
background:linear-gradient(135deg,var(--accent),var(--accent-2));
|
||||
border-color:transparent;color:#fff;
|
||||
}
|
||||
#market-load-btn{width:auto;padding:.55rem 1.25rem;font-size:.85rem}
|
||||
.market-quote{
|
||||
display:flex;flex-wrap:wrap;align-items:baseline;gap:.5rem 1rem;
|
||||
margin-bottom:.75rem;padding:.65rem .85rem;
|
||||
background:var(--card-inner);border-radius:10px;border:1px solid var(--card-border);
|
||||
min-height:2.75rem;
|
||||
}
|
||||
.market-quote-name{font-weight:600;color:var(--text-title)}
|
||||
.market-quote-price{font-size:1.35rem;font-weight:700;color:var(--accent);font-variant-numeric:tabular-nums}
|
||||
.market-quote-prev{font-size:.78rem}
|
||||
.market-chart-toolbar{
|
||||
display:flex;align-items:center;justify-content:space-between;gap:.75rem;
|
||||
margin-bottom:.5rem;flex-wrap:wrap;
|
||||
min-height:2rem;
|
||||
}
|
||||
.market-chart-options{display:flex;flex-wrap:wrap;gap:.5rem .85rem;align-items:center}
|
||||
.chart-opt{
|
||||
display:flex;align-items:center;gap:.35rem;font-size:.78rem;
|
||||
color:var(--text-muted);cursor:pointer;user-select:none;
|
||||
}
|
||||
.chart-opt input{width:auto;margin:0;cursor:pointer}
|
||||
.chart-ema-periods{display:flex;align-items:center;gap:.25rem;font-size:.78rem;color:var(--text-muted)}
|
||||
.chart-ema-input{
|
||||
width:3.1rem;padding:.28rem .35rem;border-radius:6px;
|
||||
border:1px solid var(--input-border);background:var(--toggle-bg);
|
||||
color:var(--text-primary);font-size:.78rem;font-variant-numeric:tabular-nums;
|
||||
}
|
||||
.chart-ema-input:disabled{opacity:.45;cursor:not-allowed}
|
||||
.chart-ema-sep{opacity:.6}
|
||||
.market-chart-zoom{display:flex;gap:.35rem;align-items:center}
|
||||
.chart-zoom-btn{
|
||||
width:32px;height:32px;padding:0;border-radius:8px;
|
||||
border:1px solid var(--input-border);background:var(--toggle-bg);
|
||||
color:var(--text-primary);font-size:1rem;line-height:1;cursor:pointer;
|
||||
}
|
||||
.chart-zoom-btn:hover{border-color:var(--accent);color:var(--accent)}
|
||||
.chart-zoom-reset{width:auto;padding:0 .65rem;font-size:.75rem}
|
||||
.market-refresh-hint{font-size:.72rem}
|
||||
.market-chart-auto-row{
|
||||
display:flex;align-items:center;gap:.65rem;flex-wrap:wrap;
|
||||
margin-bottom:.5rem;
|
||||
}
|
||||
.chart-auto-btn{
|
||||
padding:.38rem .85rem;border-radius:999px;
|
||||
border:1px solid var(--input-border);background:var(--toggle-bg);
|
||||
color:var(--text-muted);font-size:.78rem;cursor:pointer;width:auto;
|
||||
}
|
||||
.chart-auto-btn:hover{border-color:var(--accent);color:var(--accent)}
|
||||
.chart-auto-btn.is-active{
|
||||
background:linear-gradient(135deg,var(--accent),var(--accent-2));
|
||||
border-color:transparent;color:#fff;
|
||||
}
|
||||
.market-auto-hint{font-size:.72rem;margin:0}
|
||||
.market-chart-wrap{
|
||||
position:relative;border-radius:12px;border:1px solid var(--card-border);
|
||||
background:var(--card-inner);
|
||||
height:min(68vh,560px);min-height:420px;
|
||||
}
|
||||
.market-chart{width:100%;height:100%}
|
||||
.market-chart-empty,
|
||||
.market-chart-loading{
|
||||
position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
|
||||
color:var(--text-muted);font-size:.9rem;pointer-events:none;
|
||||
}
|
||||
.market-chart-loading{display:none}
|
||||
html[data-theme="light"] .market-chart-loading{background:rgba(244,247,252,.75)}
|
||||
.market-chart-wrap.has-data .market-chart-empty{display:none}
|
||||
.market-chart-wrap.loading .market-chart-loading{
|
||||
display:flex;background:rgba(10,12,20,.35);
|
||||
}
|
||||
html[data-theme="light"] .market-chart-wrap.loading .market-chart-loading{background:rgba(244,247,252,.75)}
|
||||
.market-chart-wrap.loading .market-chart-empty{display:none}
|
||||
@media(max-width:767px){
|
||||
.market-toolbar{align-items:stretch}
|
||||
.market-symbol-wrap{max-width:none}
|
||||
.market-period-tabs{order:3;width:100%}
|
||||
#market-load-btn{order:4;width:100%}
|
||||
.market-chart-wrap{min-height:300px;height:50vh}
|
||||
.market-chart-toolbar{flex-direction:column;align-items:stretch}
|
||||
.market-chart-options{order:1}
|
||||
.market-chart-zoom{order:2}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script src="https://unpkg.com/lightweight-charts@4.2.0/dist/lightweight-charts.standalone.production.js"></script>
|
||||
<script src="{{ url_for('static', filename='js/market.js') }}"></script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user