@tfdesign/b-end 1.0.14 → 1.0.15
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/package.json +1 -1
- package/skills/tfds/CHECKLIST.md +5 -0
- package/skills/tfds/COMMON_FAILURES.md +48 -0
- package/skills/tfds/DESIGN_PRINCIPLES.md +5 -0
- package/skills/tfds/GLOBAL_DESIGN_RULES.md +31 -0
- package/skills/tfds/LAYOUT_RULES.md +31 -0
- package/skills/tfds/components.index.json +61 -19
- package/skills/tfds/components.summary.json +10 -10
- package/src/_b_end_runtime/components/Card.jsx +147 -11
- package/src/_b_end_runtime/components/Card.tokens.js +26 -4
- package/src/_b_end_runtime/components/CardPreview.jsx +11 -3
- package/src/_b_end_runtime/components/ChatMessage.jsx +59 -1
- package/src/_b_end_runtime/components/ConversationList.jsx +15 -10
- package/src/_b_end_runtime/components/ConversationList.tokens.js +5 -3
- package/src/_b_end_runtime/components/Tabs.jsx +46 -3
- package/src/_b_end_runtime/components/Tabs.tokens.js +3 -0
- package/src/_b_end_runtime/components.js +18 -7
- package/src/_b_end_runtime/page-patterns/ChatConversationPattern.jsx +516 -115
- package/src/_b_end_runtime/page-patterns/CustomerServiceWorkspaceFramePattern.jsx +66 -5
- package/src/_b_end_runtime/page-patterns/IMConversationPattern.jsx +38 -4
- package/src/_b_end_runtime/page-patterns/TabTopBarListPage.jsx +26 -78
- package/src/_b_end_runtime/patterns.js +24 -17
- package/src/_b_end_runtime/preview-registry.jsx +18 -2
- package/src/index.d.ts +2 -0
|
@@ -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: '
|
|
116
|
-
{ label: '
|
|
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
|
|
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={
|
|
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 ? (
|
|
@@ -46,7 +46,7 @@ const DEFAULT_SECTIONS = [
|
|
|
46
46
|
time: '13:32',
|
|
47
47
|
avatarSrc: liSiruAvatar,
|
|
48
48
|
tags: [
|
|
49
|
-
{ label: '
|
|
49
|
+
{ label: '异常提醒', variant: 'grey' },
|
|
50
50
|
{ label: '待干预', variant: 'red' },
|
|
51
51
|
],
|
|
52
52
|
},
|
|
@@ -70,7 +70,7 @@ const DEFAULT_SECTIONS = [
|
|
|
70
70
|
time: '12:40',
|
|
71
71
|
avatarSrc: guoZhezhiAvatar,
|
|
72
72
|
tags: [
|
|
73
|
-
{ label: '
|
|
73
|
+
{ label: '退款异常', variant: 'grey' },
|
|
74
74
|
{ label: '待干预', variant: 'red' },
|
|
75
75
|
],
|
|
76
76
|
},
|
|
@@ -89,7 +89,7 @@ const DEFAULT_SECTIONS = [
|
|
|
89
89
|
time: '11:22',
|
|
90
90
|
avatarSrc: chengchengAvatar,
|
|
91
91
|
tags: [
|
|
92
|
-
{ label: '24
|
|
92
|
+
{ label: '24时+', variant: 'grey', iconName: 'alarm-clock-stroked' },
|
|
93
93
|
{ label: '托管中', variant: 'green' },
|
|
94
94
|
],
|
|
95
95
|
},
|
|
@@ -112,7 +112,7 @@ const DEFAULT_SECTIONS = [
|
|
|
112
112
|
time: '09:46',
|
|
113
113
|
avatarSrc: liuDelinAvatar,
|
|
114
114
|
tags: [
|
|
115
|
-
{ label: '
|
|
115
|
+
{ label: '协商未成', variant: 'grey' },
|
|
116
116
|
{ label: '托管中', variant: 'green' },
|
|
117
117
|
],
|
|
118
118
|
},
|
|
@@ -248,7 +248,7 @@ const TIME = 'ml-auto shrink-0 pl-2 text-foreground-muted';
|
|
|
248
248
|
const AVATAR_ONLY_HEADER = 'flex h-8 w-full items-center justify-center';
|
|
249
249
|
const AVATAR_ONLY_LIST = 'flex min-h-0 flex-1 flex-col self-stretch gap-1 overflow-y-auto';
|
|
250
250
|
const AVATAR_ONLY_ITEM = [
|
|
251
|
-
'relative inline-flex h-[68px] w-full items-center rounded-xl px-4',
|
|
251
|
+
'relative inline-flex h-[68px] min-h-[68px] w-full shrink-0 items-center rounded-xl px-4',
|
|
252
252
|
'transition-colors duration-150 hover:bg-fill active:bg-fill-active',
|
|
253
253
|
'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-200',
|
|
254
254
|
].join(' ');
|
|
@@ -276,10 +276,11 @@ const CARD_MESSAGE_REGION_GENERATING = 'h-[196px] gap-3';
|
|
|
276
276
|
const CARD_MESSAGE_REGION_EDITABLE = 'h-[155px] gap-3';
|
|
277
277
|
const CARD_MESSAGE_ROW_BASE = 'flex w-full';
|
|
278
278
|
const CARD_MESSAGE_ROW_USER = 'justify-start';
|
|
279
|
-
const
|
|
279
|
+
const CARD_MESSAGE_ROW_OUTGOING = 'justify-end';
|
|
280
280
|
const CARD_MESSAGE_BUBBLE_BASE = 'max-w-[calc(100%-24px)] rounded-lg px-2 py-1.5 text-xs leading-4 text-foreground';
|
|
281
281
|
const CARD_MESSAGE_BUBBLE_USER = 'rounded-tl-none bg-fill';
|
|
282
|
-
const
|
|
282
|
+
const CARD_MESSAGE_BUBBLE_BOT = 'rounded-tr-none bg-[image:var(--gradient-ai-fill-1)]';
|
|
283
|
+
const CARD_MESSAGE_BUBBLE_AGENT = 'rounded-tr-none bg-chat-outgoing';
|
|
283
284
|
const CARD_DIVIDER = 'h-px w-full bg-border-default';
|
|
284
285
|
const CARD_FOOTER_BASE = 'shrink-0';
|
|
285
286
|
const CARD_FOOTER_REPLIED = 'px-4 py-4 text-xs leading-4 text-[#22272759]';
|
|
@@ -407,13 +408,17 @@ function sortCardTags(tags) {
|
|
|
407
408
|
}
|
|
408
409
|
|
|
409
410
|
function ConversationCardMessage({ message }) {
|
|
410
|
-
const
|
|
411
|
+
const isUser = message.role === 'user';
|
|
412
|
+
const isBot = message.role === 'bot';
|
|
413
|
+
const bubbleClassName = isUser
|
|
414
|
+
? CARD_MESSAGE_BUBBLE_USER
|
|
415
|
+
: (isBot ? CARD_MESSAGE_BUBBLE_BOT : CARD_MESSAGE_BUBBLE_AGENT);
|
|
411
416
|
|
|
412
417
|
return (
|
|
413
|
-
<div className={[CARD_MESSAGE_ROW_BASE,
|
|
418
|
+
<div className={[CARD_MESSAGE_ROW_BASE, isUser ? CARD_MESSAGE_ROW_USER : CARD_MESSAGE_ROW_OUTGOING].join(' ')}>
|
|
414
419
|
<div className={[
|
|
415
420
|
CARD_MESSAGE_BUBBLE_BASE,
|
|
416
|
-
|
|
421
|
+
bubbleClassName,
|
|
417
422
|
].join(' ')}>
|
|
418
423
|
{message.text}
|
|
419
424
|
</div>
|
|
@@ -63,7 +63,7 @@ export const CONVERSATION_LIST_TOKEN_MAP = {
|
|
|
63
63
|
],
|
|
64
64
|
收起态: [
|
|
65
65
|
{ label: '列宽', cssProp: 'width', value: '88px' },
|
|
66
|
-
{ label: '行高', cssProp: 'height', value: '68px' },
|
|
66
|
+
{ label: '行高', cssProp: 'height/min-height', value: '68px(固定高度,不参与 flex 压缩;运行时需同时具备 h-[68px] min-h-[68px] shrink-0)' },
|
|
67
67
|
{ label: '选中投影', cssProp: 'box-shadow', token: '--shadow-list', value: '0 0 16px 0 rgba(101,115,137,0.06)', state: 'active' },
|
|
68
68
|
{ label: '选中左条', cssProp: 'background', token: '--color-data-0', value: '#24CDA5', state: 'active' },
|
|
69
69
|
],
|
|
@@ -93,14 +93,16 @@ export const CONVERSATION_LIST_TOKEN_MAP = {
|
|
|
93
93
|
{ label: '消息区高度', cssProp: 'height', value: '155px(输入文案)', state: 'card-editable' },
|
|
94
94
|
{ label: '消息区横向内边距', cssProp: 'padding-inline', token: '--spacing-4', value: '16px', state: 'card' },
|
|
95
95
|
{ label: '消息区底部内边距', cssProp: 'padding-bottom', token: '--spacing-2', value: '8px', state: 'card' },
|
|
96
|
-
{ label: '消息区滚动', cssProp: 'overflow-y', value: 'auto', state: 'card' },
|
|
96
|
+
{ label: '消息区滚动', cssProp: 'overflow-y', value: 'auto(固定高度内滚动查看与右侧 IM 同源的完整气泡消息流,不允许按条数截断)', state: 'card' },
|
|
97
97
|
{ label: '消息气泡纵向间距', cssProp: 'row-gap', token: '--spacing-3', value: '12px', state: 'card' },
|
|
98
98
|
],
|
|
99
99
|
卡片消息气泡: [
|
|
100
100
|
{ label: '用户气泡背景', cssProp: 'background', token: '--color-fill', value: 'rgba(83, 96, 143, 0.07)', state: 'card-user' },
|
|
101
|
-
{ label: 'AI 气泡背景', cssProp: 'background-image', token: '--gradient-ai-fill-1', value: 'linear-gradient(90deg, rgba(230, 247, 244, 1) 0%, rgba(239, 246, 255, 1) 55%, rgba(243, 245, 255, 1) 90%, rgba(252, 243, 255, 1) 100%)', state: 'card-
|
|
101
|
+
{ label: 'AI 气泡背景', cssProp: 'background-image', token: '--gradient-ai-fill-1', value: 'linear-gradient(90deg, rgba(230, 247, 244, 1) 0%, rgba(239, 246, 255, 1) 55%, rgba(243, 245, 255, 1) 90%, rgba(252, 243, 255, 1) 100%)', state: 'card-bot' },
|
|
102
|
+
{ label: '人工客服气泡背景', cssProp: 'background', token: '--color-chat-outgoing', value: 'var(--bg-chat-outgoing)', state: 'card-agent' },
|
|
102
103
|
{ label: '气泡圆角', cssProp: 'border-radius', token: '--radius-md', value: '8px(对角缺口按消息方向处理)', state: 'card' },
|
|
103
104
|
{ label: '气泡字号', cssProp: 'font-size', token: '--text-xs', value: '12px', state: 'card' },
|
|
105
|
+
{ label: '与右侧聊天一致性', cssProp: 'content/style-source', value: '卡片气泡是右侧 IM 对话气泡的缩小版完整消息流;文案、角色、颜色语义必须同源一致。user -> grey,bot -> ai 渐变,agent -> default 浅青;允许降低字号或隐藏辅助元素,但不允许截断消息条数', state: 'card-preview' },
|
|
104
106
|
],
|
|
105
107
|
卡片底部: [
|
|
106
108
|
{ label: '已回复内边距', cssProp: 'padding', token: '--spacing-4', value: '16px', state: 'replied' },
|