From bbe575f38fcf95571061fecf8dcc2962ab687890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=BF=E4=BA=91=E7=A7=8B=E6=9C=88?= <15273589815@163.com> Date: Sat, 12 Sep 2026 16:41:26 +0800 Subject: [PATCH] =?UTF-8?q?fix(portal):=20=E6=A8=A1=E6=80=81=E6=89=93?= =?UTF-8?q?=E4=B8=8D=E5=BC=80=E6=97=B6=E7=AB=8B=E5=88=BB=20resolve?= =?UTF-8?q?=EF=BC=8C=E9=81=BF=E5=85=8D=20await=20=E6=B0=B8=E4=B9=85?= =?UTF-8?q?=E6=8C=82=E8=B5=B7=EF=BC=88=E8=A1=A8=E7=8E=B0=E4=B8=BA=E7=82=B9?= =?UTF-8?q?=E5=87=BB=E6=B2=A1=E5=8F=8D=E5=BA=94=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - askConfirm/askText 在 openModal 失败时立即 resolve(false)/resolve(null): 此前 Promise 永不 resolve,await 会一直挂着,用户看到的就是点了没反应 - toast/openModal 在容器缺失时给出明确提示而不是静默失败 - 顶栏显示页面构建时间(取 portal.py 修改时间),用于一眼确认加载的不是缓存旧页 --- tools/portal.py | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/tools/portal.py b/tools/portal.py index fac6d14..4486431 100644 --- a/tools/portal.py +++ b/tools/portal.py @@ -38,6 +38,7 @@ from __future__ import annotations import argparse import sys import uuid +from datetime import datetime from pathlib import Path from typing import Any @@ -552,6 +553,7 @@ PAGE = r""" + @@ -583,8 +585,11 @@ async function jpost(url, body) { 17- 文档明确要求:不使用浏览器原生 alert/prompt 作为正式交互方案, 不能只用控制台日志代替用户提示。这里用 Toast + 模态框替代。 */ +const PAGE_BUILD = "__BUILD__"; + function toast(text, kind) { const box = $('toasts'); + if (!box) { console.error('[portal] #toasts 缺失,退回原生提示'); (kind === 'bad' ? console.error : console.log)(text); return; } const el = document.createElement('div'); el.className = 'toast ' + (kind || ''); el.textContent = text; @@ -598,19 +603,29 @@ function bad(text) { toast(text, 'bad'); } function warn(text) { toast(text, 'warn'); } function openModal(title, bodyHtml, footHtml) { + const back = $('modal-back'); + if (!back) { + // 兜底:容器缺失(多半是浏览器缓存了旧页面)时不要静默失败 + console.error('[portal] #modal-back 缺失:页面可能是旧版本,请硬刷新(Ctrl+F5)'); + window.alert(String(title) + '\n\n页面组件缺失,请硬刷新后再试(Ctrl+F5)'); + return false; + } $('modal-title').textContent = title; $('modal-body').innerHTML = bodyHtml; $('modal-foot').innerHTML = footHtml || ''; - $('modal-back').classList.add('on'); + back.classList.add('on'); + return true; } function closeModal() { $('modal-back').classList.remove('on'); } /** 替代 confirm:返回 Promise。 */ function askConfirm(title, text, okLabel) { return new Promise((resolve) => { - openModal(title, `

${esc(text)}

`, + const opened = openModal(title, `

${esc(text)}

`, ` `); + // 模态打不开时必须**立刻 resolve**,否则 await 永远挂着 —— 表现就是"点了没反应" + if (!opened) { resolve(false); return; } window.__ask = resolve; }); } @@ -621,21 +636,23 @@ function askText(title, label, options) { if (opts.choices) { return new Promise((resolve) => { const sel = opts.choices.map((c) => ``).join(''); - openModal(title, + const opened = openModal(title, ` `, ` `); + if (!opened) { resolve(null); return; } window.__ask = resolve; }); } return new Promise((resolve) => { - openModal(title, + const opened = openModal(title, `
${esc(opts.hint || '')}
`, ` `); + if (!opened) { resolve(null); return; } window.__ask = resolve; }); } @@ -735,6 +752,7 @@ function showApp() { $('who').textContent = ME.username + '(' + ME.user_id + ')'; $('role').textContent = ME.roles.join(' / ') || '无角色'; $('env').textContent = (ME.environment?.mode || '') + ' · ' + (ME.environment?.mysql || ME.environment?.target || ''); + if ($('build')) $('build').textContent = 'build ' + PAGE_BUILD; VIEW = ME.view; const tabs = [{ id: VIEW, label: ME.view_label }]; // 多角色时允许手动切到其他已具备的界面,便于一次演示 @@ -1848,6 +1866,11 @@ boot(); """ +#: 页面构建标记:取本文件的修改时间。刷新后这个值应当变化 —— +#: 用它一眼确认浏览器加载的不是缓存旧页(旧页缺 Toast/模态容器时,点击会静默失效)。 +_PAGE_BUILD = datetime.fromtimestamp(Path(__file__).stat().st_mtime).strftime("%m-%d %H:%M") +PAGE = PAGE.replace("__BUILD__", _PAGE_BUILD) + def main() -> None: parser = argparse.ArgumentParser(description="统一登录门户(按角色分流)")