From b30ce409ea63597947b98f02192d1d5622419497 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=BD=E5=B7=A5=E5=91=98?= <4846a27a-ba87-49c9-939d-7275f63efcc7@agents.multica.local> Date: Tue, 25 Aug 2026 02:11:46 +0800 Subject: [PATCH] =?UTF-8?q?fix(HEL-105):=20P3b=E6=80=BB=E5=B7=A5=E8=BF=94?= =?UTF-8?q?=E5=B7=A5=E5=9B=9B=E6=9D=A1=E6=95=B4=E6=94=B9=E2=80=94=E2=80=94?= =?UTF-8?q?=E6=8F=90=E9=86=92=E8=AF=A6=E6=83=85=E5=B7=B2=E8=AF=BB/?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E4=B8=8E=E5=85=B3=E8=81=94=E8=82=A1=E7=A5=A8?= =?UTF-8?q?=E5=A4=B1=E6=95=88=E3=80=81=E7=BC=96=E8=BE=91=E4=BA=A4=E6=98=93?= =?UTF-8?q?=E5=8A=A8=E4=BD=9C=E6=83=85=E7=BB=AA=E8=A2=AB=E9=87=8D=E7=BD=AE?= =?UTF-8?q?=E3=80=81=E8=87=AA=E9=80=89=E8=82=A1=E8=B7=9F=E8=B8=AA=E5=A4=87?= =?UTF-8?q?=E6=B3=A8=E7=BC=96=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 提醒详情:委派先匹配 data-review-alert-read/delete,列表行收窄为 .m-alert-row[data-alert-id] - 提醒详情关联股票:按钮补 data-alert-stock 属性,接通 openDetailSheet - 编辑交易:openForm 从 opts.trade 带入 action/emotion,不再强制 buy/calm - 自选股:双图详情补「跟踪备注」展示+编辑抽屉(≤240),保存走 /api/watchlist upsert 并刷新列表 Co-authored-by: multica-agent --- app/frontend/m/css/features.css | 34 ++++++++++++++ app/frontend/m/js/pages.js | 81 +++++++++++++++++++++++++++++++-- 2 files changed, 111 insertions(+), 4 deletions(-) diff --git a/app/frontend/m/css/features.css b/app/frontend/m/css/features.css index 455d1a2..bb169e8 100644 --- a/app/frontend/m/css/features.css +++ b/app/frontend/m/css/features.css @@ -2765,6 +2765,40 @@ .m-num-strong { font-weight: 600; } .m-cell-text--empty { color: var(--text-tertiary); } +/* 详情 Sheet 内跟踪备注行 */ +.m-remark-row { + display: flex; + align-items: center; + gap: 10px; + margin: 12px 0 0; + padding: 10px 12px; + background: var(--surface); + border-radius: 8px; +} +.m-remark-label { flex: 0 0 auto; font-size: 12px; color: var(--text-secondary); } +.m-remark-text { + flex: 1 1 auto; + min-width: 0; + font-size: 13.5px; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.m-remark-edit { + flex: 0 0 auto; + appearance: none; + border: none; + background: transparent; + color: var(--action); + font-size: 13px; + font-weight: 600; + cursor: pointer; + padding: 4px 2px; + -webkit-tap-highlight-color: transparent; +} +.m-remark-edit:active { opacity: 0.7; } + /* 自选股:颜色圆点标记 */ .m-stock--watch { flex-direction: row; align-items: center; gap: 8px; } .m-stock-copy { display: flex; flex-direction: column; align-items: flex-start; gap: 1px; min-width: 0; } diff --git a/app/frontend/m/js/pages.js b/app/frontend/m/js/pages.js index 251a665..6ca154f 100644 --- a/app/frontend/m/js/pages.js +++ b/app/frontend/m/js/pages.js @@ -3392,6 +3392,63 @@ }); } + function openRemarkEditSheet() { + const detail = state.detail; + if (!detail || !detail.code) return; + const item = detail.watchItem || {}; + const current = item.remark || ""; + openSheet( + '

编辑跟踪备注

' + + '
" + + '
' + + '" + + '
' + + "
", + { detail: false } + ); + const input = document.getElementById("m-remark-input"); + if (input) { + input.addEventListener("input", function () { + const counter = document.querySelector('[data-counter-for="m-remark-input"]'); + if (counter) counter.textContent = input.value.length + "/240"; + }); + } + } + + function saveRemark() { + const detail = state.detail; + if (!detail || !detail.code) return; + const item = detail.watchItem || {}; + const code = detail.code; + const remark = document.getElementById("m-remark-input").value.trim(); + const payload = { + code: code, + name: item.name || detail.name || code, + sector: item.sector || detail.sector || "其他", + color: item.color || "red", + remark: remark + }; + global.MobileAPI.request("/api/watchlist", "POST", payload).then(function () { + const refresh = state.review && state.review.watchlist + ? global.MobileAPI.request("/api/watchlist?trade_date=" + encodeURIComponent(todayString())).then(function (res) { + state.review.watchlist = res || state.review.watchlist; + renderReviewWatchlist(); + }) + : Promise.resolve(); + return refresh; + }).then(function () { + closeSheet(); + openDetailSheet(code); + showToast("备注已保存"); + }).catch(function (error) { + showToast(error && error.message ? error.message : "保存失败"); + }); + } + function showToast(message) { const root = document.getElementById("m-sheet-root"); if (!root) return; @@ -3427,6 +3484,7 @@ name: name === "--" ? "" : name, sector: industry === "其他" ? "" : industry, watchlist: Boolean(stock.watchlist), + watchItem: stock.watchlist || null, tab: "intraday", payload: payload, }; @@ -3440,6 +3498,15 @@ const intradayOk = meta.intraday_status === "available"; const chartHtml = intradayOk ? intradayChart(payload) : emptyChart(meta.intraday_notice || "分时数据暂不可用"); + const watchItem = stock.watchlist || null; + const remarkRow = watchItem + ? '
' + + '跟踪备注' + + '' + escapeHtml(watchItem.remark || "未填写") + "" + + '' + + "
" + : ""; + body.innerHTML = '
' + '
' + @@ -3454,6 +3521,7 @@ '' + escapeHtml(chartCaptionHtml("intraday")) + "" + "
" + '
' + chartHtml + "
" + + remarkRow + '
点按图面可读数值 · 数据来源:本地行情
'; } @@ -4207,7 +4275,7 @@ '

' + escapeHtml(item.title) + "

" + '
' + escapeHtml(displayCompactDate(item.available_date)) + (upcoming ? "(未到期)" : "") + "
" + (item.content ? '
' + escapeHtml(item.content) + "
" : "") + - (item.code ? '" : "") + + (item.code ? '" : "") + "
" + '
' + (canRead ? '' : "") + @@ -4258,7 +4326,8 @@ /* ----- 全屏表单页 ----- */ function openForm(kind, opts) { - state.review.form = { kind: kind, opts: opts || {}, action: "buy", emotion: "calm", stock: null }; + const t = (opts && opts.trade) || {}; + state.review.form = { kind: kind, opts: opts || {}, action: t.action || "buy", emotion: t.emotion || "calm", stock: null }; state.review.dirty = false; const title = kind === "trade" ? ((opts && opts.trade) ? "编辑交易" : "新增交易") : kind === "daily" ? "写复盘" : "新增笔记"; global.MobileRouter.updateHeader({ title: title, back: true, actions: "" }); @@ -4946,12 +5015,12 @@ if (reviewDeleteNote) { deleteNote(reviewDeleteNote.dataset.noteId); return; } const noteCard = event.target.closest("[data-note-id]"); if (noteCard) { openNoteDetail(noteCard.dataset.noteId); return; } - const alertRow = event.target.closest("[data-alert-id]"); - if (alertRow) { openAlertDetail(alertRow.dataset.alertId); return; } const alertRead = event.target.closest("[data-review-alert-read]"); if (alertRead) { markAlertRead(alertRead.dataset.alertId); return; } const alertDelete = event.target.closest("[data-review-alert-delete]"); if (alertDelete) { deleteAlert(alertDelete.dataset.alertId); return; } + const alertRow = event.target.closest(".m-alert-row[data-alert-id]"); + if (alertRow) { openAlertDetail(alertRow.dataset.alertId); return; } const alertFilter = event.target.closest("[data-review-alert-filter]"); if (alertFilter) { state.review.alertStatus = alertFilter.dataset.reviewAlertFilter === "unread" ? "unread" : "all"; @@ -4964,6 +5033,10 @@ if (alertSave) { submitAlertAdd(); return; } const alertStock = event.target.closest("[data-alert-stock]"); if (alertStock) { openDetailSheet(alertStock.dataset.code); return; } + const remarkEdit = event.target.closest("[data-remark-edit]"); + if (remarkEdit) { openRemarkEditSheet(); return; } + const remarkSave = event.target.closest("[data-remark-save]"); + if (remarkSave) { saveRemark(); return; } const row = event.target.closest("[data-code]"); if (row) {