Initial standalone crypto_okx with one-click deploy.
Add deploy/manage.sh bootstrap for git.bz121.com/dekun/crypto_okx and point docs at this repo. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* 复盘表单 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();
|
||||
if (typeof ev.stopImmediatePropagation === "function") ev.stopImmediatePropagation();
|
||||
return;
|
||||
}
|
||||
}
|
||||
ev.preventDefault();
|
||||
if (typeof ev.stopImmediatePropagation === "function") ev.stopImmediatePropagation();
|
||||
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);
|
||||
Reference in New Issue
Block a user