(function () { function parseNum(v) { var n = parseFloat(v); return isNaN(n) ? null : n; } function calcRR(direction, entry, stop, target) { if (!entry || !stop || !target) return ''; var risk, reward; if (direction === 'long') { risk = entry - stop; reward = target - entry; } else if (direction === 'short') { risk = stop - entry; reward = entry - target; } else { return ''; } if (risk <= 0) return ''; return (reward / risk).toFixed(2); } 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 openT = form.querySelector('[name="open_time"]').value; var closeT = form.querySelector('[name="close_time"]').value; var hold = document.getElementById('holding_duration'); var initR = document.getElementById('initial_rr'); var actR = document.getElementById('actual_rr'); if (hold) hold.value = calcDuration(openT, closeT); if (initR) initR.value = calcRR(dir, entry, sl, tp); if (actR) actR.value = calcRR(dir, entry, sl, close); } 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 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) { var mask = document.getElementById('review-modal'); var body = document.getElementById('review-modal-body'); if (!mask || !body) return; var labels = [ '品种', '合约', '方向', '张数', '周期', '成交价', '止损', '止盈', '平仓价', '开仓时间', '平仓时间', '持仓时长', '盈利金额', '手续费', '净盈亏', '盈亏比', '开仓类型', '行为标签' ]; var values = [ 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), esc(data.fee), esc(data.pnl_net), fmtRR(data), esc(data.open_type), fmtTags(data) ]; var html = '