@ucloud-fe/udesign-cli 0.1.3 → 0.2.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.
@@ -219,13 +219,56 @@ class Demo extends React.Component {
219
219
  <!-- MANUAL_START: best-practices -->
220
220
  ## 最佳实践
221
221
 
222
- _(待补充)_
222
+ 1. **根据语义选择 styleType**:成功用 success、警告用 warning、错误用 error
223
+ 2. **重要提示设置 closable={false}**:不可忽略的提示禁止关闭
224
+ 3. **Modal 中常配合 Notice**:在弹窗中使用 Notice 提供额外提示信息
225
+ 4. **区分 Notice 和 Message**:Notice 是嵌入式静态提示,Message 是全局弹出式提示
226
+
227
+ ### 常见场景
228
+
229
+ #### 页面顶部提示
230
+
231
+ ```jsx
232
+ <Notice styleType="warning" closable={false}>
233
+ 当前地域资源配额即将用完,请及时清理或申请扩容。
234
+ </Notice>
235
+ ```
236
+
237
+ #### Modal 中的提示
238
+
239
+ ```jsx
240
+ <Modal visible={visible} title="删除资源" onClose={handleClose}>
241
+ <Notice styleType="error" closable={false}>
242
+ 删除操作不可撤销,请谨慎操作。
243
+ </Notice>
244
+ <Modal.Content>
245
+ 确定要删除该资源吗?
246
+ </Modal.Content>
247
+ </Modal>
248
+ ```
249
+
250
+ #### 带操作的提示
251
+
252
+ ```jsx
253
+ <Notice
254
+ styleType="warning"
255
+ action={<Button size="sm" onClick={handleRenew}>立即续费</Button>}
256
+ >
257
+ 您的资源将于 3 天后到期
258
+ </Notice>
259
+ ```
223
260
  <!-- MANUAL_END: best-practices -->
224
261
 
225
262
  <!-- MANUAL_START: faq -->
226
263
  ## 常见问题
227
264
 
228
- _(待补充)_
265
+ ### Q: Notice 和 Message 的区别?
266
+
267
+ A: Notice 是嵌入在页面中的静态提示组件,不会自动消失;Message 是全局弹出的消息提示,会自动消失。
268
+
269
+ ### Q: 如何隐藏前置图标?
270
+
271
+ A: 将 `icon` 设为 `null` 或 `false`:`<Notice icon={null}>内容</Notice>`。
229
272
  <!-- MANUAL_END: faq -->
230
273
 
231
274
  <!-- MANUAL_START: critical -->
@@ -549,13 +549,53 @@ class Demo extends React.Component {
549
549
  <!-- MANUAL_START: best-practices -->
550
550
  ## 最佳实践
551
551
 
552
- _(待补充)_
552
+ 1. **设置合理的 min/max**:限制输入范围防止非法值
553
+ 2. **使用 onNumberChange 而非 onChange**:`onNumberChange` 只在有效值变化时触发,避免中间态
554
+ 3. **formatter 和 parser 配对使用**:格式化显示时需要同时提供 parser 解析输入
555
+ 4. **使用 suffix 添加单位**:比 formatter 更简单的方式添加单位后缀
556
+
557
+ ### 常见场景
558
+
559
+ #### 资源数量选择
560
+
561
+ ```jsx
562
+ <Form.Item label="实例数量">
563
+ <NumberInput
564
+ value={instanceCount}
565
+ onChange={setInstanceCount}
566
+ min={1}
567
+ max={100}
568
+ step={1}
569
+ />
570
+ </Form.Item>
571
+ ```
572
+
573
+ #### 带单位的数值输入
574
+
575
+ ```jsx
576
+ <Form.Item label="磁盘大小">
577
+ <NumberInput
578
+ value={diskSize}
579
+ onChange={setDiskSize}
580
+ min={10}
581
+ max={32000}
582
+ step={10}
583
+ suffix="GB"
584
+ />
585
+ </Form.Item>
586
+ ```
553
587
  <!-- MANUAL_END: best-practices -->
554
588
 
555
589
  <!-- MANUAL_START: faq -->
556
590
  ## 常见问题
557
591
 
558
- _(待补充)_
592
+ ### Q: onChange 和 onNumberChange 的区别?
593
+
594
+ A: `onChange` 在每次输入时都会触发(包括输入中间态),`onNumberChange` 只在有效数字确定时触发(按钮点击、回车、失焦)。
595
+
596
+ ### Q: 如何自定义合法值的计算方式?
597
+
598
+ A: 使用 `computeValidNumber` 属性,传入一个函数接收当前数值和选项,返回合法的数值。
559
599
  <!-- MANUAL_END: faq -->
560
600
 
561
601
  <!-- MANUAL_START: critical -->
@@ -486,13 +486,41 @@ const Demo = () => (
486
486
  <!-- MANUAL_START: best-practices -->
487
487
  ## 最佳实践
488
488
 
489
- _(待补充)_
489
+ 1. **children 必须是 React 元素**:Popover 需要获取子元素的 ref,文本节点不可用
490
+ 2. **overflow 容器内使用 forwardPopupContainer**:避免弹出层被裁剪
491
+ 3. **click 触发时注意关闭时机**:可能需要受控模式手动管理 visible
492
+ 4. **默认容器为 body**:如需在特定容器内弹出,使用 getPopupContainer
493
+
494
+ ### 常见场景
495
+
496
+ #### 自定义操作菜单
497
+
498
+ ```jsx
499
+ <Popover
500
+ trigger={['click']}
501
+ placement="bottomLeft"
502
+ popup={
503
+ <div>
504
+ <div onClick={handleEdit}>编辑</div>
505
+ <div onClick={handleDelete}>删除</div>
506
+ </div>
507
+ }
508
+ >
509
+ <Button icon="more" />
510
+ </Popover>
511
+ ```
490
512
  <!-- MANUAL_END: best-practices -->
491
513
 
492
514
  <!-- MANUAL_START: faq -->
493
515
  ## 常见问题
494
516
 
495
- _(待补充)_
517
+ ### Q: 弹出层被裁剪或位置偏移?
518
+
519
+ A: 通常是因为父容器有 `overflow: hidden` 或 `overflow: auto`。使用 `forwardPopupContainer` 自动查找安全容器,或使用 `getPopupContainer` 手动指定。
520
+
521
+ ### Q: Popover 中使用表单元素无法聚焦?
522
+
523
+ A: 检查 trigger 是否包含 `focus`,可能导致 focus 事件冲突。建议使用 `click` 触发。
496
524
  <!-- MANUAL_END: faq -->
497
525
 
498
526
  <!-- MANUAL_START: critical -->
@@ -284,13 +284,51 @@ class Demo extends React.Component {
284
284
  <!-- MANUAL_START: best-practices -->
285
285
  ## 最佳实践
286
286
 
287
- _(待补充)_
287
+ 1. **使用 Radio.Group 管理**:避免单独使用 Radio 管理状态
288
+ 2. **使用 options 快速配置**:简单选项直接使用 options 数组
289
+ 3. **根据场景选择 styleType**:表单切换用 button、地域选择用 card
290
+ 4. **button 样式禁用注意 Tooltip**:需要用 fakeDisabled 处理
291
+
292
+ ### 常见场景
293
+
294
+ #### 表单中的单选
295
+
296
+ ```jsx
297
+ <Form.Item label="付费方式">
298
+ <Radio.Group
299
+ value={payType}
300
+ onChange={setPayType}
301
+ styleType="button"
302
+ options={[
303
+ { label: '按月', value: 'monthly' },
304
+ { label: '按年', value: 'yearly' },
305
+ { label: '按需', value: 'demand' }
306
+ ]}
307
+ />
308
+ </Form.Item>
309
+ ```
310
+
311
+ #### 卡片选择
312
+
313
+ ```jsx
314
+ <Radio.Group value={region} onChange={setRegion} styleType="card">
315
+ <Radio value="cn-bj2" title="北京二">华北地域</Radio>
316
+ <Radio value="cn-sh2" title="上海二">华东地域</Radio>
317
+ <Radio value="cn-gd" title="广州">华南地域</Radio>
318
+ </Radio.Group>
319
+ ```
288
320
  <!-- MANUAL_END: best-practices -->
289
321
 
290
322
  <!-- MANUAL_START: faq -->
291
323
  ## 常见问题
292
324
 
293
- _(待补充)_
325
+ ### Q: Radio.Group 的 onChange 返回什么?
326
+
327
+ A: 返回当前选中的 Radio 的 `value` 值。
328
+
329
+ ### Q: styleType 为 card 时如何显示标题?
330
+
331
+ A: 使用 Radio 的 `title` 属性:`<Radio value="a" title="标题">描述内容</Radio>`。
294
332
  <!-- MANUAL_END: faq -->
295
333
 
296
334
  <!-- MANUAL_START: critical -->
@@ -1225,13 +1225,57 @@ class Demo extends React.Component {
1225
1225
  <!-- MANUAL_START: best-practices -->
1226
1226
  ## 最佳实践
1227
1227
 
1228
- _(待补充)_
1228
+ 1. **优先使用 options 属性**:比 children 方式性能更好,且支持虚拟列表
1229
+ 2. **大量选项启用 virtualList**:选项超过 100 条时建议启用
1230
+ 3. **多选默认可清空**:多选模式下 clearable 自动启用
1231
+ 4. **搜索时建议自定义 handleSearch**:默认搜索为模糊匹配,如需精确匹配可自定义
1232
+
1233
+ ### 常见场景
1234
+
1235
+ #### 表单中的下拉选择
1236
+
1237
+ ```jsx
1238
+ <Form.Item label="地域" required>
1239
+ <Select
1240
+ value={region}
1241
+ onChange={setRegion}
1242
+ placeholder="请选择地域"
1243
+ options={regionOptions}
1244
+ />
1245
+ </Form.Item>
1246
+ ```
1247
+
1248
+ #### 带搜索的多选
1249
+
1250
+ ```jsx
1251
+ <Form.Item label="标签">
1252
+ <Select
1253
+ multiple
1254
+ search
1255
+ showSelectAll
1256
+ value={tags}
1257
+ onChange={setTags}
1258
+ options={tagOptions}
1259
+ placeholder="请选择标签"
1260
+ />
1261
+ </Form.Item>
1262
+ ```
1229
1263
  <!-- MANUAL_END: best-practices -->
1230
1264
 
1231
1265
  <!-- MANUAL_START: faq -->
1232
1266
  ## 常见问题
1233
1267
 
1234
- _(待补充)_
1268
+ ### Q: 单选清空后 onChange 回调的值是什么?
1269
+
1270
+ A: 清空时回调值为 `undefined`。
1271
+
1272
+ ### Q: virtualList 为什么不生效?
1273
+
1274
+ A: 虚拟列表仅在使用 `options` 属性时生效,children 方式不支持。
1275
+
1276
+ ### Q: 弹出层位置偏移?
1277
+
1278
+ A: 默认使用 `forwardPopupContainer` 自动查找安全容器。如仍有问题,使用 `popoverProps.getPopupContainer` 手动指定。
1235
1279
  <!-- MANUAL_END: faq -->
1236
1280
 
1237
1281
  <!-- MANUAL_START: critical -->
@@ -558,13 +558,56 @@ class Demo extends React.Component {
558
558
  <!-- MANUAL_START: best-practices -->
559
559
  ## 最佳实践
560
560
 
561
- _(待补充)_
561
+ 1. **设置合理的 min/max/step**:确保 (max - min) 是 step 的整数倍
562
+ 2. **使用 marks 标记关键值**:帮助用户理解值的含义
563
+ 3. **使用 onLastChange 处理异步操作**:避免拖拽过程中频繁触发请求
564
+ 4. **资源配置建议添加单位**:通过 numberInput.suffix 和 tipFormatter 展示单位
565
+
566
+ ### 常见场景
567
+
568
+ #### 资源配置选择
569
+
570
+ ```jsx
571
+ <Form.Item label="CPU 核数">
572
+ <Slider
573
+ value={cpuCount}
574
+ onChange={setCpuCount}
575
+ min={1}
576
+ max={64}
577
+ step={1}
578
+ marks={{ 1: '1', 8: '8', 16: '16', 32: '32', 64: '64' }}
579
+ numberInput={{ suffix: '核' }}
580
+ />
581
+ </Form.Item>
582
+ ```
583
+
584
+ #### 带宽选择
585
+
586
+ ```jsx
587
+ <Form.Item label="带宽">
588
+ <Slider
589
+ value={bandwidth}
590
+ onChange={setBandwidth}
591
+ min={1}
592
+ max={200}
593
+ step={1}
594
+ numberInput={{ suffix: 'Mbps' }}
595
+ tipFormatter={value => `${value} Mbps`}
596
+ />
597
+ </Form.Item>
598
+ ```
562
599
  <!-- MANUAL_END: best-practices -->
563
600
 
564
601
  <!-- MANUAL_START: faq -->
565
602
  ## 常见问题
566
603
 
567
- _(待补充)_
604
+ ### Q: range 模式下 numberInput 为什么不显示?
605
+
606
+ A: numberInput 仅在 range 为 false 时生效。range 模式不支持 NumberInput。
607
+
608
+ ### Q: step 设置后为什么报错?
609
+
610
+ A: 确保 (max - min) 是 step 的整数倍,且 step 大于 0。
568
611
  <!-- MANUAL_END: faq -->
569
612
 
570
613
  <!-- MANUAL_START: critical -->
@@ -289,13 +289,48 @@ class Demo extends React.Component {
289
289
  <!-- MANUAL_START: best-practices -->
290
290
  ## 最佳实践
291
291
 
292
- _(待补充)_
292
+ 1. **明确语义**:使用 onText/offText 让用户清楚开关的含义
293
+ 2. **即时生效的操作使用 Switch**:切换后立即生效的场景适合用 Switch
294
+ 3. **表单提交使用 Checkbox**:需要表单提交才生效的场景更适合用 Checkbox
295
+
296
+ ### 常见场景
297
+
298
+ #### 表单中的开关
299
+
300
+ ```jsx
301
+ <Form.Item label="自动续费">
302
+ <Switch
303
+ checked={autoRenew}
304
+ onChange={setAutoRenew}
305
+ onText="是"
306
+ offText="否"
307
+ />
308
+ </Form.Item>
309
+ ```
310
+
311
+ #### 功能开关
312
+
313
+ ```jsx
314
+ <Form.Item label="启用监控">
315
+ <Switch
316
+ checked={monitorEnabled}
317
+ onChange={enabled => {
318
+ setMonitorEnabled(enabled);
319
+ if (enabled) {
320
+ Message.success('监控已开启');
321
+ }
322
+ }}
323
+ />
324
+ </Form.Item>
325
+ ```
293
326
  <!-- MANUAL_END: best-practices -->
294
327
 
295
328
  <!-- MANUAL_START: faq -->
296
329
  ## 常见问题
297
330
 
298
- _(待补充)_
331
+ ### Q: Switch 和 Checkbox 的使用场景区别?
332
+
333
+ A: Switch 适用于切换后立即生效的场景(如开启/关闭功能),Checkbox 适用于需要表单提交后才生效的场景(如同意协议)。
299
334
  <!-- MANUAL_END: faq -->
300
335
 
301
336
  <!-- MANUAL_START: critical -->
@@ -2931,13 +2931,59 @@ class Demo extends React.Component {
2931
2931
  <!-- MANUAL_START: best-practices -->
2932
2932
  ## 最佳实践
2933
2933
 
2934
- _(待补充)_
2934
+ 1. **务必设置 rowKey 或保证数据有 key**:避免使用 index 作为 key
2935
+ 2. **大数据量使用服务端分页**:配合 `doNotHandleCondition` 和 `onConditionChange`
2936
+ 3. **固定列时设置列宽**:使用 `fixed` 时需要给列设置 `width`
2937
+ 4. **Loading 包裹 Table**:使用 Loading 组件展示加载状态
2938
+
2939
+ ### 常见场景
2940
+
2941
+ #### 资源列表页
2942
+
2943
+ ```jsx
2944
+ const [loading, setLoading] = useState(true);
2945
+ const [dataSource, setDataSource] = useState([]);
2946
+ const [selectedRowKeys, setSelectedRowKeys] = useState([]);
2947
+ const [page, setPage] = useState(1);
2948
+
2949
+ <Loading loading={loading}>
2950
+ <Table
2951
+ rowKey="resourceId"
2952
+ columns={columns}
2953
+ dataSource={dataSource}
2954
+ rowSelection={{
2955
+ selectedRowKeys,
2956
+ onChange: setSelectedRowKeys
2957
+ }}
2958
+ pagination={{
2959
+ current: page,
2960
+ pageSize: 10,
2961
+ total,
2962
+ onChange: setPage
2963
+ }}
2964
+ contextMenu={record => [
2965
+ { label: '编辑', onClick: () => handleEdit(record) },
2966
+ { label: '删除', onClick: () => handleDelete(record) }
2967
+ ]}
2968
+ />
2969
+ </Loading>
2970
+ ```
2935
2971
  <!-- MANUAL_END: best-practices -->
2936
2972
 
2937
2973
  <!-- MANUAL_START: faq -->
2938
2974
  ## 常见问题
2939
2975
 
2940
- _(待补充)_
2976
+ ### Q: 表格数据异常、行错乱?
2977
+
2978
+ A: 检查 key 是否唯一。务必使用 `rowKey` 指定唯一标识字段,不要依赖 index。
2979
+
2980
+ ### Q: 如何实现后端分页?
2981
+
2982
+ A: 设置 `doNotHandleCondition`,在 `onConditionChange` 和 `pagination.onChange` 中请求后端数据。
2983
+
2984
+ ### Q: 固定列后横向无法滚动?
2985
+
2986
+ A: 检查是否设置了 `scroll.x`,且 `scroll.x` 的值需要大于表格容器宽度。
2941
2987
  <!-- MANUAL_END: faq -->
2942
2988
 
2943
2989
  <!-- MANUAL_START: critical -->
@@ -612,13 +612,57 @@ const Demo = () => {
612
612
  <!-- MANUAL_START: best-practices -->
613
613
  ## 最佳实践
614
614
 
615
- _(待补充)_
615
+ 1. **Pane 必须有唯一 key**:这是 Tabs 正常工作的前提
616
+ 2. **表单 Tab 使用受控模式**:方便校验时切换到有错误的 tab
617
+ 3. **按需销毁内容**:大量内容的 Tab 使用 `destroyInactiveTabPane` 减少内存占用
618
+ 4. **首次不需要渲染的使用懒加载**:默认非活动 tab 不会渲染,切换到才渲染
619
+
620
+ ### 常见场景
621
+
622
+ #### 资源详情页
623
+
624
+ ```jsx
625
+ <Tabs defaultActiveKey="overview">
626
+ <Tabs.Pane key="overview" tab="概览">
627
+ <OverviewPanel />
628
+ </Tabs.Pane>
629
+ <Tabs.Pane key="monitor" tab="监控">
630
+ <MonitorPanel />
631
+ </Tabs.Pane>
632
+ <Tabs.Pane key="log" tab="日志">
633
+ <LogPanel />
634
+ </Tabs.Pane>
635
+ </Tabs>
636
+ ```
637
+
638
+ #### 配置切换
639
+
640
+ ```jsx
641
+ <Tabs
642
+ activeKey={configTab}
643
+ onChange={setConfigTab}
644
+ styleType="ink"
645
+ >
646
+ <Tabs.Pane key="basic" tab="基础配置">
647
+ <BasicConfigForm />
648
+ </Tabs.Pane>
649
+ <Tabs.Pane key="advanced" tab="高级配置">
650
+ <AdvancedConfigForm />
651
+ </Tabs.Pane>
652
+ </Tabs>
653
+ ```
616
654
  <!-- MANUAL_END: best-practices -->
617
655
 
618
656
  <!-- MANUAL_START: faq -->
619
657
  ## 常见问题
620
658
 
621
- _(待补充)_
659
+ ### Q: key 被 React 修改导致切换失败?
660
+
661
+ A: 在某些情况下 React 会修改 key,可以使用 `tabKey` 属性替代。
662
+
663
+ ### Q: Tab 数量多时如何处理?
664
+
665
+ A: 组件内置了滚动功能,当 tab 数量超出容器宽度时会自动显示滚动按钮。
622
666
  <!-- MANUAL_END: faq -->
623
667
 
624
668
  <!-- MANUAL_START: critical -->
@@ -412,13 +412,48 @@ class Demo extends React.Component {
412
412
  <!-- MANUAL_START: best-practices -->
413
413
  ## 最佳实践
414
414
 
415
- _(待补充)_
415
+ 1. **用颜色表达语义**:绿色表示成功/运行,红色表示错误/异常,黄色表示警告
416
+ 2. **使用 icon 增强标识**:配合 `circle-fill` 图标标识状态
417
+ 3. **标签列表使用 closable**:可删除的标签场景
418
+ 4. **使用 customStyle 扩展颜色**:内置颜色不满足时自定义
419
+
420
+ ### 常见场景
421
+
422
+ #### 资源状态标签
423
+
424
+ ```jsx
425
+ const statusMap = {
426
+ running: { styleType: 'green', icon: 'circle-fill', text: '运行中' },
427
+ stopped: { styleType: 'gray', icon: 'circle-fill', text: '已停止' },
428
+ error: { styleType: 'red', icon: 'circle-fill', text: '异常' }
429
+ };
430
+
431
+ const { styleType, icon, text } = statusMap[status];
432
+ <Tag styleType={styleType} icon={icon}>{text}</Tag>
433
+ ```
434
+
435
+ #### 标签列表
436
+
437
+ ```jsx
438
+ {tags.map(tag => (
439
+ <Tag
440
+ key={tag.id}
441
+ closable
442
+ onClose={() => handleRemoveTag(tag.id)}
443
+ styleType="blue"
444
+ >
445
+ {tag.name}
446
+ </Tag>
447
+ ))}
448
+ ```
416
449
  <!-- MANUAL_END: best-practices -->
417
450
 
418
451
  <!-- MANUAL_START: faq -->
419
452
  ## 常见问题
420
453
 
421
- _(待补充)_
454
+ ### Q: 有哪些可用的 styleType?
455
+
456
+ A: 常用的有 `gray`、`green`、`yellow`、`red`、`blue`、`orange`、`purple`、`cyan` 等,以及对应的 `-crisped` 后缀变体。可通过 `Tag.StyleType` 获取完整列表。
422
457
  <!-- MANUAL_END: faq -->
423
458
 
424
459
  <!-- MANUAL_START: critical -->
@@ -124,13 +124,51 @@ class Demo extends React.Component {
124
124
  <!-- MANUAL_START: best-practices -->
125
125
  ## 最佳实践
126
126
 
127
- _(待补充)_
127
+ 1. **使用 rows 控制初始高度**:通过 `rows` 设置合理的初始显示行数
128
+ 2. **设置 maxLength 限制长度**:避免用户输入过长内容
129
+ 3. **onChange 返回原生 event**:取值用 `e.target.value`,与 Input 一致
130
+ 4. **短文本用 Input**:单行文本输入使用 Input,多行才用 Textarea
131
+
132
+ ### 常见场景
133
+
134
+ #### 表单中的描述输入
135
+
136
+ ```jsx
137
+ <Form.Item label="描述">
138
+ <Textarea
139
+ value={description}
140
+ onChange={e => setDescription(e.target.value)}
141
+ placeholder="请输入描述信息"
142
+ rows={4}
143
+ />
144
+ </Form.Item>
145
+ ```
146
+
147
+ #### 备注输入
148
+
149
+ ```jsx
150
+ <Form.Item label="备注">
151
+ <Textarea
152
+ value={remark}
153
+ onChange={e => setRemark(e.target.value)}
154
+ placeholder="请输入备注(选填)"
155
+ rows={3}
156
+ maxLength={500}
157
+ />
158
+ </Form.Item>
159
+ ```
128
160
  <!-- MANUAL_END: best-practices -->
129
161
 
130
162
  <!-- MANUAL_START: faq -->
131
163
  ## 常见问题
132
164
 
133
- _(待补充)_
165
+ ### Q: 如何固定高度不允许拖拽?
166
+
167
+ A: 设置 `style={{ resize: 'none' }}`。
168
+
169
+ ### Q: onChange 的参数是什么?
170
+
171
+ A: 与 Input 一致,接收原生 `ChangeEvent`,通过 `e.target.value` 获取值。
134
172
  <!-- MANUAL_END: faq -->
135
173
 
136
174
  <!-- MANUAL_START: critical -->
@@ -235,13 +235,50 @@ const Demo = () => (
235
235
  <!-- MANUAL_START: best-practices -->
236
236
  ## 最佳实践
237
237
 
238
- _(待补充)_
238
+ 1. **简短提示用 Tooltip**:复杂内容用 Popover
239
+ 2. **暗色主题用于操作提示**:按钮等操作元素的提示建议用 dark 主题
240
+ 3. **禁用按钮配合 fakeDisabled**:确保 Tooltip 在禁用按钮上也能正常工作
241
+ 4. **popup 为 null 时不渲染**:可以通过传入 null 条件性隐藏 Tooltip
242
+
243
+ ### 常见场景
244
+
245
+ #### 按钮操作说明
246
+
247
+ ```jsx
248
+ <Tooltip popup="上传文件到服务器">
249
+ <Button icon="upload" shape="circle" />
250
+ </Tooltip>
251
+ ```
252
+
253
+ #### 禁用按钮提示(配合 fakeDisabled)
254
+
255
+ ```jsx
256
+ <Tooltip popup="该操作暂不可用">
257
+ <Button disabled fakeDisabled>操作</Button>
258
+ </Tooltip>
259
+ ```
260
+
261
+ #### 文本截断提示
262
+
263
+ ```jsx
264
+ <Tooltip popup={fullText}>
265
+ <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: 200 }}>
266
+ {fullText}
267
+ </span>
268
+ </Tooltip>
269
+ ```
239
270
  <!-- MANUAL_END: best-practices -->
240
271
 
241
272
  <!-- MANUAL_START: faq -->
242
273
  ## 常见问题
243
274
 
244
- _(待补充)_
275
+ ### Q: 禁用的按钮无法显示 Tooltip?
276
+
277
+ A: 原生 `disabled` 会屏蔽所有事件。解决方案是同时添加 `disabled` 和 `fakeDisabled`。
278
+
279
+ ### Q: Tooltip 位置偏移?
280
+
281
+ A: 参考 Popover 的容器问题,使用 `forwardPopupContainer` 或 `getPopupContainer` 处理。
245
282
  <!-- MANUAL_END: faq -->
246
283
 
247
284
  <!-- MANUAL_START: critical -->