反馈改右上角轻提示;侧栏加撤回重做等配置动作

设置页的横幅换成 toast(右滑入、几秒后上移淡出,web/toast.ts 单例):
动作结果看一眼就行,不值得在页面里占一块常驻位置。表单校验类的错误
仍留在各自表单里。

撤回栈就是备份链:撤回把最新备份应用回去并消费掉它,撤回前的状态进
重做栈(.patchbay-redo,同样留 10 份);重做反向;新的正常保存清空
重做栈,不留分叉历史。这两条路复用 applyConfig 的保护链路,用
restoreFrom 参数跳过自建备份——否则备份链会被撤回本身污染,撤两次
等于原地打转。侧栏左下角一排图标:撤回、重做、历史记录(备份列表
弹窗)、更新配置(POST /api/kernel/reload,给 SSH 手改后用)、重启
内核;收拢时容器变窄自动折成一列。

设置页顺手修三处:GEO 行的编辑按钮在输入框开着时再点会闪(blur 关了
click 又开),mousedown.prevent 保焦点、click 显式开关;上传按钮改
「选择或拖放文件」,投放区拖入高亮;恢复策略挪到恢复行下面。
This commit is contained in:
2026-08-12 01:59:22 +08:00
parent 2580c03bfa
commit f761d13579
9 changed files with 798 additions and 106 deletions
+43
View File
@@ -0,0 +1,43 @@
/**
* 右上角的轻提示(成就弹窗式):从右滑入一行字,几秒后上移淡出。
*
* 模块级单例,谁都能发;渲染在外壳的 ToastHost 里。适合「动作已完成/失败」
* 这类看一眼就行的反馈;需要用户处理的内容(表单校验、带重试的错误)
* 仍然留在页面里,不进这里。
*/
import { ref } from 'vue'
import { ApiError } from './api'
export interface Toast {
id: number
kind: 'ok' | 'error'
message: string
hint: string
}
export const toasts = ref<Toast[]>([])
let nextId = 1
export function toast(message: string, opts?: { kind?: 'ok' | 'error'; hint?: string }): void {
const item: Toast = {
id: nextId++,
kind: opts?.kind ?? 'ok',
message,
hint: opts?.hint ?? '',
}
toasts.value = [...toasts.value, item]
// 错误多留一会儿,看清 hint 里的下一步
const ttl = item.kind === 'error' ? 8000 : 4000
setTimeout(() => dismiss(item.id), ttl)
}
export function dismiss(id: number): void {
toasts.value = toasts.value.filter((t) => t.id !== id)
}
/** 接口报错的统一出口:message 说发生了什么,hint 说下一步。 */
export function toastError(error: unknown): void {
if (error instanceof ApiError) toast(error.message, { kind: 'error', hint: error.hint })
else toast(String(error), { kind: 'error' })
}