反馈改右上角轻提示;侧栏加撤回重做等配置动作
设置页的横幅换成 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:
@@ -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' })
|
||||
}
|
||||
Reference in New Issue
Block a user