8.7 KiB
8.7 KiB
name, overview, design, todos
| name | overview | design | todos | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| fms-vue-tabs | 在 fms-vue 组件库中新增 Tabs 标签页组件:line 线型 + top/left/right 三种标签方向,同时支持 items 数组与 TabPane 插槽双数据源,含键盘方向键导航、懒渲染缓存、禁用态与完整 ARIA,并在演示站 App.vue 中新增展示。 |
|
|
产品概述
为 fms-vue 自研 UI 组件库新增 Tabs 标签页组件,遵循 fms-vue/开发规范.md 约束并借鉴 参考组件库/antdv-next 的 Tabs 设计语义。包含 Tabs(根)与 TabPane(子声明)两个组件,用于在同一区域内分组展示内容、切换视图。
核心功能
- 双数据源:支持
items数组([{ key, label, disabled?, content? }])与 TabPane 插槽两种声明方式,与 Select 的 options/插槽模式一致,二选一使用。 - line 线型指示:激活标签底部(top)或侧边(left/right)显示主色指示条,标签栏有浅色分隔线。
- 侧边/垂直方向:
placement支持top(默认,横排在上)、left、right(纵向排列在左/右,内容区自适应剩余宽度)。 - 禁用态:支持单个 TabPane 禁用与 Tabs 整组禁用,反映到原生属性与视觉(降透明 + not-allowed)。
- 键盘与无障碍:tablist/tab/tabpanel 语义,方向键循环切换激活(top 用左右键,left/right 用上下键),roving tabindex、aria-selected/aria-controls/aria-labelledby,
:focus-visible焦点样式。 - 懒渲染 + 缓存:非激活面板不渲染,首次激活后缓存(切换不销毁),符合性能优先规范。
- 自定义标签:
#label插槽支持自定义标题内容(TabPane 与 items 两种模式均可用)。
技术栈
- Vue 3.5
<script setup>+ Sass(项目现有技术栈,不引入新依赖) - 样式复用
src/theme/tokens.css的--fms-*token,图标按需从@lucide/vue导入(本组件无需图标) - 参照
radio-group/radio的协作组件写法与select的"数组 + 插槽"双数据源模式
实现思路
Tabs 根组件通过 slots.default() 遍历插槽 vnode(展平 Fragment、过滤注释),识别 type.name === 'TabPane' 的子组件并提取其 props 与子插槽(default 内容、label 自定义标题)——此机制参考 antdv-next 的 useLegacyItems。归一化后的 panes 统一管理激活态、渲染与键盘导航;items 数组模式 normalize 为同构数据结构,两条路径输出一致的渲染结果。
关键技术决策
- vnode 解析而非 provide/inject:TabPane 是纯数据声明组件(自身渲染空),激活状态必须由根组件统一管理,根组件负责渲染 tab 头列表与内容区;这与 antd 的 TabPane 语义一致,优于 Radio 的 provide/inject 协作模式(radio 每个子项自己渲染视觉)。
- 指示条不加 JS 测量:line 模式指示条用激活 tab 自身的
border-bottom/border-left(2px 主色)实现,标签栏底部/侧边用 1px--fms-border。不做滑动跟随动画,避免布局读取与高频重排,符合"过渡限低成本属性、不主动构建"的规范。 - 懒渲染 + 缓存:
renderedKeysSet 记录已激活 pane,模板v-if(首次激活才渲染)+v-show(激活显示)组合,首屏只渲染激活项且切换不重建内容。 - 激活回退:
modelValue未传或指向不存在/禁用的 pane 时,回退到第一个可用 pane,且不写回 modelValue(保持受控性)。
实现注意
- 键盘仅遍历 enabled panes 循环移动,禁用项跳过;方向键按
placement映射(top: Left/Right,left/right: Up/Down,Home/End 定位首尾)。 - 切换时先更新内部激活态再 emit
update:modelValue与change;点击触发tabClick(key, event)。 - id 用
useId()生成,tab 与 tabpanel 通过aria-controls/aria-labelledby关联。 - 卸载无需清理(无全局监听器、无定时器)。
架构设计
Tabs(根组件)
├─ 数据源归一(items 数组 / TabPane 插槽 vnode 解析)→ mergedPanes
├─ 激活管理(modelValue 回退、select、emit)
├─ 渲染:nav 列表(role=tablist)+ 内容区(role=tabpanel,v-if/v-show 懒渲染)
├─ 键盘导航(方向键循环 + roving tabindex)
└─ 布局:placement top(纵向堆叠)/ left|right(flex 行)
目录结构
fms-vue/src/components/ui/tabs/
├── tabs.vue # [NEW] Tabs 根组件。props: modelValue/placement/disabled/items;
│ # emits: update:modelValue/change/tabClick;实现 vnode 解析、激活管理、
│ # 懒渲染缓存、键盘导航与 ARIA 完整模板。
├── tab-pane.vue # [NEW] TabPane 数据声明组件。props: tabKey/label/disabled;
│ # 插槽: default(面板内容)/ label(自定义标题);
│ # defineOptions({ name: 'TabPane' }) 供根组件识别;自身渲染空节点。
└── index.scss # [NEW] 样式:top/left/right 三种布局、line 指示条、hover/disabled/focus 态、
# prefers-reduced-motion;全部复用 --fms-* token,tabs.vue 内 @use 引入。
fms-vue/src/App.vue # [MODIFY] 通用分组新增导航项 { key: 'tabs', label: 'Tabs 标签页' },
# 新增演示 section(基础用法/items 数组/侧边方向/禁用/自定义标签/懒渲染)。
关键数据结构(伪类型)
// 归一化后的 pane(items 数组与 TabPane 插槽统一输出)
{ key: String, label: String|VNode, disabled: Boolean,
content: () => VNode[], // 内容渲染函数(懒渲染时按需调用)
labelVNode: () => VNode[] // 自定义标题渲染函数,无则 undefined
}
设计风格
与现有组件库一致的克制现代风格:干净的分层、克制的交互反馈、清晰的状态表达。
视觉要点
- 布局:top 模式标签横排在上、内容在下,标签栏底部 1px
--fms-border分隔;left/right 模式为 flex 行,标签纵向排列于左/右,内容区flex: 1 1 auto自适应。垂直方向标签栏加--fms-border侧边分隔,激活标签文字主色并配--fms-secondary-hover浅背景增强选中识别。 - 指示条(line):top 模式激活标签自身 2px 主色下边框,left/right 为 2px 主色侧边框,无滑动动画,稳定且零布局读取。
- 状态反馈:hover 文字变主色;禁用用
--fms-disabled-text+not-allowed光标并降透明;选中不单靠颜色,同时用指示条与文字粗细双重表达。 - 间距:标签 padding 约 0 16px、高度对齐
--fms-control-height(32px),标签间距紧凑;内容区 padding 16px 0 与标签栏对齐。 - 焦点:
:focus-visible主色 outline 2px + 2px offset。 - 动画:仅 hover 文字颜色过渡(低成本属性),
prefers-reduced-motion下关闭。
Agent 扩展
Skill
- antdv-next
- 用途:核对 antdv-next Tabs/TabPane 的 API 语义(tabKey 解析顺序、键盘交互、ARIA 细节),确保借鉴准确。
- 预期产出:tabs.vue 的 props/事件/键盘映射与参考库一致,无遗漏交互。
- ui-ux-pro-max
- 用途:辅助校验 Tabs 视觉细节(间距、指示条、禁用/焦点状态表达),与组件库整体风格对齐。
- 预期产出:index.scss 的视觉规格(间距、指示条、状态色)符合现代组件库实践。