20260817172952
This commit is contained in:
1 parent
4ef678ba65
commit
5bbd3c88b4
1865 files changed
+2261
-901
No files matched your search
@@ -0,0 +1,146 @@
|
||||
---
|
||||
name: dropdown-enhance
|
||||
overview: "增强组件库 Dropdown:支持 item 自带 onClick、新增 #header 富头部插槽、新增 DropdownItem 子组件(类 el-dropdown-item 写法),并把 AppTopbar 用户下拉迁移到新能力(头像+名字+组织头部)。同时保留现有 items 数组 + 全局 @click 与 #menu 逃生舱。"
|
||||
todos:
|
||||
- id: enhance-menu-onclick
|
||||
content: 修改 menu.vue 普通项点击先执行 item.onClick 再 selectItem
|
||||
status: completed
|
||||
- id: add-dropdown-item
|
||||
content: 新建 DropdownItem.vue 声明式子组件并定义 props
|
||||
status: completed
|
||||
dependencies:
|
||||
- enhance-menu-onclick
|
||||
- id: enhance-dropdown-header
|
||||
content: "dropdown.vue 增加 #header 插槽与 DropdownItem VNode 收集转 items"
|
||||
status: completed
|
||||
dependencies:
|
||||
- add-dropdown-item
|
||||
- id: add-header-style
|
||||
content: index.scss 新增 .dropdown-header 样式
|
||||
status: completed
|
||||
dependencies:
|
||||
- enhance-dropdown-header
|
||||
- id: refactor-topbar
|
||||
content: "AppTopbar 用户下拉改用 #header 富头部并精简 items"
|
||||
status: completed
|
||||
dependencies:
|
||||
- enhance-dropdown-header
|
||||
- add-header-style
|
||||
- id: verify-uses
|
||||
content: 用 [subagent:code-explorer] 核查所有 Dropdown 用法无回归
|
||||
status: completed
|
||||
dependencies:
|
||||
- refactor-topbar
|
||||
---
|
||||
|
||||
## 用户需求
|
||||
|
||||
参考 Element Plus 的 `<el-dropdown-item>` 写法,增强项目组件库 `@/components/ui/dropdown` 的下拉菜单能力,使其更灵活、可自定义。
|
||||
|
||||
## 产品概述
|
||||
|
||||
为 Dropdown 组件库新增三种能力并保持向后兼容:
|
||||
|
||||
1. items 数组项支持自带 `onClick(item)` 回调,点击某项时优先执行该项自己的回调,同时保留全局 `@click` 分发。
|
||||
2. 支持 `<DropdownItem>` 子组件写法(具名/默认插槽声明每个菜单项),类似 el-dropdown-item 的声明式用法。
|
||||
3. 新增 `#header` 插槽,在菜单项列表上方渲染「头像 + 名字 + 组织」等富头部内容。
|
||||
|
||||
现有能力(items + 全局 @click、#menu 逃生舱、键盘导航、子菜单 hover)必须全部保留不破坏。
|
||||
|
||||
## 核心特性
|
||||
|
||||
- item 级别 `onClick`:在 items 配置里直接写点击处理函数,命中该项时直接触发,无需统一在 @click 里 switch。
|
||||
- `<DropdownItem>` 子组件:支持 `key`/`icon`/`danger`/`disabled`/`divider` 等属性与默认插槽内容,可替代纯数组声明。
|
||||
- `#header` 富头部插槽:Dropdown 浮层顶部可插入任意自定义富内容(顶部 header 区,与菜单项列表之间有分隔)。
|
||||
- 向后兼容:未配置 onClick 的项仍走全局 @click;未使用 DropdownItem 时仍可用 items 数组;未使用 #header 时浮层不变。
|
||||
- AppTopbar 用户下拉改造:将「头像 + 名字 + 组织」从 trigger 移到菜单 #header 插槽,trigger 仅保留头像或精简触发按钮。
|
||||
|
||||
## 技术栈
|
||||
|
||||
- 前端框架:Vue 3(`<script setup>` SFC + scoped 样式)
|
||||
- 组件库模式:`@/components/ui/` 下 SFC + `index.scss`(`@use` 引入),复用现有 Dropdown / DropdownMenu / Popover
|
||||
- 样式 Token:`--fms-card` / `--fms-border` / `--fms-primary` / `--fms-secondary-hover` / `--fms-text` / `--fms-text-secondary` / `--fms-control-radius` / `--fms-danger` / `--fms-dropdown-*`
|
||||
- 图标:Lucide(`@lucide/vue`)
|
||||
|
||||
## 实现方案
|
||||
|
||||
### 总体策略
|
||||
|
||||
在现有 Dropdown 组件上做增量增强,不改动 Popover 与定位逻辑;新增 `DropdownItem.vue` 子组件承载声明式写法;在 `DropdownMenu.vue` 与 `dropdown.vue` 中分别支持 `item.onClick` 与 `#header` 插槽。所有增强均为「可选能力」,未使用时不改变既有渲染与行为。
|
||||
|
||||
### 关键技术决策
|
||||
|
||||
1. **item.onClick 优先级**:在 `menu.vue` 普通项点击处改为「先执行 `item.onClick?.(item)`,再 `selectItem(item)`(继续 emit 全局 click)」。这样既支持项自带回调,又保留全局 @click 分发,互不冲突;项回调里可执行 `close()` 关闭(selectItem 内部已关闭)。
|
||||
2. **DropdownItem 子组件**:新建 `DropdownItem.vue`,仅用于「声明语义」——它不作为独立渲染节点,而是被 Dropdown 在 `#default` 默认插槽中收集其 props/插槽内容并转为 items;或直接由 DropdownMenu 消费。为最小化复杂度并避免与现有 items 递归体系冲突,采用「DropdownItem 仅暴露 props + 提供 default 插槽内容,Dropdown 在运行时通过 `useSlots().default` 读取子节点(VNode)还原成 items」的方案,复用 `DropdownMenu` 渲染。这样既获得 el-dropdown-item 的写法,又不重复一套渲染逻辑。
|
||||
|
||||
- 权衡:直接把 DropdownItem 写成独立可点击 DOM 会与现有键盘导航/`activeIndex` provide 体系割裂,需大量重构。采用「VNode 收集转 items」更符合现有架构、改动最小、风险最低。
|
||||
- 取舍:DropdownItem 用法与 items 用法二选一(同一次 Dropdown 内不要混用,混用时以 items 优先或合并,计划以 items 优先、默认插槽 fallback)。
|
||||
|
||||
3. **#header 插槽**:在 `dropdown.vue` 的 Popover `#content` 内、`<DropdownMenu>` 上方插入 `<slot name="header" />`(仅当 items != null 时显示,#menu 逃生舱模式仍由用户自行决定 header 是否出现)。头部区加 `.dropdown-header` 样式(padding、与下方列表之间 1px 分隔线、不可点击、user-select:none)。
|
||||
|
||||
### 性能与可靠性
|
||||
|
||||
- 渲染开销:DropdownItem 的 VNode 收集仅在挂载时执行一次(非热路径),无性能问题;items 递归渲染与现有一致。
|
||||
- 键盘导航:`enabledIndices` 已跳过 divider/label/disabled,新增的 header 不参与索引,无需改动;DropdownItem 转换出的项若带 disabled 自动纳入跳过逻辑。
|
||||
- 向后兼容:所有新 props/插槽均默认空/可选;现有 AppTopbar 改法只在「使用 #header」时调整结构,未用到的 Dropdown 实例零影响。
|
||||
|
||||
## 实现注意事项
|
||||
|
||||
- 复用现有 CSS 变量与 `.dropdown-*` 样式体系,不引入新 token;header 分隔线复用 `.dropdown-divider` 视觉或新建 `.dropdown-header` 独立样式(建议独立,避免 margin 冲突)。
|
||||
- `DropdownItem` 的 `divider` 属性转换为 `{ divider: true }`;`disabled`/`danger`/`icon`/`key` 直接映射;默认插槽内容映射为 `label`(支持富内容时允许 `label` 为 VNode/函数?本期仅支持文本 label,富内容走 #header 或 #menu 逃生舱,保持 YAGNI)。
|
||||
- AppTopbar 改造:删除 `userMenuItems` 里 `{ type:'label', label: org }`,改为在 `#header` 插槽内渲染「头像 + 名字 + 组织」;trigger 保留头像 + 名字按钮(或精简)。注意 scoped 样式 `.fms-avatar`/`.fms-user-name` 需在 AppTopbar 内保留(头部在浮层内,仍属该组件 scoped 作用域通过 slot 传递)。
|
||||
- 保持 0 lint:新增 SFC 遵循现有 ESLint 规则(defineProps 默认值、无未使用变量)。
|
||||
|
||||
## 架构设计
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Dropdown.vue 主组件] -->|items 数组| B[DropdownMenu.vue 递归渲染]
|
||||
A -->|default 插槽含 DropdownItem| C[收集 VNode 转 items]
|
||||
C --> B
|
||||
A -->|#header 插槽| D[富头部区 dropdown-header]
|
||||
A -->|#menu 逃生舱| E[完全自定义内容]
|
||||
B -->|普通项点击| F[执行 item.onClick 再 selectItem emit click]
|
||||
A --> G[Popover 浮层/定位/关闭]
|
||||
```
|
||||
|
||||
组件关系不变,仅在 Dropdown 入口增加「VNode→items」「header 插槽」两路输入,DropdownMenu 增加「onClick 优先」分支。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
fms-vue/src/components/ui/dropdown/
|
||||
├── dropdown.vue # [MODIFY] 新增 #header 插槽(Popover #content 内,DropdownMenu 上方);支持从 default 插槽收集 DropdownItem VNode 转换为 items(fallback 于未传 items 时);onClick 透传由 menu.vue 处理。
|
||||
├── menu.vue # [MODIFY] 普通项点击逻辑改为:先 item.onClick?.(item),再 selectItem(item);其余 divider/label/children 渲染不变。
|
||||
├── DropdownItem.vue # [NEW] 声明式菜单项子组件。仅承载 props(key/icon/danger/disabled/divider/label) 与默认插槽,不独立渲染交互,由 Dropdown 收集为 items 项。
|
||||
└── index.scss # [MODIFY] 新增 .dropdown-header 样式(padding、底部 1px 分隔、user-select:none、非交互)。
|
||||
|
||||
fms-vue/src/layouts/components/
|
||||
└── AppTopbar.vue # [MODIFY] 用户下拉:移除 userMenuItems 中的 type:'label' 组织行;新增 #header 插槽渲染「头像+名字+组织」富头部(复用现有 .fms-avatar/.fms-user-name 样式,新增组织文本样式);trigger 保持头像+名字按钮。
|
||||
```
|
||||
|
||||
## 关键代码结构(接口级)
|
||||
|
||||
```ts
|
||||
// DropdownItem.vue props(声明式项,仅用于被 Dropdown 收集)
|
||||
defineProps<{
|
||||
key?: string | number
|
||||
icon?: Component
|
||||
label?: string
|
||||
danger?: boolean
|
||||
disabled?: boolean
|
||||
divider?: boolean
|
||||
}>()
|
||||
|
||||
// menu.vue 普通项点击(增强后)
|
||||
@click="!item.disabled && onItemClick(item)"
|
||||
// onItemClick: item.onClick?.(item); selectItem(item)
|
||||
```
|
||||
|
||||
## Agent Extensions
|
||||
|
||||
### SubAgent
|
||||
|
||||
- **code-explorer**
|
||||
- 用途:在实施前深入核对 Dropdown/Popover/AppTopbar 的所有引用点与 slot/props 用法,确认改动无遗漏调用方。
|
||||
- 预期结果:产出受影响文件清单与调用关系,确保增强不破坏现有使用 Dropdown 的其他页面(如主题色板 Dropdown 用 #menu 逃生舱)。
|
||||
Reference in new issue
Block a user