修复关键位sina_code字段;复盘详情全屏两行;开单计划表单布局优化
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -174,6 +174,7 @@ def init_db():
|
|||||||
"ALTER TABLE order_plans ADD COLUMN sina_code TEXT",
|
"ALTER TABLE order_plans ADD COLUMN sina_code TEXT",
|
||||||
"ALTER TABLE order_plans ADD COLUMN market_code TEXT",
|
"ALTER TABLE order_plans ADD COLUMN market_code TEXT",
|
||||||
"ALTER TABLE key_monitors ADD COLUMN market_code TEXT",
|
"ALTER TABLE key_monitors ADD COLUMN market_code TEXT",
|
||||||
|
"ALTER TABLE key_monitors ADD COLUMN sina_code TEXT",
|
||||||
"ALTER TABLE trade_records ADD COLUMN market_code TEXT",
|
"ALTER TABLE trade_records ADD COLUMN market_code TEXT",
|
||||||
"ALTER TABLE order_plans ADD COLUMN plan_date TEXT",
|
"ALTER TABLE order_plans ADD COLUMN plan_date TEXT",
|
||||||
"ALTER TABLE order_plans ADD COLUMN decision_reason TEXT",
|
"ALTER TABLE order_plans ADD COLUMN decision_reason TEXT",
|
||||||
|
|||||||
+68
-16
@@ -59,30 +59,82 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function esc(v) {
|
||||||
|
if (v === null || v === undefined || v === '') return '-';
|
||||||
|
return String(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fmtTime(v) {
|
||||||
|
if (!v) return '-';
|
||||||
|
return String(v).replace('T', ' ').slice(0, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fmtRR(data) {
|
||||||
|
var init = data.initial_pnl;
|
||||||
|
var act = data.actual_pnl;
|
||||||
|
if (init && act) return init + ' / ' + act;
|
||||||
|
return act || init || '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
function fmtTags(data) {
|
||||||
|
var tags = data.behavior_tags || '';
|
||||||
|
if (data.is_emotion && tags.indexOf('情绪') === -1) {
|
||||||
|
return tags ? '情绪单 · ' + tags : '情绪单';
|
||||||
|
}
|
||||||
|
return tags || '-';
|
||||||
|
}
|
||||||
|
|
||||||
function showModal(data) {
|
function showModal(data) {
|
||||||
var mask = document.getElementById('review-modal');
|
var mask = document.getElementById('review-modal');
|
||||||
var body = document.getElementById('review-modal-body');
|
var body = document.getElementById('review-modal-body');
|
||||||
if (!mask || !body) return;
|
if (!mask || !body) return;
|
||||||
var html = '<div class="modal-grid">';
|
|
||||||
var fields = [
|
var labels = [
|
||||||
['品种', data.symbol], ['方向', data.direction],
|
'品种', '合约', '方向', '张数', '周期',
|
||||||
['成交价', data.entry_price], ['止损', data.stop_loss],
|
'成交价', '止损', '止盈', '平仓价',
|
||||||
['止盈', data.take_profit], ['平仓价', data.close_price],
|
'开仓时间', '平仓时间', '持仓时长', '盈利金额', '盈亏比',
|
||||||
['张数', data.lots], ['开仓时间', data.open_time],
|
'开仓类型', '行为标签'
|
||||||
['平仓时间', data.close_time], ['持仓时长', data.holding_duration],
|
|
||||||
['初始盈亏比', data.initial_pnl], ['实际盈亏比', data.actual_pnl],
|
|
||||||
['盈亏金额', data.pnl], ['开仓类型', data.open_type],
|
|
||||||
['离场触发', data.exit_trigger], ['离场补充', data.exit_supplement],
|
|
||||||
['情绪单', data.is_emotion ? '是' : '否'],
|
|
||||||
['行为标签', data.behavior_tags], ['备注', data.notes]
|
|
||||||
];
|
];
|
||||||
fields.forEach(function (pair) {
|
var values = [
|
||||||
html += '<div class="item"><label>' + pair[0] + '</label><div>' + (pair[1] || '-') + '</div></div>';
|
esc(data.symbol),
|
||||||
|
esc(data.symbol_code),
|
||||||
|
esc(data.direction),
|
||||||
|
esc(data.lots),
|
||||||
|
esc(data.timeframe),
|
||||||
|
esc(data.entry_price),
|
||||||
|
esc(data.stop_loss),
|
||||||
|
esc(data.take_profit),
|
||||||
|
esc(data.close_price),
|
||||||
|
fmtTime(data.open_time),
|
||||||
|
fmtTime(data.close_time),
|
||||||
|
esc(data.holding_duration),
|
||||||
|
esc(data.pnl),
|
||||||
|
fmtRR(data),
|
||||||
|
esc(data.open_type),
|
||||||
|
fmtTags(data)
|
||||||
|
];
|
||||||
|
|
||||||
|
var html = '<div class="review-detail-table">';
|
||||||
|
html += '<div class="review-detail-headers">';
|
||||||
|
labels.forEach(function (lb) {
|
||||||
|
html += '<span>' + lb + '</span>';
|
||||||
});
|
});
|
||||||
html += '</div>';
|
html += '</div><div class="review-detail-values">';
|
||||||
|
values.forEach(function (val, i) {
|
||||||
|
var cls = '';
|
||||||
|
if (i === 15 && data.is_emotion) cls = ' class="emotion-val"';
|
||||||
|
html += '<span' + cls + '>' + val + '</span>';
|
||||||
|
});
|
||||||
|
html += '</div></div>';
|
||||||
|
|
||||||
|
html += '<div class="review-detail-image">';
|
||||||
if (data.screenshot) {
|
if (data.screenshot) {
|
||||||
html += '<div style="margin-top:1rem"><img src="/uploads/' + data.screenshot + '" style="max-width:100%;border-radius:8px"></div>';
|
html += '<img src="/uploads/' + data.screenshot + '" alt="复盘截图">';
|
||||||
|
} else {
|
||||||
|
html += '<div class="no-img">暂无截图 / K线图</div>';
|
||||||
}
|
}
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
body.innerHTML = html;
|
body.innerHTML = html;
|
||||||
mask.classList.add('show');
|
mask.classList.add('show');
|
||||||
}
|
}
|
||||||
|
|||||||
+42
-1
@@ -103,7 +103,7 @@
|
|||||||
}
|
}
|
||||||
.page-wrap{max-width:1800px;margin:0 auto;min-height:100vh}
|
.page-wrap{max-width:1800px;margin:0 auto;min-height:100vh}
|
||||||
.site-header{text-align:center;padding:1.5rem 1rem 1.25rem;border-bottom:1px solid var(--border-header);position:relative}
|
.site-header{text-align:center;padding:1.5rem 1rem 1.25rem;border-bottom:1px solid var(--border-header);position:relative}
|
||||||
.site-title{font-size:1.75rem;font-weight:700;color:var(--text-title);margin-bottom:1rem;line-height:1.3}
|
.site-title{font-size:1.75rem;font-weight:700;color:var(--text-title);margin-bottom:1.65rem;line-height:1.3}
|
||||||
.header-tools{position:absolute;top:1rem;left:1.5rem;display:flex;gap:.5rem;align-items:center}
|
.header-tools{position:absolute;top:1rem;left:1.5rem;display:flex;gap:.5rem;align-items:center}
|
||||||
.theme-toggle{
|
.theme-toggle{
|
||||||
display:flex;align-items:center;gap:.35rem;
|
display:flex;align-items:center;gap:.35rem;
|
||||||
@@ -290,6 +290,47 @@
|
|||||||
.modal-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:.75rem;font-size:.9rem}
|
.modal-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:.75rem;font-size:.9rem}
|
||||||
.modal-grid .item label{color:var(--text-muted);font-size:.75rem;display:block}
|
.modal-grid .item label{color:var(--text-muted);font-size:.75rem;display:block}
|
||||||
.modal-grid .item div{margin-top:.2rem;color:var(--text-primary)}
|
.modal-grid .item div{margin-top:.2rem;color:var(--text-primary)}
|
||||||
|
.form-compact .line-plan-1{grid-template-columns:minmax(0,1fr) minmax(72px,0.45fr) minmax(0,1.6fr)}
|
||||||
|
.form-compact .line-plan-2{grid-template-columns:repeat(4,minmax(0,1fr)) auto;align-items:center}
|
||||||
|
.form-compact .field-short select{padding:.55rem .5rem}
|
||||||
|
.badge.emotion{background:var(--loss-bg);color:var(--loss);font-weight:600;border:1px solid var(--loss)}
|
||||||
|
tr.row-emotion td{color:var(--loss)}
|
||||||
|
tr.row-emotion td .badge.emotion{background:var(--loss);color:#fff;border-color:var(--loss)}
|
||||||
|
.modal-box.review-modal-wide{max-width:960px}
|
||||||
|
.modal-box.review-modal-fullscreen{
|
||||||
|
width:calc(100vw - 1.5rem);max-width:none;
|
||||||
|
height:calc(100vh - 1.5rem);max-height:none;
|
||||||
|
border-radius:12px;display:flex;flex-direction:column;
|
||||||
|
}
|
||||||
|
.modal-box.review-modal-fullscreen h3{flex-shrink:0}
|
||||||
|
#review-modal-body.review-modal-body{flex:1;overflow:auto;min-height:0}
|
||||||
|
.review-detail-table{margin-bottom:1rem;overflow-x:auto}
|
||||||
|
.review-detail-headers,.review-detail-values{
|
||||||
|
display:grid;
|
||||||
|
grid-template-columns:repeat(16,minmax(52px,1fr));
|
||||||
|
gap:.35rem .4rem;align-items:start;
|
||||||
|
}
|
||||||
|
.review-detail-headers span{
|
||||||
|
font-size:.68rem;color:var(--text-muted);
|
||||||
|
white-space:nowrap;line-height:1.3;
|
||||||
|
}
|
||||||
|
.review-detail-values span{
|
||||||
|
font-size:.82rem;color:var(--text-primary);
|
||||||
|
line-height:1.35;word-break:break-all;
|
||||||
|
}
|
||||||
|
.review-detail-values span.emotion-val{color:var(--loss);font-weight:600}
|
||||||
|
.review-detail-fields{padding:.25rem 0}
|
||||||
|
.review-detail-section{margin-bottom:.85rem}
|
||||||
|
.review-detail-section h4{font-size:.78rem;color:var(--text-label);margin-bottom:.45rem;font-weight:600}
|
||||||
|
.review-detail-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:.45rem .65rem}
|
||||||
|
.review-detail-item label{display:block;font-size:.7rem;color:var(--text-muted);margin-bottom:.15rem}
|
||||||
|
.review-detail-item div{font-size:.85rem;color:var(--text-primary);line-height:1.35}
|
||||||
|
.review-detail-item.wide{grid-column:span 2}
|
||||||
|
.review-detail-item.full{grid-column:1/-1}
|
||||||
|
.review-detail-item.emotion div{color:var(--loss);font-weight:600}
|
||||||
|
.review-detail-image{flex-shrink:0;padding-top:.75rem;border-top:1px solid var(--table-border)}
|
||||||
|
.review-detail-image img{width:100%;border-radius:10px;border:1px solid var(--card-border)}
|
||||||
|
.review-detail-image .no-img{color:var(--text-muted);font-size:.85rem;padding:2rem;text-align:center;background:var(--card-inner);border-radius:10px}
|
||||||
.modal-close{float:right;color:var(--text-muted);cursor:pointer;font-size:1.2rem}
|
.modal-close{float:right;color:var(--text-muted);cursor:pointer;font-size:1.2rem}
|
||||||
.calc-readonly{background:var(--calc-bg);color:var(--accent)}
|
.calc-readonly{background:var(--calc-bg);color:var(--accent)}
|
||||||
@media(max-width:1100px){
|
@media(max-width:1100px){
|
||||||
|
|||||||
@@ -6,11 +6,11 @@
|
|||||||
<span class="card-corner tl"></span><span class="card-corner br"></span>
|
<span class="card-corner tl"></span><span class="card-corner br"></span>
|
||||||
<h2>今日计划 <span class="text-muted" style="font-size:.8rem;font-weight:normal">今日 {{ today }}</span></h2>
|
<h2>今日计划 <span class="text-muted" style="font-size:.8rem;font-weight:normal">今日 {{ today }}</span></h2>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p class="hint" style="margin-bottom:.75rem">开盘前制定,当日有效;下方为进行中计划。</p>
|
<p class="hint" style="margin-bottom:.75rem">开盘前制定,当日有效;请先选择<strong>主力合约</strong>,下方为进行中计划。</p>
|
||||||
<form action="{{ url_for('add_plan') }}" method="post" class="form-compact">
|
<form action="{{ url_for('add_plan') }}" method="post" class="form-compact">
|
||||||
<div class="form-line line-3">
|
<div class="form-line line-plan-1">
|
||||||
<div class="symbol-wrap">
|
<div class="symbol-wrap">
|
||||||
<input type="text" class="symbol-input" placeholder="品种" autocomplete="off" required>
|
<input type="text" class="symbol-input" placeholder="主力合约" autocomplete="off" required>
|
||||||
<input type="hidden" name="symbol" required>
|
<input type="hidden" name="symbol" required>
|
||||||
<input type="hidden" name="symbol_name">
|
<input type="hidden" name="symbol_name">
|
||||||
<input type="hidden" name="market_code" required>
|
<input type="hidden" name="market_code" required>
|
||||||
@@ -18,20 +18,18 @@
|
|||||||
<div class="symbol-dropdown"></div>
|
<div class="symbol-dropdown"></div>
|
||||||
<div class="symbol-selected"></div>
|
<div class="symbol-selected"></div>
|
||||||
</div>
|
</div>
|
||||||
<select name="direction" required>
|
<select name="direction" class="field-short" required>
|
||||||
<option value="">方向</option>
|
<option value="">方向</option>
|
||||||
<option value="long">做多</option>
|
<option value="long">做多</option>
|
||||||
<option value="short">做空</option>
|
<option value="short">做空</option>
|
||||||
</select>
|
</select>
|
||||||
<input name="decision_reason" type="text" placeholder="决策理由">
|
<input name="decision_reason" type="text" placeholder="决策理由">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-line line-4">
|
<div class="form-line line-plan-2">
|
||||||
<input name="zone_lower" type="number" step="0.0001" placeholder="决策区间下限" required>
|
<input name="zone_lower" type="number" step="0.0001" placeholder="决策区间下限" required>
|
||||||
<input name="zone_upper" type="number" step="0.0001" placeholder="决策区间上限" required>
|
<input name="zone_upper" type="number" step="0.0001" placeholder="决策区间上限" required>
|
||||||
<input name="stop_loss" type="number" step="0.0001" placeholder="止损" required>
|
<input name="stop_loss" type="number" step="0.0001" placeholder="止损" required>
|
||||||
<input name="take_profit" type="number" step="0.0001" placeholder="止盈" required>
|
<input name="take_profit" type="number" step="0.0001" placeholder="止盈" required>
|
||||||
</div>
|
|
||||||
<div class="form-line line-btn">
|
|
||||||
<button type="submit" class="btn-primary">添加</button>
|
<button type="submit" class="btn-primary">添加</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
+12
-8
@@ -107,7 +107,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for r in reviews %}
|
{% for r in reviews %}
|
||||||
<tr>
|
<tr class="{% if r.is_emotion %}row-emotion{% endif %}">
|
||||||
<td>{{ r.close_time[:16] if r.close_time else '' }}</td>
|
<td>{{ r.close_time[:16] if r.close_time else '' }}</td>
|
||||||
<td>{{ r.symbol_name or r.symbol }}</td>
|
<td>{{ r.symbol_name or r.symbol }}</td>
|
||||||
<td><span class="badge dir">{{ '多' if r.direction == 'long' else '空' }}</span></td>
|
<td><span class="badge dir">{{ '多' if r.direction == 'long' else '空' }}</span></td>
|
||||||
@@ -116,17 +116,21 @@
|
|||||||
{% elif r.pnl and r.pnl < 0 %}<span class="badge loss">{{ r.pnl }}</span>
|
{% elif r.pnl and r.pnl < 0 %}<span class="badge loss">{{ r.pnl }}</span>
|
||||||
{% else %}{{ r.actual_pnl or '-' }}{% endif %}
|
{% else %}{{ r.actual_pnl or '-' }}{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>{% if r.is_emotion %}<span class="badge loss">情绪</span>{% else %}-{% endif %}</td>
|
<td>{% if r.is_emotion %}<span class="badge emotion">情绪单</span>{% else %}-{% endif %}</td>
|
||||||
<td>
|
<td>
|
||||||
<button type="button" class="btn-link review-view-btn" data-review='{{ {
|
<button type="button" class="btn-link review-view-btn" data-review='{{ {
|
||||||
"symbol": r.symbol_name or r.symbol, "symbol_code": r.symbol, "direction": "做多" if r.direction=="long" else "做空",
|
"symbol": r.symbol_name or r.symbol, "symbol_code": r.symbol,
|
||||||
|
"direction": "做多" if r.direction=="long" else "做空",
|
||||||
|
"lots": r.lots, "timeframe": r.timeframe,
|
||||||
"entry_price": r.entry_price, "stop_loss": r.stop_loss, "take_profit": r.take_profit,
|
"entry_price": r.entry_price, "stop_loss": r.stop_loss, "take_profit": r.take_profit,
|
||||||
"close_price": r.close_price, "lots": r.lots,
|
"close_price": r.close_price,
|
||||||
"open_time": r.open_time, "close_time": r.close_time,
|
"open_time": r.open_time, "close_time": r.close_time,
|
||||||
"holding_duration": r.holding_duration, "initial_pnl": r.initial_pnl,
|
"holding_duration": r.holding_duration,
|
||||||
"actual_pnl": r.actual_pnl, "pnl": r.pnl,
|
"initial_pnl": r.initial_pnl, "actual_pnl": r.actual_pnl, "pnl": r.pnl,
|
||||||
"open_type": r.open_type,
|
"open_type": r.open_type,
|
||||||
"exit_trigger": r.exit_trigger, "exit_supplement": r.exit_supplement,
|
"exit_trigger": r.exit_trigger, "exit_supplement": r.exit_supplement,
|
||||||
|
"watch_after_breakeven": r.watch_after_breakeven,
|
||||||
|
"new_position_while_occupied": r.new_position_while_occupied,
|
||||||
"is_emotion": r.is_emotion, "behavior_tags": r.behavior_tags,
|
"is_emotion": r.is_emotion, "behavior_tags": r.behavior_tags,
|
||||||
"notes": r.notes, "screenshot": r.screenshot
|
"notes": r.notes, "screenshot": r.screenshot
|
||||||
} | tojson }}'>放大查看</button>
|
} | tojson }}'>放大查看</button>
|
||||||
@@ -144,10 +148,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="review-modal" class="modal-mask">
|
<div id="review-modal" class="modal-mask">
|
||||||
<div class="modal-box">
|
<div class="modal-box review-modal-fullscreen">
|
||||||
<span class="modal-close">✕</span>
|
<span class="modal-close">✕</span>
|
||||||
<h3>复盘详情</h3>
|
<h3>复盘详情</h3>
|
||||||
<div id="review-modal-body"></div>
|
<div id="review-modal-body" class="review-modal-body"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user