@tfdesign/b-end 1.0.14 → 1.0.16

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "system": "b-end",
3
3
  "skill": "tfds",
4
- "generatedAt": "2026-05-13T05:01:47.099Z",
4
+ "generatedAt": "2026-05-13T13:51:39.617Z",
5
5
  "purpose": "轻量组件与页面模板目录。AI 先读本文件做选型;确定命中组件后,再到 components.index.json 按 id 读取 props / rules / examples。",
6
6
  "counts": {
7
7
  "total": 47,
@@ -262,8 +262,8 @@
262
262
  "order card",
263
263
  "图片文字卡片"
264
264
  ],
265
- "ruleCount": 36,
266
- "exampleCount": 26,
265
+ "ruleCount": 39,
266
+ "exampleCount": 29,
267
267
  "hasCode": false,
268
268
  "detailRef": "components.index.json#card"
269
269
  },
@@ -549,8 +549,8 @@
549
549
  "Input Output 切换",
550
550
  "Playground 切换"
551
551
  ],
552
- "ruleCount": 18,
553
- "exampleCount": 15,
552
+ "ruleCount": 20,
553
+ "exampleCount": 17,
554
554
  "hasCode": false,
555
555
  "detailRef": "components.index.json#tabs"
556
556
  },
@@ -1634,7 +1634,7 @@
1634
1634
  "工单处理框架",
1635
1635
  "CustomerServiceWorkspaceFrame"
1636
1636
  ],
1637
- "ruleCount": 25,
1637
+ "ruleCount": 26,
1638
1638
  "exampleCount": 0,
1639
1639
  "hasCode": true,
1640
1640
  "detailRef": "components.index.json#customer-service-workspace-frame"
@@ -1675,7 +1675,7 @@
1675
1675
  "数据资产管理",
1676
1676
  "筛选栏"
1677
1677
  ],
1678
- "ruleCount": 36,
1678
+ "ruleCount": 37,
1679
1679
  "exampleCount": 0,
1680
1680
  "hasCode": true,
1681
1681
  "detailRef": "components.index.json#base-page-frame"
@@ -1767,7 +1767,7 @@
1767
1767
  "kind": "page-pattern",
1768
1768
  "name": "AI 对话页",
1769
1769
  "category": "page-pattern",
1770
- "description": "独立的 AI 助手会话详情页:顶导栏(返回 + 标题 + 操作按钮组)+ 800px 居中消息流 + 底部吸底 ChatInput。所有消息(用户 / AI)一律用 ChatMessage 渲染(用户走 role=\"user\",AI 走 header=true)。支持 phase 切换:默认 chat 阶段展示端到端 mock 对话,点「新会话」清空回到 welcome 欢迎屏(CATCAT 头像 + 推荐 chip)。关键词驱动完整任务流:「整理 / 分析 / 生成」→ 任务规划卡用户点开始流式执行流(每 600ms 推一步) 同条消息更新为总结+产物+追问。",
1770
+ "description": "独立的 AI 助手会话详情页:顶导栏(返回 + 标题 + 操作按钮组)+ 800px 居中消息流 + 底部吸底 ChatInput。所有消息(用户 / AI)一律用 ChatMessage 渲染(用户走 role=\"user\",AI 走 header=true)。支持 phase 切换:默认 welcome 阶段直接展示新对话页(CATCAT 头像 + 推荐 chip);点「新会话」后也回到该欢迎屏。若外部显式传入 initialMessages,则可直接打开已有会话。底部输入框真实发送后自动编排完整 AI 输出链路:用户气泡 AI 深度思考 人工澄清确认卡片AI 自动执行流加载/完成AI 结论 + 最终产物 + 追问;追问继续追加在同一会话流,并自动继承上一轮账号、环境、Mock、结论和产物上下文。",
1771
1771
  "import": {
1772
1772
  "from": "@tfdesign/b-end",
1773
1773
  "default": "ChatConversationPattern"
@@ -1796,9 +1796,9 @@
1796
1796
  "推荐 chip",
1797
1797
  "新会话",
1798
1798
  "流式执行",
1799
- "任务规划卡"
1799
+ "自动编排"
1800
1800
  ],
1801
- "ruleCount": 19,
1801
+ "ruleCount": 22,
1802
1802
  "exampleCount": 0,
1803
1803
  "hasCode": true,
1804
1804
  "detailRef": "components.index.json#chat-conversation-page"
@@ -35,6 +35,13 @@ const DEFAULT_ANIMATED_DESCRIPTION = '基于业务目标、历史数据和运营
35
35
  const DEFAULT_ANIMATED_ICON = 'magic-wand-01-stroked';
36
36
  const DEFAULT_ANIMATED_BADGE = 'AI 推荐';
37
37
  const DEFAULT_ANIMATED_ACTION_TEXT = '立即体验';
