@lmy54321/design-system 1.1.0 → 1.1.2
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/dist/index.d.mts +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +27 -17
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +27 -17
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -3
- package/rules/design-system.mdc +585 -0
- package/styles/globals.css +10 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lmy54321/design-system",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "A comprehensive React component library and design system based on Tailwind CSS and Motion.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -11,12 +11,14 @@
|
|
|
11
11
|
"import": "./dist/index.mjs",
|
|
12
12
|
"require": "./dist/index.js"
|
|
13
13
|
},
|
|
14
|
-
"./styles": "./styles/globals.css"
|
|
14
|
+
"./styles": "./styles/globals.css",
|
|
15
|
+
"./rules/*": "./rules/*"
|
|
15
16
|
},
|
|
16
17
|
"sideEffects": false,
|
|
17
18
|
"files": [
|
|
18
19
|
"dist",
|
|
19
|
-
"styles"
|
|
20
|
+
"styles",
|
|
21
|
+
"rules"
|
|
20
22
|
],
|
|
21
23
|
"scripts": {
|
|
22
24
|
"dev": "vite",
|
|
@@ -0,0 +1,585 @@
|
|
|
1
|
+
---
|
|
2
|
+
description:
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
enabled: true
|
|
5
|
+
updatedAt: 2026-03-05T12:00:00.000Z
|
|
6
|
+
provider:
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# @lmy54321/design-system 使用规范
|
|
10
|
+
|
|
11
|
+
本项目使用团队设计系统 `@lmy54321/design-system`,所有 UI 开发必须优先使用该组件库。
|
|
12
|
+
|
|
13
|
+
## 可用组件清单
|
|
14
|
+
|
|
15
|
+
生成 UI 代码时,**必须优先**从以下组件中选用,禁止重复造轮子:
|
|
16
|
+
|
|
17
|
+
### 基础组件
|
|
18
|
+
| 组件 | 用途 | 示例 |
|
|
19
|
+
|------|------|------|
|
|
20
|
+
| `Btn` | 按钮(支持 size/variant) | `<Btn variant="primary" size="lg">确认</Btn>` |
|
|
21
|
+
| `Switch` | 开关切换 | `<Switch checked={on} onChange={setOn} />` |
|
|
22
|
+
| `Tag` | 标签 | `<Tag>标签文字</Tag>` |
|
|
23
|
+
| `Loading` | 加载状态 | `<Loading />` |
|
|
24
|
+
| `SegmentedControl` | 分段控制器 | `<SegmentedControl items={['tab1','tab2']} />` |
|
|
25
|
+
| `StatGrid` | 数据统计网格 | `<StatGrid items={stats} />` |
|
|
26
|
+
|
|
27
|
+
### 反馈组件
|
|
28
|
+
| 组件 | 用途 |
|
|
29
|
+
|------|------|
|
|
30
|
+
| `Dialog` | 对话框(variant: default/confirm/input) |
|
|
31
|
+
| `Toast` | 轻提示 |
|
|
32
|
+
| `ActionSheet` | 底部操作菜单 |
|
|
33
|
+
| `NotificationBar` | 通知栏 |
|
|
34
|
+
| `BubbleTip` | 气泡提示 |
|
|
35
|
+
| `Push` | 推送通知卡片 |
|
|
36
|
+
| `EmptyState` | 空状态页面 |
|
|
37
|
+
|
|
38
|
+
### 导航组件
|
|
39
|
+
| 组件 | 用途 |
|
|
40
|
+
|------|------|
|
|
41
|
+
| `TopToolbar` | 顶部导航栏 |
|
|
42
|
+
| `BottomNavigationBar` | 底部标签栏 |
|
|
43
|
+
|
|
44
|
+
### 容器组件
|
|
45
|
+
| 组件 | 用途 |
|
|
46
|
+
|------|------|
|
|
47
|
+
| `BottomSheet` | 底部弹出面板 |
|
|
48
|
+
| `DraggablePanel` | 可拖拽面板(三档高度) |
|
|
49
|
+
|
|
50
|
+
### 搜索组件
|
|
51
|
+
| 组件 | 用途 |
|
|
52
|
+
|------|------|
|
|
53
|
+
| `SearchBox` | 搜索输入框(variant: card/map) |
|
|
54
|
+
|
|
55
|
+
### 列表/卡片
|
|
56
|
+
| 组件 | 用途 |
|
|
57
|
+
|------|------|
|
|
58
|
+
| `POIListItem` | POI 列表项 |
|
|
59
|
+
| `PoiItem` | POI 简要卡片 |
|
|
60
|
+
| `NewsItem` | 新闻/资讯列表项 |
|
|
61
|
+
|
|
62
|
+
**Btn 独占一行时默认居中:** `Btn` 组件内置 `mx-auto`,当按钮独占一行(未使用 `w-full`)时自动水平居中。如果需要左对齐或右对齐,通过 `className` 覆盖即可(如 `className="mx-0"` 或 `className="ml-auto mr-0"`)。
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
// ✅ 默认居中(无需额外 class)
|
|
66
|
+
<Btn label="退出登录" variant="ghost" size="large" icon={null} />
|
|
67
|
+
|
|
68
|
+
// ✅ 需要左对齐时手动覆盖
|
|
69
|
+
<Btn label="操作" className="mx-0" />
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 底部操作栏
|
|
73
|
+
| 组件 | 用途 |
|
|
74
|
+
|------|------|
|
|
75
|
+
| `BottomActionButtons` | 底部操作按钮组 |
|
|
76
|
+
| `BottomToolbar` | 底部工具栏 |
|
|
77
|
+
|
|
78
|
+
**底部操作栏必须始终吸底(sticky):** 底部操作栏(`BottomActionButtons`、`BottomToolbar` 或手动组装的底部按钮组)在 `DraggablePanel` 中**必须**通过 `bottomBar` prop 传入,**严禁**放在 `children` 内部。`bottomBar` 由 DraggablePanel 的 flex 布局固定在面板底部(`shrink-0`),内容区域独立滚动,确保操作栏始终可见、不被内容推走。
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
// ✅ 正确:通过 bottomBar prop 吸底
|
|
82
|
+
<DraggablePanel
|
|
83
|
+
bottomBar={
|
|
84
|
+
<BottomActionButtons mode="weighted" mainText="导航" secondaryText="分享" />
|
|
85
|
+
}
|
|
86
|
+
>
|
|
87
|
+
{/* 可滚动内容 */}
|
|
88
|
+
</DraggablePanel>
|
|
89
|
+
|
|
90
|
+
// ❌ 错误:放在 children 末尾,会随内容滚动
|
|
91
|
+
<DraggablePanel>
|
|
92
|
+
{/* 内容 */}
|
|
93
|
+
<BottomActionButtons ... /> {/* 会被滚走 */}
|
|
94
|
+
</DraggablePanel>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**DraggablePanel 内部 Flex 子项必须声明 `min-h-0`:** 当 `DraggablePanel` 的 `children` 内部使用 Flexbox 布局(`flex flex-col`)时,可滚动内容区域(通常为 `flex-1 overflow-y-auto` 的 div)**必须**同时声明 `min-h-0`,否则 `bottomBar` 可能被内容撑出面板的 `overflow-hidden` 边界而不可见。
|
|
98
|
+
|
|
99
|
+
> **适用场景:** 任何在 `DraggablePanel` 的 `children` 中使用 `flex-1` 分配剩余空间的布局。典型如:顶部固定区域(`shrink-0`)+ 中间可滚动列表(`flex-1`)+ 底部操作栏(`bottomBar`)的三段式结构。
|
|
100
|
+
>
|
|
101
|
+
> **根因说明:** CSS Flexbox 规范中,`flex-1` 子项的默认 `min-height` 为 `auto`(等于内容的固有高度),即使设置了 `overflow-auto`,子项也不会收缩到比内容更小。这导致内容区域以完整高度撑开父容器,将 `bottomBar` 推到面板可视区域之外,被外层 `overflow-hidden` 裁切。加上 `min-h-0` 后,子项可以收缩到任意高度,`overflow-auto` 正常生效,`bottomBar` 自然吸底。
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
// ✅ 正确:flex-1 + min-h-0 + overflow-y-auto 三者缺一不可
|
|
105
|
+
<DraggablePanel
|
|
106
|
+
bottomBar={<BottomActionButtons mode="weighted" mainText="导航" secondaryText="分享" />}
|
|
107
|
+
>
|
|
108
|
+
<div className="flex flex-col h-full">
|
|
109
|
+
<div className="shrink-0">{/* 固定区域:标题/统计/Tab栏等 */}</div>
|
|
110
|
+
<div className="flex-1 min-h-0 overflow-y-auto">
|
|
111
|
+
{/* 可滚动内容:列表、详情等 */}
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
</DraggablePanel>
|
|
115
|
+
|
|
116
|
+
// ❌ 错误:缺少 min-h-0,bottomBar 会被推出可视区域
|
|
117
|
+
<DraggablePanel
|
|
118
|
+
bottomBar={<BottomActionButtons ... />}
|
|
119
|
+
>
|
|
120
|
+
<div className="flex flex-col h-full">
|
|
121
|
+
<div className="shrink-0">{/* 固定区域 */}</div>
|
|
122
|
+
<div className="flex-1 overflow-y-auto"> {/* 缺 min-h-0! */}
|
|
123
|
+
{/* 内容过多时 bottomBar 不可见 */}
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
</DraggablePanel>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 其他
|
|
130
|
+
| 组件 | 用途 |
|
|
131
|
+
|------|------|
|
|
132
|
+
| `TencentMap` | 腾讯地图组件 |
|
|
133
|
+
| `ImageWithFallback` | 带降级的图片组件 |
|
|
134
|
+
|
|
135
|
+
### 图标组件(基于 TDesign 图标库,2300+ 图标)
|
|
136
|
+
|
|
137
|
+
**严格禁止使用 emoji(如 🍜🛒🏨🎬⛽☕)作为图标!所有图标必须且只能通过 `IconFont` 组件使用。** 违反此规则的代码不可接受。
|
|
138
|
+
|
|
139
|
+
基本用法:
|
|
140
|
+
```tsx
|
|
141
|
+
<IconFont name="location" />
|
|
142
|
+
<IconFont name="search" variant="filled" size="24px" />
|
|
143
|
+
<IconFont name="star" variant="filled" className="text-[#FFB800]" />
|
|
144
|
+
<IconFont name="map-navigation" className="text-primary" />
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
常见场景图标映射(用这些代替 emoji):
|
|
148
|
+
| 场景 | 图标 name | 说明 |
|
|
149
|
+
|------|-----------|------|
|
|
150
|
+
| 美食/餐饮 | `fork` | 替代 🍜🍔🍕(TDesign 无 restaurant) |
|
|
151
|
+
| 咖啡/茶饮 | `tea` | 替代 ☕(TDesign 无 coffee) |
|
|
152
|
+
| 酒店/住宿 | `building` | 替代 🏨(TDesign 无 hotel) |
|
|
153
|
+
| 购物 | `shop` | 替代 🛒🛍 |
|
|
154
|
+
| 加油站/交通 | `vehicle` | 替代 ⛽🚌(TDesign 无 gas-station/bus) |
|
|
155
|
+
| 医院 | `hospital` | 替代 🏥 |
|
|
156
|
+
| 电影/娱乐 | `film` | 替代 🎬 |
|
|
157
|
+
| 景点/户外 | `map-marked` | 替代 🏞 |
|
|
158
|
+
| 导航 | `map-navigation` | 替代 🧭 |
|
|
159
|
+
| 首页 | `home` | 替代 🏠 |
|
|
160
|
+
| 用户/我的 | `user` | 替代 👤 |
|
|
161
|
+
| 设置 | `setting` | 替代 ⚙ |
|
|
162
|
+
| 收藏 | `star` | 替代 ⭐ |
|
|
163
|
+
| 分享 | `share` | 替代 📤 |
|
|
164
|
+
| 电话 | `call` | 替代 📞 |
|
|
165
|
+
| 深色模式 | `mode-dark` | TDesign 无 dark-mode |
|
|
166
|
+
| 花/自然 | `cherry` | TDesign 无 flower |
|
|
167
|
+
| 博物馆 | `museum` | 直接可用 |
|
|
168
|
+
| 拍照 | `camera` | 直接可用 |
|
|
169
|
+
| 日历 | `calendar` | 直接可用 |
|
|
170
|
+
| 饮品 | `drink` | 直接可用 |
|
|
171
|
+
|
|
172
|
+
> 图标库有 2300+ 图标可用。如果以上映射没有覆盖到,请从 TDesign 图标库中选择最接近语义的图标名,**绝对不要用 emoji 凑数**。
|
|
173
|
+
|
|
174
|
+
### 工具函数
|
|
175
|
+
- `cn(...classes)` — className 合并工具(替代 clsx + tailwind-merge)
|
|
176
|
+
|
|
177
|
+
## 导入方式
|
|
178
|
+
|
|
179
|
+
所有组件统一从包根路径导入:
|
|
180
|
+
```tsx
|
|
181
|
+
import { Dialog, Toast, Btn, SearchBox, IconFont, cn } from '@lmy54321/design-system'
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## 设计 Token
|
|
185
|
+
|
|
186
|
+
所有颜色使用 CSS 变量,禁止硬编码色值。在 Tailwind 中使用 `bg-primary`、`text-muted-foreground`、`border-border` 等。
|
|
187
|
+
|
|
188
|
+
| Token | 用途 |
|
|
189
|
+
|-------|------|
|
|
190
|
+
| `--primary` | 主色 `rgba(75, 82, 107, 1)` |
|
|
191
|
+
| `--accent` | 强调色 `rgba(54, 123, 246, 1)` |
|
|
192
|
+
| `--destructive` | 危险操作 `rgba(255, 41, 59, 1)` |
|
|
193
|
+
| `--foreground` | 主文字(黑色 90%) |
|
|
194
|
+
| `--muted-foreground` | 辅助文字(黑色 42%) |
|
|
195
|
+
| `--background` | 页面背景(白色) |
|
|
196
|
+
| `--card` / `--card-muted` | 卡片背景 |
|
|
197
|
+
| `--border` | 边框(黑色 12%) |
|
|
198
|
+
|
|
199
|
+
间距:`--spacing-xxs`(4px) / `--spacing-xs`(8px) / `--spacing-sm`(12px) / `--spacing-base`(16px) / `--spacing-md`(20px) / `--spacing-lg`(24px) / `--spacing-xl`(32px)
|
|
200
|
+
|
|
201
|
+
圆角:`--radius`(24px) / `--radius-button`(50px 药丸形) / `--radius-card`(32px)
|
|
202
|
+
|
|
203
|
+
字体:PingFang SC → system-ui fallback
|
|
204
|
+
|
|
205
|
+
## 三段式页卡(DraggablePanel)与底图使用规范
|
|
206
|
+
|
|
207
|
+
### 核心原则
|
|
208
|
+
|
|
209
|
+
应用底层始终保持一个底图(如腾讯地图 `TencentMap`)。页卡内容与底图的关系决定页卡的状态:
|
|
210
|
+
|
|
211
|
+
- **与底图有联动关系** → 使用 `DraggablePanel` 的 **SMALL / MEDIUM / LARGE** 状态(露出底图)
|
|
212
|
+
- **与底图无关** → 使用 `DraggablePanel` 的 **FULL** 全屏状态(完全遮盖底图)
|
|
213
|
+
|
|
214
|
+
### DraggablePanel 四种状态
|
|
215
|
+
|
|
216
|
+
| 状态 | 常量 | 高度 | 底图可见性 | 背景样式 | 适用场景 |
|
|
217
|
+
|------|------|------|------------|----------|----------|
|
|
218
|
+
| **SMALL** | `DRAWER_STATES.SMALL` | 160px (可配置) | ✅ 大面积可见 | 毛玻璃 `from-card/48 to-card/84` | 地图浏览为主,快速预览 |
|
|
219
|
+
| **MEDIUM** | `DRAWER_STATES.MEDIUM` | 400px (可配置) | ✅ 部分可见 | 半透明 `card/90` | 平衡地图和内容 |
|
|
220
|
+
| **LARGE** | `DRAWER_STATES.LARGE` | `calc(100% - 8px)` | ⚠️ 微量可见(带60%黑色遮罩) | 纯色 `card` | 内容详情,保留关联感 |
|
|
221
|
+
| **FULL** | `DRAWER_STATES.FULL` | `100%` | ❌ 完全遮盖 | 纯色 `card` | 独立页面,与底图无关 |
|
|
222
|
+
|
|
223
|
+
### 场景判断规则
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
需求分析
|
|
227
|
+
├── 页卡内容需要与底图联动?
|
|
228
|
+
│ ├── 是 → 使用 SMALL/MEDIUM/LARGE(支持拖拽切换)
|
|
229
|
+
│ │ ├── 底图为主,快速预览 → SMALL
|
|
230
|
+
│ │ ├── 平衡展示 → MEDIUM
|
|
231
|
+
│ │ └── 内容为主,保留关联 → LARGE
|
|
232
|
+
│ └── 否 → 使用 FULL 全屏状态
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### 联动场景(SMALL/MEDIUM/LARGE)— 需要底图
|
|
236
|
+
|
|
237
|
+
适用于地图首页、行程规划、团队出行、搜索结果地图标注、导航等需要地图交互的页面。
|
|
238
|
+
|
|
239
|
+
```tsx
|
|
240
|
+
// 底层必须有地图底图
|
|
241
|
+
<TencentMap className="absolute inset-0" />
|
|
242
|
+
<DraggablePanel
|
|
243
|
+
state={panelState}
|
|
244
|
+
onStateChange={setPanelState}
|
|
245
|
+
customSmallHeight={260}
|
|
246
|
+
>
|
|
247
|
+
{/* 与地图联动的内容 */}
|
|
248
|
+
</DraggablePanel>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**视觉规范:**
|
|
252
|
+
- 圆角:顶部 32px,底部 44px
|
|
253
|
+
- 边距:左右 8px,底部 8px
|
|
254
|
+
- 拖拽:✅ 支持手势切换
|
|
255
|
+
- LARGE 时显示 60% 黑色遮罩
|
|
256
|
+
- **TopToolbar 定位**:非全屏状态下 TopToolbar 悬浮在页卡外部 `top-[20px]`,LARGE 状态 `appearance="gray"`,其他状态 `appearance="glass"`(毛玻璃)
|
|
257
|
+
|
|
258
|
+
### 独立场景(FULL)— 不需要底图
|
|
259
|
+
|
|
260
|
+
适用于个人中心、设置页、独立详情页、订单/收藏列表等与地图无关的页面。
|
|
261
|
+
|
|
262
|
+
```tsx
|
|
263
|
+
<DraggablePanel
|
|
264
|
+
state={DRAWER_STATES.FULL}
|
|
265
|
+
fullScreenContent={<ProfileContent />}
|
|
266
|
+
topToolbar={{ mode: "left-title", title: "个人中心", showBack: true }}
|
|
267
|
+
showTopToolbarInStates={[DRAWER_STATES.FULL]}
|
|
268
|
+
>
|
|
269
|
+
{/* children 用于非全屏时 */}
|
|
270
|
+
</DraggablePanel>
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**视觉规范:**
|
|
274
|
+
- 圆角:0(贴边)
|
|
275
|
+
- 边距:无
|
|
276
|
+
- 拖拽:❌ 禁用
|
|
277
|
+
- 背景:纯色 `bg-card`
|
|
278
|
+
|
|
279
|
+
### 规范总结表
|
|
280
|
+
|
|
281
|
+
| 维度 | 联动场景(小/中/大) | 独立场景(全屏) |
|
|
282
|
+
|------|---------------------|------------------|
|
|
283
|
+
| 底图 | 可见(透过毛玻璃/遮罩) | 完全遮盖 |
|
|
284
|
+
| 圆角 | 顶部32px 底部44px | 0(贴边) |
|
|
285
|
+
| 边距 | 左右8px 底部8px | 无边距 |
|
|
286
|
+
| 背景 | 毛玻璃/半透明 | 纯色 bg-card |
|
|
287
|
+
| 拖拽 | ✅ 手势切换 | ❌ 禁用 |
|
|
288
|
+
| 遮罩 | LARGE时60%黑色遮罩 | 无需遮罩 |
|
|
289
|
+
|
|
290
|
+
### 严禁行为
|
|
291
|
+
|
|
292
|
+
1. **禁止**在需要底图联动的页面直接使用全屏页卡遮盖底图
|
|
293
|
+
2. **禁止**在与底图无关的页面使用 SMALL/MEDIUM/LARGE 状态(浪费底图资源)
|
|
294
|
+
3. **禁止**在非联动页面中自行实现毛玻璃面板代替 `DraggablePanel`
|
|
295
|
+
4. **禁止**使用渐变背景 `div` 模拟地图底图,必须使用 `TencentMap` 组件
|
|
296
|
+
5. **禁止**将底部操作栏(`BottomActionButtons`/`BottomToolbar`/底部按钮组)放在 `DraggablePanel` 的 `children` 内部,必须通过 `bottomBar` prop 传入以保持吸底
|
|
297
|
+
6. **禁止**在 `DraggablePanel` 的 `children` 内部对 `flex-1` 子项省略 `min-h-0`,否则 `bottomBar` 将被内容推出可视区域
|
|
298
|
+
|
|
299
|
+
## 瀑布流布局规范
|
|
300
|
+
|
|
301
|
+
### 标准数据结构(FeedItem)
|
|
302
|
+
|
|
303
|
+
内容卡片(资讯/帖子/路线推荐)应使用以下标准字段:
|
|
304
|
+
|
|
305
|
+
| 字段 | 类型 | 必填 | 说明 |
|
|
306
|
+
|------|------|------|------|
|
|
307
|
+
| `id` | `string` | ✅ | 唯一标识 |
|
|
308
|
+
| `type` | `"article" \| "route"` | ✅ | 内容类型 |
|
|
309
|
+
| `image` | `string` | ✅ | 头图URL |
|
|
310
|
+
| `title` | `string` | ✅ | 标题 |
|
|
311
|
+
| `content` | `string` | ✅ | 正文摘要(卡片显示2行 `line-clamp-2`) |
|
|
312
|
+
| `author` | `string` | ✅ | 作者名 |
|
|
313
|
+
| `authorAvatar` | `string` | ✅ | 作者头像URL |
|
|
314
|
+
| `authorLevel` | `string` | ❌ | 身份标签(本地达人/美食博主等) |
|
|
315
|
+
| `likes` | `number` | ✅ | 点赞数 |
|
|
316
|
+
| `comments` | `number` | ✅ | 评论数 |
|
|
317
|
+
| `shares` | `number` | ✅ | 分享数 |
|
|
318
|
+
| `tags` | `string[]` | ✅ | 内容标签 |
|
|
319
|
+
| `location` | `string` | ❌ | 发布地点 |
|
|
320
|
+
| `publishTime` | `string` | ✅ | 发布时间 |
|
|
321
|
+
| `aspectH` | `number` | ✅ | 卡片图片区高度(px),用于瀑布流错落效果 |
|
|
322
|
+
| `isLiked` / `isCollected` | `boolean` | ❌ | 交互状态 |
|
|
323
|
+
|
|
324
|
+
### 瀑布流双列布局
|
|
325
|
+
|
|
326
|
+
```tsx
|
|
327
|
+
<div className="flex gap-[10px]">
|
|
328
|
+
{/* 左列 */}
|
|
329
|
+
<div className="flex-1 min-w-0 flex flex-col gap-[10px]">
|
|
330
|
+
{items.filter((_, i) => i % 2 === 0).map(item => <FeedCard ... />)}
|
|
331
|
+
</div>
|
|
332
|
+
{/* 右列 */}
|
|
333
|
+
<div className="flex-1 min-w-0 flex flex-col gap-[10px]">
|
|
334
|
+
{items.filter((_, i) => i % 2 === 1).map(item => <FeedCard ... />)}
|
|
335
|
+
</div>
|
|
336
|
+
</div>
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### FeedCard 卡片结构
|
|
340
|
+
|
|
341
|
+
卡片分为**图片区**和**信息区**两部分:
|
|
342
|
+
|
|
343
|
+
**图片区:**
|
|
344
|
+
- 高度由 `aspectH` 动态控制,实现瀑布流错落效果
|
|
345
|
+
- 路线类型(`type === "route"`):底部渐变遮罩 + 左上角路线标签 + 底部站点/距离/时长信息
|
|
346
|
+
- 文章类型:左上角地点标签(`bg-black/40 backdrop-blur-sm`)
|
|
347
|
+
|
|
348
|
+
**信息区(`p-[10px]`):**
|
|
349
|
+
- 标题:`text-[13px] font-medium line-clamp-2`,路线类型前置导航图标
|
|
350
|
+
- 内容摘要:`text-[11px] text-muted-foreground line-clamp-2`
|
|
351
|
+
- 路线站点预览:最多显示3站 + 剩余数量
|
|
352
|
+
- 标签:`#标签` 样式,`bg-accent/8 text-accent`
|
|
353
|
+
- 作者信息 + 点赞按钮(底部一行)
|
|
354
|
+
|
|
355
|
+
### 身份标签样式
|
|
356
|
+
|
|
357
|
+
作者身份标签使用黄色系配色,统一样式:
|
|
358
|
+
|
|
359
|
+
```tsx
|
|
360
|
+
{item.authorLevel && (
|
|
361
|
+
<span className="px-[4px] py-[1px] rounded-[3px] bg-[#FFB800]/15 text-[9px] text-[#B88600] font-medium shrink-0">
|
|
362
|
+
{item.authorLevel}
|
|
363
|
+
</span>
|
|
364
|
+
)}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### 点赞交互
|
|
368
|
+
|
|
369
|
+
使用 `Set<string>` 管理已点赞帖子,点赞/取消切换红心样式:
|
|
370
|
+
|
|
371
|
+
```tsx
|
|
372
|
+
<IconFont
|
|
373
|
+
name="heart"
|
|
374
|
+
variant={isLiked ? "filled" : undefined}
|
|
375
|
+
size="13px"
|
|
376
|
+
className={isLiked ? "text-destructive" : "text-muted-foreground"}
|
|
377
|
+
/>
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### 数字格式化
|
|
381
|
+
|
|
382
|
+
超过 1000 显示 `k`,超过 10000 显示 `w`:
|
|
383
|
+
|
|
384
|
+
```tsx
|
|
385
|
+
function formatNumber(n: number): string {
|
|
386
|
+
if (n >= 10000) return `${(n / 10000).toFixed(1)}w`;
|
|
387
|
+
if (n >= 1000) return `${(n / 1000).toFixed(1)}k`;
|
|
388
|
+
return String(n);
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
## 地图标注规范
|
|
393
|
+
|
|
394
|
+
### POI 标注点实现
|
|
395
|
+
|
|
396
|
+
在 `TencentMap` 上添加自定义标注点,使用绝对定位层叠(**非** TencentMap 内置 marker):
|
|
397
|
+
|
|
398
|
+
```tsx
|
|
399
|
+
<TencentMap className="absolute inset-0" ... />
|
|
400
|
+
<div className="absolute inset-0 pointer-events-none z-[5]">
|
|
401
|
+
{pois.map((poi) => (
|
|
402
|
+
<motion.div
|
|
403
|
+
key={poi.name}
|
|
404
|
+
className="absolute flex flex-col items-center pointer-events-auto"
|
|
405
|
+
style={{ top: poi.top, left: poi.left, transform: "translate(-50%,-100%)" }}
|
|
406
|
+
initial={{ opacity: 0, scale: 0.5, y: 10 }}
|
|
407
|
+
animate={{ opacity: 1, scale: 1, y: 0 }}
|
|
408
|
+
transition={{ delay: 0.2, duration: 0.4, type: "spring" }}
|
|
409
|
+
>
|
|
410
|
+
<div className="bg-accent text-white text-[10px] font-medium px-[6px] py-[2px] rounded-[6px] mb-[3px] whitespace-nowrap shadow-sm">
|
|
411
|
+
{poi.name}
|
|
412
|
+
</div>
|
|
413
|
+
<div className="size-[10px] rounded-full bg-accent shadow-[0_0_0_3px_rgba(54,123,246,0.2)]" />
|
|
414
|
+
</motion.div>
|
|
415
|
+
))}
|
|
416
|
+
</div>
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
**要点:**
|
|
420
|
+
- 标注容器 `pointer-events-none`,单个标注 `pointer-events-auto`
|
|
421
|
+
- 标注定位使用 `transform: translate(-50%,-100%)` 使其锚点在底部中心
|
|
422
|
+
- 进场使用 `motion` 弹簧动画(`scale + y`),增加活力感
|
|
423
|
+
- 标注点样式:`bg-accent` 蓝色圆点 + `0_0_0_3px` 辉光环
|
|
424
|
+
|
|
425
|
+
### 地图控件样式
|
|
426
|
+
|
|
427
|
+
右侧悬浮控件统一使用毛玻璃卡片样式:
|
|
428
|
+
|
|
429
|
+
```tsx
|
|
430
|
+
<button className="size-[40px] rounded-[20px] bg-card/80 shadow-[0px_4px_16px_0px_rgba(0,0,0,0.10)] backdrop-blur-md border border-white/40 flex items-center justify-center active:bg-card/60 transition-colors">
|
|
431
|
+
<IconFont name="layers" size="20px" className="text-foreground" />
|
|
432
|
+
</button>
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
控件组定位在地图右上方:`absolute right-[16px] top-[56px] z-40 flex flex-col gap-[8px]`
|
|
436
|
+
|
|
437
|
+
## 横向滚动列表规范
|
|
438
|
+
|
|
439
|
+
横向滚动列表需要使用**负边距技巧**实现出血效果(内容可以滚动到容器 padding 区域):
|
|
440
|
+
|
|
441
|
+
```tsx
|
|
442
|
+
<div className="flex gap-[10px] overflow-x-auto scrollbar-hide -mx-[16px] px-[16px] pb-[4px]">
|
|
443
|
+
{items.map(item => (
|
|
444
|
+
<div className="shrink-0 w-[140px] rounded-[14px] overflow-hidden bg-card-muted">
|
|
445
|
+
{/* 卡片内容 */}
|
|
446
|
+
</div>
|
|
447
|
+
))}
|
|
448
|
+
</div>
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
**要点:**
|
|
452
|
+
- `-mx-[16px] px-[16px]` 使滚动区域超出父容器 padding,视觉上"出血"
|
|
453
|
+
- `scrollbar-hide` 隐藏滚动条
|
|
454
|
+
- 子项 `shrink-0` 防止被压缩
|
|
455
|
+
- `pb-[4px]` 为阴影留出空间
|
|
456
|
+
|
|
457
|
+
## 筛选标签组规范
|
|
458
|
+
|
|
459
|
+
横向可滚动的筛选标签组,选中/未选中状态切换:
|
|
460
|
+
|
|
461
|
+
```tsx
|
|
462
|
+
<div className="flex gap-[8px] overflow-x-auto scrollbar-hide -mx-[16px] px-[16px]">
|
|
463
|
+
{categories.map((cat) => (
|
|
464
|
+
<button
|
|
465
|
+
key={cat.label}
|
|
466
|
+
onClick={() => setSelected(cat.label)}
|
|
467
|
+
className={cn(
|
|
468
|
+
"shrink-0 flex items-center gap-[4px] px-[14px] py-[6px] rounded-full text-[13px] font-medium transition-all",
|
|
469
|
+
selected === cat.label
|
|
470
|
+
? "bg-primary text-white shadow-sm"
|
|
471
|
+
: "bg-black/[0.04] text-muted-foreground active:bg-black/[0.08]"
|
|
472
|
+
)}
|
|
473
|
+
>
|
|
474
|
+
<IconFont name={cat.icon} size="13px" />
|
|
475
|
+
{cat.label}
|
|
476
|
+
</button>
|
|
477
|
+
))}
|
|
478
|
+
</div>
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
**样式说明:**
|
|
482
|
+
- 选中态:`bg-primary text-white shadow-sm`(主色药丸)
|
|
483
|
+
- 未选中态:`bg-black/[0.04] text-muted-foreground`(浅灰药丸)
|
|
484
|
+
- 同样使用负边距出血技巧
|
|
485
|
+
|
|
486
|
+
## 内容详情页规范(帖子/文章详情)
|
|
487
|
+
|
|
488
|
+
内容详情页同样采用**三段式布局**(底图 + DraggablePanel + bottomBar),与地图联动:
|
|
489
|
+
|
|
490
|
+
### 页面结构
|
|
491
|
+
|
|
492
|
+
```
|
|
493
|
+
┌──────────────────────┐
|
|
494
|
+
│ TencentMap │ ← 底图 + 位置标记点
|
|
495
|
+
│ (位置标记点) │
|
|
496
|
+
├──────────────────────┤
|
|
497
|
+
│ TopToolbar(返回+标题)│ ← DraggablePanel topToolbar
|
|
498
|
+
├──────────────────────┤
|
|
499
|
+
│ 头图(圆角裁切) │ ← shrink-0 固定区域
|
|
500
|
+
│ 标题 + 标签 │
|
|
501
|
+
│ 正文内容 │ ← flex-1 min-h-0 overflow-y-auto
|
|
502
|
+
│ 路线信息卡片(可选) │
|
|
503
|
+
│ 作者信息 │
|
|
504
|
+
│ 互动数据统计 │
|
|
505
|
+
├──────────────────────┤
|
|
506
|
+
│ 底部操作栏 │ ← bottomBar prop
|
|
507
|
+
│ (赞/评论/收藏/分享) │
|
|
508
|
+
└──────────────────────┘
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### 底部操作栏布局
|
|
512
|
+
|
|
513
|
+
```tsx
|
|
514
|
+
<div className="px-[16px] pb-[16px] pt-[8px] border-t border-black/[0.04] shrink-0">
|
|
515
|
+
<div className="flex items-center gap-[16px]">
|
|
516
|
+
{/* 左侧:互动按钮组 */}
|
|
517
|
+
<div className="flex items-center gap-[20px] flex-1">
|
|
518
|
+
<button className="flex flex-col items-center gap-[2px]">
|
|
519
|
+
<IconFont name="heart" size="22px" />
|
|
520
|
+
<span className="text-[10px]">{likes}</span>
|
|
521
|
+
</button>
|
|
522
|
+
{/* ... 评论/收藏/分享 */}
|
|
523
|
+
</div>
|
|
524
|
+
{/* 右侧:主操作按钮(如「查看路线」) */}
|
|
525
|
+
<Btn size="large" variant="primary" label="查看路线" />
|
|
526
|
+
</div>
|
|
527
|
+
</div>
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### 内容标签样式(详情页版本)
|
|
531
|
+
|
|
532
|
+
详情页中标签尺寸略大于卡片版本:
|
|
533
|
+
|
|
534
|
+
```tsx
|
|
535
|
+
<span className="px-[8px] py-[3px] rounded-[6px] bg-accent/8 text-[12px] text-accent font-medium">
|
|
536
|
+
#{tag}
|
|
537
|
+
</span>
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
| 场景 | 字号 | 内边距 | 圆角 |
|
|
541
|
+
|------|------|--------|------|
|
|
542
|
+
| 卡片标签 | `10px` | `px-[6px] py-[1px]` | `4px` |
|
|
543
|
+
| 详情页标签 | `12px` | `px-[8px] py-[3px]` | `6px` |
|
|
544
|
+
|
|
545
|
+
## 视觉设计原则
|
|
546
|
+
|
|
547
|
+
### 核心原则:用色克制
|
|
548
|
+
|
|
549
|
+
**禁止使用彩色色块来区分功能或填充视觉空白。** 即使是"很浅"的彩色背景(如浅蓝、浅绿、浅粉),多个叠加在一起也会显得花哨、廉价。
|
|
550
|
+
|
|
551
|
+
| 错误做法 | 正确做法 |
|
|
552
|
+
|----------|----------|
|
|
553
|
+
| 用不同彩色背景区分功能卡片 | **中性色为主**(`bg-black/[0.04]`、`bg-card-muted`) |
|
|
554
|
+
| 到处用彩色强调 | **仅在关键操作**使用色彩(主按钮、重要状态) |
|
|
555
|
+
| 用颜色填充空白区域 | **留白 + 精致细节**营造呼吸感 |
|
|
556
|
+
| 浅彩色药丸标签堆砌 | 统一使用中性底色,仅选中态上色 |
|
|
557
|
+
|
|
558
|
+
### 风格点缀的正确方式
|
|
559
|
+
|
|
560
|
+
用**形式**而非**颜色**来创造视觉层次和趣味性:
|
|
561
|
+
|
|
562
|
+
1. **倾斜的配图**:装饰图片带轻微倾斜角度(`-rotate-6`),制造微妙的动态感,比彩色背景高级得多
|
|
563
|
+
2. **线形 icon**:默认使用 `IconFont` 的线形变体(非 `filled`),保持视觉轻盈
|
|
564
|
+
3. **透明度层次**:用 `bg-black/[0.04]`、`bg-black/[0.08]` 等黑色透明度变化区分层级,而非不同色相
|
|
565
|
+
4. **精致圆角与阴影**:通过圆角大小、投影深浅营造层次,而非靠颜色差异
|
|
566
|
+
|
|
567
|
+
### 彩色的正确使用场景
|
|
568
|
+
|
|
569
|
+
彩色是**点睛之笔**,只用于真正需要吸引注意力的地方:
|
|
570
|
+
|
|
571
|
+
| 场景 | 允许用色 | 示例 |
|
|
572
|
+
|------|----------|------|
|
|
573
|
+
| 主操作按钮 | ✅ `bg-primary` | 「导航」「确认」 |
|
|
574
|
+
| 强调链接/操作 | ✅ `text-accent` | 「查看全部」「查看路线」 |
|
|
575
|
+
| 危险操作 | ✅ `text-destructive` | 「删除」「退出登录」 |
|
|
576
|
+
| 选中态标签 | ✅ `bg-primary text-white` | 筛选标签选中 |
|
|
577
|
+
| 地图标注点 | ✅ `bg-accent` | POI 蓝色标记 |
|
|
578
|
+
| 功能卡片背景 | ❌ 禁止 | 不要用浅蓝/浅绿/浅粉区分 |
|
|
579
|
+
| 装饰性色块 | ❌ 禁止 | 不要用彩色填充空间 |
|
|
580
|
+
|
|
581
|
+
### 装饰元素规范
|
|
582
|
+
|
|
583
|
+
1. **信任资源原始配色**:专业设计的图片/插图不需要叠加 `opacity` 压暗,它的色彩已经过考量
|
|
584
|
+
2. **用尺寸和位置控制权重**:装饰图片放在边角、控制尺寸(如 36x36),而非用透明度弱化
|
|
585
|
+
3. **每个效果要有目的**:如果 `opacity-40` 只是为了"看起来柔和"但让图片看不清,就不该用
|
package/styles/globals.css
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
@import "tailwindcss";
|
|
2
2
|
|
|
3
|
-
@source "../
|
|
4
|
-
@source "../components";
|
|
3
|
+
@source "../dist";
|
|
5
4
|
|
|
6
5
|
@custom-variant dark (&:is(.dark *));
|
|
7
6
|
|
|
@@ -166,6 +165,15 @@
|
|
|
166
165
|
body {
|
|
167
166
|
@apply bg-background text-foreground;
|
|
168
167
|
font-family: var(--font-family-sans);
|
|
168
|
+
overflow-x: hidden;
|
|
169
|
+
width: 100%;
|
|
170
|
+
max-width: 100vw;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
#root {
|
|
174
|
+
width: 100%;
|
|
175
|
+
max-width: 100vw;
|
|
176
|
+
overflow-x: hidden;
|
|
169
177
|
}
|
|
170
178
|
}
|
|
171
179
|
|