@tfdesign/b-end 1.0.4
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/AI_READ_FIRST.md +131 -0
- package/LICENSE +21 -0
- package/README.md +353 -0
- package/package.json +67 -0
- package/scripts/check-tfds-contract.mjs +334 -0
- package/scripts/check-tfds-integration.mjs +263 -0
- package/scripts/postinstall-cursor-skill.mjs +382 -0
- package/scripts/setup.mjs +520 -0
- package/skills/tfds/CHECKLIST.md +205 -0
- package/skills/tfds/COMMON_FAILURES.md +238 -0
- package/skills/tfds/DESIGN_PRINCIPLES.md +477 -0
- package/skills/tfds/GLOBAL_DESIGN_RULES.md +636 -0
- package/skills/tfds/LAYOUT_RECIPES.md +140 -0
- package/skills/tfds/LAYOUT_RULES.md +1355 -0
- package/skills/tfds/PAGE_ARCHETYPES.md +201 -0
- package/skills/tfds/SKILL.md +188 -0
- package/skills/tfds/components.index.json +7305 -0
- package/skills/tfds/components.summary.json +1809 -0
- package/src/_b_end_runtime/components/AiSuggestionShared.jsx +166 -0
- package/src/_b_end_runtime/components/Avatar.jsx +325 -0
- package/src/_b_end_runtime/components/Avatar.tokens.js +76 -0
- package/src/_b_end_runtime/components/AvatarGridPreview.jsx +56 -0
- package/src/_b_end_runtime/components/AvatarGroup.jsx +80 -0
- package/src/_b_end_runtime/components/AvatarGroup.tokens.js +28 -0
- package/src/_b_end_runtime/components/Button.jsx +144 -0
- package/src/_b_end_runtime/components/Button.tokens.js +90 -0
- package/src/_b_end_runtime/components/Card.jsx +460 -0
- package/src/_b_end_runtime/components/Card.tokens.js +124 -0
- package/src/_b_end_runtime/components/CardPreview.jsx +51 -0
- package/src/_b_end_runtime/components/ChatBubble.jsx +384 -0
- package/src/_b_end_runtime/components/ChatBubble.tokens.js +60 -0
- package/src/_b_end_runtime/components/ChatBubblePreview.jsx +129 -0
- package/src/_b_end_runtime/components/ChatInput.jsx +1399 -0
- package/src/_b_end_runtime/components/ChatInput.tokens.js +75 -0
- package/src/_b_end_runtime/components/ChatMessage.jsx +2215 -0
- package/src/_b_end_runtime/components/ChatMessage.tokens.js +257 -0
- package/src/_b_end_runtime/components/ChatMessagePreview.jsx +388 -0
- package/src/_b_end_runtime/components/Checkbox.jsx +317 -0
- package/src/_b_end_runtime/components/Checkbox.tokens.js +59 -0
- package/src/_b_end_runtime/components/ConversationList.jsx +1264 -0
- package/src/_b_end_runtime/components/ConversationList.tokens.js +135 -0
- package/src/_b_end_runtime/components/ConversationListPreview.jsx +108 -0
- package/src/_b_end_runtime/components/CustomerServiceWorkspaceFrame.jsx +324 -0
- package/src/_b_end_runtime/components/CustomerServiceWorkspaceFrame.tokens.js +69 -0
- package/src/_b_end_runtime/components/DatePicker.jsx +739 -0
- package/src/_b_end_runtime/components/DatePicker.tokens.js +99 -0
- package/src/_b_end_runtime/components/Empty.jsx +141 -0
- package/src/_b_end_runtime/components/Empty.tokens.js +40 -0
- package/src/_b_end_runtime/components/Form.jsx +609 -0
- package/src/_b_end_runtime/components/Form.tokens.js +77 -0
- package/src/_b_end_runtime/components/FormFieldStack.jsx +123 -0
- package/src/_b_end_runtime/components/FormFieldStack.tokens.js +12 -0
- package/src/_b_end_runtime/components/FormTitle.jsx +119 -0
- package/src/_b_end_runtime/components/FormTitle.tokens.js +87 -0
- package/src/_b_end_runtime/components/FullScreenPage.jsx +97 -0
- package/src/_b_end_runtime/components/FullScreenPage.tokens.js +19 -0
- package/src/_b_end_runtime/components/Icon.jsx +172 -0
- package/src/_b_end_runtime/components/Icon.tokens.js +26 -0
- package/src/_b_end_runtime/components/IconGridPreview.jsx +277 -0
- package/src/_b_end_runtime/components/InfoDisplayPanel.jsx +620 -0
- package/src/_b_end_runtime/components/InfoDisplayPanel.tokens.js +71 -0
- package/src/_b_end_runtime/components/InfoDisplayPanelPreview.jsx +133 -0
- package/src/_b_end_runtime/components/Input.jsx +258 -0
- package/src/_b_end_runtime/components/Input.tokens.js +68 -0
- package/src/_b_end_runtime/components/InputNumber.jsx +242 -0
- package/src/_b_end_runtime/components/InputNumber.tokens.js +55 -0
- package/src/_b_end_runtime/components/Modal.jsx +155 -0
- package/src/_b_end_runtime/components/Modal.tokens.js +73 -0
- package/src/_b_end_runtime/components/NavBar.jsx +842 -0
- package/src/_b_end_runtime/components/NavBar.tokens.js +97 -0
- package/src/_b_end_runtime/components/NavBarPreview.jsx +11 -0
- package/src/_b_end_runtime/components/Radio.jsx +227 -0
- package/src/_b_end_runtime/components/Radio.tokens.js +59 -0
- package/src/_b_end_runtime/components/Select.jsx +766 -0
- package/src/_b_end_runtime/components/Select.tokens.js +99 -0
- package/src/_b_end_runtime/components/Sheet.jsx +132 -0
- package/src/_b_end_runtime/components/Sheet.tokens.js +61 -0
- package/src/_b_end_runtime/components/Slider.jsx +346 -0
- package/src/_b_end_runtime/components/Slider.tokens.js +47 -0
- package/src/_b_end_runtime/components/Switch.jsx +124 -0
- package/src/_b_end_runtime/components/Switch.tokens.js +38 -0
- package/src/_b_end_runtime/components/Table.jsx +1338 -0
- package/src/_b_end_runtime/components/Table.tokens.js +147 -0
- package/src/_b_end_runtime/components/TablePreview.jsx +599 -0
- package/src/_b_end_runtime/components/Tabs.jsx +149 -0
- package/src/_b_end_runtime/components/Tabs.tokens.js +102 -0
- package/src/_b_end_runtime/components/Tag.jsx +199 -0
- package/src/_b_end_runtime/components/Tag.tokens.js +171 -0
- package/src/_b_end_runtime/components/TagBar.jsx +1134 -0
- package/src/_b_end_runtime/components/TagBar.tokens.js +75 -0
- package/src/_b_end_runtime/components/TagGridPreview.jsx +23 -0
- package/src/_b_end_runtime/components/TagInput.jsx +382 -0
- package/src/_b_end_runtime/components/TagInput.tokens.js +52 -0
- package/src/_b_end_runtime/components/TextArea.jsx +363 -0
- package/src/_b_end_runtime/components/TextArea.tokens.js +65 -0
- package/src/_b_end_runtime/components/TimePicker.jsx +444 -0
- package/src/_b_end_runtime/components/TimePicker.tokens.js +77 -0
- package/src/_b_end_runtime/components/Toast.jsx +120 -0
- package/src/_b_end_runtime/components/Toast.tokens.js +146 -0
- package/src/_b_end_runtime/components/Tooltip.jsx +282 -0
- package/src/_b_end_runtime/components/Tooltip.tokens.js +48 -0
- package/src/_b_end_runtime/components/TooltipPreview.jsx +50 -0
- package/src/_b_end_runtime/components/Upload.jsx +455 -0
- package/src/_b_end_runtime/components/Upload.tokens.js +47 -0
- package/src/_b_end_runtime/components/avatar-assets/avatar-default.png +0 -0
- package/src/_b_end_runtime/components/avatar-group-assets/avatar-group-1.png +0 -0
- package/src/_b_end_runtime/components/avatar-group-assets/avatar-group-2.png +0 -0
- package/src/_b_end_runtime/components/avatar-group-assets/avatar-group-3.png +0 -0
- package/src/_b_end_runtime/components/avatar-group-assets/avatar-group-4.png +0 -0
- package/src/_b_end_runtime/components/avatar-group-assets/avatar-group-5.png +0 -0
- package/src/_b_end_runtime/components/empty-assets/administrator-1.svg +40 -0
- package/src/_b_end_runtime/components/empty-assets/administrator-2.svg +33 -0
- package/src/_b_end_runtime/components/empty-assets/construction.svg +33 -0
- package/src/_b_end_runtime/components/empty-assets/failure.svg +49 -0
- package/src/_b_end_runtime/components/empty-assets/idle.svg +34 -0
- package/src/_b_end_runtime/components/empty-assets/no-access.svg +36 -0
- package/src/_b_end_runtime/components/empty-assets/no-content.svg +77 -0
- package/src/_b_end_runtime/components/empty-assets/no-result.svg +61 -0
- package/src/_b_end_runtime/components/empty-assets/not-found.svg +46 -0
- package/src/_b_end_runtime/components/empty-assets/success.svg +38 -0
- package/src/_b_end_runtime/components/file-type-assets/batch-report.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/catcat.svg +21 -0
- package/src/_b_end_runtime/components/file-type-assets/code.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/conversation.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/document.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/feishu-card.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/feishu-sheet.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/feishu.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/image.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/index.js +105 -0
- package/src/_b_end_runtime/components/file-type-assets/knowledge.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/pdf.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/pe.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/strategy.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/table.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/webpage.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/xmind.png +0 -0
- package/src/_b_end_runtime/components/icons/icon-data.js +12496 -0
- package/src/_b_end_runtime/components/nav-bar-assets/bytehi-logo-mark.svg +21 -0
- package/src/_b_end_runtime/components/table-assets/avatar.png +0 -0
- package/src/_b_end_runtime/components/table-assets/button.png +0 -0
- package/src/_b_end_runtime/components/table-assets/icon-chevron-down.png +0 -0
- package/src/_b_end_runtime/components/table-cell-assets/avatar.png +0 -0
- package/src/_b_end_runtime/components/table-cell-assets/button.png +0 -0
- package/src/_b_end_runtime/components/table-cell-assets/checkbox.png +0 -0
- package/src/_b_end_runtime/components/table-cell-assets/icon-chevron-right.png +0 -0
- package/src/_b_end_runtime/components/table-cell-assets/icon.png +0 -0
- package/src/_b_end_runtime/components/table-cell-assets/semi-icons-handle.png +0 -0
- package/src/_b_end_runtime/components/table-cell-assets/semi-icons-tree-triangle-right.png +0 -0
- package/src/_b_end_runtime/components/table-cell-assets/switch.png +0 -0
- package/src/_b_end_runtime/components/tagShared.js +3 -0
- package/src/_b_end_runtime/components/team-avatar-assets/chengcheng-murphy.png +0 -0
- package/src/_b_end_runtime/components/team-avatar-assets/duan-ran.png +0 -0
- package/src/_b_end_runtime/components/team-avatar-assets/guo-zhezhi.png +0 -0
- package/src/_b_end_runtime/components/team-avatar-assets/li-siru.png +0 -0
- package/src/_b_end_runtime/components/team-avatar-assets/liu-delin.png +0 -0
- package/src/_b_end_runtime/components.js +3499 -0
- package/src/_b_end_runtime/index.js +9 -0
- package/src/_b_end_runtime/page-patterns/BasePageFramePattern.jsx +395 -0
- package/src/_b_end_runtime/page-patterns/ChatConversationPattern.jsx +989 -0
- package/src/_b_end_runtime/page-patterns/ChatHomePagePattern.jsx +281 -0
- package/src/_b_end_runtime/page-patterns/CopilotPagePattern.jsx +380 -0
- package/src/_b_end_runtime/page-patterns/CustomerServiceWorkspaceFramePattern.jsx +392 -0
- package/src/_b_end_runtime/page-patterns/IMConversationPattern.jsx +590 -0
- package/src/_b_end_runtime/page-patterns/McpManagementPage.jsx +237 -0
- package/src/_b_end_runtime/page-patterns/StrategyListPage.jsx +189 -0
- package/src/_b_end_runtime/page-patterns/TabTopBarListPage.jsx +594 -0
- package/src/_b_end_runtime/page-patterns/VariableManagementPage.jsx +87 -0
- package/src/_b_end_runtime/page-patterns/pageListShared.jsx +177 -0
- package/src/_b_end_runtime/patterns.js +428 -0
- package/src/_b_end_runtime/preview-registry.jsx +4719 -0
- package/src/_b_end_runtime/teamMembers.js +56 -0
- package/src/_b_end_runtime/tokens.js +500 -0
- package/src/index.d.ts +1073 -0
- package/src/index.js +52 -0
- package/theme.css +350 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// B端设计系统 — 统一导出
|
|
2
|
+
//
|
|
3
|
+
// 新增系统时,在自己的目录下创建同样结构的 index.js 即可自动注册。
|
|
4
|
+
// system-registry.js 会通过 import.meta.glob 自动发现所有 index.js。
|
|
5
|
+
|
|
6
|
+
export { COMPONENTS } from './components.js';
|
|
7
|
+
export { PREVIEW_REGISTRY } from './preview-registry.jsx';
|
|
8
|
+
export { PATTERNS } from './patterns.js';
|
|
9
|
+
export * from './tokens';
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
import { useCallback, useRef, useState } from 'react';
|
|
2
|
+
import NavBar from '../components/NavBar';
|
|
3
|
+
import Modal from '../components/Modal';
|
|
4
|
+
import Empty from '../components/Empty';
|
|
5
|
+
import Button from '../components/Button';
|
|
6
|
+
import FullScreenPage from '../components/FullScreenPage';
|
|
7
|
+
import McpManagementPage from './McpManagementPage';
|
|
8
|
+
import StrategyListPage from './StrategyListPage';
|
|
9
|
+
import TabTopBarListPage from './TabTopBarListPage';
|
|
10
|
+
import VariableManagementPage from './VariableManagementPage';
|
|
11
|
+
import { WHITE_CARD_STYLE } from './pageListShared';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* BasePageFramePattern — B 端基础页面框架
|
|
15
|
+
*
|
|
16
|
+
* 整体充当"模板示例集"的外壳:
|
|
17
|
+
* 外框:blueGrey-200 浅灰底,业务页面根容器贴浏览器边缘,不额外加整页圆角或描边
|
|
18
|
+
* 左侧:NavBar 业务侧边导航(OLA 平台变体),菜单项替换为通用"列表示例 1~5",
|
|
19
|
+
* 每项对应一个独立的列表页模板(业务可直接复用整页代码)
|
|
20
|
+
* 右侧:根据当前选中的菜单 id 渲染对应模板
|
|
21
|
+
* · example-1 → 变量管理(单白卡:标题/筛选/表格)
|
|
22
|
+
* · example-2 → MCP 工具管理(双白卡:左 TagBar + 右主白卡)
|
|
23
|
+
* · example-3 → 胶囊 Tab 标题栏 + 白卡(顶部 TopBar 承担一级切换 + 平台级操作)
|
|
24
|
+
* · example-4/5 → 暂留空白卡占位,后续可继续添加新模板
|
|
25
|
+
*
|
|
26
|
+
* 业务接入:
|
|
27
|
+
* · 直接复制 VariableManagementPage / McpManagementPage 单文件即可作为新页面起点
|
|
28
|
+
* · 共用结构(标题栏 / 筛选栏 / 列定义)封装在 pageListShared.jsx
|
|
29
|
+
*
|
|
30
|
+
* @prop {string} [defaultSelectedItemId='example-1'] — 初始高亮菜单 id
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
const NAV_ITEMS = [
|
|
34
|
+
{ id: 'example-1', label: '页面示例1', iconName: 'layout-alt-01-stroked' },
|
|
35
|
+
{ id: 'example-2', label: '页面示例2', iconName: 'layout-alt-01-stroked' },
|
|
36
|
+
{ id: 'example-3', label: '页面示例3', iconName: 'layout-alt-01-stroked' },
|
|
37
|
+
{ id: 'example-4', label: '页面示例4', iconName: 'layout-alt-01-stroked' },
|
|
38
|
+
{ id: 'example-5', label: '页面示例5', iconName: 'layout-alt-01-stroked' },
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
const QA_PANEL_LIMITS = {
|
|
42
|
+
left: { min: 260, max: 420, initial: 320 },
|
|
43
|
+
middle: { min: 280, max: 520, initial: 360 },
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const QA_PANEL_CONTENT = [
|
|
47
|
+
{
|
|
48
|
+
key: 'left',
|
|
49
|
+
step: '1',
|
|
50
|
+
title: '步骤标题一',
|
|
51
|
+
hint: '支持拖拽调宽',
|
|
52
|
+
body: '该区域模拟左侧辅助工作区。横向大卡布局下,辅助卡默认支持宽度拖拽调整,便于用户按任务分配浏览与编辑空间。内容过高时仍在卡片内部滚动,不把整页撑出视口。',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
key: 'middle',
|
|
56
|
+
step: '2',
|
|
57
|
+
title: '步骤标题二',
|
|
58
|
+
hint: '默认可调宽',
|
|
59
|
+
body: '该区域模拟中间次级工作区。基础页面框架示例不再使用静态三等分网格,而是提供可拖拽的大卡工作区样板,帮助生成页面时默认学习到“主卡弹性、辅卡可拖”的结构。',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
key: 'right',
|
|
63
|
+
step: '3',
|
|
64
|
+
title: '步骤标题三',
|
|
65
|
+
hint: '主卡弹性',
|
|
66
|
+
body: '右侧主卡继续保持 flex 弹性,自动占满剩余空间。拖拽分隔条只改变相邻大卡宽度,不改变卡片内标题、正文和滚动容器的结构关系。',
|
|
67
|
+
},
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
function clampQaPanelWidth(key, width) {
|
|
71
|
+
const limits = QA_PANEL_LIMITS[key];
|
|
72
|
+
if (!limits) return width;
|
|
73
|
+
return Math.min(limits.max, Math.max(limits.min, width));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
export default function BasePageFramePattern({
|
|
78
|
+
defaultSelectedItemId = 'module',
|
|
79
|
+
}) {
|
|
80
|
+
const [selectedItemId, setSelectedItemId] = useState(defaultSelectedItemId);
|
|
81
|
+
const [newVarModalOpen, setNewVarModalOpen] = useState(false);
|
|
82
|
+
const [createQAOpen, setCreateQAOpen] = useState(false);
|
|
83
|
+
|
|
84
|
+
const PAGE_BY_ITEM_DYNAMIC = {
|
|
85
|
+
'module': <StrategyListPage />,
|
|
86
|
+
'example-1': <VariableManagementPage onNewVariable={() => setNewVarModalOpen(true)} />,
|
|
87
|
+
'example-2': <McpManagementPage />,
|
|
88
|
+
'example-3': <TabTopBarListPage onCreateQA={() => setCreateQAOpen(true)} />,
|
|
89
|
+
'example-4': <NoAccessPage />,
|
|
90
|
+
'example-5': <ConstructionPage />,
|
|
91
|
+
};
|
|
92
|
+
const activePage = PAGE_BY_ITEM_DYNAMIC[selectedItemId];
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<div
|
|
96
|
+
className="relative flex h-full min-h-0 min-w-0 w-full items-stretch overflow-hidden"
|
|
97
|
+
style={{
|
|
98
|
+
flex: '1 1 auto',
|
|
99
|
+
alignSelf: 'stretch',
|
|
100
|
+
minHeight: '100%',
|
|
101
|
+
background: 'var(--color-blueGrey-200, #F2F4F7)',
|
|
102
|
+
/* 基础页面框架:作为整页背景容器,贴浏览器边缘展示,不做外圈圆角 */
|
|
103
|
+
borderRadius: 0,
|
|
104
|
+
}}
|
|
105
|
+
>
|
|
106
|
+
{/* 新建变量弹窗遮罩 — absolute 覆盖整个红框区域(含 NavBar) */}
|
|
107
|
+
{newVarModalOpen && (
|
|
108
|
+
<div
|
|
109
|
+
className="absolute inset-0 z-50 flex items-start justify-center"
|
|
110
|
+
style={{ background: 'rgba(0,0,0,0.4)', borderRadius: 0, paddingTop: '80px' }}
|
|
111
|
+
>
|
|
112
|
+
<Modal
|
|
113
|
+
size="sm"
|
|
114
|
+
title="新建变量"
|
|
115
|
+
subtitle={null}
|
|
116
|
+
showFooterHint={false}
|
|
117
|
+
confirmText="创建"
|
|
118
|
+
cancelText="取消"
|
|
119
|
+
onClose={() => setNewVarModalOpen(false)}
|
|
120
|
+
onCancel={() => setNewVarModalOpen(false)}
|
|
121
|
+
onConfirm={() => setNewVarModalOpen(false)}
|
|
122
|
+
>
|
|
123
|
+
<div style={{ height: '200px' }} />
|
|
124
|
+
</Modal>
|
|
125
|
+
</div>
|
|
126
|
+
)}
|
|
127
|
+
|
|
128
|
+
{/* 创建QA对 — 全屏页面,absolute inset-0 覆盖含 NavBar 在内的整个框架 */}
|
|
129
|
+
{createQAOpen && (
|
|
130
|
+
<FullScreenPage
|
|
131
|
+
bg="grey"
|
|
132
|
+
title="创建QA对"
|
|
133
|
+
onBack={() => setCreateQAOpen(false)}
|
|
134
|
+
headerActions={
|
|
135
|
+
<>
|
|
136
|
+
<Button variant="outline-black" onClick={() => setCreateQAOpen(false)}>
|
|
137
|
+
保存草稿
|
|
138
|
+
</Button>
|
|
139
|
+
<Button variant="primary" onClick={() => setCreateQAOpen(false)}>
|
|
140
|
+
发布
|
|
141
|
+
</Button>
|
|
142
|
+
</>
|
|
143
|
+
}
|
|
144
|
+
>
|
|
145
|
+
<CreateQAContent />
|
|
146
|
+
</FullScreenPage>
|
|
147
|
+
)}
|
|
148
|
+
|
|
149
|
+
{/* 左侧 NavBar 区(受控:点击切换右侧模板) */}
|
|
150
|
+
<div className="flex shrink-0">
|
|
151
|
+
<NavBar
|
|
152
|
+
platform="ola"
|
|
153
|
+
moduleLabel="页面示例0"
|
|
154
|
+
moduleIconName="layout-alt-01-stroked"
|
|
155
|
+
navItems={NAV_ITEMS}
|
|
156
|
+
selectedItemId={selectedItemId}
|
|
157
|
+
onSelect={(nextKey) => setSelectedItemId(nextKey)}
|
|
158
|
+
/>
|
|
159
|
+
</div>
|
|
160
|
+
|
|
161
|
+
{/* 右侧内容区:按当前菜单 id 渲染对应模板;未实现的示例显示占位白卡 */}
|
|
162
|
+
<div className="flex flex-1 min-h-0 min-w-0 flex-col overflow-hidden">
|
|
163
|
+
{activePage || <PlaceholderPage />}
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/* ── 卡片标题行(对齐 Figma 14757:86669:黑色数字徽章 + 标题 + 右侧辅助文字) ── */
|
|
170
|
+
function QACardHeader({ step, title, hint }) {
|
|
171
|
+
return (
|
|
172
|
+
<div
|
|
173
|
+
className="flex items-center justify-between"
|
|
174
|
+
style={{
|
|
175
|
+
padding: '20px 24px',
|
|
176
|
+
flexShrink: 0,
|
|
177
|
+
}}
|
|
178
|
+
>
|
|
179
|
+
<div className="flex items-center" style={{ gap: 10 }}>
|
|
180
|
+
{/* 黑色数字徽章 */}
|
|
181
|
+
<div
|
|
182
|
+
style={{
|
|
183
|
+
width: 20,
|
|
184
|
+
height: 20,
|
|
185
|
+
borderRadius: 6,
|
|
186
|
+
background: 'var(--color-grey-900, #1C1C23)',
|
|
187
|
+
display: 'flex',
|
|
188
|
+
alignItems: 'center',
|
|
189
|
+
justifyContent: 'center',
|
|
190
|
+
flexShrink: 0,
|
|
191
|
+
}}
|
|
192
|
+
>
|
|
193
|
+
<span style={{ fontSize: 12, fontWeight: 600, color: '#fff', lineHeight: 1, letterSpacing: '-0.01em' }}>
|
|
194
|
+
{step}
|
|
195
|
+
</span>
|
|
196
|
+
</div>
|
|
197
|
+
{/* 标题 */}
|
|
198
|
+
<span style={{ fontSize: 16, fontWeight: 600, color: 'var(--color-foreground, #182230)', lineHeight: '22px' }}>
|
|
199
|
+
{title}
|
|
200
|
+
</span>
|
|
201
|
+
</div>
|
|
202
|
+
{/* 右侧辅助文字 */}
|
|
203
|
+
{hint && (
|
|
204
|
+
<span style={{ fontSize: 14, color: 'var(--color-foreground-secondary, #475467)', lineHeight: '20px' }}>
|
|
205
|
+
{hint}
|
|
206
|
+
</span>
|
|
207
|
+
)}
|
|
208
|
+
</div>
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function ResizablePanelHandle({
|
|
213
|
+
label,
|
|
214
|
+
value,
|
|
215
|
+
min,
|
|
216
|
+
max,
|
|
217
|
+
onPointerDown,
|
|
218
|
+
onKeyDown,
|
|
219
|
+
}) {
|
|
220
|
+
return (
|
|
221
|
+
<div
|
|
222
|
+
role="separator"
|
|
223
|
+
aria-label={label}
|
|
224
|
+
aria-orientation="vertical"
|
|
225
|
+
aria-valuenow={value}
|
|
226
|
+
aria-valuemin={min}
|
|
227
|
+
aria-valuemax={max}
|
|
228
|
+
tabIndex={0}
|
|
229
|
+
className="group relative flex w-2 shrink-0 cursor-col-resize items-stretch justify-center outline-none"
|
|
230
|
+
onPointerDown={onPointerDown}
|
|
231
|
+
onKeyDown={onKeyDown}
|
|
232
|
+
>
|
|
233
|
+
<span
|
|
234
|
+
className="pointer-events-none my-2 w-px rounded-full bg-transparent transition-colors duration-150 group-hover:bg-[var(--color-border-default)] group-focus-visible:bg-[var(--color-primary)]"
|
|
235
|
+
aria-hidden="true"
|
|
236
|
+
/>
|
|
237
|
+
</div>
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/* ── 创建QA对 — 全屏页面内容演示(灰底 + 横向可拖拽大卡) ── */
|
|
242
|
+
function CreateQAContent() {
|
|
243
|
+
const [panelWidths, setPanelWidths] = useState(() => ({
|
|
244
|
+
left: QA_PANEL_LIMITS.left.initial,
|
|
245
|
+
middle: QA_PANEL_LIMITS.middle.initial,
|
|
246
|
+
}));
|
|
247
|
+
const dragStateRef = useRef(null);
|
|
248
|
+
|
|
249
|
+
const updatePanelWidth = useCallback((key, nextWidth) => {
|
|
250
|
+
setPanelWidths((prev) => {
|
|
251
|
+
const clampedWidth = clampQaPanelWidth(key, nextWidth);
|
|
252
|
+
if (prev[key] === clampedWidth) return prev;
|
|
253
|
+
return {
|
|
254
|
+
...prev,
|
|
255
|
+
[key]: clampedWidth,
|
|
256
|
+
};
|
|
257
|
+
});
|
|
258
|
+
}, []);
|
|
259
|
+
|
|
260
|
+
const handleResizePointerDown = useCallback((key, event) => {
|
|
261
|
+
event.preventDefault();
|
|
262
|
+
const startWidth = panelWidths[key];
|
|
263
|
+
if (!startWidth) return;
|
|
264
|
+
|
|
265
|
+
dragStateRef.current = {
|
|
266
|
+
key,
|
|
267
|
+
startX: event.clientX,
|
|
268
|
+
startWidth,
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const previousUserSelect = document.body.style.userSelect;
|
|
272
|
+
document.body.style.userSelect = 'none';
|
|
273
|
+
|
|
274
|
+
const handlePointerMove = (moveEvent) => {
|
|
275
|
+
if (!dragStateRef.current) return;
|
|
276
|
+
const deltaX = moveEvent.clientX - dragStateRef.current.startX;
|
|
277
|
+
updatePanelWidth(key, dragStateRef.current.startWidth + deltaX);
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
const stopDragging = () => {
|
|
281
|
+
dragStateRef.current = null;
|
|
282
|
+
document.body.style.userSelect = previousUserSelect;
|
|
283
|
+
window.removeEventListener('pointermove', handlePointerMove);
|
|
284
|
+
window.removeEventListener('pointerup', stopDragging);
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
window.addEventListener('pointermove', handlePointerMove);
|
|
288
|
+
window.addEventListener('pointerup', stopDragging, { once: true });
|
|
289
|
+
}, [panelWidths, updatePanelWidth]);
|
|
290
|
+
|
|
291
|
+
const handleResizeKeyDown = useCallback((key, event) => {
|
|
292
|
+
const limits = QA_PANEL_LIMITS[key];
|
|
293
|
+
if (!limits) return;
|
|
294
|
+
|
|
295
|
+
let nextWidth = panelWidths[key];
|
|
296
|
+
if (event.key === 'ArrowLeft') nextWidth -= 16;
|
|
297
|
+
if (event.key === 'ArrowRight') nextWidth += 16;
|
|
298
|
+
if (event.key === 'Home') nextWidth = limits.min;
|
|
299
|
+
if (event.key === 'End') nextWidth = limits.max;
|
|
300
|
+
if (nextWidth === panelWidths[key]) return;
|
|
301
|
+
|
|
302
|
+
event.preventDefault();
|
|
303
|
+
updatePanelWidth(key, nextWidth);
|
|
304
|
+
}, [panelWidths, updatePanelWidth]);
|
|
305
|
+
|
|
306
|
+
return (
|
|
307
|
+
<div
|
|
308
|
+
className="flex flex-1 min-h-0 min-w-0 overflow-hidden"
|
|
309
|
+
style={{ padding: '16px' }}
|
|
310
|
+
>
|
|
311
|
+
<div
|
|
312
|
+
className="flex min-h-0 min-w-0 flex-1 overflow-hidden"
|
|
313
|
+
style={{ gap: '8px' }}
|
|
314
|
+
>
|
|
315
|
+
{QA_PANEL_CONTENT.map((panel) => {
|
|
316
|
+
const limits = QA_PANEL_LIMITS[panel.key];
|
|
317
|
+
const panelStyle = panel.key === 'right'
|
|
318
|
+
? WHITE_CARD_STYLE
|
|
319
|
+
: { ...WHITE_CARD_STYLE, width: `${panelWidths[panel.key]}px` };
|
|
320
|
+
const panelWrapClassName = panel.key === 'right'
|
|
321
|
+
? 'flex min-h-0 min-w-0 flex-1 items-stretch'
|
|
322
|
+
: 'flex min-h-0 shrink-0 items-stretch';
|
|
323
|
+
const panelClassName = panel.key === 'right'
|
|
324
|
+
? 'flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden'
|
|
325
|
+
: 'flex min-h-0 shrink-0 flex-col overflow-hidden';
|
|
326
|
+
|
|
327
|
+
return (
|
|
328
|
+
<div key={panel.key} className={panelWrapClassName}>
|
|
329
|
+
<div className={panelClassName} style={panelStyle}>
|
|
330
|
+
<QACardHeader step={panel.step} title={panel.title} hint={panel.hint} />
|
|
331
|
+
<div className="flex-1 min-h-0 overflow-y-auto p-6 text-sm leading-6 text-blueGrey-600">
|
|
332
|
+
{panel.body}
|
|
333
|
+
</div>
|
|
334
|
+
</div>
|
|
335
|
+
{limits ? (
|
|
336
|
+
<ResizablePanelHandle
|
|
337
|
+
label={`${panel.title}宽度调节`}
|
|
338
|
+
value={panelWidths[panel.key]}
|
|
339
|
+
min={limits.min}
|
|
340
|
+
max={limits.max}
|
|
341
|
+
onPointerDown={(event) => handleResizePointerDown(panel.key, event)}
|
|
342
|
+
onKeyDown={(event) => handleResizeKeyDown(panel.key, event)}
|
|
343
|
+
/>
|
|
344
|
+
) : null}
|
|
345
|
+
</div>
|
|
346
|
+
);
|
|
347
|
+
})}
|
|
348
|
+
</div>
|
|
349
|
+
</div>
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/* ── 示例4:暂无权限(含申请权限操作) ── */
|
|
354
|
+
function NoAccessPage() {
|
|
355
|
+
return (
|
|
356
|
+
<div
|
|
357
|
+
className="flex flex-1 min-h-0"
|
|
358
|
+
style={{ margin: '16px' }}
|
|
359
|
+
>
|
|
360
|
+
<div
|
|
361
|
+
className="flex flex-1 min-w-0 items-center justify-center p-6"
|
|
362
|
+
style={WHITE_CARD_STYLE}
|
|
363
|
+
>
|
|
364
|
+
<Empty
|
|
365
|
+
illustrationType="no-access"
|
|
366
|
+
title="暂无权限"
|
|
367
|
+
description="您没有访问该页面的权限,请联系管理员申请。"
|
|
368
|
+
primaryLabel="申请权限"
|
|
369
|
+
onPrimary={() => {}}
|
|
370
|
+
/>
|
|
371
|
+
</div>
|
|
372
|
+
</div>
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/* ── 示例5:功能建设中(仅文案,无操作) ── */
|
|
377
|
+
function ConstructionPage() {
|
|
378
|
+
return (
|
|
379
|
+
<div
|
|
380
|
+
className="flex flex-1 min-h-0"
|
|
381
|
+
style={{ margin: '16px' }}
|
|
382
|
+
>
|
|
383
|
+
<div
|
|
384
|
+
className="flex flex-1 min-w-0 items-center justify-center p-6"
|
|
385
|
+
style={WHITE_CARD_STYLE}
|
|
386
|
+
>
|
|
387
|
+
<Empty
|
|
388
|
+
illustrationType="construction"
|
|
389
|
+
title="功能建设中"
|
|
390
|
+
description="该功能正在开发中,敬请期待。"
|
|
391
|
+
/>
|
|
392
|
+
</div>
|
|
393
|
+
</div>
|
|
394
|
+
);
|
|
395
|
+
}
|