feat: toast notifications replace URL parameter messages — auto-dismiss, no page reload

This commit is contained in:
tian 2026-07-17 17:43:56 +08:00
parent b2fbae9e4b
commit 53577b0ea6
3 changed files with 42 additions and 2 deletions

View File

@ -16,7 +16,7 @@
|---|------|------|------|------|
| 6 | 告警浏览器通知SSE / 标题闪烁) | ✅ 已完成 | 2026-07-17 | 2026-07-17 |
| 7 | 设备列表服务健康指示 | ✅ 已完成 | 2026-07-17 | 2026-07-17 |
| 8 | 操作结果提示优化toast 替代 URL 消息) | ⬜ 待开始 | - | - |
| 8 | 操作结果提示优化toast 替代 URL 消息) | ✅ 已完成 | 2026-07-17 | 2026-07-17 |
| 9 | 术语统一 | ⬜ 待开始 | - | - |
## P2 — 长期优化
@ -42,6 +42,11 @@
- 3 步引导:选设备 → 配检测(从设备读取运行通道,下拉选已有视频源或手动添加) → 一键下发
- 标准模式可见,复用的 AutoConfigService 链路
- **P1-8 操作结果提示优化**
- JS 读取 URL 中 `?msg=` / `?error=` 参数,显示为右上角浮动 toast
- 绿色成功 / 红色失败4 秒自动滑出消失
- 展示同时清理 URL刷新不重复显示
- **P1-7 设备服务健康指示**
- 模板本已有媒体服务状态(运行中/未运行/待确认)
- 新增:在线但媒体服务异常的行背景变淡橙色警告
@ -74,4 +79,4 @@
---
**已完成:5/1533.3%**
**已完成:6/1540%**

View File

@ -150,6 +150,13 @@ code,.mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
.topbar-icon-btn .ui-icon{width:16px;height:16px}
.topbar-dot{position:absolute;right:6px;top:6px;width:6px;height:6px;border-radius:999px;background:var(--red)}
.topbar-badge{position:absolute;right:-4px;top:-4px;min-width:16px;height:16px;padding:0 4px;border-radius:8px;background:var(--red);color:#fff;font-size:10px;font-weight:600;line-height:16px;text-align:center}
.toast-container{position:fixed;top:68px;right:20px;z-index:9999;display:flex;flex-direction:column;gap:8px;pointer-events:none}
.toast{display:flex;align-items:center;gap:8px;padding:10px 14px;border-radius:var(--radius);font-size:13px;font-weight:500;box-shadow:var(--shadow);pointer-events:auto;animation:toastIn .25s ease}
.toast.success{background:var(--surface-strong);border:1px solid var(--green);color:var(--green)}
.toast.error{background:var(--danger-soft);border:1px solid var(--danger-soft-hover);color:var(--danger-soft-text)}
.toast.out{animation:toastOut .25s ease forwards}
@keyframes toastIn{from{opacity:0;transform:translateX(40px)}to{opacity:1;transform:translateX(0)}}
@keyframes toastOut{from{opacity:1;transform:translateX(0)}to{opacity:0;transform:translateX(40px)}}
.theme-menu{position:relative}
.theme-menu-panel{position:fixed;right:28px;top:54px;min-width:132px;padding:6px;border:1px solid var(--border-strong);border-radius:var(--radius);background:var(--surface);box-shadow:var(--shadow);z-index:9999}
.theme-menu-panel button{display:flex;width:100%;justify-content:flex-start;min-height:28px;border-color:transparent;background:transparent;color:var(--text)}

View File

@ -87,6 +87,7 @@
</main>
</div>
</div>
<div id="toast-container" class="toast-container"></div>
<script src="/ui/assets/vendor/tabler.min.js"></script>
<script>
(function () {
@ -389,6 +390,33 @@
setInterval(pollAlarmCount, 15000);
var container = document.getElementById("toast-container");
if (container) {
var params = new URLSearchParams(window.location.search);
function showToast(text, type) {
var el = document.createElement("div");
el.className = "toast " + type;
el.textContent = text;
container.appendChild(el);
setTimeout(function(){
el.classList.add("out");
setTimeout(function(){el.remove()}, 250);
}, 4000);
}
var msg = params.get("msg");
var err = params.get("error");
if (msg) showToast(decodeURIComponent(msg), "success");
if (err) showToast(decodeURIComponent(err), "error");
if (msg || err) {
params.delete("msg");
params.delete("error");
var newUrl = window.location.pathname;
var qs = params.toString();
if (qs) newUrl += "?" + qs;
history.replaceState(null, "", newUrl);
}
}
})();
</script>
</body>