/** * 复盘表单 AJAX 保存:避免整页刷新卡顿;配合 FormSubmitGuard 即时反馈. * 使用 document 委托,兼容中控 embed 后插入的 #journal-form. */ (function (global) { "use strict"; function $(id) { return document.getElementById(id); } function toast(msg) { if (!msg) return; try { if (global.InstanceTheme && typeof InstanceTheme.toast === "function") { InstanceTheme.toast(msg); return; } } catch (_) {} try { alert(msg); } catch (_) {} } function refreshLists() { if (global.RecordsReviewPage && typeof RecordsReviewPage.loadJournals === "function") { try { RecordsReviewPage.loadJournals(); } catch (_) {} } else if (typeof global.loadJournals === "function") { try { global.loadJournals(); } catch (_) {} } // 交易记录保持可见:soft 刷新,勿「加载中…」占位 if (global.RecordsReviewPage && typeof RecordsReviewPage.loadTradeRecords === "function") { try { RecordsReviewPage.loadTradeRecords({ soft: true }); } catch (_) {} } else if (typeof global.loadTradeRecords === "function") { try { global.loadTradeRecords({ soft: true }); } catch (_) {} } } function resetJournalForm(form) { if (!form) return; form.reset(); ["risk-amount-hint", "entry-price-hint", "stop-loss-hint", "exit-price-hint", "direction-hint"].forEach( function (id) { var el = $(id); if (el) el.value = ""; } ); if (global.JournalUploadSlots && typeof JournalUploadSlots.reset === "function") { JournalUploadSlots.reset(form); } if (typeof global.syncEarlyExitNoteRequired === "function") { try { global.syncEarlyExitNoteRequired(); } catch (_) {} } if (global.RecordsReviewPage && typeof RecordsReviewPage.hideJournalCard === "function") { try { RecordsReviewPage.hideJournalCard(); } catch (_) {} } } function onSuccess(form, data) { var msg = (data && data.msg) || "交易复盘记录已保存"; toast(msg); resetJournalForm(form); refreshLists(); if (data && data.chart_pending) { setTimeout(refreshLists, 4000); setTimeout(refreshLists, 12000); } } function parseJsonSafe(res) { var ct = (res.headers.get("content-type") || "").toLowerCase(); if (ct.indexOf("application/json") >= 0) { return res.json().then(function (data) { return { kind: "json", ok: res.ok, data: data }; }); } return res.text().then(function (text) { return { kind: "html", ok: res.ok, redirected: !!res.redirected, text: text }; }); } function submitAjax(form) { if (global.FormSubmitGuard && FormSubmitGuard.isLocked(form)) return; if (global.FormSubmitGuard) FormSubmitGuard.lock(form, "保存中…"); var fd = new FormData(form); fd.set("ajax", "1"); var action = form.getAttribute("action") || "/add_journal"; fetch(action, { method: "POST", body: fd, credentials: "same-origin", redirect: "manual", headers: { "X-Requested-With": "XMLHttpRequest", Accept: "application/json", }, }) .then(function (res) { // opaque redirect:后端未认 AJAX,仍走了 302 if (res.type === "opaqueredirect" || (res.status >= 300 && res.status < 400)) { throw new Error("保存接口未返回 JSON,请硬刷新页面后重试"); } return parseJsonSafe(res); }) .then(function (result) { if (result.kind !== "json") { throw new Error("保存接口未返回 JSON,请硬刷新页面后重试"); } if (!result.ok || !result.data || result.data.ok === false) { var err = (result.data && (result.data.msg || result.data.error)) || "保存失败"; throw new Error(err); } onSuccess(form, result.data); }) .catch(function (err) { toast((err && err.message) || "保存失败,请稍后重试"); }) .finally(function () { if (global.FormSubmitGuard) FormSubmitGuard.unlock(form); }); } function onSubmit(ev) { var form = ev.target; if (!form || form.id !== "journal-form") return; if (typeof global.validateJournalEntryReason === "function") { if (!global.validateJournalEntryReason()) { ev.preventDefault(); return; } } ev.preventDefault(); submitAjax(form); } function bind(form) { // 保留显式 bind 入口(embed 切 tab 时可再调);真正拦截靠 document 委托 if (form) form.dataset.journalAjaxBound = "1"; } function init() { if (document.documentElement.dataset.journalFormSaveDelegated === "1") { bind($("journal-form")); return; } document.documentElement.dataset.journalFormSaveDelegated = "1"; document.addEventListener("submit", onSubmit, true); bind($("journal-form")); } global.JournalFormSave = { init: init, bind: bind }; if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", init); } else { init(); } })(typeof window !== "undefined" ? window : globalThis);