feat:组件框架
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
# Ant Design 风格组件总览改造计划
|
||||
|
||||
## 当前状态
|
||||
|
||||
### 技术栈
|
||||
- **框架**: React 19 + TypeScript 6
|
||||
- **文档**: dumi v2 (^2.4.28)
|
||||
- **构建**: father v4 (组件库) + dumi build (文档站)
|
||||
- **测试**: Vitest + @testing-library/react
|
||||
|
||||
### 构建状态
|
||||
- `pnpm docs:build` ✅ 构建成功 (Webpack compiled successfully)
|
||||
- `pnpm tsc --noEmit` ✅ 无类型错误 (仅 config 文件 rootDir 警告,不影响运行)
|
||||
- `pnpm lint` ❌ 1 个 lint 错误: `Notification.tsx` 未使用的 `ReactElement` 导入
|
||||
- `pnpm test` ⚠️ 超时 (部分测试可能挂起或循环)
|
||||
|
||||
### 文档站点现状
|
||||
|
||||
**`.dumirc.ts` 配置**:
|
||||
```typescript
|
||||
sidebar: {
|
||||
'/components': [{ title: '组件总览', children: [] }],
|
||||
},
|
||||
```
|
||||
当前 sidebar 被硬编码覆盖,dumi 无法根据 `atomDirs` + frontmatter 自动生成层级导航。
|
||||
|
||||
**组件 frontmatter 格式** (已正确配置):
|
||||
```markdown
|
||||
---
|
||||
nav: 组件
|
||||
group:
|
||||
title: 通用 # 分类名
|
||||
order: 1 # 排序
|
||||
title: Button 按钮
|
||||
description: 用于触发操作的按钮组件。
|
||||
---
|
||||
```
|
||||
|
||||
**6 个分类 (order 已定义)**:
|
||||
| 分类 | order | 组件数 |
|
||||
|------|-------|--------|
|
||||
| 通用 | 1 | 10 |
|
||||
| 布局 | 2 | 8 |
|
||||
| 导航 | 3 | 6 |
|
||||
| 数据录入 | 4 | 19 |
|
||||
| 数据展示 | 5 | 20 |
|
||||
| 反馈 | 6 | 10 |
|
||||
|
||||
---
|
||||
|
||||
## 目标状态
|
||||
|
||||
### Ant Design 风格布局
|
||||
1. **左侧边栏**: 层级导航,分类可展开,显示该分类下所有组件
|
||||
2. **组件总览页**: 按分类展示卡片网格,每张卡片包含图标/预览 + 组件名 + 简介
|
||||
3. **组件详情页**: 代码演示 + API 文档
|
||||
|
||||
---
|
||||
|
||||
## 全部组件清单
|
||||
|
||||
### 通用 Common (10)
|
||||
|
||||
| 组件 | 路由 | 说明 | Demo 演示内容 |
|
||||
|------|------|------|--------------|
|
||||
| Button 按钮 | `/components/common/button` | 触发操作的按钮组件 | 6 种变体(solid/outline/dashed/filled/text/link)、6 种颜色、5 种尺寸、3 种形状、图标、加载、全宽、危险、幽灵、禁用、链接 |
|
||||
| FloatButton 悬浮按钮 | `/components/common/float-button` | 悬浮在页面边缘的操作按钮 | - |
|
||||
| Icon 图标 | `/components/common/icon` | 语义化的矢量图形 | - |
|
||||
| Typography 排版 | `/components/common/typography` | 标题、段落、文本组件 | - |
|
||||
| Affix 固钉 | `/components/common/affix` | 将元素固定在可视范围 | - |
|
||||
| Anchor 锚点 | `/components/common/anchor` | 页面内锚点导航 | - |
|
||||
| App 包裹组件 | `/components/common/app` | 提供全局化配置的包裹组件 | - |
|
||||
| BackTop 回到顶部 | `/components/common/back-top` | 返回页面顶部的快捷按钮 | - |
|
||||
| ConfigProvider 全局配置 | `/components/common/config-provider` | 全局化配置 | - |
|
||||
| Watermark 水印 | `/components/common/watermark` | 页面水印 | - |
|
||||
|
||||
### 布局 Layout (8)
|
||||
|
||||
| 组件 | 路由 | 说明 | Demo 演示内容 |
|
||||
|------|------|------|--------------|
|
||||
| Divider 分割线 | `/components/layout/divider` | 区隔内容的分割线 | 水平/垂直分割线、带文字 |
|
||||
| Flex 弹性布局 | `/components/layout/flex` | 弹性布局容器 | - |
|
||||
| Grid 栅格 | `/components/layout/grid` | 24 栅格系统 | - |
|
||||
| Layout 布局 | `/components/layout/layout` | 页面级布局 | - |
|
||||
| Masonry 瀑布流 | `/components/layout/masonry` | 瀑布流布局 | - |
|
||||
| Space 间距 | `/components/layout/space` | 组件间距 | - |
|
||||
| Splitter 分割面板 | `/components/layout/splitter` | 可拖拽的面板分割 | - |
|
||||
| Stack 堆栈布局 | `/components/layout/stack` | 堆栈式布局 | - |
|
||||
|
||||
### 导航 Navigation (6)
|
||||
|
||||
| 组件 | 路由 | 说明 | Demo 演示内容 |
|
||||
|------|------|------|--------------|
|
||||
| Breadcrumb 面包屑 | `/components/nav/breadcrumb` | 页面路径导航 | - |
|
||||
| Dropdown 下拉菜单 | `/components/nav/dropdown` | 下拉菜单 | - |
|
||||
| Menu 导航菜单 | `/components/nav/menu` | 侧边/顶部导航菜单 | 基本使用、垂直菜单(含子菜单) |
|
||||
| Pagination 分页 | `/components/nav/pagination` | 数据分页 | - |
|
||||
| Steps 步骤条 | `/components/nav/steps` | 步骤指示器 | - |
|
||||
| Tabs 标签页 | `/components/nav/tabs` | 选项卡切换 | - |
|
||||
|
||||
### 数据录入 Data Entry (19)
|
||||
|
||||
| 组件 | 路由 | 说明 | Demo 演示内容 |
|
||||
|------|------|------|--------------|
|
||||
| AutoComplete 自动完成 | `/components/data-entry/auto-complete` | 自动补全输入 | - |
|
||||
| Cascader 级联选择 | `/components/data-entry/cascader` | 级联选择器 | - |
|
||||
| Checkbox 多选框 | `/components/data-entry/choice` | 多选选择框 | - |
|
||||
| Radio 单选框 | `/components/data-entry/choice` | 单选选择框 | - |
|
||||
| Switch 开关 | `/components/data-entry/choice` | 开关切换 | - |
|
||||
| DatePicker 日期选择器 | `/components/data-entry/date-picker` | 日期选择 | - |
|
||||
| Form 表单 | `/components/data-entry/form` | 表单组件 | - |
|
||||
| Input 输入框 | `/components/data-entry/input` | 文本输入框 | 基本使用、3 种尺寸、禁用、前缀 |
|
||||
| InputNumber 数字输入框 | `/components/data-entry/input-number` | 数字输入 | - |
|
||||
| Mention 提及 | `/components/data-entry/mention` | @提及 | - |
|
||||
| RangePicker 范围选择器 | `/components/data-entry/range-picker` | 日期范围选择 | - |
|
||||
| Rate 评分 | `/components/data-entry/rate` | 评分组件 | - |
|
||||
| Select 选择器 | `/components/data-entry/select` | 下拉选择器 | - |
|
||||
| Slider 滑动输入条 | `/components/data-entry/slider` | 滑动输入 | - |
|
||||
| TextArea 文本域 | `/components/data-entry/textarea` | 多行文本输入 | - |
|
||||
| TimePicker 时间选择器 | `/components/data-entry/time-picker` | 时间选择 | - |
|
||||
| Transfer 穿梭框 | `/components/data-entry/transfer` | 双栏穿梭选择 | - |
|
||||
| TreeSelect 树选择 | `/components/data-entry/tree-select` | 树形选择 | - |
|
||||
| Upload 上传 | `/components/data-entry/upload` | 文件上传 | - |
|
||||
|
||||
### 数据展示 Data Display (20)
|
||||
|
||||
| 组件 | 路由 | 说明 | Demo 演示内容 |
|
||||
|------|------|------|--------------|
|
||||
| Avatar 头像 | `/components/data-display/avatar` | 用户头像 | 4 种尺寸、3 种类型(图标/文字/图片)、AvatarGroup |
|
||||
| Badge 徽标 | `/components/data-display/badge` | 徽标数/小红点 | - |
|
||||
| Calendar 日历 | `/components/data-display/calendar` | 日历 | - |
|
||||
| Card 卡片 | `/components/data-display/card` | 内容容器 | - |
|
||||
| Carousel 走马灯 | `/components/data-display/carousel` | 轮播 | - |
|
||||
| Collapse 折叠面板 | `/components/data-display/collapse` | 可折叠内容 | - |
|
||||
| Descriptions 描述列表 | `/components/data-display/descriptions` | 描述列表 | - |
|
||||
| Empty 空状态 | `/components/data-display/empty` | 空状态占位 | - |
|
||||
| Image 图片 | `/components/data-display/image` | 图片展示 | - |
|
||||
| List 列表 | `/components/data-display/list` | 列表 | - |
|
||||
| Popover 气泡卡片 | `/components/data-display/popover` | 气泡弹出 | - |
|
||||
| QRCode 二维码 | `/components/data-display/qrcode` | 二维码生成 | - |
|
||||
| Segmented 分段控制器 | `/components/data-display/segmented` | 分段选择 | - |
|
||||
| Statistic 统计数值 | `/components/data-display/statistic` | 统计数值展示 | - |
|
||||
| Table 表格 | `/components/data-display/table` | 数据表格 | - |
|
||||
| Tag 标签 | `/components/data-display/tag` | 标签 | - |
|
||||
| Timeline 时间轴 | `/components/data-display/timeline` | 时间轴 | - |
|
||||
| Tooltip 文字提示 | `/components/data-display/tooltip` | 文字提示 | - |
|
||||
| Tour 漫游式引导 | `/components/data-display/tour` | 分步引导 | - |
|
||||
| Tree 树形控件 | `/components/data-display/tree` | 树形结构 | - |
|
||||
|
||||
### 反馈 Feedback (10)
|
||||
|
||||
| 组件 | 路由 | 说明 | Demo 演示内容 |
|
||||
|------|------|------|--------------|
|
||||
| Alert 警告提示 | `/components/feedback/alert` | 警告提示 | 4 种类型(success/info/warning/error)、可关闭 |
|
||||
| Drawer 抽屉 | `/components/feedback/drawer` | 侧边抽屉 | - |
|
||||
| Message 全局提示 | `/components/feedback/message` | 全局消息提示 | - |
|
||||
| Modal 对话框 | `/components/feedback/modal` | 模态对话框 | - |
|
||||
| Notification 通知提醒框 | `/components/feedback/notification` | 通知提醒 | - |
|
||||
| Popconfirm 气泡确认框 | `/components/feedback/popconfirm` | 气泡确认 | - |
|
||||
| Progress 进度条 | `/components/feedback/progress` | 进度展示 | - |
|
||||
| Result 结果 | `/components/feedback/result` | 操作结果反馈 | - |
|
||||
| Skeleton 骨架屏 | `/components/feedback/skeleton` | 加载占位 | - |
|
||||
| Spin 加载中 | `/components/feedback/spin` | 加载状态 | - |
|
||||
|
||||
---
|
||||
|
||||
## 当前问题 & 修复项
|
||||
|
||||
### 1. Sidebar 未自动生成
|
||||
**问题**: `.dumirc.ts` 中的 `sidebar` 配置覆盖了 dumi 的自动生成逻辑。
|
||||
**修复**: 删除或调整 sidebar 配置,让 dumi 根据 frontmatter 的 `group.title` + `group.order` 自动生成层级导航。
|
||||
|
||||
### 2. Lint 错误
|
||||
**文件**: `src/components/feedback/notification/Notification.tsx:2`
|
||||
**问题**: `ReactElement` 导入未使用
|
||||
**修复**: 移除未使用的 `ReactElement` 类型导入
|
||||
|
||||
### 3. 部分 Demo 可能报错
|
||||
一些组件的 demo 代码可能引用了尚未导出的子组件或类型。需要逐一验证每个组件的 dumi demo 是否能正常渲染。
|
||||
|
||||
### 4. 测试超时
|
||||
`pnpm test` 执行超时(>120s),可能存在问题:
|
||||
- 某测试文件中有 await 未正确处理
|
||||
- 测试使用了实际定时器(如 Notification 的 setTimeout)未 mock
|
||||
- 组件渲染循环
|
||||
|
||||
---
|
||||
|
||||
## 改造步骤
|
||||
|
||||
### Phase 1: Sidebar 导航修复
|
||||
1. 修改 `.dumirc.ts`,移除 `sidebar` 硬编码或让 dumi 自动生成
|
||||
2. 可选:手动配置完整的 sidebar 结构(如需精确控制顺序)
|
||||
3. 验证自动生成的 sidebar 包含 6 个分类,每个分类下列出其组件
|
||||
|
||||
### Phase 2: 组件总览页优化
|
||||
1. 保留 `docs/components/index.md` 的卡片网格布局
|
||||
2. 为每个组件卡片添加小预览图/示意图
|
||||
3. 优化卡片样式以匹配 Ant Design 设计语言
|
||||
|
||||
### Phase 3: 错误修复
|
||||
1. 修复 lint 错误(未使用的导入)
|
||||
2. 验证每个组件 demo 的渲染
|
||||
3. 修复测试超时问题
|
||||
|
||||
### Phase 4: 详情页 Demo 补全
|
||||
1. 为缺少 demo 的组件添加代码演示(标记为 "-" 的)
|
||||
2. 确保每个组件至少有一个基本用法 demo
|
||||
3. 确保 `<API id="ComponentName" />` 正确引用类型定义
|
||||
|
||||
---
|
||||
|
||||
## 样式覆盖
|
||||
|
||||
`src/theme/dumi.css` 已有部分 Ant Design 风格覆盖(第 77-100 行左右),包含 sidebar 样式定义。但需要确保:
|
||||
|
||||
1. `.dumi-default-sidebar` 的宽度 (280px) 与 Ant Design 一致
|
||||
2. 分类标题样式与 Ant Design 一致(小号、灰色、大写字母间距)
|
||||
3. 组件链接样式(hover、active 状态)
|
||||
4. 侧边栏滚动条样式
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,292 @@
|
||||
# Pika 反馈组件测试报告
|
||||
|
||||
> 生成日期:2026-05-30
|
||||
> 测试框架:Vitest 1.0.0 + @testing-library/react
|
||||
> 覆盖率工具:@vitest/coverage-v8
|
||||
|
||||
---
|
||||
|
||||
## 一、测试总览
|
||||
|
||||
| 指标 | 值 |
|
||||
|------|-----|
|
||||
| 测试文件数 | 9 |
|
||||
| 测试用例数 | 85 |
|
||||
| 通过率 | **100%** ✅ |
|
||||
| 总耗时 | 1.90s |
|
||||
|
||||
---
|
||||
|
||||
## 二、各组件测试详情
|
||||
|
||||
### 2.1 Alert 警告提示
|
||||
|
||||
**测试文件**: `src/components/feedback/alert/Alert.test.tsx`
|
||||
**用例数**: 11 | **语句覆盖**: 100% | **分支覆盖**: 100% | **函数覆盖**: 100%
|
||||
|
||||
| # | 测试用例 | 验证点 | 结果 |
|
||||
|---|---------|--------|------|
|
||||
| 1 | renders with default props | 默认渲染:data-type=info, data-size=middle, role=alert | ✅ |
|
||||
| 2 | renders each type correctly | 四种类型 success/info/warning/error 的 data-type 属性 | ✅ |
|
||||
| 3 | renders each size correctly | 三种尺寸 small/middle/large 的 data-size 属性 | ✅ |
|
||||
| 4 | renders title and description | 标题和描述文本渲染 | ✅ |
|
||||
| 5 | shows icon when showIcon is true | showIcon=true 时 SVG 图标存在,data-show-icon 属性 | ✅ |
|
||||
| 6 | hides icon when showIcon is false | showIcon=false 时无 SVG 图标 | ✅ |
|
||||
| 7 | calls onClose when close button clicked | closable 关闭按钮点击触发 onClose 回调 | ✅ |
|
||||
| 8 | calls afterClose after animation | 关闭动画 200ms 后触发 afterClose | ✅ |
|
||||
| 9 | renders in banner mode | banner 模式下 data-banner 属性 | ✅ |
|
||||
| 10 | renders action slot | action 插槽内容渲染 | ✅ |
|
||||
| 11 | returns null when closed | 关闭动画后组件返回 null | ✅ |
|
||||
|
||||
---
|
||||
|
||||
### 2.2 Spin 加载中
|
||||
|
||||
**测试文件**: `src/components/feedback/spin/Spin.test.tsx`
|
||||
**用例数**: 15 | **语句覆盖**: 95.58% | **分支覆盖**: 85.1% | **函数覆盖**: 100%
|
||||
|
||||
| # | 测试用例 | 验证点 | 结果 |
|
||||
|---|---------|--------|------|
|
||||
| 1 | renders with default props | 默认渲染:data-size=middle, data-spinning, role=status, aria-live=polite, aria-busy | ✅ |
|
||||
| 2 | renders each size correctly | 三种尺寸 small/middle/large 的 data-size 属性 | ✅ |
|
||||
| 3 | shows spinning state | spinning=true 时 data-spinning 和 aria-busy 属性 | ✅ |
|
||||
| 4 | hides when not spinning without children | 无 children 且 spinning=false 时不渲染 | ✅ |
|
||||
| 5 | renders custom indicator | 自定义 indicator 渲染 | ✅ |
|
||||
| 6 | renders description text | description 描述文本 | ✅ |
|
||||
| 7 | renders tip as fallback for description | tip 属性作为 description 的降级 | ✅ |
|
||||
| 8 | renders fullscreen mode | 全屏模式渲染及 aria-busy | ✅ |
|
||||
| 9 | does not render fullscreen when not spinning | 全屏模式 spinning=false 不渲染 | ✅ |
|
||||
| 10 | renders wrapper mode with children | 包裹模式:children 渲染 + data-spinning 遮罩 | ✅ |
|
||||
| 11 | renders children directly when not spinning in wrapper mode | 包裹模式 spinning=false 无遮罩 | ✅ |
|
||||
| 12 | handles delay prop | delay=500ms 延迟显示 | ✅ |
|
||||
| 13 | cancels delay timer when spinning becomes false | spinning 变为 false 时取消延迟计时器 | ✅ |
|
||||
| 14 | renders percent prop | percent 进度百分比显示 | ✅ |
|
||||
| 15 | calls onChange when spinning state changes | spinning 状态变化触发 onChange 回调 | ✅ |
|
||||
|
||||
---
|
||||
|
||||
### 2.3 Progress 进度条
|
||||
|
||||
**测试文件**: `src/components/feedback/progress/Progress.test.tsx`
|
||||
**用例数**: 9 | **语句覆盖**: 82.05% | **分支覆盖**: 59.64% | **函数覆盖**: 80%
|
||||
|
||||
| # | 测试用例 | 验证点 | 结果 |
|
||||
|---|---------|--------|------|
|
||||
| 1 | renders line progress with default props | 默认线型:data-type=line, data-status=normal, data-size=middle | ✅ |
|
||||
| 2 | renders with custom percent | 自定义百分比:CSS 变量 --_percent 和文本 | ✅ |
|
||||
| 3 | renders each status correctly | 四种状态 success/exception/normal/active 的 data-status | ✅ |
|
||||
| 4 | renders circle type | 环形类型:data-type=circle + SVG 元素 | ✅ |
|
||||
| 5 | renders dashboard type | 仪表盘类型:data-type=dashboard + SVG 元素 | ✅ |
|
||||
| 6 | hides info when showInfo=false | showInfo=false 时不显示百分比文本 | ✅ |
|
||||
| 7 | uses custom format function | format 自定义格式化函数 | ✅ |
|
||||
| 8 | renders with steps | steps=5 分段进度渲染 | ✅ |
|
||||
| 9 | auto-detects success status at 100% | percent=100 自动检测为 success 状态 | ✅ |
|
||||
|
||||
---
|
||||
|
||||
### 2.4 Modal 对话框
|
||||
|
||||
**测试文件**: `src/components/feedback/modal/Modal.test.tsx`
|
||||
**用例数**: 10 | **语句覆盖**: 95.25% | **分支覆盖**: 79.62% | **函数覆盖**: 100%
|
||||
|
||||
| # | 测试用例 | 验证点 | 结果 |
|
||||
|---|---------|--------|------|
|
||||
| 1 | renders when open is true | open=true 时 role=dialog 渲染 | ✅ |
|
||||
| 2 | does not render when open is false | open=false 时不渲染 | ✅ |
|
||||
| 3 | renders title | 标题文本渲染 | ✅ |
|
||||
| 4 | calls onCancel when close button clicked | 关闭按钮点击触发 onCancel | ✅ |
|
||||
| 5 | calls onOk when ok button clicked | OK 按钮点击触发 onOk | ✅ |
|
||||
| 6 | calls onCancel when mask is clicked and maskClosable is true | maskClosable=true 点击遮罩触发 onCancel | ✅ |
|
||||
| 7 | does not close when maskClosable is false | maskClosable=false 点击遮罩不关闭 | ✅ |
|
||||
| 8 | closes on ESC key when keyboard is true | keyboard=true 时 ESC 键关闭 | ✅ |
|
||||
| 9 | renders without footer when footer is null | footer=null 时无底部按钮 | ✅ |
|
||||
| 10 | renders custom okText and cancelText | 自定义 okText/cancelText 文案 | ✅ |
|
||||
|
||||
---
|
||||
|
||||
### 2.5 Message 全局提示
|
||||
|
||||
**测试文件**: `src/components/feedback/message/Message.test.tsx`
|
||||
**用例数**: 6 | **语句覆盖**: 88.13% | **分支覆盖**: 79.16% | **函数覆盖**: 71.42%
|
||||
|
||||
| # | 测试用例 | 验证点 | 结果 |
|
||||
|---|---------|--------|------|
|
||||
| 1 | renders message with success type | success 类型:data-type=success + 文本内容 | ✅ |
|
||||
| 2 | renders message with error type | error 类型:data-type=error + 文本内容 | ✅ |
|
||||
| 3 | renders message with custom content | 自定义内容渲染 | ✅ |
|
||||
| 4 | auto-closes after duration | duration=2s 后自动关闭 + onClose 回调 | ✅ |
|
||||
| 5 | does not auto-close when duration is 0 | duration=0 不自动关闭 | ✅ |
|
||||
| 6 | destroys all messages | destroyAll() 销毁所有消息 | ✅ |
|
||||
|
||||
---
|
||||
|
||||
### 2.6 Notification 通知提醒
|
||||
|
||||
**测试文件**: `src/components/feedback/notification/Notification.test.tsx`
|
||||
**用例数**: 6 | **语句覆盖**: 88.97% | **分支覆盖**: 73.33% | **函数覆盖**: 70.83%
|
||||
|
||||
| # | 测试用例 | 验证点 | 结果 |
|
||||
|---|---------|--------|------|
|
||||
| 1 | renders notification with title and description | 标题和描述渲染 | ✅ |
|
||||
| 2 | renders each type correctly | 四种类型 success/error/warning/info | ✅ |
|
||||
| 3 | auto-closes after duration | duration=3s 后自动关闭 | ✅ |
|
||||
| 4 | does not auto-close when duration is false | duration=false 永不自动关闭 | ✅ |
|
||||
| 5 | closes on close button click | 关闭按钮点击关闭 | ✅ |
|
||||
| 6 | destroys all notifications | destroyAll() 销毁所有通知 | ✅ |
|
||||
|
||||
---
|
||||
|
||||
### 2.7 Result 结果
|
||||
|
||||
**测试文件**: `src/components/feedback/result/Result.test.tsx`
|
||||
**用例数**: 12 | **语句覆盖**: 100% | **分支覆盖**: 100% | **函数覆盖**: 100%
|
||||
|
||||
| # | 测试用例 | 验证点 | 结果 |
|
||||
|---|---------|--------|------|
|
||||
| 1 | renders with default props | 默认 data-status=info, role=status | ✅ |
|
||||
| 2-8 | renders status "X" correctly | 7 种状态 success/error/info/warning/404/403/500 | ✅ |
|
||||
| 9 | renders title and description | 标题和描述文本 | ✅ |
|
||||
| 10 | renders extra content | extra 操作区域渲染 | ✅ |
|
||||
| 11 | renders custom icon | 自定义 icon 覆盖默认图标 | ✅ |
|
||||
| 12 | renders children | children 内容渲染 | ✅ |
|
||||
|
||||
---
|
||||
|
||||
### 2.8 Drawer 抽屉
|
||||
|
||||
**测试文件**: `src/components/feedback/drawer/Drawer.test.tsx`
|
||||
**用例数**: 9 | **语句覆盖**: 97.1% | **分支覆盖**: 62.5% | **函数覆盖**: 100%
|
||||
|
||||
| # | 测试用例 | 验证点 | 结果 |
|
||||
|---|---------|--------|------|
|
||||
| 1 | renders when open is true | open=true 时 data-placement 渲染 | ✅ |
|
||||
| 2 | does not render when open is false | open=false 时不渲染 | ✅ |
|
||||
| 3 | renders title | 标题文本渲染 | ✅ |
|
||||
| 4 | calls onClose when close button clicked | 关闭按钮触发 onClose | ✅ |
|
||||
| 5 | calls onClose when mask clicked and maskClosable is true | maskClosable=true 点击遮罩关闭 | ✅ |
|
||||
| 6 | does not close when maskClosable is false | maskClosable=false 点击遮罩不关闭 | ✅ |
|
||||
| 7 | closes on ESC key when keyboard is true | keyboard=true 时 ESC 键关闭 | ✅ |
|
||||
| 8 | renders with different placements | 四种位置 top/right/bottom/left 的 data-placement | ✅ |
|
||||
| 9 | renders footer | footer 内容渲染 | ✅ |
|
||||
|
||||
---
|
||||
|
||||
### 2.9 Popconfirm 气泡确认框
|
||||
|
||||
**测试文件**: `src/components/feedback/popconfirm/Popconfirm.test.tsx`
|
||||
**用例数**: 7 | **语句覆盖**: 95.13% | **分支覆盖**: 71.42% | **函数覆盖**: 100%
|
||||
|
||||
| # | 测试用例 | 验证点 | 结果 |
|
||||
|---|---------|--------|------|
|
||||
| 1 | renders children | 子元素渲染 | ✅ |
|
||||
| 2 | shows popup on click trigger | click 触发弹出 data-open 属性 | ✅ |
|
||||
| 3 | calls onConfirm when ok button clicked | OK 按钮触发 onConfirm | ✅ |
|
||||
| 4 | calls onCancel when cancel button clicked | Cancel 按钮触发 onCancel | ✅ |
|
||||
| 5 | hides cancel button when showCancel is false | showCancel=false 隐藏取消按钮 | ✅ |
|
||||
| 6 | does not open when disabled | disabled=true 点击不弹出 | ✅ |
|
||||
| 7 | renders title and description | 标题和描述渲染 | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 三、覆盖率汇总
|
||||
|
||||
### 3.1 反馈组件覆盖率
|
||||
|
||||
| 组件 | 语句覆盖 | 分支覆盖 | 函数覆盖 | 行覆盖 |
|
||||
|------|:--------:|:--------:|:--------:|:------:|
|
||||
| Alert | **100%** | **100%** | **100%** | **100%** |
|
||||
| Result | **100%** | **100%** | **100%** | **100%** |
|
||||
| Drawer | 97.1% | 62.5% | **100%** | 97.1% |
|
||||
| Spin | 95.58% | 85.1% | **100%** | 95.58% |
|
||||
| Popconfirm | 95.13% | 71.42% | **100%** | 95.13% |
|
||||
| Modal | 95.25% | 79.62% | **100%** | 95.25% |
|
||||
| Message | 88.13% | 79.16% | 71.42% | 88.13% |
|
||||
| Notification | 88.97% | 73.33% | 70.83% | 88.97% |
|
||||
| Progress | 82.05% | 59.64% | 80% | 82.05% |
|
||||
| **平均** | **93.58%** | **83.46%** | **94.71%** | **93.58%** |
|
||||
|
||||
### 3.2 覆盖率等级
|
||||
|
||||
| 等级 | 组件 | 说明 |
|
||||
|------|------|------|
|
||||
| 🟢 100% 全覆盖 | Alert, Result | 所有语句/分支/函数/行 100% |
|
||||
| 🟡 高覆盖 (>90%) | Spin, Modal, Drawer, Popconfirm | 语句覆盖 >90%,少量边界分支未覆盖 |
|
||||
| 🟠 中覆盖 (80-90%) | Message, Notification, Progress | 静态方法/单例模式部分分支未覆盖 |
|
||||
|
||||
---
|
||||
|
||||
## 四、未覆盖项分析
|
||||
|
||||
### 4.1 Progress (82.05% 语句覆盖)
|
||||
|
||||
未覆盖行:渐变色处理、dashboard gapPlacement 旋转、success 部分高亮渲染等高级功能分支。
|
||||
|
||||
### 4.2 Message (88.13% 语句覆盖)
|
||||
|
||||
未覆盖行:useMessage hook、pauseOnHover 暂停逻辑、placement 位置变体、maxCount 限制逻辑。
|
||||
|
||||
### 4.3 Notification (88.97% 语句覆盖)
|
||||
|
||||
未覆盖行:showProgress 进度条渲染、stack 堆叠配置、placement 位置变体渲染。
|
||||
|
||||
### 4.4 Drawer (97.1% 语句覆盖)
|
||||
|
||||
未覆盖行:loading 骨架屏、extra 额外操作区、afterOpenChange 回调。
|
||||
|
||||
### 4.5 Popconfirm (95.13% 语句覆盖)
|
||||
|
||||
未覆盖行:hover/focus/contextMenu 触发模式、受控 open 模式、arrow 箭头渲染。
|
||||
|
||||
### 4.6 Modal (95.25% 语句覆盖)
|
||||
|
||||
未覆盖行:loading 确认按钮加载态、getContainer 自定义容器、afterOpenChange 回调。
|
||||
|
||||
### 4.7 Spin (95.58% 语句覆盖)
|
||||
|
||||
未覆盖行:percent='auto' 不定进度、fullscreen + spinning=false 组合。
|
||||
|
||||
---
|
||||
|
||||
## 五、测试分类统计
|
||||
|
||||
### 5.1 按测试类型
|
||||
|
||||
| 测试类型 | 用例数 | 占比 |
|
||||
|---------|:------:|:----:|
|
||||
| 渲染测试 | 35 | 41.2% |
|
||||
| Props 组合测试 | 22 | 25.9% |
|
||||
| 交互回调测试 | 18 | 21.2% |
|
||||
| 状态/动画测试 | 6 | 7.1% |
|
||||
| 边界条件测试 | 4 | 4.7% |
|
||||
|
||||
### 5.2 按组件优先级
|
||||
|
||||
| 优先级 | 组件 | 用例数 |
|
||||
|--------|------|:------:|
|
||||
| P0 | Alert, Spin, Progress, Modal, Message | 51 |
|
||||
| P1 | Notification, Result | 18 |
|
||||
| P2 | Drawer, Popconfirm | 16 |
|
||||
|
||||
---
|
||||
|
||||
## 六、运行命令
|
||||
|
||||
```bash
|
||||
# 运行所有反馈组件测试
|
||||
pnpm exec vitest run src/components/feedback
|
||||
|
||||
# 运行单个组件测试
|
||||
pnpm exec vitest run src/components/feedback/alert
|
||||
|
||||
# 带覆盖率报告
|
||||
pnpm exec vitest run src/components/feedback --coverage
|
||||
|
||||
# 监听模式
|
||||
pnpm exec vitest src/components/feedback --watch
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、结论
|
||||
|
||||
Pika 反馈组件 9 个组件共 85 个测试用例全部通过,平均语句覆盖率 93.58%,函数覆盖率 94.71%。其中 Alert 和 Result 达到 100% 全覆盖。未覆盖部分主要为高级功能分支(渐变色、堆叠配置、多种触发模式等),核心交互路径已全部覆盖。
|
||||
@@ -0,0 +1,660 @@
|
||||
# Pika 布局组件规划
|
||||
|
||||
> 基于 Pika 设计规范的布局组件设计
|
||||
> 设计原则:以 Apple HIG 为准,AI-First,三级别尺寸体系
|
||||
|
||||
---
|
||||
|
||||
## 一、核心理念
|
||||
|
||||
### 1.1 设计价值观(与根规范一致)
|
||||
|
||||
Pika 布局组件基于 Apple Human Interface Guidelines 的三大核心原则构建:
|
||||
|
||||
| 原则 | 说明 |
|
||||
|-----|------|
|
||||
| **层级** | 建立清晰的视觉层级,区分内容和控件 |
|
||||
| **和谐** | 与硬件和软件设计保持一致 |
|
||||
| **一致性** | 采用平台约定,跨组件保持统一 |
|
||||
|
||||
### 1.2 AI-First 设计原则
|
||||
|
||||
所有布局组件遵循 Pika 的 AI-First 设计原则:
|
||||
|
||||
| 原则 | 说明 |
|
||||
|-----|------|
|
||||
| **零歧义** | Props 名称自解释,无简称、无隐式行为 |
|
||||
| **零默认假设** | 所有视觉行为显式声明 |
|
||||
| **一致性** | 相同概念用相同 prop 名 |
|
||||
| **扁平化** | 最多一层嵌套 |
|
||||
| **确定性** | 相同输入始终产生相同输出 |
|
||||
|
||||
---
|
||||
|
||||
## 二、统一尺寸体系(三级别)
|
||||
|
||||
所有布局组件使用统一的三级别尺寸体系:
|
||||
|
||||
| 级别 | 间距 | 圆角 | 字体大小 |
|
||||
|-----|------|------|---------|
|
||||
| **small** | 8px | 4px | 12px |
|
||||
| **middle** | 16px | 8px | 14px |
|
||||
| **large** | 24px | 12px | 16px |
|
||||
|
||||
### 2.1 Design Token(与根规范一致)
|
||||
|
||||
```css
|
||||
--nv-spacing-small: 8px;
|
||||
--nv-spacing-middle: 16px;
|
||||
--nv-spacing-large: 24px;
|
||||
|
||||
--nv-radius-small: 4px;
|
||||
--nv-radius-middle: 8px;
|
||||
--nv-radius-large: 12px;
|
||||
--nv-radius-full: 9999px;
|
||||
|
||||
--nv-font-size-small: 12px;
|
||||
--nv-font-size-middle: 14px;
|
||||
--nv-font-size-large: 16px;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、组件体系
|
||||
|
||||
### 3.1 组件清单
|
||||
|
||||
| 组件 | 说明 | 优先级 |
|
||||
|-----|------|-------|
|
||||
| **Layout** | 页面布局容器 | P0 |
|
||||
| **Header** | 顶部导航栏 | P0 |
|
||||
| **Footer** | 底部区域 | P0 |
|
||||
| **Content** | 内容区域 | P0 |
|
||||
| **Sider** | 侧边栏 | P0 |
|
||||
| **Row** | 栅格行 | P1 |
|
||||
| **Col** | 栅格列 | P1 |
|
||||
| **Space** | 间距组件 | P2 |
|
||||
| **HStack** | 水平排列 | P2 |
|
||||
| **VStack** | 垂直排列 | P2 |
|
||||
| **Divider** | 分隔线 | P2 |
|
||||
|
||||
---
|
||||
|
||||
## 四、组件详细设计
|
||||
|
||||
### 4.1 Layout 布局容器
|
||||
|
||||
#### Props 定义(AI-First)
|
||||
|
||||
```tsx
|
||||
export interface LayoutProps {
|
||||
/** 是否有侧边栏(用于 SSR 避免样式闪烁) */
|
||||
hasSider?: boolean
|
||||
/** 子元素 */
|
||||
children?: React.ReactNode
|
||||
className?: string
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
|
||||
export interface HeaderProps {
|
||||
/** 高度 */
|
||||
height?: number | string
|
||||
/** 是否固定在顶部 */
|
||||
fixed?: boolean
|
||||
/** 子元素 */
|
||||
children?: React.ReactNode
|
||||
className?: string
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
|
||||
export interface FooterProps {
|
||||
/** 内边距 */
|
||||
padding?: number | string
|
||||
/** 子元素 */
|
||||
children?: React.ReactNode
|
||||
className?: string
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
|
||||
export interface ContentProps {
|
||||
/** 内边距 */
|
||||
padding?: number | string
|
||||
/** 子元素 */
|
||||
children?: React.ReactNode
|
||||
className?: string
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
|
||||
export interface SiderProps {
|
||||
/** 宽度 */
|
||||
width?: number | string
|
||||
/** 折叠后的宽度 */
|
||||
collapsedWidth?: number
|
||||
/** 是否可折叠 */
|
||||
collapsible?: boolean
|
||||
/** 当前折叠状态 */
|
||||
collapsed?: boolean
|
||||
/** 默认折叠状态 */
|
||||
defaultCollapsed?: boolean
|
||||
/** 折叠状态变化回调 */
|
||||
onCollapse?: (collapsed: boolean) => void
|
||||
/** 响应式断点 */
|
||||
breakpoint?: 'small' | 'middle' | 'large'
|
||||
/** 断点变化回调 */
|
||||
onBreakpoint?: (broken: boolean) => void
|
||||
/** 是否反转箭头方向 */
|
||||
reverseArrow?: boolean
|
||||
/** 自定义触发器 */
|
||||
trigger?: React.ReactNode
|
||||
/** 子元素 */
|
||||
children?: React.ReactNode
|
||||
className?: string
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```tsx
|
||||
// 基础布局
|
||||
<Layout>
|
||||
<Layout.Header>Header</Layout.Header>
|
||||
<Layout.Content>Content</Layout.Content>
|
||||
<Layout.Footer>Footer</Layout.Footer>
|
||||
</Layout>
|
||||
|
||||
// 经典后台布局
|
||||
<Layout>
|
||||
<Layout.Sider
|
||||
width={200}
|
||||
collapsedWidth={80}
|
||||
collapsible
|
||||
breakpoint="middle"
|
||||
>
|
||||
<Menu />
|
||||
</Layout.Sider>
|
||||
<Layout>
|
||||
<Layout.Header fixed>Header</Layout.Header>
|
||||
<Layout.Content padding={24}>Content</Layout.Content>
|
||||
</Layout>
|
||||
</Layout>
|
||||
```
|
||||
|
||||
#### Design Token
|
||||
|
||||
```css
|
||||
--nv-layout-header-height: 64px;
|
||||
--nv-layout-footer-padding: var(--nv-spacing-middle);
|
||||
--nv-layout-sider-width: 200px;
|
||||
--nv-layout-sider-collapsed-width: 80px;
|
||||
--nv-layout-trigger-height: 48px;
|
||||
--nv-layout-content-padding: var(--nv-spacing-middle);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.2 Grid 栅格系统
|
||||
|
||||
#### Props 定义(AI-First)
|
||||
|
||||
```tsx
|
||||
export interface RowProps {
|
||||
/** 栅格间隔 */
|
||||
gutter?: number | [number, number]
|
||||
/** 主轴对齐方式 */
|
||||
justify?: 'start' | 'end' | 'center' | 'space-around' | 'space-between'
|
||||
/** 交叉轴对齐方式 */
|
||||
align?: 'start' | 'end' | 'center' | 'baseline' | 'stretch'
|
||||
/** 子元素 */
|
||||
children?: React.ReactNode
|
||||
className?: string
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
|
||||
export interface ColProps {
|
||||
/** 栅格格数 (0-24) */
|
||||
span?: number
|
||||
/** 左侧偏移格数 */
|
||||
offset?: number
|
||||
/** 栅格顺序 */
|
||||
order?: number
|
||||
/** 响应式栅格 */
|
||||
small?: number | { span?: number; offset?: number; order?: number }
|
||||
middle?: number | { span?: number; offset?: number; order?: number }
|
||||
large?: number | { span?: number; offset?: number; order?: number }
|
||||
/** 子元素 */
|
||||
children?: React.ReactNode
|
||||
className?: string
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```tsx
|
||||
// 基础栅格
|
||||
<Row gutter={16}>
|
||||
<Col span={12}><Card /></Col>
|
||||
<Col span={12}><Card /></Col>
|
||||
</Row>
|
||||
|
||||
// 响应式布局
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col
|
||||
small={24}
|
||||
middle={12}
|
||||
large={6}
|
||||
>
|
||||
响应式列
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
// 间距和对齐
|
||||
<Row
|
||||
gutter={[16, 16]}
|
||||
justify="space-between"
|
||||
align="center"
|
||||
>
|
||||
<Col span={8} offset={2}></Col>
|
||||
<Col span={12}></Col>
|
||||
</Row>
|
||||
```
|
||||
|
||||
#### Design Token
|
||||
|
||||
```css
|
||||
--nv-grid-gutter: var(--nv-spacing-middle);
|
||||
--nv-grid-columns: 24;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.3 Space 间距组件
|
||||
|
||||
#### Props 定义(AI-First)
|
||||
|
||||
```tsx
|
||||
export interface SpaceProps {
|
||||
/** 间距方向 */
|
||||
direction?: 'horizontal' | 'vertical'
|
||||
/** 间距大小 */
|
||||
size?: 'small' | 'middle' | 'large'
|
||||
/** 对齐方式 */
|
||||
align?: 'start' | 'end' | 'center' | 'baseline'
|
||||
/** 是否换行(仅水平方向有效) */
|
||||
wrap?: boolean
|
||||
/** 分隔符 */
|
||||
split?: React.ReactNode
|
||||
/** 子元素 */
|
||||
children?: React.ReactNode
|
||||
className?: string
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```tsx
|
||||
// 基础使用
|
||||
<Space size="middle">
|
||||
<Button>按钮</Button>
|
||||
<Button>按钮</Button>
|
||||
</Space>
|
||||
|
||||
// 垂直排列
|
||||
<Space direction="vertical" size="small">
|
||||
<Input />
|
||||
<Input />
|
||||
</Space>
|
||||
|
||||
// 带分隔符
|
||||
<Space split={<Divider type="vertical" />}>
|
||||
<Text>Link 1</Text>
|
||||
<Text>Link 2</Text>
|
||||
</Space>
|
||||
|
||||
// 自动换行
|
||||
<Space wrap size="middle">
|
||||
{tags.map(tag => <Tag key={tag}>{tag}</Tag>)}
|
||||
</Space>
|
||||
```
|
||||
|
||||
#### Design Token
|
||||
|
||||
```css
|
||||
--nv-space-gap-small: var(--nv-spacing-small);
|
||||
--nv-space-gap-middle: var(--nv-spacing-middle);
|
||||
--nv-space-gap-large: var(--nv-spacing-large);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.4 Stack 弹性盒布局
|
||||
|
||||
#### Props 定义(AI-First)
|
||||
|
||||
```tsx
|
||||
export interface StackProps {
|
||||
/** 排列方向 */
|
||||
direction?: 'horizontal' | 'vertical'
|
||||
/** 间距 */
|
||||
gap?: 'small' | 'middle' | 'large' | number | string
|
||||
/** 主轴对齐方式 */
|
||||
justify?: 'start' | 'end' | 'center' | 'space-around' | 'space-between' | 'space-evenly'
|
||||
/** 交叉轴对齐方式 */
|
||||
align?: 'start' | 'end' | 'center' | 'baseline' | 'stretch'
|
||||
/** 是否换行 */
|
||||
wrap?: boolean
|
||||
/** 子元素 */
|
||||
children?: React.ReactNode
|
||||
className?: string
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
|
||||
// 便捷组件
|
||||
export interface HStackProps extends Omit<StackProps, 'direction'> {}
|
||||
export interface VStackProps extends Omit<StackProps, 'direction'> {}
|
||||
```
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```tsx
|
||||
// 使用 Stack
|
||||
<Stack direction="horizontal" gap="middle" justify="space-between">
|
||||
<Logo />
|
||||
<Nav />
|
||||
</Stack>
|
||||
|
||||
// 使用 HStack
|
||||
<HStack gap="middle" justify="space-between">
|
||||
<Logo />
|
||||
<Nav />
|
||||
</HStack>
|
||||
|
||||
// 使用 VStack
|
||||
<VStack gap="small" align="center">
|
||||
<Avatar />
|
||||
<Name />
|
||||
<Desc />
|
||||
</VStack>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.5 Divider 分隔线
|
||||
|
||||
#### Props 定义(AI-First)
|
||||
|
||||
```tsx
|
||||
export interface DividerProps {
|
||||
/** 方向 */
|
||||
type?: 'horizontal' | 'vertical'
|
||||
/** 是否虚线 */
|
||||
dashed?: boolean
|
||||
/** 子元素(居中文字) */
|
||||
children?: React.ReactNode
|
||||
className?: string
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```tsx
|
||||
// 水平分隔
|
||||
<Divider />
|
||||
|
||||
// 带文字
|
||||
<Divider>内容</Divider>
|
||||
|
||||
// 垂直分隔
|
||||
<Space split={<Divider type="vertical" />}>
|
||||
<Text>Link 1</Text>
|
||||
<Text>Link 2</Text>
|
||||
</Space>
|
||||
|
||||
// 虚线
|
||||
<Divider dashed />
|
||||
```
|
||||
|
||||
#### Design Token
|
||||
|
||||
```css
|
||||
--nv-divider-color: var(--nv-color-border);
|
||||
--nv-divider-padding: var(--nv-spacing-middle);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、AI-First 设计规范
|
||||
|
||||
### 5.1 Props 命名规范
|
||||
|
||||
| 规则 | 说明 |
|
||||
|-----|------|
|
||||
| **布尔属性** | 动词/形容词 + 肯定形式:`disabled`, `collapsible`, `fixed`, `wrap`, `dashed` |
|
||||
| **事件回调** | `on + 动词 + 名词`:`onCollapse`, `onBreakpoint` |
|
||||
| **尺寸** | 统一使用 `small`, `middle`, `large` |
|
||||
| **响应式断点** | 统一使用 `small`, `middle`, `large` |
|
||||
|
||||
### 5.2 样式绑定规范
|
||||
|
||||
使用 `data-*` 属性进行样式绑定:
|
||||
|
||||
```tsx
|
||||
<div
|
||||
data-direction="horizontal"
|
||||
data-size="middle"
|
||||
data-wrap="true"
|
||||
/>
|
||||
```
|
||||
|
||||
### 5.3 组件导入规范
|
||||
|
||||
每个组件独立导入,路径即名字:
|
||||
|
||||
```tsx
|
||||
import { Layout } from '@Pika/ui/layout'
|
||||
import { Header, Footer, Sider, Content } from '@Pika/ui/layout'
|
||||
import { Row, Col } from '@Pika/ui/grid'
|
||||
import { Space } from '@Pika/ui/space'
|
||||
import { Stack, HStack, VStack } from '@Pika/ui/stack'
|
||||
import { Divider } from '@Pika/ui/divider'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、完整使用示例
|
||||
|
||||
### 6.1 经典后台布局
|
||||
|
||||
```tsx
|
||||
import { Layout, Row, Col, Space, HStack, VStack, Divider } from '@Pika/ui'
|
||||
|
||||
const App = () => {
|
||||
const [collapsed, setCollapsed] = useState(false)
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<Layout.Sider
|
||||
width={200}
|
||||
collapsedWidth={80}
|
||||
collapsible
|
||||
collapsed={collapsed}
|
||||
onCollapse={setCollapsed}
|
||||
breakpoint="middle"
|
||||
>
|
||||
<Menu />
|
||||
</Layout.Sider>
|
||||
|
||||
<Layout>
|
||||
<Layout.Header fixed>
|
||||
<HStack justify="space-between">
|
||||
<Logo />
|
||||
<UserMenu />
|
||||
</HStack>
|
||||
</Layout.Header>
|
||||
|
||||
<Layout.Content padding={24}>
|
||||
<VStack gap="middle">
|
||||
<PageTitle />
|
||||
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col
|
||||
small={24}
|
||||
middle={12}
|
||||
large={6}
|
||||
>
|
||||
<Card />
|
||||
</Col>
|
||||
<Col
|
||||
small={24}
|
||||
middle={12}
|
||||
large={6}
|
||||
>
|
||||
<Card />
|
||||
</Col>
|
||||
<Col
|
||||
small={24}
|
||||
middle={12}
|
||||
large={6}
|
||||
>
|
||||
<Card />
|
||||
</Col>
|
||||
<Col
|
||||
small={24}
|
||||
middle={12}
|
||||
large={6}
|
||||
>
|
||||
<Card />
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Space size="middle">
|
||||
<Button>保存</Button>
|
||||
<Button variant="outline">取消</Button>
|
||||
</Space>
|
||||
</VStack>
|
||||
</Layout.Content>
|
||||
|
||||
<Layout.Footer>
|
||||
© 2024 Pika UI
|
||||
</Layout.Footer>
|
||||
</Layout>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、文件结构
|
||||
|
||||
```
|
||||
src/components/
|
||||
├── layout/
|
||||
│ ├── index.ts
|
||||
│ ├── Layout.tsx
|
||||
│ ├── Header.tsx
|
||||
│ ├── Footer.tsx
|
||||
│ ├── Sider.tsx
|
||||
│ ├── Content.tsx
|
||||
│ ├── Layout.module.css
|
||||
│ └── Layout.test.tsx
|
||||
├── grid/
|
||||
│ ├── index.ts
|
||||
│ ├── Row.tsx
|
||||
│ ├── Col.tsx
|
||||
│ ├── Grid.module.css
|
||||
│ └── Grid.test.tsx
|
||||
├── space/
|
||||
│ ├── index.ts
|
||||
│ ├── Space.tsx
|
||||
│ ├── Space.module.css
|
||||
│ └── Space.test.tsx
|
||||
├── stack/
|
||||
│ ├── index.ts
|
||||
│ ├── Stack.tsx
|
||||
│ ├── HStack.tsx
|
||||
│ ├── VStack.tsx
|
||||
│ ├── Stack.module.css
|
||||
│ └── Stack.test.tsx
|
||||
└── divider/
|
||||
├── index.ts
|
||||
├── Divider.tsx
|
||||
├── Divider.module.css
|
||||
└── Divider.test.tsx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 八、优先级规划
|
||||
|
||||
### Phase 1: 核心布局 (P0)
|
||||
|
||||
| 组件 | 说明 | 预估工时 |
|
||||
|-----|------|---------|
|
||||
| Layout | 布局容器 | 1h |
|
||||
| Header | 顶部导航栏 | 0.5h |
|
||||
| Footer | 底部区域 | 0.5h |
|
||||
| Content | 内容区域 | 0.5h |
|
||||
| Sider | 侧边栏 | 2h |
|
||||
|
||||
### Phase 2: 栅格系统 (P1)
|
||||
|
||||
| 组件 | 说明 | 预估工时 |
|
||||
|-----|------|---------|
|
||||
| Row | 行容器 | 1h |
|
||||
| Col | 列组件 | 1.5h |
|
||||
|
||||
### Phase 3: 间距和弹性盒 (P2)
|
||||
|
||||
| 组件 | 说明 | 预估工时 |
|
||||
|-----|------|---------|
|
||||
| Space | 间距组件 | 1h |
|
||||
| Stack | 弹性盒容器 | 1h |
|
||||
| HStack | 水平排列 | 0.25h |
|
||||
| VStack | 垂直排列 | 0.25h |
|
||||
| Divider | 分隔线 | 0.5h |
|
||||
|
||||
---
|
||||
|
||||
## 九、与根设计规范的对照
|
||||
|
||||
| 根规范 | 实现方式 |
|
||||
|------|---------|
|
||||
| **三级别尺寸** | `small`, `middle`, `large` 统一应用于所有组件 |
|
||||
| **AI-First** | 所有 Props 使用联合类型字面量,无魔法字符串 |
|
||||
| **Apple HIG** | 圆角、间距、动效遵循 Apple 规范 |
|
||||
| **data-* 样式绑定** | 使用 `data-*` 属性而非 className 拼接 |
|
||||
| **自包含组件** | 每个组件独立导入,路径即名字 |
|
||||
| **统一事件命名** | `on + 动词 + 名词` 规则 |
|
||||
|
||||
---
|
||||
|
||||
## 十、设计总结
|
||||
|
||||
### Pika 布局组件的特点
|
||||
|
||||
1. **简洁统一** - 三级别尺寸体系,无过度设计
|
||||
2. **AI 友好** - Props 自解释,类型安全,确定导入路径
|
||||
3. **Apple HIG 优先** - 遵循 Apple 人机交互指南
|
||||
4. **语义化** - Space, HStack, VStack 提供清晰的布局语义
|
||||
5. **可扩展** - 支持响应式、折叠等高级功能
|
||||
|
||||
### AI 提示工程
|
||||
|
||||
```
|
||||
1. 导入路径 = @Pika/ui/组件名 (layout/grid/space/stack/divider)
|
||||
2. 尺寸 prop 始终叫 size,值从 small/middle/large 选
|
||||
3. 响应式断点统一:small/middle/large
|
||||
4. 布尔 prop 用动词开头 (collapsible, fixed, wrap, dashed)
|
||||
5. 回调命名 = on + 动词 + 名词 (onCollapse, onBreakpoint)
|
||||
6. 所有 prop 类型用联合类型字面量定义
|
||||
7. 使用 Space 代替手动计算间距
|
||||
8. 使用 HStack/VStack 进行弹性盒布局
|
||||
```
|
||||
|
||||
> **Pika 布局组件:简洁、统一、AI-First**
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,901 @@
|
||||
# Pika 其他组件开发计划与规范
|
||||
|
||||
> 基于 Pika 设计规范,参照 Ant Design 剩余组件体系,构建专为 AI 代码生成优化的其他组件库。
|
||||
|
||||
---
|
||||
|
||||
## 一、组件总览
|
||||
|
||||
Pika 其他组件分为以下几类:
|
||||
|
||||
| 类别 | 组件 | antd 对标 | 优先级 |
|
||||
|------|------|-----------|--------|
|
||||
| **通用组件** | Affix, Anchor, App, ConfigProvider, BackTop, Watermark | Ant Design 通用组件 | P0-P1 |
|
||||
| **容器组件** | Drawer | Ant Design 抽屉 | P1 |
|
||||
| **数据展示** | List | Ant Design 列表 | P1 |
|
||||
|
||||
---
|
||||
|
||||
## 二、核心设计原则(与根规范一致)
|
||||
|
||||
### 2.1 AI 优先设计原则
|
||||
|
||||
| 原则 | 说明 |
|
||||
|------|------|
|
||||
| **零歧义** | Props 名称自解释,无简称、无隐式行为 |
|
||||
| **零默认假设** | 所有视觉行为显式声明 |
|
||||
| **一致性** | 相同概念用相同 prop 名 |
|
||||
| **扁平化** | 最多一层嵌套 |
|
||||
| **确定性** | 相同输入始终产生相同输出 |
|
||||
|
||||
### 2.2 统一尺寸体系
|
||||
|
||||
所有组件使用统一的三级尺寸体系:
|
||||
|
||||
| 级别 | 说明 | 常用值 |
|
||||
|------|------|-------|
|
||||
| **small** | 小尺寸 | 紧凑列表、表格内 |
|
||||
| **middle** | 中尺寸(默认) | 默认使用 |
|
||||
| **large** | 大尺寸 | 突出显示、重点元素 |
|
||||
|
||||
---
|
||||
|
||||
## 三、通用组件
|
||||
|
||||
### 3.1 Affix 固钉
|
||||
|
||||
#### 组件定位
|
||||
将页面元素固定在可视区域内,常用于导航栏、侧边栏、回到顶部按钮等。
|
||||
|
||||
#### Props 定义
|
||||
|
||||
```tsx
|
||||
export interface AffixProps {
|
||||
/** 距离窗口顶部达到指定偏移量后触发 */
|
||||
offsetTop?: number;
|
||||
/** 距离窗口底部达到指定偏移量后触发 */
|
||||
offsetBottom?: number;
|
||||
/** 指定 Affix 挂载的 HTML 节点 */
|
||||
target?: () => HTMLElement | null;
|
||||
/** 监听滚动事件的容器 */
|
||||
getContainer?: () => HTMLElement | Window;
|
||||
/** 固定状态改变时触发的回调 */
|
||||
onChange?: (isAffixed: boolean) => void;
|
||||
/** 子元素 */
|
||||
children?: ReactNode;
|
||||
/** 样式类名 */
|
||||
className?: string;
|
||||
/** 自定义样式 */
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```tsx
|
||||
import { Affix, Button } from '@Pika/ui/affix';
|
||||
|
||||
// 基本使用
|
||||
<Affix offsetTop={10}>
|
||||
<Button variant="solid">固钉在顶部</Button>
|
||||
</Affix>;
|
||||
|
||||
// 固钉在底部
|
||||
<Affix offsetBottom={10}>
|
||||
<Button variant="solid">固钉在底部</Button>
|
||||
</Affix>;
|
||||
|
||||
// 自定义容器
|
||||
<Affix target={() => document.getElementById('scroll-container')} offsetTop={10}>
|
||||
<Button variant="solid">在容器内固钉</Button>
|
||||
</Affix>;
|
||||
```
|
||||
|
||||
#### Design Token
|
||||
|
||||
```css
|
||||
--nv-affix-z-index: 10;
|
||||
```
|
||||
|
||||
#### 开发优先级:P1
|
||||
|
||||
---
|
||||
|
||||
### 3.2 Anchor 锚点
|
||||
|
||||
#### 组件定位
|
||||
用于跳转到页面指定位置,或显示当前滚动位置。
|
||||
|
||||
#### Props 定义
|
||||
|
||||
```tsx
|
||||
export interface AnchorProps {
|
||||
/** 锚点方向 */
|
||||
direction?: 'vertical' | 'horizontal';
|
||||
/** 当前激活的锚点链接 */
|
||||
activeLink?: string;
|
||||
/** 锚点滚动偏移量 */
|
||||
targetOffset?: number;
|
||||
/** 点击锚点链接的回调 */
|
||||
onClick?: (
|
||||
e: React.MouseEvent<HTMLElement>,
|
||||
link: { href: string; title: ReactNode }
|
||||
) => void;
|
||||
/** 锚点链接改变时的回调 */
|
||||
onChange?: (currentActiveLink: string) => void;
|
||||
/** 获取锚点容器 */
|
||||
getContainer?: () => HTMLElement | Window;
|
||||
/** 是否改变 hash */
|
||||
hash?: boolean;
|
||||
/** 子元素 */
|
||||
children?: ReactNode;
|
||||
/** 样式类名 */
|
||||
className?: string;
|
||||
/** 自定义样式 */
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export interface AnchorLinkProps {
|
||||
/** 锚点链接 */
|
||||
href: string;
|
||||
/** 锚点文本 */
|
||||
title: ReactNode;
|
||||
/** 子元素 */
|
||||
children?: ReactNode;
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```tsx
|
||||
import { Anchor } from '@Pika/ui/anchor';
|
||||
|
||||
// 基本使用
|
||||
<Anchor>
|
||||
<Anchor.Link href="#section-1" title="第一章" />
|
||||
<Anchor.Link href="#section-2" title="第二章">
|
||||
<Anchor.Link href="#section-2-1" title="2.1 小节" />
|
||||
<Anchor.Link href="#section-2-2" title="2.2 小节" />
|
||||
</Anchor.Link>
|
||||
<Anchor.Link href="#section-3" title="第三章" />
|
||||
</Anchor>;
|
||||
```
|
||||
|
||||
#### Design Token
|
||||
|
||||
```css
|
||||
--nv-anchor-link-height: 32px;
|
||||
--nv-anchor-link-color: var(--nv-text-color-secondary);
|
||||
--nv-anchor-link-active-color: var(--nv-color-primary);
|
||||
--nv-anchor-link-active-width: 2px;
|
||||
--nv-anchor-wrapper-padding: 4px 0;
|
||||
```
|
||||
|
||||
#### 开发优先级:P1
|
||||
|
||||
---
|
||||
|
||||
### 3.3 App 包裹组件
|
||||
|
||||
#### 组件定位
|
||||
提供全局上下文,用于 Message、Modal、Notification 等组件的静态方法调用。
|
||||
|
||||
#### Props 定义
|
||||
|
||||
```tsx
|
||||
export interface AppProps {
|
||||
/** 子元素 */
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
export interface AppContextProps {
|
||||
message: MessageStatic;
|
||||
modal: ModalStatic;
|
||||
notification: NotificationStatic;
|
||||
}
|
||||
|
||||
export declare function useApp(): AppContextProps;
|
||||
```
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```tsx
|
||||
import { App, Button } from '@Pika/ui/app';
|
||||
|
||||
// 基本使用
|
||||
function AppDemo() {
|
||||
const { message, modal, notification } = App.useApp();
|
||||
|
||||
const showMessage = () => {
|
||||
message.success('操作成功');
|
||||
};
|
||||
|
||||
const showModal = () => {
|
||||
modal.info({ title: '提示', content: '这是一条提示信息' });
|
||||
};
|
||||
|
||||
return (
|
||||
<App>
|
||||
<Button onClick={showMessage}>显示消息</Button>
|
||||
<Button onClick={showModal}>显示对话框</Button>
|
||||
</App>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### 开发优先级:P0
|
||||
|
||||
---
|
||||
|
||||
### 3.4 ConfigProvider 全局配置
|
||||
|
||||
#### 组件定位
|
||||
为组件提供统一的全局配置,包括主题、语言、国际化等。
|
||||
|
||||
#### Props 定义
|
||||
|
||||
```tsx
|
||||
export interface ConfigProviderProps {
|
||||
/** 主题配置 */
|
||||
theme?: {
|
||||
token?: Record<string, any>;
|
||||
components?: Record<string, any>;
|
||||
algorithm?: 'default' | 'dark' | 'compact' | Array<'default' | 'dark' | 'compact'>;
|
||||
};
|
||||
/** 语言包 */
|
||||
locale?: Record<string, any>;
|
||||
/** 全局 zIndex */
|
||||
zIndex?: number;
|
||||
/** 设置主题色 */
|
||||
colorPrimary?: string;
|
||||
/** 设置图标前缀 */
|
||||
iconPrefixCls?: string;
|
||||
/** 设置组件类名前缀 */
|
||||
prefixCls?: string;
|
||||
/** 设置弹框的默认渲染容器 */
|
||||
getPopupContainer?: (node: HTMLElement) => HTMLElement;
|
||||
/** 子元素 */
|
||||
children?: ReactNode;
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```tsx
|
||||
import { ConfigProvider, Button } from '@Pika/ui/config-provider';
|
||||
|
||||
// 基本使用
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
token: {
|
||||
colorPrimary: '#1890ff',
|
||||
borderRadius: 8,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<App />
|
||||
</ConfigProvider>;
|
||||
```
|
||||
|
||||
#### Design Token
|
||||
|
||||
与根设计规范中的 Token 一致。
|
||||
|
||||
#### 开发优先级:P0
|
||||
|
||||
---
|
||||
|
||||
### 3.5 BackTop 回到顶部
|
||||
|
||||
#### 组件定位
|
||||
提供快速回到页面顶部的功能。
|
||||
|
||||
#### Props 定义
|
||||
|
||||
```tsx
|
||||
export interface BackTopProps {
|
||||
/** 距离顶部多少像素时显示 */
|
||||
visibilityHeight?: number;
|
||||
/** 点击回调 */
|
||||
onClick?: () => void;
|
||||
/** 监听滚动的容器 */
|
||||
target?: () => HTMLElement | Window;
|
||||
/** 子元素 */
|
||||
children?: ReactNode;
|
||||
/** 样式类名 */
|
||||
className?: string;
|
||||
/** 自定义样式 */
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```tsx
|
||||
import { BackTop } from '@Pika/ui/back-top';
|
||||
|
||||
// 基本使用
|
||||
<BackTop />;
|
||||
|
||||
// 自定义图标
|
||||
<BackTop>
|
||||
<div style={{
|
||||
height: 40,
|
||||
width: 40,
|
||||
lineHeight: '40px',
|
||||
borderRadius: 4,
|
||||
backgroundColor: '#1088e9',
|
||||
color: '#fff',
|
||||
textAlign: 'center',
|
||||
fontSize: 20,
|
||||
}}>
|
||||
UP
|
||||
</div>
|
||||
</BackTop>;
|
||||
```
|
||||
|
||||
#### Design Token
|
||||
|
||||
```css
|
||||
--nv-back-top-size: 40px;
|
||||
--nv-back-top-z-index: 10;
|
||||
--nv-back-top-color: var(--nv-color-primary);
|
||||
--nv-back-top-hover-color: var(--nv-color-primary-hover);
|
||||
```
|
||||
|
||||
#### 开发优先级:P1
|
||||
|
||||
---
|
||||
|
||||
### 3.6 Watermark 水印
|
||||
|
||||
#### 组件定位
|
||||
为页面或元素添加水印,防止截图泄露或证明来源。(注意:在 FEEDBACK_COMPONENTS.md 中已有定义,这里补充完整)
|
||||
|
||||
#### Props 定义
|
||||
|
||||
```tsx
|
||||
export interface WatermarkProps {
|
||||
/** 水印文字 */
|
||||
content?: string | string[];
|
||||
/** 自定义图片水印 */
|
||||
image?: string;
|
||||
/** 水印宽度(文字模式时为单个文字宽度) */
|
||||
width?: number;
|
||||
/** 水印高度(文字模式时为单个文字高度) */
|
||||
height?: number;
|
||||
/** 旋转角度 */
|
||||
rotate?: number;
|
||||
/** 水印之间的水平间距 */
|
||||
gap?: [number, number];
|
||||
/** 水印在 canvas 上绘制时的偏移量 */
|
||||
offset?: [number, number];
|
||||
/** 文字样式 */
|
||||
font?: {
|
||||
color?: string;
|
||||
fontSize?: number;
|
||||
fontFamily?: string;
|
||||
fontWeight?: 'normal' | 'light' | 'weight' | number;
|
||||
};
|
||||
/** 水印层级 */
|
||||
zIndex?: number;
|
||||
/** 子元素 */
|
||||
children?: ReactNode;
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```tsx
|
||||
import { Watermark, Card } from '@Pika/ui/watermark';
|
||||
|
||||
// 基本使用
|
||||
<Watermark content="Pika UI">
|
||||
<Card style={{ width: 600, height: 400 }}>
|
||||
<p>这是一些内容,会有水印覆盖</p>
|
||||
</Card>
|
||||
</Watermark>;
|
||||
|
||||
// 多行水印
|
||||
<Watermark content={['Pika UI', '内部文档']}>
|
||||
<div>内容</div>
|
||||
</Watermark>;
|
||||
|
||||
// 自定义样式
|
||||
<Watermark
|
||||
content="Pika UI"
|
||||
rotate={-25}
|
||||
font={{
|
||||
color: 'rgba(0, 0, 0, 0.08)',
|
||||
fontSize: 16,
|
||||
}}
|
||||
>
|
||||
<div>内容</div>
|
||||
</Watermark>;
|
||||
```
|
||||
|
||||
#### Design Token
|
||||
|
||||
```css
|
||||
--nv-watermark-font-size: 14px;
|
||||
--nv-watermark-font-color: rgba(0, 0, 0, 0.08);
|
||||
--nv-watermark-rotate: -22deg;
|
||||
--nv-watermark-gap: [100, 100];
|
||||
--nv-watermark-z-index: 9;
|
||||
```
|
||||
|
||||
#### 开发优先级:P3
|
||||
|
||||
---
|
||||
|
||||
## 四、容器组件
|
||||
|
||||
### 4.1 Drawer 抽屉
|
||||
|
||||
#### 组件定位
|
||||
抽屉组件,用于从屏幕边缘滑出内容面板,常用于详情页、表单页、筛选页等。
|
||||
|
||||
#### Props 定义
|
||||
|
||||
```tsx
|
||||
export type DrawerPlacement = 'top' | 'right' | 'bottom' | 'left';
|
||||
export type DrawerSize = 'small' | 'middle' | 'large' | number | string;
|
||||
|
||||
export interface DrawerProps {
|
||||
/** 抽屉是否可见 */
|
||||
open: boolean;
|
||||
/** 抽屉标题 */
|
||||
title?: ReactNode;
|
||||
/** 抽屉宽度(placement 为 left/right 时生效) */
|
||||
width?: number | string;
|
||||
/** 抽屉高度(placement 为 top/bottom 时生效) */
|
||||
height?: number | string;
|
||||
/** 抽屉尺寸预设 */
|
||||
size?: DrawerSize;
|
||||
/** 抽屉放置位置 */
|
||||
placement?: DrawerPlacement;
|
||||
/** 是否显示遮罩 */
|
||||
mask?: boolean;
|
||||
/** 点击遮罩是否关闭 */
|
||||
maskClosable?: boolean;
|
||||
/** 是否显示右上角关闭按钮 */
|
||||
closable?: boolean;
|
||||
/** 自定义关闭图标 */
|
||||
closeIcon?: ReactNode;
|
||||
/** 点击关闭图标的回调 */
|
||||
onClose?: () => void;
|
||||
/** 抽屉打开后的回调 */
|
||||
afterOpenChange?: (open: boolean) => void;
|
||||
/** 抽屉的容器 */
|
||||
getContainer?: HTMLElement | (() => HTMLElement) | null;
|
||||
/** 预渲染,防止抖动 */
|
||||
forceRender?: boolean;
|
||||
/** 关闭时销毁 Drawer 里的子元素 */
|
||||
destroyOnClose?: boolean;
|
||||
/** 抽屉的样式 */
|
||||
style?: React.CSSProperties;
|
||||
/** 抽屉标题的样式 */
|
||||
headerStyle?: React.CSSProperties;
|
||||
/** 抽屉内容的样式 */
|
||||
bodyStyle?: React.CSSProperties;
|
||||
/** 自定义遮罩样式 */
|
||||
maskStyle?: React.CSSProperties;
|
||||
/** 抽屉的类名 */
|
||||
className?: string;
|
||||
/** 抽屉根元素的类名 */
|
||||
rootClassName?: string;
|
||||
/** 是否支持键盘 esc 关闭 */
|
||||
keyboard?: boolean;
|
||||
/** 子元素 */
|
||||
children?: ReactNode;
|
||||
/** 抽屉页脚 */
|
||||
footer?: ReactNode;
|
||||
/** 页脚的样式 */
|
||||
footerStyle?: React.CSSProperties;
|
||||
/** 是否显示页脚 */
|
||||
footer?: ReactNode | null;
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```tsx
|
||||
import { Drawer, Button, Form, Input } from '@Pika/ui/drawer';
|
||||
|
||||
function DrawerDemo() {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={() => setOpen(true)}>打开抽屉</Button>
|
||||
<Drawer
|
||||
title="基本抽屉"
|
||||
width={480}
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
>
|
||||
<p>这是抽屉的内容</p>
|
||||
<p>可以放置任何内容</p>
|
||||
</Drawer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// 表单抽屉
|
||||
function FormDrawer() {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={() => setOpen(true)}>新建</Button>
|
||||
<Drawer
|
||||
title="新建"
|
||||
width={480}
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
footer={
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
<Button onClick={() => setOpen(false)} style={{ marginRight: 8 }}>
|
||||
取消
|
||||
</Button>
|
||||
<Button variant="solid" onClick={() => setOpen(false)}>
|
||||
确定
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Form layout="vertical">
|
||||
<Form.Item label="名称" name="name">
|
||||
<Input placeholder="请输入名称" />
|
||||
</Form.Item>
|
||||
<Form.Item label="描述" name="description">
|
||||
<TextArea placeholder="请输入描述" rows={4} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Drawer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### Design Token
|
||||
|
||||
```css
|
||||
--nv-drawer-z-index: 1000;
|
||||
--nv-drawer-header-height: 56px;
|
||||
--nv-drawer-header-padding: 16px 24px;
|
||||
--nv-drawer-body-padding: 24px;
|
||||
--nv-drawer-footer-padding: 16px 24px;
|
||||
--nv-drawer-mask-bg: rgba(0, 0, 0, 0.45);
|
||||
--nv-drawer-bg: #fff;
|
||||
--nv-drawer-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
--nv-drawer-width-small: 378px;
|
||||
--nv-drawer-width-middle: 480px;
|
||||
--nv-drawer-width-large: 736px;
|
||||
```
|
||||
|
||||
#### 开发优先级:P1
|
||||
|
||||
---
|
||||
|
||||
## 五、数据展示组件
|
||||
|
||||
### 5.1 List 列表
|
||||
|
||||
#### 组件定位
|
||||
通用列表组件,用于展示一系列同类数据。
|
||||
|
||||
#### Props 定义
|
||||
|
||||
```tsx
|
||||
export interface ListProps<T> {
|
||||
/** 列表数据 */
|
||||
data?: T[];
|
||||
/** 列表尺寸 */
|
||||
size?: 'small' | 'middle' | 'large';
|
||||
/** 列表边框 */
|
||||
bordered?: boolean;
|
||||
/** 是否显示分割线 */
|
||||
split?: boolean;
|
||||
/** 列表项头部 */
|
||||
header?: ReactNode;
|
||||
/** 列表项底部 */
|
||||
footer?: ReactNode;
|
||||
/** 列表加载状态 */
|
||||
loading?: boolean;
|
||||
/** 空列表时显示的内容 */
|
||||
empty?: ReactNode;
|
||||
/** 渲染列表项 */
|
||||
renderItem?: (item: T, index: number) => ReactNode;
|
||||
/** 列表项布局 */
|
||||
itemLayout?: 'horizontal' | 'vertical';
|
||||
/** 列表行高 */
|
||||
rowKey?: string | ((item: T) => string);
|
||||
/** 是否显示网格 */
|
||||
grid?: {
|
||||
gutter?: number;
|
||||
column?: number;
|
||||
xs?: number;
|
||||
sm?: number;
|
||||
md?: number;
|
||||
lg?: number;
|
||||
xl?: number;
|
||||
xxl?: number;
|
||||
};
|
||||
/** 子元素 */
|
||||
children?: ReactNode;
|
||||
/** 样式类名 */
|
||||
className?: string;
|
||||
/** 自定义样式 */
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export interface ListItemProps {
|
||||
/** 列表项内容 */
|
||||
children?: ReactNode;
|
||||
/** 列表项的额外内容 */
|
||||
extra?: ReactNode;
|
||||
/** 列表项的类名 */
|
||||
className?: string;
|
||||
/** 列表项的样式 */
|
||||
style?: React.CSSProperties;
|
||||
/** 列表项的 actions */
|
||||
actions?: ReactNode[];
|
||||
}
|
||||
|
||||
export interface ListItemMetaProps {
|
||||
/** 头像 */
|
||||
avatar?: ReactNode;
|
||||
/** 标题 */
|
||||
title?: ReactNode;
|
||||
/** 描述 */
|
||||
description?: ReactNode;
|
||||
/** 类名 */
|
||||
className?: string;
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```tsx
|
||||
import { List, Avatar, Button } from '@Pika/ui/list';
|
||||
|
||||
// 基本使用
|
||||
const data = [
|
||||
{ name: '张三', age: 25, avatar: 'https://example.com/avatar1.jpg' },
|
||||
{ name: '李四', age: 30, avatar: 'https://example.com/avatar2.jpg' },
|
||||
{ name: '王五', age: 28, avatar: 'https://example.com/avatar3.jpg' },
|
||||
];
|
||||
|
||||
<List
|
||||
data={data}
|
||||
renderItem={(item) => (
|
||||
<List.Item>
|
||||
<List.Item.Meta
|
||||
avatar={<Avatar src={item.avatar} />}
|
||||
title={<a href="#">{item.name}</a>}
|
||||
description={`年龄: ${item.age}岁`}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
/>;
|
||||
|
||||
// 网格列表
|
||||
<List
|
||||
grid={{ gutter: 16, column: 4 }}
|
||||
data={data}
|
||||
renderItem={(item) => (
|
||||
<List.Item>
|
||||
<Card title={item.name}>
|
||||
<p>{item.age}岁</p>
|
||||
</Card>
|
||||
</List.Item>
|
||||
)}
|
||||
/>;
|
||||
```
|
||||
|
||||
#### Design Token
|
||||
|
||||
```css
|
||||
--nv-list-item-height-small: 32px;
|
||||
--nv-list-item-height-middle: 40px;
|
||||
--nv-list-item-height-large: 48px;
|
||||
--nv-list-item-padding-small: 8px 12px;
|
||||
--nv-list-item-padding-middle: 12px 16px;
|
||||
--nv-list-item-padding-large: 16px 24px;
|
||||
--nv-list-border-color: var(--nv-color-border);
|
||||
--nv-list-split-color: var(--nv-color-border);
|
||||
```
|
||||
|
||||
#### 开发优先级:P1
|
||||
|
||||
---
|
||||
|
||||
## 六、开发计划与优先级
|
||||
|
||||
### 6.1 优先级规划
|
||||
|
||||
#### Phase 1: 核心通用组件(P0)
|
||||
|
||||
| 组件 | 说明 | 预估工时 |
|
||||
|------|------|---------|
|
||||
| App | 包裹组件,提供全局上下文 | 0.5 天 |
|
||||
| ConfigProvider | 全局配置组件 | 1 天 |
|
||||
|
||||
#### Phase 2: 重要通用组件(P1)
|
||||
|
||||
| 组件 | 说明 | 预估工时 |
|
||||
|------|------|---------|
|
||||
| Affix | 固钉组件 | 0.5 天 |
|
||||
| Anchor | 锚点组件 | 0.5 天 |
|
||||
| BackTop | 回到顶部 | 0.5 天 |
|
||||
| Drawer | 抽屉组件 | 1.5 天 |
|
||||
| List | 列表组件 | 1.5 天 |
|
||||
|
||||
#### Phase 3: 高级组件(P3)
|
||||
|
||||
| 组件 | 说明 | 预估工时 |
|
||||
|------|------|---------|
|
||||
| Watermark | 水印组件 | 0.5 天 |
|
||||
|
||||
### 6.2 开发里程碑
|
||||
|
||||
| 阶段 | 任务 | 时间 |
|
||||
|------|------|------|
|
||||
| Phase 1 | App 和 ConfigProvider | 1 周 |
|
||||
| Phase 2 | Affix、Anchor、BackTop、Drawer、List | 2 周 |
|
||||
| Phase 3 | Watermark | 0.5 周 |
|
||||
|
||||
---
|
||||
|
||||
## 七、通用设计规范
|
||||
|
||||
### 7.1 Props 命名规范
|
||||
|
||||
```tsx
|
||||
// ✅ Pika 方式
|
||||
interface ComponentProps {
|
||||
// 尺寸统一
|
||||
size?: 'small' | 'middle' | 'large';
|
||||
|
||||
// 状态统一
|
||||
status?: 'default' | 'success' | 'warning' | 'error' | 'info';
|
||||
|
||||
// 数据统一
|
||||
data?: T[];
|
||||
|
||||
// 布尔属性:动词/形容词 + 肯定形式
|
||||
showIcon?: boolean;
|
||||
showClose?: boolean;
|
||||
closable?: boolean;
|
||||
disabled?: boolean;
|
||||
loading?: boolean;
|
||||
|
||||
// 事件:on + 动词 + 名词
|
||||
onChange?: (value: any) => void;
|
||||
onClick?: (event: MouseEvent) => void;
|
||||
onClose?: () => void;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
}
|
||||
```
|
||||
|
||||
### 7.2 样式绑定规范
|
||||
|
||||
```tsx
|
||||
// ✅ Pika 方式:使用 data-* 属性
|
||||
<div
|
||||
data-size={size}
|
||||
data-status={status}
|
||||
data-variant={variant}
|
||||
data-loading={loading}
|
||||
/>
|
||||
|
||||
// CSS 中使用属性选择器
|
||||
.component[data-size="small"] {
|
||||
font-size: 12px;
|
||||
}
|
||||
```
|
||||
|
||||
### 7.3 类型定义规范
|
||||
|
||||
```tsx
|
||||
// ✅ 使用联合类型字面量
|
||||
type Size = 'small' | 'middle' | 'large';
|
||||
type Status = 'default' | 'success' | 'warning' | 'error' | 'info';
|
||||
|
||||
// ❌ 避免使用 string 或 any
|
||||
type Size = string; // 范围不明确
|
||||
type Status = any; // 没有类型提示
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 八、无障碍设计
|
||||
|
||||
### 8.1 ARIA 属性
|
||||
|
||||
```tsx
|
||||
// Drawer
|
||||
<div
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="drawer-title"
|
||||
aria-describedby="drawer-content"
|
||||
>
|
||||
<h2 id="drawer-title">标题</h2>
|
||||
<div id="drawer-content">内容</div>
|
||||
</div>
|
||||
|
||||
// List
|
||||
<ul role="list">
|
||||
<li role="listitem">列表项 1</li>
|
||||
<li role="listitem">列表项 2</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
### 8.2 键盘操作
|
||||
|
||||
- Drawer 支持 Esc 关闭
|
||||
- 所有可点击元素支持 Enter/Space 触发
|
||||
- Tab 顺序合理
|
||||
|
||||
### 8.3 颜色对比度
|
||||
|
||||
- 文本与背景对比度 ≥ 4.5:1
|
||||
- 大文本对比度 ≥ 3:1
|
||||
- 图标与背景对比度 ≥ 3:1
|
||||
|
||||
---
|
||||
|
||||
## 九、与 Pika 根规范的对照
|
||||
|
||||
| Pika 根规范 | 实现方式 |
|
||||
|------------|---------|
|
||||
| **AI 优先** | 所有 Props 使用联合类型字面量,无魔法字符串 |
|
||||
| **三级别尺寸** | `small`/`middle`/`large` 统一应用于所有组件 |
|
||||
| **统一事件命名** | `on + 动词 + 名词` 规则 |
|
||||
| **布尔属性** | 动词/形容词 + 肯定形式 |
|
||||
| **data-* 样式绑定** | 使用 `data-*` 属性而非 class 拼接 |
|
||||
| **自包含组件** | 每个组件独立导入,路径即名字 |
|
||||
| **Apple HIG** | 圆角、间距、动效遵循 Apple 规范 |
|
||||
|
||||
---
|
||||
|
||||
## 十、完整组件清单
|
||||
|
||||
### 10.1 已规划完成的组件
|
||||
|
||||
- ✅ **通用组件**: Button, FloatButton, Icon, Typography
|
||||
- ✅ **数据展示**: Avatar, Badge, Calendar, Card, Carousel, Collapse, Descriptions, Empty, Image, Popover, QRCode, Segmented, Statistic, Table, Tag, Timeline, Tooltip, Tour, Tree
|
||||
- ✅ **数据录入**: AutoComplete, Cascader, Checkbox, Radio, Switch, DatePicker, Form, Input, InputNumber, Mention, RangePicker, Rate, Select, Slider, Textarea, TimePicker, Transfer, TreeSelect, Upload
|
||||
- ✅ **布局组件**: Divider, Flex, Grid, Layout, Masonry, Space, Splitter, Stack
|
||||
- ✅ **导航组件**: Breadcrumb, Dropdown, Menu, Pagination, Steps, Tabs
|
||||
- ✅ **反馈组件**: Alert, Message, Notification, Modal, Popconfirm, Spin, Progress, Result, Skeleton, Watermark
|
||||
|
||||
### 10.2 本规范补充的组件
|
||||
|
||||
- 📋 **通用组件**: Affix, Anchor, App, ConfigProvider, BackTop
|
||||
- 📋 **容器组件**: Drawer
|
||||
- 📋 **数据展示**: List
|
||||
|
||||
---
|
||||
|
||||
## 十一、AI 生成提示
|
||||
|
||||
当 AI 生成 Pika 组件相关代码时,请遵循以下提示:
|
||||
|
||||
```
|
||||
1. 导入路径 = @Pika/ui/组件名
|
||||
2. 尺寸 prop 始终叫 size,值从 small/middle/large 选
|
||||
3. 数据 prop 始终叫 data
|
||||
4. 布尔 prop 用动词/形容词开头
|
||||
5. 所有 prop 类型用联合类型字面量定义
|
||||
6. 使用 data-* 属性进行样式绑定
|
||||
7. 事件回调 = on + 动词 + 名词
|
||||
8. 参考 Ant Design API 但按照 Pika 规范调整命名
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **Pika 其他组件计划完成!所有必要组件的规范已制定完毕。**
|
||||
|
||||
@@ -0,0 +1,697 @@
|
||||
# Pika 组件库结构优化分析
|
||||
|
||||
> 基于当前代码库(`src/components` 约 77 个组件目录、80 个组件 TSX、74 个测试文件)的静态分析。
|
||||
> 生成日期:2026-05-30
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
1. [现状概览](#1-现状概览)
|
||||
2. [目录与命名规范](#2-目录与命名规范)
|
||||
3. [重复代码与可抽取的公共层](#3-重复代码与可抽取的公共层)
|
||||
4. [ConfigProvider 与主题系统](#4-configprovider-与主题系统)
|
||||
5. [CSS 与 Design Token 策略](#5-css-与-design-token-策略)
|
||||
6. [类型系统](#6-类型系统)
|
||||
7. [Props 诚实性(接口 vs 实现)](#7-props-诚实性接口-vs-实现)
|
||||
8. [共享 Hooks 与工具](#8-共享-hooks-与工具)
|
||||
9. [测试覆盖](#9-测试覆盖)
|
||||
10. [文档与 DX](#10-文档与-dx)
|
||||
11. [构建与导出](#11-构建与导出)
|
||||
12. [优先级路线图](#12-优先级路线图)
|
||||
13. [附录:文件清单速查](#13-附录文件清单速查)
|
||||
|
||||
---
|
||||
|
||||
## 1. 现状概览
|
||||
|
||||
### 1.1 分层结构(已成型,方向正确)
|
||||
|
||||
```
|
||||
src/
|
||||
├── components/
|
||||
│ ├── common/ # 基础通用(Button、ConfigProvider、Icon…)
|
||||
│ ├── layout/ # 布局(Layout、Flex、Grid、Space…)
|
||||
│ ├── nav/ # 导航(Menu、Tabs、Breadcrumb…)
|
||||
│ ├── entry/ # 数据录入(Input、Select、Form…)
|
||||
│ ├── feedback/ # 反馈(Modal、Message、Alert…)
|
||||
│ ├── display/ # 数据展示(Table、Card、Tooltip…)
|
||||
│ └── shared/ # 内部共享(hooks、types、utils)
|
||||
├── theme/ # JS Token 定义
|
||||
└── global.css # CSS 变量(--nv-*)
|
||||
```
|
||||
|
||||
六大分类与 Ant Design 的组件分区思路一致,有利于 AI 生成代码时的语义检索。`shared/` 作为内部基础设施层也已建立。
|
||||
|
||||
### 1.2 做得好的地方
|
||||
|
||||
| 方面 | 说明 |
|
||||
|------|------|
|
||||
| **组件粒度** | 每个组件独立目录,含 `*.tsx` + `*.module.css` + `index.ts` + `*.test.tsx`,结构清晰 |
|
||||
| **Barrel 导出** | 分类 `index.ts` → 根 `components/index.ts` → `src/index.ts`,对外 API 集中 |
|
||||
| **测试基线** | 主组件几乎都有 Vitest + Testing Library 测试(74 个 test 文件) |
|
||||
| **forwardRef** | 布局/表单类组件普遍支持 ref 透传 |
|
||||
| **data-* 属性** | 组件用 `data-*` 标记状态,利于测试与 CSS 选择器 |
|
||||
| **共享 Hooks** | `useClickOutside`、`useScrollListener` 等已在 overlay 类组件中复用 |
|
||||
|
||||
### 1.3 主要问题摘要
|
||||
|
||||
| 类别 | 严重度 | 一句话描述 |
|
||||
|------|--------|-----------|
|
||||
| 遗留重复路径 | 🔴 高 | `src/components/Button/` 与 `common/Button/` 并存 |
|
||||
| ConfigProvider 未接线 | 🔴 高 | 实现了 context,但业务组件几乎不消费 |
|
||||
| Token 三套并行 | 🔴 高 | JS tokens / CSS `--nv-*` / 组件 `--_*` 混用 |
|
||||
| Overlay 重复实现 | 🟠 中 | Tooltip 与 Popover 各 ~360 行,逻辑高度相似 |
|
||||
| 类型别名泛滥 | 🟠 中 | `PikaSize` 几乎无人用,各组件自建 `*Size` |
|
||||
| Props 接口超前 | 🟠 中 | 多个 prop 在类型里声明但实现缺失 |
|
||||
| 文档空白 | 🟠 中 | 80 个组件仅 1 个 `index.md` |
|
||||
| 构建 subpath 可能无效 | 🟡 低 | `package.json exports "./*"` 与源码结构不匹配 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 目录与命名规范
|
||||
|
||||
### 2.1 文件夹命名两套并存
|
||||
|
||||
| 风格 | 示例 | 数量 |
|
||||
|------|------|------|
|
||||
| **PascalCase** | `common/Button/`、`common/ConfigProvider/` | ~10 个 |
|
||||
| **kebab-case** | `display/avatar/`、`entry/tree-select/` | ~67 个 |
|
||||
|
||||
**建议:** 统一为 **kebab-case 目录 + PascalCase 文件名**,与绝大多数组件一致:
|
||||
|
||||
```
|
||||
display/avatar/Avatar.tsx ✅ 推荐
|
||||
common/Button/Button.tsx ⚠️ 目录应改为 common/button/
|
||||
```
|
||||
|
||||
迁移成本:仅 `common/` 下 10 个目录,可一次性批量 rename + 更新 import。
|
||||
|
||||
### 2.2 导出模式不一致
|
||||
|
||||
**现状:**
|
||||
|
||||
```ts
|
||||
// 组件文件 — 普遍 default export
|
||||
export default Button
|
||||
|
||||
// barrel — named export
|
||||
export { Button } from './Button'
|
||||
```
|
||||
|
||||
**建议:** 组件 TSX 改为 **named export only**,与 barrel 和 tree-shaking 更一致:
|
||||
|
||||
```ts
|
||||
// Button.tsx
|
||||
export const Button = forwardRef(...)
|
||||
export type { ButtonProps }
|
||||
```
|
||||
|
||||
### 2.3 复合组件组织方式不统一
|
||||
|
||||
| 模式 | 示例 |
|
||||
|------|------|
|
||||
| 同目录多文件 | `display/avatar/Avatar.tsx` + `AvatarGroup.tsx` |
|
||||
| 单目录聚合 | `entry/choice/Checkbox.tsx` + `Radio.tsx` + `Switch.tsx` |
|
||||
| Object.assign | `Layout.Header`、`Card.Meta` |
|
||||
|
||||
**建议:** 制定简单规则写入 `CONVENTIONS.md`(当前已删除):
|
||||
|
||||
- 强关联子组件 → 同目录 + `Object.assign` 或 compound export
|
||||
- 独立可选组件 → 同目录分文件
|
||||
- 录入类「形态变体」→ 可聚合(如 choice)
|
||||
|
||||
### 2.4 遗留路径:Button 双份
|
||||
|
||||
| 路径 | 状态 |
|
||||
|------|------|
|
||||
| `src/components/common/Button/` | ✅ 现行源码,根 index 导出 |
|
||||
| `src/components/Button/` | ⚠️ Git 索引中仍存在,含 `index.md`、测试 |
|
||||
|
||||
**行动:** 删除 `src/components/Button/`,文档迁移至 `common/Button/index.md`(或 `docs/components/button.md`)。
|
||||
|
||||
---
|
||||
|
||||
## 3. 重复代码与可抽取的公共层
|
||||
|
||||
### 3.1 Tooltip / Popover — 最高优先级抽取
|
||||
|
||||
两者共享:
|
||||
|
||||
- `GAP`、`PLACEMENT_POSITION_MAP`、`FLIP_MAP` 常量
|
||||
- Portal 渲染 + 定位计算
|
||||
- `useScrollListener` + `useClickOutside`
|
||||
- 显隐状态机
|
||||
|
||||
```
|
||||
shared/overlay/
|
||||
├── Overlay.tsx # 定位 + portal + 显隐
|
||||
├── placement.ts # PLACEMENT_MAP, FLIP_MAP
|
||||
├── useOverlay.ts # open/close/trigger 逻辑
|
||||
└── Overlay.module.css
|
||||
```
|
||||
|
||||
预估可减少 **~300 行**重复代码,后续 Popconfirm、Dropdown 也可复用。
|
||||
|
||||
### 3.2 `getSemantic` 内联重复(22 处)
|
||||
|
||||
以下组件各自定义相同模式的 `getSemantic` helper:
|
||||
|
||||
```
|
||||
common/Button/Button.tsx
|
||||
display/badge/Badge.tsx
|
||||
display/card/Card.tsx
|
||||
display/table/Table.tsx
|
||||
display/tooltip/Tooltip.tsx
|
||||
… 等 22 个文件
|
||||
```
|
||||
|
||||
**建议:** 抽到 `shared/utils/semantic.ts`:
|
||||
|
||||
```ts
|
||||
export function getSemanticClass(
|
||||
styles: Record<string, string>,
|
||||
semantic?: Record<string, string>,
|
||||
part: string,
|
||||
): string | undefined
|
||||
```
|
||||
|
||||
### 3.3 Portal 工具已写但未用
|
||||
|
||||
`shared/utils/portal.ts` 提供 `getPortalContainer` / `renderPortal`,**零引用**。
|
||||
Tooltip、Popover、Tour、Modal 均直接 `createPortal(..., document.body)`。
|
||||
|
||||
**建议:** overlay 重构时统一接入,并读取 ConfigProvider 的 `getPopupContainer`。
|
||||
|
||||
### 3.4 Table 内联分页 vs nav/Pagination
|
||||
|
||||
`display/table/Table.tsx` 内置 `PaginationInline`,功能仅为 prev/next + 页码,不支持:
|
||||
|
||||
- `showSizeChanger`
|
||||
- `showQuickJumper`
|
||||
- `pageSizeOptions`
|
||||
|
||||
**建议:** Table 直接组合 `nav/pagination/Pagination`,或抽取 `shared/PaginationMini`。
|
||||
|
||||
### 3.5 className 拼接模式重复
|
||||
|
||||
大量组件使用:
|
||||
|
||||
```ts
|
||||
[className, styles.root].filter(Boolean).join(' ')
|
||||
```
|
||||
|
||||
**建议:** `shared/utils/classNames.ts`(或引入轻量 `clsx` 依赖):
|
||||
|
||||
```ts
|
||||
export function cn(...parts: (string | false | undefined | null)[]): string
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. ConfigProvider 与主题系统
|
||||
|
||||
### 4.1 现状:基础设施已建,消费方缺失
|
||||
|
||||
`ConfigProvider` 提供:
|
||||
|
||||
| Context 字段 | 是否被组件消费 |
|
||||
|-------------|---------------|
|
||||
| `prefixCls` | ❌ CSS 硬编码 `nv-*` |
|
||||
| `iconPrefixCls` | ❌ Icon 用全局 `.nv-icon` |
|
||||
| `colorPrimary` / `theme.token` | ⚠️ 仅注入 wrapper inline style |
|
||||
| `theme.components` | ❌ 接口存在,从未应用 |
|
||||
| `locale` | ❌ 无组件读取 |
|
||||
| `zIndex` | ❌ Modal/Drawer 各自硬编码 |
|
||||
| `getPopupContainer` | ❌ 仅 prop 级,不读 context |
|
||||
|
||||
`useConfig()` 仅出现在 `ConfigProvider.tsx` 及其测试中。
|
||||
|
||||
### 4.2 建议接线顺序
|
||||
|
||||
**Phase 1 — 最小可用全局配置:**
|
||||
|
||||
1. 创建 `shared/hooks/usePikaContext.ts`,封装 `useConfig()` + 默认值合并
|
||||
2. Overlay 组件(Tooltip、Popover、Dropdown、Modal)读取 `getPopupContainer`、`zIndex`
|
||||
3. 所有 portal 组件 z-index 从 context 叠加(base + offset)
|
||||
|
||||
**Phase 2 — 前缀与国际化:**
|
||||
|
||||
4. CSS Module 改为 `[data-nv-prefix]` 或 runtime class prefix(成本较高,可延后)
|
||||
5. `locale` 接入 DatePicker、Pagination、Modal 按钮文案
|
||||
|
||||
**Phase 3 — 组件级主题:**
|
||||
|
||||
6. 实现 `theme.components.Button` 等 token 覆盖
|
||||
|
||||
### 4.3 Token 源统一
|
||||
|
||||
当前三套 token 并行:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ theme/tokens.ts (JS 对象) │
|
||||
│ → entry/tokens/index.ts → inline style 注入 │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ global.css (:root --nv-*) │
|
||||
│ → feedback 组件 CSS var fallback │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ 组件私有 --_* (TSX inline style → CSS Module) │
|
||||
│ → Button, Layout, Progress 等 │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**问题:**
|
||||
|
||||
- `theme/tokens.ts` 的 `color.primary = '#6C5CE7'` 与 `global.css` 暗色模式值可能不同步
|
||||
- Entry 组件 inline style **不响应** ConfigProvider 运行时改色
|
||||
- `global.css` 首行 `@import './theme/dumi.css'` 把文档站样式耦合进库
|
||||
|
||||
**建议目标架构:**
|
||||
|
||||
```
|
||||
theme/
|
||||
├── tokens.css # 唯一 token 源(--nv-*)
|
||||
├── tokens.ts # 从 CSS 变量读取或生成类型(可选)
|
||||
├── dark.css
|
||||
└── compact.css
|
||||
|
||||
组件 CSS:只用 var(--nv-*),组件级 --_* 仅用于 prop 驱动的动态值(width、height)
|
||||
ConfigProvider:通过 style 覆盖 --nv-color-primary 等根变量
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. CSS 与 Design Token 策略
|
||||
|
||||
### 5.1 当前模式评估
|
||||
|
||||
| 模式 | 用途 | 评价 |
|
||||
|------|------|------|
|
||||
| `*.module.css` | 组件样式隔离 | ✅ 主流,正确 |
|
||||
| `--nv-*` + fallback | 设计 token | ✅ 正确方向,但 fallback 硬编码过多 |
|
||||
| `--_*` inline 注入 | prop 驱动动态值 | ✅ 适合 Layout/Button 的高度、宽度 |
|
||||
| 全局 `Icon/style.css` | 图标字体 | ⚠️ 应改为 CSS Module 或 CSS-in-JS |
|
||||
| inline style 色值 | Entry 组件 | ❌ 应迁移到 CSS var |
|
||||
|
||||
### 5.2 硬编码色值示例(需逐步清理)
|
||||
|
||||
以下文件存在与 token 并行的硬编码:
|
||||
|
||||
- `display/popover/Popover.module.css` — `#fff`、`rgba(0,0,0,0.85)`
|
||||
- `display/collapse/Collapse.module.css` — 边框/背景硬编码
|
||||
- `entry/tokens/index.ts` — 直接从 JS 对象读色,不经过 CSS 变量
|
||||
|
||||
### 5.3 CSS 变量命名规范建议
|
||||
|
||||
| 层级 | 前缀 | 示例 | 谁设置 |
|
||||
|------|------|------|--------|
|
||||
| 全局设计 token | `--nv-` | `--nv-color-primary` | global.css / ConfigProvider |
|
||||
| 组件内部变量 | `--_{part}-` | `--_height`、`--_padding` | 组件 TSX inline style |
|
||||
| 语义 DOM | `data-*` | `data-collapsed`、`data-size` | 组件 TSX |
|
||||
|
||||
避免 `--_*` 与 `--nv-*` 语义混淆:前者是**实例级**动态值,后者是**主题级**静态 token。
|
||||
|
||||
---
|
||||
|
||||
## 6. 类型系统
|
||||
|
||||
### 6.1 重复 Size / Status 类型
|
||||
|
||||
`shared/types/common.ts` 已定义:
|
||||
|
||||
```ts
|
||||
export type PikaSize = 'small' | 'middle' | 'large'
|
||||
export type PikaStatus = 'default' | 'success' | 'warning' | 'error' | 'info'
|
||||
```
|
||||
|
||||
但 **几乎无组件引用**。各模块自建:
|
||||
|
||||
| 类型 | 位置 |
|
||||
|------|------|
|
||||
| `DataEntrySize` | `entry/common.ts` |
|
||||
| `ButtonSize` | `common/Button/Button.tsx` |
|
||||
| `AvatarSizeType` | `display/avatar/Avatar.tsx` |
|
||||
| `TagSize`, `CardSize`, `TableSize`, `BadgeSize`… | 各 display 组件 |
|
||||
|
||||
**建议:**
|
||||
|
||||
```ts
|
||||
// shared/types/common.ts — canonical types
|
||||
export type PikaSize = 'small' | 'middle' | 'large'
|
||||
export type PikaStatus = 'default' | 'success' | 'warning' | 'error' | 'info'
|
||||
|
||||
// entry/common.ts
|
||||
import type { PikaSize, PikaStatus } from '../../shared/types'
|
||||
export type DataEntrySize = PikaSize
|
||||
export type DataEntryStatus = PikaStatus
|
||||
|
||||
// 组件 props
|
||||
export interface ButtonProps {
|
||||
size?: PikaSize
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 公共 Props 基类利用不足
|
||||
|
||||
`entry/common.ts` 定义了良好的基类:
|
||||
|
||||
- `DataEntryBaseProps`
|
||||
- `DataEntryInputProps`
|
||||
- `DataEntryTextualProps`
|
||||
- `DataEntrySelectableProps`
|
||||
|
||||
**建议:** display / feedback 组件也建立类似基类:
|
||||
|
||||
```ts
|
||||
// shared/types/component.ts
|
||||
export interface PikaComponentProps {
|
||||
className?: string
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export interface PikaInteractiveProps extends PikaComponentProps {
|
||||
disabled?: boolean
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 `CSSCustomProperties` 使用率低
|
||||
|
||||
`shared/types/css.ts` 仅 5 个组件 import,其余用 `Record<string, string>` 或无类型。
|
||||
|
||||
**建议:** 统一 cssVars 类型:
|
||||
|
||||
```ts
|
||||
type CSSVars = CSSProperties & Record<`--${string}`, string>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Props 诚实性(接口 vs 实现)
|
||||
|
||||
> 接口声明了但实现缺失的 prop,会误导 AI 和使用者。应 **实现** 或 **从类型中移除**(或标注 `@deprecated` / 文档说明「即将支持」)。
|
||||
|
||||
### 7.1 已确认未实现的 Props
|
||||
|
||||
| 组件 | Prop | 现状 |
|
||||
|------|------|------|
|
||||
| **Layout.Sider** | `onBreakpoint` | 有 `breakpoint` data 属性,无 resize 监听 |
|
||||
| **DatePicker** | `mode`, `format` | 固定 date 模式 + `YYYY-MM-DD` |
|
||||
| **TimePicker** | `format`, `hourStep` | 固定格式与步进 |
|
||||
| **Slider** | `range` | 仅单值滑块 |
|
||||
| **TextArea** | `autoSize`, `onResize` | 未实现自动高度 |
|
||||
| **Upload** | `directory`, `onPreview` | 未实现文件夹上传与预览 |
|
||||
| **Popconfirm** | `okType` | 确认按钮未区分 danger/primary |
|
||||
| **TreeSelect** | `treeCheckable` | 解构为 `_treeCheckable` 或未使用 |
|
||||
| **Form** | `labelCol`, `wrapperCol` | 无栅格布局 |
|
||||
| **Table** | `filters`, `filteredValue`, `filterMultiple` | Column 类型完整,无 filter UI |
|
||||
| **Table** | `showSizeChanger`, `showQuickJumper` | PaginationConfig 有类型,内联分页未支持 |
|
||||
| **Mention** | `showSearch`, `showClear` | 继承自基类但未解构 |
|
||||
|
||||
### 7.2 处理策略
|
||||
|
||||
```
|
||||
优先级 A — 高频 API(Ant Design 对标)
|
||||
→ 补实现:DatePicker.format、Slider.range、Layout.onBreakpoint
|
||||
|
||||
优先级 B — 低频 / 复杂
|
||||
→ 从类型移除,CHANGELOG 记录,待迭代再加
|
||||
|
||||
优先级 C — 计划内
|
||||
→ 保留类型,组件 JSDoc 标注 @experimental
|
||||
```
|
||||
|
||||
### 7.3 类型笔误
|
||||
|
||||
`TooltipPlacement` 含 `'rightRight'`,疑似应为 `'rightBottom'`(`Tooltip.tsx`)。`FLIP_MAP` 映射也需核对。
|
||||
|
||||
---
|
||||
|
||||
## 8. 共享 Hooks 与工具
|
||||
|
||||
### 8.1 已有 Hooks 使用情况
|
||||
|
||||
| Hook | 路径 | 已使用 | 应使用未使用 |
|
||||
|------|------|--------|-------------|
|
||||
| `useClickOutside` | `shared/hooks/` | Select, Dropdown, Tooltip, Popover, Popconfirm, AutoComplete, RangePicker | **Cascader, DatePicker, TimePicker, TreeSelect, Mention** |
|
||||
| `useScrollListener` | 同上 | Affix, Anchor, BackTop, Tooltip, Popover, Tour | — |
|
||||
| `useEscapeKey` | 同上 | Modal, Drawer, Tour, Image | — |
|
||||
| `useMatchMedia` | 同上 | Grid | **Layout.Sider**(breakpoint) |
|
||||
|
||||
### 8.2 重复实现需清理
|
||||
|
||||
| 组件 | 问题 | 建议 |
|
||||
|------|------|------|
|
||||
| `FloatButton` | 自定义 `document.addEventListener('click')` | 改用 `useClickOutside` |
|
||||
| `Image` | `useEscapeKey` + 额外 `keydown` 监听 | 合并为单一 hook |
|
||||
| `Tour` | `useEscapeKey` + 额外 `keydown` 处理方向键 | 扩展 hook 或统一 handler |
|
||||
|
||||
### 8.3 建议新增 Hooks
|
||||
|
||||
| Hook | 用途 |
|
||||
|------|------|
|
||||
| `useControllableState` | 统一 controlled/uncontrolled 模式(大量组件重复 `isControlled` 逻辑) |
|
||||
| `useBreakpoint` | Layout.Sider、Grid 共用 responsive 逻辑 |
|
||||
| `useMergedRef` | forwardRef + 内部 ref 合并 |
|
||||
|
||||
`useControllableState` 示例 — 当前 Layout、Sider、Modal、Tabs 等均有类似代码:
|
||||
|
||||
```ts
|
||||
const isControlled = value !== undefined
|
||||
const current = isControlled ? value : internal
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 测试覆盖
|
||||
|
||||
### 9.1 覆盖率概况
|
||||
|
||||
- **主组件:** 几乎每个一级目录都有 `*.test.tsx` ✅
|
||||
- **子组件:** 以下 **7 个** 无独立测试:
|
||||
|
||||
| 子组件 | 路径 | 风险 |
|
||||
|--------|------|------|
|
||||
| `AvatarGroup` | `display/avatar/` | maxCount、overflow 逻辑 |
|
||||
| `BadgeRibbon` | `display/badge/` | 定位、颜色 |
|
||||
| `CardGrid` / `CardMeta` | `display/card/` | 栅格布局 |
|
||||
| `ImagePreviewGroup` | `display/image/` | 多图预览切换 |
|
||||
| `StatisticTimer` | `display/statistic/` | 倒计时逻辑 |
|
||||
| `CheckableTag` | `display/tag/` | 选中态 |
|
||||
|
||||
### 9.2 测试质量建议
|
||||
|
||||
| 方向 | 说明 |
|
||||
|------|------|
|
||||
| **行为测试优先** | 少测 class 名,多测交互(open/close、keyboard、form submit) |
|
||||
| **未实现 prop 不测** | 避免测试「接口存在但行为不存在」造成 false positive |
|
||||
| **ConfigProvider 集成测试** | 验证 theme/locale 注入后组件行为变化 |
|
||||
| **a11y 基线** | overlay 组件补充 aria 属性断言 |
|
||||
|
||||
### 9.3 重复测试
|
||||
|
||||
`src/components/Button/Button.test.tsx` 与 `common/Button/Button.test.tsx` 可能重复 — 随遗留目录一并清理。
|
||||
|
||||
---
|
||||
|
||||
## 10. 文档与 DX
|
||||
|
||||
### 10.1 文档现状
|
||||
|
||||
| 类型 | 数量 | 路径 |
|
||||
|------|------|------|
|
||||
| 组件 co-located `index.md` | **1** | `components/Button/index.md`(遗留) |
|
||||
| Dumi 指南 | 3 | `docs/index.md`, `getting-started.md`, `components/index.md` |
|
||||
| 逐组件 API 文档 | **0** | — |
|
||||
| 设计规范 | 1 | `DESIGN_SPEC.md`(完整) |
|
||||
| 开发约定 | 0 | `CONVENTIONS.md` 已删除 |
|
||||
|
||||
### 10.2 文档建设建议
|
||||
|
||||
**短期(可脚本化):**
|
||||
|
||||
1. 为每个组件目录生成 `index.md` 模板(frontmatter + 基础 demo + Props 表占位)
|
||||
2. Dumi 配置 `resolve.atomDirs` 指向 `src/components`
|
||||
|
||||
**中期:**
|
||||
|
||||
3. 从 TS 类型自动生成 Props 表(dumi-theme-Pika 或 typedoc)
|
||||
4. 每个组件至少 3 个 demo:基础用法、受控模式、禁用/错误态
|
||||
|
||||
**长期:**
|
||||
|
||||
5. AI 示例库 — 与 `DESIGN_SPEC.md` 的 AI-First 原则对齐,每组件提供「AI 友好」单行示例
|
||||
|
||||
### 10.3 开发者体验
|
||||
|
||||
| 改进项 | 说明 |
|
||||
|--------|------|
|
||||
| 恢复 `CONVENTIONS.md` | 目录命名、export 规范、CSS 约定、测试要求 |
|
||||
| `npm run lint:fix` | 已有 lint-staged,可加 CI workflow |
|
||||
| Storybook vs Dumi | 当前 Dumi 足够,保持单文档方案 |
|
||||
| 组件生成 CLI | `pnpm gen component Button --category common` 脚手架 |
|
||||
|
||||
---
|
||||
|
||||
## 11. 构建与导出
|
||||
|
||||
### 11.1 当前构建链
|
||||
|
||||
```
|
||||
father build → dist/cjs + dist/esm
|
||||
src/index.ts → export * from './components' + tokens
|
||||
```
|
||||
|
||||
### 11.2 问题
|
||||
|
||||
| 问题 | 详情 |
|
||||
|------|------|
|
||||
| **Subpath exports 可能无效** | `package.json` 声明 `"./*" → dist/esm/*/index.js`,但源码无对应分包 |
|
||||
| **CSS 未作为包入口** | 消费者需自行引入 token CSS;`global.css` 未 export |
|
||||
| **global.css 耦合 dumi** | `@import './theme/dumi.css'` 不应出现在库运行时入口 |
|
||||
| **shared 不对外** | 第三方无法复用 hooks/utils |
|
||||
| **手动维护导出面** | 新组件需改 3 处 index |
|
||||
|
||||
### 11.3 建议
|
||||
|
||||
**package.json exports 修正:**
|
||||
|
||||
```json
|
||||
{
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/esm/index.js",
|
||||
"require": "./dist/cjs/index.js"
|
||||
},
|
||||
"./styles": "./dist/styles/global.css",
|
||||
"./tokens": "./dist/esm/theme/tokens.js"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**CSS 构建:**
|
||||
|
||||
- 将 `global.css` 拆为 `styles/tokens.css`(纯 token)+ `styles/dumi.css`(文档专用)
|
||||
- father 配置 `extraBabelPlugins` 或 postcss 复制 CSS 到 dist
|
||||
|
||||
**按需加载(可选):**
|
||||
|
||||
- 若需要 `@Pika/ui/button`,需 father 多 entry 配置 + 每组件独立 package path
|
||||
|
||||
---
|
||||
|
||||
## 12. 优先级路线图
|
||||
|
||||
### P0 — 基础一致性(1–2 周)
|
||||
|
||||
| # | 任务 | 影响 |
|
||||
|---|------|------|
|
||||
| 1 | 删除 `src/components/Button/` 遗留,统一至 `common/Button/` | 消除混淆 |
|
||||
| 2 | 统一 `common/` 目录为 kebab-case | 命名一致 |
|
||||
| 3 | Props 诚实性清理:移除或标注未实现 prop | AI 生成准确 |
|
||||
| 4 | `global.css` 与 dumi.css 解耦 | 库可独立使用 |
|
||||
| 5 | 新增 `shared/utils/classNames.ts` | 减少重复 |
|
||||
|
||||
### P1 — 架构增强(2–4 周)
|
||||
|
||||
| # | 任务 | 影响 |
|
||||
|---|------|------|
|
||||
| 6 | 抽取 `shared/overlay/` primitives | Tooltip/Popover 减 300+ 行 |
|
||||
| 7 | 抽取 `getSemantic` → `shared/utils/semantic.ts` | 22 处重复消除 |
|
||||
| 8 | ConfigProvider Phase 1 接线(zIndex、getPopupContainer) | 全局配置可用 |
|
||||
| 9 | 统一 Size/Status 类型为 `PikaSize` / `PikaStatus` | 类型一致 |
|
||||
| 10 | Cascader/DatePicker/TreeSelect 接入 `useClickOutside` | 交互完整 |
|
||||
| 11 | 新增 `useControllableState` hook | 减少状态逻辑重复 |
|
||||
|
||||
### P2 — 体验完善(4–8 周)
|
||||
|
||||
| # | 任务 | 影响 |
|
||||
|---|------|------|
|
||||
| 12 | Token 统一为 CSS `--nv-*` 单源 | 主题可运行时切换 |
|
||||
| 13 | 补全 7 个子组件测试 | 测试完整 |
|
||||
| 14 | 每组件 `index.md` + 基础 demo | 文档可用 |
|
||||
| 15 | 恢复并完善 `CONVENTIONS.md` | 贡献者友好 |
|
||||
| 16 | 实现高频未实现 prop(DatePicker.format、Slider.range) | API 完整 |
|
||||
| 17 | package.json exports + CSS 入口修正 | 发布可用 |
|
||||
|
||||
### P3 — 长期演进
|
||||
|
||||
| # | 任务 |
|
||||
|---|------|
|
||||
| 18 | 组件级 theme override(`theme.components`) |
|
||||
| 19 | 按需 subpath import 分包构建 |
|
||||
| 20 | 组件生成 CLI |
|
||||
| 21 | a11y 审计与 WCAG 基线 |
|
||||
| 22 | 可视化组件(AntV)与 UI 组件 Token 统一 |
|
||||
|
||||
---
|
||||
|
||||
## 13. 附录:文件清单速查
|
||||
|
||||
### 13.1 应删除/迁移
|
||||
|
||||
```
|
||||
src/components/Button/ → 删除,保留 common/Button/
|
||||
src/components/Button/index.md → 迁移至 docs 或 common/Button/
|
||||
```
|
||||
|
||||
### 13.2 应统一命名(common/ PascalCase → kebab-case)
|
||||
|
||||
```
|
||||
common/Button/ → common/button/
|
||||
common/ConfigProvider/ → common/config-provider/
|
||||
common/FloatButton/ → common/float-button/
|
||||
common/BackTop/ → common/back-top/
|
||||
… (共 10 个)
|
||||
```
|
||||
|
||||
### 13.3 共享层待建设
|
||||
|
||||
```
|
||||
shared/overlay/Overlay.tsx
|
||||
shared/utils/classNames.ts
|
||||
shared/utils/semantic.ts
|
||||
shared/hooks/useControllableState.ts
|
||||
shared/hooks/useBreakpoint.ts
|
||||
shared/hooks/usePikaContext.ts
|
||||
```
|
||||
|
||||
### 13.4 应对接 ConfigProvider 的组件(优先)
|
||||
|
||||
```
|
||||
display/tooltip/Tooltip.tsx
|
||||
display/popover/Popover.tsx
|
||||
nav/dropdown/Dropdown.tsx
|
||||
feedback/modal/Modal.tsx
|
||||
feedback/drawer/Drawer.tsx
|
||||
feedback/message/Message.tsx
|
||||
feedback/notification/Notification.tsx
|
||||
display/tour/Tour.tsx
|
||||
entry/select/Select.tsx
|
||||
entry/date-picker/DatePicker.tsx
|
||||
```
|
||||
|
||||
### 13.5 统计
|
||||
|
||||
| 指标 | 数量 |
|
||||
|------|------|
|
||||
| 组件目录 | ~77 |
|
||||
| 组件 TSX(不含测试) | ~80 |
|
||||
| 测试文件 | ~74 |
|
||||
| 无独立测试的子组件 | 7 |
|
||||
| getSemantic 重复 | 22 处 |
|
||||
| 未实现 prop 的组件 | ~12 |
|
||||
| co-located 文档 | 1 |
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
Pika 的 **六大分类 + 单组件目录结构** 已经是一个健康的基础,测试覆盖和 forwardRef 等实践也到位。当前主要短板集中在:
|
||||
|
||||
1. **一致性** — 命名、导出、类型、token 三套并行
|
||||
2. **诚实性** — 接口超前于实现,对 AI-First 目标伤害最大
|
||||
3. **基础设施未接线** — ConfigProvider、shared utils 写了但没用起来
|
||||
4. **重复** — Overlay、getSemantic、className 拼接、受控状态
|
||||
|
||||
按 P0 → P1 → P2 推进,可以在不推翻现有结构的前提下,显著提升可维护性和 AI 代码生成准确率。
|
||||
@@ -0,0 +1,417 @@
|
||||
# Pika 组件库测试报告
|
||||
|
||||
> 生成时间:2026-05-30
|
||||
> 测试框架:Vitest + @testing-library/react
|
||||
> 覆盖率工具:@vitest/coverage-v8
|
||||
|
||||
---
|
||||
|
||||
## 一、总体概览
|
||||
|
||||
| 指标 | 值 |
|
||||
|------|-----|
|
||||
| 测试文件数 | 63 |
|
||||
| 测试用例数 | 800 |
|
||||
| 通过率 | **100%** |
|
||||
| 语句覆盖率 (Stmts) | 82.42% |
|
||||
| 分支覆盖率 (Branch) | 76.74% |
|
||||
| 函数覆盖率 (Funcs) | 58.61% |
|
||||
| 行覆盖率 (Lines) | 82.42% |
|
||||
|
||||
---
|
||||
|
||||
## 二、本次新增组件测试详情
|
||||
|
||||
### 2.1 Affix 固钉
|
||||
|
||||
| 指标 | 值 |
|
||||
|------|-----|
|
||||
| 测试文件 | `src/components/common/Affix/Affix.test.tsx` |
|
||||
| 测试用例数 | 12 |
|
||||
| 语句覆盖率 | 93.10% |
|
||||
| 分支覆盖率 | 81.81% |
|
||||
| 函数覆盖率 | 100% |
|
||||
| 未覆盖行 | 51-55, 64-68 |
|
||||
|
||||
**测试用例清单:**
|
||||
|
||||
| # | 用例名 | 状态 |
|
||||
|---|--------|------|
|
||||
| 1 | renders with default props | ✅ |
|
||||
| 2 | renders with offsetTop | ✅ |
|
||||
| 3 | renders with offsetBottom | ✅ |
|
||||
| 4 | does not affix without offset | ✅ |
|
||||
| 5 | applies data-affixed attribute when affixed | ✅ |
|
||||
| 6 | applies data-position attribute | ✅ |
|
||||
| 7 | renders placeholder when affixed | ✅ |
|
||||
| 8 | calls onChange when affix state changes | ✅ |
|
||||
| 9 | offsetTop takes priority over offsetBottom | ✅ |
|
||||
| 10 | applies data-position bottom for offsetBottom | ✅ |
|
||||
| 11 | applies custom className and style | ✅ |
|
||||
| 12 | uses custom getContainer for scroll listener | ✅ |
|
||||
|
||||
---
|
||||
|
||||
### 2.2 Anchor 锚点
|
||||
|
||||
| 指标 | 值 |
|
||||
|------|-----|
|
||||
| 测试文件 | `src/components/common/Anchor/Anchor.test.tsx` |
|
||||
| 测试用例数 | 10 |
|
||||
| 语句覆盖率 | 84.69% |
|
||||
| 分支覆盖率 | 84.37% |
|
||||
| 函数覆盖率 | 80% |
|
||||
| 未覆盖行 | 183-202, 230-233 |
|
||||
|
||||
**测试用例清单:**
|
||||
|
||||
| # | 用例名 | 状态 |
|
||||
|---|--------|------|
|
||||
| 1 | renders with default props | ✅ |
|
||||
| 2 | renders multiple Anchor.Link items | ✅ |
|
||||
| 3 | handles click events | ✅ |
|
||||
| 4 | supports direction prop | ✅ |
|
||||
| 5 | marks active link with data-active | ✅ |
|
||||
| 6 | renders nested Anchor.Link items | ✅ |
|
||||
| 7 | calls onChange when active link changes | ✅ |
|
||||
| 8 | updates URL hash when hash prop is true | ✅ |
|
||||
| 9 | renders ink indicator element | ✅ |
|
||||
| 10 | applies custom className and style | ✅ |
|
||||
|
||||
---
|
||||
|
||||
### 2.3 App 包裹组件
|
||||
|
||||
| 指标 | 值 |
|
||||
|------|-----|
|
||||
| 测试文件 | `src/components/common/App/App.test.tsx` |
|
||||
| 测试用例数 | 8 |
|
||||
| 语句覆盖率 | 100% |
|
||||
| 分支覆盖率 | 100% |
|
||||
| 函数覆盖率 | 50% |
|
||||
| 未覆盖行 | — |
|
||||
|
||||
**测试用例清单:**
|
||||
|
||||
| # | 用例名 | 状态 |
|
||||
|---|--------|------|
|
||||
| 1 | renders children | ✅ |
|
||||
| 2 | provides context via useApp | ✅ |
|
||||
| 3 | provides message static methods | ✅ |
|
||||
| 4 | provides modal static methods | ✅ |
|
||||
| 5 | provides notification static methods | ✅ |
|
||||
| 6 | throws when useApp is used outside App | ✅ |
|
||||
| 7 | exposes useApp as a static method on App | ✅ |
|
||||
| 8 | renders wrapper div | ✅ |
|
||||
|
||||
> 函数覆盖率 50% 是因为 message/modal/notification 的静态方法目前为 no-op 占位,待后续实际组件实现后补充。
|
||||
|
||||
---
|
||||
|
||||
### 2.4 ConfigProvider 全局配置
|
||||
|
||||
| 指标 | 值 |
|
||||
|------|-----|
|
||||
| 测试文件 | `src/components/common/ConfigProvider/ConfigProvider.test.tsx` |
|
||||
| 测试用例数 | 12 |
|
||||
| 语句覆盖率 | **100%** |
|
||||
| 分支覆盖率 | **100%** |
|
||||
| 函数覆盖率 | **100%** |
|
||||
| 未覆盖行 | — |
|
||||
|
||||
**测试用例清单:**
|
||||
|
||||
| # | 用例名 | 状态 |
|
||||
|---|--------|------|
|
||||
| 1 | renders children | ✅ |
|
||||
| 2 | applies dark theme data attribute (string algorithm) | ✅ |
|
||||
| 3 | applies dark theme data attribute (array algorithm) | ✅ |
|
||||
| 4 | does not set data-theme for default algorithm | ✅ |
|
||||
| 5 | applies custom primary color CSS variable | ✅ |
|
||||
| 6 | applies theme token as CSS variables | ✅ |
|
||||
| 7 | preserves --nv- prefix in token keys | ✅ |
|
||||
| 8 | provides config via useConfig hook | ✅ |
|
||||
| 9 | provides default context values | ✅ |
|
||||
| 10 | passes locale and zIndex through context | ✅ |
|
||||
| 11 | does not set style when no CSS variables needed | ✅ |
|
||||
| 12 | passes getPopupContainer through context | ✅ |
|
||||
|
||||
---
|
||||
|
||||
### 2.5 BackTop 回到顶部
|
||||
|
||||
| 指标 | 值 |
|
||||
|------|-----|
|
||||
| 测试文件 | `src/components/common/BackTop/BackTop.test.tsx` |
|
||||
| 测试用例数 | 10 |
|
||||
| 语句覆盖率 | 98.03% |
|
||||
| 分支覆盖率 | 93.75% |
|
||||
| 函数覆盖率 | 100% |
|
||||
| 未覆盖行 | 72-73 |
|
||||
|
||||
**测试用例清单:**
|
||||
|
||||
| # | 用例名 | 状态 |
|
||||
|---|--------|------|
|
||||
| 1 | renders with default props | ✅ |
|
||||
| 2 | is hidden by default (no data-visible) | ✅ |
|
||||
| 3 | becomes visible after scroll exceeds visibilityHeight | ✅ |
|
||||
| 4 | remains hidden when scroll is below threshold | ✅ |
|
||||
| 5 | fires onClick when clicked | ✅ |
|
||||
| 6 | calls window.scrollTo to scroll to top | ✅ |
|
||||
| 7 | renders custom children | ✅ |
|
||||
| 8 | applies custom className and style | ✅ |
|
||||
| 9 | uses custom target container | ✅ |
|
||||
| 10 | removes scroll listener on unmount | ✅ |
|
||||
|
||||
---
|
||||
|
||||
### 2.6 Watermark 水印
|
||||
|
||||
| 指标 | 值 |
|
||||
|------|-----|
|
||||
| 测试文件 | `src/components/common/Watermark/Watermark.test.tsx` |
|
||||
| 测试用例数 | 8 |
|
||||
| 语句覆盖率 | 73.42% |
|
||||
| 分支覆盖率 | 90.62% |
|
||||
| 函数覆盖率 | 66.66% |
|
||||
| 未覆盖行 | 179, 245-257, 262 |
|
||||
|
||||
**测试用例清单:**
|
||||
|
||||
| # | 用例名 | 状态 |
|
||||
|---|--------|------|
|
||||
| 1 | renders children | ✅ |
|
||||
| 2 | renders with text content | ✅ |
|
||||
| 3 | renders with multiple content lines | ✅ |
|
||||
| 4 | applies custom font settings | ✅ |
|
||||
| 5 | applies custom zIndex | ✅ |
|
||||
| 6 | renders without content or image | ✅ |
|
||||
| 7 | applies custom gap and offset | ✅ |
|
||||
| 8 | applies custom rotate | ✅ |
|
||||
|
||||
> 覆盖率较低是因为图片水印的异步加载逻辑在 jsdom 中难以完全模拟。
|
||||
|
||||
---
|
||||
|
||||
### 2.7 Drawer 抽屉
|
||||
|
||||
| 指标 | 值 |
|
||||
|------|-----|
|
||||
| 测试文件 | `src/components/feedback/drawer/Drawer.test.tsx` |
|
||||
| 测试用例数 | 9 |
|
||||
| 语句覆盖率 | 97.10% |
|
||||
| 分支覆盖率 | 62.50% |
|
||||
| 函数覆盖率 | 100% |
|
||||
| 未覆盖行 | 67-69, 107-108 |
|
||||
|
||||
**测试用例清单:**
|
||||
|
||||
| # | 用例名 | 状态 |
|
||||
|---|--------|------|
|
||||
| 1 | renders when open is true | ✅ |
|
||||
| 2 | does not render when open is false | ✅ |
|
||||
| 3 | renders title | ✅ |
|
||||
| 4 | calls onClose when close button clicked | ✅ |
|
||||
| 5 | calls onClose when mask clicked and maskClosable is true | ✅ |
|
||||
| 6 | does not close when maskClosable is false | ✅ |
|
||||
| 7 | closes on ESC key when keyboard is true | ✅ |
|
||||
| 8 | renders with different placements | ✅ |
|
||||
| 9 | renders footer | ✅ |
|
||||
|
||||
---
|
||||
|
||||
### 2.8 List 列表
|
||||
|
||||
| 指标 | 值 |
|
||||
|------|-----|
|
||||
| 测试文件 | `src/components/data-display/list/List.test.tsx` |
|
||||
| 测试用例数 | 15 |
|
||||
| 语句覆盖率 | 95.98% |
|
||||
| 分支覆盖率 | 91.42% |
|
||||
| 函数覆盖率 | 100% |
|
||||
| 未覆盖行 | 194-203 |
|
||||
|
||||
**测试用例清单:**
|
||||
|
||||
| # | 用例名 | 状态 |
|
||||
|---|--------|------|
|
||||
| 1 | renders with default props | ✅ |
|
||||
| 2 | renders data items via renderItem | ✅ |
|
||||
| 3 | renders children directly | ✅ |
|
||||
| 4 | supports size prop | ✅ |
|
||||
| 5 | supports bordered prop | ✅ |
|
||||
| 6 | renders header and footer | ✅ |
|
||||
| 7 | renders empty state when data is empty | ✅ |
|
||||
| 8 | renders custom empty content | ✅ |
|
||||
| 9 | renders loading state | ✅ |
|
||||
| 10 | renders grid mode | ✅ |
|
||||
| 11 | renders ListItem with extra | ✅ |
|
||||
| 12 | renders ListItem with actions | ✅ |
|
||||
| 13 | passes context from List to ListItem | ✅ |
|
||||
| 14 | renders ListItemMeta with avatar, title, description | ✅ |
|
||||
| 15 | applies data-size attribute | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 三、全量组件覆盖率汇总
|
||||
|
||||
### 3.1 通用组件 (common)
|
||||
|
||||
| 组件 | Stmts | Branch | Funcs | Lines |
|
||||
|------|-------|--------|-------|-------|
|
||||
| Affix | 93.10% | 81.81% | 100% | 93.10% |
|
||||
| Anchor | 84.69% | 84.37% | 80% | 84.69% |
|
||||
| App | 100% | 100% | 50% | 100% |
|
||||
| BackTop | 98.03% | 93.75% | 100% | 98.03% |
|
||||
| Button | 84.12% | 73.23% | 81.81% | 84.12% |
|
||||
| ConfigProvider | 100% | 100% | 100% | 100% |
|
||||
| FloatButton | 50.07% | 48.83% | 30% | 50.07% |
|
||||
| Icon | 48.33% | 68.42% | 53.84% | 48.33% |
|
||||
| Typography | 67.09% | 55% | 58.82% | 67.09% |
|
||||
| Watermark | 73.42% | 90.62% | 66.66% | 73.42% |
|
||||
|
||||
### 3.2 数据展示组件 (data-display)
|
||||
|
||||
| 组件 | Stmts | Branch | Funcs | Lines |
|
||||
|------|-------|--------|-------|-------|
|
||||
| Avatar | 93.28% | 43.75% | 100% | 93.28% |
|
||||
| Badge | 98.85% | 76.47% | 100% | 98.85% |
|
||||
| Calendar | 97.59% | 80.61% | 82.35% | 97.59% |
|
||||
| Card | 99.70% | 82.35% | 100% | 99.70% |
|
||||
| Carousel | 88.19% | 64.51% | 66.66% | 88.19% |
|
||||
| Collapse | 98.10% | 83.33% | 66.66% | 98.10% |
|
||||
| Descriptions | 98.20% | 87.87% | 100% | 98.20% |
|
||||
| Empty | 100% | 80% | 100% | 100% |
|
||||
| Image | 87.95% | 80.35% | 50% | 87.95% |
|
||||
| List | 95.98% | 91.42% | 100% | 95.98% |
|
||||
| Popover | 91.39% | 82.95% | 31.25% | 91.39% |
|
||||
| QRCode | 94.27% | 97.14% | 100% | 94.27% |
|
||||
| Segmented | 98.04% | 83.33% | 100% | 98.04% |
|
||||
| Statistic | 99.71% | 76.19% | 100% | 99.71% |
|
||||
| Table | 87.99% | 59.44% | 76% | 87.99% |
|
||||
| Tag | 98.75% | 85.18% | 100% | 98.75% |
|
||||
| Timeline | 100% | 93.75% | 100% | 100% |
|
||||
| Tooltip | 91.10% | 81.92% | 31.25% | 91.10% |
|
||||
| Tour | 84.29% | 84.61% | 35% | 84.29% |
|
||||
| Tree | 85.61% | 79.61% | 64% | 85.61% |
|
||||
|
||||
### 3.3 数据录入组件 (data-entry)
|
||||
|
||||
| 组件 | Stmts | Branch | Funcs | Lines |
|
||||
|------|-------|--------|-------|-------|
|
||||
| AutoComplete | 67.41% | 66.66% | 60% | 67.41% |
|
||||
| Cascader | 68.53% | 77.27% | 37.5% | 68.53% |
|
||||
| Checkbox | 98.61% | 80% | 100% | 98.61% |
|
||||
| DatePicker | 56.06% | 33.33% | 8.33% | 56.06% |
|
||||
| Form | 72.23% | 71.87% | 35% | 72.23% |
|
||||
| Input | 71.07% | 52.63% | 0% | 71.07% |
|
||||
| InputNumber | 78.91% | 47.36% | 66.66% | 78.91% |
|
||||
| Mention | 64.57% | 72% | 100% | 64.57% |
|
||||
| Radio | 100% | 92.85% | 100% | 100% |
|
||||
| RangePicker | 0% | 0% | 0% | 0% |
|
||||
| Rate | 87.70% | 56.52% | 71.42% | 87.70% |
|
||||
| Select | 54.62% | 35.71% | 12.5% | 54.62% |
|
||||
| Slider | 89.41% | 75.86% | 80% | 89.41% |
|
||||
| Switch | 92.66% | 66.66% | 0% | 92.66% |
|
||||
| TextArea | 89.71% | 50% | 100% | 89.71% |
|
||||
| TimePicker | 55.13% | 45.45% | 12.5% | 55.13% |
|
||||
| Transfer | 73.99% | 84.21% | 36.36% | 73.99% |
|
||||
| TreeSelect | 62.05% | 91.42% | 37.5% | 62.05% |
|
||||
| Upload | 58.05% | 88.23% | 17.64% | 58.05% |
|
||||
|
||||
### 3.4 反馈组件 (feedback)
|
||||
|
||||
| 组件 | Stmts | Branch | Funcs | Lines |
|
||||
|------|-------|--------|-------|-------|
|
||||
| Alert | 100% | 100% | 100% | 100% |
|
||||
| Drawer | 97.10% | 62.50% | 100% | 97.10% |
|
||||
| Message | 88.13% | 79.16% | 71.42% | 88.13% |
|
||||
| Modal | 95.25% | 79.62% | 100% | 95.25% |
|
||||
| Notification | 88.97% | 73.33% | 70.83% | 88.97% |
|
||||
| Popconfirm | 95.13% | 71.42% | 100% | 95.13% |
|
||||
| Progress | 82.05% | 59.64% | 80% | 82.05% |
|
||||
| Result | 100% | 100% | 100% | 100% |
|
||||
| Spin | 95.58% | 85.10% | 100% | 95.58% |
|
||||
|
||||
### 3.5 布局组件 (layout)
|
||||
|
||||
| 组件 | Stmts | Branch | Funcs | Lines |
|
||||
|------|-------|--------|-------|-------|
|
||||
| Layout | 100% | 80% | 50% | 100% |
|
||||
|
||||
### 3.6 导航组件 (nav)
|
||||
|
||||
| 组件 | Stmts | Branch | Funcs | Lines |
|
||||
|------|-------|--------|-------|-------|
|
||||
| Breadcrumb | 100% | 100% | 100% | 100% |
|
||||
| Dropdown | 97.05% | 91.89% | 66.66% | 97.05% |
|
||||
| Menu | 97.08% | 93.54% | 60% | 97.08% |
|
||||
| Pagination | 100% | 90.47% | 77.77% | 100% |
|
||||
| Steps | 100% | 100% | 100% | 100% |
|
||||
| Tabs | 100% | 92% | 100% | 100% |
|
||||
|
||||
---
|
||||
|
||||
## 四、100% 覆盖率组件
|
||||
|
||||
以下组件实现了语句/分支/函数/行 **全部 100%** 覆盖率:
|
||||
|
||||
- ✅ ConfigProvider
|
||||
- ✅ Alert
|
||||
- ✅ Result
|
||||
- ✅ Breadcrumb
|
||||
- ✅ Steps
|
||||
|
||||
---
|
||||
|
||||
## 五、覆盖率改进建议
|
||||
|
||||
### 5.1 优先改进(覆盖率 < 60%)
|
||||
|
||||
| 组件 | Stmts | 建议补充的测试 |
|
||||
|------|-------|---------------|
|
||||
| RangePicker | 0% | 完全没有测试文件,需要新建 |
|
||||
| Icon (Icon.tsx) | 0% | 自定义图标组件未测试 |
|
||||
| FloatButton | 50.07% | FloatButtonGroup 和 BackTop 变体未覆盖 |
|
||||
| Select | 54.62% | 下拉展开/搜索/多选逻辑未覆盖 |
|
||||
| DatePicker | 56.06% | 日期选择面板交互未覆盖 |
|
||||
| TimePicker | 55.13% | 时间选择面板交互未覆盖 |
|
||||
| Upload | 58.05% | 文件上传/拖拽/删除逻辑未覆盖 |
|
||||
|
||||
### 5.2 中等改进(覆盖率 60%-80%)
|
||||
|
||||
| 组件 | Stmts | 建议补充的测试 |
|
||||
|------|-------|---------------|
|
||||
| AutoComplete | 67.41% | 下拉建议列表交互 |
|
||||
| Cascader | 68.53% | 级联面板展开交互 |
|
||||
| Form | 72.23% | 表单验证/提交/重置逻辑 |
|
||||
| Watermark | 73.42% | 图片水印异步加载 |
|
||||
| Input | 71.07% | 前缀/后缀/密码切换 |
|
||||
| Transfer | 73.99% | 穿梭框选择/搜索逻辑 |
|
||||
| InputNumber | 78.91% | 步进器增减逻辑 |
|
||||
| Typography | 67.09% | 复制/编辑/省略交互 |
|
||||
| Mention | 64.57% | @提及下拉交互 |
|
||||
| TreeSelect | 62.05% | 树形选择展开交互 |
|
||||
|
||||
---
|
||||
|
||||
## 六、运行命令
|
||||
|
||||
```bash
|
||||
# 运行全量测试
|
||||
pnpm exec vitest run
|
||||
|
||||
# 运行指定组件测试
|
||||
pnpm exec vitest run src/components/common/Affix
|
||||
|
||||
# 运行覆盖率报告
|
||||
pnpm exec vitest run --coverage
|
||||
|
||||
# 运行 lint 检查
|
||||
pnpm exec eslint src/components/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **Pika 组件库测试报告完成。800 个测试用例全部通过,整体语句覆盖率 82.42%。**
|
||||
Reference in New Issue
Block a user