Files
workspace/code/fms/.qoder/repowiki/zh/content/UI组件库/反馈组件.md
T
2026-08-23 21:02:41 +08:00

20 KiB
Raw Blame History

反馈组件

**本文引用的文件** - [modal.vue](file://fms-vue/src/components/ui/modal/modal.vue) - [drawer.vue](file://fms-vue/src/components/ui/drawer/drawer.vue) - [message.vue](file://fms-vue/src/components/ui/message/message.vue) - [notification.vue](file://fms-vue/src/components/ui/notification/notification.vue) - [popover.vue](file://fms-vue/src/components/ui/popover/popover.vue) - [tooltip.vue](file://fms-vue/src/components/ui/tooltip/tooltip.vue) - [focus.js](file://fms-vue/src/components/ui/utils/focus.js) - [scroll.js](file://fms-vue/src/components/ui/utils/scroll.js) - [drag.js](file://fms-vue/src/components/ui/utils/drag.js) - [position.js](file://fms-vue/src/components/ui/utils/position.js)

目录

  1. 简介
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能与可访问性
  8. 异步操作与错误处理
  9. 动画、定位与多语言
  10. 故障排查
  11. 结论

简介

本章节面向 FMS 前端组件库中的“用户反馈”类组件,涵盖模态框(Modal)、抽屉(Drawer)、消息提示(Message)、通知(Notification)、气泡卡片(Popover)与工具提示(Tooltip)。文档从设计理念、触发机制、生命周期管理、状态控制、选择策略、交互原则、异步反馈与错误处理、动画效果、定位算法、多语言支持等维度进行系统化说明,帮助开发者在不同场景下正确选用并组合这些组件。

项目结构

反馈组件集中在 fms-vue 的 ui 目录下,按功能划分:

  • 弹层容器与交互:modal、drawer、popover、tooltip
  • 全局轻提示与通知:message、notification
  • 通用能力:focus(焦点陷阱)、scroll(滚动锁)、drag(拖拽/缩放)、position(浮层定位)
graph TB
subgraph "弹层与浮层"
M["Modal"]
D["Drawer"]
P["Popover"]
T["Tooltip"]
end
subgraph "全局反馈"
MSG["Message"]
NTF["Notification"]
end
subgraph "通用能力"
F["focus.js"]
S["scroll.js"]
G["drag.js"]
POS["position.js"]
end
M --> F
M --> S
M --> G
D --> F
D --> S
D --> G
P --> POS
T --> P
MSG --> |渲染| MSG
NTF --> |渲染| NTF

图表来源

章节来源

核心组件

  • Modal:居中或顶部对齐的对话框,支持受控/非受控开关、遮罩关闭、Esc 关闭、标题/内容/底部插槽、确定/取消按钮、加载态、全屏切换、拖拽与四边缩放、焦点陷阱与滚动锁定、ARIA 无障碍。
  • Drawer:贴边滑出面板,支持四方向 placement、尺寸可调、三段式布局(header/body/footer)、焦点与滚动管理。
  • Message:页面顶部居中的轻量提示,自动消失,支持类型与手动关闭。
  • Notification:右上角通知卡片,承载标题+描述,支持手动关闭与自动消失、堆叠重排。
  • Popover:基于触发器的浮层,支持多种触发方式、智能定位(flip/shift)、箭头、跟随宽度等。
  • Tooltip:对 Popover 的 hover 薄封装,提供轻量文本提示。

章节来源

架构总览

反馈组件采用“声明式组件 + 命令式管理器”的组合模式:

  • Modal/Drawer/Popover/Tooltip:以组件形式声明使用,内部通过 Teleport 挂载到 body,结合 Transition 实现动画;Modal/Drawer 共享 focus/scroll/drag 能力。
  • Message/Notification:提供命令式 API(如 success/open),在首次调用时创建单例容器挂载至 body,内部维护活跃条目数组并使用 TransitionGroup 完成堆叠与过渡。
sequenceDiagram
participant U as "业务代码"
participant M as "Modal/Drawer"
participant F as "focus.js"
participant S as "scroll.js"
participant G as "drag.js"
U->>M : 打开(open=true)
M->>S : lockScroll()
M->>F : focusFirst(panelRef)
M->>G : bindDrag/bindResize(可选)
Note over M : 键盘监听/ESC关闭/焦点陷阱生效
U->>M : 关闭(open=false)
M->>F : trapFocus解除
M->>G : destroy()
M->>S : unlockScroll()

图表来源

详细组件分析

Modal 模态框

  • 触发机制:v-model:open 受控或 defaultOpen 非受控;支持遮罩点击、Esc、关闭按钮、取消按钮关闭;confirmLoading 期间阻止关闭。
  • 生命周期:打开时记录触发元素、锁定滚动、聚焦首项、绑定键盘事件、启用拖拽/缩放;关闭时恢复滚动、解绑事件、还原焦点。
  • 状态控制:isOpen 计算属性统一受控/非受控;panelStyle 根据是否缩放决定宽高;isFullscreen 控制全屏。
  • 可访问性:role=dialog、aria-modal、aria-labelledby 关联标题,关闭按钮 aria-label。
  • 交互增强:标题栏拖拽移动、四边缩放手柄、全屏切换。
flowchart TD
Start(["打开"]) --> Lock["锁定滚动"]
Lock --> Focus["聚焦首项"]
Focus --> Bind["绑定键盘/拖拽/缩放"]
Bind --> Wait{"等待关闭"}
Wait --> |Esc/遮罩/取消| Close["请求关闭"]
Close --> Unlock["解锁滚动"]
Unlock --> Restore["还原焦点"]
Restore --> End(["结束"])

图表来源

章节来源

Drawer 抽屉

  • 触发机制:placement 控制滑出方向(left/right/top/bottom),默认右侧;支持遮罩点击、Esc、关闭按钮关闭。
  • 生命周期:打开时锁定滚动、聚焦首项、绑定键盘事件、启用边缘缩放;关闭时恢复滚动、解绑事件、还原焦点。
  • 状态控制:isOpen 计算属性统一受控/非受控;panelStyle 根据方向与是否缩放决定 width/height。
  • 可访问性:role=dialog、aria-modal、aria-labelledby 关联标题,关闭按钮 aria-label。
classDiagram
class Drawer {
+open
+defaultOpen
+title
+placement
+width
+height
+mask
+maskClosable
+keyboard
+closable
+resizable
+setOpen(value)
+requestClose()
}
Drawer --> Focus : "focusFirst/trapFocus"
Drawer --> Scroll : "lock/unlock"
Drawer --> Drag : "bindResize"

图表来源

章节来源

Message 消息提示

  • 触发机制:通常由命令式 API 调用(见计划文档),组件本身支持 props 配置 type/content/duration/closable。
  • 生命周期:mounted 启动自动关闭计时器,beforeUnmount 清理计时器;close 事件交由容器移除条目。
  • 状态控制:duration<=0 不自动关闭;closable 显示关闭按钮。
  • 可访问性:role="alert",图标与文案清晰表达类型。
sequenceDiagram
participant C as "容器"
participant M as "Message"
C->>M : 渲染条目(id,type,content,duration,closable)
M->>M : onMounted -> startTimer()
alt 自动关闭
M-->>C : emit('close') after duration
else 手动关闭
C->>M : 点击关闭
M-->>C : emit('close')
end

图表来源

章节来源

Notification 通知

  • 触发机制:命令式 open(options),组件支持 title/description/duration/closable。
  • 生命周期:同 Message,自动关闭计时器在 mounted 启动、卸载时清理。
  • 状态控制:duration<=0 不自动关闭;closable 显示关闭按钮。
  • 可访问性:role="alert",标题与描述层次清晰。
sequenceDiagram
participant C as "容器"
participant N as "Notification"
C->>N : 渲染条目(id,type,title,description,duration,closable)
N->>N : onMounted -> startTimer()
alt 自动关闭
N-->>C : emit('close') after duration
else 手动关闭
C->>N : 点击关闭
N-->>C : emit('close')
end

图表来源

章节来源

Popover 气泡卡片

  • 触发机制:trigger 支持 click/hover/focus/contextmenu/manual;hover 时通过延迟定时器避免误触。
  • 定位算法:computePosition 基于期望 placement,计算初始坐标,溢出时 flip 翻转主轴方向,再 shift 约束回视口内;支持箭头与跟随触发器宽度。
  • 生命周期:打开时注册全局 pointerdown/keydown/resize/scroll 监听,nextTick 后计算位置并启动 ResizeObserver;关闭时清理所有监听与定时器。
  • 可访问性:hover 触发时 contentRole 为 tooltip,其余为 dialog。
flowchart TD
A["打开"] --> B["nextTick 计算位置"]
B --> C{"溢出?"}
C -- 是 --> D["flip 翻转主轴"]
D --> E{"仍溢出?"}
E -- 是 --> F["shift 约束回视口"]
E -- 否 --> G["应用位置"]
C -- 否 --> G
G --> H["监听 resize/scroll 更新"]

图表来源

章节来源

Tooltip 工具提示

  • 触发机制:固定 trigger=hover,其他行为(定位、Teleport、延迟、Esc 关闭)由 Popover 提供。
  • 内容优先级:content 纯文本与 #content 插槽同时存在时,插槽优先。

章节来源

依赖关系分析

  • Modal/Drawer 依赖 focus.js(焦点陷阱)、scroll.js(滚动锁)、drag.js(拖拽/缩放)。
  • Popover/Tooltip 依赖 position.js(定位算法)。
  • Message/Notification 作为单条渲染单元,被各自管理器(计划文档中定义)在 body 上以单例容器管理,使用 TransitionGroup 完成堆叠与过渡。
graph LR
Modal --> Focus
Modal --> Scroll
Modal --> Drag
Drawer --> Focus
Drawer --> Scroll
Drawer --> Drag
Popover --> Position
Tooltip --> Popover
Message --> |被容器管理| Message
Notification --> |被容器管理| Notification

图表来源

章节来源

性能与可访问性

  • 性能
    • Modal/Drawer:仅在打开期间注册全局键盘事件与拖拽/缩放监听,关闭/卸载时彻底清理;Transition 仅使用 opacity/transform 等低成本属性。
    • Popover:使用 requestAnimationFrame 合并高频滚动/缩放更新;ResizeObserver 观察浮层尺寸变化;hover 触发使用延迟定时器减少抖动。
    • Message/Notification:单例容器,条目数量受控;计时器在卸载时清理,避免内存泄漏。
  • 可访问性
    • Modal/Drawer:role=dialog、aria-modal、aria-labelledby 关联标题,关闭按钮具备 aria-label;焦点陷阱确保 Tab 循环。
    • Popover:hover 触发时 contentRole 为 tooltip,其余为 dialog;箭头与定位保证信息可读。
    • Message/Notification:role="alert",明确语义。

章节来源

异步操作与错误处理

  • 异步操作反馈
    • Modal 确定按钮 loading 态(confirmLoading):在加载中禁止重复提交与关闭,避免打断异步流程。
    • Message/Notification:自动关闭时长可通过 duration 配置;0 表示不自动关闭,适合需要用户确认的错误提示。
  • 错误处理
    • 全局键盘事件与拖拽/缩放监听均在打开时注册、关闭/卸载时清理,防止残留导致异常。
    • 滚动锁计数式管理,支持多层弹窗嵌套,最后一个关闭才恢复滚动,避免样式错乱。
    • 计时器在 beforeUnmount 清理,避免组件销毁后回调引发异常。

章节来源

动画、定位与多语言

  • 动画效果
    • Modal:遮罩淡入/淡出,面板缩放进入/退出;支持 prefers-reduced-motion 降级。
    • Drawer:遮罩淡入/淡出,面板按方向平移滑入/滑出。
    • Popover/Tooltip:使用 Transition 名称与 CSS 变量控制过渡。
    • Message/Notification:单条过渡与 TransitionGroup 重排,实现平滑堆叠与退场。
  • 定位算法
    • Popover 使用 computePosition:先按期望 placement 计算坐标,溢出则 flip 翻转主轴方向,再 shift 约束回视口内;支持 offset 与箭头吸附。
  • 多语言支持
    • 组件文案(如 Modal 的 okText/cancelText、Drawer 的关闭按钮标签)通过 props 或插槽注入,便于接入 i18n 系统;计划文档指出复用 lucide 图标与主题变量,未硬编码颜色与文案。

章节来源

故障排查

  • 焦点丢失或无法关闭
    • 检查是否在打开时成功执行 focusFirst 与 trapFocus;确认 panelRef 已渲染且可聚焦。
    • 确认 Esc 键监听已绑定且未被 preventDefault 拦截。
  • 滚动异常
    • 检查 lockScroll/unlockScroll 是否成对调用;多个弹窗嵌套时确保计数归零才恢复滚动。
  • 浮层遮挡或错位
    • 检查 Popover 的 placement、offset 与 followTriggerWidth;确认 computePosition 返回值与 floatStyle 应用正确。
  • 计时器未清理
    • 确认 message/notification 的 beforeUnmount 清理了 setTimeout;避免组件销毁后继续触发 close。

章节来源

结论

FMS 反馈组件体系围绕“可控、可访问、可组合”的原则构建:Modal/Drawer 提供强交互弹层能力,Message/Notification 提供轻量全局反馈,Popover/Tooltip 提供灵活的上下文提示。通过统一的焦点与滚动管理、健壮的定位算法与低开销动画,满足复杂业务场景下的用户体验需求。建议在实际项目中:

  • 根据反馈强度与上下文选择合适组件(强中断用 Modal,弱提示用 Message/Notification,上下文提示用 Popover/Tooltip)。
  • 使用受控 open 状态管理生命周期,确保资源释放与状态一致。
  • 借助 props 与插槽注入多语言文案,保持视觉与交互一致性。
  • 在异步操作中合理使用 loading 与自动关闭策略,提升可用性。