@tfdesign/b-end 1.0.12 → 1.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -25
- package/package.json +1 -1
- package/skills/tfds/components.index.json +226 -59
- package/skills/tfds/components.summary.json +93 -54
- package/src/_b_end_runtime/components/Card.jsx +4 -2
- package/src/_b_end_runtime/components/Card.tokens.js +2 -0
- package/src/_b_end_runtime/components/ChatMessage.jsx +1 -1
- package/src/_b_end_runtime/components/ConversationList.jsx +53 -58
- package/src/_b_end_runtime/components/Filter.jsx +390 -0
- package/src/_b_end_runtime/components/Filter.tokens.js +98 -0
- package/src/_b_end_runtime/components/FullScreenPage.jsx +1 -0
- package/src/_b_end_runtime/components/InfoDisplayPanel.jsx +13 -15
- package/src/_b_end_runtime/components/InfoDisplayPanel.tokens.js +2 -0
- package/src/_b_end_runtime/components/Input.jsx +3 -1
- package/src/_b_end_runtime/components/Modal.jsx +11 -3
- package/src/_b_end_runtime/components/Sheet.jsx +1 -0
- package/src/_b_end_runtime/components/Table.jsx +7 -0
- package/src/_b_end_runtime/components/TagBar.jsx +2 -0
- package/src/_b_end_runtime/components/Toast.jsx +1 -0
- package/src/_b_end_runtime/components/Upload.jsx +1 -0
- package/src/_b_end_runtime/components.js +98 -5
- package/src/_b_end_runtime/page-patterns/ChatConversationPattern.jsx +34 -22
- package/src/_b_end_runtime/page-patterns/ChatHomePagePattern.jsx +1 -1
- package/src/_b_end_runtime/page-patterns/CopilotPagePattern.jsx +6 -6
- package/src/_b_end_runtime/page-patterns/IMConversationPattern.jsx +12 -13
- package/src/_b_end_runtime/page-patterns/McpManagementPage.jsx +14 -1
- package/src/_b_end_runtime/page-patterns/StrategyListPage.jsx +19 -12
- package/src/_b_end_runtime/page-patterns/TabTopBarListPage.jsx +16 -1
- package/src/_b_end_runtime/page-patterns/VariableManagementPage.jsx +15 -2
- package/src/_b_end_runtime/page-patterns/pageListShared.jsx +54 -36
- package/src/_b_end_runtime/patterns.js +10 -6
- package/src/_b_end_runtime/preview-registry.jsx +97 -2
- package/src/index.d.ts +29 -2
- package/src/index.js +2 -1
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 用于客服工作台 / 在线 Agent / 工单详情右侧信息区,将多个可切换的信息面板
|
|
5
5
|
* 组织为“主栏 + 拆分栏”结构。组件只负责框架、拆分/合并和 Tabs,不规定 tabs 内容。
|
|
6
|
+
* tabs 来自业务数据动态配置:1 个分类单栏展示,2 个分类最多双栏,3 个及以上分类且宽度足够时最多三栏。
|
|
7
|
+
* 嵌入客服工作台框架时,必须保留整体信息栏拖拽、内部栏间拖拽、拆分/合并和最小宽度保护。
|
|
6
8
|
*/
|
|
7
9
|
|
|
8
10
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
@@ -10,7 +12,6 @@ import Button from './Button';
|
|
|
10
12
|
import FormTitle from './FormTitle';
|
|
11
13
|
import Icon from './Icon';
|
|
12
14
|
import Tabs from './Tabs';
|
|
13
|
-
import Tooltip from './Tooltip';
|
|
14
15
|
|
|
15
16
|
const DEFAULT_PANELS = [
|
|
16
17
|
{
|
|
@@ -307,20 +308,17 @@ function PanelHeader({
|
|
|
307
308
|
|
|
308
309
|
{!useTitleFallback ? (
|
|
309
310
|
<div className={ACTIONS}>
|
|
310
|
-
<
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
/>
|
|
322
|
-
</span>
|
|
323
|
-
</Tooltip>
|
|
311
|
+
<Button
|
|
312
|
+
type="button"
|
|
313
|
+
variant="ghost-black"
|
|
314
|
+
size="md"
|
|
315
|
+
icon={<Icon name={splitActionIcon} size={16} />}
|
|
316
|
+
iconOnly
|
|
317
|
+
tooltip={splitActionTooltip}
|
|
318
|
+
disabled={splitActionDisabled}
|
|
319
|
+
aria-label={splitActionLabel}
|
|
320
|
+
onClick={onSplitAction}
|
|
321
|
+
/>
|
|
324
322
|
</div>
|
|
325
323
|
) : null}
|
|
326
324
|
<span className={HEADER_LINE} aria-hidden="true" />
|
|
@@ -17,6 +17,7 @@ export const INFO_DISPLAY_PANEL_TOKEN_MAP = {
|
|
|
17
17
|
分栏: [
|
|
18
18
|
{ label: '布局模型', cssProp: 'layout', value: '主栏 + 拆分栏;主栏永远在最右侧,拆分栏固定追加在主栏左侧' },
|
|
19
19
|
{ label: '最大栏数', cssProp: 'max-columns', value: '3 栏(1 个主栏 + 最多 2 个拆分栏)' },
|
|
20
|
+
{ label: '动态拆分能力', cssProp: 'split-model', value: '按业务 tabs 动态支持 tab1 / tab2 / tab3:1 个分类单栏展示,2 个分类最多双栏,3 个及以上分类且宽度足够时最多三栏' },
|
|
20
21
|
{ label: 'Tab数量上限', cssProp: 'max-columns', value: '可见栏数同时受 tab 总数限制:1 个 tab 不拆分,2 个 tab 最多 2 栏,>= 3 个 tab 才允许 3 栏' },
|
|
21
22
|
{ label: '单栏最小宽度', cssProp: 'minPanelWidth', value: '200px' },
|
|
22
23
|
{ label: '双栏门槛', cssProp: 'container-width', value: '>= 401px(200px * 2 + 1px 分隔线)' },
|
|
@@ -24,6 +25,7 @@ export const INFO_DISPLAY_PANEL_TOKEN_MAP = {
|
|
|
24
25
|
{ label: '栏间分隔线', cssProp: 'border-left', token: 'border-default', value: '1px' },
|
|
25
26
|
{ label: '空间不足策略', cssProp: 'split-limit', value: '容器宽度 < 401px 时只允许单栏;401px-601px 允许双栏;>= 602px 才允许三栏。宽度不足时主栏拆分按钮进入 Button disabled 态' },
|
|
26
27
|
{ label: '拖拽边界', cssProp: 'resize-handle', value: '组件整体宽度由左外边界单独拖拽;组件内部仅栏与栏之间的分隔线支持拖拽调宽' },
|
|
28
|
+
{ label: '客服工作台保留能力', cssProp: 'customer-service-rule', value: '嵌入客服工作台框架时,必须完整保留整体信息栏拖拽、内部栏间拖拽、拆分/合并、宽度门槛和最小栏宽保护' },
|
|
27
29
|
{ label: '拖拽最小宽度', cssProp: 'min-column-width', value: '每栏最小 200px' },
|
|
28
30
|
{ label: '预览验证', cssProp: 'preview-resize', value: '预览态默认撑满容器,并支持从组件左侧边缘拖拽整体宽度,用于验证单栏 / 双栏 / 三栏门槛' },
|
|
29
31
|
],
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
* @prop {(suggestion: string) => void} [onAdoptSuggestion] — 采纳 AI 推荐回调
|
|
20
20
|
* @prop {() => void} [onRefreshAiSuggestions] — 刷新 AI 推荐回调
|
|
21
21
|
* @prop {string} [className=''] — 类名
|
|
22
|
+
* @prop {import('react').CSSProperties} [style] — 根容器样式,常用于设置 `--size-input-width`
|
|
22
23
|
*/
|
|
23
24
|
import { useState, useRef, useCallback, useEffect, useMemo } from 'react';
|
|
24
25
|
import AiSuggestionPanel, { AiRefreshButton, buildSuggestionGroupsFromFlatList } from './AiSuggestionShared';
|
|
@@ -124,6 +125,7 @@ export default function Input({
|
|
|
124
125
|
onAdoptSuggestion,
|
|
125
126
|
onRefreshAiSuggestions,
|
|
126
127
|
className = '',
|
|
128
|
+
style,
|
|
127
129
|
...rest
|
|
128
130
|
}) {
|
|
129
131
|
const inputRef = useRef(null);
|
|
@@ -208,7 +210,7 @@ export default function Input({
|
|
|
208
210
|
].filter(Boolean).join(' ');
|
|
209
211
|
|
|
210
212
|
return (
|
|
211
|
-
<div className={wrapperCls}>
|
|
213
|
+
<div className={wrapperCls} style={style}>
|
|
212
214
|
<div className={fieldCls} data-tfds-component="Input">
|
|
213
215
|
{prefix ? <span className={ADDON_CLASS}>{prefix}</span> : null}
|
|
214
216
|
<input
|
|
@@ -40,6 +40,7 @@ const FOOTER_ROW =
|
|
|
40
40
|
const FOOTER_HINT =
|
|
41
41
|
'm-0 min-w-[7.5rem] flex-1 text-xs font-normal leading-4 text-foreground-secondary';
|
|
42
42
|
const ACTIONS = 'flex shrink-0 items-center justify-end gap-3';
|
|
43
|
+
const BODY_BOTTOM_GAP = 'shrink-0 h-6';
|
|
43
44
|
|
|
44
45
|
/**
|
|
45
46
|
* Modal — 模态对话框面板(仅面板本体,遮罩与定位由外层处理)
|
|
@@ -60,7 +61,7 @@ const ACTIONS = 'flex shrink-0 items-center justify-end gap-3';
|
|
|
60
61
|
* @prop {function} [onClose=null] — 关闭回调,传入才显示关闭按钮
|
|
61
62
|
* @prop {function} [onCancel=null] — 取消回调,传入才显示取消按钮
|
|
62
63
|
* @prop {function} [onConfirm=null] — 确定回调,传入才显示确定按钮
|
|
63
|
-
* @prop {ReactNode} [footer=null] —
|
|
64
|
+
* @prop {ReactNode} [footer=null] — 自定义底栏,传入则替换默认底栏;若底栏整体为空则自动保留 24px 底部留白
|
|
64
65
|
* @prop {string} [bodyClassName=''] — 内容区附加类名
|
|
65
66
|
* @prop {string} [className=''] — 附加类名
|
|
66
67
|
* @prop {object} [style] — 内联样式
|
|
@@ -88,6 +89,10 @@ export default function Modal({
|
|
|
88
89
|
const subtitleId = `${uid}-subtitle`;
|
|
89
90
|
|
|
90
91
|
const sizeClass = layout === 'center' ? SIZE_CLASS[size] : '';
|
|
92
|
+
const hasDefaultFooterContent = Boolean((showFooterHint && footerHint) || onCancel || onConfirm);
|
|
93
|
+
const shouldRenderCustomFooter = footer != null;
|
|
94
|
+
const shouldRenderDefaultFooter = !shouldRenderCustomFooter && hasDefaultFooterContent;
|
|
95
|
+
const shouldRenderBodyBottomGap = !shouldRenderCustomFooter && !hasDefaultFooterContent;
|
|
91
96
|
|
|
92
97
|
return (
|
|
93
98
|
<div
|
|
@@ -117,6 +122,7 @@ export default function Modal({
|
|
|
117
122
|
icon={<X size={16} strokeWidth={2} />}
|
|
118
123
|
iconOnly
|
|
119
124
|
onClick={onClose}
|
|
125
|
+
tooltip="关闭"
|
|
120
126
|
aria-label="关闭"
|
|
121
127
|
className="shrink-0"
|
|
122
128
|
/>
|
|
@@ -125,9 +131,9 @@ export default function Modal({
|
|
|
125
131
|
|
|
126
132
|
<div className={[BODY_CLASS[layout], bodyClassName].filter(Boolean).join(' ')}>{children}</div>
|
|
127
133
|
|
|
128
|
-
{
|
|
134
|
+
{shouldRenderCustomFooter ? (
|
|
129
135
|
<div className={FOOTER}>{footer}</div>
|
|
130
|
-
) : (
|
|
136
|
+
) : shouldRenderDefaultFooter ? (
|
|
131
137
|
<footer className={FOOTER}>
|
|
132
138
|
<div className={FOOTER_ROW}>
|
|
133
139
|
{showFooterHint && footerHint ? (
|
|
@@ -149,6 +155,8 @@ export default function Modal({
|
|
|
149
155
|
</div>
|
|
150
156
|
</div>
|
|
151
157
|
</footer>
|
|
158
|
+
) : (
|
|
159
|
+
<div className={BODY_BOTTOM_GAP} aria-hidden="true" />
|
|
152
160
|
)}
|
|
153
161
|
</div>
|
|
154
162
|
);
|
|
@@ -607,6 +607,7 @@ function renderTextButtonValue(value) {
|
|
|
607
607
|
iconOnly
|
|
608
608
|
icon={<Icon name={iconName} size="sm" />}
|
|
609
609
|
className={[ICON_ONLY_BUTTON_RESET, TEXT_BUTTON_ICON_CLASS, 'ml-[2px]'].join(' ')}
|
|
610
|
+
tooltip={typeof value === 'object' ? value.tooltip || value.ariaLabel || value.label || '单元格操作' : '单元格操作'}
|
|
610
611
|
aria-label="单元格操作"
|
|
611
612
|
onClick={onClick}
|
|
612
613
|
/>
|
|
@@ -720,6 +721,7 @@ function renderActionsValue(value) {
|
|
|
720
721
|
icon={<Icon name={action.iconName || 'dots-horizontal-stroked'} size="sm" />}
|
|
721
722
|
className={[ICON_ONLY_BUTTON_RESET, MORE_ACTION_BUTTON_CLASS].join(' ')}
|
|
722
723
|
onClick={action.onClick}
|
|
724
|
+
tooltip={action.tooltip || action.ariaLabel || action.label || '更多操作'}
|
|
723
725
|
aria-label={action.ariaLabel || action.label || '更多操作'}
|
|
724
726
|
/>
|
|
725
727
|
);
|
|
@@ -755,6 +757,7 @@ function renderDragHandleValue(value) {
|
|
|
755
757
|
iconOnly
|
|
756
758
|
icon={<Icon name="dots-grid-stroked" size="sm" />}
|
|
757
759
|
className={[ICON_ONLY_BUTTON_RESET, DRAG_HANDLE_BUTTON_CLASS].join(' ')}
|
|
760
|
+
tooltip="拖拽排序"
|
|
758
761
|
aria-label="拖拽排序"
|
|
759
762
|
onClick={onClick}
|
|
760
763
|
/>
|
|
@@ -888,6 +891,7 @@ function CardFormActions({ record, context, onView, onMore }) {
|
|
|
888
891
|
variant="ghost-black"
|
|
889
892
|
iconOnly
|
|
890
893
|
icon={<Icon name="dots-horizontal-stroked" />}
|
|
894
|
+
tooltip="更多"
|
|
891
895
|
aria-label="更多"
|
|
892
896
|
onClick={() => onMore?.(record, context)}
|
|
893
897
|
/>
|
|
@@ -956,6 +960,7 @@ function CardFormItem({
|
|
|
956
960
|
variant="ghost-black"
|
|
957
961
|
iconOnly
|
|
958
962
|
icon={<Icon name="chevron-right-stroked" />}
|
|
963
|
+
tooltip={expanded ? '收起子项' : '展开子项'}
|
|
959
964
|
aria-label={expanded ? '收起子项' : '展开子项'}
|
|
960
965
|
aria-expanded={hasChildren ? expanded : undefined}
|
|
961
966
|
onClick={(event) => {
|
|
@@ -1169,6 +1174,7 @@ export default function Table({
|
|
|
1169
1174
|
className={[ICON_ONLY_BUTTON_RESET, PAGE_ARROW_BUTTON_CLASS].join(' ')}
|
|
1170
1175
|
onClick={() => handlePageChange(safeCurrent - 1)}
|
|
1171
1176
|
disabled={safeCurrent <= 1}
|
|
1177
|
+
tooltip="上一页"
|
|
1172
1178
|
aria-label="上一页"
|
|
1173
1179
|
data-tfds-component="Table.Pagination"
|
|
1174
1180
|
/>
|
|
@@ -1207,6 +1213,7 @@ export default function Table({
|
|
|
1207
1213
|
className={[ICON_ONLY_BUTTON_RESET, PAGE_ARROW_BUTTON_CLASS].join(' ')}
|
|
1208
1214
|
onClick={() => handlePageChange(safeCurrent + 1)}
|
|
1209
1215
|
disabled={safeCurrent >= totalPages}
|
|
1216
|
+
tooltip="下一页"
|
|
1210
1217
|
aria-label="下一页"
|
|
1211
1218
|
data-tfds-component="Table.Pagination"
|
|
1212
1219
|
/>
|
|
@@ -566,6 +566,7 @@ function BusinessSwitcher({
|
|
|
566
566
|
iconOnly
|
|
567
567
|
icon={<Icon name="switch-horizontal-01-stroked" size="xs" className="text-foreground-muted" aria-hidden="true" />}
|
|
568
568
|
className="shrink-0"
|
|
569
|
+
tooltip="切换业务线"
|
|
569
570
|
aria-label="切换业务线"
|
|
570
571
|
aria-haspopup="menu"
|
|
571
572
|
aria-expanded={menuOpen}
|
|
@@ -1097,6 +1098,7 @@ export default function TagBar({
|
|
|
1097
1098
|
radius="rounded"
|
|
1098
1099
|
iconOnly
|
|
1099
1100
|
icon={<Icon name="layout-left-stroked" size="sm" aria-hidden="true" />}
|
|
1101
|
+
tooltip={isCollapsed ? '展开标签栏' : '收起标签栏'}
|
|
1100
1102
|
aria-label={isCollapsed ? '展开标签栏' : '收起标签栏'}
|
|
1101
1103
|
onClick={() => updateCollapsed(!isCollapsed)}
|
|
1102
1104
|
data-tfds-component="TagBar.CollapseToggle"
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
// AvatarGroup:同一语境下「多人」的缩略表达(固定 5 人交叠),不做可配人数列表头。
|
|
10
10
|
// Icon:装饰性或功能性矢量符号(按钮内、列表前缀、空状态),不承担「人像」语义。
|
|
11
11
|
// Button:触发提交/打开弹窗/行内操作等离散动作;不负责「切换同页多块内容」。
|
|
12
|
+
// Filter:筛选栏中的单个筛选胶囊/筛选触发器;传入 options 时内置多选下拉面板。
|
|
12
13
|
// Tabs:同一容器内多块平级内容的视图切换(不离开当前页);不负责路由级站点导航。
|
|
13
14
|
// Input:用户自造字符串(名称、搜索词、备注);选项来自固定枚举时不要用 Input 冒充选择器。
|
|
14
15
|
// Select:从较多或会增长的枚举里选一个/多选(tag 模式);选项很少且需一眼比较时用 Radio。
|
|
@@ -570,6 +571,30 @@ const TAG_PREVIEW = {
|
|
|
570
571
|
},
|
|
571
572
|
};
|
|
572
573
|
|
|
574
|
+
const FILTER_PREVIEW = {
|
|
575
|
+
state: {
|
|
576
|
+
outline: { bgColor: '#FFFFFF', borderColor: '#E4E7EC', textColor: '#182230' },
|
|
577
|
+
fill: { bgColor: 'rgba(83, 96, 143, 0.07)', borderColor: '#E4E7EC', textColor: '#182230' },
|
|
578
|
+
selected: { bgColor: '#EAFAF6', borderColor: '#87DEC9', textColor: '#129683' },
|
|
579
|
+
disabled: { bgColor: '#F9FAFB', borderColor: '#E6E7EA', textColor: '#98A2B3' },
|
|
580
|
+
selectedDisabled: { bgColor: '#EAFAF6', borderColor: '#A8E8D7', textColor: '#87DEC9' },
|
|
581
|
+
},
|
|
582
|
+
base: {
|
|
583
|
+
height: '36px',
|
|
584
|
+
paddingLeft: '12px',
|
|
585
|
+
paddingRight: '12px',
|
|
586
|
+
paddingY: 'auto',
|
|
587
|
+
gap: '8px',
|
|
588
|
+
borderRadius: '9999px',
|
|
589
|
+
fontSize: '14px',
|
|
590
|
+
lineHeight: '20px',
|
|
591
|
+
labelWeight: '600',
|
|
592
|
+
valueWeight: '400',
|
|
593
|
+
iconSize: '12px',
|
|
594
|
+
clearIconSize: '16px',
|
|
595
|
+
},
|
|
596
|
+
};
|
|
597
|
+
|
|
573
598
|
const TAGINPUT_PREVIEW = {
|
|
574
599
|
status: {
|
|
575
600
|
default: { bgColor: '#FFFFFF', borderColor: 'rgba(45,66,107,0.12)', hoverBorderColor: '#D0D5DD', focusBorderColor: '#56D3BC' },
|
|
@@ -1040,7 +1065,7 @@ export const COMPONENTS = [
|
|
|
1040
1065
|
name: 'Card',
|
|
1041
1066
|
element: 'article',
|
|
1042
1067
|
category: 'business',
|
|
1043
|
-
description: '业务信息摘要卡片,支持数据卡片、商品卡片、信息卡片1和信息卡片2
|
|
1068
|
+
description: '业务信息摘要卡片,支持数据卡片、商品卡片、信息卡片1和信息卡片2。所有卡片分类统一遵循背景反衬原则:灰色/浅灰/非纯白背景用白底卡,纯白背景用灰底卡。',
|
|
1044
1069
|
componentFile: './components/Card.jsx',
|
|
1045
1070
|
tokensFile: './components/Card.tokens.js',
|
|
1046
1071
|
props: [
|
|
@@ -1112,9 +1137,9 @@ export const COMPONENTS = [
|
|
|
1112
1137
|
'【信息卡片2视觉】info2 不包含柔光、流动光带或特殊 hover 位移;背景颜色、边框颜色和 hover 投影完全复用普通 Card 的 color=white / grey 规则。animatedTone 只影响左上图标容器色系,不影响卡片背景、边框和底部标签。',
|
|
1113
1138
|
'【信息卡片2图标 Hover】卡片 hover 时,左上图标容器必须自动切换为当前 animatedTone 对应的 hover 背景、纯白 icon、透明描边:brand→brand-500、blue→blue-500、purple→purple-500、green→green-500、orange→orange-500、grey→black。禁止 hover 后继续显示浅底、深色 icon 或描边色。',
|
|
1114
1139
|
'【颜色】color=white 使用 65% 白底 + 白色描边;color=grey 使用 Blue Grey 100 背景 + Blue Grey 300 描边',
|
|
1115
|
-
'【容器映射(强约束)】Card
|
|
1140
|
+
'【容器映射(强约束)】Card 的颜色必须与所处背景做反衬,而且这条规则对所有 Card 分类统一生效:当 Card 所在父容器是“灰底页面/灰底区块/浅灰分区/其他非纯白背景”→ Card 用 `color="white"`;当父容器本身是“纯白底卡片/纯白底面板”→ Card 用 `color="grey"`,用灰底卡片做二级区块分层,避免白底叠白底',
|
|
1116
1141
|
'【配置面板颜色】所有 Card 分类(数据卡片、商品卡片、信息卡片1、信息卡片2)都必须优先展示“颜色”配置项,并提供白底 / 灰底两种选择。平台预览中切换右上角画布底色时,卡片颜色必须按反衬规则自动同步:灰底画布 → 白底卡片,白底画布 → 灰底卡片。',
|
|
1117
|
-
'
|
|
1142
|
+
'【一句话记忆】如果卡片背景环境是灰色、浅灰色或其他非纯白色,就选“白底”分类卡片;如果卡片背景环境是纯白色,就选“灰底”分类卡片。不要让 Card 和父级背景同色相贴。',
|
|
1118
1143
|
'【状态】白底卡默认态使用 65% 白底 + 轻白描边;hover 后补满白底并出现业务卡片专用投影;灰底卡保持灰底与灰描边并保留投影反馈',
|
|
1119
1144
|
'【指标】指标项推荐控制在 3 项以内,图标 14px、文字 12px,项间距 20px',
|
|
1120
1145
|
'【标签】通用 tags 只用于数据卡片,展示在左下角并与右侧操作按钮水平对齐;商品卡片和信息卡片1禁止渲染通用 tags,主标题左侧不得出现“标签”等前缀标签,运行时会隐藏误注入的 white Tag。Card 内所有标签默认必须使用圆角矩形样式 `radius="md"`,不使用全圆胶囊 `radius="full"`;数据卡片、商品状态标签、信息卡片1徽标都遵守该规则。数据卡片不论是否展示图标、也不论 dataIconStyle 为 tone 或 inverse,默认标签都必须统一使用 grey 标签样式。商品状态标签和信息卡片1徽标仅可展示在右上角并与标题水平对齐。信息卡片1徽标与 infoIconStyle 联动:`tone` 时一律 grey,`inverse` 时默认彩色 purple;卡片彩色标签优先使用 purple(紫色)/ teal(青绿色)/ blue(蓝色)/ cyan(青色)/ orange(橙色),避免优先使用 pink、red、yellow 等过强警示或装饰色。商品状态标签复用 green + l + md;不显示图标和关闭按钮,建议使用 2-4 字短标签。',
|
|
@@ -1277,7 +1302,7 @@ export const COMPONENTS = [
|
|
|
1277
1302
|
name: 'InfoDisplayPanel',
|
|
1278
1303
|
element: 'section',
|
|
1279
1304
|
category: 'business',
|
|
1280
|
-
description: '信息展示面板框架,用于客服工作台、在线 Agent、工单详情右侧信息区等场景;默认以主栏承载全部 tabs
|
|
1305
|
+
description: '信息展示面板框架,用于客服工作台、在线 Agent、工单详情右侧信息区等场景;默认以主栏承载全部 tabs,并按业务动态 tab1 / tab2 / tab3 拆分为 1-3 栏,支持整体宽度与栏间宽度拖拽。',
|
|
1281
1306
|
componentFile: './components/InfoDisplayPanel.jsx',
|
|
1282
1307
|
tokensFile: './components/InfoDisplayPanel.tokens.js',
|
|
1283
1308
|
props: [
|
|
@@ -1319,6 +1344,7 @@ export const COMPONENTS = [
|
|
|
1319
1344
|
'【不适用场景】整页客服工作台外框用 CustomerServiceWorkspaceFrame;左侧会话 / 工单队列用 ConversationList;单个业务摘要用 Card;字段型详情或可批量操作数据用 Form / Table。',
|
|
1320
1345
|
'【结构】外层为一个白色圆角容器,默认使用 `w-full` 撑满父容器可用宽度;内部按“拆分栏数组 + 主栏”组织。每一栏都必须包含 56px 顶部 Tabs 栏和内容区;顶部横线只保留 Header 内 1 条通栏下描边,左右贴满当前栏容器。Tabs 交互、文字、选中态与 3px Brand 指示线必须复用基础 `Tabs variant="line" size="lg"`,禁止手写 tab button 或手写选中线。栏与栏之间用 1px 垂直分隔线,禁止通过 gap 制造分栏空隙。',
|
|
1321
1346
|
'【分栏能力】默认单栏时,全部 tabs 位于主栏;主栏永远在最右侧,拆分出的独立栏固定追加在主栏左侧。每栏右上角始终只有 1 个动作按钮,拆分与合并统一使用 `Button variant="ghost-black" size="md" iconOnly tooltip="拆分/合并"` 显示提示文案。最多支持 3 栏,即 1 个主栏 + 最多 2 个拆分栏。',
|
|
1347
|
+
'【动态 tab1/tab2/tab3 拆分】InfoDisplayPanel 必须按业务实际 tabs 动态决定可见栏数,而不是写死三栏:只有 1 个分类时自动降级为单栏标题 + 内容;有 2 个分类时最多支持 2 栏;有 3 个及以上分类且容器宽度达到三栏门槛时才支持 3 栏。tab 的数量、名称、顺序、内容都必须来自 `panels[].tabs` / `panels[].content` 或 `renderPanelContent`,不要把“托管助手 / 历史工单 / 工单日志”等示例固定写死到所有业务页面。',
|
|
1322
1348
|
'【Tabs来源】Tabs 的数量、名称和顺序必须跟随具体生成页面的业务信息架构动态生成,由 `panels[].tabs` 提供;不要把默认示例 tabs 当作真实业务规则,也不要为了凑满 3 栏强行生成无意义的 tab。',
|
|
1323
1349
|
'【Tab数量与拆分上限】可拆栏数同时受容器宽度和 tab 总数限制:仅 1 个 tab 时不使用 Tabs,运行时自动降级为 `FormTitle variant="card"` 标题展示且不显示拆分按钮;仅 2 个 tab 时最多拆分为 2 栏;大于等于 3 个 tab 且容器宽度达到 602px 时才允许拆分为 3 栏。',
|
|
1324
1350
|
'【拆分规则】点击主栏右上角“拆分”时,只拆出当前选中的主栏 tab;拆分后该 tab 从主栏 tabs 中移除,并以独立栏形式出现在主栏左侧。主栏保留剩余未拆分 tabs,顺序始终保持传入时的原始顺序。',
|
|
@@ -1335,6 +1361,7 @@ export const COMPONENTS = [
|
|
|
1335
1361
|
'【组合关系】InfoDisplayPanel 可作为 CustomerServiceWorkspaceFrame 的 mainPanel 内容;放入主白卡时宽高使用 `size-full` 或父容器约束,外层不要再套一层大白卡。',
|
|
1336
1362
|
'【嵌入客服工作台时的语义】在客服工作台框架模版里,InfoDisplayPanel 不是独立静态侧栏,而是“当前左侧选中会话 / 工单”的辅助信息区。左侧会话列表默认选中项变化时,InfoDisplayPanel 内各 tab 的内容也必须同步切换到该当前对象对应的数据上下文,例如用户信息、历史工单、工单日志、视频信息、沟通记录都应属于同一个当前处理对象。',
|
|
1337
1363
|
'【联动边界】客服工作台里的右侧白卡应被视为一个整体工作区:左侧 IM 对话区负责当前线程,右侧 InfoDisplayPanel 负责同一线程的辅助信息。两者由外层模板统一接收当前对象 id 并同步刷新;InfoDisplayPanel 不应自己脱离左侧上下文单独维持另一份“当前对象”。',
|
|
1364
|
+
'【嵌入客服工作台时的能力保留】当 InfoDisplayPanel 被客服工作台框架使用时,必须保留整体信息栏宽度拖拽、内部栏间拖拽、动态 tab 拆分 / 合并、单 tab 降级、宽度门槛和最小栏宽保护。AI 生成页面不得把 InfoDisplayPanel 替换成静态 Card 列、普通 Tabs 面板或固定宽 div,也不得删除拖拽热区和拆分按钮。',
|
|
1338
1365
|
'【可控状态】业务需要同步 URL / 埋点 / 右侧主区状态时,使用 `activeTabs + onTabChange` 控制每栏当前 tab,并使用 `onSplitChange` 感知当前拆分出来的 tab 列表与实际栏数;`columnCount` / `defaultColumnCount` 仅保留为兼容字段,不再作为主交互入口推荐,默认最大栏数按组件逻辑自动支持到 3 栏。',
|
|
1339
1366
|
],
|
|
1340
1367
|
examples: [
|
|
@@ -1610,7 +1637,7 @@ export const COMPONENTS = [
|
|
|
1610
1637
|
'【消息原子】一个 ChatMessage = AI 对话页中的一条消息原子,role="ai" 渲染 AI 消息(适配容器宽度),role="user" 渲染用户气泡(右对齐 + 8% 容器宽度左缩进 ≈ 500px 容器下 40px 缩进)',
|
|
1611
1638
|
'【AI 友好命名】props 一律用最短同义词:header / thinking / plan / confirms / followUps / actions / userContent;读 props 即知 DOM 结构(<ChatMessage header thinking plan ... />)',
|
|
1612
1639
|
'【7 类子组件】内部按 AI 头像 / 文本回复 / 执行流 / 卡片回复 / 深度思考 / 操作栏 / 用户气泡 7 类子组件按需组合,全部通过 props 启用,互相正交不冲突',
|
|
1613
|
-
'【theme 必引】入口 CSS 必须 @import "@
|
|
1640
|
+
'【theme 必引】入口 CSS 必须 @import "@tfdesign/b-end/theme.css"。ChatMessage 子区块使用 --color-border-default、--color-neutral、--color-neutral-500/300/700、--tfds-ai-execution-* 等;未引入 theme 时 var() 无效,描边易退化为浏览器默认深黑(追问按钮等)。',
|
|
1614
1641
|
'【禁止手搓描边】勿包一层 div 并加 border-black / ring-black;追问 followUps 为白底 + border-border-default + hover:border-border-strong。',
|
|
1615
1642
|
|
|
1616
1643
|
/* —— 1. AI 头像 —— */
|
|
@@ -3220,6 +3247,72 @@ export const COMPONENTS = [
|
|
|
3220
3247
|
'span rounded bg-', '手搓 Tag', '自制状态标签', 'px-2 py-1 bg-red-100',
|
|
3221
3248
|
],
|
|
3222
3249
|
},
|
|
3250
|
+
{
|
|
3251
|
+
id: 'filter',
|
|
3252
|
+
name: 'Filter',
|
|
3253
|
+
element: 'div',
|
|
3254
|
+
category: 'basic',
|
|
3255
|
+
description:
|
|
3256
|
+
'筛选胶囊项:用于筛选栏中的单个筛选触发器或已选条件展示,36px 高、全圆角、标签 semibold + 值 regular。支持点击展开下拉面板、多选、白底常态、填充态、品牌选中态、禁用态和可清除态。',
|
|
3257
|
+
componentFile: './components/Filter.jsx',
|
|
3258
|
+
tokensFile: './components/Filter.tokens.js',
|
|
3259
|
+
props: [
|
|
3260
|
+
{ name: 'label', type: 'string', default: '筛选项' },
|
|
3261
|
+
{ name: 'value', type: 'string|number|null', default: null },
|
|
3262
|
+
{ name: 'options', type: 'array', default: [] },
|
|
3263
|
+
{ name: 'selectedValues', type: 'array', default: undefined },
|
|
3264
|
+
{ name: 'defaultValue', type: 'array', default: [] },
|
|
3265
|
+
{ name: 'onChange', type: 'function', default: null },
|
|
3266
|
+
{ name: 'selected', type: 'boolean', default: false },
|
|
3267
|
+
{ name: 'filled', type: 'boolean', default: false },
|
|
3268
|
+
{ name: 'disabled', type: 'boolean', default: false },
|
|
3269
|
+
{ name: 'closable', type: 'boolean', default: false },
|
|
3270
|
+
{ name: 'onClear', type: 'function', default: null },
|
|
3271
|
+
{ name: 'className', type: 'string', default: '' },
|
|
3272
|
+
],
|
|
3273
|
+
labels: {
|
|
3274
|
+
selected: { true: '选中', false: '未选中' },
|
|
3275
|
+
filled: { true: '填充态', false: '白底态' },
|
|
3276
|
+
closable: { true: '清除图标', false: '下拉箭头' },
|
|
3277
|
+
disabled: { true: '禁用', false: '可交互' },
|
|
3278
|
+
},
|
|
3279
|
+
_preview: FILTER_PREVIEW,
|
|
3280
|
+
rules: [
|
|
3281
|
+
FONT_WEIGHT_RUNTIME_RULE,
|
|
3282
|
+
'【定位】Filter 是筛选栏中的单个胶囊触发器/已选筛选项,不是完整筛选条;完整筛选区域由多个 Filter 横向组合。',
|
|
3283
|
+
'【选型·vs Tag】Tag 是展示型短标签,不承载打开筛选面板;Filter 可点击、可打开筛选面板、可展示筛选值和清除入口。',
|
|
3284
|
+
'【选型·vs Button】Filter 只用于筛选条件选择/收起/清除;普通动作(提交、取消、导出、新建)仍使用 Button。',
|
|
3285
|
+
'【尺寸】固定高度 36px(--size-control-md),左右内距 12px(--spacing-3,对齐 Select md),内容 gap 8px(--spacing-2);不要随意压缩为 24px 或 32px,以保证和 B 端 Input/Select 默认高度对齐。',
|
|
3286
|
+
'【文字】label 使用 semibold 600,value 使用 normal 400;文案建议 label 2-6 字、value 1-8 字,过长值应在业务层截断或改用 Tooltip。',
|
|
3287
|
+
'【下拉多选】传入 options 后点击胶囊展开下拉面板;支持 selectedValues 受控、defaultValue 非受控、onChange(nextValues) 回调;点击外部或 Escape 关闭。',
|
|
3288
|
+
'【值展示】未显式传 value 时,单选中展示选项 label,多选中展示“已选 N 项”;显式 value 优先用于自定义展示。',
|
|
3289
|
+
'【状态】selected=true 使用品牌浅底 + 品牌描边 + 品牌文字;filled=true 使用中性填充底;disabled=true 使用禁用文字与禁用底,且不可点击。',
|
|
3290
|
+
'【图标】无选中值时显示 12px 下拉箭头;closable=true 或已选中且未禁用时显示 16px 清除按钮(视觉与 Select 清除入口一致),点击清空多选值、关闭下拉并触发 onClear,不触发展开。',
|
|
3291
|
+
'【语义结构】外层使用 role="combobox/button" 的 div 触发器,避免清除 button 嵌套在 button 内;不要改回 button 包 button 的非法 DOM。',
|
|
3292
|
+
'【组合】有值但仍可展开修改时传 options;已选条件需要一键移除时使用 closable 或依赖默认已选清除入口。',
|
|
3293
|
+
'【禁止手搓】不要用 div/span + rounded-full 自行拼筛选 chip;筛选入口统一使用 Filter 保证 token、字重、焦点态与禁用态一致。',
|
|
3294
|
+
],
|
|
3295
|
+
examples: [
|
|
3296
|
+
{ label: '基础筛选项', code: '<Filter label="筛选项" />' },
|
|
3297
|
+
{ label: '多选下拉', code: '<Filter label="筛选项" options={[{ label: "选项一", value: "1" }, { label: "选项二", value: "2" }]} />' },
|
|
3298
|
+
{ label: '默认已选', code: '<Filter label="筛选项" options={options} defaultValue={["1"]} />' },
|
|
3299
|
+
{ label: '受控多选', code: '<Filter label="筛选项" options={options} selectedValues={values} onChange={setValues} />' },
|
|
3300
|
+
{ label: '选中态', code: '<Filter label="筛选项" options={options} defaultValue={["1"]} selected />' },
|
|
3301
|
+
{ label: '填充态', code: '<Filter label="筛选项" filled />' },
|
|
3302
|
+
{ label: '可清除', code: '<Filter label="筛选项" options={options} defaultValue={["1"]} filled closable onClear={() => {}} />' },
|
|
3303
|
+
{ label: '选中可清除', code: '<Filter label="筛选项" options={options} defaultValue={["1"]} selected closable onClear={() => {}} />' },
|
|
3304
|
+
{ label: '禁用态', code: '<Filter label="筛选项" disabled />' },
|
|
3305
|
+
{ label: '禁用选中态', code: '<Filter label="筛选项" options={options} defaultValue={["1"]} selected disabled />' },
|
|
3306
|
+
{ label: '❌ Bad(手搓筛选 chip)', code: '/* 禁止!筛选入口不要手写 span/div */\n<span className="rounded-full border px-4 py-2">筛选项</span>' },
|
|
3307
|
+
{ label: '✅ Good(用 Filter)', code: '<Filter label="筛选项" options={options} defaultValue={["1"]} selected />' },
|
|
3308
|
+
],
|
|
3309
|
+
keywords: [
|
|
3310
|
+
'Filter', 'filter', '筛选', '筛选项', '筛选组件', '筛选胶囊', '筛选 chip',
|
|
3311
|
+
'筛选触发器', '已选筛选', '过滤条件', 'filter item', 'filter chip', 'pill filter',
|
|
3312
|
+
'closable', 'clear', '清除筛选', '下拉筛选', '多选筛选', 'selected filter',
|
|
3313
|
+
'span rounded-full border', '手搓筛选', '自制筛选 chip',
|
|
3314
|
+
],
|
|
3315
|
+
},
|
|
3223
3316
|
{
|
|
3224
3317
|
id: 'toast',
|
|
3225
3318
|
name: 'Toast',
|
|
@@ -22,8 +22,9 @@ import ChatMessage, {
|
|
|
22
22
|
* - 800px 居中对话流
|
|
23
23
|
*
|
|
24
24
|
* 状态:
|
|
25
|
-
* -
|
|
26
|
-
* - phase = '
|
|
25
|
+
* - 空对话 / 新建会话态 → 当消息为空时,必须展示新建会话页面(Hero + 居中 ChatInput + 推荐 prompt)
|
|
26
|
+
* - phase = 'chat' → 有消息时展示消息流
|
|
27
|
+
* - phase = 'welcome' → 点「新会话」后进入空对话 / 新建会话态
|
|
27
28
|
*
|
|
28
29
|
* 关键词路由(输入框发送文本时):
|
|
29
30
|
* - 含「整理 / 分析 / 生成 / 梳理」 → 走完整任务规划链路:
|
|
@@ -325,9 +326,15 @@ function messageToChatProps(msg, isLatest, handlers = {}) {
|
|
|
325
326
|
|
|
326
327
|
export default function ChatConversationPattern({
|
|
327
328
|
title = '抖音电商客服售后政策梳理',
|
|
329
|
+
initialMessages,
|
|
328
330
|
}) {
|
|
329
|
-
const
|
|
330
|
-
|
|
331
|
+
const resolvedInitialMessagesRef = useRef(
|
|
332
|
+
Array.isArray(initialMessages) ? initialMessages : buildInitMessages(),
|
|
333
|
+
);
|
|
334
|
+
const [messages, setMessages] = useState(() => resolvedInitialMessagesRef.current);
|
|
335
|
+
const [phase, setPhase] = useState(() => (
|
|
336
|
+
resolvedInitialMessagesRef.current.length === 0 ? 'welcome' : 'chat'
|
|
337
|
+
)); // 'chat' | 'welcome'
|
|
331
338
|
/* ── ChatInput 受控状态机 ──
|
|
332
339
|
* inputView:'default' | 'replying' | 'busy'
|
|
333
340
|
* · default → 静止 / 失焦 / AI 完成回复(含 replying & busy 完成)
|
|
@@ -361,7 +368,7 @@ export default function ChatConversationPattern({
|
|
|
361
368
|
/* 自动滚到底:用 ResizeObserver 监听消息容器内容高度变化
|
|
362
369
|
* 覆盖所有场景:新消息追加、流式 step 逐步推出、ChatMessage 内部折叠展开 */
|
|
363
370
|
useEffect(() => {
|
|
364
|
-
if (phase !== 'chat') return undefined;
|
|
371
|
+
if (phase !== 'chat' || messages.length === 0) return undefined;
|
|
365
372
|
const el = scrollRef.current;
|
|
366
373
|
if (!el) return undefined;
|
|
367
374
|
const inner = el.firstElementChild;
|
|
@@ -371,12 +378,14 @@ export default function ChatConversationPattern({
|
|
|
371
378
|
});
|
|
372
379
|
ro.observe(inner);
|
|
373
380
|
return () => ro.disconnect();
|
|
374
|
-
}, [phase]);
|
|
381
|
+
}, [phase, messages.length]);
|
|
375
382
|
|
|
376
383
|
/* 重置回欢迎状态 */
|
|
377
384
|
const handleNewSession = useCallback(() => {
|
|
378
385
|
setMessages([]);
|
|
379
386
|
setPhase('welcome');
|
|
387
|
+
setPrefillText('');
|
|
388
|
+
setPrefillSeed((s) => s + 1);
|
|
380
389
|
}, []);
|
|
381
390
|
|
|
382
391
|
/* ── 取消链路:用户在任务规划卡里点「取消」 ──
|
|
@@ -610,9 +619,10 @@ export default function ChatConversationPattern({
|
|
|
610
619
|
}, [handleNewSession]);
|
|
611
620
|
|
|
612
621
|
const lastIdx = messages.length - 1;
|
|
622
|
+
const isNewConversationState = phase === 'welcome' || messages.length === 0;
|
|
613
623
|
|
|
614
|
-
/*
|
|
615
|
-
const displayTitle =
|
|
624
|
+
/* 当前阶段标题:空对话 / 新建会话态统一显示「新会话」 */
|
|
625
|
+
const displayTitle = isNewConversationState ? '新会话' : title;
|
|
616
626
|
|
|
617
627
|
return (
|
|
618
628
|
<div
|
|
@@ -625,9 +635,18 @@ export default function ChatConversationPattern({
|
|
|
625
635
|
border: '1px solid var(--color-border-default, #E4E7EC)',
|
|
626
636
|
}}
|
|
627
637
|
>
|
|
628
|
-
<TopBar title={displayTitle} onNewSession={handleNewSessionFull} disableNewSession={
|
|
638
|
+
<TopBar title={displayTitle} onNewSession={handleNewSessionFull} disableNewSession={isNewConversationState} />
|
|
629
639
|
|
|
630
|
-
{
|
|
640
|
+
{isNewConversationState ? (
|
|
641
|
+
<NewConversationPhase
|
|
642
|
+
onSend={handleSend}
|
|
643
|
+
onStop={handleStop}
|
|
644
|
+
onPrefill={prefillInput}
|
|
645
|
+
inputView={inputView}
|
|
646
|
+
prefillText={prefillText}
|
|
647
|
+
prefillSeed={prefillSeed}
|
|
648
|
+
/>
|
|
649
|
+
) : (
|
|
631
650
|
<ChatPhase
|
|
632
651
|
scrollRef={scrollRef}
|
|
633
652
|
messages={messages}
|
|
@@ -643,15 +662,6 @@ export default function ChatConversationPattern({
|
|
|
643
662
|
prefillText={prefillText}
|
|
644
663
|
prefillSeed={prefillSeed}
|
|
645
664
|
/>
|
|
646
|
-
) : (
|
|
647
|
-
<WelcomePhase
|
|
648
|
-
onSend={handleSend}
|
|
649
|
-
onStop={handleStop}
|
|
650
|
-
onPrefill={prefillInput}
|
|
651
|
-
inputView={inputView}
|
|
652
|
-
prefillText={prefillText}
|
|
653
|
-
prefillSeed={prefillSeed}
|
|
654
|
-
/>
|
|
655
665
|
)}
|
|
656
666
|
</div>
|
|
657
667
|
);
|
|
@@ -755,10 +765,10 @@ function ChatPhase({ scrollRef, messages, lastIdx, handlers, onSend, onStop, inp
|
|
|
755
765
|
}
|
|
756
766
|
|
|
757
767
|
/* ============================================================
|
|
758
|
-
*
|
|
759
|
-
*
|
|
768
|
+
* NewConversationPhase — AI 对话页的唯一空会话 / 新建会话页面
|
|
769
|
+
* 当 messages 为空时,必须展示该页面;复用 Copilot welcome 视觉,ChatInput 仍底部吸底
|
|
760
770
|
* ============================================================ */
|
|
761
|
-
function
|
|
771
|
+
function NewConversationPhase({ onSend, onStop, onPrefill, inputView, prefillText, prefillSeed }) {
|
|
762
772
|
return (
|
|
763
773
|
<>
|
|
764
774
|
{/* 中部 hero:自适应剩余高度,居中展示头像/标题/欢迎语/chips */}
|
|
@@ -936,12 +946,14 @@ function TopBar({ title, onNewSession, disableNewSession }) {
|
|
|
936
946
|
icon={<Icon name="message-plus-square-stroked" />}
|
|
937
947
|
onClick={onNewSession}
|
|
938
948
|
disabled={disableNewSession}
|
|
949
|
+
tooltip="新建会话"
|
|
939
950
|
aria-label="新建会话"
|
|
940
951
|
/>
|
|
941
952
|
<Button
|
|
942
953
|
variant="ghost-black"
|
|
943
954
|
iconOnly
|
|
944
955
|
icon={<Icon name="clock-rewind-stroked" />}
|
|
956
|
+
tooltip="历史记录"
|
|
945
957
|
aria-label="历史记录"
|
|
946
958
|
/>
|
|
947
959
|
</div>
|
|
@@ -91,7 +91,7 @@ function TopBar({ copilotOpen, onToggleCopilot }) {
|
|
|
91
91
|
function TopBarLead({ copilotOpen, onToggleCopilot }) {
|
|
92
92
|
return (
|
|
93
93
|
<>
|
|
94
|
-
<Button variant="outline-black" iconOnly icon={<Icon name="arrow-left-stroked" />} aria-label="返回" />
|
|
94
|
+
<Button variant="outline-black" iconOnly icon={<Icon name="arrow-left-stroked" />} tooltip="返回" aria-label="返回" />
|
|
95
95
|
|
|
96
96
|
<span
|
|
97
97
|
className="font-semibold text-base leading-[22px] whitespace-nowrap"
|
|
@@ -158,7 +158,7 @@ function TopBarActions() {
|
|
|
158
158
|
</div>
|
|
159
159
|
|
|
160
160
|
<div className="flex items-center gap-2">
|
|
161
|
-
<Button variant="ghost-black" iconOnly icon={<Icon name="dots-horizontal-stroked" />} aria-label="更多" />
|
|
161
|
+
<Button variant="ghost-black" iconOnly icon={<Icon name="dots-horizontal-stroked" />} tooltip="更多" aria-label="更多" />
|
|
162
162
|
<Button variant="outline-black">次操作</Button>
|
|
163
163
|
<Button variant="primary" icon={<Icon name="plus-stroked" />}>主操作</Button>
|
|
164
164
|
</div>
|
|
@@ -191,9 +191,9 @@ function CopilotPanel({ onClose }) {
|
|
|
191
191
|
</span>
|
|
192
192
|
</div>
|
|
193
193
|
<div className="flex items-center gap-0.5 shrink-0">
|
|
194
|
-
<Button variant="ghost-black" iconOnly icon={<Icon name="message-plus-square-stroked" />} aria-label="新建会话" />
|
|
195
|
-
<Button variant="ghost-black" iconOnly icon={<Icon name="clock-stroked" />} aria-label="历史记录" />
|
|
196
|
-
<Button variant="ghost-black" iconOnly icon={<Icon name="layout-left-stroked" />} aria-label="收起面板" onClick={onClose} />
|
|
194
|
+
<Button variant="ghost-black" iconOnly icon={<Icon name="message-plus-square-stroked" />} tooltip="新建会话" aria-label="新建会话" />
|
|
195
|
+
<Button variant="ghost-black" iconOnly icon={<Icon name="clock-stroked" />} tooltip="历史记录" aria-label="历史记录" />
|
|
196
|
+
<Button variant="ghost-black" iconOnly icon={<Icon name="layout-left-stroked" />} tooltip="收起面板" aria-label="收起面板" onClick={onClose} />
|
|
197
197
|
</div>
|
|
198
198
|
</div>
|
|
199
199
|
|
|
@@ -362,7 +362,7 @@ function ContentCard() {
|
|
|
362
362
|
|
|
363
363
|
<Button variant="ghost-black">流程单测</Button>
|
|
364
364
|
<Button variant="ghost-black">批量测试</Button>
|
|
365
|
-
<Button variant="ghost-black" iconOnly icon={<Icon name="git-branch-02-stroked" />} aria-label="对比" />
|
|
365
|
+
<Button variant="ghost-black" iconOnly icon={<Icon name="git-branch-02-stroked" />} tooltip="对比" aria-label="对比" />
|
|
366
366
|
</div>
|
|
367
367
|
</div>
|
|
368
368
|
|