Fix journal AJAX save in embed so trades stay loaded.

Delegate submit handling for late-mounted #journal-form across OKX/Binance/Gate, avoid full reload fallback, and soft-refresh trade rows after save.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-18 15:23:48 +08:00
parent 8f4ae8ad31
commit a244850b25
5 changed files with 102 additions and 65 deletions
+3
View File
@@ -155,6 +155,9 @@
const root = pageRoot() || document;
global.JournalUploadSlots.init(root);
}
if (global.JournalFormSave && typeof global.JournalFormSave.init === "function") {
global.JournalFormSave.init();
}
}
}
+91 -60
View File
@@ -1,5 +1,6 @@
/**
* 复盘表单 AJAX 保存:避免整页刷新卡顿;配合 FormSubmitGuard 即时反馈.
* 使用 document 委托,兼容中控 embed 后插入的 #journal-form.
*/
(function (global) {
"use strict";
@@ -22,13 +23,23 @@
}
function refreshLists() {
if (typeof global.loadJournals === "function") {
if (global.RecordsReviewPage && typeof RecordsReviewPage.loadJournals === "function") {
try {
RecordsReviewPage.loadJournals();
} catch (_) {}
} else if (typeof global.loadJournals === "function") {
try {
global.loadJournals();
} catch (_) {}
} else if (global.RecordsReviewPage && typeof RecordsReviewPage.loadJournals === "function") {
}
// 交易记录保持可见:soft 刷新,勿「加载中…」占位
if (global.RecordsReviewPage && typeof RecordsReviewPage.loadTradeRecords === "function") {
try {
RecordsReviewPage.loadJournals();
RecordsReviewPage.loadTradeRecords({ soft: true });
} catch (_) {}
} else if (typeof global.loadTradeRecords === "function") {
try {
global.loadTradeRecords({ soft: true });
} catch (_) {}
}
}
@@ -63,72 +74,92 @@
resetJournalForm(form);
refreshLists();
if (data && data.chart_pending) {
// K 线后台生成,数秒后再刷一次列表拿缩略图
setTimeout(refreshLists, 4000);
setTimeout(refreshLists, 12000);
}
}
function bind(form) {
if (!form || form.dataset.journalAjaxBound === "1") return;
form.dataset.journalAjaxBound = "1";
form.addEventListener("submit", function (ev) {
if (typeof global.validateJournalEntryReason === "function") {
if (!global.validateJournalEntryReason()) {
ev.preventDefault();
return;
}
}
ev.preventDefault();
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",
headers: {
"X-Requested-With": "XMLHttpRequest",
Accept: "application/json",
},
})
.then(function (res) {
var ct = (res.headers.get("content-type") || "").toLowerCase();
if (ct.indexOf("application/json") >= 0) {
return res.json().then(function (data) {
return { ok: res.ok, data: data, redirected: false };
});
}
// 旧后端未返回 JSON 时走整页逻辑
if (res.redirected || res.ok) {
global.location.reload();
return { ok: true, data: null, redirected: true };
}
throw new Error("save failed");
})
.then(function (result) {
if (result.redirected) return;
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 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"));
}
+3
View File
@@ -589,6 +589,9 @@
updatePager("trades");
updatePager("journals");
updatePager("reviews");
if (global.JournalFormSave && typeof global.JournalFormSave.init === "function") {
global.JournalFormSave.init();
}
loadTradeRecords({ soft: false });
loadJournalsPaged();
loadReviewsPaged();