38
+ const DEFAULT_INFO3_TITLE = '抖音生服订单已签收后还能申请退款吗?';
39
+ const DEFAULT_INFO3_DESCRIPTION = '已签收订单仍可在 7 天无理由保障期内发起退款,需提供商品照片或服务异常说明,平台介入后通常 24h 内出结果。';
40
+ const DEFAULT_INFO3_STATUS = '生效';
41
+ const DEFAULT_INFO3_META_ITEMS = [
42
+ { iconName: 'tag-01-stroked', value: 'QA' },
43
+ { iconName: 'clock-stroked', value: '2026-04-21 10:30' },
44
+ ];
38
45
  const DEFAULT_INFO_STATS = [
39
46
  { iconName: 'users-01-stroked', value: '128.6K', tooltip: '累计启用该能力的创作者数量' },
40
47
  { iconName: 'star-01-stroked', value: '4.9', tooltip: '用户综合评分,满分 5 分' },
@@ -53,6 +60,12 @@ const CARD_SURFACE = [
53
60
  'hover:shadow-card',
54
61
  ].join(' ');
55
62
 
63
+ const CARD_INTERACTIVE = [
64
+ 'cursor-pointer',
65
+ 'focus:outline-none',
66
+ 'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blueGrey-300',
67
+ ].join(' ');
68
+
56
69
  const DATA_CARD = 'min-h-[var(--size-card-min-height)] flex-col justify-between gap-7';
57
70
  const PRODUCT_CARD = 'min-h-[96px] flex-row items-center gap-5 [&_.tfds-tag.bg-white]:hidden';
58
71
  const INFO_CARD = 'min-h-[148px] flex-row items-start gap-6 p-6 [&_.tfds-tag.bg-white]:hidden';
@@ -266,6 +279,34 @@ const ANIMATED_TONE_STYLE = {
266
279
  },
267
280
  };
268
281
 
282
+ /* ── 信息卡片3 ── */
283
+ const INFO3_CARD = [
284
+ 'min-h-[148px] flex-col gap-2 rounded-lg p-4',
285
+ 'text-left',
286
+ ].join(' ');
287
+ const INFO3_HEADER = 'flex items-start justify-between gap-2';
288
+ const INFO3_TITLE = [
289
+ 'm-0 min-w-0 flex-1 text-base [font-weight:var(--font-semibold)] leading-6 text-blueGrey-900',
290
+ 'line-clamp-2',
291
+ ].join(' ');
292
+ const INFO3_DESCRIPTION = 'm-0 line-clamp-2 text-sm font-normal leading-[22px] text-blueGrey-600';
293
+ const INFO3_META = 'flex min-w-0 flex-wrap items-center gap-3 text-xs font-normal leading-4 text-blueGrey-500';
294
+ const INFO3_META_ITEM = 'inline-flex min-w-0 items-center gap-1';
295
+ const INFO3_META_AVATAR = [
296
+ '[&>img]:!scale-100',
297
+ '[&>img]:!translate-y-0',
298
+ '[&>img]:!object-center',
299
+ ].join(' ');
300
+
301
+ const INFO3_STATUS_VARIANT = {
302
+ 生效: 'green',
303
+ 已发布: 'green',
304
+ 草稿: 'grey',
305
+ 审核中: 'orange',
306
+ 已停用: 'red',
307
+ 已下架: 'red',
308
+ };
309
+
269
310
  /* ── 底部 ── */
270
311
  const FOOTER = 'flex w-full items-center justify-between gap-4';
