feat(web): 不支持内嵌时明确弹层;修掉被 promise 吞掉的 ReferenceError
1) 点不支持在工作区显示的应用 → 弹出提示卡片 标题「该应用不支持在工作区显示」+ 域名 + 服务器返回的原因 + 「在新标签页打开 / 知道了」两个按钮;面板里同时保留同样的说明。 之前只在面板里显示一行字,用户点了之后缺少明确反馈。 2) 修 ReferenceError: openUrl is not defined(线上真实故障) setWorkspaceState 第 4 个参数早先从 openUrl 改名为 note 时,blocked 分支漏改,于是每次被拒内嵌都在函数内部抛异常,异常沿 promise 链被吞, 导致后面的弹层根本没有执行、面板提示也只渲染到一半(URL 行和按钮缺失)。 教训:验收必须采集 Runtime.exceptionThrown,并且断言交互元素真的存在; 只断言"状态=blocked + 提示文字前缀"会恰好掩盖这类错误。
This commit is contained in:
@ -25,29 +25,8 @@ pageInfo:
|
||||
description: Self-hosted unified navigation
|
||||
navLinks: []
|
||||
footer: ''
|
||||
sections:
|
||||
- name: Favorites
|
||||
icon: fas fa-star
|
||||
collapsed: false
|
||||
items:
|
||||
- title: Gitea
|
||||
description: Code repository
|
||||
icon: fas fa-code-branch
|
||||
url: http://192.168.1.100:3000
|
||||
statusCheck: true
|
||||
- name: History
|
||||
icon: fas fa-history
|
||||
collapsed: false
|
||||
items:
|
||||
- title: 安装浏览历史插件
|
||||
description: 一键安装,自动按访问频次排序
|
||||
icon: fas fa-download
|
||||
url: /extension/install.html
|
||||
- name: Tools
|
||||
icon: fas fa-tools
|
||||
collapsed: true
|
||||
items:
|
||||
- title: Example Service
|
||||
description: Add your own services
|
||||
icon: fas fa-cog
|
||||
url: http://192.168.1.100:8080
|
||||
sections: []
|
||||
# 分组由管理后台(/admin/ 的「导航分组」)增删,初始为空:
|
||||
# 新建一个分组后再往里添加服务即可。
|
||||
# 「历史访问」分组由 history-server 根据浏览记录自动创建,
|
||||
# 请不要手工写在这里。
|
||||
|
||||
@ -537,11 +537,25 @@ func rebuildDashySections() {
|
||||
break
|
||||
}
|
||||
}
|
||||
foundHist := false
|
||||
for _, si := range secs {
|
||||
if isHistSection(si.name) {
|
||||
si.raw["items"] = histItems
|
||||
foundHist = true
|
||||
}
|
||||
}
|
||||
// 配置里没有「历史访问」分组时补一个。该分组只能由这里创建:
|
||||
// 管理后台明确禁止手工新建/改名/删除它(否则下次同步就被覆写),
|
||||
// 所以若只靠用户手动建,功能就一直不会出现。
|
||||
if !foundHist {
|
||||
sectionsRaw = append(sectionsRaw, map[string]any{
|
||||
"name": histSectionZh,
|
||||
"icon": "fas fa-history",
|
||||
"collapsed": false,
|
||||
"items": histItems,
|
||||
})
|
||||
fmt.Println("[history] 已补建「历史访问」分组")
|
||||
}
|
||||
}
|
||||
|
||||
cfg["sections"] = sectionsRaw
|
||||
|
||||
78
web/navi.css
78
web/navi.css
@ -1034,6 +1034,84 @@ html[data-theme] .navi-ws-notice-url {
|
||||
word-break: break-all !important;
|
||||
}
|
||||
|
||||
/* ── 「不支持在工作区显示」弹层 ──
|
||||
被拒内嵌时不能只是面板里一行字:用户点了必须有明确反馈。
|
||||
z-index 低于 Dashy 自己的配置弹层(10000),避免挡住它。 */
|
||||
html[data-theme] .navi-ws-modal {
|
||||
position: fixed !important;
|
||||
inset: 0 !important;
|
||||
z-index: 9990 !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
padding: 18px !important;
|
||||
background: rgba(15, 23, 42, .28) !important;
|
||||
backdrop-filter: blur(4px) !important;
|
||||
-webkit-backdrop-filter: blur(4px) !important;
|
||||
animation: navi-fade-in .18s ease both;
|
||||
}
|
||||
|
||||
html[data-theme] .navi-ws-modal-card {
|
||||
box-sizing: border-box !important;
|
||||
width: min(420px, 100%) !important;
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
align-items: center !important;
|
||||
gap: 8px !important;
|
||||
padding: 24px 22px 20px !important;
|
||||
text-align: center !important;
|
||||
background: rgba(255, 255, 255, .96) !important;
|
||||
border: 1px solid rgba(255, 255, 255, .9) !important;
|
||||
border-radius: 20px !important;
|
||||
box-shadow: 0 26px 60px rgba(15, 23, 42, .28), inset 0 1px 0 rgba(255, 255, 255, .95) !important;
|
||||
animation: navi-fade-up .22s cubic-bezier(.22, 1, .36, 1) both;
|
||||
}
|
||||
|
||||
html[data-theme] .navi-ws-modal-title {
|
||||
color: #0f172a !important;
|
||||
font-size: 17px !important;
|
||||
font-weight: 650 !important;
|
||||
}
|
||||
|
||||
html[data-theme] .navi-ws-modal-host {
|
||||
color: #0284c7 !important;
|
||||
font-size: 13px !important;
|
||||
word-break: break-all !important;
|
||||
}
|
||||
|
||||
html[data-theme] .navi-ws-modal-text {
|
||||
color: #64748b !important;
|
||||
font-size: 13.5px !important;
|
||||
line-height: 1.5 !important;
|
||||
}
|
||||
|
||||
html[data-theme] .navi-ws-modal-actions {
|
||||
display: flex !important;
|
||||
flex-wrap: wrap !important;
|
||||
gap: 10px !important;
|
||||
justify-content: center !important;
|
||||
margin-top: 10px !important;
|
||||
}
|
||||
|
||||
html[data-theme] .navi-ws-modal-ok {
|
||||
padding: 9px 18px !important;
|
||||
color: #334155 !important;
|
||||
font-family: inherit !important;
|
||||
font-size: 13.5px !important;
|
||||
background: rgba(241, 245, 249, .95) !important;
|
||||
border: 1px solid rgba(203, 213, 225, .9) !important;
|
||||
border-radius: 999px !important;
|
||||
cursor: pointer;
|
||||
-webkit-tap-highlight-color: transparent !important;
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
html[data-theme] .navi-ws-modal-ok:hover,
|
||||
html[data-theme] .navi-ws-modal-ok:focus {
|
||||
color: #0f172a !important;
|
||||
background: rgba(255, 255, 255, .98) !important;
|
||||
}
|
||||
|
||||
html[data-theme] .navi-ws-open {
|
||||
margin-top: 6px !important;
|
||||
padding: 9px 18px !important;
|
||||
|
||||
61
web/navi.js
61
web/navi.js
@ -290,7 +290,9 @@
|
||||
notice.append(wsText('navi-ws-notice-text', reason
|
||||
? '服务器返回:' + reason
|
||||
: '服务器返回了禁止内嵌的响应头。'));
|
||||
notice.append(wsText('navi-ws-notice-url', openUrl || url || ''));
|
||||
// 注意:这里只能用 url。早先本函数第 4 个参数叫 openUrl,改成 note 之后
|
||||
// 本分支漏改,导致每次被拒内嵌都抛 ReferenceError,后面的弹层根本执行不到。
|
||||
notice.append(wsText('navi-ws-notice-url', url || ''));
|
||||
const btn = document.createElement('button');
|
||||
btn.type = 'button';
|
||||
btn.className = 'navi-ws-open';
|
||||
@ -298,7 +300,7 @@
|
||||
btn.addEventListener('click', (evt) => {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
if (nativeWindowOpen) nativeWindowOpen(openUrl || url, '_blank');
|
||||
if (nativeWindowOpen) nativeWindowOpen(url, '_blank');
|
||||
});
|
||||
notice.append(btn);
|
||||
}
|
||||
@ -357,6 +359,60 @@
|
||||
return bar;
|
||||
}
|
||||
|
||||
// 被拒内嵌时明确弹一个提示(不只是面板里的一行字):
|
||||
// 用户点了却什么也没发生是最差的体验。
|
||||
let wsModal = null;
|
||||
|
||||
function closeWorkspaceModal() {
|
||||
if (wsModal && wsModal.overlay.parentNode) wsModal.overlay.parentNode.removeChild(wsModal.overlay);
|
||||
wsModal = null;
|
||||
}
|
||||
|
||||
function showWorkspaceBlockedModal(url, reason) {
|
||||
closeWorkspaceModal();
|
||||
const overlay = document.createElement('div');
|
||||
overlay.className = 'navi-ws-modal';
|
||||
const card = document.createElement('div');
|
||||
card.className = 'navi-ws-modal-card';
|
||||
const title = document.createElement('div');
|
||||
title.className = 'navi-ws-modal-title';
|
||||
title.textContent = '该应用不支持在工作区显示';
|
||||
const host = document.createElement('div');
|
||||
host.className = 'navi-ws-modal-host';
|
||||
host.textContent = hostOf(url);
|
||||
const text = document.createElement('div');
|
||||
text.className = 'navi-ws-modal-text';
|
||||
text.textContent = reason
|
||||
? ('服务器返回:' + reason + ',所以它不允许被嵌在其它页面里。')
|
||||
: '服务器返回了禁止内嵌的响应头,所以它不允许被嵌在其它页面里。';
|
||||
const actions = document.createElement('div');
|
||||
actions.className = 'navi-ws-modal-actions';
|
||||
const openBtn = document.createElement('button');
|
||||
openBtn.type = 'button';
|
||||
openBtn.className = 'navi-ws-open';
|
||||
openBtn.textContent = '在新标签页打开';
|
||||
openBtn.addEventListener('click', () => {
|
||||
closeWorkspaceModal();
|
||||
if (nativeWindowOpen) nativeWindowOpen(url, '_blank');
|
||||
});
|
||||
const okBtn = document.createElement('button');
|
||||
okBtn.type = 'button';
|
||||
okBtn.className = 'navi-ws-modal-ok';
|
||||
okBtn.textContent = '知道了';
|
||||
okBtn.addEventListener('click', closeWorkspaceModal);
|
||||
actions.append(openBtn, okBtn);
|
||||
card.append(title, host, text, actions);
|
||||
overlay.append(card);
|
||||
overlay.addEventListener('click', (evt) => {
|
||||
if (evt.target === overlay) closeWorkspaceModal();
|
||||
});
|
||||
document.addEventListener('keydown', (evt) => {
|
||||
if (evt.key === 'Escape') closeWorkspaceModal();
|
||||
});
|
||||
document.body.append(overlay);
|
||||
wsModal = { overlay };
|
||||
}
|
||||
|
||||
async function frameable(url) {
|
||||
if (wsFrameCache.has(url)) return wsFrameCache.get(url);
|
||||
let info = { frameable: true, conclusive: false, reason: '' };
|
||||
@ -393,6 +449,7 @@
|
||||
wsLastUrl = '';
|
||||
parts.frame.removeAttribute('src');
|
||||
setWorkspaceState('blocked', url, info.reason, url);
|
||||
showWorkspaceBlockedModal(url, info.reason);
|
||||
return;
|
||||
}
|
||||
wsLastUrl = url;
|
||||
|
||||
Reference in New Issue
Block a user