双栏布局:开单计划/关键位/复盘;复盘字段与情绪单

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-15 12:27:20 +08:00
parent 9fc41b6a46
commit 9b4bbbe8a3
6 changed files with 511 additions and 338 deletions
+106
View File
@@ -0,0 +1,106 @@
(function () {
function parseNum(v) {
var n = parseFloat(v);
return isNaN(n) ? null : n;
}
function calcPnl(direction, entry, target, lots) {
if (!entry || !target || !lots) return '';
if (direction === 'long') return ((target - entry) * lots).toFixed(2);
if (direction === 'short') return ((entry - target) * lots).toFixed(2);
return '';
}
function calcDuration(openVal, closeVal) {
if (!openVal || !closeVal) return '';
var o = new Date(openVal);
var c = new Date(closeVal);
var secs = Math.floor((c - o) / 1000);
if (secs < 0) return '';
var h = Math.floor(secs / 3600);
var m = Math.floor((secs % 3600) / 60);
return h ? h + '小时' + m + '分钟' : m + '分钟';
}
function recalc() {
var form = document.getElementById('review-form');
if (!form) return;
var dir = form.querySelector('[name="direction"]').value;
var entry = parseNum(form.querySelector('[name="entry_price"]').value);
var sl = parseNum(form.querySelector('[name="stop_loss"]').value);
var tp = parseNum(form.querySelector('[name="take_profit"]').value);
var close = parseNum(form.querySelector('[name="close_price"]').value);
var lots = parseNum(form.querySelector('[name="lots"]').value) || 1;
var openT = form.querySelector('[name="open_time"]').value;
var closeT = form.querySelector('[name="close_time"]').value;
var hold = document.getElementById('holding_duration');
var initP = document.getElementById('initial_pnl');
var actP = document.getElementById('actual_pnl');
if (hold) hold.value = calcDuration(openT, closeT);
if (initP) initP.value = calcPnl(dir, entry, tp, lots);
if (actP) actP.value = calcPnl(dir, entry, close, lots);
}
function bindForm() {
var form = document.getElementById('review-form');
if (!form) return;
form.querySelectorAll('input, select').forEach(function (el) {
el.addEventListener('input', recalc);
el.addEventListener('change', recalc);
});
}
function showModal(data) {
var mask = document.getElementById('review-modal');
var body = document.getElementById('review-modal-body');
if (!mask || !body) return;
var html = '<div class="modal-grid">';
var fields = [
['品种', 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],
['预期RR', data.expected_rr], ['实际RR', data.actual_rr],
['离场触发', data.exit_trigger], ['离场补充', data.exit_supplement],
['情绪单', data.is_emotion ? '是' : '否'],
['行为标签', data.behavior_tags], ['备注', data.notes]
];
fields.forEach(function (pair) {
html += '<div class="item"><label>' + pair[0] + '</label><div>' + (pair[1] || '-') + '</div></div>';
});
html += '</div>';
if (data.screenshot) {
html += '<div style="margin-top:1rem"><img src="/uploads/' + data.screenshot + '" style="max-width:100%;border-radius:8px"></div>';
}
body.innerHTML = html;
mask.classList.add('show');
}
function bindModal() {
var mask = document.getElementById('review-modal');
if (!mask) return;
mask.querySelector('.modal-close').addEventListener('click', function () {
mask.classList.remove('show');
});
mask.addEventListener('click', function (e) {
if (e.target === mask) mask.classList.remove('show');
});
document.querySelectorAll('.review-view-btn').forEach(function (btn) {
btn.addEventListener('click', function () {
try {
showModal(JSON.parse(btn.getAttribute('data-review')));
} catch (e) { /* ignore */ }
});
});
}
document.addEventListener('DOMContentLoaded', function () {
bindForm();
bindModal();
recalc();
});
})();