271
312
  const ACTION = [
@@ -279,9 +320,30 @@ const ACTION = [
279
320
  'group-hover:border-black group-hover:text-black',
280
321
  ].join(' ');
281
322
 
323
+ function getInteractiveCardProps(onAction, actionAriaLabel) {
324
+ if (typeof onAction !== 'function') return {};
325
+
326
+ return {
327
+ role: 'button',
328
+ tabIndex: 0,
329
+ 'aria-label': actionAriaLabel,
330
+ onClick: onAction,
331
+ onKeyDown: (event) => {
332
+ if (event.key !== 'Enter' && event.key !== ' ') return;
333
+ event.preventDefault();
334
+ onAction(event);
335
+ },
336
+ };
337
+ }
338
+
339
+ function handleNestedActionClick(event, onAction) {
340
+ event.stopPropagation();
341
+ onAction?.(event);
342
+ }
343
+
282
344
  /**
283
345
  * Card — 业务信息摘要卡片
284
- * @prop {'data'|'product'|'info'|'info2'|'animated'} [type='data'] — 卡片类型:data 为数据卡片,product 为商品卡片,info 为信息卡片1,info2 为信息卡片2;animated 为旧版兼容别名
346
+ * @prop {'data'|'product'|'info'|'info2'|'info3'|'animated'} [type='data'] — 卡片类型:data 为数据卡片,product 为商品卡片,info 为信息卡片1,info2 为信息卡片2,info3 为信息卡片3;animated 为旧版兼容别名
285
347
  * @prop {string} [title] — 卡片标题,商品卡片中为商品标题
286
348
  * @prop {string} [description] — 卡片描述,商品卡片中为数量、价格等辅助信息
287
349
  * @prop {'white'|'grey'} [color='white'] — 卡片颜色:white 为白底样式,适用于灰/浅灰/非纯白容器;grey 为 Blue Grey 灰底样式,适用于纯白容器
@@ -302,20 +364,25 @@ const ACTION = [
302
364
  * @prop {string|null} [animatedIconName='magic-wand-01-stroked'] — 信息卡片2左上角图标
303
365
  * @prop {string|null} [animatedBadge='AI 推荐'] — 信息卡片2左下角徽标
304
366
  * @prop {string|null} [animatedActionText='立即体验'] — 信息卡片2底部右侧操作文案
367
+ * @prop {boolean} [selected=false] — 信息卡片3整体选中态,使用浅绿色背景与 brand 描边
368
+ * @prop {string} [status='生效'] — 信息卡片3右上角状态标签文案
369
+ * @prop {'brand'|'red'|'orange'|'yellow'|'green'|'cyan'|'blue'|'purple'|'pink'|'teal'|'grey'|'white'|'ai'|null} [statusVariant=null] — 信息卡片3右上角状态标签颜色;不传时按常见状态自动映射
370
+ * @prop {Array<{iconName?: string, value: string, avatarSrc?: string, avatarAlt?: string}>|null} [metaItems=null] — 信息卡片3底部元信息,适合类型、更新时间、创建人等密集信息
305
371
  * @prop {string} [infoMetaLabel='段然'] — 信息卡片1底部人名昵称
306
372
  * @prop {string} [infoMetaBadge='官方能力'] — 信息卡片1右上角徽标文案
307
373
  * @prop {'brand'|'red'|'orange'|'yellow'|'green'|'cyan'|'blue'|'purple'|'pink'|'teal'|'grey'|'white'|'ai'|null} [infoMetaBadgeVariant=null] — 信息卡片1右上角徽标颜色;黑底白标时可覆盖,彩色浅底时固定 grey
308
374
  * @prop {string} [infoMetaAvatarSrc] — 信息卡片1底部人名昵称头像
309
375
  * @prop {string} [infoMetaAvatarAlt='用户头像'] — 信息卡片1底部人名昵称头像无障碍文案
310
376
  * @prop {Array<{iconName: string, value: string, tooltip?: string}>|null} [infoStats=null] — 信息卡片1底部右侧辅助项数组,最多展示 3 项;tooltip 用于 hover/focus 展示详细说明
311
- * @prop {function|null} [onAction=null] — 数据卡片右侧箭头操作回调
312
- * @prop {string} [actionAriaLabel='查看卡片详情'] — 右侧操作按钮无障碍文案
377
+ * @prop {function|null} [onAction=null] — 详情跳转 / 进入二级页回调;传入后整张卡片默认可点击
378
+ * @prop {string} [actionAriaLabel='查看卡片详情'] — 整卡与右侧操作按钮共用的无障碍文案
313
379
  * @prop {string} [className=''] — 附加类名
314
380
  * @prop {object} [style] — 内联样式
315
381
  *
316
382
  * 通用 tags 仅用于数据卡片左下角;商品和信息卡片1不渲染标题左侧通用标签。
317
383
  * 所有 Card 分类都遵循“背景反衬”规则:父容器是纯白色时必须使用灰底卡;父容器是灰色、浅灰色或其他非纯白底时必须使用白底卡。
318
384
  * 卡片容器默认半透明白底,hover 后补满白底并出现业务卡片专用投影;灰底卡保持灰底与灰描边,并保留相同投影反馈。
385
+ * 所有 Card 分类只要传入 onAction,就默认整张卡片区域可点击进入;右下角箭头仅作为进入提示,不是唯一热区。
319
386
  */
320
387
  export default function Card({
321
388
  type = 'data',
@@ -345,13 +412,17 @@ export default function Card({
345
412
  animatedIconName = DEFAULT_ANIMATED_ICON,
346
413
  animatedBadge = DEFAULT_ANIMATED_BADGE,
347
414
  animatedActionText = DEFAULT_ANIMATED_ACTION_TEXT,
415
+ selected = false,
416
+ status = DEFAULT_INFO3_STATUS,
417
+ statusVariant = null,
418
+ metaItems = null,
348
419
  children,
349
420
  onAction,
350
421
  actionAriaLabel = '查看卡片详情',
351
422
  className = '',
352
423
  style,
353
424
  }) {
354
- const resolvedType = type === 'product' || type === 'info'
425
+ const resolvedType = type === 'product' || type === 'info' || type === 'info3'
355
426
  ? type
356
427
  : (type === 'info2' || type === 'animated' ? 'info2' : 'data');
357
428
  // Card 颜色依赖父级背景做反衬,不按卡片类型区分。
@@ -359,12 +430,12 @@ export default function Card({
359
430
  const resolvedTitle = title || (
360
431
  resolvedType === 'product'
361
432
  ? DEFAULT_PRODUCT_TITLE
362
- : (resolvedType === 'info' ? DEFAULT_INFO_TITLE : (resolvedType === 'info2' ? DEFAULT_ANIMATED_TITLE : DEFAULT_DATA_TITLE))
433
+ : (resolvedType === 'info' ? DEFAULT_INFO_TITLE : (resolvedType === 'info2' ? DEFAULT_ANIMATED_TITLE : (resolvedType === 'info3' ? DEFAULT_INFO3_TITLE : DEFAULT_DATA_TITLE)))
363
434
  );
364
435
  const resolvedDescription = description || (
365
436
  resolvedType === 'product'
366
437
  ? DEFAULT_PRODUCT_DESCRIPTION
367
- : (resolvedType === 'info' ? DEFAULT_INFO_DESCRIPTION : (resolvedType === 'info2' ? DEFAULT_ANIMATED_DESCRIPTION : DEFAULT_DATA_DESCRIPTION))
438
+ : (resolvedType === 'info' ? DEFAULT_INFO_DESCRIPTION : (resolvedType === 'info2' ? DEFAULT_ANIMATED_DESCRIPTION : (resolvedType === 'info3' ? DEFAULT_INFO3_DESCRIPTION : DEFAULT_DATA_DESCRIPTION)))
368
439
  );
369
440
  const resolvedStats = (Array.isArray(stats) && stats.length > 0 ? stats : DEFAULT_STATS).slice(0, 3);
370
441
  const resolvedTags = resolvedType === 'data'
@@ -394,24 +465,34 @@ export default function Card({
394
465
  const resolvedAnimatedTone = Object.prototype.hasOwnProperty.call(ANIMATED_TONE_STYLE, animatedTone)
395
466
  ? animatedTone
396
467
  : 'grey';
468
+ const resolvedInfo3MetaItems = Array.isArray(metaItems) && metaItems.length > 0
469
+ ? metaItems
470
+ : DEFAULT_INFO3_META_ITEMS;
471
+ const resolvedStatusVariant = INFO_BADGE_VARIANTS.has(statusVariant)
472
+ ? statusVariant
473
+ : (INFO3_STATUS_VARIANT[status] || 'grey');
474
+ const isClickable = typeof onAction === 'function';
397
475
  const cardStyle = {
398
476
  ...(CARD_VARIANT_STYLE[resolvedColor] || {}),
399
477
  ...(resolvedType === 'info' && resolvedInfoIconStyle === 'tone' ? INFO_ICON_TONE_HOVER_STYLE[resolvedInfoIconTone] : {}),
400
478
  ...(resolvedType === 'info2' ? ANIMATED_TONE_STYLE[resolvedAnimatedTone] : {}),
401
479
  ...style,
402
480
  };
481
+ const interactiveProps = getInteractiveCardProps(onAction, actionAriaLabel);
403
482
 
404
483
  if (resolvedType === 'product') {
405
484
  return (
406
485
  <article
407
486
  className={[
408
487
  CARD_SURFACE,
488
+ isClickable ? CARD_INTERACTIVE : null,
409
489
  resolvedInfoLayout === 'icon-right' ? PRODUCT_CARD_ICON_RIGHT : PRODUCT_CARD,
410
490
  CARD_VARIANT_CLASS[resolvedColor],
411
491
  className,
412
492
  ].filter(Boolean).join(' ')}
413
493
  style={cardStyle}
414
494
  data-tfds-component="Card"
495
+ {...interactiveProps}
415
496
  >
416
497
  <div className={PRODUCT_IMAGE_FRAME}>
417
498
  <img
@@ -465,9 +546,10 @@ export default function Card({
465
546
 
466
547
  return (
467
548
  <article
468
- className={[CARD_SURFACE, INFO_CARD, CARD_VARIANT_CLASS[resolvedColor], className].filter(Boolean).join(' ')}
549
+ className={[CARD_SURFACE, isClickable ? CARD_INTERACTIVE : null, INFO_CARD, CARD_VARIANT_CLASS[resolvedColor], className].filter(Boolean).join(' ')}
469
550
  style={cardStyle}
470
551
  data-tfds-component="Card"
552
+ {...interactiveProps}
471
553
  >
472
554
  {resolvedInfoLayout === 'icon-right' ? null : iconNode}
473
555
 
@@ -519,9 +601,10 @@ export default function Card({
519
601
  if (resolvedType === 'info2') {
520
602
  return (
521
603
  <article
522
- className={[CARD_SURFACE, ANIMATED_CARD, CARD_VARIANT_CLASS[resolvedColor], className].filter(Boolean).join(' ')}
604
+ className={[CARD_SURFACE, isClickable ? CARD_INTERACTIVE : null, ANIMATED_CARD, CARD_VARIANT_CLASS[resolvedColor], className].filter(Boolean).join(' ')}
523
605
  style={cardStyle}
524
606
  data-tfds-component="Card"
607
+ {...interactiveProps}
525
608
  >
526
609
  <div className={ANIMATED_CONTENT}>
527
610
  <div className={ANIMATED_HEADER}>
@@ -558,7 +641,7 @@ export default function Card({
558
641
  type="button"
559
642
  className={ACTION}
560
643
  aria-label={animatedActionText || actionAriaLabel}
561
- onClick={onAction}
644
+ onClick={(event) => handleNestedActionClick(event, onAction)}
562
645
  disabled={!onAction}
563
646
  >
564
647
  <Icon name="arrow-narrow-right-stroked" size={16} />
@@ -570,11 +653,64 @@ export default function Card({
570
653
  );
571
654
  }
572
655
 
656
+ if (resolvedType === 'info3') {
657
+ return (
658
+ <article
659
+ className={[
660
+ CARD_SURFACE,
661
+ isClickable ? CARD_INTERACTIVE : null,
662
+ INFO3_CARD,
663
+ CARD_VARIANT_CLASS[resolvedColor],
664
+ className,
665
+ ].filter(Boolean).join(' ')}
666
+ style={{
667
+ ...cardStyle,
668
+ ...(selected ? { borderColor: 'var(--color-brand-500, #00B384)' } : {}),
669
+ }}
670
+ data-tfds-component="Card"
671
+ data-tfds-card-type="info3"
672
+ aria-selected={selected || undefined}
673
+ {...interactiveProps}
674
+ >
675
+ <div className={INFO3_HEADER}>
676
+ <h3 className={INFO3_TITLE}>{resolvedTitle}</h3>
677
+ {status ? (
678
+ <Tag variant={resolvedStatusVariant} className="shrink-0">
679
+ {status}
680
+ </Tag>
681
+ ) : null}
682
+ </div>
683
+
684
+ <p className={INFO3_DESCRIPTION}>{resolvedDescription}</p>
685
+
686
+ <div className={INFO3_META}>
687
+ {resolvedInfo3MetaItems.map((item, index) => (
688
+ <span key={`${item.value}-${index}`} className={INFO3_META_ITEM}>
689
+ {item.avatarSrc ? (
690
+ <Avatar
691
+ size="xxs"
692
+ type="image"
693
+ src={item.avatarSrc}
694
+ alt={item.avatarAlt || `${item.value}头像`}
695
+ className={INFO3_META_AVATAR}
696
+ />
697
+ ) : item.iconName ? (
698
+ <Icon name={item.iconName} size="xs" />
699
+ ) : null}
700
+ <span className="truncate">{item.value}</span>
701
+ </span>
702
+ ))}
703
+ </div>
704
+ </article>
705
+ );
706
+ }
707
+
573
708
  return (
574
709
  <article
575
- className={[CARD_SURFACE, DATA_CARD, CARD_VARIANT_CLASS[resolvedColor], className].filter(Boolean).join(' ')}
710
+ className={[CARD_SURFACE, isClickable ? CARD_INTERACTIVE : null, DATA_CARD, CARD_VARIANT_CLASS[resolvedColor], className].filter(Boolean).join(' ')}
576
711
  style={cardStyle}
577
712
  data-tfds-component="Card"
713
+ {...interactiveProps}
578
714
  >
579
715
  {hasDataIcon ? (
580
716
  <div className={DATA_ICON_ROW}>
@@ -624,7 +760,7 @@ export default function Card({
624
760
  type="button"
625
761
  className={ACTION}
626
762
  aria-label={actionAriaLabel}
627
- onClick={onAction}
763
+ onClick={(event) => handleNestedActionClick(event, onAction)}
628
764
  disabled={!onAction}
629
765
  >
630
766
  <Icon name="arrow-narrow-right-stroked" size={16} />
@@ -98,7 +98,7 @@ export const CARD_TOKEN_MAP = {
98
98
  ],
99
99
  信息卡片2: [
100
100
  { label: '适用场景', cssProp: 'usage', value: 'AI 能力推荐 / 重点工具入口 / 配置向导 / 首页推荐能力;单页建议最多 1-2 张' },
101
- { label: '结构', cssProp: 'layout', value: '左上图标 + 标题/描述 + 左下角徽标 + 右下角圆形箭头操作入口' },
101
+ { label: '结构', cssProp: 'layout', value: '左上图标 + 标题/描述 + 左下角徽标 + 右下角圆形箭头提示入口' },
102
102
  { label: '背景与描边', cssProp: 'background/border', value: '完全复用 Card color=white / grey 的背景、描边和 hover 投影规则' },
103
103
  { label: '交互', cssProp: 'motion', value: '无柔光、无流动动画、无特殊 hover 位移;仅保留普通 Card hover 投影' },
104
104
  { label: '图标色系配置', cssProp: 'prop', value: 'animatedTone: brand / blue / purple / green / orange / grey,默认 grey;仅影响左上图标容器;grey 背景使用 fill-default,描边使用 border-default' },
@@ -112,12 +112,31 @@ export const CARD_TOKEN_MAP = {
112
112
  { label: '描述行数', cssProp: 'line-clamp', value: '最多 2 行' },
113
113
  { label: '徽标位置', cssProp: 'placement', value: '左下角,与标题左边界对齐,并与右下角圆形箭头操作入口同一行垂直居中' },
114
114
  { label: '徽标样式', cssProp: 'component-style', value: 'Tag variant="white" / fontWeight="regular" / size=l / radius=md' },
115
- { label: '操作按钮', cssProp: 'component-style', value: '复用数据卡片 ACTION:28px 圆形箭头按钮 / hover 黑色描边与黑色箭头 / active 缩放;无 onAction 时禁用' },
116
- { label: '操作文案', cssProp: 'a11y-label', value: 'animatedActionText 作为圆形箭头按钮语义文案,不直接渲染为文字按钮' },
115
+ { label: '整卡点击', cssProp: 'interaction', value: '传入 onAction 时整张卡片默认可点击进入,支持鼠标点击与 Enter / Space 键盘触发' },
116
+ { label: '操作按钮', cssProp: 'component-style', value: '复用数据卡片 ACTION:28px 圆形箭头按钮 / hover 黑色描边与黑色箭头 / active 缩放;当 onAction 存在时仅作为进入提示热区,点击后与整卡复用同一回调' },
117
+ { label: '操作文案', cssProp: 'a11y-label', value: 'animatedActionText / actionAriaLabel 同时作为整卡与圆形箭头按钮语义文案,不直接渲染为文字按钮' },
118
+ ],
119
+ 信息卡片3: [
120
+ { label: '适用场景', cssProp: 'usage', value: '密集信息列表 / 知识库知识列表 / 规则列表 / 策略列表 / 工单摘要列表;适合在“列表 + 详情面板”联动页面中作为对象条目' },
121
+ { label: '结构', cssProp: 'layout', value: '标题 + 右上角状态标签 + 两行描述 + 底部元信息行' },
122
+ { label: '标题', cssProp: 'component-style', value: '主标题最多 2 行,字号 16px,字重 600,行高 24px,颜色 blueGrey-900' },
123
+ { label: '描述', cssProp: 'component-style', value: '描述最多 2 行,字号 14px,行高 22px,颜色 blueGrey-600' },
124
+ { label: '状态标签', cssProp: 'component', value: 'Tag / 右上角 / 状态映射 variant:生效/已发布=green,草稿=grey,审核中=orange,已停用/已下架=red;也可通过 statusVariant 覆盖' },
125
+ { label: '底部元信息', cssProp: 'component', value: 'metaItems: Array<{ iconName?, value, avatarSrc?, avatarAlt? }>;默认用于类型、更新时间、创建人头像与姓名' },
126
+ { label: '元信息字号', cssProp: 'font-size', token: '--text-xs', value: '12px' },
127
+ { label: '元信息间距', cssProp: 'gap', value: '12px;每个元信息内部 icon/avatar 与文字间距 4px' },
128
+ { label: '默认背景', cssProp: 'background', value: '完全复用 Card color=white / grey 的背景规则,不单独定义信息卡片3专属底色' },
129
+ { label: '默认描边', cssProp: 'border-color', value: '完全复用 Card color=white / grey 的描边规则,不单独定义信息卡片3专属描边' },
130
+ { label: 'Hover 背景', cssProp: 'background', value: '完全复用 Card color=white / grey 的 hover 背景规则' },
131
+ { label: '圆角', cssProp: 'border-radius', token: '--radius-lg', value: '12px,使用 rounded-lg' },
132
+ { label: '选中提示', cssProp: 'border-color', token: '--color-brand-500', value: '通过 selected 将原本同一条描边从灰色切换为绿色,不新增 ring / outline / 外扩描边' },
133
+ { label: '选中交互', cssProp: 'interaction', value: '通过 selected 控制整卡描边颜色;Card 只负责样式,选中/取消选中的业务状态由页面维护' },
134
+ { label: '整卡点击', cssProp: 'interaction', value: '传入 onAction 后整张卡片默认可点击,支持鼠标点击与 Enter / Space 键盘触发;适用于打开或收起右侧详情面板' },
117
135
  ],
118
136
  卡片: [
119
137
  { label: '容器映射规则', cssProp: 'usage', value: '所有卡片分类统一按父容器背景反衬:纯白容器用 grey 灰底卡;灰色 / 浅灰 / 其他非纯白容器用 white 白底卡' },
120
- { label: '规则适用范围', cssProp: 'scope', value: '数据卡片 / 商品卡片 / 信息卡片1 / 信息卡片2 全部生效,不因卡片类型变化' },
138
+ { label: '规则适用范围', cssProp: 'scope', value: '数据卡片 / 商品卡片 / 信息卡片1 / 信息卡片2 / 信息卡片3 全部生效,不因卡片类型变化' },
139
+ { label: '查看交互规则', cssProp: 'interaction', value: '所有 Card 分类只要传入 onAction(详情跳转能力),默认整张卡片区域都支持点击查看;箭头按钮仅作为进入提示,不是唯一热区' },
121
140
  { label: '白底背景', cssProp: 'background', token: '--color-card-secondary', value: 'rgba(255,255,255,0.65)', semanticRef: 'bg-card-secondary', state: 'default' },
122
141
  { label: '白底 Hover 背景', cssProp: 'background', token: '--color-surface', value: '#FFFFFF', semanticRef: 'bg-surface', state: 'hover' },
123
142
  { label: '白底描边', cssProp: 'border-color', token: '--color-white', value: '#FFFFFF' },
@@ -127,6 +146,7 @@ export const CARD_TOKEN_MAP = {
127
146
  { label: '投影', cssProp: 'box-shadow', token: '--shadow-card', value: '0 30px 50px 0 rgba(0, 9, 36, 0.05)', state: 'hover' },
128
147
  ],
129
148
  操作: [
149
+ { label: '触发热区', cssProp: 'interaction', value: '当 onAction 存在时,整卡为主热区;数据卡片 / 信息卡片2右下角圆形箭头为辅助提示热区' },
130
150
  { label: '图标色', cssProp: 'color', token: '--color-foreground-muted', value: '#667085', semanticRef: 'text-tertiary', state: 'default' },
131
151
  { label: '图标色', cssProp: 'color', token: '--color-black', value: '#000000', state: 'hover' },
132
152
  { label: '描边色', cssProp: 'border-color', token: '--color-transparent', value: 'transparent', state: 'default' },
@@ -147,6 +167,8 @@ export const CARD_TOKEN_MAP = {
147
167
  { label: '信息卡片1标题', cssProp: 'component', value: 'FormTitle / variant=form / showDescription' },
148
168
  { label: '信息卡片1徽标', cssProp: 'component', value: 'Tag / infoMetaBadgeVariant / size=l / radius=md' },
149
169
  { label: '信息卡片1昵称头像', cssProp: 'component', value: 'Avatar / type=image / shape=round / size=xxs' },
170
+ { label: '信息卡片3状态标签', cssProp: 'component', value: 'Tag / 右上角状态 / variant 由 statusVariant 或状态映射决定' },
171
+ { label: '信息卡片3元信息头像', cssProp: 'component', value: 'Avatar / type=image / shape=round / size=xxs' },
150
172
  { label: '数据卡片图标', cssProp: 'component', value: 'Icon / users-01-stroked / message-chat-square-stroked / hearts-stroked / arrow-narrow-right-stroked' },
151
173
  { label: '信息卡片1图标', cssProp: 'component', value: 'Icon / magic-wand-01-stroked / users-01-stroked / star-01-stroked / check-circle-stroked' },
152
174
  ],
@@ -16,7 +16,8 @@ export default function CardPreview({
16
16
  const isProduct = type === 'product';
17
17
  const isInfo = type === 'info';
18
18
  const isInfo2 = type === 'info2' || type === 'animated';
19
- const isData = !isProduct && !isInfo && !isInfo2;
19
+ const isInfo3 = type === 'info3';
20
+ const isData = !isProduct && !isInfo && !isInfo2 && !isInfo3;
20
21
  const resolvedInfoIconStyle = isInfo ? (infoIconStyle || 'inverse') : infoIconStyle;
21
22
  const resolvedInfoLayout = isInfo
22
23
  ? (infoLayout || 'icon-right')
@@ -30,8 +31,8 @@ export default function CardPreview({
30
31
  <Card
31
32
  type={type}
32
33
  color={color}
33
- title={isProduct ? '海底捞门店通用双人套餐' : (isInfo2 ? '智能策略生成' : undefined)}
34
- description={isProduct ? '数量 1 · ¥128.00 · 月售 2,361' : (isInfo2 ? '基于业务目标、历史数据和运营规则,自动生成可执行策略建议。' : undefined)}
34
+ title={isProduct ? '海底捞门店通用双人套餐' : (isInfo2 ? '智能策略生成' : (isInfo3 ? '抖音生服订单已签收后还能申请退款吗?' : undefined))}
35
+ description={isProduct ? '数量 1 · ¥128.00 · 月售 2,361' : (isInfo2 ? '基于业务目标、历史数据和运营规则,自动生成可执行策略建议。' : (isInfo3 ? '已签收订单仍可在 7 天无理由保障期内发起退款,需提供商品照片或服务异常说明,平台介入后通常 24h 内出结果。' : undefined))}
35
36
  tags={isProduct || isInfo ? [] : undefined}
36
37
  productStatus={isProduct ? '已使用' : undefined}
37
38
  infoIconTone={isInfo ? infoIconTone : undefined}
@@ -48,6 +49,13 @@ export default function CardPreview({
48
49
  animatedIconName={isInfo2 ? 'magic-wand-01-stroked' : undefined}
49
50
  animatedBadge={isInfo2 ? 'AI 推荐' : undefined}
50
51
  animatedActionText={isInfo2 ? '立即体验' : undefined}
52
+ selected={false}
53
+ status={isInfo3 ? '生效' : undefined}
54
+ metaItems={isInfo3 ? [
55
+ { iconName: 'tag-01-stroked', value: 'QA' },
56
+ { iconName: 'clock-stroked', value: '2026-04-21 10:30' },
57
+ { value: '李思儒' },
58
+ ] : undefined}
51
59
  actionAriaLabel={isProduct ? undefined : '查看卡片详情'}
52
60
  onAction={isProduct ? undefined : () => {}}
53
61
  />
@@ -797,7 +797,9 @@ function normalizeHumanConfirmNode(node, index) {
797
797
  options,
798
798
  defaultSelectedValue,
799
799
  formItems,
800
+ formValues: node?.formValues && typeof node.formValues === 'object' ? node.formValues : null,
800
801
  onOptionChange: typeof node?.onOptionChange === 'function' ? node.onOptionChange : null,
802
+ onFormChange: typeof node?.onFormChange === 'function' ? node.onFormChange : null,
801
803
  onPrimaryAction: typeof node?.onPrimaryAction === 'function' ? node.onPrimaryAction : null,
802
804
  onSecondaryAction: typeof node?.onSecondaryAction === 'function' ? node.onSecondaryAction : null,
803
805
  onToggleCollapsed: typeof node?.onToggleCollapsed === 'function' ? node.onToggleCollapsed : null,
@@ -1643,6 +1645,26 @@ function UserMessageContent({ tokens }) {
1643
1645
  );
1644
1646
  }
1645
1647
 
1648
+ function getInitialFormValues(formItems = []) {
1649
+ return formItems.reduce((values, item) => {
1650
+ const key = item?.id || item?.name;
1651
+ if (!key) return values;
1652
+ if (item.value !== undefined) values[key] = item.value;
1653
+ else if (item.defaultValue !== undefined) values[key] = item.defaultValue;
1654
+ else if (item.defaultChecked !== undefined) values[key] = item.defaultChecked;
1655
+ else values[key] = '';
1656
+ return values;
1657
+ }, {});
1658
+ }
1659
+
1660
+ function getFieldChangeValue(raw) {
1661
+ if (raw && typeof raw === 'object' && raw.target) {
1662
+ const { type, checked, value } = raw.target;
1663
+ return type === 'checkbox' ? checked : value;
1664
+ }
1665
+ return raw;
1666
+ }
1667
+
1646
1668
  /* ── 人工确认节点 ── */
1647
1669
  function HumanConfirmNode({ node, tokenStyles }) {
1648
1670
  /* 内部自管折叠态(兼容外部受控:node.collapsed + node.onToggleCollapsed 都传时走外部) */
@@ -1656,18 +1678,23 @@ function HumanConfirmNode({ node, tokenStyles }) {
1656
1678
  /* 点击主按钮后进入"已确认"禁用态:内容半透明、按钮禁用 */
1657
1679
  const [confirmed, setConfirmed] = useState(node.defaultConfirmed === true);
1658
1680
  const [selectedOptionValue, setSelectedOptionValue] = useState(node.defaultSelectedValue);
1681
+ const [formValues, setFormValues] = useState(() => node.formValues || getInitialFormValues(node.formItems));
1659
1682
  useEffect(() => {
1660
1683
  if (node.defaultConfirmed === true) setConfirmed(true);
1661
1684
  }, [node.defaultConfirmed]);
1662
1685
  useEffect(() => {
1663
1686
  setSelectedOptionValue(node.defaultSelectedValue);
1664
1687
  }, [node.defaultSelectedValue]);
1688
+ useEffect(() => {
1689
+ setFormValues(node.formValues || getInitialFormValues(node.formItems));
1690
+ }, [node.formItems, node.formValues]);
1665
1691
  const handlePrimary = () => {
1666
1692
  const selectedOption = node.options.find((option) => String(option.value) === String(selectedOptionValue)) ?? null;
1667
1693
  if (typeof node.onPrimaryAction === 'function') {
1668
1694
  node.onPrimaryAction({
1669
1695
  value: selectedOptionValue,
1670
1696
  option: selectedOption,
1697
+ formValues,
1671
1698
  });
1672
1699
  }
1673
1700
  setConfirmed(true);
@@ -1682,11 +1709,41 @@ function HumanConfirmNode({ node, tokenStyles }) {
1682
1709
  node.onOptionChange(nextValue, selectedOption);
1683
1710
  }
1684
1711
  };
1712
+ const handleFormValueChange = (item, rawValue) => {
1713
+ const key = item?.id || item?.name;
1714
+ if (!key) return;
1715
+ const nextValue = getFieldChangeValue(rawValue);
1716
+ setFormValues((prev) => {
1717
+ const nextValues = { ...prev, [key]: nextValue };
1718
+ if (typeof node.onFormChange === 'function') {
1719
+ node.onFormChange(nextValues, {
1720
+ fieldId: key,
1721
+ value: nextValue,
1722
+ item,
1723
+ });
1724
+ }
1725
+ return nextValues;
1726
+ });
1727
+ };
1685
1728
 
1686
1729
  const showIntroText = node.mode === 'text-card' && node.introText && !isCollapsed;
1687
1730
  const showCardBody = !isCollapsed;
1688
1731
  const isOptionCard = node.mode === 'option-card';
1689
1732
  const isFormCard = node.mode === 'form-card';
1733
+ const resolvedFormItems = isFormCard
1734
+ ? node.formItems.map((item) => {
1735
+ const key = item?.id || item?.name;
1736
+ if (!key) return item;
1737
+ return {
1738
+ ...item,
1739
+ value: formValues[key],
1740
+ onChange: (nextValue, ...args) => {
1741
+ handleFormValueChange(item, nextValue);
1742
+ if (typeof item.onChange === 'function') item.onChange(nextValue, ...args);
1743
+ },
1744
+ };
1745
+ })
1746
+ : node.formItems;
1690
1747
 
1691
1748
  const headerClassName = isOptionCard ? HUMAN_CONFIRM_OPTION_HEADER : HUMAN_CONFIRM_HEADER;
1692
1749
  const iconWrapClassName = isOptionCard ? HUMAN_CONFIRM_OPTION_ICON_WRAP : HUMAN_CONFIRM_ICON_WRAP;
@@ -1770,11 +1827,12 @@ function HumanConfirmNode({ node, tokenStyles }) {
1770
1827
  </RadioGroup>
1771
1828
  ) : isFormCard ? (
1772
1829
  <Form
1773
- items={node.formItems}
1830
+ items={resolvedFormItems}
1774
1831
  layout="vertical"
1775
1832
  size="md"
1776
1833
  disabled={confirmed}
1777
1834
  className="w-full min-w-0 gap-3 pb-2"
1835
+ onSubmit={(event) => event.preventDefault()}
1778
1836
  data-tfds-component="ChatMessage.FormCardForm"
1779
1837
  />
1780
1838
  ) : node.description ? (