@teamix-evo/ui 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +184 -184
- package/manifest.json +680 -492
- package/package.json +20 -10
- package/src/components/accordion/accordion.meta.md +5 -4
- package/src/components/accordion/accordion.stories.tsx +14 -9
- package/src/components/accordion/accordion.tsx +104 -8
- package/src/components/affix/affix.meta.md +20 -2
- package/src/components/affix/affix.stories.tsx +102 -25
- package/src/components/affix/affix.tsx +79 -9
- package/src/components/alert/alert.meta.md +44 -13
- package/src/components/alert/alert.stories.tsx +66 -21
- package/src/components/alert/alert.tsx +81 -34
- package/src/components/alert-dialog/alert-dialog.meta.md +61 -16
- package/src/components/alert-dialog/alert-dialog.stories.tsx +145 -3
- package/src/components/alert-dialog/alert-dialog.tsx +60 -13
- package/src/components/anchor/anchor.meta.md +8 -3
- package/src/components/anchor/anchor.stories.tsx +3 -3
- package/src/components/anchor/anchor.tsx +2 -2
- package/src/components/app/app.meta.md +9 -4
- package/src/components/app/app.stories.tsx +9 -7
- package/src/components/aspect-ratio/aspect-ratio.meta.md +4 -3
- package/src/components/aspect-ratio/aspect-ratio.stories.tsx +3 -3
- package/src/components/auto-complete/auto-complete.meta.md +14 -6
- package/src/components/auto-complete/auto-complete.stories.tsx +47 -4
- package/src/components/auto-complete/auto-complete.tsx +119 -71
- package/src/components/avatar/avatar.meta.md +6 -7
- package/src/components/avatar/avatar.stories.tsx +21 -3
- package/src/components/avatar/avatar.tsx +24 -23
- package/src/components/badge/badge.meta.md +10 -9
- package/src/components/badge/badge.stories.tsx +2 -2
- package/src/components/badge/badge.tsx +9 -15
- package/src/components/breadcrumb/breadcrumb.meta.md +27 -7
- package/src/components/breadcrumb/breadcrumb.stories.tsx +127 -4
- package/src/components/breadcrumb/breadcrumb.tsx +22 -8
- package/src/components/button/button.meta.md +258 -21
- package/src/components/button/button.stories.tsx +549 -41
- package/src/components/button/button.tsx +335 -33
- package/src/components/button/demo/as-child.tsx +24 -0
- package/src/components/button/demo/basic.tsx +8 -0
- package/src/components/button/demo/block.tsx +16 -0
- package/src/components/button/demo/loading.tsx +19 -0
- package/src/components/button/demo/shapes.tsx +18 -0
- package/src/components/button/demo/sizes.tsx +19 -0
- package/src/components/button/demo/variants.tsx +19 -0
- package/src/components/button/demo/with-icon.tsx +20 -0
- package/src/components/calendar/calendar.meta.md +13 -3
- package/src/components/calendar/calendar.stories.tsx +6 -6
- package/src/components/calendar/calendar.tsx +73 -8
- package/src/components/card/card.meta.md +27 -5
- package/src/components/card/card.stories.tsx +42 -3
- package/src/components/card/card.tsx +146 -63
- package/src/components/carousel/carousel.meta.md +4 -3
- package/src/components/carousel/carousel.stories.tsx +11 -6
- package/src/components/cascader/cascader.meta.md +47 -17
- package/src/components/cascader/cascader.stories.tsx +22 -10
- package/src/components/cascader/cascader.tsx +428 -85
- package/src/components/checkbox/checkbox.meta.md +75 -7
- package/src/components/checkbox/checkbox.stories.tsx +161 -3
- package/src/components/checkbox/checkbox.tsx +77 -9
- package/src/components/collapsible/collapsible.meta.md +14 -6
- package/src/components/collapsible/collapsible.stories.tsx +10 -2
- package/src/components/collapsible/collapsible.tsx +93 -6
- package/src/components/color-picker/color-picker.meta.md +12 -7
- package/src/components/color-picker/color-picker.stories.tsx +86 -7
- package/src/components/color-picker/color-picker.tsx +20 -9
- package/src/components/command/command.meta.md +29 -13
- package/src/components/command/command.stories.tsx +4 -4
- package/src/components/command/command.tsx +19 -8
- package/src/components/context-menu/context-menu.meta.md +11 -8
- package/src/components/context-menu/context-menu.stories.tsx +11 -3
- package/src/components/context-menu/context-menu.tsx +21 -8
- package/src/components/data-table/data-table.meta.md +6 -5
- package/src/components/data-table/data-table.stories.tsx +13 -6
- package/src/components/data-table/data-table.tsx +2 -2
- package/src/components/date-picker/date-picker.meta.md +88 -19
- package/src/components/date-picker/date-picker.stories.tsx +55 -5
- package/src/components/date-picker/date-picker.tsx +1489 -91
- package/src/components/descriptions/descriptions.meta.md +10 -5
- package/src/components/descriptions/descriptions.stories.tsx +3 -3
- package/src/components/descriptions/descriptions.tsx +22 -14
- package/src/components/dialog/dialog.meta.md +76 -13
- package/src/components/dialog/dialog.stories.tsx +182 -20
- package/src/components/dialog/dialog.tsx +67 -15
- package/src/components/dialog/imperative.tsx +252 -0
- package/src/components/drawer/drawer.meta.md +33 -34
- package/src/components/drawer/drawer.stories.tsx +29 -12
- package/src/components/drawer/drawer.tsx +22 -113
- package/src/components/dropdown-menu/dropdown-menu.meta.md +78 -10
- package/src/components/dropdown-menu/dropdown-menu.stories.tsx +88 -2
- package/src/components/dropdown-menu/dropdown-menu.tsx +24 -10
- package/src/components/ellipsis/ellipsis.meta.md +87 -0
- package/src/components/ellipsis/ellipsis.stories.tsx +72 -0
- package/src/components/ellipsis/ellipsis.tsx +153 -0
- package/src/components/empty/empty.meta.md +9 -4
- package/src/components/empty/empty.stories.tsx +4 -4
- package/src/components/empty/empty.tsx +10 -3
- package/src/components/field/field.meta.md +47 -9
- package/src/components/field/field.stories.tsx +385 -5
- package/src/components/field/field.tsx +263 -35
- package/src/components/filter-bar/filter-bar.meta.md +92 -0
- package/src/components/filter-bar/filter-bar.stories.tsx +1083 -0
- package/src/components/filter-bar/filter-bar.tsx +568 -0
- package/src/components/flex/flex.meta.md +54 -6
- package/src/components/flex/flex.stories.tsx +107 -20
- package/src/components/flex/flex.tsx +27 -4
- package/src/components/float-button/float-button.meta.md +8 -3
- package/src/components/float-button/float-button.stories.tsx +9 -7
- package/src/components/float-button/float-button.tsx +1 -1
- package/src/components/form/form.meta.md +39 -17
- package/src/components/form/form.stories.tsx +350 -3
- package/src/components/form/form.tsx +101 -35
- package/src/components/grid/grid.meta.md +7 -2
- package/src/components/grid/grid.stories.tsx +6 -4
- package/src/components/hover-card/hover-card.meta.md +20 -9
- package/src/components/hover-card/hover-card.stories.tsx +34 -5
- package/src/components/hover-card/hover-card.tsx +51 -13
- package/src/components/icon/DEVELOPMENT.md +809 -0
- package/src/components/icon/icon.meta.md +170 -0
- package/src/components/icon/icon.stories.tsx +344 -0
- package/src/components/icon/icon.tsx +248 -0
- package/src/components/image/image.meta.md +9 -4
- package/src/components/image/image.stories.tsx +3 -3
- package/src/components/image/image.tsx +6 -4
- package/src/components/input/demo/basic.tsx +12 -0
- package/src/components/input/demo/clearable.tsx +21 -0
- package/src/components/input/demo/show-count.tsx +18 -0
- package/src/components/input/demo/sizes.tsx +15 -0
- package/src/components/input/input.meta.md +39 -33
- package/src/components/input/input.stories.tsx +62 -35
- package/src/components/input/input.tsx +97 -98
- package/src/components/input-group/input-group.meta.md +54 -22
- package/src/components/input-group/input-group.stories.tsx +49 -16
- package/src/components/input-group/input-group.tsx +44 -8
- package/src/components/input-number/input-number.meta.md +64 -7
- package/src/components/input-number/input-number.stories.tsx +46 -8
- package/src/components/input-number/input-number.tsx +99 -26
- package/src/components/input-otp/input-otp.meta.md +4 -3
- package/src/components/input-otp/input-otp.stories.tsx +3 -3
- package/src/components/input-otp/input-otp.tsx +1 -1
- package/src/components/item/item.meta.md +8 -3
- package/src/components/item/item.stories.tsx +8 -5
- package/src/components/item/item.tsx +7 -6
- package/src/components/kbd/kbd.meta.md +13 -4
- package/src/components/kbd/kbd.stories.tsx +4 -4
- package/src/components/kbd/kbd.tsx +10 -5
- package/src/components/label/label.meta.md +18 -10
- package/src/components/label/label.stories.tsx +64 -6
- package/src/components/label/label.tsx +91 -19
- package/src/components/masonry/masonry.meta.md +8 -3
- package/src/components/masonry/masonry.stories.tsx +7 -5
- package/src/components/masonry/masonry.tsx +1 -0
- package/src/components/mentions/mentions.meta.md +36 -6
- package/src/components/mentions/mentions.stories.tsx +120 -6
- package/src/components/mentions/mentions.tsx +11 -5
- package/src/components/menubar/menubar.meta.md +30 -12
- package/src/components/menubar/menubar.stories.tsx +62 -2
- package/src/components/menubar/menubar.tsx +9 -9
- package/src/components/native-select/native-select.meta.md +8 -3
- package/src/components/native-select/native-select.stories.tsx +8 -5
- package/src/components/native-select/native-select.tsx +1 -1
- package/src/components/navigation-menu/navigation-menu.meta.md +19 -9
- package/src/components/navigation-menu/navigation-menu.stories.tsx +112 -9
- package/src/components/navigation-menu/navigation-menu.tsx +8 -4
- package/src/components/notification/notification.meta.md +52 -10
- package/src/components/notification/notification.stories.tsx +11 -9
- package/src/components/notification/notification.tsx +36 -21
- package/src/components/page-header/DEVELOPMENT.md +842 -0
- package/src/components/page-header/page-header.meta.md +208 -0
- package/src/components/page-header/page-header.stories.tsx +421 -0
- package/src/components/page-header/page-header.tsx +281 -0
- package/src/components/pagination/pagination.meta.md +140 -37
- package/src/components/pagination/pagination.stories.tsx +232 -10
- package/src/components/pagination/pagination.tsx +355 -63
- package/src/components/popconfirm/popconfirm.meta.md +9 -4
- package/src/components/popconfirm/popconfirm.stories.tsx +3 -4
- package/src/components/popconfirm/popconfirm.tsx +2 -2
- package/src/components/popover/popover.meta.md +62 -5
- package/src/components/popover/popover.stories.tsx +83 -7
- package/src/components/popover/popover.tsx +77 -28
- package/src/components/progress/progress.meta.md +38 -6
- package/src/components/progress/progress.stories.tsx +3 -3
- package/src/components/progress/progress.tsx +24 -16
- package/src/components/radio-group/radio-group.meta.md +79 -7
- package/src/components/radio-group/radio-group.stories.tsx +39 -3
- package/src/components/radio-group/radio-group.tsx +149 -18
- package/src/components/rate/rate.meta.md +35 -4
- package/src/components/rate/rate.stories.tsx +13 -5
- package/src/components/rate/rate.tsx +37 -10
- package/src/components/resizable/resizable.meta.md +7 -4
- package/src/components/resizable/resizable.stories.tsx +6 -6
- package/src/components/resizable/resizable.tsx +1 -1
- package/src/components/result/result.meta.md +7 -2
- package/src/components/result/result.stories.tsx +4 -8
- package/src/components/result/result.tsx +24 -15
- package/src/components/scroll-area/scroll-area.meta.md +4 -3
- package/src/components/scroll-area/scroll-area.stories.tsx +12 -4
- package/src/components/scroll-area/scroll-area.tsx +3 -3
- package/src/components/segmented/segmented.meta.md +7 -4
- package/src/components/segmented/segmented.stories.tsx +37 -8
- package/src/components/segmented/segmented.tsx +15 -7
- package/src/components/select/select.meta.md +197 -52
- package/src/components/select/select.stories.tsx +238 -63
- package/src/components/select/select.tsx +718 -171
- package/src/components/separator/separator.meta.md +4 -3
- package/src/components/separator/separator.stories.tsx +3 -3
- package/src/components/separator/separator.tsx +3 -7
- package/src/components/sheet/sheet.meta.md +32 -16
- package/src/components/sheet/sheet.stories.tsx +116 -10
- package/src/components/sheet/sheet.tsx +116 -29
- package/src/components/sidebar/sidebar.meta.md +37 -18
- package/src/components/sidebar/sidebar.stories.tsx +701 -29
- package/src/components/sidebar/sidebar.tsx +615 -142
- package/src/components/skeleton/skeleton.meta.md +4 -5
- package/src/components/skeleton/skeleton.stories.tsx +4 -4
- package/src/components/skeleton/skeleton.tsx +7 -7
- package/src/components/slider/slider.meta.md +57 -5
- package/src/components/slider/slider.stories.tsx +58 -6
- package/src/components/slider/slider.tsx +154 -13
- package/src/components/sonner/sonner.meta.md +58 -7
- package/src/components/sonner/sonner.stories.tsx +78 -5
- package/src/components/sonner/sonner.tsx +137 -8
- package/src/components/spinner/spinner.meta.md +62 -13
- package/src/components/spinner/spinner.stories.tsx +66 -14
- package/src/components/spinner/spinner.tsx +111 -9
- package/src/components/statistic/statistic.meta.md +7 -2
- package/src/components/statistic/statistic.stories.tsx +3 -7
- package/src/components/statistic/statistic.tsx +5 -6
- package/src/components/steps/steps.meta.md +18 -4
- package/src/components/steps/steps.stories.tsx +43 -3
- package/src/components/steps/steps.tsx +15 -12
- package/src/components/switch/switch.meta.md +51 -5
- package/src/components/switch/switch.stories.tsx +6 -6
- package/src/components/switch/switch.tsx +109 -41
- package/src/components/table/table.meta.md +17 -6
- package/src/components/table/table.stories.tsx +10 -5
- package/src/components/table/table.tsx +4 -4
- package/src/components/tabs/tabs.meta.md +38 -25
- package/src/components/tabs/tabs.stories.tsx +111 -25
- package/src/components/tabs/tabs.tsx +125 -54
- package/src/components/tag/tag.meta.md +105 -40
- package/src/components/tag/tag.stories.tsx +189 -16
- package/src/components/tag/tag.tsx +222 -21
- package/src/components/textarea/textarea.meta.md +35 -19
- package/src/components/textarea/textarea.stories.tsx +32 -6
- package/src/components/textarea/textarea.tsx +33 -9
- package/src/components/time-picker/time-picker.meta.md +124 -32
- package/src/components/time-picker/time-picker.stories.tsx +85 -15
- package/src/components/time-picker/time-picker.tsx +913 -61
- package/src/components/timeline/timeline.meta.md +14 -6
- package/src/components/timeline/timeline.stories.tsx +37 -7
- package/src/components/timeline/timeline.tsx +35 -14
- package/src/components/toggle/toggle.meta.md +5 -4
- package/src/components/toggle/toggle.stories.tsx +4 -4
- package/src/components/toggle/toggle.tsx +4 -3
- package/src/components/toggle-group/toggle-group.meta.md +5 -4
- package/src/components/toggle-group/toggle-group.stories.tsx +3 -3
- package/src/components/toggle-group/toggle-group.tsx +2 -2
- package/src/components/tooltip/tooltip.meta.md +55 -5
- package/src/components/tooltip/tooltip.stories.tsx +42 -5
- package/src/components/tooltip/tooltip.tsx +81 -21
- package/src/components/tour/tour.meta.md +9 -4
- package/src/components/tour/tour.stories.tsx +3 -3
- package/src/components/tour/tour.tsx +4 -4
- package/src/components/transfer/transfer.meta.md +11 -6
- package/src/components/transfer/transfer.stories.tsx +4 -8
- package/src/components/transfer/transfer.tsx +28 -21
- package/src/components/tree/tree.meta.md +63 -5
- package/src/components/tree/tree.stories.tsx +31 -12
- package/src/components/tree/tree.tsx +9 -8
- package/src/components/tree-select/tree-select.meta.md +59 -8
- package/src/components/tree-select/tree-select.stories.tsx +3 -3
- package/src/components/tree-select/tree-select.tsx +42 -7
- package/src/components/typography/typography.meta.md +61 -14
- package/src/components/typography/typography.stories.tsx +12 -11
- package/src/components/typography/typography.tsx +43 -28
- package/src/components/upload/upload.meta.md +49 -4
- package/src/components/upload/upload.stories.tsx +72 -12
- package/src/components/upload/upload.tsx +170 -37
- package/src/components/watermark/watermark.meta.md +7 -2
- package/src/components/watermark/watermark.stories.tsx +101 -9
- package/src/components/watermark/watermark.tsx +1 -0
- package/src/hooks/use-breakpoint.ts +117 -0
- package/src/hooks/use-debounce-callback.ts +52 -0
- package/src/hooks/use-mobile.ts +23 -0
- package/src/stories/theme-tokens.stories.tsx +747 -0
- package/src/utils/trigger-input.ts +53 -0
- package/src/components/button-group/button-group.meta.md +0 -92
- package/src/components/button-group/button-group.stories.tsx +0 -90
- package/src/components/button-group/button-group.tsx +0 -75
- package/src/components/combobox/combobox.meta.md +0 -93
- package/src/components/combobox/combobox.stories.tsx +0 -55
- package/src/components/combobox/combobox.tsx +0 -130
- package/src/components/space/space.meta.md +0 -94
- package/src/components/space/space.stories.tsx +0 -94
- package/src/components/space/space.tsx +0 -106
|
@@ -0,0 +1,842 @@
|
|
|
1
|
+
# PageHeader 组件研发文档
|
|
2
|
+
|
|
3
|
+
> 本文档为 `@teamix-evo/ui` PageHeader 组件的完整研发分析,基于 teamix-pro 中 `ProPageHeader` 的功能 fork,按照 ui 库第零条准则(能力做并集,理念跟 design,工程跟 shadcn)重新设计实现。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 一、源头分析 — ProPageHeader(teamix-pro)
|
|
8
|
+
|
|
9
|
+
### 1.1 原始组件定位
|
|
10
|
+
|
|
11
|
+
ProPageHeader 是面向控制台/中后台场景的页头组件,一般用于详情页、表单页或列表页的顶部区域,承载以下信息层次:
|
|
12
|
+
|
|
13
|
+
| 层次 | 内容 | 原始实现位置 |
|
|
14
|
+
| ---------- | ------------------------------------ | --------------------------------- |
|
|
15
|
+
| 导航层 | 面包屑 + 右上角额外链接 | 第一层 section (nav + extra) |
|
|
16
|
+
| 标题层 | 返回按钮 + 图标 + 标题 + 标签 + Tabs | 第二层 section left (title) |
|
|
17
|
+
| 描述层 | 描述文案 | 第二层 section left (description) |
|
|
18
|
+
| 数据概览层 | 统计卡片组 | 第二层 section right (data) |
|
|
19
|
+
| 操作层 | 按钮组 | 第二层 section right (operation) |
|
|
20
|
+
| 详情层 | 结构化信息面板 | 第三层 section (info) |
|
|
21
|
+
| 背景装饰层 | 右下角背景图 | header style backgroundImage |
|
|
22
|
+
|
|
23
|
+
### 1.2 原始 Props 清单
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
type ProPageHeaderProps = {
|
|
27
|
+
title?: React.ReactNode;
|
|
28
|
+
titleTooltip?: React.ReactNode;
|
|
29
|
+
description?: React.ReactNode;
|
|
30
|
+
icon?: React.ReactNode;
|
|
31
|
+
iconColor?: 'blue' | 'green' | 'orange' | 'red' | 'yellow' | string;
|
|
32
|
+
iconBackgroundType?: 'circle' | 'square';
|
|
33
|
+
iconBackgroundColor?: Color;
|
|
34
|
+
goback?: boolean | LinkPath | ((e: React.MouseEvent) => void);
|
|
35
|
+
image?: string;
|
|
36
|
+
loading?: boolean;
|
|
37
|
+
breadcrumb?: ProPageHeaderBreadcrumbItem[];
|
|
38
|
+
extra?: ProActionGroupProps | React.ReactNode | any[];
|
|
39
|
+
operation?: ProActionGroupProps | React.ReactNode | any[];
|
|
40
|
+
tags?: ProPageHeaderTagItem[];
|
|
41
|
+
data?: ProPageHeaderDataItem[];
|
|
42
|
+
info?: ProInfoProps | React.ReactNode;
|
|
43
|
+
tabs?: CapsuleTabType;
|
|
44
|
+
} & Omit<React.HTMLAttributes<HTMLElement>, 'title'>;
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 1.3 原始依赖关系
|
|
48
|
+
|
|
49
|
+
| 依赖组件 | 来源 | 作用 |
|
|
50
|
+
| --------------------- | -------------------------- | ---------- |
|
|
51
|
+
| Breadcrumb | @ali/alicloudfe-components | 面包屑导航 |
|
|
52
|
+
| TeamixIcon | @ali/teamix-icon | 图标渲染 |
|
|
53
|
+
| Tooltip/Balloon | @ali/alicloudfe-components | 标题提示 |
|
|
54
|
+
| ProActionGroup | ../actions | 按钮操作组 |
|
|
55
|
+
| ProInfo | ../info | 详情面板 |
|
|
56
|
+
| CapsuleTab | ../field | 选项卡切换 |
|
|
57
|
+
| ProSkeletonRaw | ../skeleton | 骨架屏加载 |
|
|
58
|
+
| ProTagItem/renderTags | ../field | 标签渲染 |
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 二、第零条准则对照表(三层对齐)
|
|
63
|
+
|
|
64
|
+
### 2.1 能力对标 — shadcn ∪ antd 并集
|
|
65
|
+
|
|
66
|
+
> shadcn 没有 PageHeader 原子组件,antd 在 v5 已废弃 `PageHeader` 并移至 `@ant-design/pro-components`。本组件属于 **antd 有对标(ProComponents)、shadcn 无对标** 的类型,功能全面承接 antd ProPageHeader。
|
|
67
|
+
|
|
68
|
+
| 能力维度 | antd/Pro 提供 | shadcn 提供 | 本组件实现 |
|
|
69
|
+
| ---------- | ----------------------------- | ----------- | ----------------------------------- |
|
|
70
|
+
| 标题 | ✅ title + subTitle | ❌ | ✅ title(ReactNode 自由渲染) |
|
|
71
|
+
| 标题提示 | ❌ | ❌ | ✅ titleTooltip(teamix-pro 特色) |
|
|
72
|
+
| 返回按钮 | ✅ onBack | ❌ | ✅ goBack(多态:布尔/路径/函数) |
|
|
73
|
+
| 面包屑 | ✅ breadcrumb (routes) | ❌ | ✅ breadcrumb(复用 ui/breadcrumb) |
|
|
74
|
+
| 标签组 | ✅ tags | ❌ | ✅ tags(复用 ui/tag) |
|
|
75
|
+
| 额外操作 | ✅ extra | ❌ | ✅ extra(ReactNode,自由度更高) |
|
|
76
|
+
| 描述 | ❌(ProComponents 有) | ❌ | ✅ description |
|
|
77
|
+
| 图标 | ❌(ProComponents 有 avatar) | ❌ | ✅ icon + iconVariant |
|
|
78
|
+
| 数据概览 | ❌(ProComponents 有) | ❌ | ✅ data(统计卡片组) |
|
|
79
|
+
| 操作按钮组 | ✅ extra (antd 统一放 extra) | ❌ | ✅ actions(主操作区) |
|
|
80
|
+
| 详情面板 | ❌(ProComponents 有) | ❌ | ✅ footer(ReactNode) |
|
|
81
|
+
| Tabs 切换 | ✅ tabList + tabActiveKey | ❌ | ✅ tabs(复用 ui/tabs) |
|
|
82
|
+
| 背景装饰图 | ❌(teamix-pro 特色) | ❌ | ✅ backgroundImage |
|
|
83
|
+
| 加载骨架屏 | ❌(ProComponents 有) | ❌ | ✅ loading(复用 ui/skeleton) |
|
|
84
|
+
| 响应式 | ❌ | ❌ | ✅ 通过 Tailwind 响应式 utilities |
|
|
85
|
+
|
|
86
|
+
### 2.2 设计理念 — 跟 `@teamix-evo/tokens` OpenTrek 体系
|
|
87
|
+
|
|
88
|
+
- 颜色走 semantic tokens:`text-foreground`、`text-muted-foreground`、`bg-muted`、`border-border` 等
|
|
89
|
+
- 间距走 4px 倍数体系:`gap-2`(8px)、`gap-4`(16px)、`p-6`(24px) 等
|
|
90
|
+
- 字号走 tokens 档位:标题 `text-2xl`(24px)、描述 `text-sm`(14px)、数据标题 `text-xs`(12px)
|
|
91
|
+
- 圆角走五档:图标 `rounded-md`(square) / `rounded-full`(circle)
|
|
92
|
+
- 不使用 ProPageHeader 原有的 CSS 变量体系(`--color-text1-*` / `--s-*` / `--font-size-*`)
|
|
93
|
+
- 不照搬 antd 或 teamix-pro 的蓝/绿/红预设色,改用 semantic color system
|
|
94
|
+
|
|
95
|
+
### 2.3 代码实现 — 跟 shadcn
|
|
96
|
+
|
|
97
|
+
- **文件结构**:`src/components/page-header/page-header.tsx` + `.meta.md` + `.stories.tsx`
|
|
98
|
+
- **cva 范式**:图标变体(颜色/形状)用 `cva`
|
|
99
|
+
- **forwardRef**:所有导出组件用 `React.forwardRef`
|
|
100
|
+
- **假路径 import**:`@/utils/cn`、`@/components/xxx`
|
|
101
|
+
- **TypeScript strict**:所有 props 有 JSDoc + `@default`
|
|
102
|
+
- **Tailwind CSS**:所有样式通过 Tailwind utilities + semantic tokens
|
|
103
|
+
- **无运行时主题**:不建 ConfigProvider,不引入 less/scss
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 三、组件拆分设计
|
|
108
|
+
|
|
109
|
+
### 3.1 复合组件结构(Compound Components 模式)
|
|
110
|
+
|
|
111
|
+
遵循 shadcn 风格(对标 Card / Dialog / Command 的复合拆分思路),纯组合式导出,**不保留 ProPageHeader 的配置式能力**,用户自由组装:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
// ─── 导出清单 ────────────────────────────────────────────────────
|
|
115
|
+
export { PageHeader }; // 顶层容器 <header>,负责背景图 + 间距
|
|
116
|
+
export { PageHeaderNav }; // 导航层:面包屑 + 右侧额外链接
|
|
117
|
+
export { PageHeaderHeading }; // 标题区域:返回 + Icon + 标题文字 + tooltip + tags
|
|
118
|
+
export { PageHeaderDescription }; // 描述文字
|
|
119
|
+
export { PageHeaderActions }; // 操作按钮区(右对齐)
|
|
120
|
+
export { PageHeaderContent }; // 中间主区域容器(left + right flex 布局)
|
|
121
|
+
export { PageHeaderData }; // 数据概览容器
|
|
122
|
+
export { PageHeaderDataItem }; // 单个数据项(label + value)
|
|
123
|
+
export { PageHeaderFooter }; // 底部区域(详情/自定义内容)
|
|
124
|
+
export { PageHeaderSkeleton }; // 整体骨架屏(loading 态一键替换)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**设计哲学**:每个子组件只做一件事(布局 or 语义),不接受配置对象。用户用 `<Breadcrumb>` / `<Tag>` / `<Button>` 等原子组件自行填充内容,PageHeader 只提供**排版骨架**。
|
|
128
|
+
|
|
129
|
+
### 3.2 复用的原子组件映射
|
|
130
|
+
|
|
131
|
+
| ProPageHeader 原始依赖 | teamix-evo/ui 对应组件 | 复用方式 |
|
|
132
|
+
| ---------------------- | ---------------------------- | ----------------------------------- |
|
|
133
|
+
| Breadcrumb | `@/components/breadcrumb` | 直接 import + 组合 |
|
|
134
|
+
| TeamixIcon | `lucide-react` | 替换为 lucide 图标 |
|
|
135
|
+
| Tooltip/Balloon | `@/components/tooltip` | 直接 import |
|
|
136
|
+
| ProActionGroup | `@/components/button` + Slot | 不封装 ActionGroup,用户传 ReactNode |
|
|
137
|
+
| ProSkeletonRaw | `@/components/skeleton` | 直接 import + 组合 |
|
|
138
|
+
| ProTagItem/renderTags | `@/components/tag` | 直接 import |
|
|
139
|
+
| CapsuleTab | `@/components/tabs` | 用 Tabs 组件(Segmented 变体) |
|
|
140
|
+
| ProInfo | `@/components/descriptions` | 复用 Descriptions 组件 |
|
|
141
|
+
| classnames | `@/utils/cn` | 用 cn() 替代 |
|
|
142
|
+
|
|
143
|
+
### 3.3 关键设计决策
|
|
144
|
+
|
|
145
|
+
| 决策点 | teamix-pro 做法 | teamix-evo/ui 做法 | 原因 |
|
|
146
|
+
| -------------- | --------------------------------------- | ------------------------------------------------ | --------------------------------------------- |
|
|
147
|
+
| 整体范式 | 单一巨组件 + 配置对象驱动 | 纯复合组件,用户组装 | shadcn 理念 = composable, 不做 props 配置封装 |
|
|
148
|
+
| 操作区 API | `ProActionGroupProps` 配置式 | children 直接传 Button | 无需二次抽象,用户自由度最大 |
|
|
149
|
+
| 面包屑 API | `breadcrumb: BreadcrumbItem[]` 配置数组 | 用户直接用 `<Breadcrumb>` 组合 | 复用已有原子组件,零额外 API |
|
|
150
|
+
| 标签 API | `tags: TagItem[]` 配置数组 | 用户在 Heading 内放 `<Tag>` | 同上 |
|
|
151
|
+
| 数据概览 | `data: DataItem[]` 配置数组 | `<PageHeaderData>` + `<PageHeaderDataItem>` 复合 | 保留语义容器但不传配置对象 |
|
|
152
|
+
| 图标系统 | `TeamixIcon`(字符串 type) | `lucide-react`(ReactNode 直接传入) | 工程约束: icon 固定来源 lucide-react |
|
|
153
|
+
| 颜色预设 | 字面色名 `blue/green/red/yellow` | 语义色 `primary/success/warning/error/info` | 对齐 design tokens semantic system |
|
|
154
|
+
| 路由依赖 | `useRouterHistory` + `goToLink` | 纯回调 `onBack`,不内置路由 | 源码注入式无法假设路由库 |
|
|
155
|
+
| 全局 props HOC | `withGlobalProps` | 不需要 | 无运行时主题体系 |
|
|
156
|
+
| 样式方案 | SCSS + CSS 变量 | Tailwind utilities + cn() | 工程跟 shadcn |
|
|
157
|
+
| 骨架屏 | 内置专属骨架子组件 | `<PageHeaderSkeleton>` 组合 Skeleton 原子 | 复用原子组件,不重复造轮子 |
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## 四、Props 接口设计(纯复合组件)
|
|
162
|
+
|
|
163
|
+
每个子组件只暴露自身需要的 props,**不接收配置对象、不做内部循环渲染**。
|
|
164
|
+
|
|
165
|
+
### 4.1 PageHeader — 顶层容器
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
export interface PageHeaderProps extends React.HTMLAttributes<HTMLElement> {
|
|
169
|
+
/**
|
|
170
|
+
* 右下角背景装饰图 URL。通过 inline style 注入。
|
|
171
|
+
*/
|
|
172
|
+
backgroundImage?: string;
|
|
173
|
+
/** 子内容(复合子组件)。 */
|
|
174
|
+
children?: React.ReactNode;
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### 4.2 PageHeaderNav — 导航层
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
export interface PageHeaderNavProps
|
|
182
|
+
extends React.HTMLAttributes<HTMLDivElement> {
|
|
183
|
+
/** 导航层内容(通常左侧放 Breadcrumb,右侧放额外链接)。 */
|
|
184
|
+
children?: React.ReactNode;
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### 4.3 PageHeaderHeading — 标题区域
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
export interface PageHeaderHeadingProps
|
|
192
|
+
extends React.HTMLAttributes<HTMLDivElement> {
|
|
193
|
+
/**
|
|
194
|
+
* 前置返回按钮。为 `true` 时渲染 ArrowLeft icon,点击触发 `onBack`。
|
|
195
|
+
* @default false
|
|
196
|
+
*/
|
|
197
|
+
goBack?: boolean;
|
|
198
|
+
/** 返回按钮点击回调。 */
|
|
199
|
+
onBack?: (e: React.MouseEvent) => void;
|
|
200
|
+
/** 标题内容。建议用 `<Icon>` + `<Title level={2}>` + `<Tag>` 等原子组件自由组合。 */
|
|
201
|
+
children?: React.ReactNode;
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
> **标题文字规范**:推荐用户在 `<PageHeaderHeading>` 内使用 `<Title level={2}>` 组件(来自 `@/components/typography`)作为标题文字载体,对应 `text-2xl font-semibold tracking-tight` —— 这与原 ProPageHeader 的 24px/500 标题视觉一致,同时复用 Typography 原子组件的语义化 HTML 标签(`<h2>`)。`PageHeaderHeading` 自身不强制字号,只做 flex 布局容器。
|
|
206
|
+
>
|
|
207
|
+
> **图标规范**:图标展示(含背景色/形状/尺寸变体)由后续独立的 `<Icon>` 原子组件统一实现。PageHeader 不内置图标逻辑,用户在 children 中放置 `<Icon>` 组件即可。`<Icon>` 将承载 cva 变体(color / shape / withBackground)。
|
|
208
|
+
|
|
209
|
+
### 4.4 PageHeaderDescription — 描述文字
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
export interface PageHeaderDescriptionProps
|
|
213
|
+
extends React.HTMLAttributes<HTMLParagraphElement> {
|
|
214
|
+
children?: React.ReactNode;
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### 4.5 PageHeaderContent — 主内容区容器
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
/** 包裹 Heading + Description(左) 与 Actions + Data(右) 的 flex 容器。 */
|
|
222
|
+
export interface PageHeaderContentProps
|
|
223
|
+
extends React.HTMLAttributes<HTMLDivElement> {
|
|
224
|
+
children?: React.ReactNode;
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### 4.6 PageHeaderActions — 操作按钮区
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
export interface PageHeaderActionsProps
|
|
232
|
+
extends React.HTMLAttributes<HTMLDivElement> {
|
|
233
|
+
/** 操作按钮(直接放 <Button> 组件)。 */
|
|
234
|
+
children?: React.ReactNode;
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### 4.7 PageHeaderData — 数据概览容器
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
export interface PageHeaderDataProps
|
|
242
|
+
extends React.HTMLAttributes<HTMLDivElement> {
|
|
243
|
+
children?: React.ReactNode;
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### 4.8 PageHeaderDataItem — 单个数据项
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
export interface PageHeaderDataItemProps
|
|
251
|
+
extends React.HTMLAttributes<HTMLDivElement> {
|
|
252
|
+
/** 数据标题/标签。 */
|
|
253
|
+
label: React.ReactNode;
|
|
254
|
+
/**
|
|
255
|
+
* 值的语义色。
|
|
256
|
+
* @default "default"
|
|
257
|
+
*/
|
|
258
|
+
color?: 'default' | 'primary' | 'success' | 'warning' | 'error' | 'info';
|
|
259
|
+
/** 数据值(children)。 */
|
|
260
|
+
children?: React.ReactNode;
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### 4.9 PageHeaderFooter — 底部区域
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
export interface PageHeaderFooterProps
|
|
268
|
+
extends React.HTMLAttributes<HTMLDivElement> {
|
|
269
|
+
children?: React.ReactNode;
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### 4.10 PageHeaderSkeleton — 骨架屏
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
export interface PageHeaderSkeletonProps
|
|
277
|
+
extends React.HTMLAttributes<HTMLDivElement> {
|
|
278
|
+
/** 是否显示面包屑骨架行。 @default true */
|
|
279
|
+
showNav?: boolean;
|
|
280
|
+
/** 是否显示描述骨架行。 @default true */
|
|
281
|
+
showDescription?: boolean;
|
|
282
|
+
/** 是否显示底部详情骨架。 @default false */
|
|
283
|
+
showFooter?: boolean;
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## 五、渲染结构设计
|
|
290
|
+
|
|
291
|
+
每个子组件对应一个 HTML 节点,用户自由组装。以下是推荐的典型组合结构:
|
|
292
|
+
|
|
293
|
+
```
|
|
294
|
+
<PageHeader> <!-- <header> 容器, 背景图 -->
|
|
295
|
+
├── <PageHeaderNav> <!-- flex justify-between 导航层 -->
|
|
296
|
+
│ ├── <Breadcrumb>...</Breadcrumb> <!-- 用户自己组装原子 Breadcrumb -->
|
|
297
|
+
│ └── <div>...</div> <!-- 右侧额外链接 / 操作 -->
|
|
298
|
+
│
|
|
299
|
+
├── <PageHeaderContent> <!-- flex justify-between 主内容层 -->
|
|
300
|
+
│ ├── <div.left> <!-- 左侧: heading + description -->
|
|
301
|
+
│ │ ├── <PageHeaderHeading> <!-- flex items-center 标题行 -->
|
|
302
|
+
│ │ │ ├── [ArrowLeft goBack] <!-- 返回按钮 -->
|
|
303
|
+
│ │ │ ├── [icon + bg] <!-- 用户传入的图标 -->
|
|
304
|
+
│ │ │ ├── {children} <!-- 标题文字 + Tooltip + Tag 等 -->
|
|
305
|
+
│ │ │ └── ...
|
|
306
|
+
│ │ └── <PageHeaderDescription> <!-- <p> 描述 -->
|
|
307
|
+
│ └── <div.right> <!-- 右侧: data + actions -->
|
|
308
|
+
│ ├── <PageHeaderData> <!-- flex gap 数据概览 -->
|
|
309
|
+
│ │ ├── <PageHeaderDataItem>
|
|
310
|
+
│ │ └── ...
|
|
311
|
+
│ └── <PageHeaderActions> <!-- flex gap 操作按钮 -->
|
|
312
|
+
│ └── <Button>... × N
|
|
313
|
+
│
|
|
314
|
+
├── <PageHeaderFooter> <!-- 底部区域 -->
|
|
315
|
+
│ └── <Descriptions>...</Descriptions> <!-- 或任意自定义内容 -->
|
|
316
|
+
│
|
|
317
|
+
└── {children} <!-- 其他自定义内容 -->
|
|
318
|
+
</PageHeader>
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
> **组合自由度**:用户可以省略任何子组件(不需要面包屑就不放 `<PageHeaderNav>`,不需要数据概览就不放 `<PageHeaderData>`)。各子组件**不做内部条件渲染**,存在即渲染。
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## 5.1 视觉参考拆解(基于 ProPageHeader 实际截图)
|
|
326
|
+
|
|
327
|
+

|
|
328
|
+
|
|
329
|
+
从截图中提取的关键视觉规则:
|
|
330
|
+
|
|
331
|
+
| 区域 | 视觉特征 | 实现要点 |
|
|
332
|
+
| -------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
|
|
333
|
+
| **导航层** | 面包屑用 `/` 分隔符,链接文字与普通文字色差明显;右侧“帮助文档”是带图标的文字链接(非按钮) | `<PageHeaderNav>` 左右两端 flex justify-between;右侧用 variant="link" Button 或纯 anchor |
|
|
334
|
+
| **返回箭头** | 与图标、标题同行垂直居中,尺寸约 20px,无背景,cursor pointer | `ArrowLeft` from lucide, `size-5`, 无 padding/bg |
|
|
335
|
+
| **标题图标** | 蓝色方形背景(`rounded-md`) + 白色图标,背景尺寸约 36×36px,图标居中 | `iconBackground` + `iconColor="primary"` → `bg-primary text-primary-foreground rounded-md p-1.5` |
|
|
336
|
+
| **标题文字** | 24px / font-weight 500~600 / 黑色,与图标、Tag 垂直居中 | `<Title level={2}>` 提供 `text-2xl font-semibold tracking-tight` |
|
|
337
|
+
| **标签组** | 与标题同行,间距约 8px;标签三 = filled green / outline green / outline red | 用户在 `<PageHeaderHeading>` children 中直接放 `<Tag>` 组件 |
|
|
338
|
+
| **操作按钮区** | 右对齐,outline(普通) + primary(主操作) 并排,带图标 | `<PageHeaderActions>` 内放 `<Button>` 组件,gap-2 |
|
|
339
|
+
| **描述文字** | 单独一行,muted 灰色(#5a5a5a),字号 14px | `<PageHeaderDescription>` → `text-sm text-muted-foreground` |
|
|
340
|
+
| **背景装饰图** | 右下角半透明装饰图,不影响内容层 | `backgroundImage` prop, `bg-right-bottom bg-no-repeat` |
|
|
341
|
+
| **层级间距** | 导航层 → 标题层 ≈ 8px(mb-2),标题层 → 描述层 ≈ 8px(mt-2) | 各子组件自带 margin 或父容器用 `space-y-2` |
|
|
342
|
+
|
|
343
|
+
### 图标背景色的视觉修正
|
|
344
|
+
|
|
345
|
+
截图中图标背景是**实心蓝色 + 白色 icon**(而非淡蓝背景 + 蓝色 icon)。这意味着 `iconBackground` 为 true 时的 compoundVariant 应该考虑两种模式:
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
// 模式 A: 淡色背景 + 深色 icon (subtle)
|
|
349
|
+
compoundVariants: [
|
|
350
|
+
{ color: 'primary', withBackground: true, class: 'bg-primary/10 text-primary' },
|
|
351
|
+
...
|
|
352
|
+
]
|
|
353
|
+
|
|
354
|
+
// 模式 B: 实心背景 + 白色 icon (solid) — 截图中是这种
|
|
355
|
+
compoundVariants: [
|
|
356
|
+
{ color: 'primary', withBackground: true, class: 'bg-primary text-primary-foreground' },
|
|
357
|
+
{ color: 'success', withBackground: true, class: 'bg-success text-white' },
|
|
358
|
+
{ color: 'warning', withBackground: true, class: 'bg-warning text-white' },
|
|
359
|
+
{ color: 'error', withBackground: true, class: 'bg-destructive text-white' },
|
|
360
|
+
...
|
|
361
|
+
]
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
建议默认采用 **模式 B(实心背景)** 对齐截图视觉,同时支持通过 className 覆盖为 subtle 模式。
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## 5.2 更多变体模式视觉参考
|
|
369
|
+
|
|
370
|
+
### 变体 A: Tabs 切换模式
|
|
371
|
+
|
|
372
|
+

|
|
373
|
+
|
|
374
|
+
视觉特征:
|
|
375
|
+
|
|
376
|
+
- **无图标背景**:返回箭头 + 标题文字后直接跟图标切换按钮
|
|
377
|
+
- **图标 Tabs**:3 个 icon-only 切换按钮,用 `|` 分隔符隔开,形态类似 ToggleGroup + Separator
|
|
378
|
+
- **标题行与 Tabs 同层**:所有元素在同一个 flex 行内垂直居中
|
|
379
|
+
- **底部无分割线**:PageHeader 本体不带 `border-b`,与 teamix-pro ProPageHeader 行为对齐;如需与下方内容划分,由页面侧自行补充分隔
|
|
380
|
+
|
|
381
|
+
实现要点:
|
|
382
|
+
|
|
383
|
+
- 用户在 `<PageHeaderHeading>` children 中放置 `<ToggleGroup>` 或 `<Separator>` + icons 组合
|
|
384
|
+
- PageHeader 容器不内置 Tabs 逻辑,纯 composable
|
|
385
|
+
|
|
386
|
+
### 变体 B: 数据概览模式
|
|
387
|
+
|
|
388
|
+

|
|
389
|
+
|
|
390
|
+
视觉特征:
|
|
391
|
+
|
|
392
|
+
- **无导航层 / 无返回 / 无图标**:只有标题 + 描述 + 数据
|
|
393
|
+
- **左右分层布局**:左侧 = 标题(粗体 24px) + 描述(灰色 14px);右侧 = 数据概览卡片组
|
|
394
|
+
- **数据项布局**:每项上下结构 — label(上,灰色小字 12px) + value(下,黑色粗体 16px)
|
|
395
|
+
- **彩色值**:“停止”的数值为红色(`text-destructive`)
|
|
396
|
+
- **数据项水平排列**:5 项等距排开,间距约 24px(gap-6)
|
|
397
|
+
- **数字等宽**:使用 `tabular-nums` 保证对齐
|
|
398
|
+
|
|
399
|
+
`<PageHeaderDataItem>` 内部结构确认:
|
|
400
|
+
|
|
401
|
+
```tsx
|
|
402
|
+
<div className="flex flex-col">
|
|
403
|
+
<span className="text-xs text-muted-foreground">{label}</span>
|
|
404
|
+
<span className={cn('text-base font-semibold tabular-nums', colorClass)}>
|
|
405
|
+
{children}
|
|
406
|
+
</span>
|
|
407
|
+
</div>
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### 变体 C: 详情面板模式
|
|
411
|
+
|
|
412
|
+

|
|
413
|
+
|
|
414
|
+
视觉特征:
|
|
415
|
+
|
|
416
|
+
- **圆形图标**:绿色实心圆形背景 + 白色勾选图标,确认 `iconShape="circle"` 变体
|
|
417
|
+
- **操作按钮顺序**:这里 primary 在左,outline 在右(与之前截图相反),说明顺序由用户自定
|
|
418
|
+
- **Footer 详情区**:
|
|
419
|
+
- 3 列网格布局,2 行
|
|
420
|
+
- Label 灰色(小字) + Value 黑色(正常字号)
|
|
421
|
+
- 状态值带圆点指示器(`● 运行中`)
|
|
422
|
+
- 某些字段带蓝色链接样式(“优先级”后的数字)
|
|
423
|
+
- 备注字段可跨列(占 2 列宽度)
|
|
424
|
+
|
|
425
|
+
实现要点:
|
|
426
|
+
|
|
427
|
+
- `<PageHeaderFooter>` 内放 `<Descriptions>` 组件(已有原子组件,支持 column/bordered/layout)
|
|
428
|
+
- 状态点由用户自行组合(`<Badge>` 或纯 `<span>` + 圆点)
|
|
429
|
+
|
|
430
|
+
### 变体 D: Loading 骨架屏
|
|
431
|
+
|
|
432
|
+

|
|
433
|
+
|
|
434
|
+
视觉特征:
|
|
435
|
+
|
|
436
|
+
- **第一层**:短条骨架(宽约 200px,高 12px) — 对应面包屑位置
|
|
437
|
+
- **第二层**:中等长条骨架(宽约 360px,高 24px) — 对应标题位置
|
|
438
|
+
- **第三层**:3 列 × 2 行网格骨架条 — 对应 Footer/Info 详情区
|
|
439
|
+
- 各骨架条宽度不一,模拟自然内容长度差异
|
|
440
|
+
- 层级间距与实际内容一致
|
|
441
|
+
|
|
442
|
+
`<PageHeaderSkeleton>` 确认结构:
|
|
443
|
+
|
|
444
|
+
```tsx
|
|
445
|
+
<div className={cn('space-y-3', className)} {...props}>
|
|
446
|
+
{/* Nav 骨架 */}
|
|
447
|
+
{showNav && <Skeleton shape="line" className="h-3 w-48" />}
|
|
448
|
+
{/* Title 骨架 */}
|
|
449
|
+
<Skeleton shape="line" className="h-6 w-80" />
|
|
450
|
+
{/* Footer/Info 骨架 */}
|
|
451
|
+
{showFooter && (
|
|
452
|
+
<div className="grid grid-cols-3 gap-x-8 gap-y-3 pt-3">
|
|
453
|
+
<Skeleton shape="line" className="h-3 w-full" />
|
|
454
|
+
<Skeleton shape="line" className="h-3 w-4/5" />
|
|
455
|
+
<Skeleton shape="line" className="h-3 w-full" />
|
|
456
|
+
<Skeleton shape="line" className="h-3 w-4/5" />
|
|
457
|
+
<Skeleton shape="line" className="h-3 w-3/5" />
|
|
458
|
+
<Skeleton shape="line" className="h-3 w-full" />
|
|
459
|
+
</div>
|
|
460
|
+
)}
|
|
461
|
+
</div>
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
---
|
|
465
|
+
|
|
466
|
+
## 六、样式方案映射
|
|
467
|
+
|
|
468
|
+
### 6.1 原始 SCSS → Tailwind 映射表
|
|
469
|
+
|
|
470
|
+
| ProPageHeader SCSS | Tailwind 等价 |
|
|
471
|
+
| ---------------------------------------------------------------- | ---------------------------------------------------- |
|
|
472
|
+
| `position: relative; overflow: hidden` | `relative overflow-hidden` |
|
|
473
|
+
| `background-position: right bottom` | `bg-right-bottom bg-no-repeat bg-[length:auto_100%]` |
|
|
474
|
+
| `display: flex; justify-content: space-between` | `flex items-center justify-between` |
|
|
475
|
+
| `min-height: 18px; margin-bottom: 8px` | `min-h-[18px] mb-2` |
|
|
476
|
+
| `font-size: 24px; font-weight: 500` | `text-2xl font-medium` |
|
|
477
|
+
| `color: var(--color-text1-4)` | `text-foreground` |
|
|
478
|
+
| `color: var(--color-text1-2)` | `text-muted-foreground` |
|
|
479
|
+
| `color: var(--color-text1-8)` | `text-muted-foreground/70` |
|
|
480
|
+
| `font-size: 16px; font-family: DIN` | `text-base font-semibold tabular-nums` |
|
|
481
|
+
| `padding-right: 16px` | `pr-4` |
|
|
482
|
+
| `flex-shrink: 0` | `shrink-0` |
|
|
483
|
+
| `cursor: pointer` | `cursor-pointer` |
|
|
484
|
+
| `white-space: nowrap; overflow: hidden; text-overflow: ellipsis` | `truncate` |
|
|
485
|
+
|
|
486
|
+
### 6.2 颜色系统映射
|
|
487
|
+
|
|
488
|
+
| ProPageHeader 颜色 | OpenTrek Semantic Token |
|
|
489
|
+
| ---------------------------- | ---------------------------------------- |
|
|
490
|
+
| `--color-notice-5` (blue) | `text-primary` |
|
|
491
|
+
| `--color-success-5` (green) | `text-success` |
|
|
492
|
+
| `--color-warning-5` (orange) | `text-warning` |
|
|
493
|
+
| `--color-error-5` (red) | `text-destructive` |
|
|
494
|
+
| `--color-help-5` (yellow) | `text-warning`(合并,或待 tokens 补) |
|
|
495
|
+
| icon 背景色预设 | `bg-primary/10` / `bg-success-subtle` 等 |
|
|
496
|
+
|
|
497
|
+
### 6.3 图标变体 cva 设计
|
|
498
|
+
|
|
499
|
+
基于截图视觉:图标背景为**实心色 + 白色 icon**(非淡色背景),方形圆角约 6px,内距约 6px,整体尺寸约 36×36px:
|
|
500
|
+
|
|
501
|
+
```typescript
|
|
502
|
+
const iconVariants = cva(
|
|
503
|
+
'inline-flex items-center justify-center shrink-0 [&>svg]:size-5',
|
|
504
|
+
{
|
|
505
|
+
variants: {
|
|
506
|
+
color: {
|
|
507
|
+
default: 'text-foreground',
|
|
508
|
+
primary: 'text-primary',
|
|
509
|
+
success: 'text-success',
|
|
510
|
+
warning: 'text-warning',
|
|
511
|
+
error: 'text-destructive',
|
|
512
|
+
info: 'text-info',
|
|
513
|
+
},
|
|
514
|
+
shape: {
|
|
515
|
+
square: 'rounded-md',
|
|
516
|
+
circle: 'rounded-full',
|
|
517
|
+
},
|
|
518
|
+
withBackground: {
|
|
519
|
+
true: 'size-9 p-1.5', // 36px 背景块,内距 6px
|
|
520
|
+
false: '',
|
|
521
|
+
},
|
|
522
|
+
},
|
|
523
|
+
compoundVariants: [
|
|
524
|
+
// 实心背景 + 白色 icon(对齐截图视觉)
|
|
525
|
+
{
|
|
526
|
+
color: 'primary',
|
|
527
|
+
withBackground: true,
|
|
528
|
+
class: 'bg-primary text-primary-foreground',
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
color: 'success',
|
|
532
|
+
withBackground: true,
|
|
533
|
+
class: 'bg-success text-white',
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
color: 'warning',
|
|
537
|
+
withBackground: true,
|
|
538
|
+
class: 'bg-warning text-white',
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
color: 'error',
|
|
542
|
+
withBackground: true,
|
|
543
|
+
class: 'bg-destructive text-destructive-foreground',
|
|
544
|
+
},
|
|
545
|
+
{ color: 'info', withBackground: true, class: 'bg-info text-white' },
|
|
546
|
+
{
|
|
547
|
+
color: 'default',
|
|
548
|
+
withBackground: true,
|
|
549
|
+
class: 'bg-muted text-muted-foreground',
|
|
550
|
+
},
|
|
551
|
+
],
|
|
552
|
+
defaultVariants: {
|
|
553
|
+
color: 'default',
|
|
554
|
+
shape: 'square',
|
|
555
|
+
withBackground: false,
|
|
556
|
+
},
|
|
557
|
+
},
|
|
558
|
+
);
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
---
|
|
562
|
+
|
|
563
|
+
## 七、Loading 骨架屏设计
|
|
564
|
+
|
|
565
|
+
复用 `@/components/skeleton` 组合实现,不另建专用骨架子组件:
|
|
566
|
+
|
|
567
|
+
```tsx
|
|
568
|
+
// loading 态布局
|
|
569
|
+
<header className="...">
|
|
570
|
+
{/* 面包屑骨架 */}
|
|
571
|
+
<div className="flex items-center gap-2 mb-2">
|
|
572
|
+
<Skeleton shape="line" className="h-3 w-16" />
|
|
573
|
+
<Skeleton shape="line" className="h-3 w-16" />
|
|
574
|
+
<Skeleton shape="line" className="h-3 w-24" />
|
|
575
|
+
</div>
|
|
576
|
+
{/* 标题骨架 */}
|
|
577
|
+
<div className="flex items-center gap-3">
|
|
578
|
+
<Skeleton shape="rect" className="size-8 rounded-md" />
|
|
579
|
+
<Skeleton shape="line" className="h-6 w-48" />
|
|
580
|
+
</div>
|
|
581
|
+
{/* 描述骨架 */}
|
|
582
|
+
<Skeleton shape="line" className="mt-2 h-4 w-96" />
|
|
583
|
+
{/* 详情骨架 */}
|
|
584
|
+
<SkeletonParagraph rows={2} className="mt-4" />
|
|
585
|
+
</header>
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
---
|
|
589
|
+
|
|
590
|
+
## 八、registryDependencies 分析
|
|
591
|
+
|
|
592
|
+
基于复用的原子组件,manifest.json entry 需声明:
|
|
593
|
+
|
|
594
|
+
```json
|
|
595
|
+
{
|
|
596
|
+
"id": "page-header",
|
|
597
|
+
"name": "PageHeader",
|
|
598
|
+
"type": "component",
|
|
599
|
+
"registryDependencies": ["cn", "skeleton"],
|
|
600
|
+
"dependencies": {
|
|
601
|
+
"class-variance-authority": "^0.7.0",
|
|
602
|
+
"lucide-react": "^0.460.0"
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
> **为什么只声明 `cn` 和 `skeleton`?** 复合组件模式下,`breadcrumb` / `tag` / `tooltip` / `descriptions` 等原子组件由用户在使用侧自行引入组合,不是 PageHeader 内部的硬依赖。只有 `PageHeaderSkeleton` 内部复用了 `Skeleton` 组件才需要声明。用户 `add page-header` 后,CLI 会提示推荐搭配的原子组件。
|
|
608
|
+
|
|
609
|
+
---
|
|
610
|
+
|
|
611
|
+
## 九、使用示例预览
|
|
612
|
+
|
|
613
|
+
### 9.1 极简用法
|
|
614
|
+
|
|
615
|
+
```tsx
|
|
616
|
+
import {
|
|
617
|
+
PageHeader,
|
|
618
|
+
PageHeaderContent,
|
|
619
|
+
PageHeaderHeading,
|
|
620
|
+
} from '@/components/page-header';
|
|
621
|
+
import { Title } from '@/components/typography';
|
|
622
|
+
|
|
623
|
+
<PageHeader>
|
|
624
|
+
<PageHeaderContent>
|
|
625
|
+
<PageHeaderHeading>
|
|
626
|
+
<Title level={2}>实例管理</Title>
|
|
627
|
+
</PageHeaderHeading>
|
|
628
|
+
</PageHeaderContent>
|
|
629
|
+
</PageHeader>;
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
### 9.2 标准用法(面包屑 + 标题 + 标签 + 操作)
|
|
633
|
+
|
|
634
|
+
```tsx
|
|
635
|
+
import {
|
|
636
|
+
PageHeader,
|
|
637
|
+
PageHeaderNav,
|
|
638
|
+
PageHeaderContent,
|
|
639
|
+
PageHeaderHeading,
|
|
640
|
+
PageHeaderActions,
|
|
641
|
+
} from '@/components/page-header';
|
|
642
|
+
import { Title } from '@/components/typography';
|
|
643
|
+
import {
|
|
644
|
+
Breadcrumb,
|
|
645
|
+
BreadcrumbList,
|
|
646
|
+
BreadcrumbItem,
|
|
647
|
+
BreadcrumbLink,
|
|
648
|
+
BreadcrumbSeparator,
|
|
649
|
+
BreadcrumbPage,
|
|
650
|
+
} from '@/components/breadcrumb';
|
|
651
|
+
import { Tag } from '@/components/tag';
|
|
652
|
+
import { Button } from '@/components/button';
|
|
653
|
+
import { Server } from 'lucide-react';
|
|
654
|
+
|
|
655
|
+
<PageHeader>
|
|
656
|
+
<PageHeaderNav>
|
|
657
|
+
<Breadcrumb>
|
|
658
|
+
<BreadcrumbList>
|
|
659
|
+
<BreadcrumbItem>
|
|
660
|
+
<BreadcrumbLink href="/">首页</BreadcrumbLink>
|
|
661
|
+
</BreadcrumbItem>
|
|
662
|
+
<BreadcrumbSeparator />
|
|
663
|
+
<BreadcrumbItem>
|
|
664
|
+
<BreadcrumbLink href="/resources">资源管理</BreadcrumbLink>
|
|
665
|
+
</BreadcrumbItem>
|
|
666
|
+
<BreadcrumbSeparator />
|
|
667
|
+
<BreadcrumbItem>
|
|
668
|
+
<BreadcrumbPage>实例详情</BreadcrumbPage>
|
|
669
|
+
</BreadcrumbItem>
|
|
670
|
+
</BreadcrumbList>
|
|
671
|
+
</Breadcrumb>
|
|
672
|
+
</PageHeaderNav>
|
|
673
|
+
|
|
674
|
+
<PageHeaderContent>
|
|
675
|
+
<PageHeaderHeading
|
|
676
|
+
goBack
|
|
677
|
+
onBack={() => router.back()}
|
|
678
|
+
icon={<Server className="size-5" />}
|
|
679
|
+
iconColor="primary"
|
|
680
|
+
iconBackground
|
|
681
|
+
>
|
|
682
|
+
<Title level={2}>i-2ze3m5j8w</Title>
|
|
683
|
+
<Tag color="success">运行中</Tag>
|
|
684
|
+
<Tag>华东1</Tag>
|
|
685
|
+
</PageHeaderHeading>
|
|
686
|
+
|
|
687
|
+
<PageHeaderActions>
|
|
688
|
+
<Button variant="outline">停止</Button>
|
|
689
|
+
<Button>远程连接</Button>
|
|
690
|
+
</PageHeaderActions>
|
|
691
|
+
</PageHeaderContent>
|
|
692
|
+
</PageHeader>;
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
### 9.3 带数据概览
|
|
696
|
+
|
|
697
|
+
```tsx
|
|
698
|
+
import {
|
|
699
|
+
PageHeader,
|
|
700
|
+
PageHeaderContent,
|
|
701
|
+
PageHeaderHeading,
|
|
702
|
+
PageHeaderDescription,
|
|
703
|
+
PageHeaderData,
|
|
704
|
+
PageHeaderDataItem,
|
|
705
|
+
} from '@/components/page-header';
|
|
706
|
+
import { Title } from '@/components/typography';
|
|
707
|
+
|
|
708
|
+
<PageHeader>
|
|
709
|
+
<PageHeaderContent>
|
|
710
|
+
<PageHeaderHeading>
|
|
711
|
+
<Title level={2}>集群总览</Title>
|
|
712
|
+
</PageHeaderHeading>
|
|
713
|
+
<PageHeaderDescription>杭州地域集群资源使用情况</PageHeaderDescription>
|
|
714
|
+
<PageHeaderData>
|
|
715
|
+
<PageHeaderDataItem label="总实例">456</PageHeaderDataItem>
|
|
716
|
+
<PageHeaderDataItem label="运行中" color="success">
|
|
717
|
+
324
|
|
718
|
+
</PageHeaderDataItem>
|
|
719
|
+
<PageHeaderDataItem label="已停止" color="error">
|
|
720
|
+
14
|
|
721
|
+
</PageHeaderDataItem>
|
|
722
|
+
</PageHeaderData>
|
|
723
|
+
</PageHeaderContent>
|
|
724
|
+
</PageHeader>;
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
### 9.4 带详情面板
|
|
728
|
+
|
|
729
|
+
```tsx
|
|
730
|
+
import {
|
|
731
|
+
PageHeader,
|
|
732
|
+
PageHeaderNav,
|
|
733
|
+
PageHeaderContent,
|
|
734
|
+
PageHeaderHeading,
|
|
735
|
+
PageHeaderFooter,
|
|
736
|
+
} from '@/components/page-header';
|
|
737
|
+
import { Descriptions } from '@/components/descriptions';
|
|
738
|
+
import { Tag } from '@/components/tag';
|
|
739
|
+
|
|
740
|
+
<PageHeader>
|
|
741
|
+
<PageHeaderNav>
|
|
742
|
+
<Breadcrumb>...</Breadcrumb>
|
|
743
|
+
</PageHeaderNav>
|
|
744
|
+
|
|
745
|
+
<PageHeaderContent>
|
|
746
|
+
<PageHeaderHeading goBack onBack={() => navigate(-1)}>
|
|
747
|
+
实例详情
|
|
748
|
+
</PageHeaderHeading>
|
|
749
|
+
</PageHeaderContent>
|
|
750
|
+
|
|
751
|
+
<PageHeaderFooter>
|
|
752
|
+
<Descriptions
|
|
753
|
+
column={3}
|
|
754
|
+
items={[
|
|
755
|
+
{ label: '实例 ID', value: 'i-2ze3m5j8w' },
|
|
756
|
+
{ label: '状态', value: <Tag color="success">运行中</Tag> },
|
|
757
|
+
{ label: '地域', value: '华东1(杭州)' },
|
|
758
|
+
{ label: '创建时间', value: '2026-03-15 10:30:00' },
|
|
759
|
+
]}
|
|
760
|
+
/>
|
|
761
|
+
</PageHeaderFooter>
|
|
762
|
+
</PageHeader>;
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
### 9.5 背景装饰图
|
|
766
|
+
|
|
767
|
+
```tsx
|
|
768
|
+
import { Title } from '@/components/typography';
|
|
769
|
+
|
|
770
|
+
<PageHeader backgroundImage="https://img.alicdn.com/imgextra/...">
|
|
771
|
+
<PageHeaderContent>
|
|
772
|
+
<PageHeaderHeading icon={<Cloud />} iconColor="primary" iconBackground>
|
|
773
|
+
<Title level={2}>云服务器 ECS</Title>
|
|
774
|
+
</PageHeaderHeading>
|
|
775
|
+
<PageHeaderDescription>弹性计算服务</PageHeaderDescription>
|
|
776
|
+
</PageHeaderContent>
|
|
777
|
+
</PageHeader>;
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
### 9.6 Loading 态
|
|
781
|
+
|
|
782
|
+
```tsx
|
|
783
|
+
import { PageHeaderSkeleton } from '@/components/page-header';
|
|
784
|
+
|
|
785
|
+
{
|
|
786
|
+
loading ? (
|
|
787
|
+
<PageHeaderSkeleton showNav showDescription />
|
|
788
|
+
) : (
|
|
789
|
+
<PageHeader>...</PageHeader>
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
---
|
|
795
|
+
|
|
796
|
+
## 十、与 ProPageHeader 的差异总结
|
|
797
|
+
|
|
798
|
+
| 对比项 | ProPageHeader (teamix-pro) | PageHeader (teamix-evo/ui) |
|
|
799
|
+
| ---------- | --------------------------- | ------------------------------------- |
|
|
800
|
+
| 组件范式 | 单一巨组件 + 配置驱动 | 纯复合组件,用户自由组装 |
|
|
801
|
+
| 样式方案 | SCSS + CSS 变量 | Tailwind + cn() + semantic tokens |
|
|
802
|
+
| 图标系统 | TeamixIcon (字符串 type) | lucide-react (ReactNode 传入) |
|
|
803
|
+
| 操作区 | ProActionGroupProps 配置式 | children 直接放 Button |
|
|
804
|
+
| 面包屑 | breadcrumb[] 配置数组 | 用户组合 Breadcrumb 原子组件 |
|
|
805
|
+
| 标签 | tags[] 配置数组 | 用户在 Heading 内放 Tag 原子组件 |
|
|
806
|
+
| 详情面板 | ProInfoProps 配置式 | Footer 内放 Descriptions 或自定义内容 |
|
|
807
|
+
| 路由耦合 | useRouterHistory + goToLink | 纯回调 onBack,不耦合路由 |
|
|
808
|
+
| 颜色预设 | 字面色名 (blue/green/...) | 语义色名 (primary/success/...) |
|
|
809
|
+
| 全局配置 | withGlobalProps HOC | 无,源码注入不需要 |
|
|
810
|
+
| 选项卡 | CapsuleTab 内置 | 用户自行组合 Tabs 或放 children |
|
|
811
|
+
| 分发方式 | npm 包引入 | 源码注入 (teamix-evo ui add) |
|
|
812
|
+
| TypeScript | 基础类型 | strict mode + 完整 JSDoc + @default |
|
|
813
|
+
|
|
814
|
+
---
|
|
815
|
+
|
|
816
|
+
## 十一、研发任务拆分
|
|
817
|
+
|
|
818
|
+
| 序号 | 任务 | 产出文件 |
|
|
819
|
+
| ---- | ----------------------------------- | ---------------------------- |
|
|
820
|
+
| 1 | 编写 page-header.tsx 主组件源码 | `page-header.tsx` |
|
|
821
|
+
| 2 | 编写 page-header.meta.md | `page-header.meta.md` |
|
|
822
|
+
| 3 | 编写 page-header.stories.tsx | `page-header.stories.tsx` |
|
|
823
|
+
| 4 | 注册 manifest.json entry | `manifest.json` (追加 entry) |
|
|
824
|
+
| 5 | 运行验证 (`validate` + `typecheck`) | - |
|
|
825
|
+
| 6 | Storybook 视觉验证 | - |
|
|
826
|
+
| 7 | gen:meta 同步 props 表 | - |
|
|
827
|
+
| 8 | gen:check 确认无漂移 | - |
|
|
828
|
+
|
|
829
|
+
---
|
|
830
|
+
|
|
831
|
+
## 十二、注意事项 & AI 生成纪律
|
|
832
|
+
|
|
833
|
+
- **不要**封装任何配置式 API(不接受 `breadcrumb[]` / `tags[]` / `data[]` 等数组 props),一切由用户组合原子组件实现
|
|
834
|
+
- **不要**在组件内 hardcode 任何 icon 字符串,所有图标由用户通过 `icon` prop 传入(lucide-react 组件)
|
|
835
|
+
- **不要**引入路由库依赖,返回逻辑通过 `onBack` 回调由消费方实现
|
|
836
|
+
- **不要**使用 CSS 变量直接写值,一律走 Tailwind semantic token classes
|
|
837
|
+
- **不要**在子组件内做条件渲染“某个 prop 不存在就不渲染”,存在即渲染的操控权在用户侧
|
|
838
|
+
- **goBack** 图标固定用 `ArrowLeft` from lucide-react
|
|
839
|
+
- **titleTooltip** 用户自行用 `@/components/tooltip` 的 `<Tooltip>` 包裹标题文字
|
|
840
|
+
- **PageHeaderDataItem** 内部用 `tabular-nums` class 保持数字等宽对齐
|
|
841
|
+
- 背景图通过 inline style 的 `backgroundImage` 注入(Tailwind 不支持动态 URL)
|
|
842
|
+
- 每个子组件都用 `React.forwardRef` + `cn()` 透传 className
|