8f4ae8ad31
Defer exchange chart generation to a background thread and save via XHR so fill-from-trade no longer blocks on full page reload. Co-authored-by: Cursor <cursoragent@cursor.com>
143 lines
4.2 KiB
JavaScript
143 lines
4.2 KiB
JavaScript
/**
|
|
* 复盘表单 AJAX 保存:避免整页刷新卡顿;配合 FormSubmitGuard 即时反馈.
|
|
*/
|
|
(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 (typeof global.loadJournals === "function") {
|
|
try {
|
|
global.loadJournals();
|
|
} catch (_) {}
|
|
} else if (global.RecordsReviewPage && typeof RecordsReviewPage.loadJournals === "function") {
|
|
try {
|
|
RecordsReviewPage.loadJournals();
|
|
} 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) {
|
|
// 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 init() {
|
|
bind($("journal-form"));
|
|
}
|
|
|
|
global.JournalFormSave = { init: init, bind: bind };
|
|
|
|
if (document.readyState === "loading") {
|
|
document.addEventListener("DOMContentLoaded", init);
|
|
} else {
|
|
init();
|
|
}
|
|
})(typeof window !== "undefined" ? window : globalThis);
|