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();