Toast 轻提示
轻提示只通报结果、不打断操作,通常出现在保存成功、请求失败、状态切换之后。它同时提供两种用法:编程式的 toast 服务适合在事件回调与请求封装里随手调用;声明式的 MToast 组件适合需要跟随 v-model、或正文要放复杂节点的场景,两者定位与样式一致。
代码演示
语义类型(编程式)
toast.show / success / error / warning / info 对应五种语义,各自带语义色与图标。
<script setup lang="ts">
import { toast } from 'miao-design'
</script>
<template>
<div class="m-row-demo">
<MButton variant="contained" color="success" @click="toast.success('保存成功')">成功</MButton>
<MButton variant="contained" color="error" @click="toast.error('操作失败,请重试')">失败</MButton>
<MButton variant="contained" color="warning" @click="toast.warning('空间即将用尽')">警告</MButton>
<MButton variant="contained" color="info" @click="toast.info('有 3 条新消息')">信息</MButton>
<MButton variant="outlined" @click="toast.show('一条普通提示')">show</MButton>
</div>
</template>自定义参数
duration、position、closable、offset、title 都通过第二个参数传入;duration 为 0 时提示常驻,只能手动关闭。
<script setup lang="ts">
import { toast } from 'miao-design'
function withOptions() {
toast.error('请求超时,请检查网络后重试', {
title: '加载失败',
duration: 5000,
position: 'top-right',
closable: true,
offset: 24
})
}
function sticky() {
toast.show('这条提示不会自动消失,需要手动关闭', { duration: 0, closable: true })
}
function plainSlot() {
toast.show('没有额外配置的提示,3 秒后自动消失')
}
</script>
<template>
<div class="m-row-demo">
<MButton variant="outlined" @click="plainSlot">默认配置</MButton>
<MButton variant="outlined" @click="withOptions">标题 + 5 秒 + 右上角</MButton>
<MButton variant="outlined" @click="sticky">常驻(duration: 0)</MButton>
</div>
</template>六个角位
六个角位各自维护一堆提示,互不干扰,同一角位的多条按添加顺序排列。
<script setup lang="ts">
import { toast } from 'miao-design'
const positions = [
'top-left',
'top-center',
'top-right',
'bottom-left',
'bottom-center',
'bottom-right'
] as const
function show(position: (typeof positions)[number]) {
toast.info(`position: ${position}`, { position, duration: 2000 })
}
</script>
<template>
<div class="m-row-demo">
<MButton
v-for="position in positions"
:key="position"
variant="outlined"
size="small"
@click="show(position)">
{{ position }}
</MButton>
</div>
</template>关闭单条与清空
toast.close(id) 关闭单条,toast.clearAll() 清空全部;toastItems 是编程式队列的共享响应式数组,可以拿它统计条数或取回 id。
<script setup lang="ts">
import { computed } from 'vue'
import { toast, toastItems } from 'miao-design'
/** toastItems 是编程式队列的共享响应式数组,可用来统计与取回 id */
const count = computed(() => toastItems.length)
let seed = 0
function createSticky() {
seed += 1
toast.show(`第 ${seed} 条常驻提示`, { duration: 0, closable: true, position: 'bottom-right' })
}
function closeLast() {
const last = toastItems[toastItems.length - 1]
if (last) toast.close(last.id)
}
</script>
<template>
<div class="m-row-demo">
<MButton variant="outlined" @click="createSticky">新建常驻提示</MButton>
<MButton variant="outlined" :disabled="!count" @click="closeLast">关闭最后一条</MButton>
<MButton variant="outlined" :disabled="!count" @click="toast.clearAll()">清空全部</MButton>
<span style="color: var(--m-text-sub)">当前队列 {{ count }} 条</span>
</div>
</template>声明式组件
用 v-model 控制显隐,type、position、duration、closable 等属性与编程式一致。
声明式与编程式的队列互相独立,这条提示不会出现在 toastItems 里。
<script setup lang="ts">
import { ref } from 'vue'
const visible = ref(false)
const closed = ref(0)
</script>
<template>
<div class="m-row-demo">
<MButton variant="contained" color="success" @click="visible = true">显示声明式提示</MButton>
<MToast
v-model="visible"
type="success"
title="提交成功"
position="top-right"
:duration="4000"
:offset="24"
closable
@close="closed += 1">
正文来自默认插槽,可以放 <strong>任意节点</strong>;不写插槽时回退到 message 属性。
</MToast>
<span style="color: var(--m-text-sub)">close 触发次数:{{ closed }}</span>
</div>
</template>API
toast 方法
| 方法 | 签名 | 说明 |
|---|---|---|
toast.show | (message: string, options?: ToastOptions) => void | 弹出提示,type 取 options.type,缺省为 'default' |
toast.success | (message: string, options?: ToastOptions) => void | 成功提示,type 强制为 'success' |
toast.error | (message: string, options?: ToastOptions) => void | 失败提示,type 强制为 'error' |
toast.warning | (message: string, options?: ToastOptions) => void | 警告提示,type 强制为 'warning' |
toast.info | (message: string, options?: ToastOptions) => void | 信息提示,type 强制为 'info' |
toast.close | (id: number) => void | 按 id 关闭单条 |
toast.clearAll | () => void | 清空编程式队列中的全部提示 |
四个语义方法会把 options.type 覆盖为对应值。等价底层函数 showToast(message, options?) 也可以单独导入使用。
方法不返回 id
toast.show 与 toast.success 等方法的返回值是 void,拿不到新建提示的 id。需要精确关闭某一条时,从 toastItems 里取(例如最后一条的 id),或者干脆改用声明式 MToast + v-model。
ToastOptions
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
type | 'default' | 'success' | 'error' | 'warning' | 'info' | 'default' | 语义类型;调用 toast.success 等方法时被强制覆盖 |
title | string | undefined | 标题 |
duration | number | 3000 | 自动关闭时长(ms),0 表示不自动关闭 |
closable | boolean | false | 是否显示关闭按钮 |
position | 'top-right' | 'top-left' | 'top-center' | 'bottom-right' | 'bottom-left' | 'bottom-center' | 'bottom-center' | 显示角位 |
offset | number | 16 | 距视口边缘间距(px) |
MToast Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
modelValue | boolean | undefined | 是否显示,配合 v-model 使用 |
message | string | undefined | 提示内容;默认插槽有内容时优先用插槽 |
title | string | undefined | 标题 |
type | 'default' | 'success' | 'error' | 'warning' | 'info' | 'default' | 语义类型 |
duration | number | 3000 | 自动关闭时长(ms),0 表示不自动关闭 |
closable | boolean | false | 是否显示关闭按钮 |
position | 'top-right' | 'top-left' | 'top-center' | 'bottom-right' | 'bottom-left' | 'bottom-center' | 'bottom-center' | 显示角位 |
offset | number | 16 | 距视口边缘间距(px) |
MToast Events
| 事件 | 参数 | 说明 |
|---|---|---|
update:modelValue | value: boolean | 自动超时或点击关闭按钮时置为 false |
close | 无 | 自动超时或点击关闭按钮时触发 |
MToast Slots
| 插槽 | 参数 | 说明 |
|---|---|---|
default | 无 | 消息正文;无内容时回退到 message prop |
导出常量
| 常量 | 值 | 说明 |
|---|---|---|
TOAST_DEFAULT_DURATION | 3000 | 默认自动关闭时长(ms) |
TOAST_DEFAULT_POSITION | 'bottom-center' | 默认角位 |
export type ToastType = 'default' | 'success' | 'error' | 'warning' | 'info'
export type ToastPosition =
| 'top-right' | 'top-left' | 'top-center'
| 'bottom-right' | 'bottom-left' | 'bottom-center'toastItems 共享状态
toastItems 是容器消费的响应式数组,service 通过内部的 addToast / removeToast / clearToasts 增删。它只记录编程式提示,声明式 MToast 不写入这个数组。
import { toastItems } from 'miao-design'
export interface ToastItem {
id: number
message: string
title?: string
type: ToastType
duration: number
closable: boolean
position: ToastPosition
offset: number
}使用建议
首次调用时才挂载容器
编程式提示的容器是模块级单例:第一次调用 toast.* 时才会把 ToastContainer 挂到 document.body 上,此后所有调用复用同一个容器。所以没有提示时页面上不存在多余的 DOM 节点,也不需要手动引入任何组件。
不要在模块顶层调用
toast.show() 内部会创建 DOM 并调用 render(),属于浏览器环境操作。SSR 渲染阶段执行模块顶层代码会直接报错,请在事件回调、onMounted 或请求返回之后调用;同理,toastItems 只做状态读写,在服务端读它是安全的。
声明式 MToast 自身会 Teleport 到 body,在 SSR 场景下也建议配合 v-model 只在客户端展示。
计时与堆叠规则
duration > 0 时才会渲染进度条,倒计时在提示显示期间运行,提示关闭或组件卸载都会清理计时器。
同一角位的多条提示按添加顺序堆叠,不同角位互不影响,堆叠间距取该角位第一条的 offset —— 也就是说同一角位里后加入的 offset 不生效。
type='error' 的提示使用 aria-live="assertive",其余为 polite,屏幕阅读器会优先播报错误。
相关组件
- Alert 警告提示:需要常驻页面内的提示条时用它,而不是轻提示。
- Dialog 对话框:需要用户确认的关键操作,应当用对话框。
- Button 按钮:轻提示常由按钮点击触发。