反馈提示组件概述
反馈提示组件用于向用户传达系统状态、操作结果或需要注意的信息。合理使用可以:
- 降低焦虑 - 让用户知道系统正在响应
- 确认操作 - 告知操作成功或失败
- 引导决策 - 提供必要的选择和确认
组件类型与使用场景
| 组件 | 特点 | 适用场景 |
|---|---|---|
| Toast | 轻量、自动消失 | 操作成功/失败提示 |
| Notification | 可交互、可持续 | 系统通知、消息推送 |
| Alert | 页面内嵌 | 表单错误、重要提醒 |
| Modal | 阻断式 | 确认操作、重要决策 |
| Loading | 状态指示 | 加载中、处理中 |
Toast 消息组件
设计规范
interface ToastConfig {
type: 'success' | 'error' | 'warning' | 'info'
message: string
duration?: number // 默认 3000ms
position?: 'top' | 'bottom' | 'top-right'
closable?: boolean // 是否可手动关闭
}
使用原则
适合场景:
- 操作成功/失败的即时反馈
- 非关键性信息提示
- 不需要用户确认的通知
设计要点:
- 默认 3 秒自动消失
- 位置一致,避免用户寻找
- 同类型消息合并或排队
- 信息简洁,不超过一行
实现示例
<template>
<Teleport to="body">
<TransitionGroup name="toast" tag="div" class="toast-container">
<div
v-for="toast in toasts"
:key="toast.id"
:class="['toast', `toast-${toast.type}`]"
>
<component :is="iconMap[toast.type]" class="toast-icon" />
<span class="toast-message">{{ toast.message }}</span>
<button v-if="toast.closable" @click="remove(toast.id)">
<IconClose />
</button>
</div>
</TransitionGroup>
</Teleport>
</template>
<script setup>
const toasts = ref([])
const show = (config) => {
const id = Date.now()
toasts.value.push({ id, ...config })
if (config.duration !== 0) {
setTimeout(() => remove(id), config.duration || 3000)
}
}
const remove = (id) => {
toasts.value = toasts.value.filter(t => t.id !== id)
}
// 暴露给外部使用
defineExpose({ show, remove })
</script>
Modal 对话框组件
设计规范
模态框层级:
| 类型 | z-index | 用途 |
|---|---|---|
| 基础 Modal | 1000 | 普通对话框 |
| 确认 Modal | 1010 | 二次确认 |
| 全局 Modal | 1020 | 最高优先级 |
| 遮罩层 | 999 | 背景遮罩 |
使用原则
适合场景:
- 需要用户确认的重要操作
- 需要专注处理的子任务
- 复杂表单或流程
设计要点:
- 清晰的标题说明目的
- 主操作按钮突出显示
- 提供明确的取消/关闭方式
- 支持 ESC 键关闭
- 遮罩层点击可选关闭
确认对话框
<template>
<Modal v-model="visible">
<template #header>
<h3>确认删除</h3>
</template>
<p class="text-gray-600">
确定要删除这条记录吗?此操作不可恢复。
</p>
<template #footer>
<Button variant="outline" @click="visible = false">
取消
</Button>
<Button variant="danger" @click="handleConfirm">
删除
</Button>
</template>
</Modal>
</template>
可访问性要求
<template>
<div
role="dialog"
aria-modal="true"
:aria-labelledby="titleId"
:aria-describedby="descId"
>
<h2 :id="titleId">{{ title }}</h2>
<p :id="descId">{{ description }}</p>
<!-- 焦点陷阱:Tab 键在 Modal 内循环 -->
<FocusTrap>
<slot />
</FocusTrap>
</div>
</template>
Notification 通知组件
设计规范
相比 Toast 更丰富,支持:
- 标题 + 描述
- 操作按钮
- 自定义图标
- 持久显示
实现示例
<template>
<div class="notification" :class="`notification-${type}`">
<div class="notification-icon">
<component :is="iconMap[type]" />
</div>
<div class="notification-content">
<h4 class="notification-title">{{ title }}</h4>
<p v-if="description" class="notification-desc">
{{ description }}
</p>
<div v-if="actions" class="notification-actions">
<button
v-for="action in actions"
:key="action.text"
@click="action.onClick"
>
{{ action.text }}
</button>
</div>
</div>
<button class="notification-close" @click="$emit('close')">
<IconClose />
</button>
</div>
</template>
Alert 提示组件
页面内嵌提示
适用于需要持续显示的信息:
<template>
<div class="alert" :class="`alert-${type}`" role="alert">
<component :is="iconMap[type]" class="alert-icon" />
<div class="alert-content">
<slot />
</div>
<button v-if="closable" @click="$emit('close')" class="alert-close">
<IconClose />
</button>
</div>
</template>
<style scoped>
.alert {
display: flex;
align-items: flex-start;
padding: 1rem;
border-radius: 0.5rem;
margin-bottom: 1rem;
}
.alert-info {
background: #eff6ff;
border: 1px solid #bfdbfe;
color: #1e40af;
}
.alert-warning {
background: #fffbeb;
border: 1px solid #fde68a;
color: #92400e;
}
.alert-error {
background: #fef2f2;
border: 1px solid #fecaca;
color: #991b1b;
}
.alert-success {
background: #f0fdf4;
border: 1px solid #bbf7d0;
color: #166534;
}
</style>
Loading 加载状态
全局加载
<template>
<Teleport to="body">
<Transition name="fade">
<div v-if="loading" class="global-loading">
<div class="loading-overlay" />
<div class="loading-content">
<Spinner />
<p v-if="message">{{ message }}</p>
</div>
</div>
</Transition>
</Teleport>
</template>
骨架屏
<template>
<div v-if="loading" class="skeleton">
<div class="skeleton-avatar" />
<div class="skeleton-content">
<div class="skeleton-line w-3/4" />
<div class="skeleton-line w-1/2" />
</div>
</div>
<div v-else>
<slot />
</div>
</template>
<style scoped>
.skeleton-line {
height: 1rem;
background: linear-gradient(
90deg,
#f0f0f0 25%,
#e0e0e0 50%,
#f0f0f0 75%
);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
border-radius: 0.25rem;
}
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
</style>
使用场景决策
用户需要知道操作结果?
├── 是否需要确认?
│ ├── 是 → Modal
│ └── 否 → Toast
│
用户需要采取行动?
├── 紧急程度高?
│ ├── 是 → Modal / Notification
│ └── 否 → Alert(内嵌)
│
单纯信息展示?
├── 自动消失 → Toast
└── 持续展示 → Alert / Notification
最佳实践
| 原则 | 说明 |
|---|---|
| 适度使用 | 避免过多干扰用户 |
| 位置一致 | 同类提示位置固定 |
| 信息清晰 | 简洁明了的文案 |
| 可操作性 | 提供明确的后续操作 |
| 可访问性 | 支持屏幕阅读器 |
总结
反馈提示组件设计的核心在于:
- 选择合适类型 - 根据场景选择 Toast/Modal/Alert
- 把握干扰程度 - 轻提示用 Toast,重决策用 Modal
- 保持一致性 - 统一的视觉风格和交互方式
- 注重可访问性 - 遵循 ARIA 规范
好的反馈系统让用户始终了解系统状态,提升使用信心。


