19 KiB
19 KiB
基础组件
**本文引用的文件** - [Button.vue](file://fms-vue/src/components/ui/button/Button.vue) - [button-group.vue](file://fms-vue/src/components/ui/button-group/button-group.vue) - [Input.vue](file://fms-vue/src/components/ui/input/Input.vue) - [Tag.vue](file://fms-vue/src/components/ui/tag/Tag.vue) - [Spin.vue](file://fms-vue/src/components/ui/spin/Spin.vue) - [Form.vue](file://fms-vue/src/components/ui/form/Form.vue) - [index.js](file://fms-vue/src/theme/index.js) - [presets.js](file://fms-vue/src/theme/presets.js) - [tokens.css](file://fms-vue/src/theme/tokens.css)目录
简介
本章节面向 FMS 前端工程中的基础 UI 组件,聚焦按钮、输入框、标签、加载指示器与表单容器等高频交互元素。文档从设计理念、API 接口、属性配置、事件处理、样式定制、可访问性(a11y)与响应式特性等方面进行全面说明,并提供组合使用示例与性能优化建议,帮助开发者快速、正确地集成与扩展这些基础组件。
项目结构
FMS 的 UI 组件位于 fms-vue 工程的 src/components/ui 目录下,采用“按功能分目录”的组织方式:每个组件一个独立目录,包含 Vue 组件与对应样式;主题与令牌定义集中在 theme 目录,便于统一风格与深浅色适配。
graph TB
subgraph "UI 组件"
B["Button"]
BG["ButtonGroup"]
I["Input"]
T["Tag"]
S["Spin"]
F["Form"]
end
subgraph "主题系统"
TI["theme/index.js"]
TP["theme/presets.js"]
TK["theme/tokens.css"]
end
B --> TI
BG --> TI
I --> TI
T --> TI
S --> TI
F --> TI
TI --> TP
TI --> TK
图表来源
- Button.vue:1-69
- button-group.vue:1-83
- Input.vue:1-370
- Tag.vue:1-86
- Spin.vue:1-93
- Form.vue:1-128
- index.js
- presets.js
- tokens.css
章节来源
- Button.vue:1-69
- button-group.vue:1-83
- Input.vue:1-370
- Tag.vue:1-86
- Spin.vue:1-93
- Form.vue:1-128
- index.js
- presets.js
- tokens.css
核心组件
本节概述各基础组件的职责与能力边界:
- Button:提供多种视觉变体、禁用态、加载态、块级模式与原生表单类型支持,内置无障碍提示。
- ButtonGroup:将多个按钮组合为连续控件组,支持水平/垂直排列,自动处理圆角与焦点层级。
- Input:支持文本、密码、数字、多行文本四种模式;具备清除、字数统计、前缀/后缀插槽、拖拽调整高度、键盘操作与校验状态。
- Tag:展示轻量标签,支持关闭、禁用、链接跳转与图标插槽。
- Spin:提供延迟显示的加载指示器,支持包裹内容遮罩或独立指示器两种形态。
- Form:表单容器,提供布局、网格列数、对齐、必填标记、校验时机与全量校验能力,并通过 provide/inject 向子项下发上下文。
章节来源
架构总览
基础组件围绕“主题令牌 + 语义化 DOM + 无障碍属性”构建:
- 主题层:通过 tokens.css 暴露 CSS 变量(如控制半径、尺寸、颜色),由 presets.js 预设默认值,index.js 负责导出与注入。
- 组件层:各组件以语义化标签渲染(button/input/span/div),结合 aria-* 属性提升可访问性。
- 交互层:通过 Vue 的 props/emits/slots 暴露 API,事件冒泡到父组件进行业务处理。
graph LR
TK["tokens.css<br/>CSS 变量"] --> P["presets.js<br/>主题预设"]
P --> I["index.js<br/>主题入口"]
I --> C1["Button"]
I --> C2["ButtonGroup"]
I --> C3["Input"]
I --> C4["Tag"]
I --> C5["Spin"]
I --> C6["Form"]
图表来源
- tokens.css
- presets.js
- index.js
- Button.vue:1-69
- button-group.vue:1-83
- Input.vue:1-370
- Tag.vue:1-86
- Spin.vue:1-93
- Form.vue:1-128
详细组件分析
按钮 Button
- 设计理念:统一的视觉语言与行为语义,兼顾表单场景与通用操作。
- 关键属性
- type:视觉变体(主按钮、次要、描边、幽灵、危险、链接)。
- disabled:禁用态。
- loading:加载态,内部显示旋转图标并禁用交互。
- block:块级模式,占满整行宽度。
- htmlType:原生 button/submit/reset,用于表单提交/重置。
- 事件
- 透传原生点击事件至父组件。
- 可访问性
- 在加载时设置 aria-busy,辅助技术可感知忙碌状态。
- 样式定制
- 通过 CSS 类名 button/button-{type}/button-block/button-loading 覆盖样式。
- 与主题变量联动(如圆角、间距、颜色)。
- 组合使用
- 与 ButtonGroup 组合实现工具栏、分页、分段控制器等。
- 性能
- 仅计算必要 class,避免多余重排。
classDiagram
class Button {
+type
+disabled
+loading
+block
+htmlType
+click()
}
图表来源
章节来源
按钮组 ButtonGroup
- 设计理念:将一组按钮视为单一控件组,保证视觉连贯性与键盘可达性。
- 关键属性
- orientation:horizontal | vertical,控制排列方向。
- 行为
- 自动去除相邻按钮重叠边框,首尾按钮保留圆角。
- 聚焦时提升 z-index,确保焦点可见。
- 可访问性
- role="group",data-orientation 标识方向,便于测试与样式定位。
- 样式定制
- 通过 .button-group / .button-group-vertical 及 :deep(.button) 选择器覆盖。
flowchart TD
Start(["渲染"]) --> CheckOri{"方向?"}
CheckOri --> |水平| H["横向排列<br/>去圆角+贴合"]
CheckOri --> |垂直| V["纵向排列<br/>去圆角+贴合"]
H --> Focus["聚焦时提升层级"]
V --> Focus
Focus --> End(["完成"])
图表来源
章节来源
输入框 Input
- 设计理念:高内聚的输入体验,覆盖常见输入场景与交互细节。
- 关键属性
- modelValue:双向绑定值。
- type:default | password | number | textarea。
- disabled / readonly:禁用/只读。
- status:校验状态 '' | error | warning。
- allowClear:有值时显示清除按钮(hover/focus 时出现)。
- showCount / maxlength:字数统计。
- rows:textarea 初始行数。
- autocomplete:浏览器自动填充语义。
- 事件
- update:modelValue、change、blur、focus、clear。
- 特殊能力
- 密码模式:显示/隐藏切换,带无障碍标签。
- 数字模式:中间态容错(允许小数点过渡),失焦时提交数值。
- 多行文本:拖拽调整高度,键盘上下/Home 微调,requestAnimationFrame 节流更新。
- 前缀/后缀插槽:增强输入语义与操作。
- 可访问性
- 错误状态设置 aria-invalid。
- 密码切换按钮提供 aria-label 与 aria-pressed。
- 拖拽手柄提供 role="slider" 与相关 aria 属性。
- 样式定制
- 通过 input-wrapper/input-focused/input-disabled/input-textarea 等类名覆盖。
- 与主题变量联动(尺寸、圆角、颜色)。
sequenceDiagram
participant U as "用户"
participant I as "Input"
U->>I : 输入/聚焦/失焦/清除
I->>I : 校验状态/计数/数字归一化
I-->>U : 触发 change/update : modelValue/clear
Note over I,U : 密码模式切换/拖拽高度/键盘微调
图表来源
章节来源
标签 Tag
- 设计理念:轻量信息标注,支持关闭与链接跳转。
- 关键属性
- variant:视觉变体(default/secondary/outline/danger/ghost)。
- closable:是否显示关闭按钮。
- disabled:禁用态。
- href:传入后渲染为 ,支持跳转。
- 事件
- close:点击关闭时触发,可通过 preventDefault 阻止隐藏。
- 可访问性
- 关闭按钮 role="button",支持 Enter/Space 触发。
- 禁用态设置 aria-disabled。
- 样式定制
- 通过 tag/tag-{variant}/tag-disabled/tag-hidden 类名覆盖。
flowchart TD
Click["点击关闭"] --> Disabled{"禁用?"}
Disabled --> |是| Stop["忽略"]
Disabled --> |否| Emit["触发 close"]
Emit --> Prevent{"是否阻止默认?"}
Prevent --> |是| Keep["保持可见"]
Prevent --> |否| Hide["隐藏自身"]
图表来源
章节来源
加载指示器 Spin
- 设计理念:防抖短请求闪烁,提供两种形态(独立指示器/包裹内容遮罩)。
- 关键属性
- spinning:是否加载中。
- delay:延迟显示毫秒数,避免短暂请求导致闪烁。
- description:加载文案。
- 行为
- 有默认插槽时,作为容器包裹内容并居中显示遮罩;否则直接渲染指示器。
- 卸载时清理定时器。
- 可访问性
- 根节点 aria-busy 与 aria-live="polite",描述区域 role="status"。
- 样式定制
- 通过 spin/spin-nested/spin-spinning 类名覆盖。
flowchart TD
Start(["spinning/delay 变化"]) --> ClearTimer["清理旧定时器"]
ClearTimer --> CheckSpinning{"spinning?"}
CheckSpinning --> |否| SetInactive["active=false"]
CheckSpinning --> |是| HasDelay{"delay>0?"}
HasDelay --> |是| Wait["setTimeout(delay)"]
HasDelay --> |否| SetActive["active=true"]
Wait --> SetActive
SetInactive --> End(["结束"])
SetActive --> End
图表来源
章节来源
表单容器 Form
- 设计理念:集中管理表单数据、规则与校验流程,提供灵活布局与网格能力。
- 关键属性
- model:表单数据对象。
- rules:表单级校验规则(字段级优先级更高)。
- layout:horizontal | vertical。
- labelAlign:label 对齐 left | right。
- labelWidth:label 宽度(横向布局生效)。
- columns:网格列数(>1 启用栅格布局)。
- maxWidth:最大宽度限制。
- requiredMark:必填红星标记。
- validateTrigger:change | blur。
- 事件
- submit:校验通过后触发,携带模型副本。
- submitFailed:校验失败,携带 values 与 errorFields。
- reset:重置后触发。
- 能力
- 提供 validateAll 方法,返回带错误的字段列表。
- 通过 provide('fms-form') 向子项下发上下文(model/rules/layout/对齐/列数/必填标记/校验时机/注册注销字段)。
- 可访问性
- 原生 form 语义,配合子项的 a11y 属性形成完整无障碍链路。
- 样式定制
- 通过 fms-form/fms-form-{layout}/fms-form-grid 类名覆盖。
sequenceDiagram
participant Parent as "父组件"
participant Form as "Form"
participant Item as "FormItem(子项)"
Parent->>Form : 提交/重置
Form->>Form : validateAll()
loop 遍历已注册字段
Form->>Item : validate()
Item-->>Form : errors[]
end
alt 无错误
Form-->>Parent : emit("submit", model)
else 有错误
Form-->>Parent : emit("submitFailed", {values, errorFields})
end
图表来源
章节来源
依赖关系分析
- 组件间耦合
- ButtonGroup 依赖 Button 的类名约定(.button)以实现无缝拼接。
- Form 通过 provide/inject 与子项协作,但当前仓库未包含 Form.Item 的具体实现,因此此处仅描述容器职责。
- 外部依赖
- 图标来自 @lucide/vue(Loader/X/Eye/EyeOff/MoveDiagonal2)。
- 样式基于 SCSS 模块化导入,配合主题变量。
- 主题依赖
- 所有组件通过主题层提供的 CSS 变量保持一致的视觉风格与深浅色适配。
graph LR
L["@lucide/vue"] --> B["Button"]
L --> I["Input"]
L --> T["Tag"]
L --> S["Spin"]
TK["tokens.css"] --> Theme["主题层"]
Theme --> B
Theme --> I
Theme --> T
Theme --> S
Theme --> F["Form"]
图表来源
章节来源
性能考量
- 输入框 Input
- 数字模式在输入过程中保留中间态,减少不必要的 Number 转换与回退。
- 拖拽调整高度使用 requestAnimationFrame 节流,避免频繁重绘。
- 仅在必要时计算 hasValue/showClear/countText 等派生状态。
- 加载指示器 Spin
- 通过 delay 防止短请求导致的闪烁;卸载时清理定时器,避免内存泄漏。
- 按钮 Button
- 使用 computed 合并 class,减少模板中条件判断开销。
- 表单 Form
- validateAll 并行校验所有字段,缩短整体校验耗时。
- 主题与样式
- 通过 CSS 变量与模块化 SCSS 减少重复样式,利于缓存与复用。
[本节为通用性能指导,不直接分析具体代码片段]
故障排查指南
- 按钮不可点击
- 检查 disabled/loading 状态;确认 htmlType 是否符合预期。
- 参考:Button.vue:1-69
- 输入框数值异常
- 数字模式下中间态允许小数点,失焦才会提交数值;如需严格格式,可在父组件监听 change 做二次校验。
- 参考:Input.vue:107-150
- 多行文本拖拽卡顿
- 确认未同时大量更新其他视图;组件已使用 requestAnimationFrame 节流。
- 参考:Input.vue:166-238
- 标签关闭无效
- 若父组件在 close 事件中调用 preventDefault,则不会隐藏;请检查事件处理逻辑。
- 参考:Tag.vue:40-56
- 加载指示器闪烁
- 适当增大 delay 以避免短请求闪烁;或在父层统一控制 spinning。
- 参考:Spin.vue:18-44
- 表单校验结果不符合预期
- 确认 validateTrigger 与子项触发时机一致;提交前可调用 validateAll 强制校验。
- 参考:Form.vue:64-92
章节来源
结论
FMS 基础组件以“语义化 + 可访问性 + 主题化”为核心设计原则,提供了稳定、易用且可扩展的交互基元。通过统一的 API 与样式体系,开发者可以快速搭建一致的界面,并在复杂场景中保持良好的性能与可维护性。建议在项目中优先使用这些基础组件,并结合主题系统进行品牌化定制。
[本节为总结性内容,不直接分析具体代码片段]
附录
- 主题适配与深浅色
- 通过 tokens.css 暴露 CSS 变量,presets.js 提供默认值,index.js 负责导出与注入。
- 建议在应用启动时按需切换主题预设,即可全局生效。
- 参考:tokens.css、presets.js、index.js
- 最佳实践
- 表单:使用 Form 统一管理 model/rules/校验时机;提交前调用 validateAll。
- 输入:合理使用 type/status/allowClear/showCount,提升用户体验。
- 按钮:在异步操作中开启 loading,完成后关闭,避免重复提交。
- 标签:在需要可关闭的场景使用 closable,并通过 close 事件控制显隐。
- 加载:对短请求使用 delay 避免闪烁;长任务使用 Spin 包裹内容。
章节来源