Files
oci-portal-dash/src/composables/useToast.ts
T
Wang Defa 5464d2dfea
CI / test (push) Successful in 27s
Release / release (push) Successful in 26s
发布 0.1.0:通知渠道、告警规则与多项体验修复
2026-07-10 17:31:19 +08:00

99 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useMessage } from 'naive-ui'
import { h, ref, type Ref, type VNodeChild } from 'vue'
type ToastKind = 'error' | 'warning'
const AUTO_CLOSE_MS = 8000
/** 超过此长度的消息自动折叠成「标题 + 展开详情」 */
const LONG_TEXT = 64
/** 长错误提炼标题:优先 hint 分隔符(request.ts 的「hinterror」),次选首个冒号操作前缀,兜底截断 */
function summarize(text: string): string {
const bar = text.indexOf('')
if (bar > 0 && bar <= 40) return text.slice(0, bar)
const colon = text.search(/|: /)
if (colon > 0 && colon <= 40) return text.slice(0, colon)
return [...text].slice(0, 40).join('') + '…'
}
function renderDetail(kind: ToastKind, detail: string, copied: Ref<boolean>): VNodeChild {
return h('div', { class: 'toast-code' }, [
h('div', { class: 'toast-code-head' }, [
h('span', { class: `toast-tag toast-tag-${kind}` }, kind === 'error' ? 'ERROR' : 'WARN'),
h(
'button',
{
class: 'toast-copy',
onClick: () => {
void navigator.clipboard.writeText(detail)
copied.value = true
},
},
copied.value ? '已复制' : '⎘ 复制',
),
]),
h('pre', { class: 'toast-code-pre' }, detail),
])
}
function renderToast(
kind: ToastKind,
text: string,
detail: string,
expanded: Ref<boolean>,
copied: Ref<boolean>,
toggle: () => void,
): VNodeChild {
const rows: VNodeChild[] = [
h('div', { class: 'toast-line' }, [
h('span', text),
h('a', { class: 'toast-toggle', onClick: toggle }, expanded.value ? '收起 ▴' : '展开详情 ▾'),
]),
]
if (expanded.value) rows.push(renderDetail(kind, detail, copied))
return h('div', { class: 'toast-body' }, rows)
}
/**
* 带可展开详情的消息封装:error/warning 传 detail 时,toast 内嵌
* 「展开详情」代码块(带复制);展开期间暂停 8s 自动关闭,收起后重新计时。
*/
export function useToast() {
const message = useMessage()
function show(kind: ToastKind, text: string, detail?: string) {
// 未显式给 detail 的超长消息(如 OCI 透传错误)自动折叠,避免整段撑爆 toast
if (!detail && text.length > LONG_TEXT) {
detail = text
text = summarize(text)
}
if (!detail) {
message[kind](text)
return
}
const expanded = ref(false)
const copied = ref(false)
let timer = 0
const inst = message[kind](
() => renderToast(kind, text, detail, expanded, copied, toggle),
{ duration: 0, closable: true },
)
const arm = () => {
timer = window.setTimeout(() => inst.destroy(), AUTO_CLOSE_MS)
}
function toggle() {
expanded.value = !expanded.value
window.clearTimeout(timer)
if (!expanded.value) arm()
}
arm()
}
return {
success: (text: string) => message.success(text),
info: (text: string) => message.info(text),
error: (text: string, detail?: string) => show('error', text, detail),
warning: (text: string, detail?: string) => show('warning', text, detail),
}
}