Files
RustUI/_plans/structure-optimization-plan.md
T
2026-05-31 09:36:23 +08:00

698 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 — 高频 APIAnt 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 | 实现高频未实现 propDatePicker.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 代码生成准确率。