ui: 复盘表单紧凑布局、即时上传截图并移除占用时新开仓
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1660,63 +1660,118 @@ html[data-theme="light"] .symbol-live-price--ok {
|
||||
}
|
||||
|
||||
/* ── 复盘:四周期截图上传 / 详情四宫格 ── */
|
||||
.journal-upload-slots {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
margin-top: 10px;
|
||||
.journal-card .journal-form-row1 {
|
||||
grid-template-columns: repeat(7, minmax(0, 1fr));
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.journal-upload-slot {
|
||||
.journal-card .journal-form-row2 {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.journal-card .journal-form-row2 select[name="entry_reason"] {
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.journal-upload-slots {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: 10px;
|
||||
align-items: flex-start;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.journal-upload-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
padding: 8px 10px;
|
||||
border: 1px dashed rgba(130, 145, 190, 0.45);
|
||||
border-radius: 8px;
|
||||
background: rgba(12, 16, 32, 0.35);
|
||||
font-size: 0.82rem;
|
||||
align-items: stretch;
|
||||
gap: 4px;
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.journal-upload-slot-label {
|
||||
color: #9aa3c7;
|
||||
font-weight: 600;
|
||||
font-size: 0.78rem;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
.journal-upload-slot-input {
|
||||
font-size: 0.78rem;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.journal-upload-slot-preview {
|
||||
min-height: 72px;
|
||||
border-radius: 6px;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.journal-upload-slot-preview--filled {
|
||||
min-height: 96px;
|
||||
}
|
||||
|
||||
.journal-upload-slot-thumb {
|
||||
width: 100%;
|
||||
max-height: 140px;
|
||||
object-fit: contain;
|
||||
cursor: zoom-in;
|
||||
border-radius: 4px;
|
||||
min-width: 0;
|
||||
font-size: 0.72rem;
|
||||
padding: 3px 4px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.journal-upload-status {
|
||||
font-size: 0.68rem;
|
||||
color: #8892b0;
|
||||
min-height: 1.1em;
|
||||
line-height: 1.25;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.journal-upload-status--pending {
|
||||
color: #c9b458;
|
||||
}
|
||||
|
||||
.journal-upload-status--ok {
|
||||
color: #6bc98a;
|
||||
}
|
||||
|
||||
.journal-upload-status--err {
|
||||
color: #ff7b7b;
|
||||
}
|
||||
|
||||
.journal-upload-hint {
|
||||
margin-top: 6px;
|
||||
margin-top: 4px;
|
||||
margin-bottom: 0;
|
||||
font-size: 0.72rem;
|
||||
color: #8892b0;
|
||||
}
|
||||
|
||||
.journal-card .journal-upload-slots {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.journal-card .form-row.journal-chart-options {
|
||||
margin-top: 6px;
|
||||
margin-bottom: 6px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.journal-card .mood-grid {
|
||||
margin-top: 6px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.journal-card .journal-form-row1 {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.journal-card .journal-form-row2 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.journal-upload-slots {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.journal-upload-row {
|
||||
flex: 1 1 calc(50% - 8px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
.journal-upload-row {
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.journal-detail-images {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
@@ -1747,15 +1802,6 @@ html[data-theme="light"] .symbol-live-price--ok {
|
||||
cursor: zoom-in;
|
||||
}
|
||||
|
||||
html[data-theme="light"] .journal-upload-slot {
|
||||
background: #f6f8fb;
|
||||
border-color: #c5d0de;
|
||||
}
|
||||
|
||||
html[data-theme="light"] .journal-upload-slot-preview {
|
||||
background: #eef2f7;
|
||||
}
|
||||
|
||||
html[data-theme="light"] .journal-detail-images {
|
||||
border-top-color: #d0dae4;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@
|
||||
`预期RR:${escapeHtml(o.expect_rr || "-")}`,
|
||||
`实际RR:${escapeHtml(o.real_rr || "-")}`,
|
||||
`保本后盯盘:${escapeHtml(o.post_breakeven_stare || "-")}`,
|
||||
`占用时新开仓:${escapeHtml(o.new_trade_while_occupied || "-")}`,
|
||||
`心态标签:${escapeHtml(moodTags)}`,
|
||||
`备注:${escapeHtml(o.note || "无")}`,
|
||||
];
|
||||
|
||||
@@ -1,63 +1,139 @@
|
||||
/**
|
||||
* 复盘表单:四周期截图槽位本地预览。
|
||||
* 复盘表单:四周期截图即时上传与状态展示。
|
||||
*/
|
||||
(function (global) {
|
||||
"use strict";
|
||||
|
||||
const previewUrls = new WeakMap();
|
||||
|
||||
function clearPreview(cell) {
|
||||
if (!cell) return;
|
||||
const prev = previewUrls.get(cell);
|
||||
if (prev) {
|
||||
URL.revokeObjectURL(prev);
|
||||
previewUrls.delete(cell);
|
||||
function newDraftId() {
|
||||
if (global.crypto && typeof global.crypto.randomUUID === "function") {
|
||||
return global.crypto.randomUUID().replace(/-/g, "");
|
||||
}
|
||||
cell.innerHTML = "";
|
||||
cell.classList.remove("journal-upload-slot-preview--filled");
|
||||
var s = "";
|
||||
for (var i = 0; i < 32; i++) {
|
||||
s += Math.floor(Math.random() * 16).toString(16);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function renderPreview(cell, file, tf) {
|
||||
clearPreview(cell);
|
||||
if (!file || !cell) return;
|
||||
const url = URL.createObjectURL(file);
|
||||
previewUrls.set(cell, url);
|
||||
const img = document.createElement("img");
|
||||
img.src = url;
|
||||
img.alt = tf + " 预览";
|
||||
img.className = "journal-upload-slot-thumb";
|
||||
img.addEventListener("click", function (e) {
|
||||
e.preventDefault();
|
||||
if (typeof global.showImage === "function") {
|
||||
global.showImage(url);
|
||||
}
|
||||
});
|
||||
cell.appendChild(img);
|
||||
cell.classList.add("journal-upload-slot-preview--filled");
|
||||
function ensureDraftId(root) {
|
||||
var scope = root || document;
|
||||
var el = scope.querySelector("#journal-draft-id");
|
||||
if (!el) return "";
|
||||
if (!el.value) {
|
||||
el.value = newDraftId();
|
||||
}
|
||||
return el.value;
|
||||
}
|
||||
|
||||
function rowParts(input) {
|
||||
var row = input.closest(".journal-upload-row");
|
||||
if (!row) return {};
|
||||
return {
|
||||
row: row,
|
||||
status: row.querySelector(".journal-upload-status"),
|
||||
hidden: row.querySelector(".journal-upload-hidden-file"),
|
||||
};
|
||||
}
|
||||
|
||||
function setStatus(statusEl, text, kind) {
|
||||
if (!statusEl) return;
|
||||
statusEl.textContent = text || "";
|
||||
statusEl.classList.remove(
|
||||
"journal-upload-status--pending",
|
||||
"journal-upload-status--ok",
|
||||
"journal-upload-status--err"
|
||||
);
|
||||
if (kind) {
|
||||
statusEl.classList.add("journal-upload-status--" + kind);
|
||||
}
|
||||
}
|
||||
|
||||
function uploadSlotFile(input, file) {
|
||||
var parts = rowParts(input);
|
||||
var draftId = ensureDraftId(input.form || document);
|
||||
if (!draftId || !file) {
|
||||
setStatus(parts.status, "上传失败", "err");
|
||||
return;
|
||||
}
|
||||
|
||||
setStatus(parts.status, "上传中…", "pending");
|
||||
if (parts.hidden) {
|
||||
parts.hidden.value = "";
|
||||
}
|
||||
|
||||
var fd = new FormData();
|
||||
fd.append("journal_draft_id", draftId);
|
||||
fd.append("tf", input.getAttribute("data-tf") || "");
|
||||
fd.append("file", file);
|
||||
|
||||
fetch("/api/journal_upload_slot", { method: "POST", body: fd, credentials: "same-origin" })
|
||||
.then(function (res) {
|
||||
return res.json().then(function (data) {
|
||||
return { ok: res.ok, data: data };
|
||||
});
|
||||
})
|
||||
.then(function (result) {
|
||||
if (!result.ok || !result.data || !result.data.ok) {
|
||||
throw new Error(
|
||||
(result.data && result.data.error) || "upload failed"
|
||||
);
|
||||
}
|
||||
var fname = String(result.data.file || "").trim();
|
||||
if (parts.hidden) {
|
||||
parts.hidden.value = fname;
|
||||
}
|
||||
input.value = "";
|
||||
setStatus(parts.status, "上传成功 " + fname, "ok");
|
||||
})
|
||||
.catch(function () {
|
||||
if (parts.hidden) {
|
||||
parts.hidden.value = "";
|
||||
}
|
||||
setStatus(parts.status, "上传失败", "err");
|
||||
});
|
||||
}
|
||||
|
||||
function bindInput(input) {
|
||||
if (!input || input.dataset.journalSlotBound === "1") return;
|
||||
input.dataset.journalSlotBound = "1";
|
||||
const tf = input.getAttribute("data-tf") || "";
|
||||
const cell = input.closest(".journal-upload-slot");
|
||||
const preview = cell ? cell.querySelector(".journal-upload-slot-preview") : null;
|
||||
input.addEventListener("change", function () {
|
||||
const file = input.files && input.files[0];
|
||||
var file = input.files && input.files[0];
|
||||
if (!file) {
|
||||
clearPreview(preview);
|
||||
var parts = rowParts(input);
|
||||
if (parts.hidden) {
|
||||
parts.hidden.value = "";
|
||||
}
|
||||
setStatus(parts.status, "", "");
|
||||
return;
|
||||
}
|
||||
renderPreview(preview, file, tf);
|
||||
uploadSlotFile(input, file);
|
||||
});
|
||||
}
|
||||
|
||||
function resetSlots(root) {
|
||||
var scope = root || document;
|
||||
var draftEl = scope.querySelector("#journal-draft-id");
|
||||
if (draftEl) {
|
||||
draftEl.value = newDraftId();
|
||||
}
|
||||
scope.querySelectorAll(".journal-upload-hidden-file").forEach(function (el) {
|
||||
el.value = "";
|
||||
});
|
||||
scope.querySelectorAll(".journal-upload-slot-input").forEach(function (el) {
|
||||
el.value = "";
|
||||
});
|
||||
scope.querySelectorAll(".journal-upload-status").forEach(function (el) {
|
||||
setStatus(el, "", "");
|
||||
});
|
||||
}
|
||||
|
||||
function init(root) {
|
||||
const scope = root || document;
|
||||
var scope = root || document;
|
||||
ensureDraftId(scope);
|
||||
scope.querySelectorAll(".journal-upload-slot-input").forEach(bindInput);
|
||||
}
|
||||
|
||||
global.JournalUploadSlots = { init: init };
|
||||
global.JournalUploadSlots = { init: init, reset: resetSlots };
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
|
||||
Reference in New Issue
Block a user