20260816220132

This commit is contained in:
oneao committed 2026-08-16 22:01:32 +08:00
1 parent b6f9a4c912
commit 72993ecdcd
179 files changed
+20050 -633

No files matched your search

@@ -0,0 +1,168 @@
---
name: dropdown-component
overview: 在 fms-vue 组件库中新增 Dropdown 下拉菜单组件:基于现有 Popover 浮层派生,采用数据驱动 items(label/icon/shortcut/disabled/danger/divider/子菜单)+ 默认插槽逃生舱,支持 click/hover/contextmenu 触发与键盘导航,样式参考 shadcn(白卡片、圆角、hover 高亮、分隔线、危险项、快捷键),并补充 demo 与 App.vue 导航注册。
design:
architecture:
framework: vue
styleKeywords:
- 卡片式浮层
- 紧凑列表
- hover 高亮
- 危险变体
- 精致微交互
fontSystem:
fontFamily: Noto Sans SC, PingFang SC
heading:
size: 14px
weight: 500
subheading:
size: 12px
weight: 500
body:
size: 14px
weight: 400
colorSystem:
primary:
- "#0f172b"
- "#1677ff"
background:
- "#ffffff"
- "#f5f6f8"
- "#1a1d24"
text:
- "#1f2329"
- "#86909c"
functional:
- "#ef4444"
- "#e5e6eb"
todos:
- id: popover-contextmenu
content: 用 [skill:antdv-next] 确认右键触发交互,扩展 Popover 支持 trigger=contextmenu(validator + 右键坐标定位)
status: completed
- id: dropdown-tokens
content: tokens.css 新增 --fms-dropdown-* 尺寸 token(min-width/max-height/item padding/radius/divider margin)
status: completed
- id: dropdown-style
content: 用 [skill:ui-ux-pro-max] 参考设计,创建 dropdown/index.scss(shadcn 风格菜单卡片/项/分隔线/危险态/子菜单箭头)
status: completed
dependencies:
- dropdown-tokens
- id: dropdown-component
content: 创建 dropdown/dropdown.vue(items 数据驱动、键盘导航、子菜单、插槽逃生舱、ARIA)
status: completed
dependencies:
- popover-contextmenu
- dropdown-style
- id: dropdown-demo
content: 创建 demo/dropdown.vue 并注册到 App.vue「数据展示」分类与 demoMap
status: completed
dependencies:
- dropdown-component
- id: static-check
content: 静态检查全部改动文件(oxlint)并汇报变更清单与已知风险
status: completed
dependencies:
- dropdown-demo
---
## 产品概述
在 fms-vue 组件库中新增 **Dropdown 下拉菜单**组件:功能与交互参考 antdv-next(数据驱动 items、多种触发方式、子菜单、键盘操作),样式与设计参考 shadcn DropdownMenu(卡片式浮层、hover 高亮、危险项、快捷键、图标、勾选指示),并严格遵循 `fms-vue/开发规范.md`。
## 核心功能
- **数据驱动菜单**:`items` prop 定义菜单项,结构支持 `key / label / icon / shortcut / disabled / danger / divider / type: 'label'(分组标题)/ children(子菜单)`;不传 items 时提供默认插槽逃生舱(对齐 Select 模式)
- **多种触发方式**:`click`(默认)/ `hover` / `contextmenu`(右键,需扩展 Popover 以支持右键坐标定位)
- **子菜单**:`children` 嵌套渲染,hover 展开右侧子菜单
- **键盘导航**:打开后聚焦菜单容器,方向键/Home/End 移动高亮,Enter 选择,Esc 关闭,`aria-activedescendant` 无障碍
- **受控/非受控**:`open` / `default-open`,事件 `update:open` / `openChange` / `click`
- **视觉要素**(shadcn 风格):卡片浮层(圆角/边框/阴影)、菜单项 hover/focus 高亮、禁用灰、danger 红、分隔线、分组小标签、快捷键右侧、图标左侧
- **演示接入**:新增 `demo/dropdown.vue` 并在 App.vue「数据展示」分类注册,演示基础/图标+快捷键/分隔线/禁用/危险/子菜单/hover/右键/受控
## 技术栈
- Vue 3 `<script setup>`、Sass、`@lucide/vue` 图标按需导入
- 复用现有 `Popover`(Teleport 定位、外部点击关闭、Esc 关闭、滚动/缩放跟随)与 `utils/position.js` 纯函数
- 样式复用 `--fms-*` token,缺失 token 补到 `src/theme/tokens.css`
## 实现方案
### 1. 扩展 Popover 支持 contextmenu 触发
- `popover.vue`:`trigger` validator 增加 `'contextmenu'`;新增 `onContextMenu` 处理——`preventDefault`,用事件 `clientX/clientY` 构造零尺寸锚点 rect(`{left, top, width:0, height:0, right:clientX, bottom:clientY}`)存入变量,`setOpen(true)` 后 `updatePosition()`
- `updatePosition()`:锚点 rect 存在时优先使用,否则回退 `triggerEl.getBoundingClientRect()`(`position.js` 纯函数不改,仅传参变化)
- 关闭时清空锚点 rect;模板触发器元素绑定 `@contextmenu`
### 2. Dropdown 主组件(dropdown.vue)
- **props**:`items`(Array)、`open`/`default-open`、`trigger`(click/hover/contextmenu,validator)、`placement`(默认 `bottom-start`)、`disabled`、`mouse-enter-delay`/`mouse-leave-delay`、`arrow=false`
- **结构**:`<Popover :trigger="trigger" :placement="placement" :arrow="false">`,`#trigger` 插槽透传默认插槽;`#content` 渲染菜单容器
- **菜单渲染**:`items` 循环——`divider` 渲染分隔线;`type==='label'` 渲染分组标签;`children` 渲染嵌套 `Popover`(hover、`right-start`、offset 4)作为子菜单,父项右侧 ChevronRight 箭头;常规项含 icon/label/shortcut
- **键盘导航**(参照 Select 模式):菜单容器 `role="menu"` `tabindex="-1"`,打开后 `nextTick` focus;`activeIndex` 仅遍历可交互项(跳过 divider/label/disabled),方向键/Home/End 移动并滚动到可视,Enter 触发 `click` 事件,Esc 关闭;`aria-activedescendant` 指向高亮项
- **事件**:点击项 `emit('click', { key, item })` 后关闭;disabled/danger 项均可触发(danger 是视觉变体);子菜单项事件冒泡由嵌套 Popover 隔离
- **插槽逃生舱**:无 `items` 时渲染默认插槽(透出 `close` 函数)
### 3. 样式(参考 shadcn style-nova.css 的 cn-dropdown-menu-*)
- 菜单容器:`min-width: 160px`、`padding: 4px`、`background: --fms-card`、`border: 1px solid --fms-border`、`border-radius: --fms-control-radius`、`box-shadow: --fms-popover-shadow`、`max-height` 溢出滚动
- 菜单项:flex、`gap: 8px`、`padding: 5px 8px`、`border-radius: 4px`、hover/focus 背景 `--fms-secondary-hover`;icon/svg `1em`;shortcut `margin-left: auto`、`12px`、`--fms-text-secondary`;danger 文字 `--fms-danger`;disabled 用 `--fms-disabled-text` + `not-allowed`
- 分隔线:`height: 1px`、`background: --fms-border`、`margin: 4px 0`;分组标签:`12px`、`--fms-text-secondary`
- 过渡复用 popover 的 scale+fade transition(低开销、支持 reduced-motion)
### 4. Token 新增(tokens.css)
```css
/* 下拉菜单(dropdown) */
--fms-dropdown-min-width: 160px;
--fms-dropdown-max-height: 256px;
--fms-dropdown-item-padding-y: 5px;
--fms-dropdown-item-padding-x: 8px;
--fms-dropdown-item-radius: 4px;
--fms-dropdown-divider-margin: 4px 0;
```
### 5. 演示接入
- `src/components/ui/demo/dropdown.vue`:基础菜单、图标+快捷键、分隔线+分组标签、禁用项、危险项、子菜单、hover 触发、右键触发、受控模式
- `App.vue`:import DropdownDemo,`demoMap` 注册,分类「数据展示」加 `{ key: 'dropdown', label: 'Dropdown 下拉菜单' }`
## 性能与可靠性
- 全局监听(外部点击/Esc/scroll/resize)由 Popover 打开时注册、关闭时清理,无长期监听
- 键盘导航仅维护 `activeIndex` 单一状态,`scrollIntoView({ block: 'nearest' })` 惰性滚动,无高频计算
- 子菜单使用嵌套 Popover,复用其移入浮层不关闭、Esc 关闭能力;多层 Esc 一次性全部关闭(MVP 行为,后续可逐层)
- 右键定位基于事件坐标构造零尺寸 rect,`computePosition` 的 flip/shift 兜底防溢出
## 风险与边界
- contextmenu 锚点为鼠标点,浮层大小变化时由 ResizeObserver 重新定位(Popover 已实现)
- `trigger` 数组组合(如 click+hover 并存)本期不支持,保持单一枚举(antdv 亦以单一触发为主),API 最小
- 不提供 size 枚举、不做旧 API 兼容分支(遵循规范 7.1/4.2)
## 设计风格
Dropdown 为浮层类菜单组件,视觉参考 shadcn DropdownMenu,与项目现有 Popover/Select 下拉视觉体系统一。
- **浮层卡片**:与 Popover 一致的白/深色卡片(`--fms-card`),1px 边框(`--fms-border`)、6px 圆角、柔和投影(`--fms-popover-shadow`),内容内边距 4px,最小宽度 160px,超长列表滚动
- **菜单项**:紧凑单行布局——图标(1em 灰色)在左,文字居中偏左,快捷键右对齐浅灰小字;hover/聚焦时整行浅灰高亮(`--fms-secondary-hover`),选中态主色;禁用项灰字不可点;危险项(如删除/退出)文字为红色 `--fms-danger`
- **分组与分隔**:分组小标签(12px 浅灰小字)、1px 分隔线,信息层级清晰
- **子菜单**:父项右侧 ChevronRight 小箭头指示,hover 展开右侧次级菜单卡片,视觉同主菜单
- **动效**:淡入 + 轻微缩放(150ms,复用 popover 过渡),hover 高亮 150ms 过渡,支持 `prefers-reduced-motion`
- **交互反馈**:打开时菜单项方向键可达、Enter 确认、Esc 关闭;触发器打开态可用 popover 现有高亮语义
## Agent Extensions
### Skill
- **antdv-next**
- Purpose: 查询 antdv-next Dropdown 的 props/events/slots API 文档与触发交互细节(trigger、placement、子菜单),验证 API 设计一致性
- Expected outcome: 确认 Dropdown 的 trigger 枚举、事件签名、子菜单数据结构,确保与参考实现对齐
- **ui-ux-pro-max**
- Purpose: 为下拉菜单浮层样式与交互细节(hover 态、危险项、分组分隔、动效时长)提供设计参考
- Expected outcome: 产出与 shadcn DropdownMenu 风格一致的视觉规范,落地到 index.scss
### SubAgent
- **code-explorer**
- Purpose: 实施前确认 Popover 触发机制、Select 键盘导航细节与 App.vue demoMap 注册模式,避免遗漏调用点
- Expected outcome: 提供精确的修改目标清单与插入位置