fix: show assistant member gate in place
This commit is contained in:
+19
-13
@@ -4510,13 +4510,14 @@ function renderAlerts() {
|
||||
}
|
||||
|
||||
async function openReviewAssistant() {
|
||||
if (!hasMemberAccess()) {
|
||||
openSettings("membership", "复盘助手仅对会员开放");
|
||||
return;
|
||||
}
|
||||
toggleHeaderCommandMenu(false);
|
||||
toggleAccountDropdown(false);
|
||||
if (!elements.assistantDialog.open) elements.assistantDialog.showModal();
|
||||
updateAssistantControls();
|
||||
if (!hasMemberAccess()) {
|
||||
document.querySelector("#closeAssistantDialog").focus();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const payload = await apiRequest("/api/assistant/messages");
|
||||
state.assistantMessages = payload.items || [];
|
||||
@@ -4648,10 +4649,17 @@ function renderAssistantMessages() {
|
||||
}
|
||||
|
||||
function updateAssistantControls() {
|
||||
document.querySelector("#assistantQuestion").disabled = state.assistantLoading;
|
||||
document.querySelector("#sendAssistant").disabled = state.assistantLoading;
|
||||
document.querySelector("#stopAssistant").hidden = !state.assistantLoading;
|
||||
document.querySelector("#clearAssistantMessages").disabled = state.assistantLoading || !state.assistantMessages.length;
|
||||
const unlocked = hasMemberAccess();
|
||||
elements.assistantDialog.classList.toggle("member-locked", !unlocked);
|
||||
document.querySelector("#assistantMemberGate").hidden = unlocked;
|
||||
document.querySelector("#assistantMemberContent").setAttribute("aria-disabled", String(!unlocked));
|
||||
document.querySelector("#assistantQuestion").disabled = !unlocked || state.assistantLoading;
|
||||
document.querySelector("#sendAssistant").disabled = !unlocked || state.assistantLoading;
|
||||
document.querySelector("#stopAssistant").hidden = !unlocked || !state.assistantLoading;
|
||||
document.querySelector("#clearAssistantMessages").disabled = !unlocked || state.assistantLoading || !state.assistantMessages.length;
|
||||
document.querySelectorAll("[data-assistant-prompt]").forEach((button) => {
|
||||
button.disabled = !unlocked || state.assistantLoading;
|
||||
});
|
||||
}
|
||||
|
||||
function openGlobalSearch() {
|
||||
@@ -5028,6 +5036,7 @@ function applyMembershipAccess() {
|
||||
const assistantButton = document.querySelector("#assistantButton");
|
||||
assistantButton.classList.toggle("member-locked-control", !unlocked);
|
||||
assistantButton.title = unlocked ? "复盘助手" : "复盘助手(会员可用)";
|
||||
updateAssistantControls();
|
||||
}
|
||||
|
||||
function openView(viewId, updateHash = true) {
|
||||
@@ -5188,13 +5197,10 @@ function selectAccountPanel(panel) {
|
||||
return selected;
|
||||
}
|
||||
|
||||
async function openSettings(panel = "profile", accessNotice = "") {
|
||||
const selectedPanel = selectAccountPanel(panel);
|
||||
async function openSettings(panel = "profile") {
|
||||
selectAccountPanel(panel);
|
||||
toggleAccountDropdown(false);
|
||||
toggleHeaderCommandMenu(false);
|
||||
const membershipNotice = document.querySelector("#membershipAccessNotice");
|
||||
membershipNotice.textContent = accessNotice;
|
||||
membershipNotice.hidden = selectedPanel !== "membership" || !accessNotice;
|
||||
const status = document.querySelector("#connectionStatus");
|
||||
status.className = "connection-status";
|
||||
status.textContent = "正在读取账号状态";
|
||||
|
||||
+22
-17
@@ -1148,24 +1148,30 @@
|
||||
<button id="closeAssistantDialog" class="icon-button" type="button" aria-label="关闭" title="关闭"><i data-lucide="x"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="assistantMessages" class="assistant-messages" aria-live="polite">
|
||||
<div class="empty-state">可以从市场、策略或自己的交易记录开始复盘</div>
|
||||
<div id="assistantMemberGate" class="member-gate assistant-member-gate" hidden>
|
||||
<div class="member-gate-icon"><i data-lucide="lock-keyhole"></i></div>
|
||||
<div><strong>复盘助手仅对会员开放</strong><span>开通会员后可读取市场统计、策略跟踪与个人复盘记录,进行统一复盘分析。</span></div>
|
||||
</div>
|
||||
<div class="assistant-quick-prompts" aria-label="快捷问题">
|
||||
<button type="button" data-assistant-prompt="结合近十日情绪和今天的数据,当前市场处于什么位置?">市场位置</button>
|
||||
<button type="button" data-assistant-prompt="总结当前市场主线,并说明证据和可能的失效条件。">市场主线</button>
|
||||
<button type="button" data-assistant-prompt="结合我的策略跟踪和交易日志,找出最值得修正的一个行为模式。">交易复盘</button>
|
||||
<button type="button" data-assistant-prompt="为下一个交易日给出条件化观察清单,不给无条件买卖指令。">明日清单</button>
|
||||
</div>
|
||||
<form id="assistantForm" class="assistant-form">
|
||||
<label class="sr-only" for="assistantQuestion">向复盘助手提问</label>
|
||||
<textarea id="assistantQuestion" maxlength="2000" placeholder="询问市场位置、主线、策略表现或自己的交易模式" required></textarea>
|
||||
<div class="assistant-form-actions">
|
||||
<button id="stopAssistant" class="button" type="button" hidden><i data-lucide="square"></i>停止</button>
|
||||
<button id="sendAssistant" class="button primary" type="submit"><i data-lucide="send"></i>发送</button>
|
||||
<div id="assistantMemberContent" class="assistant-member-content">
|
||||
<div id="assistantMessages" class="assistant-messages" aria-live="polite">
|
||||
<div class="empty-state">可以从市场、策略或自己的交易记录开始复盘</div>
|
||||
</div>
|
||||
</form>
|
||||
<p class="assistant-disclaimer">助手仅用于复盘与条件化计划,不执行交易,不构成投资建议。</p>
|
||||
<div class="assistant-quick-prompts" aria-label="快捷问题">
|
||||
<button type="button" data-assistant-prompt="结合近十日情绪和今天的数据,当前市场处于什么位置?">市场位置</button>
|
||||
<button type="button" data-assistant-prompt="总结当前市场主线,并说明证据和可能的失效条件。">市场主线</button>
|
||||
<button type="button" data-assistant-prompt="结合我的策略跟踪和交易日志,找出最值得修正的一个行为模式。">交易复盘</button>
|
||||
<button type="button" data-assistant-prompt="为下一个交易日给出条件化观察清单,不给无条件买卖指令。">明日清单</button>
|
||||
</div>
|
||||
<form id="assistantForm" class="assistant-form">
|
||||
<label class="sr-only" for="assistantQuestion">向复盘助手提问</label>
|
||||
<textarea id="assistantQuestion" maxlength="2000" placeholder="询问市场位置、主线、策略表现或自己的交易模式" required></textarea>
|
||||
<div class="assistant-form-actions">
|
||||
<button id="stopAssistant" class="button" type="button" hidden><i data-lucide="square"></i>停止</button>
|
||||
<button id="sendAssistant" class="button primary" type="submit"><i data-lucide="send"></i>发送</button>
|
||||
</div>
|
||||
</form>
|
||||
<p class="assistant-disclaimer">助手仅用于复盘与条件化计划,不执行交易,不构成投资建议。</p>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<dialog id="settingsDialog" class="settings-dialog">
|
||||
@@ -1179,7 +1185,6 @@
|
||||
<div id="connectionStatus" class="connection-status" aria-live="polite">正在读取账号状态</div>
|
||||
<section class="settings-section membership-overview account-settings-panel" data-account-panel-content="membership" hidden>
|
||||
<div class="settings-section-heading"><h3>会员状态</h3><span id="membershipBadge">普通用户</span></div>
|
||||
<p id="membershipAccessNotice" class="membership-access-notice" role="status" aria-live="polite" hidden></p>
|
||||
<p id="membershipDetail" class="settings-lead">开通会员后可使用智能选股、问师、问天、复盘助手等智能功能。</p>
|
||||
<p id="membershipQuotaHint" class="form-hint">会员默认每日智能分析额度 -- 次,由管理员统一设置。</p>
|
||||
<div id="membershipUsage" class="membership-usage">今日智能分析 -- 次</div>
|
||||
|
||||
+10
-11
@@ -11014,17 +11014,6 @@ button.account-role-badge:focus-visible { outline: 2px solid var(--blue); outlin
|
||||
.membership-status-grid > div { padding: 12px 13px; border: 1px solid var(--line, #e2e8ee); border-radius: 10px; background: #fbfcfd; }
|
||||
.membership-status-grid span { display: block; color: #74808c; font-size: 11px; }
|
||||
.membership-status-grid strong { display: block; margin-top: 5px; color: #26323d; font-size: 15px; font-variant-numeric: tabular-nums; }
|
||||
.membership-access-notice {
|
||||
margin: 0 0 12px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #ead9ac;
|
||||
border-radius: 6px;
|
||||
background: #fff9ea;
|
||||
color: #775316;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.membership-comparison { overflow: hidden; border: 1px solid var(--line, #e2e8ee); border-radius: 10px; font-size: 12px; }
|
||||
.membership-comparison > div { display: grid; grid-template-columns: minmax(0, 1.6fr) .7fr .7fr; gap: 8px; align-items: center; padding: 9px 11px; border-top: 1px solid var(--line, #e2e8ee); }
|
||||
.membership-comparison > div:first-child { border-top: 0; }
|
||||
@@ -11544,6 +11533,14 @@ button.account-role-badge:focus-visible { outline: 2px solid var(--blue); outlin
|
||||
|
||||
.assistant-button.member-locked-control { color: var(--text-secondary); opacity: 0.72; }
|
||||
.assistant-dialog { width: min(820px, calc(100vw - 32px)); max-height: min(860px, calc(100dvh - 32px)); }
|
||||
.assistant-member-gate { margin: 14px 18px 0; }
|
||||
.assistant-member-content { transition: opacity var(--motion-medium) ease, filter var(--motion-medium) ease; }
|
||||
.assistant-dialog.member-locked .assistant-member-content {
|
||||
opacity: .42;
|
||||
filter: grayscale(.38);
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
.assistant-messages { min-height: 320px; max-height: min(540px, calc(100dvh - 340px)); overflow-y: auto; padding: 18px; background: var(--surface-muted); scroll-behavior: smooth; }
|
||||
.assistant-message { max-width: 88%; margin-bottom: 14px; }
|
||||
.assistant-message.user { margin-left: auto; }
|
||||
@@ -11588,6 +11585,7 @@ button.account-role-badge:focus-visible { outline: 2px solid var(--blue); outlin
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.assistant-dialog { width: calc(100vw - 16px); max-height: calc(100dvh - 16px); margin: 8px auto; }
|
||||
.assistant-member-gate { margin: 10px 13px 0; }
|
||||
.assistant-messages { min-height: 260px; max-height: calc(100dvh - 390px); padding: 13px; }
|
||||
.assistant-message { max-width: 96%; }
|
||||
.assistant-form { grid-template-columns: 1fr; }
|
||||
@@ -11596,6 +11594,7 @@ button.account-role-badge:focus-visible { outline: 2px solid var(--blue); outlin
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.assistant-member-content { transition: none; }
|
||||
.assistant-stream-caret { animation: none; }
|
||||
.assistant-messages { scroll-behavior: auto; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user