@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.
Files changed (176) hide show
  1. package/AI_READ_FIRST.md +131 -0
  2. package/LICENSE +21 -0
  3. package/README.md +353 -0
  4. package/package.json +67 -0
  5. package/scripts/check-tfds-contract.mjs +334 -0
  6. package/scripts/check-tfds-integration.mjs +263 -0
  7. package/scripts/postinstall-cursor-skill.mjs +382 -0
  8. package/scripts/setup.mjs +520 -0
  9. package/skills/tfds/CHECKLIST.md +205 -0
  10. package/skills/tfds/COMMON_FAILURES.md +238 -0
  11. package/skills/tfds/DESIGN_PRINCIPLES.md +477 -0
  12. package/skills/tfds/GLOBAL_DESIGN_RULES.md +636 -0
  13. package/skills/tfds/LAYOUT_RECIPES.md +140 -0
  14. package/skills/tfds/LAYOUT_RULES.md +1355 -0
  15. package/skills/tfds/PAGE_ARCHETYPES.md +201 -0
  16. package/skills/tfds/SKILL.md +188 -0
  17. package/skills/tfds/components.index.json +7305 -0
  18. package/skills/tfds/components.summary.json +1809 -0
  19. package/src/_b_end_runtime/components/AiSuggestionShared.jsx +166 -0
  20. package/src/_b_end_runtime/components/Avatar.jsx +325 -0
  21. package/src/_b_end_runtime/components/Avatar.tokens.js +76 -0
  22. package/src/_b_end_runtime/components/AvatarGridPreview.jsx +56 -0
  23. package/src/_b_end_runtime/components/AvatarGroup.jsx +80 -0
  24. package/src/_b_end_runtime/components/AvatarGroup.tokens.js +28 -0
  25. package/src/_b_end_runtime/components/Button.jsx +144 -0
  26. package/src/_b_end_runtime/components/Button.tokens.js +90 -0
  27. package/src/_b_end_runtime/components/Card.jsx +460 -0
  28. package/src/_b_end_runtime/components/Card.tokens.js +124 -0
  29. package/src/_b_end_runtime/components/CardPreview.jsx +51 -0
  30. package/src/_b_end_runtime/components/ChatBubble.jsx +384 -0
  31. package/src/_b_end_runtime/components/ChatBubble.tokens.js +60 -0
  32. package/src/_b_end_runtime/components/ChatBubblePreview.jsx +129 -0
  33. package/src/_b_end_runtime/components/ChatInput.jsx +1399 -0
  34. package/src/_b_end_runtime/components/ChatInput.tokens.js +75 -0
  35. package/src/_b_end_runtime/components/ChatMessage.jsx +2215 -0
  36. package/src/_b_end_runtime/components/ChatMessage.tokens.js +257 -0
  37. package/src/_b_end_runtime/components/ChatMessagePreview.jsx +388 -0
  38. package/src/_b_end_runtime/components/Checkbox.jsx +317 -0
  39. package/src/_b_end_runtime/components/Checkbox.tokens.js +59 -0
  40. package/src/_b_end_runtime/components/ConversationList.jsx +1264 -0
  41. package/src/_b_end_runtime/components/ConversationList.tokens.js +135 -0
  42. package/src/_b_end_runtime/components/ConversationListPreview.jsx +108 -0
  43. package/src/_b_end_runtime/components/CustomerServiceWorkspaceFrame.jsx +324 -0
  44. package/src/_b_end_runtime/components/CustomerServiceWorkspaceFrame.tokens.js +69 -0
  45. package/src/_b_end_runtime/components/DatePicker.jsx +739 -0
  46. package/src/_b_end_runtime/components/DatePicker.tokens.js +99 -0
  47. package/src/_b_end_runtime/components/Empty.jsx +141 -0
  48. package/src/_b_end_runtime/components/Empty.tokens.js +40 -0
  49. package/src/_b_end_runtime/components/Form.jsx +609 -0
  50. package/src/_b_end_runtime/components/Form.tokens.js +77 -0
  51. package/src/_b_end_runtime/components/FormFieldStack.jsx +123 -0
  52. package/src/_b_end_runtime/components/FormFieldStack.tokens.js +12 -0
  53. package/src/_b_end_runtime/components/FormTitle.jsx +119 -0
  54. package/src/_b_end_runtime/components/FormTitle.tokens.js +87 -0
  55. package/src/_b_end_runtime/components/FullScreenPage.jsx +97 -0
  56. package/src/_b_end_runtime/components/FullScreenPage.tokens.js +19 -0
  57. package/src/_b_end_runtime/components/Icon.jsx +172 -0
  58. package/src/_b_end_runtime/components/Icon.tokens.js +26 -0
  59. package/src/_b_end_runtime/components/IconGridPreview.jsx +277 -0
  60. package/src/_b_end_runtime/components/InfoDisplayPanel.jsx +620 -0
  61. package/src/_b_end_runtime/components/InfoDisplayPanel.tokens.js +71 -0
  62. package/src/_b_end_runtime/components/InfoDisplayPanelPreview.jsx +133 -0
  63. package/src/_b_end_runtime/components/Input.jsx +258 -0
  64. package/src/_b_end_runtime/components/Input.tokens.js +68 -0
  65. package/src/_b_end_runtime/components/InputNumber.jsx +242 -0
  66. package/src/_b_end_runtime/components/InputNumber.tokens.js +55 -0
  67. package/src/_b_end_runtime/components/Modal.jsx +155 -0
  68. package/src/_b_end_runtime/components/Modal.tokens.js +73 -0
  69. package/src/_b_end_runtime/components/NavBar.jsx +842 -0
  70. package/src/_b_end_runtime/components/NavBar.tokens.js +97 -0
  71. package/src/_b_end_runtime/components/NavBarPreview.jsx +11 -0
  72. package/src/_b_end_runtime/components/Radio.jsx +227 -0
  73. package/src/_b_end_runtime/components/Radio.tokens.js +59 -0
  74. package/src/_b_end_runtime/components/Select.jsx +766 -0
  75. package/src/_b_end_runtime/components/Select.tokens.js +99 -0
  76. package/src/_b_end_runtime/components/Sheet.jsx +132 -0
  77. package/src/_b_end_runtime/components/Sheet.tokens.js +61 -0
  78. package/src/_b_end_runtime/components/Slider.jsx +346 -0
  79. package/src/_b_end_runtime/components/Slider.tokens.js +47 -0
  80. package/src/_b_end_runtime/components/Switch.jsx +124 -0
  81. package/src/_b_end_runtime/components/Switch.tokens.js +38 -0
  82. package/src/_b_end_runtime/components/Table.jsx +1338 -0
  83. package/src/_b_end_runtime/components/Table.tokens.js +147 -0
  84. package/src/_b_end_runtime/components/TablePreview.jsx +599 -0
  85. package/src/_b_end_runtime/components/Tabs.jsx +149 -0
  86. package/src/_b_end_runtime/components/Tabs.tokens.js +102 -0
  87. package/src/_b_end_runtime/components/Tag.jsx +199 -0
  88. package/src/_b_end_runtime/components/Tag.tokens.js +171 -0
  89. package/src/_b_end_runtime/components/TagBar.jsx +1134 -0
  90. package/src/_b_end_runtime/components/TagBar.tokens.js +75 -0
  91. package/src/_b_end_runtime/components/TagGridPreview.jsx +23 -0
  92. package/src/_b_end_runtime/components/TagInput.jsx +382 -0
  93. package/src/_b_end_runtime/components/TagInput.tokens.js +52 -0
  94. package/src/_b_end_runtime/components/TextArea.jsx +363 -0
  95. package/src/_b_end_runtime/components/TextArea.tokens.js +65 -0
  96. package/src/_b_end_runtime/components/TimePicker.jsx +444 -0
  97. package/src/_b_end_runtime/components/TimePicker.tokens.js +77 -0
  98. package/src/_b_end_runtime/components/Toast.jsx +120 -0
  99. package/src/_b_end_runtime/components/Toast.tokens.js +146 -0
  100. package/src/_b_end_runtime/components/Tooltip.jsx +282 -0
  101. package/src/_b_end_runtime/components/Tooltip.tokens.js +48 -0
  102. package/src/_b_end_runtime/components/TooltipPreview.jsx +50 -0
  103. package/src/_b_end_runtime/components/Upload.jsx +455 -0
  104. package/src/_b_end_runtime/components/Upload.tokens.js +47 -0
  105. package/src/_b_end_runtime/components/avatar-assets/avatar-default.png +0 -0
  106. package/src/_b_end_runtime/components/avatar-group-assets/avatar-group-1.png +0 -0
  107. package/src/_b_end_runtime/components/avatar-group-assets/avatar-group-2.png +0 -0
  108. package/src/_b_end_runtime/components/avatar-group-assets/avatar-group-3.png +0 -0
  109. package/src/_b_end_runtime/components/avatar-group-assets/avatar-group-4.png +0 -0
  110. package/src/_b_end_runtime/components/avatar-group-assets/avatar-group-5.png +0 -0
  111. package/src/_b_end_runtime/components/empty-assets/administrator-1.svg +40 -0
  112. package/src/_b_end_runtime/components/empty-assets/administrator-2.svg +33 -0
  113. package/src/_b_end_runtime/components/empty-assets/construction.svg +33 -0
  114. package/src/_b_end_runtime/components/empty-assets/failure.svg +49 -0
  115. package/src/_b_end_runtime/components/empty-assets/idle.svg +34 -0
  116. package/src/_b_end_runtime/components/empty-assets/no-access.svg +36 -0
  117. package/src/_b_end_runtime/components/empty-assets/no-content.svg +77 -0
  118. package/src/_b_end_runtime/components/empty-assets/no-result.svg +61 -0
  119. package/src/_b_end_runtime/components/empty-assets/not-found.svg +46 -0
  120. package/src/_b_end_runtime/components/empty-assets/success.svg +38 -0
  121. package/src/_b_end_runtime/components/file-type-assets/batch-report.png +0 -0
  122. package/src/_b_end_runtime/components/file-type-assets/catcat.svg +21 -0
  123. package/src/_b_end_runtime/components/file-type-assets/code.png +0 -0
  124. package/src/_b_end_runtime/components/file-type-assets/conversation.png +0 -0
  125. package/src/_b_end_runtime/components/file-type-assets/document.png +0 -0
  126. package/src/_b_end_runtime/components/file-type-assets/feishu-card.png +0 -0
  127. package/src/_b_end_runtime/components/file-type-assets/feishu-sheet.png +0 -0
  128. package/src/_b_end_runtime/components/file-type-assets/feishu.png +0 -0
  129. package/src/_b_end_runtime/components/file-type-assets/image.png +0 -0
  130. package/src/_b_end_runtime/components/file-type-assets/index.js +105 -0
  131. package/src/_b_end_runtime/components/file-type-assets/knowledge.png +0 -0
  132. package/src/_b_end_runtime/components/file-type-assets/pdf.png +0 -0
  133. package/src/_b_end_runtime/components/file-type-assets/pe.png +0 -0
  134. package/src/_b_end_runtime/components/file-type-assets/strategy.png +0 -0
  135. package/src/_b_end_runtime/components/file-type-assets/table.png +0 -0
  136. package/src/_b_end_runtime/components/file-type-assets/webpage.png +0 -0
  137. package/src/_b_end_runtime/components/file-type-assets/xmind.png +0 -0
  138. package/src/_b_end_runtime/components/icons/icon-data.js +12496 -0
  139. package/src/_b_end_runtime/components/nav-bar-assets/bytehi-logo-mark.svg +21 -0
  140. package/src/_b_end_runtime/components/table-assets/avatar.png +0 -0
  141. package/src/_b_end_runtime/components/table-assets/button.png +0 -0
  142. package/src/_b_end_runtime/components/table-assets/icon-chevron-down.png +0 -0
  143. package/src/_b_end_runtime/components/table-cell-assets/avatar.png +0 -0
  144. package/src/_b_end_runtime/components/table-cell-assets/button.png +0 -0
  145. package/src/_b_end_runtime/components/table-cell-assets/checkbox.png +0 -0
  146. package/src/_b_end_runtime/components/table-cell-assets/icon-chevron-right.png +0 -0
  147. package/src/_b_end_runtime/components/table-cell-assets/icon.png +0 -0
  148. package/src/_b_end_runtime/components/table-cell-assets/semi-icons-handle.png +0 -0
  149. package/src/_b_end_runtime/components/table-cell-assets/semi-icons-tree-triangle-right.png +0 -0
  150. package/src/_b_end_runtime/components/table-cell-assets/switch.png +0 -0
  151. package/src/_b_end_runtime/components/tagShared.js +3 -0
  152. package/src/_b_end_runtime/components/team-avatar-assets/chengcheng-murphy.png +0 -0
  153. package/src/_b_end_runtime/components/team-avatar-assets/duan-ran.png +0 -0
  154. package/src/_b_end_runtime/components/team-avatar-assets/guo-zhezhi.png +0 -0
  155. package/src/_b_end_runtime/components/team-avatar-assets/li-siru.png +0 -0
  156. package/src/_b_end_runtime/components/team-avatar-assets/liu-delin.png +0 -0
  157. package/src/_b_end_runtime/components.js +3499 -0
  158. package/src/_b_end_runtime/index.js +9 -0
  159. package/src/_b_end_runtime/page-patterns/BasePageFramePattern.jsx +395 -0
  160. package/src/_b_end_runtime/page-patterns/ChatConversationPattern.jsx +989 -0
  161. package/src/_b_end_runtime/page-patterns/ChatHomePagePattern.jsx +281 -0
  162. package/src/_b_end_runtime/page-patterns/CopilotPagePattern.jsx +380 -0
  163. package/src/_b_end_runtime/page-patterns/CustomerServiceWorkspaceFramePattern.jsx +392 -0
  164. package/src/_b_end_runtime/page-patterns/IMConversationPattern.jsx +590 -0
  165. package/src/_b_end_runtime/page-patterns/McpManagementPage.jsx +237 -0
  166. package/src/_b_end_runtime/page-patterns/StrategyListPage.jsx +189 -0
  167. package/src/_b_end_runtime/page-patterns/TabTopBarListPage.jsx +594 -0
  168. package/src/_b_end_runtime/page-patterns/VariableManagementPage.jsx +87 -0
  169. package/src/_b_end_runtime/page-patterns/pageListShared.jsx +177 -0
  170. package/src/_b_end_runtime/patterns.js +428 -0
  171. package/src/_b_end_runtime/preview-registry.jsx +4719 -0
  172. package/src/_b_end_runtime/teamMembers.js +56 -0
  173. package/src/_b_end_runtime/tokens.js +500 -0
  174. package/src/index.d.ts +1073 -0
  175. package/src/index.js +52 -0
  176. package/theme.css +350 -0
@@ -0,0 +1,4719 @@
1
+ // B端设计系统 — 预览注册表
2
+ //
3
+ // 平台预览配置,和组件规范(components.js / 三件套)分离。
4
+ // 每个组件一段:挂组件引用、TOKEN_MAP、预览控制项、控制值→Props 的映射。
5
+
6
+ import { Plus, Heart, Star, Zap, Search, Eye } from 'lucide-react';
7
+ import { useCallback, useEffect, useState } from 'react';
8
+ import AvatarGridPreview from './components/AvatarGridPreview';
9
+ import AvatarGroup from './components/AvatarGroup';
10
+ import Button from './components/Button';
11
+ import Tabs from './components/Tabs';
12
+ import Modal from './components/Modal';
13
+ import Sheet from './components/Sheet';
14
+ import FormTitle from './components/FormTitle';
15
+ import Form, { FORM_FIELD_TYPES, FORM_SAMPLE_ITEMS } from './components/Form';
16
+ import FormFieldStack from './components/FormFieldStack';
17
+ import Input from './components/Input';
18
+ import TextArea from './components/TextArea';
19
+ import InputNumber from './components/InputNumber';
20
+ import Select from './components/Select';
21
+ import RadioGroup, { Radio } from './components/Radio';
22
+ import CheckboxGroup, { Checkbox } from './components/Checkbox';
23
+ import Switch from './components/Switch';
24
+ import ChatBubblePreview from './components/ChatBubblePreview';
25
+ import ChatInput from './components/ChatInput';
26
+ import ChatMessagePreview from './components/ChatMessagePreview';
27
+ import CardPreview from './components/CardPreview';
28
+ import ConversationListPreview from './components/ConversationListPreview';
29
+ import InfoDisplayPanelPreview from './components/InfoDisplayPanelPreview';
30
+ import NavBarPreview from './components/NavBarPreview';
31
+ import TagBar, { TAGBAR_SAMPLE_BUSINESSES, TAGBAR_SAMPLE_ITEMS } from './components/TagBar';
32
+ import Icon from './components/Icon';
33
+ import IconGridPreview from './components/IconGridPreview';
34
+ import Slider from './components/Slider';
35
+ import Upload from './components/Upload';
36
+ import DatePicker from './components/DatePicker';
37
+ import TimePicker from './components/TimePicker';
38
+ import Toast from './components/Toast';
39
+ import Tag from './components/Tag';
40
+ import TagInput from './components/TagInput';
41
+ import TagGridPreview from './components/TagGridPreview';
42
+ import TablePreview, {
43
+ TABLE_VARIANT_OPTIONS,
44
+ TABLE_COLUMN_TYPE_OPTIONS,
45
+ TABLE_CELL_SIZE_OPTIONS,
46
+ buildTableUsageFromControls,
47
+ } from './components/TablePreview';
48
+ import TooltipPreview from './components/TooltipPreview';
49
+ import { AVATAR_TOKEN_MAP } from './components/Avatar.tokens';
50
+ import { AVATAR_GROUP_TOKEN_MAP } from './components/AvatarGroup.tokens';
51
+ import { BUTTON_TOKEN_MAP } from './components/Button.tokens';
52
+ import { TABS_TOKEN_MAP } from './components/Tabs.tokens';
53
+ import { MODAL_TOKEN_MAP } from './components/Modal.tokens';
54
+ import { SHEET_TOKEN_MAP } from './components/Sheet.tokens';
55
+ import { FORM_TITLE_TOKEN_MAP } from './components/FormTitle.tokens';
56
+ import { FORM_TOKEN_MAP } from './components/Form.tokens';
57
+ import { FORM_FIELD_STACK_TOKEN_MAP } from './components/FormFieldStack.tokens';
58
+ import { INPUT_TOKEN_MAP } from './components/Input.tokens';
59
+ import { TEXTAREA_TOKEN_MAP } from './components/TextArea.tokens';
60
+ import { INPUT_NUMBER_TOKEN_MAP } from './components/InputNumber.tokens';
61
+ import { SELECT_TOKEN_MAP } from './components/Select.tokens';
62
+ import { RADIO_TOKEN_MAP } from './components/Radio.tokens';
63
+ import { CHECKBOX_TOKEN_MAP } from './components/Checkbox.tokens';
64
+ import { SWITCH_TOKEN_MAP } from './components/Switch.tokens';
65
+ import { CHATBUBBLE_TOKEN_MAP } from './components/ChatBubble.tokens';
66
+ import { CHATINPUT_TOKEN_MAP } from './components/ChatInput.tokens';
67
+ import { CHAT_MESSAGE_TOKEN_MAP } from './components/ChatMessage.tokens';
68
+ import { CARD_TOKEN_MAP } from './components/Card.tokens';
69
+ import { CONVERSATION_LIST_TOKEN_MAP } from './components/ConversationList.tokens';
70
+ import { INFO_DISPLAY_PANEL_TOKEN_MAP } from './components/InfoDisplayPanel.tokens';
71
+ import { NAVBAR_TOKEN_MAP } from './components/NavBar.tokens';
72
+ import { TAGBAR_TOKEN_MAP } from './components/TagBar.tokens';
73
+ import { ICON_TOKEN_MAP } from './components/Icon.tokens';
74
+ import { SLIDER_TOKEN_MAP } from './components/Slider.tokens';
75
+ import { UPLOAD_TOKEN_MAP } from './components/Upload.tokens';
76
+ import { DATEPICKER_TOKEN_MAP } from './components/DatePicker.tokens';
77
+ import { TIMEPICKER_TOKEN_MAP } from './components/TimePicker.tokens';
78
+ import { TOAST_TOKEN_MAP } from './components/Toast.tokens';
79
+ import { TAG_TOKEN_MAP } from './components/Tag.tokens';
80
+ import { TAGINPUT_TOKEN_MAP } from './components/TagInput.tokens';
81
+ import { TABLE_TOKEN_MAP } from './components/Table.tokens';
82
+ import { TOOLTIP_TOKEN_MAP } from './components/Tooltip.tokens';
83
+ import avatarJsxRaw from './components/Avatar.jsx?raw';
84
+ import avatarGroupJsxRaw from './components/AvatarGroup.jsx?raw';
85
+ import buttonJsxRaw from './components/Button.jsx?raw';
86
+ import tabsJsxRaw from './components/Tabs.jsx?raw';
87
+ import modalJsxRaw from './components/Modal.jsx?raw';
88
+ import sheetJsxRaw from './components/Sheet.jsx?raw';
89
+ import formTitleJsxRaw from './components/FormTitle.jsx?raw';
90
+ import formJsxRaw from './components/Form.jsx?raw';
91
+ import formFieldStackJsxRaw from './components/FormFieldStack.jsx?raw';
92
+ import inputJsxRaw from './components/Input.jsx?raw';
93
+ import textareaJsxRaw from './components/TextArea.jsx?raw';
94
+ import inputNumberJsxRaw from './components/InputNumber.jsx?raw';
95
+ import selectJsxRaw from './components/Select.jsx?raw';
96
+ import radioJsxRaw from './components/Radio.jsx?raw';
97
+ import checkboxJsxRaw from './components/Checkbox.jsx?raw';
98
+ import switchJsxRaw from './components/Switch.jsx?raw';
99
+ import chatBubbleJsxRaw from './components/ChatBubble.jsx?raw';
100
+ import chatInputJsxRaw from './components/ChatInput.jsx?raw';
101
+ import chatMessageJsxRaw from './components/ChatMessage.jsx?raw';
102
+ import cardJsxRaw from './components/Card.jsx?raw';
103
+ import conversationListJsxRaw from './components/ConversationList.jsx?raw';
104
+ import infoDisplayPanelJsxRaw from './components/InfoDisplayPanel.jsx?raw';
105
+ import navBarJsxRaw from './components/NavBar.jsx?raw';
106
+ import tagBarJsxRaw from './components/TagBar.jsx?raw';
107
+ import iconJsxRaw from './components/Icon.jsx?raw';
108
+
109
+ import sliderJsxRaw from './components/Slider.jsx?raw';
110
+ import uploadJsxRaw from './components/Upload.jsx?raw';
111
+ import datePickerJsxRaw from './components/DatePicker.jsx?raw';
112
+ import timePickerJsxRaw from './components/TimePicker.jsx?raw';
113
+ import toastJsxRaw from './components/Toast.jsx?raw';
114
+ import tagJsxRaw from './components/Tag.jsx?raw';
115
+ import tagInputJsxRaw from './components/TagInput.jsx?raw';
116
+ import tableJsxRaw from './components/Table.jsx?raw';
117
+ import tooltipJsxRaw from './components/Tooltip.jsx?raw';
118
+ import Empty from './components/Empty';
119
+ import { EMPTY_TOKEN_MAP } from './components/Empty.tokens';
120
+ import emptyJsxRaw from './components/Empty.jsx?raw';
121
+ import FullScreenPage from './components/FullScreenPage';
122
+ import { FULL_SCREEN_PAGE_TOKEN_MAP } from './components/FullScreenPage.tokens';
123
+ import fullScreenPageJsxRaw from './components/FullScreenPage.jsx?raw';
124
+
125
+ /** 预览用:切换「初始开/关」时通过 key 重挂载,避免 defaultChecked 仅在 mount 生效 */
126
+ function SwitchPreview({ variant = 'brand', defaultChecked, disabled }) {
127
+ return (
128
+ <Switch
129
+ key={`sw-${defaultChecked}-${disabled}`}
130
+ variant={variant}
131
+ defaultChecked={defaultChecked}
132
+ disabled={disabled}
133
+ />
134
+ );
135
+ }
136
+
137
+ const SLIDER_PREVIEW_MARKS = { 0: '0%', 25: '25%', 50: '50%', 75: '75%', 100: '100%' };
138
+ const SLIDER_PREVIEW_MARKS_TEXT = JSON.stringify(SLIDER_PREVIEW_MARKS, null, 2);
139
+ const SLIDER_PREVIEW_DEFAULTS = {
140
+ min: 0,
141
+ max: 100,
142
+ step: 1,
143
+ showTooltip: true,
144
+ defaultValueRangeText: '[20, 80]',
145
+ defaultValueSingleText: '50',
146
+ };
147
+
148
+ function composeHidden(hiddenA, hiddenB) {
149
+ if (!hiddenA) return hiddenB;
150
+ if (!hiddenB) return hiddenA;
151
+ return (ctx) => hiddenA(ctx) || hiddenB(ctx);
152
+ }
153
+
154
+ function getSliderControlIds(prefix = '') {
155
+ const buildId = (name) => (prefix ? `${prefix}${name.charAt(0).toUpperCase()}${name.slice(1)}` : name);
156
+ return {
157
+ initialKey: buildId('initialKey'),
158
+ min: buildId('min'),
159
+ max: buildId('max'),
160
+ step: buildId('step'),
161
+ defaultValueText: buildId('defaultValueText'),
162
+ showTooltip: buildId('showTooltip'),
163
+ hasMarks: buildId('hasMarks'),
164
+ marksText: buildId('marksText'),
165
+ disabled: buildId('disabled'),
166
+ };
167
+ }
168
+
169
+ function resolveSliderPreviewDefaultValueText(initialKey = 'range') {
170
+ return initialKey === 'single'
171
+ ? SLIDER_PREVIEW_DEFAULTS.defaultValueSingleText
172
+ : SLIDER_PREVIEW_DEFAULTS.defaultValueRangeText;
173
+ }
174
+
175
+ function parseSliderPreviewDefaultValue(raw, initialKey) {
176
+ const fallback = initialKey === 'single' ? 50 : [20, 80];
177
+ if (typeof raw === 'number') return initialKey === 'single' ? raw : [20, 80];
178
+ const text = String(raw ?? '').trim();
179
+ if (!text) return fallback;
180
+
181
+ try {
182
+ const parsed = JSON.parse(text);
183
+ if (initialKey === 'single') {
184
+ const num = Array.isArray(parsed) ? Number(parsed[0]) : Number(parsed);
185
+ return Number.isFinite(num) ? num : fallback;
186
+ }
187
+ if (Array.isArray(parsed)) {
188
+ const values = parsed.slice(0, 2).map((item) => Number(item)).filter((item) => Number.isFinite(item));
189
+ if (values.length === 2) return values;
190
+ }
191
+ } catch {
192
+ if (initialKey === 'single') {
193
+ const num = Number(text);
194
+ if (Number.isFinite(num)) return num;
195
+ }
196
+ }
197
+
198
+ return fallback;
199
+ }
200
+
201
+ function parseSliderPreviewMarks(text, enabled) {
202
+ if (!enabled) return undefined;
203
+ const raw = String(text ?? '').trim();
204
+ if (!raw) return SLIDER_PREVIEW_MARKS;
205
+ try {
206
+ const parsed = JSON.parse(raw);
207
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
208
+ return parsed;
209
+ }
210
+ } catch {
211
+ return SLIDER_PREVIEW_MARKS;
212
+ }
213
+ return SLIDER_PREVIEW_MARKS;
214
+ }
215
+
216
+ function buildSliderPreviewControls(prefix = '', hidden) {
217
+ const ids = getSliderControlIds(prefix);
218
+ return [
219
+ {
220
+ id: ids.initialKey,
221
+ label: '初始模式',
222
+ type: 'seg',
223
+ options: [
224
+ { id: 'range', label: '区间' },
225
+ { id: 'single', label: '单值' },
226
+ ],
227
+ default: 'range',
228
+ hidden,
229
+ },
230
+ {
231
+ id: ids.min,
232
+ label: '最小值',
233
+ type: 'number',
234
+ default: SLIDER_PREVIEW_DEFAULTS.min,
235
+ hidden,
236
+ },
237
+ {
238
+ id: ids.max,
239
+ label: '最大值',
240
+ type: 'number',
241
+ default: SLIDER_PREVIEW_DEFAULTS.max,
242
+ hidden,
243
+ },
244
+ {
245
+ id: ids.step,
246
+ label: '步长',
247
+ type: 'number',
248
+ default: SLIDER_PREVIEW_DEFAULTS.step,
249
+ hidden,
250
+ },
251
+ {
252
+ id: ids.defaultValueText,
253
+ label: '默认值',
254
+ type: 'text',
255
+ default: resolveSliderPreviewDefaultValueText('range'),
256
+ hidden,
257
+ },
258
+ {
259
+ id: ids.showTooltip,
260
+ label: '提示气泡',
261
+ type: 'switch',
262
+ default: SLIDER_PREVIEW_DEFAULTS.showTooltip,
263
+ hidden,
264
+ },
265
+ {
266
+ id: ids.hasMarks,
267
+ label: '刻度',
268
+ type: 'seg',
269
+ options: [
270
+ { id: 'on', label: '显示' },
271
+ { id: 'off', label: '隐藏' },
272
+ ],
273
+ default: 'off',
274
+ hidden,
275
+ },
276
+ {
277
+ id: ids.marksText,
278
+ label: '刻度配置',
279
+ type: 'textarea',
280
+ default: SLIDER_PREVIEW_MARKS_TEXT,
281
+ hidden: composeHidden(hidden, ({ controlValues }) => (controlValues[ids.hasMarks] || 'off') !== 'on'),
282
+ },
283
+ {
284
+ id: ids.disabled,
285
+ label: '禁用',
286
+ type: 'switch',
287
+ default: false,
288
+ hidden,
289
+ },
290
+ ];
291
+ }
292
+
293
+ function normalizeSliderPreviewControls(nextControlValues, prevControlValues, changedControlId, prefix = '') {
294
+ const ids = getSliderControlIds(prefix);
295
+ const next = { ...nextControlValues };
296
+ const nextInitialKey = next[ids.initialKey] || 'range';
297
+ const prevInitialKey = prevControlValues?.[ids.initialKey] || 'range';
298
+ const prevDefaultText = prevControlValues?.[ids.defaultValueText];
299
+ const nextDefaultText = next[ids.defaultValueText];
300
+
301
+ if (changedControlId === '__init__' || nextDefaultText == null || nextDefaultText === '') {
302
+ next[ids.defaultValueText] = resolveSliderPreviewDefaultValueText(nextInitialKey);
303
+ } else if (changedControlId === ids.initialKey) {
304
+ const prevTemplate = resolveSliderPreviewDefaultValueText(prevInitialKey);
305
+ if (prevDefaultText == null || prevDefaultText === prevTemplate || nextDefaultText === prevTemplate) {
306
+ next[ids.defaultValueText] = resolveSliderPreviewDefaultValueText(nextInitialKey);
307
+ }
308
+ }
309
+
310
+ if (changedControlId === '__init__' || changedControlId === ids.hasMarks) {
311
+ if ((next[ids.hasMarks] || 'off') === 'on' && !String(next[ids.marksText] || '').trim()) {
312
+ next[ids.marksText] = SLIDER_PREVIEW_MARKS_TEXT;
313
+ }
314
+ }
315
+
316
+ return next;
317
+ }
318
+
319
+ function buildSliderPreviewProps(controlValues, prefix = '') {
320
+ const ids = getSliderControlIds(prefix);
321
+ const initialKey = controlValues[ids.initialKey] || 'range';
322
+ const min = Number(controlValues[ids.min]);
323
+ const max = Number(controlValues[ids.max]);
324
+ const safeMin = Number.isFinite(min) ? min : SLIDER_PREVIEW_DEFAULTS.min;
325
+ const safeMaxCandidate = Number.isFinite(max) ? max : SLIDER_PREVIEW_DEFAULTS.max;
326
+ const safeMax = safeMaxCandidate > safeMin ? safeMaxCandidate : safeMin + 1;
327
+ const step = Number(controlValues[ids.step]);
328
+ const safeStep = Number.isFinite(step) && step > 0 ? step : SLIDER_PREVIEW_DEFAULTS.step;
329
+ const showTooltip = controlValues[ids.showTooltip] !== false;
330
+ const disabled = controlValues[ids.disabled] === true;
331
+ const marksEnabled = (controlValues[ids.hasMarks] || 'off') === 'on';
332
+
333
+ return {
334
+ initialKey,
335
+ min: safeMin,
336
+ max: safeMax,
337
+ step: safeStep,
338
+ defaultValue: parseSliderPreviewDefaultValue(controlValues[ids.defaultValueText], initialKey),
339
+ showTooltip,
340
+ marks: parseSliderPreviewMarks(controlValues[ids.marksText], marksEnabled),
341
+ disabled,
342
+ };
343
+ }
344
+
345
+ function formatUsageLiteral(value) {
346
+ if (value === undefined) return undefined;
347
+ if (typeof value === 'string') return `'${value}'`;
348
+ return JSON.stringify(value);
349
+ }
350
+
351
+ /** 预览用:key 保证切换配置时非受控 defaultValue 生效 */
352
+ function SliderPreview({ min, max, step, defaultValue, showTooltip, marks, disabled, initialKey }) {
353
+ return (
354
+ <Slider
355
+ key={`sl-${initialKey}-${min}-${max}-${step}-${Boolean(marks)}-${disabled}-${JSON.stringify(defaultValue)}`}
356
+ min={min}
357
+ max={max}
358
+ step={step}
359
+ defaultValue={defaultValue}
360
+ showTooltip={showTooltip}
361
+ marks={marks}
362
+ disabled={disabled}
363
+ />
364
+ );
365
+ }
366
+
367
+ const INPUT_SINGLE_SUGGESTIONS = [
368
+ '您好,您的问题已收到,抖音客服正在为您核实处理。',
369
+ '您好,这边已为您记录反馈,正在同步专员跟进。',
370
+ '您好,当前问题我们正在核查中,请您稍候查看处理结果。',
371
+ ];
372
+
373
+ const INPUT_MULTI_SUGGESTION_GROUPS = [
374
+ [
375
+ '您好,正在为您核实,请稍候。',
376
+ '抱歉给您带来不便,这边已经帮您加急处理。',
377
+ '您好,您反馈的问题我们已经收到,建议您先提供订单号和问题截图,方便抖音客服尽快为您核实处理。',
378
+ ],
379
+ [
380
+ '您好,这边先帮您登记反馈。',
381
+ '请您放心,当前问题已经提交相关同学加急排查。',
382
+ '为了更快帮您确认处理进度,建议您补充订单编号、联系方式以及具体异常时间,抖音客服会尽快协助您跟进。',
383
+ ],
384
+ [
385
+ '您好,已收到您的咨询。',
386
+ '很抱歉给您带来困扰,这边正在为您核实具体原因。',
387
+ '若方便的话,您可以补充问题页面截图和操作路径,我们会结合账号信息一并核查,尽快给您同步处理结果。',
388
+ ],
389
+ ];
390
+
391
+ const TEXTAREA_SINGLE_SUGGESTIONS = [
392
+ '根据当前对话内容,建议先记录用户诉求、异常现象和下一步处理动作。',
393
+ '建议在备注中补充订单号、问题发生时间和已核查结果,方便后续继续跟进。',
394
+ '可先生成一版客服备注草稿,后续结合人工判断再做补充和修订。',
395
+ ];
396
+
397
+ const TEXTAREA_MULTI_SUGGESTION_GROUPS = [
398
+ [
399
+ '根据当前对话内容,建议先记录用户诉求、异常现象和下一步处理动作。',
400
+ '建议在备注中补充订单号、问题发生时间和已核查结果,方便后续继续跟进。',
401
+ ],
402
+ [
403
+ '建议优先整理用户诉求、处理时效和需补充的材料信息。',
404
+ '如果需要后续交接,可一并记录当前判责结论和预计回访时间。',
405
+ ],
406
+ [
407
+ '可先形成一版标准备注草稿,再结合人工判断补充风险说明。',
408
+ '建议在草稿中明确“已同步处理”“待用户补充资料”“下一步动作”这三类信息。',
409
+ ],
410
+ ];
411
+
412
+ const TAGINPUT_AI_SUGGESTION_GROUPS = [
413
+ [
414
+ ['改签', '门店变更'],
415
+ ['退款', '售后处理'],
416
+ ['物流异常', '催办'],
417
+ ],
418
+ [
419
+ ['优惠券', '核销异常'],
420
+ ['客服回访', '待跟进'],
421
+ ['资损风险', '升级处理'],
422
+ ],
423
+ [
424
+ ['履约异常', '商家侧'],
425
+ ['平台规则', '需复核'],
426
+ ['用户反馈', '已记录'],
427
+ ],
428
+ ];
429
+
430
+ /** 预览用:key 随「填充 / 超长演示」切换重置非受控初值 */
431
+ function TextAreaPreview({
432
+ variant = 'default',
433
+ fillMode = 'empty',
434
+ countOverflow = false,
435
+ disabled = false,
436
+ status = 'default',
437
+ resize = 'vertical',
438
+ fillHeight = false,
439
+ minRows = 3,
440
+ aiSuggestion,
441
+ aiSuggestions,
442
+ }) {
443
+ const isCodeVariant = variant === 'code';
444
+ const sample = isCodeVariant
445
+ ? '# 角色\n你是抖音安全中心的小安智能助手,负责理解用户诉求并给出清晰答案。\n\n# 注意\n1. 回答前先判断是否需要调用知识库。\n2. 需要给出可执行、可验证的处理建议。'
446
+ : '我们非常高兴地向您介绍我们的最新产品 HiUI。HiUI是一个创新的在线客服解决方案,旨在帮助企业更好地理解并服务他们的客户。';
447
+ const defaultValue = fillMode === 'filled' ? sample : '';
448
+ const enforceMaxLength = !countOverflow;
449
+ const minRowsSafe = Math.min(5, Math.max(1, Math.round(Number(minRows)) || 3));
450
+ /** 仅「超长计数=开」时展示右下角字数区;关闭后整块不渲染,总高度随 minRows+正文收缩 */
451
+ const showCountDemo = Boolean(countOverflow);
452
+ const [singleIndex, setSingleIndex] = useState(0);
453
+ const [multiIndex, setMultiIndex] = useState(0);
454
+ const hasSingleSuggestion = typeof aiSuggestion === 'string' && aiSuggestion.trim().length > 0;
455
+ const hasMultiSuggestions = Array.isArray(aiSuggestions) && aiSuggestions.length > 0;
456
+
457
+ const handleRefresh = useCallback(() => {
458
+ if (hasMultiSuggestions) {
459
+ setMultiIndex((prev) => (prev + 1) % TEXTAREA_MULTI_SUGGESTION_GROUPS.length);
460
+ return;
461
+ }
462
+ if (hasSingleSuggestion) {
463
+ setSingleIndex((prev) => (prev + 1) % TEXTAREA_SINGLE_SUGGESTIONS.length);
464
+ }
465
+ }, [hasMultiSuggestions, hasSingleSuggestion]);
466
+
467
+ return (
468
+ <TextArea
469
+ key={`textarea-${variant}-${fillMode}-${countOverflow}-${disabled}-${status}-${resize}-${fillHeight}-${minRowsSafe}`}
470
+ variant={variant}
471
+ minRows={minRowsSafe}
472
+ status={status}
473
+ disabled={disabled}
474
+ placeholder={isCodeVariant ? '请输入 System Prompt、JSON 或代码' : '请输入'}
475
+ defaultValue={defaultValue}
476
+ showCount={showCountDemo}
477
+ {...(showCountDemo ? { maxLength: 140, enforceMaxLength } : {})}
478
+ resize={resize}
479
+ fillHeight={fillHeight}
480
+ aiSuggestion={hasSingleSuggestion ? TEXTAREA_SINGLE_SUGGESTIONS[singleIndex] : undefined}
481
+ aiSuggestions={hasMultiSuggestions ? TEXTAREA_MULTI_SUGGESTION_GROUPS[multiIndex] : undefined}
482
+ onRefreshAiSuggestions={hasSingleSuggestion || hasMultiSuggestions ? handleRefresh : undefined}
483
+ className={fillHeight ? 'flex-1 min-h-0 w-full' : (isCodeVariant ? 'w-[520px]' : '')}
484
+ />
485
+ );
486
+ }
487
+
488
+ function InputPreview(props) {
489
+ const { aiSuggestion, aiSuggestions, onRefreshAiSuggestions: _ignored, ...rest } = props;
490
+ const [singleIndex, setSingleIndex] = useState(0);
491
+ const [multiIndex, setMultiIndex] = useState(0);
492
+ const hasSingleSuggestion = typeof aiSuggestion === 'string' && aiSuggestion.trim().length > 0;
493
+ const hasMultiSuggestions = Array.isArray(aiSuggestions) && aiSuggestions.length > 0;
494
+
495
+ const handleRefresh = useCallback(() => {
496
+ if (hasMultiSuggestions) {
497
+ setMultiIndex((prev) => (prev + 1) % INPUT_MULTI_SUGGESTION_GROUPS.length);
498
+ return;
499
+ }
500
+ if (hasSingleSuggestion) {
501
+ setSingleIndex((prev) => (prev + 1) % INPUT_SINGLE_SUGGESTIONS.length);
502
+ }
503
+ }, [hasMultiSuggestions, hasSingleSuggestion]);
504
+
505
+ return (
506
+ <Input
507
+ {...rest}
508
+ aiSuggestion={hasSingleSuggestion ? INPUT_SINGLE_SUGGESTIONS[singleIndex] : undefined}
509
+ aiSuggestions={hasMultiSuggestions ? INPUT_MULTI_SUGGESTION_GROUPS[multiIndex] : undefined}
510
+ onRefreshAiSuggestions={hasSingleSuggestion || hasMultiSuggestions ? handleRefresh : undefined}
511
+ />
512
+ );
513
+ }
514
+
515
+ /** 预览用:key 保证切换初始值 / 内外置按钮 / 禁用时非受控值重置 */
516
+ function InputNumberPreview({ content = 'empty', buttonMode = 'outer', disabled = false, status = 'default' }) {
517
+ const defaultValue = content === 'filled' ? 12 : undefined;
518
+ const innerButtons = buttonMode === 'inner';
519
+ return (
520
+ <InputNumber
521
+ key={`input-number-${content}-${buttonMode}-${disabled}-${status}`}
522
+ status={status}
523
+ disabled={disabled}
524
+ innerButtons={innerButtons}
525
+ defaultValue={defaultValue}
526
+ min={0}
527
+ max={99}
528
+ step={1}
529
+ placeholder="请输入"
530
+ />
531
+ );
532
+ }
533
+
534
+ const SELECT_DEMO_FEW = [
535
+ { value: 'zj', label: '浙江省' },
536
+ { value: 'js', label: '江苏省' },
537
+ { value: 'sh', label: '上海市' },
538
+ ];
539
+
540
+ const SELECT_DEMO_MANY = [
541
+ ...SELECT_DEMO_FEW,
542
+ { value: 'bj', label: '北京市' },
543
+ { value: 'gd', label: '广东省' },
544
+ { value: 'sc', label: '四川省' },
545
+ { value: 'hub', label: '湖北省' },
546
+ { value: 'hen', label: '河南省' },
547
+ ];
548
+
549
+ const SELECT_TAG_OPTIONS = [
550
+ { value: 'brand', label: '标签', variant: 'grey' },
551
+ { value: 'risk', label: '标签', variant: 'grey' },
552
+ { value: 'service', label: '标签', variant: 'grey' },
553
+ { value: 'quality', label: '标签', variant: 'grey' },
554
+ { value: 'growth', label: '标签', variant: 'grey' },
555
+ { value: 'content', label: '标签', variant: 'grey' },
556
+ ];
557
+
558
+ const SELECT_AI_SINGLE_SUGGESTIONS = ['江苏省', '上海市', '浙江省'];
559
+
560
+ const SELECT_AI_MULTI_SUGGESTION_GROUPS = [
561
+ ['江苏省', '上海市', '浙江省'],
562
+ ['上海市', '浙江省', '江苏省'],
563
+ ['浙江省', '江苏省', '上海市'],
564
+ ];
565
+
566
+ const DATEPICKER_SAMPLE_VALUE = {
567
+ date: '2019-03-15',
568
+ datetime: '2019-03-15 16:00:00',
569
+ daterange: ['2019-03-15', '2019-03-16'],
570
+ datetimerange: ['2019-03-15 09:00:00', '2019-03-16 16:00:00'],
571
+ };
572
+
573
+ const TIMEPICKER_SAMPLE_VALUE = {
574
+ time: '12:15',
575
+ timerange: ['09:30', '18:00'],
576
+ };
577
+
578
+ const UPLOAD_SAMPLE_IMAGE_A = `data:image/svg+xml;utf8,${encodeURIComponent(
579
+ `<svg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'><defs><linearGradient id='g1' x1='0' y1='0' x2='1' y2='1'><stop offset='0%' stop-color='#2B90D9'/><stop offset='100%' stop-color='#0A355A'/></linearGradient></defs><rect width='96' height='96' fill='url(#g1)'/><circle cx='72' cy='24' r='10' fill='rgba(255,255,255,0.28)'/><path d='M0 66C13 52 25 49 38 56C50 62 59 60 69 51C76 45 85 43 96 45V96H0Z' fill='rgba(255,255,255,0.24)'/></svg>`
580
+ )}`;
581
+
582
+ const UPLOAD_SAMPLE_IMAGE_B = `data:image/svg+xml;utf8,${encodeURIComponent(
583
+ `<svg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'><defs><linearGradient id='g2' x1='0' y1='0' x2='1' y2='1'><stop offset='0%' stop-color='#0C4867'/><stop offset='100%' stop-color='#0A243A'/></linearGradient></defs><rect width='96' height='96' fill='url(#g2)'/><ellipse cx='49' cy='50' rx='22' ry='14' fill='rgba(255,255,255,0.22)'/><circle cx='49' cy='50' r='6' fill='rgba(255,255,255,0.85)'/></svg>`
584
+ )}`;
585
+
586
+ const CHATBUBBLE_PRESET_MESSAGES = {
587
+ incoming: {
588
+ single: ['为什么我无法设置点赞列表查看权限'],
589
+ multiple: ['为什么我无法设置点赞列表查看权限', '那怎么看自己的直播回放?'],
590
+ },
591
+ outgoing: {
592
+ single: ['您好,因产品功能更新,“作品点赞信息”设置开关正逐步下线中'],
593
+ multiple: [
594
+ '您好,因产品功能更新,“作品点赞信息”设置开关正逐步下线中',
595
+ '目前不支持设置“作品点赞信息”权限。该功能下线后,您发布的作品的点赞列表仅对您自己可见。',
596
+ ],
597
+ },
598
+ };
599
+
600
+ const CHATBUBBLE_PRESET_TIMESTAMPS = {
601
+ incoming: {
602
+ single: ['2026-02-26 10:24:14'],
603
+ multiple: ['2026-02-26 10:24:14', '2026-02-26 10:26:08'],
604
+ },
605
+ outgoing: {
606
+ single: ['2026-02-26 10:25:03'],
607
+ multiple: ['2026-02-26 10:25:03', '2026-02-26 10:26:44'],
608
+ },
609
+ };
610
+
611
+ /** 预览用:key 保证切换初始选中 / 禁用 / 枚举时 defaultValue 生效 */
612
+ function RadioPreview({ variant, layout, disabled, initialKey }) {
613
+ const defaultValue = initialKey === 'a' ? 'a' : initialKey === 'b' ? 'b' : undefined;
614
+ return (
615
+ <RadioGroup
616
+ key={`rp-${variant}-${layout}-${disabled}-${initialKey}`}
617
+ variant={variant}
618
+ layout={layout}
619
+ disabled={disabled}
620
+ defaultValue={defaultValue}
621
+ >
622
+ <Radio value="a">选项 A</Radio>
623
+ <Radio value="b">选项 B</Radio>
624
+ <Radio value="c" disabled>选项 C(单项禁用)</Radio>
625
+ </RadioGroup>
626
+ );
627
+ }
628
+
629
+ /** 预览用:组模式用 defaultValue 数组;半选模式为单独 Checkbox */
630
+ function CheckboxPreview({ variant, size, layout, disabled, initialKey, previewMode }) {
631
+ if (previewMode === 'indeterminate') {
632
+ return (
633
+ <Checkbox
634
+ key={`cb-ind-${variant}-${size}-${disabled}`}
635
+ variant={variant}
636
+ size={size}
637
+ disabled={disabled}
638
+ indeterminate
639
+ >
640
+ 半选状态(树形父级)
641
+ </Checkbox>
642
+ );
643
+ }
644
+ let defaultValue = [];
645
+ if (initialKey === 'a') defaultValue = ['a'];
646
+ else if (initialKey === 'ab') defaultValue = ['a', 'b'];
647
+ return (
648
+ <CheckboxGroup
649
+ key={`cbg-${variant}-${size}-${layout}-${disabled}-${initialKey}`}
650
+ variant={variant}
651
+ size={size}
652
+ layout={layout}
653
+ disabled={disabled}
654
+ defaultValue={defaultValue}
655
+ >
656
+ <Checkbox value="a">选项 A</Checkbox>
657
+ <Checkbox value="b">选项 B</Checkbox>
658
+ <Checkbox value="c" disabled>选项 C(单项禁用)</Checkbox>
659
+ </CheckboxGroup>
660
+ );
661
+ }
662
+
663
+ /** 预览用:key 保证切换「初始值 / 选项量 / 禁用」时非受控 defaultValue 生效 */
664
+ function SelectPreview({ status, disabled, initial, optionMode, selectType, aiSuggestion, aiSuggestions }) {
665
+ const isTagSelect = selectType === 'tag';
666
+ const options = isTagSelect
667
+ ? SELECT_TAG_OPTIONS
668
+ : optionMode === 'many'
669
+ ? SELECT_DEMO_MANY
670
+ : SELECT_DEMO_FEW;
671
+ const [singleIndex, setSingleIndex] = useState(0);
672
+ const [multiIndex, setMultiIndex] = useState(0);
673
+ const hasSingleSuggestion = typeof aiSuggestion === 'string' && aiSuggestion.trim().length > 0;
674
+ const hasMultiSuggestions = Array.isArray(aiSuggestions) && aiSuggestions.length > 0;
675
+ const defaultValue = initial === 'selected'
676
+ ? (isTagSelect ? ['brand', 'risk'] : 'js')
677
+ : undefined;
678
+
679
+ const handleRefresh = useCallback(() => {
680
+ if (hasMultiSuggestions) {
681
+ setMultiIndex((prev) => (prev + 1) % SELECT_AI_MULTI_SUGGESTION_GROUPS.length);
682
+ return;
683
+ }
684
+ if (hasSingleSuggestion) {
685
+ setSingleIndex((prev) => (prev + 1) % SELECT_AI_SINGLE_SUGGESTIONS.length);
686
+ }
687
+ }, [hasMultiSuggestions, hasSingleSuggestion]);
688
+
689
+ return (
690
+ <div className="w-[300px] max-w-full">
691
+ <Select
692
+ key={`selp-${selectType}-${initial}-${optionMode}-${disabled}-${status}`}
693
+ mode={isTagSelect ? 'tag' : 'default'}
694
+ options={options}
695
+ defaultValue={defaultValue}
696
+ placeholder="请选择"
697
+ status={status}
698
+ disabled={disabled}
699
+ allowClear
700
+ aiSuggestion={!isTagSelect && hasSingleSuggestion ? SELECT_AI_SINGLE_SUGGESTIONS[singleIndex] : undefined}
701
+ aiSuggestions={!isTagSelect && hasMultiSuggestions ? SELECT_AI_MULTI_SUGGESTION_GROUPS[multiIndex] : undefined}
702
+ onRefreshAiSuggestions={!isTagSelect && (hasSingleSuggestion || hasMultiSuggestions) ? handleRefresh : undefined}
703
+ />
704
+ </div>
705
+ );
706
+ }
707
+
708
+ const FORM_FIELD_LABELS = {
709
+ input: 'Input',
710
+ 'input-number': 'InputNumber',
711
+ textarea: 'TextArea',
712
+ select: 'Select',
713
+ checkbox: 'Checkbox',
714
+ radio: 'RadioGroup',
715
+ switch: 'Switch',
716
+ 'date-picker': 'DatePicker',
717
+ 'time-picker': 'TimePicker',
718
+ slider: 'Slider',
719
+ upload: 'Upload',
720
+ 'input-group': 'InputGroup',
721
+ 'tag-input': 'TagInput',
722
+ };
723
+
724
+ const TAGINPUT_SAMPLE_TAGS = [
725
+ '标签',
726
+ '标签',
727
+ '标签',
728
+ '标签',
729
+ '标签',
730
+ '标签',
731
+ ];
732
+
733
+ const FORM_FIELD_ATOM_ENUM_PROPS = {
734
+ radio: [
735
+ { name: 'controlVariant', type: 'enum', options: ['brand', 'black'], default: 'brand' },
736
+ { name: 'controlLayout', type: 'enum', options: ['horizontal', 'vertical'], default: 'horizontal' },
737
+ ],
738
+ checkbox: [
739
+ { name: 'controlVariant', type: 'enum', options: ['brand', 'black'], default: 'brand' },
740
+ { name: 'controlLayout', type: 'enum', options: ['horizontal', 'vertical'], default: 'horizontal' },
741
+ ],
742
+ switch: [
743
+ { name: 'controlVariant', type: 'enum', options: ['brand', 'black'], default: 'black' },
744
+ ],
745
+ 'date-picker': [
746
+ { name: 'pickerType', type: 'enum', options: ['date', 'datetime', 'daterange', 'datetimerange'], default: 'date' },
747
+ ],
748
+ 'time-picker': [
749
+ { name: 'timePickerType', type: 'enum', options: ['time', 'timerange'], default: 'time' },
750
+ ],
751
+ 'input-number': [
752
+ { name: 'inputNumberButtons', type: 'enum', options: ['outer', 'inner'], default: 'outer' },
753
+ ],
754
+ /** 与 TextArea 组件详情「配置」侧栏顺序、选项 id、默认项一致(内容→超长计数→状态→默认高度→状态→高度适配) */
755
+ textarea: [
756
+ { name: 'fillMode', type: 'enum', options: ['empty', 'filled'], default: 'empty' },
757
+ { name: 'countOverflow', type: 'enum', options: ['off', 'on'], default: 'off' },
758
+ { name: 'state', type: 'enum', options: ['default', 'disabled'], default: 'default' },
759
+ { name: 'minRows', type: 'enum', options: [1, 2, 3, 4, 5], default: 3 },
760
+ { name: 'status', type: 'enum', options: ['default', 'error'], default: 'default' },
761
+ { name: 'resize', type: 'enum', options: ['vertical', 'none'], default: 'vertical' },
762
+ ],
763
+ };
764
+
765
+ const FORM_TEXTAREA_SAMPLE =
766
+ '我们非常高兴地向您介绍我们的最新产品 HiUI。HiUI是一个创新的在线客服解决方案,旨在帮助企业更好地理解并服务他们的客户。';
767
+
768
+ function getFormFieldAtomEnumProps(fieldType) {
769
+ return FORM_FIELD_ATOM_ENUM_PROPS[fieldType] || [];
770
+ }
771
+
772
+ function buildFormFieldAtomProps(fieldType, enums, controlValues = {}) {
773
+ if (fieldType === 'radio' || fieldType === 'checkbox') {
774
+ return {
775
+ variant: enums.controlVariant || 'brand',
776
+ layout: enums.controlLayout || 'horizontal',
777
+ };
778
+ }
779
+ if (fieldType === 'switch') {
780
+ return {
781
+ variant: enums.controlVariant || 'black',
782
+ };
783
+ }
784
+ if (fieldType === 'date-picker') {
785
+ return {
786
+ pickerType: enums.pickerType || 'date',
787
+ };
788
+ }
789
+ if (fieldType === 'time-picker') {
790
+ return {
791
+ pickerType: ['time', 'timerange'].includes(enums.timePickerType) ? enums.timePickerType : 'time',
792
+ };
793
+ }
794
+ if (fieldType === 'input-number') {
795
+ return {
796
+ innerButtons: enums.inputNumberButtons === 'inner',
797
+ };
798
+ }
799
+ if (fieldType === 'textarea') {
800
+ const fillMode = enums.fillMode === 'filled' ? 'filled' : 'empty';
801
+ const countOn = enums.countOverflow === 'on';
802
+ const disabled = enums.state === 'disabled';
803
+ const row = Number(enums.minRows);
804
+ const minRows = [1, 2, 3, 4, 5].includes(row) ? row : 3;
805
+ const status = enums.status === 'error' ? 'error' : 'default';
806
+ const resize = enums.resize === 'none' ? 'none' : 'vertical';
807
+
808
+ const props = {
809
+ minRows,
810
+ resize,
811
+ disabled,
812
+ };
813
+ if (status === 'error') props.status = 'error';
814
+ if (fillMode === 'filled') props.defaultValue = FORM_TEXTAREA_SAMPLE;
815
+ if (countOn) {
816
+ props.showCount = true;
817
+ props.maxLength = 140;
818
+ props.enforceMaxLength = false;
819
+ }
820
+ return props;
821
+ }
822
+ if (fieldType === 'slider') {
823
+ return buildSliderPreviewProps(controlValues, 'slider');
824
+ }
825
+ return {};
826
+ }
827
+
828
+ function TagInputPreview({ content, widthMode, disabled, status, tagVariant, tagSize, showAiSuggestions = true }) {
829
+ const defaultValue = content === 'filled' ? TAGINPUT_SAMPLE_TAGS : [];
830
+ const widthClass = widthMode === 'narrow' ? '!w-[220px]' : widthMode === 'wide' ? '!w-[420px]' : '';
831
+ const [suggestionIndex, setSuggestionIndex] = useState(0);
832
+ const handleRefresh = useCallback(() => {
833
+ setSuggestionIndex((prev) => (prev + 1) % TAGINPUT_AI_SUGGESTION_GROUPS.length);
834
+ }, []);
835
+ return (
836
+ <TagInput
837
+ key={`tag-input-${content}-${widthMode}-${disabled}-${status}-${tagVariant}-${tagSize}`}
838
+ status={status}
839
+ disabled={disabled}
840
+ defaultValue={defaultValue}
841
+ placeholder="请输入"
842
+ tagVariant={tagVariant}
843
+ tagSize={tagSize}
844
+ aiSuggestions={showAiSuggestions ? TAGINPUT_AI_SUGGESTION_GROUPS[suggestionIndex] : undefined}
845
+ onRefreshAiSuggestions={showAiSuggestions ? handleRefresh : undefined}
846
+ className={widthClass}
847
+ />
848
+ );
849
+ }
850
+
851
+ function buildFormPreviewItems({ previewMode, fieldType, labelPosition, helpMode, validationState, fieldAtomProps }) {
852
+ if (previewMode === 'all') {
853
+ return FORM_SAMPLE_ITEMS.map((item) => ({
854
+ ...item,
855
+ id: `${item.id}-all`,
856
+ labelPosition: 'top',
857
+ }));
858
+ }
859
+
860
+ const type = FORM_FIELD_TYPES.includes(fieldType) ? fieldType : 'input';
861
+ return [{
862
+ id: `form-${type}`,
863
+ label: '字段标题',
864
+ type,
865
+ required: true,
866
+ labelPosition,
867
+ middleHelpText: helpMode === 'middle' ? '提示文案' : undefined,
868
+ helpText: helpMode === 'bottom' ? '提示文案' : undefined,
869
+ error: validationState === 'error',
870
+ defaultValue: type === 'radio'
871
+ ? 'a'
872
+ : type === 'checkbox'
873
+ ? ['a']
874
+ : type === 'tag-input'
875
+ ? TAGINPUT_SAMPLE_TAGS
876
+ : undefined,
877
+ ...fieldAtomProps,
878
+ }];
879
+ }
880
+
881
+ function FormPreview({
882
+ previewMode,
883
+ fieldType,
884
+ labelPosition,
885
+ helpMode,
886
+ validationState,
887
+ size,
888
+ labelOptional,
889
+ labelRequired,
890
+ labelAi,
891
+ labelHelp,
892
+ fieldAtomProps,
893
+ }) {
894
+ const baseItems = buildFormPreviewItems({
895
+ previewMode,
896
+ fieldType,
897
+ labelPosition,
898
+ helpMode,
899
+ validationState,
900
+ fieldAtomProps,
901
+ });
902
+ const sliderDefaultValue = previewMode === 'single' && fieldType === 'slider'
903
+ ? fieldAtomProps.defaultValue
904
+ : undefined;
905
+ const [sliderValue, setSliderValue] = useState(sliderDefaultValue);
906
+
907
+ useEffect(() => {
908
+ setSliderValue(Array.isArray(sliderDefaultValue) ? [...sliderDefaultValue] : sliderDefaultValue);
909
+ }, [sliderDefaultValue, fieldType, helpMode, labelPosition, previewMode, size, validationState]);
910
+
911
+ const items = previewMode === 'single' && fieldType === 'slider'
912
+ ? baseItems.map((item) => ({
913
+ ...item,
914
+ value: sliderValue,
915
+ defaultValue: undefined,
916
+ onChange: setSliderValue,
917
+ onAfterChange: setSliderValue,
918
+ }))
919
+ : baseItems;
920
+
921
+ return (
922
+ <Form
923
+ key={`form-${previewMode}-${fieldType}-${labelPosition}-${helpMode}-${validationState}-${size}-${labelOptional}-${labelRequired}-${labelAi}-${labelHelp}-${JSON.stringify(fieldAtomProps || {})}`}
924
+ layout="vertical"
925
+ columns={1}
926
+ labelPosition={labelPosition}
927
+ size={size}
928
+ labelOptional={labelOptional}
929
+ labelRequired={labelRequired}
930
+ labelAi={labelAi}
931
+ labelHelp={labelHelp}
932
+ items={items}
933
+ className={previewMode === 'all' ? '!gap-10' : ''}
934
+ />
935
+ );
936
+ }
937
+
938
+ export const PREVIEW_REGISTRY = {
939
+ avatar: {
940
+ component: AvatarGridPreview,
941
+ tokenMap: AVATAR_TOKEN_MAP,
942
+ jsxSource: avatarJsxRaw,
943
+
944
+ controls: [
945
+ {
946
+ id: 'type',
947
+ label: '场景',
948
+ type: 'seg',
949
+ options: [
950
+ { id: 'all', label: '全部' },
951
+ { id: 'image', label: '图片' },
952
+ { id: 'eng', label: '英文' },
953
+ { id: 'ch', label: '中文' },
954
+ { id: 'fallback', label: '兜底' },
955
+ { id: 'robot', label: '机器人' },
956
+ { id: 'ai', label: 'AI头像' },
957
+ ],
958
+ default: 'all',
959
+ },
960
+ {
961
+ id: 'previewMode',
962
+ label: '预览',
963
+ type: 'seg',
964
+ options: [
965
+ { id: 'matrix', label: '矩阵' },
966
+ { id: 'single', label: '单个' },
967
+ ],
968
+ default: 'matrix',
969
+ },
970
+ ],
971
+
972
+ mapProps: (cv, enums) => ({
973
+ previewMode: cv.previewMode || 'matrix',
974
+ shape: enums.shape || 'round',
975
+ size: enums.size || 'default',
976
+ type: cv.type || enums.type || 'image',
977
+ }),
978
+
979
+ getVisibleEnumProps: ({ enumProps, controlValues }) => (
980
+ (controlValues.previewMode || 'matrix') === 'matrix'
981
+ ? enumProps.filter((prop) => prop.name === 'shape')
982
+ : enumProps
983
+ ),
984
+
985
+ generateUsage: (enums, cv) => {
986
+ const lines = [`import Avatar from './components/Avatar';`];
987
+ const shape = enums.shape || 'round';
988
+ const isMatrix = (cv.previewMode || 'matrix') === 'matrix';
989
+ const size = isMatrix ? 'default' : (enums.size || 'default');
990
+ const selectedType = cv.type || enums.type || 'all';
991
+ const type = selectedType === 'all' ? 'image' : selectedType;
992
+ const props = [];
993
+
994
+ if (shape !== 'round') props.push(`shape="${shape}"`);
995
+ if (size !== 'default') props.push(`size="${size}"`);
996
+ if (type !== 'image') props.push(`type="${type}"`);
997
+
998
+ if (type === 'image') {
999
+ props.push('alt="用户头像"');
1000
+ }
1001
+
1002
+ if (type === 'eng') {
1003
+ props.push(`label="${size === 'xxs' || size === 'mini' || size === 'xs' ? 'T' : 'TF'}"`);
1004
+ }
1005
+
1006
+ if (type === 'ch') {
1007
+ props.push(`label="${size === 'default' || size === 'm' ? '体服' : '体'}"`);
1008
+ }
1009
+
1010
+ lines.push('');
1011
+ lines.push('<Avatar');
1012
+ props.forEach((p) => lines.push(` ${p}`));
1013
+ lines.push('/>');
1014
+ return lines.join('\n');
1015
+ },
1016
+ },
1017
+
1018
+ 'avatar-group': {
1019
+ component: AvatarGroup,
1020
+ tokenMap: AVATAR_GROUP_TOKEN_MAP,
1021
+ jsxSource: avatarGroupJsxRaw,
1022
+
1023
+ controls: [],
1024
+
1025
+ mapProps: (cv, enums) => ({
1026
+ size: enums.size || 's',
1027
+ }),
1028
+
1029
+ generateUsage: (enums) => {
1030
+ const lines = [`import AvatarGroup from './components/AvatarGroup';`];
1031
+ const size = enums.size || 's';
1032
+ const props = [];
1033
+
1034
+ if (size !== 's') props.push(`size="${size}"`);
1035
+
1036
+ lines.push('');
1037
+ lines.push('<AvatarGroup');
1038
+ props.forEach((p) => lines.push(` ${p}`));
1039
+ lines.push('/>');
1040
+ return lines.join('\n');
1041
+ },
1042
+ },
1043
+
1044
+ icon: {
1045
+ component: Icon,
1046
+ gridComponent: IconGridPreview,
1047
+ tokenMap: ICON_TOKEN_MAP,
1048
+ jsxSource: iconJsxRaw,
1049
+ previewType: 'icon-grid',
1050
+
1051
+ controls: [],
1052
+
1053
+ mapProps: (cv, enums) => ({
1054
+ name: 'check-stroked',
1055
+ }),
1056
+
1057
+ generateUsage: (enums) => {
1058
+ const lines = [`import Icon from './components/Icon';`];
1059
+ lines.push(`import { ICON_CATEGORIES } from './components/icons/icon-data';`);
1060
+ lines.push('');
1061
+ lines.push(`<Icon name="图标名称" size="${enums.size || 'sm'}" />`);
1062
+ return lines.join('\n');
1063
+ },
1064
+ },
1065
+
1066
+ card: {
1067
+ component: CardPreview,
1068
+ tokenMap: CARD_TOKEN_MAP,
1069
+ jsxSource: cardJsxRaw,
1070
+ configHiddenEnums: ['type', 'infoIconTone', 'infoMetaBadgeVariant', 'dataIconTone'],
1071
+ enumTitles: {
1072
+ color: '颜色',
1073
+ infoIconStyle: '图标样式',
1074
+ infoLayout: '图标位置',
1075
+ productInfoLayout: '图标位置',
1076
+ dataIconVisible: '是否显示图标',
1077
+ dataIconStyle: '图标样式',
1078
+ },
1079
+
1080
+ controls: [
1081
+ {
1082
+ id: 'category',
1083
+ label: '分类',
1084
+ type: 'seg',
1085
+ options: [
1086
+ { id: 'default', label: '数据卡片' },
1087
+ { id: 'product', label: '商品卡片' },
1088
+ { id: 'info', label: '信息卡片' },
1089
+ ],
1090
+ default: 'default',
1091
+ },
1092
+ ],
1093
+
1094
+ mapProps: (cv, enums) => {
1095
+ const type = cv.category === 'product' || cv.category === 'info' ? cv.category : 'data';
1096
+ return {
1097
+ type,
1098
+ color: enums.color || 'white',
1099
+ infoIconTone: enums.infoIconTone || 'pink',
1100
+ infoIconStyle: enums.infoIconStyle || 'inverse',
1101
+ infoLayout: type === 'info'
1102
+ ? (enums.infoLayout || 'icon-right')
1103
+ : (enums.productInfoLayout || 'default'),
1104
+ infoMetaBadgeVariant: undefined,
1105
+ dataIconVisible: enums.dataIconVisible || 'hidden',
1106
+ dataIconStyle: enums.dataIconStyle || 'inverse',
1107
+ };
1108
+ },
1109
+
1110
+ getVisibleEnumProps: ({ enumProps, controlValues = {} }) => {
1111
+ const category = controlValues.category || 'default';
1112
+ const props = enumProps || [];
1113
+ if (category === 'info') {
1114
+ return props.filter((prop) => ['color', 'infoIconStyle', 'infoLayout'].includes(prop.name));
1115
+ }
1116
+ if (category === 'product') {
1117
+ const colorProp = props.find((prop) => prop.name === 'color');
1118
+ const layoutProp = props.find((prop) => prop.name === 'infoLayout');
1119
+ return [
1120
+ colorProp,
1121
+ layoutProp ? { ...layoutProp, name: 'productInfoLayout', default: 'default' } : null,
1122
+ ].filter(Boolean);
1123
+ }
1124
+ return props.filter((prop) => ['color', 'dataIconVisible', 'dataIconStyle'].includes(prop.name));
1125
+ },
1126
+
1127
+ generateUsage: (enums, cv = {}) => {
1128
+ const lines = [`import Card from './components/Card';`];
1129
+ const color = enums.color || 'white';
1130
+ const type = cv.category === 'product' || cv.category === 'info' ? cv.category : 'data';
1131
+ lines.push('');
1132
+
1133
+ if (type === 'data') {
1134
+ lines.push('const stats = [');
1135
+ lines.push(" { iconName: 'users-01-stroked', value: '1,289' },");
1136
+ lines.push(" { iconName: 'message-chat-square-stroked', value: '1,289' },");
1137
+ lines.push(" { iconName: 'hearts-stroked', value: '276' },");
1138
+ lines.push('];');
1139
+ lines.push('');
1140
+ }
1141
+
1142
+ if (color === 'grey') {
1143
+ lines.push('/* 白色背景容器里,推荐使用灰底 Card 做层次分隔 */');
1144
+ lines.push('<div className="bg-white p-6">');
1145
+ } else {
1146
+ lines.push('/* 灰色背景容器里,推荐使用白底 Card 做内容承载 */');
1147
+ lines.push('<div className="bg-blueGrey-200 p-6">');
1148
+ }
1149
+ lines.push(' <Card');
1150
+ if (type === 'product') lines.push(' type="product"');
1151
+ if (type === 'info') lines.push(' type="info"');
1152
+ if (color !== 'white') lines.push(` color="${color}"`);
1153
+ if (type === 'product') {
1154
+ lines.push(' title="海底捞门店通用双人套餐"');
1155
+ lines.push(' description="数量 1 · ¥128.00 · 月售 2,361"');
1156
+ lines.push(` infoLayout="${enums.productInfoLayout || 'default'}"`);
1157
+ lines.push(' productStatus="已使用"');
1158
+ } else if (type === 'info') {
1159
+ lines.push(' title="抖音 AI 创作助手"');
1160
+ lines.push(' description="抖音官方 AI 创作能力,支持短视频脚本、标题推荐、封面文案和热点灵感生成,帮助创作者提升内容生产效率。"');
1161
+ lines.push(' infoIconName="magic-wand-01-stroked"');
1162
+ lines.push(` infoIconStyle="${enums.infoIconStyle || 'inverse'}"`);
1163
+ lines.push(` infoLayout="${enums.infoLayout || 'icon-right'}"`);
1164
+ lines.push(' infoMetaBadge="官方能力"');
1165
+ lines.push(' infoMetaLabel="段然"');
1166
+ lines.push(' infoStats={[');
1167
+ lines.push(' { iconName: "users-01-stroked", value: "128.6K" },');
1168
+ lines.push(' { iconName: "star-01-stroked", value: "4.9" },');
1169
+ lines.push(' { iconName: "play-circle-stroked", value: "立即体验" },');
1170
+ lines.push(' ]}');
1171
+ } else {
1172
+ lines.push(' title="更新用户资料"');
1173
+ lines.push(' description="更新用户个人资料信息,支持修改昵称、头像、简介等。"');
1174
+ lines.push(' stats={stats}');
1175
+ lines.push(' tags={["社区", "收藏星标"]}');
1176
+ if ((enums.dataIconVisible || 'hidden') === 'visible') {
1177
+ lines.push(' dataIconVisible="visible"');
1178
+ lines.push(' dataIconName="edit-04-stroked"');
1179
+ lines.push(` dataIconStyle="${enums.dataIconStyle || 'inverse'}"`);
1180
+ }
1181
+ lines.push(' actionAriaLabel="查看详情"');
1182
+ lines.push(' onAction={() => {}}');
1183
+ }
1184
+ lines.push(' />');
1185
+ lines.push('</div>');
1186
+ return lines.join('\n');
1187
+ },
1188
+ },
1189
+
1190
+ 'conversation-list': {
1191
+ component: ConversationListPreview,
1192
+ tokenMap: CONVERSATION_LIST_TOKEN_MAP,
1193
+ jsxSource: conversationListJsxRaw,
1194
+ hideBaseVariantControl: true,
1195
+ controls: [
1196
+ {
1197
+ id: 'previewVariant',
1198
+ label: '变体',
1199
+ type: 'seg',
1200
+ options: [
1201
+ { id: 'default', label: '默认列表' },
1202
+ { id: 'card', label: '卡片列表' },
1203
+ ],
1204
+ default: 'default',
1205
+ },
1206
+ {
1207
+ id: 'defaultPreviewMode',
1208
+ label: '组件状态',
1209
+ type: 'seg',
1210
+ options: [
1211
+ { id: 'expanded', label: '平铺' },
1212
+ { id: 'collapsed', label: '收起' },
1213
+ ],
1214
+ default: 'expanded',
1215
+ hidden: ({ controlValues }) => (controlValues.previewVariant || 'default') !== 'default',
1216
+ },
1217
+ {
1218
+ id: 'cardPreviewMode',
1219
+ label: '组件状态',
1220
+ type: 'seg',
1221
+ options: [
1222
+ { id: 'card-all', label: '全部' },
1223
+ { id: 'card-replied', label: '已回复' },
1224
+ { id: 'card-generating', label: '生成中' },
1225
+ { id: 'card-editable', label: '可发送' },
1226
+ ],
1227
+ default: 'card-all',
1228
+ hidden: ({ controlValues }) => (controlValues.previewVariant || 'default') !== 'card',
1229
+ },
1230
+ ],
1231
+
1232
+ mapProps: (cv) => {
1233
+ const variant = cv.previewVariant || 'default';
1234
+ const previewMode = variant === 'card'
1235
+ ? (cv.cardPreviewMode || 'card-all')
1236
+ : (cv.defaultPreviewMode || 'expanded');
1237
+
1238
+ return {
1239
+ variant,
1240
+ previewMode,
1241
+ };
1242
+ },
1243
+
1244
+ getVisibleEnumProps: ({ enumProps }) => enumProps.filter(
1245
+ (prop) => prop.name !== 'variant' && prop.name !== 'defaultVariant'
1246
+ ),
1247
+
1248
+ generateUsage: (_enums, cv = {}) => {
1249
+ const variant = cv.previewVariant || 'default';
1250
+ const previewMode = variant === 'card'
1251
+ ? (cv.cardPreviewMode || 'card-all')
1252
+ : (cv.defaultPreviewMode || 'expanded');
1253
+
1254
+ if (previewMode === 'card-replied') {
1255
+ return [
1256
+ `import ConversationList from './components/ConversationList';`,
1257
+ '',
1258
+ '<ConversationList',
1259
+ ' variant="card"',
1260
+ ' sections={[{',
1261
+ ' id: "pending",',
1262
+ ' title: "待干预",',
1263
+ ' count: 1,',
1264
+ ' items: [{',
1265
+ ' id: "pending-1",',
1266
+ ' title: "抖音账号解封",',
1267
+ ' userName: "小兔叽掉毛毛🐰",',
1268
+ ' orderId: "2918575148379876",',
1269
+ ' time: "3分钟前",',
1270
+ ' avatarSrc: avatarUrl,',
1271
+ ' tags: [{ label: "待干预", variant: "grey", iconName: "alarm-clock-stroked" }],',
1272
+ ' messages: [',
1273
+ ' { id: "m1", role: "user", text: "但是我不知道问题出在哪里" },',
1274
+ ' { id: "m2", role: "user", text: "你帮我看下这个有啥问题呢" },',
1275
+ ' { id: "m3", role: "agent", text: "《抖音网络社区自律公约》:抖音app-我-右上角≡-设置-抖音规则中心,这是咱们抖音官方的一个规则呢" },',
1276
+ ' { id: "m4", role: "user", text: "好的明白了,谢谢,没其他问题了" },',
1277
+ ' ],',
1278
+ ' status: "replied",',
1279
+ ' draftText: "",',
1280
+ ' }],',
1281
+ ' }]}',
1282
+ '/>',
1283
+ ].join('\n');
1284
+ }
1285
+
1286
+ if (previewMode === 'card-generating') {
1287
+ return [
1288
+ `import ConversationList from './components/ConversationList';`,
1289
+ '',
1290
+ '<ConversationList',
1291
+ ' variant="card"',
1292
+ ' sections={[{',
1293
+ ' id: "pending",',
1294
+ ' title: "待干预",',
1295
+ ' count: 1,',
1296
+ ' items: [{',
1297
+ ' id: "pending-1",',
1298
+ ' title: "酒旅·仲裁退款",',
1299
+ ' userName: "小兔叽掉毛毛🐰",',
1300
+ ' orderId: "2918575148379876",',
1301
+ ' time: "13:32",',
1302
+ ' avatarSrc: avatarUrl,',
1303
+ ' tags: [{ label: "异常监控提醒", variant: "grey" }, { label: "待干预", variant: "red" }],',
1304
+ ' messages: [',
1305
+ ' { id: "m1", role: "user", text: "但是我不知道问题出在哪里" },',
1306
+ ' { id: "m2", role: "user", text: "你帮我看下这个有啥问题呢" },',
1307
+ ' { id: "m3", role: "agent", text: "《抖音网络社区自律公约》:抖音app-我-右上角≡-设置-抖音规则中心,这是咱们抖音官方的一个规则呢" },',
1308
+ ' { id: "m4", role: "user", text: "好的明白了,谢谢,没其他问题了" },',
1309
+ ' ],',
1310
+ ' status: "generating",',
1311
+ ' draftText: "",',
1312
+ ' }],',
1313
+ ' }]}',
1314
+ '/>',
1315
+ ].join('\n');
1316
+ }
1317
+
1318
+ if (previewMode === 'card-editable') {
1319
+ return [
1320
+ `import ConversationList from './components/ConversationList';`,
1321
+ '',
1322
+ '<ConversationList',
1323
+ ' variant="card"',
1324
+ ' sections={[{',
1325
+ ' id: "managed",',
1326
+ ' title: "托管中",',
1327
+ ' count: 1,',
1328
+ ' items: [{',
1329
+ ' id: "managed-1",',
1330
+ ' title: "物流状态异常跟进",',
1331
+ ' userName: "软软想吃糖葫芦",',
1332
+ ' orderId: "2918575148472390",',
1333
+ ' time: "11:22",',
1334
+ ' avatarSrc: avatarUrl,',
1335
+ ' tags: [{ label: "24时30分", variant: "grey", iconName: "alarm-clock-stroked" }, { label: "托管中", variant: "green" }],',
1336
+ ' messages: [',
1337
+ ' { id: "m1", role: "user", text: "你帮我看下这个有啥问题呢" },',
1338
+ ' { id: "m2", role: "agent", text: "《抖音网络社区自律公约》:抖音app-我-右上角≡-设置-抖音规则中心,这是咱们抖音官方的一个规则呢" },',
1339
+ ' { id: "m3", role: "user", text: "好的明白了,谢谢,没其他问题了" },',
1340
+ ' ],',
1341
+ ' status: "editable",',
1342
+ ' draftText: "祝您生活愉快~如果您觉得我的服务态度还可以的话,辛苦您点击左下角结束服务为本次服务点亮小星星哦~",',
1343
+ ' }],',
1344
+ ' }]}',
1345
+ '/>',
1346
+ ].join('\n');
1347
+ }
1348
+
1349
+ return [
1350
+ `import ConversationList from './components/ConversationList';`,
1351
+ '',
1352
+ '<ConversationList',
1353
+ ' activeTab="all"',
1354
+ ' activeItemId="managed-1"',
1355
+ ' onTabChange={(tabId) => setCurrentTab(tabId)}',
1356
+ ' onItemClick={(item) => openConversation(item.id)}',
1357
+ '/>',
1358
+ ].join('\n');
1359
+ },
1360
+ },
1361
+
1362
+ 'info-display-panel': {
1363
+ component: InfoDisplayPanelPreview,
1364
+ tokenMap: INFO_DISPLAY_PANEL_TOKEN_MAP,
1365
+ jsxSource: infoDisplayPanelJsxRaw,
1366
+ hideBaseVariantControl: true,
1367
+
1368
+ getPreviewAreaStyle: () => ({
1369
+ flexDirection: 'column',
1370
+ alignItems: 'stretch',
1371
+ justifyContent: 'flex-start',
1372
+ overflow: 'auto',
1373
+ padding: 24,
1374
+ }),
1375
+
1376
+ getPreviewScalerStyle: () => ({
1377
+ width: '100%',
1378
+ minWidth: 0,
1379
+ boxSizing: 'border-box',
1380
+ }),
1381
+
1382
+ getPreviewItemStyle: () => ({
1383
+ width: '100%',
1384
+ minWidth: 0,
1385
+ height: 520,
1386
+ display: 'flex',
1387
+ alignSelf: 'stretch',
1388
+ }),
1389
+
1390
+ controls: [
1391
+ {
1392
+ id: 'contentCount',
1393
+ label: '内容数量',
1394
+ type: 'seg',
1395
+ options: [
1396
+ { id: 'single', label: '单Tab' },
1397
+ { id: 'multi', label: '多Tab' },
1398
+ ],
1399
+ default: 'multi',
1400
+ },
1401
+ ],
1402
+
1403
+ mapProps: (cv) => ({
1404
+ __previewKey: 'info-display-panel',
1405
+ contentCount: cv.contentCount || 'multi',
1406
+ }),
1407
+
1408
+ generateUsage: (_enums, cv) => {
1409
+ const isSingleTab = (cv.contentCount || 'multi') === 'single';
1410
+ const lines = [`import InfoDisplayPanel from './components/InfoDisplayPanel';`, ''];
1411
+ lines.push('<InfoDisplayPanel');
1412
+ if (isSingleTab) {
1413
+ lines.push(' panels={[');
1414
+ lines.push(' { id: "profile", tabs: [{ id: "profile", label: "用户信息" }] },');
1415
+ lines.push(' ]}');
1416
+ } else {
1417
+ lines.push(' panels={[');
1418
+ lines.push(' { id: "assistant", tabs: [{ id: "assistant", label: "托管助手" }] },');
1419
+ lines.push(' { id: "tickets", tabs: [{ id: "history", label: "历史工单" }, { id: "logs", label: "工单日志" }, { id: "tools", label: "信息工具" }] },');
1420
+ lines.push(' { id: "info", tabs: [{ id: "video", label: "视频信息" }, { id: "user", label: "用户命中实验" }, { id: "records", label: "沟通记录" }] },');
1421
+ lines.push(' ]}');
1422
+ }
1423
+ lines.push(' renderPanelContent={({ panel }) => (');
1424
+ lines.push(' <div className="rounded-md border border-border-default bg-blueGrey-50 p-3">');
1425
+ lines.push(' {/* 每栏内容区固定 p-4 + gap-4;这里放具体业务详情、工单卡片或表单块 */}');
1426
+ lines.push(' {panel.id}');
1427
+ lines.push(' </div>');
1428
+ lines.push(' )}');
1429
+ lines.push('/>');
1430
+ return lines.join('\n');
1431
+ },
1432
+ },
1433
+
1434
+ 'nav-bar': {
1435
+ component: NavBarPreview,
1436
+ tokenMap: NAVBAR_TOKEN_MAP,
1437
+ jsxSource: navBarJsxRaw,
1438
+ controls: [
1439
+ {
1440
+ id: 'platform',
1441
+ label: '平台',
1442
+ type: 'seg',
1443
+ options: [
1444
+ { id: 'ola', label: 'OLA' },
1445
+ { id: 'bytehi', label: 'ByteHi' },
1446
+ ],
1447
+ default: 'ola',
1448
+ },
1449
+ {
1450
+ id: 'utilityActions',
1451
+ label: '底部操作',
1452
+ type: 'seg',
1453
+ options: [
1454
+ { id: 'hidden', label: '隐藏' },
1455
+ { id: 'visible', label: '显示' },
1456
+ ],
1457
+ default: 'hidden',
1458
+ },
1459
+ ],
1460
+
1461
+ getVisibleEnumProps: ({ enumProps }) => enumProps.filter((prop) => prop.name !== 'avatarType' && prop.name !== 'platform' && prop.name !== 'promptText'),
1462
+
1463
+ mapProps: (cv, enums) => ({
1464
+ platform: cv.platform || 'ola',
1465
+ brandName: cv.platform === 'bytehi' ? 'ByteHi' : 'OLA',
1466
+ showUtilityActions: cv.utilityActions === 'visible',
1467
+ utilityItems: cv.utilityActions === 'visible'
1468
+ ? (cv.platform === 'bytehi'
1469
+ ? [
1470
+ { id: 'guide', label: '操作指南', iconName: 'help-circle-stroked' },
1471
+ { id: 'message', label: '消息', iconName: 'bell-03-stroked' },
1472
+ ]
1473
+ : [
1474
+ { id: 'notice', label: '通知消息', iconName: 'bell-03-stroked' },
1475
+ { id: 'settings', label: '系统设置', iconName: 'settings-02-stroked' },
1476
+ ])
1477
+ : [],
1478
+ avatarType: enums.avatarType || 'image',
1479
+ }),
1480
+
1481
+ getPreviewAreaStyle: () => ({
1482
+ alignItems: 'stretch',
1483
+ justifyContent: 'flex-start',
1484
+ padding: '0 24px 0 0',
1485
+ }),
1486
+
1487
+ getPreviewItemStyle: () => ({
1488
+ display: 'flex',
1489
+ alignItems: 'stretch',
1490
+ justifyContent: 'flex-start',
1491
+ height: '100%',
1492
+ minHeight: '100%',
1493
+ alignSelf: 'stretch',
1494
+ }),
1495
+
1496
+ generateUsage: (enums, cv = {}) => {
1497
+ const avatarType = enums.avatarType || 'image';
1498
+ const platform = cv.platform || 'ola';
1499
+ const utilityActions = cv.utilityActions || 'hidden';
1500
+ const lines = [`import NavBar from './components/NavBar';`];
1501
+
1502
+ if (platform === 'ola') {
1503
+ lines.push('');
1504
+ lines.push('const appBusinesses = [');
1505
+ lines.push(" { id: 'douyin-community', label: '抖音社区', iconSrc: 'icon-logo-douyin' },");
1506
+ lines.push(" { id: 'douyin-local-services', label: '抖音生活服务', iconSrc: 'icon-logo-douyin' },");
1507
+ lines.push(" { id: 'douyin-ecommerce', label: '抖音电商', iconSrc: 'icon-logo-douyin' },");
1508
+ lines.push('];');
1509
+ lines.push('');
1510
+ lines.push('const navItems = [');
1511
+ lines.push(" { id: 'strategy', label: '策略管理', iconName: 'if-stroked' },");
1512
+ lines.push(" { id: 'knowledge', label: '知识与案例', iconName: 'book-open-01-stroked' },");
1513
+ lines.push(" { id: 'tools', label: '工具管理', iconName: 'tool-01-stroked' },");
1514
+ lines.push(" { id: 'insight', label: '对话洞察', iconName: 'message-chat-circle-stroked' },");
1515
+ lines.push(" { id: 'monitor', label: '智能盯盘', iconName: 'grid-03-stroked' },");
1516
+ lines.push('];');
1517
+ }
1518
+ if (platform === 'ola' && utilityActions === 'visible') {
1519
+ lines.push('');
1520
+ lines.push('const utilityItems = [');
1521
+ lines.push(" { id: 'notice', label: '通知消息', iconName: 'bell-03-stroked' },");
1522
+ lines.push(" { id: 'settings', label: '系统设置', iconName: 'settings-02-stroked' },");
1523
+ lines.push('];');
1524
+ }
1525
+ if (platform === 'bytehi' && utilityActions === 'visible') {
1526
+ lines.push('');
1527
+ lines.push('const utilityItems = [');
1528
+ lines.push(" { id: 'guide', label: '操作指南', iconName: 'help-circle-stroked' },");
1529
+ lines.push(" { id: 'message', label: '消息', iconName: 'bell-03-stroked' },");
1530
+ lines.push('];');
1531
+ }
1532
+ lines.push('');
1533
+ lines.push('<NavBar');
1534
+ if (platform !== 'ola') lines.push(` platform="${platform}"`);
1535
+ if (platform === 'bytehi') lines.push(' brandName="ByteHi"');
1536
+ if (avatarType !== 'image') lines.push(` avatarType="${avatarType}"`);
1537
+ if (utilityActions === 'visible') lines.push(' showUtilityActions');
1538
+ if (platform === 'ola') lines.push(' appBusinesses={appBusinesses}');
1539
+ if (platform === 'ola') lines.push(' navItems={navItems}');
1540
+ if (utilityActions === 'visible') lines.push(' utilityItems={utilityItems}');
1541
+ lines.push('/>');
1542
+ return lines.join('\n');
1543
+ },
1544
+ },
1545
+
1546
+ 'tag-bar': {
1547
+ component: TagBar,
1548
+ tokenMap: TAGBAR_TOKEN_MAP,
1549
+ jsxSource: tagBarJsxRaw,
1550
+ controls: [
1551
+ {
1552
+ id: 'layout',
1553
+ label: '布局',
1554
+ type: 'seg',
1555
+ options: [
1556
+ { id: 'expanded', label: '展开' },
1557
+ { id: 'collapsed', label: '收起' },
1558
+ ],
1559
+ default: 'expanded',
1560
+ },
1561
+ {
1562
+ id: 'searchable',
1563
+ label: '搜索',
1564
+ type: 'seg',
1565
+ options: [
1566
+ { id: 'on', label: '开启' },
1567
+ { id: 'off', label: '关闭' },
1568
+ ],
1569
+ default: 'on',
1570
+ },
1571
+ ],
1572
+
1573
+ mapProps: (cv) => {
1574
+ const layout = cv.layout || 'expanded';
1575
+ const searchable = (cv.searchable || 'on') === 'on';
1576
+ const businesses = TAGBAR_SAMPLE_BUSINESSES;
1577
+ const expandedIds = ['account', 'account-registration-login'];
1578
+
1579
+ return {
1580
+ key: `tagbar-preview-${layout}-${searchable ? 'search' : 'nosearch'}`,
1581
+ businesses,
1582
+ defaultBusinessId: businesses[0]?.id || 'douyin-community',
1583
+ items: TAGBAR_SAMPLE_ITEMS,
1584
+ defaultSelectedItemId: 'account-registration-login-field-1',
1585
+ defaultExpandedIds: expandedIds,
1586
+ searchable,
1587
+ collapsible: true,
1588
+ defaultCollapsed: layout === 'collapsed',
1589
+ defaultSearchValue: '',
1590
+ };
1591
+ },
1592
+
1593
+ getPreviewAreaStyle: () => ({
1594
+ alignItems: 'stretch',
1595
+ justifyContent: 'flex-start',
1596
+ padding: '0 24px 0 0',
1597
+ }),
1598
+
1599
+ getPreviewItemStyle: () => ({
1600
+ display: 'flex',
1601
+ alignItems: 'stretch',
1602
+ justifyContent: 'flex-start',
1603
+ height: '100%',
1604
+ minHeight: '100%',
1605
+ alignSelf: 'stretch',
1606
+ }),
1607
+
1608
+ generateUsage: (_, cv = {}) => {
1609
+ const layout = cv.layout || 'expanded';
1610
+ const searchable = (cv.searchable || 'on') === 'on';
1611
+ const lines = [`import TagBar from './components/TagBar';`];
1612
+
1613
+ lines.push('');
1614
+ lines.push('const businesses = [');
1615
+ lines.push(" { id: 'douyin-community', label: '抖音社区' },");
1616
+ lines.push(" { id: 'douyin-ecommerce', label: '抖音电商' },");
1617
+ lines.push(" { id: 'douyin-local-services', label: '抖音生活服务' },");
1618
+ lines.push('];');
1619
+ lines.push('');
1620
+ lines.push('const items = [');
1621
+ lines.push(" { id: 'account', label: '账号', iconName: 'user-01-stroked', iconBgToken: 'brand-50', children: [{ id: 'account-registration', label: '账号注册/登录', children: [{ id: 'account-register-too-many', label: '注册时提示「注册账号过多」' }, { id: 'account-face-fail', label: '注册/登录时用户刷脸/实名认证失败' }] }, { id: 'account-punishment', label: '账号处罚' }, { id: 'account-auth', label: '实名认证' }] },");
1622
+ lines.push(" { id: 'base-product', label: '基础产品', iconName: 'layers-two-01-stroked', iconBgToken: 'blue-50' },");
1623
+ lines.push(" { id: 'social', label: '社交', iconName: 'heart-circle-stroked', iconBgToken: 'pink-50' },");
1624
+ lines.push('];');
1625
+ lines.push('');
1626
+ lines.push('{/* 双白卡场景:外层白圆角 + overflow-visible;勿用 tone="panel" 冒充白卡 */}');
1627
+ lines.push('<div style={{ background: "var(--color-white)", borderRadius: 12, overflow: "visible", width: 240 }}>');
1628
+ lines.push(' <TagBar');
1629
+ lines.push(' businesses={businesses}');
1630
+ lines.push(' items={items}');
1631
+ lines.push(' defaultBusinessId="douyin-community"');
1632
+ lines.push(' defaultSelectedItemId="account-registration-login-field-1"');
1633
+ lines.push(" defaultExpandedIds={['account', 'account-registration-login']}");
1634
+ if (layout === 'collapsed') lines.push(' defaultCollapsed');
1635
+ if (searchable) lines.push(' searchable');
1636
+ lines.push(' collapsible');
1637
+ lines.push(' tone="transparent"');
1638
+ lines.push(' className="!border-r-0"');
1639
+ lines.push(' style={{ background: "transparent" }}');
1640
+ lines.push(' />');
1641
+ lines.push('</div>');
1642
+ return lines.join('\n');
1643
+ },
1644
+ },
1645
+
1646
+ 'chat-bubble': {
1647
+ component: ChatBubblePreview,
1648
+ tokenMap: CHATBUBBLE_TOKEN_MAP,
1649
+ jsxSource: chatBubbleJsxRaw,
1650
+ hideBaseVariantControl: true,
1651
+ enumTitles: {
1652
+ incomingTone: '左侧气泡色',
1653
+ outgoingTone: '右侧气泡色',
1654
+ },
1655
+
1656
+ controls: [
1657
+ {
1658
+ id: 'previewVariant',
1659
+ label: '变体',
1660
+ type: 'seg',
1661
+ options: [
1662
+ { id: 'incoming', label: '左侧' },
1663
+ { id: 'outgoing', label: '右侧' },
1664
+ { id: 'both', label: '左+右' },
1665
+ ],
1666
+ default: 'outgoing',
1667
+ },
1668
+ ],
1669
+
1670
+ mapProps: (cv, enums) => {
1671
+ const previewVariant = cv.previewVariant || 'outgoing';
1672
+ const variant = previewVariant === 'both' ? 'incoming' : previewVariant;
1673
+ const layout = enums.layout || 'single';
1674
+ const supportsOutgoingControls = previewVariant === 'outgoing' || previewVariant === 'both';
1675
+ const resolvedReceipt = supportsOutgoingControls ? (enums.receipt || 'hidden') : 'hidden';
1676
+ return {
1677
+ previewVariant,
1678
+ variant,
1679
+ layout,
1680
+ avatarType: enums.avatarType || 'image',
1681
+ size: enums.size || 'default',
1682
+ incomingTone: enums.incomingTone || 'default',
1683
+ outgoingTone: enums.outgoingTone || 'white',
1684
+ receipt: resolvedReceipt,
1685
+ messages: CHATBUBBLE_PRESET_MESSAGES[variant]?.[layout] || CHATBUBBLE_PRESET_MESSAGES.incoming.single,
1686
+ timestamps: CHATBUBBLE_PRESET_TIMESTAMPS[variant]?.[layout] || CHATBUBBLE_PRESET_TIMESTAMPS.incoming.single,
1687
+ avatarAlt: variant === 'incoming' ? '用户头像' : '客服头像',
1688
+ };
1689
+ },
1690
+
1691
+ getVisibleEnumProps: ({ controlValues, enumProps }) => {
1692
+ const previewVariant = controlValues.previewVariant || 'outgoing';
1693
+ return enumProps.filter((prop) => {
1694
+ if (prop.name === 'variant') return false;
1695
+ if (prop.name === 'receipt' && previewVariant === 'incoming') return false;
1696
+ /* 变体与气泡色联动:仅左侧预览只配左侧色;仅右侧只配右侧色;左+右同时展示两侧 */
1697
+ if (prop.name === 'incomingTone' && previewVariant === 'outgoing') return false;
1698
+ if (prop.name === 'outgoingTone' && previewVariant === 'incoming') return false;
1699
+ return true;
1700
+ });
1701
+ },
1702
+
1703
+ generateUsage: (enums, cv) => {
1704
+ const lines = [`import ChatBubble from './components/ChatBubble';`];
1705
+ const previewVariant = cv.previewVariant || 'outgoing';
1706
+ const variant = previewVariant === 'both' ? 'incoming' : previewVariant;
1707
+ const layout = enums.layout || 'single';
1708
+ const avatarType = enums.avatarType || 'image';
1709
+ const size = enums.size || 'default';
1710
+ const incomingTone = enums.incomingTone || 'default';
1711
+ const outgoingTone = enums.outgoingTone || 'white';
1712
+ const receipt = variant === 'outgoing' || previewVariant === 'both' ? (enums.receipt || 'hidden') : 'hidden';
1713
+ const messages = CHATBUBBLE_PRESET_MESSAGES[variant]?.[layout] || CHATBUBBLE_PRESET_MESSAGES.incoming.single;
1714
+ const timestamps = CHATBUBBLE_PRESET_TIMESTAMPS[variant]?.[layout] || CHATBUBBLE_PRESET_TIMESTAMPS.incoming.single;
1715
+
1716
+ if (previewVariant === 'both') {
1717
+ lines.push('');
1718
+ lines.push('const incomingMessages = [');
1719
+ lines.push(' "为什么我无法设置点赞列表查看权限",');
1720
+ lines.push('];');
1721
+ lines.push('const incomingTimestamps = ["2026-02-26 10:24:14"];');
1722
+ lines.push('');
1723
+ lines.push(`const outgoingMessages = [`);
1724
+ (layout === 'multiple' ? CHATBUBBLE_PRESET_MESSAGES.outgoing.multiple : CHATBUBBLE_PRESET_MESSAGES.outgoing.single).forEach((message) => {
1725
+ lines.push(` "${message}",`);
1726
+ });
1727
+ lines.push('];');
1728
+ lines.push(`const outgoingTimestamps = [`);
1729
+ (layout === 'multiple' ? CHATBUBBLE_PRESET_TIMESTAMPS.outgoing.multiple : CHATBUBBLE_PRESET_TIMESTAMPS.outgoing.single).forEach((time) => {
1730
+ lines.push(` "${time}",`);
1731
+ });
1732
+ lines.push('];');
1733
+ if (layout === 'multiple') {
1734
+ lines.push('');
1735
+ lines.push('const trailingIncomingMessages = [');
1736
+ lines.push(' "那怎么看自己的直播回放?",');
1737
+ lines.push('];');
1738
+ lines.push('const trailingIncomingTimestamps = ["2026-02-26 10:26:08"];');
1739
+ }
1740
+ lines.push('');
1741
+ lines.push('<>');
1742
+ lines.push(' <ChatBubble');
1743
+ if (incomingTone !== 'default') lines.push(` incomingTone="${incomingTone}"`);
1744
+ lines.push(' messages={incomingMessages}');
1745
+ lines.push(' timestamps={incomingTimestamps}');
1746
+ lines.push(' />');
1747
+ lines.push(' <div className="h-4" />');
1748
+ lines.push(' <ChatBubble');
1749
+ lines.push(' variant="outgoing"');
1750
+ if (layout !== 'single') lines.push(` layout="${layout}"`);
1751
+ if (avatarType !== 'image') lines.push(` avatarType="${avatarType}"`);
1752
+ if (size !== 'default') lines.push(` size="${size}"`);
1753
+ if (outgoingTone !== 'white') lines.push(` outgoingTone="${outgoingTone}"`);
1754
+ if (receipt !== 'hidden') lines.push(` receipt="${receipt}"`);
1755
+ lines.push(' messages={outgoingMessages}');
1756
+ lines.push(' timestamps={outgoingTimestamps}');
1757
+ lines.push(' />');
1758
+ if (layout === 'multiple') {
1759
+ lines.push(' <div className="h-4" />');
1760
+ lines.push(' <ChatBubble');
1761
+ if (incomingTone !== 'default') lines.push(` incomingTone="${incomingTone}"`);
1762
+ lines.push(' messages={trailingIncomingMessages}');
1763
+ lines.push(' timestamps={trailingIncomingTimestamps}');
1764
+ lines.push(' />');
1765
+ }
1766
+ lines.push('</>');
1767
+ return lines.join('\n');
1768
+ }
1769
+
1770
+ lines.push('');
1771
+ lines.push('const messages = [');
1772
+ messages.forEach((message) => {
1773
+ lines.push(` "${message}",`);
1774
+ });
1775
+ lines.push('];');
1776
+ lines.push('const timestamps = [');
1777
+ timestamps.forEach((time) => {
1778
+ lines.push(` "${time}",`);
1779
+ });
1780
+ lines.push('];');
1781
+ lines.push('');
1782
+ lines.push('<ChatBubble');
1783
+ if (variant !== 'incoming') lines.push(` variant="${variant}"`);
1784
+ if (layout !== 'single') lines.push(` layout="${layout}"`);
1785
+ if (avatarType !== 'image') lines.push(` avatarType="${avatarType}"`);
1786
+ if (size !== 'default') lines.push(` size="${size}"`);
1787
+ if (previewVariant === 'incoming' && incomingTone !== 'default') {
1788
+ lines.push(` incomingTone="${incomingTone}"`);
1789
+ }
1790
+ if (previewVariant === 'outgoing' && outgoingTone !== 'white') {
1791
+ lines.push(` outgoingTone="${outgoingTone}"`);
1792
+ }
1793
+ if (receipt !== 'hidden') lines.push(` receipt="${receipt}"`);
1794
+ lines.push(' messages={messages}');
1795
+ lines.push(' timestamps={timestamps}');
1796
+ lines.push('/>');
1797
+ return lines.join('\n');
1798
+ },
1799
+ },
1800
+
1801
+ 'chat-input': {
1802
+ component: ChatInput,
1803
+ tokenMap: CHATINPUT_TOKEN_MAP,
1804
+ jsxSource: chatInputJsxRaw,
1805
+
1806
+ controls: [
1807
+ {
1808
+ id: 'seedContent', label: '示例内容', type: 'switch',
1809
+ default: false,
1810
+ },
1811
+ ],
1812
+
1813
+ /* 容器宽度走 TOKEN_MAP「容器 → 宽度」配置(T2 非 token 固定值),默认 500px;
1814
+ * 用户在属性面板改 width 后,directStyleOverrides.width 会同步到 wrapper */
1815
+ getPreviewItemStyle: ({ directStyleOverrides } = {}) => ({
1816
+ width: directStyleOverrides?.width || '500px',
1817
+ maxWidth: '100%',
1818
+ }),
1819
+
1820
+ /* ── 按 variant 自动套合理默认文案;seedContent=filled 时给 ChatInput 注入示例填充态 ── */
1821
+ mapProps: (controls, enums) => {
1822
+ const v = enums.variant || 'default';
1823
+ const seed = controls.seedContent === true;
1824
+
1825
+ if (v === 'busy') {
1826
+ return {
1827
+ catBarText: '正在查询数据接口「execute_agent/API/interface/Function call Name」',
1828
+ statusText: '已耗时 3 min,运行结束后会通过飞书消息告知',
1829
+ };
1830
+ }
1831
+ if (v === 'readonly') {
1832
+ return {
1833
+ statusText: '来自其他成员的任务分享,当前页面仅支持预览',
1834
+ actionText: '继续对话',
1835
+ };
1836
+ }
1837
+ /* default / default-sm / im-basic / replying:仅在 seed 开启时注入预览数据 */
1838
+ return seed ? { _seedContent: true } : {};
1839
+ },
1840
+
1841
+ generateUsage: (enums) => {
1842
+ const lines = [`import ChatInput from './components/ChatInput';`];
1843
+ const v = enums.variant || 'default';
1844
+ lines.push('');
1845
+ lines.push('<ChatInput');
1846
+ if (v !== 'default') lines.push(` variant="${v}"`);
1847
+ if (v === 'busy') {
1848
+ lines.push(' catBarText="正在查询数据接口"');
1849
+ lines.push(' statusText="已耗时 3 min,运行结束后会通过飞书消息告知"');
1850
+ lines.push(' onStop={() => {}}');
1851
+ } else if (v === 'readonly') {
1852
+ lines.push(' statusText="来自其他成员的任务分享,当前页面仅支持预览"');
1853
+ lines.push(' actionText="继续对话"');
1854
+ lines.push(' onAction={() => {}}');
1855
+ } else if (v === 'im-basic') {
1856
+ lines.push(' placeholder="说点什么…"');
1857
+ lines.push(' acceptFiles="image/*"');
1858
+ lines.push(' onImEmojiClick={() => {}}');
1859
+ }
1860
+ lines.push('/>');
1861
+ return lines.join('\n');
1862
+ },
1863
+ },
1864
+
1865
+ 'chat-message': {
1866
+ component: ChatMessagePreview,
1867
+ tokenMap: CHAT_MESSAGE_TOKEN_MAP,
1868
+ jsxSource: chatMessageJsxRaw,
1869
+
1870
+ getPreviewAreaStyle: () => ({
1871
+ alignItems: 'stretch',
1872
+ justifyContent: 'flex-start',
1873
+ overflow: 'auto',
1874
+ padding: '12px',
1875
+ }),
1876
+
1877
+ getPreviewItemStyle: () => ({
1878
+ width: '100%',
1879
+ minWidth: 0,
1880
+ alignSelf: 'stretch',
1881
+ }),
1882
+
1883
+ getPreviewScalerStyle: () => ({
1884
+ width: '100%',
1885
+ minWidth: 0,
1886
+ transformOrigin: 'left top',
1887
+ }),
1888
+
1889
+ /* 每个子组件的"变体"使用独立 control id,避免共用 'variant' key 互相覆盖
1890
+ * subComponent → variant control id 映射 */
1891
+ controls: [
1892
+ {
1893
+ id: 'subComponent',
1894
+ label: '子组件',
1895
+ type: 'seg',
1896
+ options: [
1897
+ { id: 'execution-flow', label: '执行流' },
1898
+ { id: 'text-reply', label: '文本回复' },
1899
+ { id: 'deep-thinking', label: '深度思考' },
1900
+ { id: 'card', label: '卡片' },
1901
+ { id: 'user-message', label: '用户消息' },
1902
+ { id: 'ai-avatar', label: 'AI头像+名称' },
1903
+ { id: 'conversation', label: '对话流' },
1904
+ { id: 'artifact', label: '产物' },
1905
+ { id: 'action-bar', label: '操作栏' },
1906
+ { id: 'follow-up', label: '追问' },
1907
+ ],
1908
+ default: 'execution-flow',
1909
+ },
1910
+ {
1911
+ id: 'executionFlowVariant',
1912
+ label: '变体',
1913
+ type: 'seg',
1914
+ options: [
1915
+ { id: 'completed', label: '已完成' },
1916
+ { id: 'running', label: '执行中' },
1917
+ ],
1918
+ default: 'completed',
1919
+ hidden: ({ controlValues }) => (controlValues.subComponent || 'execution-flow') !== 'execution-flow',
1920
+ },
1921
+ {
1922
+ id: 'deepThinkingVariant',
1923
+ label: '变体',
1924
+ type: 'seg',
1925
+ options: [
1926
+ { id: 'thinking', label: '思考中' },
1927
+ { id: 'expanded', label: '展开' },
1928
+ { id: 'collapsed', label: '折叠' },
1929
+ ],
1930
+ default: 'expanded',
1931
+ hidden: ({ controlValues }) => (controlValues.subComponent || 'execution-flow') !== 'deep-thinking',
1932
+ },
1933
+ {
1934
+ id: 'userMessageVariant',
1935
+ label: '变体',
1936
+ type: 'seg',
1937
+ options: [
1938
+ { id: 'text', label: '纯文本' },
1939
+ { id: 'mixed-tag', label: '文本+@TAG' },
1940
+ { id: 'with-attachment', label: '带附件' },
1941
+ { id: 'with-quote', label: '带划词引用' },
1942
+ ],
1943
+ default: 'text',
1944
+ hidden: ({ controlValues }) => (controlValues.subComponent || 'execution-flow') !== 'user-message',
1945
+ },
1946
+ {
1947
+ id: 'aiAvatarVariant',
1948
+ label: '变体',
1949
+ type: 'seg',
1950
+ options: [
1951
+ { id: 'default', label: '默认' },
1952
+ { id: 'loading', label: '加载中' },
1953
+ ],
1954
+ default: 'default',
1955
+ hidden: ({ controlValues }) => (controlValues.subComponent || 'execution-flow') !== 'ai-avatar',
1956
+ },
1957
+ {
1958
+ id: 'cardVariant',
1959
+ label: '变体',
1960
+ type: 'seg',
1961
+ options: [
1962
+ { id: 'task-plan', label: '任务规划' },
1963
+ { id: 'config-form', label: '配置表单' },
1964
+ ],
1965
+ default: 'task-plan',
1966
+ hidden: ({ controlValues }) => (controlValues.subComponent || 'execution-flow') !== 'card',
1967
+ },
1968
+ {
1969
+ id: 'artifactVariant',
1970
+ label: '变体',
1971
+ type: 'seg',
1972
+ options: [
1973
+ { id: 'icon', label: '产物图标' },
1974
+ { id: 'card', label: '产物卡片' },
1975
+ { id: 'group', label: '产物组' },
1976
+ ],
1977
+ default: 'group',
1978
+ hidden: ({ controlValues }) => (controlValues.subComponent || 'execution-flow') !== 'artifact',
1979
+ },
1980
+ {
1981
+ id: 'conversationVariant',
1982
+ label: '变体',
1983
+ type: 'seg',
1984
+ options: [
1985
+ { id: 'ai-rich', label: 'AI完整回复' },
1986
+ { id: 'multi-turn', label: '多轮对话' },
1987
+ { id: 'ai-task-plan', label: 'AI任务规划' },
1988
+ ],
1989
+ default: 'ai-rich',
1990
+ hidden: ({ controlValues }) => (controlValues.subComponent || 'execution-flow') !== 'conversation',
1991
+ },
1992
+ ],
1993
+
1994
+ /* 子组件 → 该子组件的 variant control id(mapProps & generateUsage 共用查表) */
1995
+ _variantKey: (sub) => ({
1996
+ 'execution-flow': 'executionFlowVariant',
1997
+ 'text-reply': null,
1998
+ 'deep-thinking': 'deepThinkingVariant',
1999
+ 'card': 'cardVariant',
2000
+ 'user-message': 'userMessageVariant',
2001
+ 'ai-avatar': 'aiAvatarVariant',
2002
+ 'conversation': 'conversationVariant',
2003
+ 'artifact': 'artifactVariant',
2004
+ 'action-bar': null,
2005
+ 'follow-up': null,
2006
+ })[sub],
2007
+
2008
+ mapProps: (cv) => {
2009
+ const sub = cv.subComponent || 'execution-flow';
2010
+ const variantKey = ({
2011
+ 'execution-flow': 'executionFlowVariant',
2012
+ 'deep-thinking': 'deepThinkingVariant',
2013
+ 'card': 'cardVariant',
2014
+ 'user-message': 'userMessageVariant',
2015
+ 'ai-avatar': 'aiAvatarVariant',
2016
+ 'conversation': 'conversationVariant',
2017
+ 'artifact': 'artifactVariant',
2018
+ })[sub];
2019
+ const variant = variantKey ? cv[variantKey] : 'default';
2020
+ return {
2021
+ subComponent: sub,
2022
+ variant: variant || 'default',
2023
+ };
2024
+ },
2025
+
2026
+ enumTitles: {
2027
+ userBubbleTone: '消息气泡背景色',
2028
+ },
2029
+
2030
+ getVisibleEnumProps: ({ enumProps, controlValues }) => {
2031
+ const sub = controlValues.subComponent || 'execution-flow';
2032
+ return enumProps.filter((prop) => {
2033
+ if (prop.name !== 'userBubbleTone') return false;
2034
+ return sub === 'user-message' || sub === 'conversation';
2035
+ });
2036
+ },
2037
+
2038
+ generateUsage: (enums, cv) => {
2039
+ const sub = cv.subComponent || 'execution-flow';
2040
+ const variantKey = ({
2041
+ 'execution-flow': 'executionFlowVariant',
2042
+ 'deep-thinking': 'deepThinkingVariant',
2043
+ 'card': 'cardVariant',
2044
+ 'user-message': 'userMessageVariant',
2045
+ 'ai-avatar': 'aiAvatarVariant',
2046
+ 'conversation': 'conversationVariant',
2047
+ 'artifact': 'artifactVariant',
2048
+ })[sub];
2049
+ const variant = (variantKey ? cv[variantKey] : 'default') || 'default';
2050
+ const userBubbleTone = enums.userBubbleTone || 'auto';
2051
+ const userBubbleToneLine = userBubbleTone === 'auto' ? null : ` userBubbleTone="${userBubbleTone}"`;
2052
+
2053
+ const usageMap = {
2054
+ 'execution-flow': {
2055
+ completed: [
2056
+ '<ChatMessage',
2057
+ ' title={DEFAULT_CHAT_TITLE}',
2058
+ ' steps={DEFAULT_CHAT_STEPS}',
2059
+ '/>',
2060
+ ],
2061
+ running: [
2062
+ '<ChatMessage',
2063
+ ' status="processing"',
2064
+ ' title={DEFAULT_CHAT_TITLE}',
2065
+ ' steps={DEFAULT_CHAT_STEPS.slice(0, 2)}',
2066
+ '/>',
2067
+ ],
2068
+ },
2069
+ 'text-reply': {
2070
+ default: [
2071
+ '<ChatMessage',
2072
+ ' header',
2073
+ ' title=""',
2074
+ ' steps={null}',
2075
+ ' resultText="收到,我先去拉平台规则和商家补充条款,分析完会给你一份统一答复口径。"',
2076
+ ' actions',
2077
+ ' timestamp="18:16"',
2078
+ '/>',
2079
+ ],
2080
+ },
2081
+ 'deep-thinking': {
2082
+ thinking: [
2083
+ '<ChatMessage',
2084
+ ' header',
2085
+ ' status="thinking"',
2086
+ ' steps={null}',
2087
+ ' thinking={{ state: "thinking", inProgressLabel: "深度思考中 ..." }}',
2088
+ '/>',
2089
+ ],
2090
+ expanded: [
2091
+ '<ChatMessage',
2092
+ ' header',
2093
+ ' title=""',
2094
+ ' steps={null}',
2095
+ ' thinking={{ ...DEFAULT_CHAT_THINKING, state: "completed", defaultExpanded: true }}',
2096
+ ' resultText="好的,我将开始为您分析。"',
2097
+ ' actions',
2098
+ ' timestamp="18:16"',
2099
+ '/>',
2100
+ ],
2101
+ collapsed: [
2102
+ '<ChatMessage',
2103
+ ' header',
2104
+ ' title=""',
2105
+ ' steps={null}',
2106
+ ' thinking={{ ...DEFAULT_CHAT_THINKING, state: "completed", defaultExpanded: false }}',
2107
+ ' resultText="好的,我将开始为您分析。"',
2108
+ ' actions',
2109
+ ' timestamp="18:16"',
2110
+ '/>',
2111
+ ],
2112
+ },
2113
+ 'card': {
2114
+ 'task-plan': [
2115
+ '<ChatMessage',
2116
+ ' header',
2117
+ ' title=""',
2118
+ ' steps={null}',
2119
+ ' plan={DEFAULT_CHAT_PLAN}',
2120
+ ' actions',
2121
+ ' timestamp="18:16"',
2122
+ '/>',
2123
+ ],
2124
+ 'config-form': [
2125
+ '<ChatMessage',
2126
+ ' header',
2127
+ ' title=""',
2128
+ ' steps={null}',
2129
+ ' confirms={DEFAULT_CHAT_CONFIRMS}',
2130
+ ' actions',
2131
+ ' timestamp="18:16"',
2132
+ '/>',
2133
+ ],
2134
+ },
2135
+ 'user-message': {
2136
+ text: [
2137
+ '<ChatMessage',
2138
+ ' role="user"',
2139
+ userBubbleToneLine,
2140
+ ' timestamp="14:02"',
2141
+ ' userContent={[{ type: "text", value: "帮我整理一下抖音电商的售后政策" }]}',
2142
+ ' actions',
2143
+ '/>',
2144
+ ],
2145
+ 'mixed-tag': [
2146
+ '<ChatMessage',
2147
+ ' role="user"',
2148
+ userBubbleToneLine,
2149
+ ' timestamp="14:05"',
2150
+ ' userContent={[',
2151
+ ' { type: "text", value: "分析一批" },',
2152
+ ' { type: "entity", icon: "message-chat-square-stroked", label: "智能会话:社交私信", tone: "teal", showChevron: true },',
2153
+ ' { type: "text", value: "的问题与机会点" },',
2154
+ ' ]}',
2155
+ ' actions',
2156
+ '/>',
2157
+ ],
2158
+ 'with-attachment': [
2159
+ '<ChatMessage',
2160
+ ' role="user"',
2161
+ userBubbleToneLine,
2162
+ ' timestamp="14:08"',
2163
+ ' userContent={[{ type: "text", value: "附件里是售后规则汇编,请基于这两份文件输出统一答复口径" }]}',
2164
+ ' userAttachments={[',
2165
+ ' { name: "抖音电商售后政策汇编.pdf", size: 327680 },',
2166
+ ' { name: "商家补充协议-生鲜定制.pdf", size: 184320 },',
2167
+ ' ]}',
2168
+ ' actions',
2169
+ '/>',
2170
+ ],
2171
+ 'with-quote': [
2172
+ '<ChatMessage',
2173
+ ' role="user"',
2174
+ userBubbleToneLine,
2175
+ ' timestamp="14:10"',
2176
+ ' userContent={[{ type: "text", value: "请帮我复盘这次服务的完整链路,并着重分析[用户评价未解决]的原因。生成飞书文档和一份完整的前端页面" }]}',
2177
+ ' userQuote={{',
2178
+ ' iconName: "type-01-stroked",',
2179
+ ' cornerIconName: "corner-up-right-stroked",',
2180
+ ' title: "接口出参",',
2181
+ ' description: "具体内容具体内容具体内容具体内容具体内容具体内",',
2182
+ ' }}',
2183
+ ' actions',
2184
+ '/>',
2185
+ ],
2186
+ },
2187
+ 'ai-avatar': {
2188
+ default: [
2189
+ '<ChatMessage',
2190
+ ' header',
2191
+ ' title=""',
2192
+ ' steps={null}',
2193
+ ' timestamp="18:16"',
2194
+ '/>',
2195
+ ],
2196
+ loading: [
2197
+ '<ChatMessage',
2198
+ ' header',
2199
+ ' status="requesting"',
2200
+ ' steps={null}',
2201
+ '/>',
2202
+ ],
2203
+ },
2204
+ 'conversation': {
2205
+ 'ai-rich': [
2206
+ '/* 单条 AI 完整回复:头像 + 文本 + 产物 + 控制条 + 追问 */',
2207
+ '<ChatMessage',
2208
+ ' header',
2209
+ ' title=""',
2210
+ ' steps={null}',
2211
+ ' resultText={DEFAULT_CHAT_RESULT}',
2212
+ ' resultArtifacts={DEFAULT_CHAT_RESULT_ARTIFACTS}',
2213
+ ' actions',
2214
+ ' followUps={DEFAULT_CHAT_FOLLOW_UPS}',
2215
+ ' timestamp="18:16"',
2216
+ '/>',
2217
+ ],
2218
+ 'multi-turn': [
2219
+ '/* 多轮对话流:每条 ChatMessage 独立 hover 隔离',
2220
+ ' 仅最新一条 actions={{ historyMode: false }} 常显操作栏',
2221
+ ' 其余消息 historyMode=true → 默认隐藏,hover 当条才显示 */',
2222
+ '<>',
2223
+ ' <ChatMessage role="user" timestamp="14:02"',
2224
+ userBubbleTone === 'auto' ? null : ` userBubbleTone="${userBubbleTone}"`,
2225
+ ' userContent={[{ type: "text", value: "帮我整理一下抖音电商的售后政策" }]}',
2226
+ ' actions={{ historyMode: true }} />',
2227
+ ' <ChatMessage header title="" steps={null} timestamp="14:02"',
2228
+ ' resultText="收到,我整理完给你统一答复口径。"',
2229
+ ' actions={{ historyMode: true }} />',
2230
+ ' <ChatMessage role="user" timestamp="14:05"',
2231
+ userBubbleTone === 'auto' ? null : ` userBubbleTone="${userBubbleTone}"`,
2232
+ ' userContent={[{ type: "text", value: "拒收场景也要分商家原因和用户原因" }]}',
2233
+ ' actions={{ historyMode: true }} />',
2234
+ ' <ChatMessage header title="" steps={null} timestamp="14:05"',
2235
+ ' resultText="好的,拒收的两类责任划分单独开一节。"',
2236
+ ' actions />',
2237
+ '</>',
2238
+ ],
2239
+ 'ai-task-plan': [
2240
+ '/* AI 任务规划:头像 + 引导文本(leadText) + 任务规划卡片',
2241
+ ' leadText 展示在卡片上方;待用户点主按钮前操作栏自动隐藏,处理后才显示 */',
2242
+ '<ChatMessage',
2243
+ ' header',
2244
+ ' title=""',
2245
+ ' steps={null}',
2246
+ ' leadText="我开始规划啦,请稍后..."',
2247
+ ' plan={DEFAULT_CHAT_PLAN}',
2248
+ ' actions',
2249
+ ' timestamp="18:16"',
2250
+ '/>',
2251
+ ],
2252
+ },
2253
+ 'artifact': {
2254
+ icon: [
2255
+ 'import { ArtifactIcon } from "./ChatMessage";',
2256
+ '',
2257
+ '/* 单个产物图标(atom):type 取自 file-type-assets 共享 lookup',
2258
+ ' 支持类型:pdf | code | pe | xmind | image | document | table | webpage',
2259
+ ' knowledge | strategy | conversation | batch-report',
2260
+ ' feishu | feishu-card | feishu-sheet */',
2261
+ '<ArtifactIcon type="pdf" size={32} />',
2262
+ ],
2263
+ card: [
2264
+ 'import { ResultArtifactCard } from "./ChatMessage";',
2265
+ '',
2266
+ '<ResultArtifactCard',
2267
+ ' artifact={{',
2268
+ ' type: "pdf",',
2269
+ ' title: "AI 品牌提案.pdf",',
2270
+ ' meta: "370.3 KB",',
2271
+ ' }}',
2272
+ '/>',
2273
+ ],
2274
+ group: [
2275
+ 'import { ResultArtifactGroup, ALL_ARTIFACT_TYPES } from "./ChatMessage";',
2276
+ '',
2277
+ '/* 直接传入完整 15 种产物类型,纵向排列 */',
2278
+ '<ResultArtifactGroup artifacts={ALL_ARTIFACT_TYPES} />',
2279
+ ],
2280
+ },
2281
+ 'action-bar': {
2282
+ default: [
2283
+ '<ChatMessage',
2284
+ ' title=""',
2285
+ ' steps={null}',
2286
+ ' actions',
2287
+ ' timestamp="18:16"',
2288
+ '/>',
2289
+ ],
2290
+ },
2291
+ 'follow-up': {
2292
+ default: [
2293
+ '<ChatMessage',
2294
+ ' title=""',
2295
+ ' steps={null}',
2296
+ ' followUps={DEFAULT_CHAT_FOLLOW_UPS}',
2297
+ ' actions',
2298
+ ' timestamp="18:16"',
2299
+ '/>',
2300
+ ],
2301
+ },
2302
+ };
2303
+
2304
+ const bucket = usageMap[sub] || usageMap['text-reply'];
2305
+ const lines = bucket[variant] || bucket.default || Object.values(bucket)[0];
2306
+ return [
2307
+ `import ChatMessage from './components/ChatMessage';`,
2308
+ '',
2309
+ ...lines.filter(Boolean),
2310
+ ].join('\n');
2311
+ },
2312
+ },
2313
+
2314
+
2315
+
2316
+ button: {
2317
+ component: Button,
2318
+ tokenMap: BUTTON_TOKEN_MAP,
2319
+ jsxSource: buttonJsxRaw,
2320
+
2321
+ controls: [
2322
+ {
2323
+ id: 'contentType', label: '类型', type: 'seg',
2324
+ options: [
2325
+ { id: 'text', label: '仅字' },
2326
+ { id: 'icon', label: '仅icon' },
2327
+ { id: 'both', label: 'icon+字' },
2328
+ ],
2329
+ default: 'text',
2330
+ },
2331
+ {
2332
+ id: 'state', label: '状态', type: 'seg',
2333
+ options: [
2334
+ { id: 'default', label: '默认' },
2335
+ { id: 'loading', label: '加载' },
2336
+ { id: 'disabled', label: '禁用' },
2337
+ ],
2338
+ default: 'default',
2339
+ },
2340
+ ],
2341
+
2342
+ mapProps: (cv) => {
2343
+ const ct = cv.contentType;
2344
+ const hasIcon = ct === 'icon' || ct === 'both';
2345
+ return {
2346
+ icon: hasIcon ? <Plus size={16} /> : undefined,
2347
+ iconOnly: ct === 'icon',
2348
+ tooltip: ct === 'icon' ? '添加' : undefined,
2349
+ children: ct !== 'icon' ? '按钮文本' : undefined,
2350
+ loading: cv.state === 'loading',
2351
+ disabled: cv.state === 'disabled',
2352
+ };
2353
+ },
2354
+
2355
+ generateUsage: (enums, cv) => {
2356
+ const lines = [`import Button from './components/Button';`];
2357
+ const ct = cv.contentType || 'text';
2358
+ const st = cv.state || 'default';
2359
+ const props = [];
2360
+ props.push(`variant="${enums.variant || 'primary'}"`);
2361
+ props.push(`size="${enums.size || 'md'}"`);
2362
+ if (enums.radius && enums.radius !== 'rounded') props.push(`radius="${enums.radius}"`);
2363
+ if (ct === 'icon') {
2364
+ props.push('iconOnly');
2365
+ props.push('tooltip="添加"');
2366
+ props.push('icon={<Plus size={16} />}');
2367
+ } else if (ct === 'both') {
2368
+ props.push('icon={<Plus size={16} />}');
2369
+ }
2370
+ if (st === 'loading') props.push('loading');
2371
+ if (st === 'disabled') props.push('disabled');
2372
+
2373
+ lines.push('');
2374
+ if (ct === 'icon') {
2375
+ lines.push(`<Button`);
2376
+ props.forEach(p => lines.push(` ${p}`));
2377
+ lines.push('/>');
2378
+ } else {
2379
+ lines.push(`<Button`);
2380
+ props.forEach(p => lines.push(` ${p}`));
2381
+ lines.push('>');
2382
+ lines.push(' 按钮文本');
2383
+ lines.push('</Button>');
2384
+ }
2385
+ return lines.join('\n');
2386
+ },
2387
+ },
2388
+
2389
+ tabs: {
2390
+ component: Tabs,
2391
+ tokenMap: TABS_TOKEN_MAP,
2392
+ jsxSource: tabsJsxRaw,
2393
+
2394
+ controls: [
2395
+ {
2396
+ id: 'iconMode', label: '图标', type: 'seg',
2397
+ options: [
2398
+ { id: 'none', label: '无图标' },
2399
+ { id: 'withIcon', label: '带图标' },
2400
+ ],
2401
+ default: 'none',
2402
+ },
2403
+ ],
2404
+
2405
+ mapProps: (cv) => {
2406
+ const showIcon = cv.iconMode === 'withIcon';
2407
+ const items = [
2408
+ { label: '标签一', icon: showIcon ? <Heart size={16} /> : undefined },
2409
+ { label: '标签二', icon: showIcon ? <Star size={16} /> : undefined },
2410
+ { label: '标签三', icon: showIcon ? <Zap size={16} /> : undefined },
2411
+ ];
2412
+ return { items };
2413
+ },
2414
+
2415
+ generateUsage: (enums, cv) => {
2416
+ const lines = [`import Tabs from './components/Tabs';`];
2417
+ const showIcon = cv.iconMode === 'withIcon';
2418
+ const variant = enums.variant || 'line';
2419
+
2420
+ lines.push('');
2421
+ if (variant === 'segment') {
2422
+ lines.push('{/* 卡片内小模块切换 / 主区顶部布局切换:默认优先 segment */}');
2423
+ }
2424
+ lines.push('<Tabs');
2425
+ lines.push(` variant="${variant}"`);
2426
+ if (enums.size && enums.size !== 'sm') {
2427
+ lines.push(` size="${enums.size}"`);
2428
+ }
2429
+ if (showIcon) {
2430
+ lines.push(' items={[');
2431
+ lines.push(" { label: '标签一', icon: <Heart size={16} /> },");
2432
+ lines.push(" { label: '标签二', icon: <Star size={16} /> },");
2433
+ lines.push(" { label: '标签三', icon: <Zap size={16} /> },");
2434
+ lines.push(' ]}');
2435
+ } else {
2436
+ lines.push(' items={[');
2437
+ lines.push(" { label: '标签一' },");
2438
+ lines.push(" { label: '标签二' },");
2439
+ lines.push(" { label: '标签三' },");
2440
+ lines.push(' ]}');
2441
+ }
2442
+ lines.push('/>');
2443
+ return lines.join('\n');
2444
+ },
2445
+ },
2446
+ 'form-title': {
2447
+ component: FormTitle,
2448
+ tokenMap: FORM_TITLE_TOKEN_MAP,
2449
+ jsxSource: formTitleJsxRaw,
2450
+
2451
+ controls: [
2452
+ {
2453
+ id: 'showDescription',
2454
+ label: '描述文案',
2455
+ type: 'seg',
2456
+ options: [
2457
+ { id: 'on', label: '显示' },
2458
+ { id: 'off', label: '隐藏' },
2459
+ ],
2460
+ default: 'off',
2461
+ },
2462
+ {
2463
+ id: 'titleTag',
2464
+ label: '标题后缀',
2465
+ type: 'seg',
2466
+ options: [
2467
+ { id: 'off', label: '无' },
2468
+ { id: 'on', label: 'Tag' },
2469
+ ],
2470
+ default: 'off',
2471
+ },
2472
+ ],
2473
+
2474
+ mapProps: (cv, enums) => {
2475
+ const variant = enums.variant || 'form';
2476
+ const sampleMap = {
2477
+ form: { title: '表单标题', description: '一级标题描述' },
2478
+ 'level-1': { title: '一级标题', description: '一级标题描述' },
2479
+ 'level-2': { title: '二级标题', description: '二级标题描述' },
2480
+ 'level-3': { title: '三级标题', description: '二级标题描述' },
2481
+ card: { title: '卡片标题', description: '二级标题描述' },
2482
+ };
2483
+ const sample = sampleMap[variant] || sampleMap.form;
2484
+ return {
2485
+ variant,
2486
+ title: sample.title,
2487
+ titleSuffix: cv.titleTag === 'on' ? <Tag variant="grey" size="m">0 条消息</Tag> : null,
2488
+ description: cv.showDescription === 'on' ? sample.description : '',
2489
+ showDescription: cv.showDescription === 'on',
2490
+ };
2491
+ },
2492
+
2493
+ generateUsage: (enums, cv) => {
2494
+ const showDescription = cv.showDescription === 'on';
2495
+ const showTitleTag = cv.titleTag === 'on';
2496
+ const variant = enums.variant || 'form';
2497
+ const sampleMap = {
2498
+ form: { title: '表单标题', description: '一级标题描述' },
2499
+ 'level-1': { title: '一级标题', description: '一级标题描述' },
2500
+ 'level-2': { title: '二级标题', description: '二级标题描述' },
2501
+ 'level-3': { title: '三级标题', description: '二级标题描述' },
2502
+ card: { title: '卡片标题', description: '二级标题描述' },
2503
+ };
2504
+ const sample = sampleMap[variant] || sampleMap.form;
2505
+ const lines = [`import FormTitle from './components/FormTitle';`];
2506
+ if (showTitleTag) {
2507
+ lines.push(`import Tag from './components/Tag';`);
2508
+ }
2509
+
2510
+ lines.push('');
2511
+ if (variant === 'level-1' && !showDescription) {
2512
+ lines.push('{/* 列表/管理页白卡顶栏:level-1 + 无描述;与按钮同行用 flex justify-between */}');
2513
+ }
2514
+ lines.push('<FormTitle');
2515
+ if (variant !== 'form') lines.push(` variant="${variant}"`);
2516
+ lines.push(` title="${sample.title}"`);
2517
+ if (showTitleTag) {
2518
+ lines.push(' titleSuffix={<Tag variant="grey" size="m">0 条消息</Tag>}');
2519
+ }
2520
+ if (showDescription) {
2521
+ lines.push(` description="${sample.description}"`);
2522
+ lines.push(' showDescription');
2523
+ }
2524
+ lines.push('/>');
2525
+ return lines.join('\n');
2526
+ },
2527
+ },
2528
+
2529
+ form: {
2530
+ component: FormPreview,
2531
+ tokenMap: FORM_TOKEN_MAP,
2532
+
2533
+ normalizeControlValues: ({ changedControlId, nextControlValues, prevControlValues }) => (
2534
+ normalizeSliderPreviewControls(nextControlValues, prevControlValues, changedControlId, 'slider')
2535
+ ),
2536
+
2537
+ getPreviewAreaStyle: ({ controlValues }) => ({
2538
+ alignItems: (controlValues.previewMode || 'single') === 'all' ? 'flex-start' : 'center',
2539
+ justifyContent: (controlValues.previewMode || 'single') === 'all' ? 'flex-start' : 'center',
2540
+ overflow: 'auto',
2541
+ padding: '48px',
2542
+ }),
2543
+
2544
+ getPreviewItemStyle: ({ controlValues }) => ({
2545
+ width: controlValues.previewMode === 'all' ? 'max-content' : 'auto',
2546
+ minWidth: controlValues.previewMode === 'all' ? '300px' : undefined,
2547
+ display: 'flex',
2548
+ alignItems: controlValues.previewMode === 'all' ? 'flex-start' : 'center',
2549
+ justifyContent: controlValues.previewMode === 'all' ? 'flex-start' : 'center',
2550
+ }),
2551
+
2552
+ controls: [
2553
+ {
2554
+ id: 'previewMode',
2555
+ label: '预览',
2556
+ type: 'seg',
2557
+ options: [
2558
+ { id: 'single', label: '单项' },
2559
+ { id: 'all', label: '全集' },
2560
+ ],
2561
+ default: 'single',
2562
+ },
2563
+ {
2564
+ id: 'fieldType',
2565
+ label: '字段类型',
2566
+ type: 'select',
2567
+ options: FORM_FIELD_TYPES.map((id) => ({ id, label: FORM_FIELD_LABELS[id] || id })),
2568
+ default: 'input',
2569
+ hidden: ({ controlValues }) => (controlValues.previewMode || 'single') === 'all',
2570
+ },
2571
+ {
2572
+ id: 'labelPosition',
2573
+ label: '标签位置',
2574
+ type: 'seg',
2575
+ options: [
2576
+ { id: 'top', label: '顶部' },
2577
+ { id: 'left', label: '左侧' },
2578
+ ],
2579
+ default: 'top',
2580
+ hidden: ({ controlValues }) => (controlValues.previewMode || 'single') === 'all',
2581
+ },
2582
+ {
2583
+ id: 'helpMode',
2584
+ label: '提示',
2585
+ type: 'seg',
2586
+ options: [
2587
+ { id: 'none', label: '无' },
2588
+ { id: 'middle', label: '中部' },
2589
+ { id: 'bottom', label: '底部' },
2590
+ ],
2591
+ default: 'none',
2592
+ hidden: ({ controlValues }) => (controlValues.previewMode || 'single') === 'all',
2593
+ },
2594
+ {
2595
+ id: 'validationState',
2596
+ label: '校验',
2597
+ type: 'seg',
2598
+ options: [
2599
+ { id: 'default', label: '默认' },
2600
+ { id: 'error', label: '错误' },
2601
+ ],
2602
+ default: 'default',
2603
+ hidden: ({ controlValues }) => (controlValues.previewMode || 'single') === 'all',
2604
+ },
2605
+ {
2606
+ id: 'labelOptional',
2607
+ label: '标题-可选',
2608
+ type: 'switch',
2609
+ default: false,
2610
+ },
2611
+ {
2612
+ id: 'labelRequired',
2613
+ label: '标题-必填',
2614
+ type: 'switch',
2615
+ default: false,
2616
+ },
2617
+ {
2618
+ id: 'labelAi',
2619
+ label: '标题-AI',
2620
+ type: 'switch',
2621
+ default: false,
2622
+ },
2623
+ {
2624
+ id: 'labelHelp',
2625
+ label: '标题-Help',
2626
+ type: 'switch',
2627
+ default: false,
2628
+ },
2629
+ ...buildSliderPreviewControls('slider', ({ controlValues }) => {
2630
+ const previewMode = controlValues.previewMode || 'single';
2631
+ return previewMode === 'all' || (controlValues.fieldType || 'input') !== 'slider';
2632
+ }),
2633
+ ],
2634
+
2635
+ enumTitles: {
2636
+ controlVariant: '组件颜色',
2637
+ controlLayout: '组件排列',
2638
+ pickerType: '日期类型',
2639
+ timePickerType: '时间类型',
2640
+ inputNumberButtons: '按钮位置',
2641
+ },
2642
+
2643
+ getVisibleEnumProps: ({ enumProps, controlValues }) => {
2644
+ const previewMode = controlValues.previewMode || 'single';
2645
+ if (previewMode === 'all') return [];
2646
+ return getFormFieldAtomEnumProps(controlValues.fieldType || 'input');
2647
+ },
2648
+
2649
+ mapProps: (cv, enums) => ({
2650
+ previewMode: cv.previewMode || 'single',
2651
+ fieldType: cv.fieldType || 'input',
2652
+ labelPosition: cv.labelPosition || 'top',
2653
+ helpMode: cv.helpMode || 'none',
2654
+ validationState: cv.validationState || 'default',
2655
+ size: 'md',
2656
+ labelOptional: cv.labelOptional === true,
2657
+ labelRequired: cv.labelRequired === true,
2658
+ labelAi: cv.labelAi === true,
2659
+ labelHelp: cv.labelHelp === true,
2660
+ fieldAtomProps: buildFormFieldAtomProps(cv.fieldType || 'input', enums, cv),
2661
+ }),
2662
+
2663
+ generateUsage: (enums, cv) => {
2664
+ const lines = [`import Form from './components/Form';`];
2665
+ const previewMode = cv.previewMode || 'single';
2666
+ const fieldType = cv.fieldType || 'input';
2667
+ const labelPosition = cv.labelPosition || 'top';
2668
+ const helpMode = cv.helpMode || 'none';
2669
+ const validationState = cv.validationState || 'default';
2670
+ const labelOptional = cv.labelOptional === true;
2671
+ const labelRequired = cv.labelRequired === true;
2672
+ const labelAi = cv.labelAi === true;
2673
+ const labelHelp = cv.labelHelp === true;
2674
+ const fieldAtomProps = buildFormFieldAtomProps(fieldType, enums, cv);
2675
+
2676
+ if (previewMode === 'all') {
2677
+ lines.push('');
2678
+ lines.push('<Form className="!gap-10" />');
2679
+ return lines.join('\n');
2680
+ }
2681
+
2682
+ lines.push('');
2683
+ lines.push('const items = [{');
2684
+ lines.push(" id: 'field-1',");
2685
+ lines.push(" label: '字段标题',");
2686
+ lines.push(` type: '${fieldType}',`);
2687
+ lines.push(' required: true,');
2688
+ if (helpMode === 'middle') lines.push(" middleHelpText: '提示文案',");
2689
+ if (helpMode === 'bottom') lines.push(" helpText: '提示文案',");
2690
+ if (validationState === 'error') lines.push(" error: true, errorText: '该项目为必填项',");
2691
+ if (fieldType === 'radio' || fieldType === 'checkbox') {
2692
+ if (fieldAtomProps.variant !== 'brand') lines.push(` variant: '${fieldAtomProps.variant}',`);
2693
+ if (fieldAtomProps.layout !== 'horizontal') lines.push(` layout: '${fieldAtomProps.layout}',`);
2694
+ }
2695
+ if (fieldType === 'switch') {
2696
+ if (fieldAtomProps.variant !== 'black') lines.push(` variant: '${fieldAtomProps.variant}',`);
2697
+ }
2698
+ if (fieldType === 'date-picker' && fieldAtomProps.pickerType !== 'date') {
2699
+ lines.push(` pickerType: '${fieldAtomProps.pickerType}',`);
2700
+ }
2701
+ if (fieldType === 'time-picker' && fieldAtomProps.pickerType !== 'time') {
2702
+ lines.push(` pickerType: '${fieldAtomProps.pickerType}',`);
2703
+ }
2704
+ if (fieldType === 'input-number' && fieldAtomProps.innerButtons) {
2705
+ lines.push(' innerButtons: true,');
2706
+ }
2707
+ if (fieldType === 'textarea') {
2708
+ if (enums.fillMode === 'filled') {
2709
+ lines.push(` defaultValue: '${FORM_TEXTAREA_SAMPLE.slice(0, 48)}…',`);
2710
+ }
2711
+ if (enums.countOverflow === 'on') {
2712
+ lines.push(' showCount: true,');
2713
+ lines.push(' maxLength: 140,');
2714
+ lines.push(' enforceMaxLength: false,');
2715
+ }
2716
+ if (enums.state === 'disabled') lines.push(' disabled: true,');
2717
+ if (enums.minRows != null && enums.minRows !== 3) lines.push(` minRows: ${enums.minRows},`);
2718
+ if (enums.status === 'error') lines.push(' status: "error",');
2719
+ if (enums.resize === 'none') lines.push(' resize: "none",');
2720
+ }
2721
+ if (fieldType === 'slider') {
2722
+ const sliderDefaultValue = formatUsageLiteral(fieldAtomProps.defaultValue);
2723
+ const sliderMarks = formatUsageLiteral(fieldAtomProps.marks);
2724
+ if (fieldAtomProps.min !== SLIDER_PREVIEW_DEFAULTS.min) lines.push(` min: ${fieldAtomProps.min},`);
2725
+ if (fieldAtomProps.max !== SLIDER_PREVIEW_DEFAULTS.max) lines.push(` max: ${fieldAtomProps.max},`);
2726
+ if (fieldAtomProps.step !== SLIDER_PREVIEW_DEFAULTS.step) lines.push(` step: ${fieldAtomProps.step},`);
2727
+ if (sliderDefaultValue) lines.push(` defaultValue: ${sliderDefaultValue},`);
2728
+ if (fieldAtomProps.showTooltip === false) lines.push(' showTooltip: false,');
2729
+ if (sliderMarks) lines.push(` marks: ${sliderMarks},`);
2730
+ if (fieldAtomProps.disabled) lines.push(' disabled: true,');
2731
+ lines.push(' onChange: (value) => {},');
2732
+ lines.push(' onAfterChange: (value) => {},');
2733
+ }
2734
+ lines.push('}];');
2735
+ lines.push('');
2736
+ lines.push('<Form');
2737
+ if (labelPosition !== 'top') lines.push(` labelPosition="${labelPosition}"`);
2738
+ if (labelOptional) lines.push(' labelOptional');
2739
+ if (labelRequired) lines.push(' labelRequired');
2740
+ if (labelHelp) lines.push(' labelHelp');
2741
+ if (labelAi) lines.push(' labelAi');
2742
+ lines.push(' items={items}');
2743
+ lines.push('/>');
2744
+ return lines.join('\n');
2745
+ },
2746
+ },
2747
+
2748
+ 'form-field-stack': {
2749
+ component: FormFieldStack,
2750
+ tokenMap: FORM_FIELD_STACK_TOKEN_MAP,
2751
+ jsxSource: formFieldStackJsxRaw,
2752
+
2753
+ getPreviewAreaStyle: () => ({
2754
+ /* 默认 .detail-preview-area 为横向 flex,子级 scaler 会随内容收缩宽度;
2755
+ 改为纵向 + stretch,让 scaler 在横轴上占满预览区,内部 w-full 才能生效 */
2756
+ flexDirection: 'column',
2757
+ alignItems: 'stretch',
2758
+ justifyContent: 'flex-start',
2759
+ overflow: 'auto',
2760
+ padding: 0,
2761
+ }),
2762
+
2763
+ getPreviewScalerStyle: () => ({
2764
+ width: '100%',
2765
+ minWidth: 0,
2766
+ boxSizing: 'border-box',
2767
+ }),
2768
+
2769
+ getPreviewItemStyle: () => ({
2770
+ width: '100%',
2771
+ minWidth: 0,
2772
+ flex: '1 1 auto',
2773
+ alignSelf: 'stretch',
2774
+ display: 'flex',
2775
+ justifyContent: 'flex-start',
2776
+ alignItems: 'stretch',
2777
+ }),
2778
+
2779
+ configHiddenEnums: ['stackLayout'],
2780
+
2781
+ controls: [
2782
+ {
2783
+ id: 'stackLayout',
2784
+ label: '布局',
2785
+ type: 'seg',
2786
+ options: [
2787
+ { id: 'select-stack', label: '纯下拉' },
2788
+ { id: 'mixed-fields', label: '多类型' },
2789
+ ],
2790
+ default: 'select-stack',
2791
+ },
2792
+ ],
2793
+
2794
+ mapProps: (cv, enums) => {
2795
+ const stackLayout = cv.stackLayout || enums.stackLayout || 'select-stack';
2796
+ return {
2797
+ stackLayout,
2798
+ __previewKey: `form-field-stack-${stackLayout}`,
2799
+ };
2800
+ },
2801
+
2802
+ generateUsage: (enums, cv) => {
2803
+ const stackLayout = cv.stackLayout || enums.stackLayout || 'select-stack';
2804
+ const lines = ["import FormFieldStack from './components/FormFieldStack';", ''];
2805
+ lines.push('export default function Example() {');
2806
+ if (stackLayout === 'mixed-fields') {
2807
+ lines.push(' return <FormFieldStack stackLayout="mixed-fields" />;');
2808
+ } else {
2809
+ lines.push(' return <FormFieldStack />;');
2810
+ }
2811
+ lines.push('}');
2812
+ return lines.join('\n');
2813
+ },
2814
+ },
2815
+
2816
+ input: {
2817
+ component: InputPreview,
2818
+ tokenMap: INPUT_TOKEN_MAP,
2819
+ jsxSource: inputJsxRaw,
2820
+
2821
+ getPreviewAreaStyle: () => ({
2822
+ alignItems: 'flex-start',
2823
+ justifyContent: 'center',
2824
+ overflow: 'auto',
2825
+ paddingTop: '96px',
2826
+ }),
2827
+
2828
+ getPreviewItemStyle: () => ({
2829
+ width: '100%',
2830
+ display: 'flex',
2831
+ justifyContent: 'center',
2832
+ alignItems: 'flex-start',
2833
+ flex: '0 0 auto',
2834
+ }),
2835
+
2836
+ controls: [
2837
+ {
2838
+ id: 'addonType', label: '附加元素', type: 'seg',
2839
+ options: [
2840
+ { id: 'none', label: '无' },
2841
+ { id: 'prefix', label: '前缀' },
2842
+ { id: 'suffix', label: '后缀' },
2843
+ { id: 'both', label: '前+后' },
2844
+ ],
2845
+ default: 'none',
2846
+ },
2847
+ {
2848
+ id: 'state', label: '状态', type: 'seg',
2849
+ options: [
2850
+ { id: 'default', label: '默认' },
2851
+ { id: 'disabled', label: '禁用' },
2852
+ ],
2853
+ default: 'default',
2854
+ },
2855
+ {
2856
+ id: 'suggestionMode', label: 'AI推荐',
2857
+ type: 'seg',
2858
+ options: [
2859
+ { id: 'off', label: '隐藏' },
2860
+ { id: 'on', label: '显示' },
2861
+ ],
2862
+ default: 'off',
2863
+ },
2864
+ {
2865
+ id: 'suggestionType', label: '推荐形式',
2866
+ type: 'seg',
2867
+ options: [
2868
+ { id: 'single', label: '单条' },
2869
+ { id: 'multi', label: '多条' },
2870
+ ],
2871
+ default: 'single',
2872
+ hidden: ({ controlValues }) => (controlValues.suggestionMode || 'off') !== 'on',
2873
+ },
2874
+ ],
2875
+
2876
+ mapProps: (cv) => {
2877
+ const addon = cv.addonType || 'none';
2878
+ const st = cv.state || 'default';
2879
+ const hasPrefix = addon === 'prefix' || addon === 'both';
2880
+ const hasSuffix = addon === 'suffix' || addon === 'both';
2881
+ const showSuggestion = cv.suggestionMode === 'on';
2882
+ const isMultiSuggestion = cv.suggestionType === 'multi';
2883
+ return {
2884
+ prefix: hasPrefix ? <Search size={16} /> : undefined,
2885
+ suffix: hasSuffix ? <Eye size={16} /> : undefined,
2886
+ disabled: st === 'disabled',
2887
+ allowClear: true,
2888
+ aiSuggestion: showSuggestion && !isMultiSuggestion ? INPUT_SINGLE_SUGGESTIONS[0] : undefined,
2889
+ aiSuggestions: showSuggestion && isMultiSuggestion
2890
+ ? INPUT_MULTI_SUGGESTION_GROUPS[0]
2891
+ : undefined,
2892
+ placeholder: '请输入',
2893
+ };
2894
+ },
2895
+
2896
+ generateUsage: (enums, cv) => {
2897
+ const lines = [`import Input from './components/Input';`];
2898
+ const addon = cv.addonType || 'none';
2899
+ const st = cv.state || 'default';
2900
+ const props = [];
2901
+ if (enums.status && enums.status !== 'default') props.push(`status="${enums.status}"`);
2902
+ if (addon === 'prefix' || addon === 'both') props.push('prefix={<Search size={16} />}');
2903
+ if (addon === 'suffix' || addon === 'both') props.push('suffix={<Eye size={16} />}');
2904
+ if (st === 'disabled') props.push('disabled');
2905
+ if (cv.suggestionMode === 'on' && cv.suggestionType === 'single') {
2906
+ props.push('aiSuggestion="您好,您的问题已收到,抖音客服正在为您核实处理。"');
2907
+ props.push('onRefreshAiSuggestions={() => {}}');
2908
+ }
2909
+ if (cv.suggestionMode === 'on' && cv.suggestionType === 'multi') {
2910
+ props.push('aiSuggestions={["您好,正在为您核实,请稍候。", "抱歉给您带来不便,这边已经帮您加急处理。", "您好,您反馈的问题我们已经收到,建议您先提供订单号和问题截图,方便抖音客服尽快为您核实处理。"]}');
2911
+ props.push('onRefreshAiSuggestions={() => {}}');
2912
+ }
2913
+ props.push('placeholder="请输入"');
2914
+
2915
+ lines.push('');
2916
+ lines.push('<Input');
2917
+ props.forEach(p => lines.push(` ${p}`));
2918
+ lines.push('/>');
2919
+ return lines.join('\n');
2920
+ },
2921
+ },
2922
+
2923
+ textarea: {
2924
+ component: TextAreaPreview,
2925
+ tokenMap: TEXTAREA_TOKEN_MAP,
2926
+ jsxSource: textareaJsxRaw,
2927
+
2928
+ getPreviewAreaStyle: () => ({
2929
+ alignItems: 'flex-start',
2930
+ justifyContent: 'center',
2931
+ overflow: 'auto',
2932
+ paddingTop: '96px',
2933
+ }),
2934
+
2935
+ getPreviewItemStyle: () => ({
2936
+ width: '100%',
2937
+ display: 'flex',
2938
+ justifyContent: 'center',
2939
+ alignItems: 'flex-start',
2940
+ flex: '0 0 auto',
2941
+ }),
2942
+
2943
+ controls: [
2944
+ {
2945
+ id: 'fillMode',
2946
+ label: '内容',
2947
+ type: 'seg',
2948
+ options: [
2949
+ { id: 'empty', label: '空' },
2950
+ { id: 'filled', label: '有值' },
2951
+ ],
2952
+ default: 'empty',
2953
+ },
2954
+ {
2955
+ id: 'countOverflow',
2956
+ label: '超长计数',
2957
+ type: 'seg',
2958
+ options: [
2959
+ { id: 'off', label: '关' },
2960
+ { id: 'on', label: '开' },
2961
+ ],
2962
+ default: 'off',
2963
+ },
2964
+ {
2965
+ id: 'state',
2966
+ label: '状态',
2967
+ type: 'seg',
2968
+ options: [
2969
+ { id: 'default', label: '默认可用' },
2970
+ { id: 'disabled', label: '禁用' },
2971
+ ],
2972
+ default: 'default',
2973
+ },
2974
+ {
2975
+ id: 'suggestionMode',
2976
+ label: 'AI推荐',
2977
+ type: 'seg',
2978
+ options: [
2979
+ { id: 'off', label: '隐藏' },
2980
+ { id: 'on', label: '显示' },
2981
+ ],
2982
+ default: 'off',
2983
+ },
2984
+ {
2985
+ id: 'suggestionType',
2986
+ label: '推荐形式',
2987
+ type: 'seg',
2988
+ options: [
2989
+ { id: 'single', label: '单条' },
2990
+ { id: 'multi', label: '多条' },
2991
+ ],
2992
+ default: 'single',
2993
+ hidden: ({ controlValues }) => (controlValues.suggestionMode || 'off') !== 'on',
2994
+ },
2995
+ ],
2996
+
2997
+ mapProps: (cv) => {
2998
+ const showSuggestion = cv.suggestionMode === 'on';
2999
+ const isMultiSuggestion = cv.suggestionType === 'multi';
3000
+ return {
3001
+ fillMode: cv.fillMode || 'empty',
3002
+ countOverflow: cv.countOverflow === 'on',
3003
+ disabled: cv.state === 'disabled',
3004
+ aiSuggestion: showSuggestion && !isMultiSuggestion ? TEXTAREA_SINGLE_SUGGESTIONS[0] : undefined,
3005
+ aiSuggestions: showSuggestion && isMultiSuggestion ? TEXTAREA_MULTI_SUGGESTION_GROUPS[0] : undefined,
3006
+ };
3007
+ },
3008
+
3009
+ generateUsage: (enums, cv) => {
3010
+ const lines = [`import TextArea from './components/TextArea';`];
3011
+ const props = [];
3012
+ if (enums.variant && enums.variant !== 'default') props.push(`variant="${enums.variant}"`);
3013
+ if (enums.minRows != null && enums.minRows !== 3) props.push(`minRows={${enums.minRows}}`);
3014
+ if (enums.status && enums.status !== 'default') props.push(`status="${enums.status}"`);
3015
+ if (cv.state === 'disabled') props.push('disabled');
3016
+ props.push(enums.variant === 'code' ? 'placeholder="请输入 System Prompt、JSON 或代码"' : 'placeholder="请输入"');
3017
+ if (cv.countOverflow === 'on') {
3018
+ props.push('showCount');
3019
+ props.push('maxLength={140}');
3020
+ props.push('enforceMaxLength={false}');
3021
+ }
3022
+ if (cv.suggestionMode === 'on' && cv.suggestionType === 'single') {
3023
+ props.push('aiSuggestion="根据当前对话内容,建议先记录用户诉求、异常现象和下一步处理动作。"');
3024
+ props.push('onRefreshAiSuggestions={() => {}}');
3025
+ }
3026
+ if (cv.suggestionMode === 'on' && cv.suggestionType === 'multi') {
3027
+ props.push('aiSuggestions={["建议优先整理用户诉求、处理时效和需补充的材料信息。", "如果需要后续交接,可一并记录当前判责结论和预计回访时间。"]}');
3028
+ props.push('onRefreshAiSuggestions={() => {}}');
3029
+ }
3030
+ if (cv.fillMode === 'filled') {
3031
+ props.push(enums.variant === 'code'
3032
+ ? 'defaultValue={"# 角色\\n你是抖音安全中心的小安智能助手…"}'
3033
+ : 'defaultValue="我们非常高兴地向您介绍我们的最新产品 HiUI…"');
3034
+ }
3035
+ if (enums.resize === 'none') {
3036
+ props.push('resize="none"');
3037
+ }
3038
+ if (enums.fillHeight) {
3039
+ props.push('fillHeight');
3040
+ props.push('className="flex-1 min-h-0 w-full"');
3041
+ } else if (enums.variant === 'code') {
3042
+ props.push('className="w-[520px]"');
3043
+ }
3044
+ lines.push('');
3045
+ lines.push('<TextArea');
3046
+ props.forEach((p) => lines.push(` ${p}`));
3047
+ lines.push('/>');
3048
+ return lines.join('\n');
3049
+ },
3050
+ },
3051
+
3052
+ 'input-number': {
3053
+ component: InputNumberPreview,
3054
+ tokenMap: INPUT_NUMBER_TOKEN_MAP,
3055
+ jsxSource: inputNumberJsxRaw,
3056
+
3057
+ getPreviewAreaStyle: () => ({
3058
+ alignItems: 'flex-start',
3059
+ justifyContent: 'center',
3060
+ overflow: 'auto',
3061
+ paddingTop: '96px',
3062
+ }),
3063
+
3064
+ getPreviewItemStyle: () => ({
3065
+ width: '100%',
3066
+ display: 'flex',
3067
+ justifyContent: 'center',
3068
+ alignItems: 'flex-start',
3069
+ flex: '0 0 auto',
3070
+ }),
3071
+
3072
+ controls: [
3073
+ {
3074
+ id: 'buttonMode',
3075
+ label: '按钮',
3076
+ type: 'seg',
3077
+ options: [
3078
+ { id: 'outer', label: '外置' },
3079
+ { id: 'inner', label: '内置' },
3080
+ ],
3081
+ default: 'outer',
3082
+ },
3083
+ {
3084
+ id: 'content',
3085
+ label: '内容',
3086
+ type: 'seg',
3087
+ options: [
3088
+ { id: 'empty', label: '无值' },
3089
+ { id: 'filled', label: '有值' },
3090
+ ],
3091
+ default: 'empty',
3092
+ },
3093
+ {
3094
+ id: 'state',
3095
+ label: '状态',
3096
+ type: 'seg',
3097
+ options: [
3098
+ { id: 'default', label: '默认' },
3099
+ { id: 'disabled', label: '禁用' },
3100
+ ],
3101
+ default: 'default',
3102
+ },
3103
+ ],
3104
+
3105
+ mapProps: (cv, enums) => ({
3106
+ buttonMode: cv.buttonMode || 'outer',
3107
+ content: cv.content || 'empty',
3108
+ disabled: cv.state === 'disabled',
3109
+ status: enums.status || 'default',
3110
+ }),
3111
+
3112
+ generateUsage: (enums, cv) => {
3113
+ const lines = [`import InputNumber from './components/InputNumber';`];
3114
+ const props = [];
3115
+ const buttonMode = cv.buttonMode || 'outer';
3116
+ const content = cv.content || 'empty';
3117
+ if (enums.status && enums.status !== 'default') props.push(`status="${enums.status}"`);
3118
+ if (buttonMode === 'inner') props.push('innerButtons');
3119
+ if (cv.state === 'disabled') props.push('disabled');
3120
+ if (content === 'filled') props.push('defaultValue={12}');
3121
+ props.push('min={0}');
3122
+ props.push('max={99}');
3123
+ props.push('step={1}');
3124
+ props.push('placeholder="请输入"');
3125
+
3126
+ lines.push('');
3127
+ lines.push('<InputNumber');
3128
+ props.forEach((p) => lines.push(` ${p}`));
3129
+ lines.push('/>');
3130
+ return lines.join('\n');
3131
+ },
3132
+ },
3133
+
3134
+ radio: {
3135
+ component: RadioPreview,
3136
+ tokenMap: RADIO_TOKEN_MAP,
3137
+ jsxSource: radioJsxRaw,
3138
+
3139
+ controls: [
3140
+ {
3141
+ id: 'initialKey',
3142
+ label: '初始选中',
3143
+ type: 'seg',
3144
+ options: [
3145
+ { id: 'none', label: '无' },
3146
+ { id: 'a', label: 'A' },
3147
+ { id: 'b', label: 'B' },
3148
+ ],
3149
+ default: 'none',
3150
+ },
3151
+ {
3152
+ id: 'state',
3153
+ label: '交互',
3154
+ type: 'seg',
3155
+ options: [
3156
+ { id: 'default', label: '可点' },
3157
+ { id: 'disabled', label: '禁用' },
3158
+ ],
3159
+ default: 'default',
3160
+ },
3161
+ ],
3162
+
3163
+ mapProps: (cv) => ({
3164
+ initialKey: cv.initialKey || 'none',
3165
+ disabled: cv.state === 'disabled',
3166
+ }),
3167
+
3168
+ generateUsage: (enums, cv) => {
3169
+ const lines = [`import RadioGroup, { Radio } from './components/Radio';`];
3170
+ const props = [];
3171
+ if (enums.variant && enums.variant !== 'brand') props.push(`variant="${enums.variant}"`);
3172
+ if (enums.layout && enums.layout !== 'vertical') props.push(`layout="${enums.layout}"`);
3173
+ if (cv.initialKey === 'a') props.push('defaultValue="a"');
3174
+ if (cv.initialKey === 'b') props.push('defaultValue="b"');
3175
+ if (cv.state === 'disabled') props.push('disabled');
3176
+ props.push('onChange={(value) => {}}');
3177
+ lines.push('');
3178
+ lines.push('<RadioGroup');
3179
+ props.forEach((p) => lines.push(` ${p}`));
3180
+ lines.push('>');
3181
+ lines.push(' <Radio value="a">选项 A</Radio>');
3182
+ lines.push(' <Radio value="b">选项 B</Radio>');
3183
+ lines.push(' <Radio value="c" disabled>选项 C(单项禁用)</Radio>');
3184
+ lines.push('</RadioGroup>');
3185
+ return lines.join('\n');
3186
+ },
3187
+ },
3188
+
3189
+ checkbox: {
3190
+ component: CheckboxPreview,
3191
+ tokenMap: CHECKBOX_TOKEN_MAP,
3192
+ jsxSource: checkboxJsxRaw,
3193
+
3194
+ controls: [
3195
+ {
3196
+ id: 'previewMode',
3197
+ label: '预览',
3198
+ type: 'seg',
3199
+ options: [
3200
+ { id: 'group', label: '多选组' },
3201
+ { id: 'indeterminate', label: '半选' },
3202
+ ],
3203
+ default: 'group',
3204
+ },
3205
+ {
3206
+ id: 'initialKey',
3207
+ label: '初始选中',
3208
+ type: 'seg',
3209
+ options: [
3210
+ { id: 'none', label: '无' },
3211
+ { id: 'a', label: 'A' },
3212
+ { id: 'ab', label: 'A+B' },
3213
+ ],
3214
+ default: 'none',
3215
+ },
3216
+ {
3217
+ id: 'state',
3218
+ label: '交互',
3219
+ type: 'seg',
3220
+ options: [
3221
+ { id: 'default', label: '可点' },
3222
+ { id: 'disabled', label: '禁用' },
3223
+ ],
3224
+ default: 'default',
3225
+ },
3226
+ ],
3227
+
3228
+ mapProps: (cv) => ({
3229
+ previewMode: cv.previewMode || 'group',
3230
+ initialKey: cv.initialKey || 'none',
3231
+ disabled: cv.state === 'disabled',
3232
+ }),
3233
+
3234
+ generateUsage: (enums, cv) => {
3235
+ const lines = [`import CheckboxGroup, { Checkbox } from './components/Checkbox';`];
3236
+ const props = [];
3237
+ if (enums.variant && enums.variant !== 'brand') props.push(`variant="${enums.variant}"`);
3238
+ if (enums.size && enums.size !== 'md') props.push(`size="${enums.size}"`);
3239
+ if (enums.layout && enums.layout !== 'vertical') props.push(`layout="${enums.layout}"`);
3240
+ if (cv.state === 'disabled') props.push('disabled');
3241
+
3242
+ if (cv.previewMode === 'indeterminate') {
3243
+ lines.push('');
3244
+ lines.push('<Checkbox');
3245
+ props.forEach((p) => lines.push(` ${p}`));
3246
+ lines.push(' indeterminate');
3247
+ lines.push('>');
3248
+ lines.push(' 部分子项已选');
3249
+ lines.push('</Checkbox>');
3250
+ return lines.join('\n');
3251
+ }
3252
+
3253
+ if (cv.initialKey === 'a') props.push('defaultValue={["a"]}');
3254
+ if (cv.initialKey === 'ab') props.push('defaultValue={["a","b"]}');
3255
+ props.push('onChange={(values) => {}}');
3256
+ lines.push('');
3257
+ lines.push('<CheckboxGroup');
3258
+ props.forEach((p) => lines.push(` ${p}`));
3259
+ lines.push('>');
3260
+ lines.push(' <Checkbox value="a">选项 A</Checkbox>');
3261
+ lines.push(' <Checkbox value="b">选项 B</Checkbox>');
3262
+ lines.push(' <Checkbox value="c" disabled>选项 C(单项禁用)</Checkbox>');
3263
+ lines.push('</CheckboxGroup>');
3264
+ return lines.join('\n');
3265
+ },
3266
+ },
3267
+
3268
+ select: {
3269
+ component: SelectPreview,
3270
+ tokenMap: SELECT_TOKEN_MAP,
3271
+ jsxSource: selectJsxRaw,
3272
+ configHiddenEnums: ['mode', 'tagVariant', 'tagSize'],
3273
+
3274
+ controls: [
3275
+ {
3276
+ id: 'selectType',
3277
+ label: '类型',
3278
+ type: 'seg',
3279
+ options: [
3280
+ { id: 'default', label: '默认' },
3281
+ { id: 'tag', label: '标签' },
3282
+ ],
3283
+ default: 'default',
3284
+ },
3285
+ {
3286
+ id: 'initial',
3287
+ label: '初始值',
3288
+ type: 'seg',
3289
+ options: [
3290
+ { id: 'empty', label: '空' },
3291
+ { id: 'selected', label: '已选' },
3292
+ ],
3293
+ default: 'empty',
3294
+ },
3295
+ {
3296
+ id: 'optionMode',
3297
+ label: '选项量',
3298
+ type: 'seg',
3299
+ options: [
3300
+ { id: 'few', label: '少' },
3301
+ { id: 'many', label: '多' },
3302
+ ],
3303
+ default: 'few',
3304
+ hidden: ({ controlValues }) => (controlValues.selectType || 'default') === 'tag',
3305
+ },
3306
+ {
3307
+ id: 'state',
3308
+ label: '交互',
3309
+ type: 'seg',
3310
+ options: [
3311
+ { id: 'default', label: '可点' },
3312
+ { id: 'disabled', label: '禁用' },
3313
+ ],
3314
+ default: 'default',
3315
+ },
3316
+ {
3317
+ id: 'suggestionMode',
3318
+ label: 'AI推荐',
3319
+ type: 'seg',
3320
+ options: [
3321
+ { id: 'off', label: '隐藏' },
3322
+ { id: 'on', label: '显示' },
3323
+ ],
3324
+ default: 'off',
3325
+ hidden: ({ controlValues }) => (controlValues.selectType || 'default') === 'tag',
3326
+ },
3327
+ {
3328
+ id: 'suggestionType',
3329
+ label: '推荐形式',
3330
+ type: 'seg',
3331
+ options: [
3332
+ { id: 'single', label: '单条' },
3333
+ { id: 'multi', label: '多条' },
3334
+ ],
3335
+ default: 'single',
3336
+ hidden: ({ controlValues }) => (
3337
+ (controlValues.selectType || 'default') === 'tag'
3338
+ || (controlValues.suggestionMode || 'off') !== 'on'
3339
+ ),
3340
+ },
3341
+ ],
3342
+
3343
+ mapProps: (cv) => {
3344
+ const showSuggestion = cv.suggestionMode === 'on' && (cv.selectType || 'default') === 'default';
3345
+ const isMultiSuggestion = cv.suggestionType === 'multi';
3346
+ return {
3347
+ selectType: cv.selectType || 'default',
3348
+ initial: cv.initial || 'empty',
3349
+ optionMode: cv.optionMode || 'few',
3350
+ disabled: cv.state === 'disabled',
3351
+ aiSuggestion: showSuggestion && !isMultiSuggestion ? SELECT_AI_SINGLE_SUGGESTIONS[0] : undefined,
3352
+ aiSuggestions: showSuggestion && isMultiSuggestion ? SELECT_AI_MULTI_SUGGESTION_GROUPS[0] : undefined,
3353
+ };
3354
+ },
3355
+
3356
+ generateUsage: (enums, cv) => {
3357
+ const lines = [`import Select from './components/Select';`];
3358
+ const props = [];
3359
+ const selectType = cv.selectType || 'default';
3360
+ const isTagSelect = selectType === 'tag';
3361
+ const showSuggestion = cv.suggestionMode === 'on' && !isTagSelect;
3362
+ const isMultiSuggestion = cv.suggestionType === 'multi';
3363
+ if (enums.status && enums.status !== 'default') props.push(`status="${enums.status}"`);
3364
+ if (isTagSelect) props.push('mode="tag"');
3365
+ if (cv.initial === 'selected') {
3366
+ props.push(isTagSelect ? 'defaultValue={["brand", "risk"]}' : 'defaultValue="js"');
3367
+ }
3368
+ if (cv.state === 'disabled') props.push('disabled');
3369
+ props.push('allowClear');
3370
+ if (showSuggestion && !isMultiSuggestion) {
3371
+ props.push('aiSuggestion="江苏省"');
3372
+ props.push('onRefreshAiSuggestions={() => {}}');
3373
+ }
3374
+ if (showSuggestion && isMultiSuggestion) {
3375
+ props.push('aiSuggestions={["江苏省", "上海市", "浙江省"]}');
3376
+ props.push('onRefreshAiSuggestions={() => {}}');
3377
+ }
3378
+ props.push('placeholder="请选择"');
3379
+ lines.push('');
3380
+ lines.push('const options = [');
3381
+ if (isTagSelect) {
3382
+ lines.push(" { value: 'brand', label: '标签', variant: 'grey' },");
3383
+ lines.push(" { value: 'risk', label: '标签', variant: 'grey' },");
3384
+ lines.push(" { value: 'service', label: '标签', variant: 'grey' },");
3385
+ } else {
3386
+ lines.push(" { value: 'zj', label: '浙江省' },");
3387
+ lines.push(" { value: 'js', label: '江苏省' },");
3388
+ lines.push(" { value: 'sh', label: '上海市' },");
3389
+ }
3390
+ lines.push('];');
3391
+ lines.push('');
3392
+ lines.push('<Select');
3393
+ props.forEach((p) => lines.push(` ${p}`));
3394
+ lines.push(' options={options}');
3395
+ lines.push(' onChange={(value, option) => {}}');
3396
+ lines.push('/>');
3397
+ return lines.join('\n');
3398
+ },
3399
+ },
3400
+
3401
+ switch: {
3402
+ component: SwitchPreview,
3403
+ tokenMap: SWITCH_TOKEN_MAP,
3404
+ jsxSource: switchJsxRaw,
3405
+
3406
+ controls: [
3407
+ {
3408
+ id: 'initial',
3409
+ label: '初始状态',
3410
+ type: 'seg',
3411
+ options: [
3412
+ { id: 'off', label: '关' },
3413
+ { id: 'on', label: '开' },
3414
+ ],
3415
+ default: 'off',
3416
+ },
3417
+ {
3418
+ id: 'state',
3419
+ label: '交互',
3420
+ type: 'seg',
3421
+ options: [
3422
+ { id: 'default', label: '可点' },
3423
+ { id: 'disabled', label: '禁用' },
3424
+ ],
3425
+ default: 'default',
3426
+ },
3427
+ ],
3428
+
3429
+ mapProps: (cv) => ({
3430
+ defaultChecked: cv.initial === 'on',
3431
+ disabled: cv.state === 'disabled',
3432
+ }),
3433
+
3434
+ generateUsage: (enums, cv) => {
3435
+ const lines = [`import Switch from './components/Switch';`];
3436
+ const props = [];
3437
+ if (enums.variant && enums.variant !== 'brand') props.push(`variant="${enums.variant}"`);
3438
+ if (cv.initial === 'on') props.push('defaultChecked');
3439
+ if (cv.state === 'disabled') props.push('disabled');
3440
+ lines.push('');
3441
+ lines.push('<Switch');
3442
+ props.forEach((p) => lines.push(` ${p}`));
3443
+ lines.push('/>');
3444
+ return lines.join('\n');
3445
+ },
3446
+ },
3447
+
3448
+ slider: {
3449
+ component: SliderPreview,
3450
+ tokenMap: SLIDER_TOKEN_MAP,
3451
+ jsxSource: sliderJsxRaw,
3452
+
3453
+ controls: buildSliderPreviewControls(),
3454
+
3455
+ normalizeControlValues: ({ changedControlId, nextControlValues, prevControlValues }) => (
3456
+ normalizeSliderPreviewControls(nextControlValues, prevControlValues, changedControlId)
3457
+ ),
3458
+
3459
+ mapProps: (cv) => ({
3460
+ ...buildSliderPreviewProps(cv),
3461
+ }),
3462
+
3463
+ generateUsage: (enums, cv) => {
3464
+ const lines = [`import Slider from './components/Slider';`];
3465
+ const sliderProps = buildSliderPreviewProps(cv);
3466
+ const props = [];
3467
+
3468
+ if (sliderProps.min !== SLIDER_PREVIEW_DEFAULTS.min) props.push(`min={${sliderProps.min}}`);
3469
+ if (sliderProps.max !== SLIDER_PREVIEW_DEFAULTS.max) props.push(`max={${sliderProps.max}}`);
3470
+ if (sliderProps.step !== SLIDER_PREVIEW_DEFAULTS.step) props.push(`step={${sliderProps.step}}`);
3471
+ props.push(`defaultValue={${formatUsageLiteral(sliderProps.defaultValue)}}`);
3472
+ if (sliderProps.showTooltip === false) props.push('showTooltip={false}');
3473
+ if (sliderProps.marks) props.push(`marks={${formatUsageLiteral(sliderProps.marks)}}`);
3474
+ if (sliderProps.disabled) props.push('disabled');
3475
+ props.push('onChange={(value) => {}}');
3476
+ props.push('onAfterChange={(value) => {}}');
3477
+
3478
+ lines.push('');
3479
+ lines.push('<Slider');
3480
+ props.forEach((p) => lines.push(` ${p}`));
3481
+ lines.push('/>');
3482
+ return lines.join('\n');
3483
+ },
3484
+ },
3485
+
3486
+ modal: {
3487
+ component: Modal,
3488
+ tokenMap: MODAL_TOKEN_MAP,
3489
+ jsxSource: modalJsxRaw,
3490
+
3491
+ controls: [
3492
+ {
3493
+ id: 'subtitleMode',
3494
+ label: '副标题',
3495
+ type: 'seg',
3496
+ options: [
3497
+ { id: 'on', label: '显示' },
3498
+ { id: 'off', label: '隐藏' },
3499
+ ],
3500
+ default: 'on',
3501
+ },
3502
+ {
3503
+ id: 'footerHintMode',
3504
+ label: '底部提示',
3505
+ type: 'seg',
3506
+ options: [
3507
+ { id: 'on', label: '显示' },
3508
+ { id: 'off', label: '隐藏' },
3509
+ ],
3510
+ default: 'on',
3511
+ },
3512
+ ],
3513
+
3514
+ mapProps: (cv, enums) => {
3515
+ const layout = enums?.layout || 'center';
3516
+ return {
3517
+ layout,
3518
+ size: enums?.size || 'md',
3519
+ subtitle: cv.subtitleMode === 'off' ? null : '模态副标题',
3520
+ showFooterHint: cv.footerHintMode !== 'off',
3521
+ /* fullscreen 变体需要 mt-auto 停靠底部,通过 className 注入 */
3522
+ className: layout === 'fullscreen' ? 'mt-auto' : '',
3523
+ onClose: () => {},
3524
+ onCancel: () => {},
3525
+ onConfirm: () => {},
3526
+ };
3527
+ },
3528
+
3529
+ /* center → 按 size 给定固定宽度,高度自适应
3530
+ * fullscreen → previewArea 改为 flex-col,scaler 撑满区域高度,
3531
+ * Modal 作为 scaler 的直接 flex 子元素、mt-auto 停靠底部,
3532
+ * max-h-[calc(100%-24px)] 此时可正确计算(parent = scaler 有定高)*/
3533
+ getPreviewItemStyle: ({ enumValues } = {}) => {
3534
+ if ((enumValues?.layout || 'center') === 'fullscreen') {
3535
+ return null; // Modal 直接作为 scaler 子节点,无需额外 wrapper
3536
+ }
3537
+ const widthMap = { sm: 450, md: 684, lg: 920 };
3538
+ const w = widthMap[enumValues?.size] || widthMap.md;
3539
+ return { width: `${w}px`, maxWidth: '100%' };
3540
+ },
3541
+
3542
+ getPreviewAreaStyle: ({ enumValues } = {}) => {
3543
+ if ((enumValues?.layout || 'center') === 'fullscreen') {
3544
+ return { padding: 0, display: 'flex', flexDirection: 'column', alignItems: 'stretch', overflow: 'hidden' };
3545
+ }
3546
+ return {};
3547
+ },
3548
+
3549
+ getPreviewScalerStyle: ({ enumValues } = {}) => {
3550
+ if ((enumValues?.layout || 'center') === 'fullscreen') {
3551
+ return {
3552
+ alignSelf: 'stretch',
3553
+ flex: 1,
3554
+ display: 'flex',
3555
+ flexDirection: 'column',
3556
+ alignItems: 'stretch',
3557
+ };
3558
+ }
3559
+ return {};
3560
+ },
3561
+
3562
+ generateUsage: (enums, cv) => {
3563
+ const lines = [`import Modal from './components/Modal';`];
3564
+ const layout = enums?.layout || 'center';
3565
+ const sub = cv.subtitleMode === 'off';
3566
+ const noHint = cv.footerHintMode === 'off';
3567
+ lines.push('');
3568
+ if (layout === 'fullscreen') {
3569
+ lines.push('/* 外层须是 flex-col 定高容器,面板通过 mt-auto 停靠底部 */');
3570
+ lines.push('<div className="fixed inset-0 flex flex-col">');
3571
+ lines.push(' <Modal');
3572
+ lines.push(' layout="fullscreen"');
3573
+ lines.push(' title="模态标题"');
3574
+ lines.push(sub ? ' subtitle={null}' : ' subtitle="模态副标题"');
3575
+ if (noHint) lines.push(' showFooterHint={false}');
3576
+ lines.push(' className="mt-auto"');
3577
+ lines.push(' onClose={() => {}}');
3578
+ lines.push(' onCancel={() => {}}');
3579
+ lines.push(' onConfirm={() => {}}');
3580
+ lines.push(' >');
3581
+ lines.push(' {/* 内容区域 */}');
3582
+ lines.push(' </Modal>');
3583
+ lines.push('</div>');
3584
+ } else {
3585
+ lines.push('<Modal');
3586
+ if (enums.size && enums.size !== 'md') lines.push(` size="${enums.size}"`);
3587
+ lines.push(' title="模态标题"');
3588
+ lines.push(sub ? ' subtitle={null}' : ' subtitle="模态副标题"');
3589
+ if (noHint) lines.push(' showFooterHint={false}');
3590
+ lines.push(' onClose={() => {}}');
3591
+ lines.push(' onCancel={() => {}}');
3592
+ lines.push(' onConfirm={() => {}}');
3593
+ lines.push('>');
3594
+ lines.push(' {/* 内容区域 */}');
3595
+ lines.push('</Modal>');
3596
+ }
3597
+ return lines.join('\n');
3598
+ },
3599
+ },
3600
+ sheet: {
3601
+ component: Sheet,
3602
+ tokenMap: SHEET_TOKEN_MAP,
3603
+ jsxSource: sheetJsxRaw,
3604
+
3605
+ controls: [
3606
+ {
3607
+ id: 'subtitleMode',
3608
+ label: '副标题',
3609
+ type: 'seg',
3610
+ options: [
3611
+ { id: 'on', label: '显示' },
3612
+ { id: 'off', label: '隐藏' },
3613
+ ],
3614
+ default: 'on',
3615
+ },
3616
+ {
3617
+ id: 'footerHintMode',
3618
+ label: '底部提示',
3619
+ type: 'seg',
3620
+ options: [
3621
+ { id: 'on', label: '显示' },
3622
+ { id: 'off', label: '隐藏' },
3623
+ ],
3624
+ default: 'on',
3625
+ },
3626
+ ],
3627
+
3628
+ mapProps: (cv, enums) => ({
3629
+ size: enums?.size || 'md',
3630
+ subtitle: cv.subtitleMode === 'off' ? null : '侧边副标题',
3631
+ showFooterHint: cv.footerHintMode !== 'off',
3632
+ onClose: () => {},
3633
+ onCancel: () => {},
3634
+ onConfirm: () => {},
3635
+ }),
3636
+
3637
+ getPreviewAreaStyle: () => ({
3638
+ padding: 0,
3639
+ alignItems: 'stretch',
3640
+ justifyContent: 'flex-end',
3641
+ }),
3642
+
3643
+ getPreviewScalerStyle: () => ({
3644
+ alignSelf: 'stretch',
3645
+ display: 'flex',
3646
+ alignItems: 'stretch',
3647
+ justifyContent: 'flex-end',
3648
+ transformOrigin: 'right top',
3649
+ }),
3650
+
3651
+ getPreviewItemStyle: ({ enumValues } = {}) => {
3652
+ const widthMap = { sm: 400, md: 560, lg: 800 };
3653
+ const w = widthMap[enumValues?.size] || widthMap.md;
3654
+ return { width: `${w}px`, maxWidth: '100%', alignSelf: 'stretch' };
3655
+ },
3656
+
3657
+ generateUsage: (enums, cv) => {
3658
+ const lines = [`import Sheet from './components/Sheet';`];
3659
+ const sub = cv.subtitleMode === 'off';
3660
+ const noHint = cv.footerHintMode === 'off';
3661
+ lines.push('');
3662
+ lines.push('<Sheet');
3663
+ if (enums.size && enums.size !== 'md') lines.push(` size="${enums.size}"`);
3664
+ lines.push(' title="侧边标题"');
3665
+ lines.push(sub ? ' subtitle={null}' : ' subtitle="侧边副标题"');
3666
+ if (noHint) lines.push(' showFooterHint={false}');
3667
+ lines.push(' onClose={() => {}}');
3668
+ lines.push(' onCancel={() => {}}');
3669
+ lines.push(' onConfirm={() => {}}');
3670
+ lines.push('>');
3671
+ lines.push(' {/* 内容区域 */}');
3672
+ lines.push('</Sheet>');
3673
+ return lines.join('\n');
3674
+ },
3675
+ },
3676
+ upload: {
3677
+ component: Upload,
3678
+ tokenMap: UPLOAD_TOKEN_MAP,
3679
+ jsxSource: uploadJsxRaw,
3680
+
3681
+ controls: [
3682
+ {
3683
+ id: 'content',
3684
+ label: '内容',
3685
+ type: 'seg',
3686
+ options: [
3687
+ { id: 'empty', label: '无图' },
3688
+ { id: 'filled', label: '有图' },
3689
+ ],
3690
+ default: 'filled',
3691
+ },
3692
+ {
3693
+ id: 'state',
3694
+ label: '状态',
3695
+ type: 'seg',
3696
+ options: [
3697
+ { id: 'default', label: '默认' },
3698
+ { id: 'hover', label: '悬浮' },
3699
+ { id: 'uploading', label: '上传中' },
3700
+ { id: 'error', label: '异常' },
3701
+ { id: 'disabled', label: '禁用' },
3702
+ ],
3703
+ default: 'default',
3704
+ },
3705
+ ],
3706
+
3707
+ mapProps: (cv) => {
3708
+ const state = cv.state || 'default';
3709
+ const content = cv.content || 'filled';
3710
+ const disabled = state === 'disabled';
3711
+
3712
+ const baseA = { uid: 'img-a', name: 'cover-a.png', url: UPLOAD_SAMPLE_IMAGE_A, status: 'done' };
3713
+ const baseB = { uid: 'img-b', name: 'cover-b.png', url: UPLOAD_SAMPLE_IMAGE_B, status: 'done' };
3714
+
3715
+ let previewList = [];
3716
+ if (content !== 'empty') {
3717
+ if (state === 'uploading') {
3718
+ previewList = [baseA, { ...baseB, status: 'uploading', percent: 56 }];
3719
+ } else if (state === 'error') {
3720
+ previewList = [baseA, { ...baseB, status: 'error' }];
3721
+ } else if (state === 'hover') {
3722
+ previewList = [{ ...baseA, showRemove: true }];
3723
+ } else {
3724
+ previewList = [baseA];
3725
+ }
3726
+ }
3727
+
3728
+ return {
3729
+ accept: 'image/*',
3730
+ multiple: true,
3731
+ limit: 3,
3732
+ disabled,
3733
+ defaultValue: previewList,
3734
+ __previewKey: `upload-${content}-${state}`,
3735
+ };
3736
+ },
3737
+
3738
+ generateUsage: (enums, cv) => {
3739
+ const lines = [`import Upload from './components/Upload';`];
3740
+ const state = cv.state || 'default';
3741
+ const content = cv.content || 'filled';
3742
+ const props = [];
3743
+
3744
+ props.push('accept="image/*"');
3745
+ props.push('multiple');
3746
+ props.push('limit={3}');
3747
+
3748
+ if (state === 'disabled') {
3749
+ props.push('disabled');
3750
+ }
3751
+
3752
+ if (content === 'filled') {
3753
+ props.push(`defaultValue={[{ uid: 'img-1', name: 'cover.png', url: 'https://example.com/cover.png', status: 'done' }]}`);
3754
+ }
3755
+
3756
+ props.push('onChange={(fileList) => {}}');
3757
+
3758
+ lines.push('');
3759
+ lines.push('<Upload');
3760
+ props.forEach((p) => lines.push(` ${p}`));
3761
+ lines.push('/>');
3762
+ return lines.join('\n');
3763
+ },
3764
+ },
3765
+
3766
+ 'date-picker': {
3767
+ component: DatePicker,
3768
+ tokenMap: DATEPICKER_TOKEN_MAP,
3769
+ jsxSource: datePickerJsxRaw,
3770
+
3771
+ getPreviewItemStyle: ({ controlValues }) => {
3772
+ const type = controlValues.previewType || 'date';
3773
+ const isOpen = controlValues.panel !== 'closed';
3774
+ const panelHeight = (type === 'datetime' || type === 'datetimerange') ? 348 : 312;
3775
+ const panelGap = 4;
3776
+
3777
+ if (isOpen) {
3778
+ return {
3779
+ transform: `translateY(-${Math.round((panelHeight + panelGap) / 2)}px)`,
3780
+ transition: 'transform 180ms ease',
3781
+ };
3782
+ }
3783
+
3784
+ return {
3785
+ transform: 'translateY(-56px)',
3786
+ transition: 'transform 180ms ease',
3787
+ };
3788
+ },
3789
+
3790
+ controls: [
3791
+ {
3792
+ id: 'previewType',
3793
+ label: '类型',
3794
+ type: 'seg',
3795
+ options: [
3796
+ { id: 'date', label: '日期' },
3797
+ { id: 'datetime', label: '日期时间' },
3798
+ { id: 'daterange', label: '日期范围' },
3799
+ { id: 'datetimerange', label: '日期时间范围' },
3800
+ ],
3801
+ default: 'date',
3802
+ },
3803
+ {
3804
+ id: 'valueState',
3805
+ label: '内容',
3806
+ type: 'seg',
3807
+ options: [
3808
+ { id: 'empty', label: '无值' },
3809
+ { id: 'filled', label: '有值' },
3810
+ ],
3811
+ default: 'empty',
3812
+ },
3813
+ {
3814
+ id: 'state',
3815
+ label: '状态',
3816
+ type: 'seg',
3817
+ options: [
3818
+ { id: 'default', label: '默认' },
3819
+ { id: 'disabled', label: '禁用' },
3820
+ ],
3821
+ default: 'default',
3822
+ },
3823
+ {
3824
+ id: 'panel',
3825
+ label: '面板',
3826
+ type: 'seg',
3827
+ options: [
3828
+ { id: 'open', label: '展开' },
3829
+ { id: 'closed', label: '收起' },
3830
+ ],
3831
+ default: 'open',
3832
+ },
3833
+ ],
3834
+
3835
+ mapProps: (cv) => {
3836
+ const type = cv.previewType || 'date';
3837
+ const isFilled = cv.valueState === 'filled';
3838
+ return {
3839
+ type,
3840
+ disabled: cv.state === 'disabled',
3841
+ value: isFilled ? DATEPICKER_SAMPLE_VALUE[type] : undefined,
3842
+ defaultOpen: cv.panel !== 'closed',
3843
+ };
3844
+ },
3845
+
3846
+ generateUsage: (enums, cv) => {
3847
+ const lines = [`import DatePicker from './components/DatePicker';`];
3848
+ const type = cv.previewType || enums.type || 'date';
3849
+ const isFilled = cv.valueState === 'filled';
3850
+ const props = [];
3851
+
3852
+ props.push(`type="${type}"`);
3853
+
3854
+ if (cv.state === 'disabled') {
3855
+ props.push('disabled');
3856
+ }
3857
+
3858
+ if (cv.panel !== 'closed') {
3859
+ props.push('defaultOpen');
3860
+ }
3861
+
3862
+ if (isFilled) {
3863
+ const sample = DATEPICKER_SAMPLE_VALUE[type];
3864
+ if (Array.isArray(sample)) {
3865
+ props.push(`defaultValue={["${sample[0]}", "${sample[1]}"]}`);
3866
+ } else {
3867
+ props.push(`defaultValue="${sample}"`);
3868
+ }
3869
+ }
3870
+
3871
+ lines.push('');
3872
+ lines.push('<DatePicker');
3873
+ props.forEach((p) => lines.push(` ${p}`));
3874
+ lines.push('/>');
3875
+ return lines.join('\n');
3876
+ },
3877
+ },
3878
+
3879
+ 'time-picker': {
3880
+ component: TimePicker,
3881
+ tokenMap: TIMEPICKER_TOKEN_MAP,
3882
+ jsxSource: timePickerJsxRaw,
3883
+
3884
+ getPreviewItemStyle: ({ controlValues }) => {
3885
+ const type = controlValues.previewType || 'time';
3886
+ const isOpen = controlValues.panel !== 'closed';
3887
+ const panelHeight = type === 'timerange' ? 304 : 252;
3888
+ const panelGap = 4;
3889
+
3890
+ if (isOpen) {
3891
+ return {
3892
+ transform: `translateY(-${Math.round((panelHeight + panelGap) / 2)}px)`,
3893
+ transition: 'transform 180ms ease',
3894
+ };
3895
+ }
3896
+
3897
+ return {
3898
+ transform: 'translateY(-56px)',
3899
+ transition: 'transform 180ms ease',
3900
+ };
3901
+ },
3902
+
3903
+ controls: [
3904
+ {
3905
+ id: 'previewType',
3906
+ label: '类型',
3907
+ type: 'seg',
3908
+ options: [
3909
+ { id: 'time', label: '时间' },
3910
+ { id: 'timerange', label: '时间范围' },
3911
+ ],
3912
+ default: 'time',
3913
+ },
3914
+ {
3915
+ id: 'valueState',
3916
+ label: '内容',
3917
+ type: 'seg',
3918
+ options: [
3919
+ { id: 'empty', label: '无值' },
3920
+ { id: 'filled', label: '有值' },
3921
+ ],
3922
+ default: 'empty',
3923
+ },
3924
+ {
3925
+ id: 'state',
3926
+ label: '状态',
3927
+ type: 'seg',
3928
+ options: [
3929
+ { id: 'default', label: '默认' },
3930
+ { id: 'disabled', label: '禁用' },
3931
+ ],
3932
+ default: 'default',
3933
+ },
3934
+ {
3935
+ id: 'panel',
3936
+ label: '面板',
3937
+ type: 'seg',
3938
+ options: [
3939
+ { id: 'open', label: '展开' },
3940
+ { id: 'closed', label: '收起' },
3941
+ ],
3942
+ default: 'open',
3943
+ },
3944
+ ],
3945
+
3946
+ mapProps: (cv) => {
3947
+ const type = cv.previewType || 'time';
3948
+ const isFilled = cv.valueState === 'filled';
3949
+ return {
3950
+ type,
3951
+ disabled: cv.state === 'disabled',
3952
+ defaultValue: isFilled ? TIMEPICKER_SAMPLE_VALUE[type] : undefined,
3953
+ defaultOpen: cv.panel !== 'closed',
3954
+ };
3955
+ },
3956
+
3957
+ generateUsage: (enums, cv) => {
3958
+ const lines = [`import TimePicker from './components/TimePicker';`];
3959
+ const type = cv.previewType || enums.type || 'time';
3960
+ const isFilled = cv.valueState === 'filled';
3961
+ const props = [];
3962
+
3963
+ props.push(`type="${type}"`);
3964
+
3965
+ if (cv.state === 'disabled') {
3966
+ props.push('disabled');
3967
+ }
3968
+
3969
+ if (cv.panel !== 'closed') {
3970
+ props.push('defaultOpen');
3971
+ }
3972
+
3973
+ if (isFilled) {
3974
+ const sample = TIMEPICKER_SAMPLE_VALUE[type];
3975
+ if (Array.isArray(sample)) {
3976
+ props.push(`defaultValue={["${sample[0]}", "${sample[1]}"]}`);
3977
+ } else {
3978
+ props.push(`defaultValue="${sample}"`);
3979
+ }
3980
+ }
3981
+
3982
+ lines.push('');
3983
+ lines.push('<TimePicker');
3984
+ props.forEach((p) => lines.push(` ${p}`));
3985
+ lines.push('/>');
3986
+ return lines.join('\n');
3987
+ },
3988
+ },
3989
+
3990
+ 'tag-input': {
3991
+ component: TagInputPreview,
3992
+ tokenMap: TAGINPUT_TOKEN_MAP,
3993
+ jsxSource: tagInputJsxRaw,
3994
+
3995
+ getPreviewAreaStyle: () => ({
3996
+ alignItems: 'flex-start',
3997
+ justifyContent: 'center',
3998
+ overflow: 'auto',
3999
+ paddingTop: '96px',
4000
+ }),
4001
+
4002
+ getPreviewItemStyle: () => ({
4003
+ width: '100%',
4004
+ display: 'flex',
4005
+ justifyContent: 'center',
4006
+ alignItems: 'flex-start',
4007
+ flex: '0 0 auto',
4008
+ }),
4009
+
4010
+ controls: [
4011
+ {
4012
+ id: 'content',
4013
+ label: '内容',
4014
+ type: 'seg',
4015
+ options: [
4016
+ { id: 'empty', label: '空' },
4017
+ { id: 'filled', label: '有值' },
4018
+ ],
4019
+ default: 'filled',
4020
+ },
4021
+ {
4022
+ id: 'widthMode',
4023
+ label: '宽度',
4024
+ type: 'seg',
4025
+ options: [
4026
+ { id: 'default', label: '默认' },
4027
+ { id: 'narrow', label: '窄' },
4028
+ { id: 'wide', label: '宽' },
4029
+ ],
4030
+ default: 'default',
4031
+ },
4032
+ {
4033
+ id: 'state',
4034
+ label: '交互',
4035
+ type: 'seg',
4036
+ options: [
4037
+ { id: 'default', label: '可点' },
4038
+ { id: 'disabled', label: '禁用' },
4039
+ ],
4040
+ default: 'default',
4041
+ },
4042
+ {
4043
+ id: 'suggestionMode',
4044
+ label: 'AI推荐',
4045
+ type: 'seg',
4046
+ options: [
4047
+ { id: 'off', label: '隐藏' },
4048
+ { id: 'on', label: '显示' },
4049
+ ],
4050
+ default: 'on',
4051
+ },
4052
+ ],
4053
+
4054
+ mapProps: (cv, enums) => ({
4055
+ content: cv.content || 'filled',
4056
+ widthMode: cv.widthMode || 'default',
4057
+ disabled: cv.state === 'disabled',
4058
+ size: enums.size || 'md',
4059
+ status: enums.status || 'default',
4060
+ tagVariant: enums.tagVariant || 'grey',
4061
+ tagSize: enums.tagSize || 'm',
4062
+ showAiSuggestions: (cv.suggestionMode || 'on') === 'on',
4063
+ }),
4064
+
4065
+ generateUsage: (enums, cv) => {
4066
+ const lines = [`import TagInput from './components/TagInput';`];
4067
+ const content = cv.content || 'filled';
4068
+ const widthMode = cv.widthMode || 'default';
4069
+ const props = [];
4070
+ if (enums.status && enums.status !== 'default') props.push(`status="${enums.status}"`);
4071
+ if (enums.tagVariant && enums.tagVariant !== 'grey') props.push(`tagVariant="${enums.tagVariant}"`);
4072
+ if (enums.tagSize && enums.tagSize !== 'm') props.push(`tagSize="${enums.tagSize}"`);
4073
+ if (cv.state === 'disabled') props.push('disabled');
4074
+ if (widthMode === 'narrow') props.push('className="!w-[220px]"');
4075
+ if (widthMode === 'wide') props.push('className="!w-[420px]"');
4076
+ props.push('placeholder="请输入"');
4077
+ if (content === 'filled') {
4078
+ props.push('defaultValue={["标签", "标签", "标签", "标签", "标签", "标签"]}');
4079
+ }
4080
+ if ((cv.suggestionMode || 'on') === 'on') {
4081
+ props.push('aiSuggestions={[["改签", "门店变更"], ["退款", "售后处理"], ["物流异常", "催办"]]}');
4082
+ props.push('onRefreshAiSuggestions={() => {}}');
4083
+ }
4084
+
4085
+ lines.push('');
4086
+ lines.push('<TagInput');
4087
+ props.forEach((p) => lines.push(` ${p}`));
4088
+ lines.push('/>');
4089
+ return lines.join('\n');
4090
+ },
4091
+ },
4092
+
4093
+ tag: {
4094
+ component: TagGridPreview,
4095
+ tokenMap: TAG_TOKEN_MAP,
4096
+ jsxSource: tagJsxRaw,
4097
+
4098
+ controls: [
4099
+ {
4100
+ id: 'fontWeight',
4101
+ label: '字体粗细',
4102
+ type: 'seg',
4103
+ options: [
4104
+ { id: 'regular', label: '细体' },
4105
+ { id: 'bold', label: '粗体' },
4106
+ ],
4107
+ default: 'bold',
4108
+ },
4109
+ {
4110
+ id: 'radius',
4111
+ label: '圆角',
4112
+ type: 'seg',
4113
+ options: [
4114
+ { id: 'md', label: '圆角' },
4115
+ { id: 'full', label: '全圆' },
4116
+ ],
4117
+ default: 'md',
4118
+ },
4119
+ {
4120
+ id: 'showIcon',
4121
+ label: '显示图标',
4122
+ type: 'seg',
4123
+ options: [
4124
+ { id: 'off', label: '隐藏' },
4125
+ { id: 'on', label: '显示' },
4126
+ ],
4127
+ default: 'off',
4128
+ },
4129
+ {
4130
+ id: 'closable',
4131
+ label: '关闭按钮',
4132
+ type: 'seg',
4133
+ options: [
4134
+ { id: 'off', label: '隐藏' },
4135
+ { id: 'on', label: '显示' },
4136
+ ],
4137
+ default: 'off',
4138
+ },
4139
+ ],
4140
+
4141
+ mapProps: (cv, enums) => {
4142
+ const closable = cv.closable === 'on';
4143
+ const showIcon = cv.showIcon === 'on';
4144
+ const radius = cv.radius || 'md';
4145
+ return {
4146
+ showIcon,
4147
+ iconName: 'tag-01-stroked',
4148
+ closable,
4149
+ fontWeight: cv.fontWeight || 'bold',
4150
+ radius,
4151
+ variant: enums.variant,
4152
+ size: enums.size || 's',
4153
+ onClose: closable ? () => {} : undefined,
4154
+ };
4155
+ },
4156
+
4157
+ generateUsage: (enums, cv) => {
4158
+ const lines = [`import Tag from './components/Tag';`];
4159
+ const variant = enums.variant || 'brand';
4160
+ const size = enums.size || 's';
4161
+ const fontWeight = cv.fontWeight || 'bold';
4162
+ const radius = cv.radius || 'md';
4163
+ const showIcon = cv.showIcon === 'on';
4164
+ const closable = cv.closable === 'on';
4165
+
4166
+ lines.push('');
4167
+ lines.push('<Tag');
4168
+ if (variant !== 'brand') lines.push(` variant="${variant}"`);
4169
+ if (fontWeight !== 'bold') lines.push(` fontWeight="${fontWeight}"`);
4170
+ if (size !== 's') lines.push(` size="${size}"`);
4171
+ if (radius !== 'md') lines.push(` radius="${radius}"`);
4172
+ if (showIcon) lines.push(' showIcon');
4173
+ if (closable) lines.push(' closable');
4174
+ if (closable) lines.push(' onClose={() => {}}');
4175
+ lines.push('>');
4176
+ lines.push(' 标签');
4177
+ lines.push('</Tag>');
4178
+ return lines.join('\n');
4179
+ },
4180
+ },
4181
+
4182
+ table: {
4183
+ component: TablePreview,
4184
+ tokenMap: TABLE_TOKEN_MAP,
4185
+ jsxSource: tableJsxRaw,
4186
+ hideBaseVariantControl: true,
4187
+
4188
+ getPreviewAreaStyle: () => ({
4189
+ alignItems: 'stretch',
4190
+ justifyContent: 'flex-start',
4191
+ padding: '24px',
4192
+ overflow: 'auto',
4193
+ }),
4194
+
4195
+ getPreviewItemStyle: () => ({
4196
+ width: '100%',
4197
+ height: '100%',
4198
+ maxWidth: '100%',
4199
+ alignSelf: 'stretch',
4200
+ minWidth: '0',
4201
+ minHeight: '0',
4202
+ display: 'flex',
4203
+ }),
4204
+
4205
+ getPreviewScalerStyle: () => ({
4206
+ width: '100%',
4207
+ height: '100%',
4208
+ minWidth: '0',
4209
+ minHeight: '0',
4210
+ display: 'flex',
4211
+ flex: '1 1 auto',
4212
+ transformOrigin: 'top center',
4213
+ }),
4214
+
4215
+ controls: [
4216
+ {
4217
+ id: 'tableVariant',
4218
+ label: '表格类型',
4219
+ type: 'seg',
4220
+ options: TABLE_VARIANT_OPTIONS,
4221
+ default: 'table',
4222
+ },
4223
+ {
4224
+ id: 'paginationMode',
4225
+ label: '分页',
4226
+ type: 'seg',
4227
+ options: [
4228
+ { id: 'on', label: '显示' },
4229
+ { id: 'off', label: '隐藏' },
4230
+ ],
4231
+ default: 'on',
4232
+ },
4233
+ {
4234
+ id: 'pageSizeMode',
4235
+ label: '每页',
4236
+ type: 'seg',
4237
+ options: [
4238
+ { id: '10', label: '10条' },
4239
+ { id: '20', label: '20条' },
4240
+ ],
4241
+ default: '20',
4242
+ hidden: ({ controlValues }) => (controlValues.paginationMode || 'on') === 'off',
4243
+ },
4244
+ {
4245
+ id: 'tableSize',
4246
+ label: '尺寸',
4247
+ type: 'seg',
4248
+ options: TABLE_CELL_SIZE_OPTIONS,
4249
+ default: 'default',
4250
+ hidden: ({ controlValues }) => (controlValues.tableVariant || 'table') === 'card-form',
4251
+ },
4252
+ {
4253
+ id: 'fixedColumnsMode',
4254
+ label: '固定栏',
4255
+ type: 'seg',
4256
+ options: [
4257
+ { id: 'none', label: '不固定' },
4258
+ { id: 'first', label: '固定第一栏' },
4259
+ { id: 'last', label: '固定尾栏' },
4260
+ { id: 'both', label: '首尾固定' },
4261
+ ],
4262
+ default: 'none',
4263
+ hidden: ({ controlValues }) => (controlValues.tableVariant || 'table') === 'card-form',
4264
+ },
4265
+ {
4266
+ id: 'headerCount',
4267
+ label: '表头数量',
4268
+ type: 'seg',
4269
+ options: [
4270
+ { id: '2', label: '2' },
4271
+ { id: '3', label: '3' },
4272
+ { id: '4', label: '4' },
4273
+ { id: '5', label: '5' },
4274
+ { id: '6', label: '6' },
4275
+ ],
4276
+ default: '5',
4277
+ hidden: ({ controlValues }) => (controlValues.tableVariant || 'table') === 'card-form',
4278
+ },
4279
+ {
4280
+ id: 'activeColumn2',
4281
+ label: '当前列',
4282
+ type: 'list',
4283
+ options: Array.from({ length: 2 }, (_, index) => ({
4284
+ id: `column${index + 1}`,
4285
+ label: `第${index + 1}列`,
4286
+ })),
4287
+ default: 'column1',
4288
+ hidden: ({ controlValues }) => (controlValues.tableVariant || 'table') === 'card-form' || (controlValues.headerCount || '6') !== '2',
4289
+ },
4290
+ {
4291
+ id: 'activeColumn3',
4292
+ label: '当前列',
4293
+ type: 'list',
4294
+ options: Array.from({ length: 3 }, (_, index) => ({
4295
+ id: `column${index + 1}`,
4296
+ label: `第${index + 1}列`,
4297
+ })),
4298
+ default: 'column1',
4299
+ hidden: ({ controlValues }) => (controlValues.tableVariant || 'table') === 'card-form' || (controlValues.headerCount || '6') !== '3',
4300
+ },
4301
+ {
4302
+ id: 'activeColumn4',
4303
+ label: '当前列',
4304
+ type: 'list',
4305
+ options: Array.from({ length: 4 }, (_, index) => ({
4306
+ id: `column${index + 1}`,
4307
+ label: `第${index + 1}列`,
4308
+ })),
4309
+ default: 'column1',
4310
+ hidden: ({ controlValues }) => (controlValues.tableVariant || 'table') === 'card-form' || (controlValues.headerCount || '6') !== '4',
4311
+ },
4312
+ {
4313
+ id: 'activeColumn5',
4314
+ label: '当前列',
4315
+ type: 'list',
4316
+ options: Array.from({ length: 5 }, (_, index) => ({
4317
+ id: `column${index + 1}`,
4318
+ label: `第${index + 1}列`,
4319
+ })),
4320
+ default: 'column1',
4321
+ hidden: ({ controlValues }) => (controlValues.tableVariant || 'table') === 'card-form' || (controlValues.headerCount || '6') !== '5',
4322
+ },
4323
+ {
4324
+ id: 'activeColumn6',
4325
+ label: '当前列',
4326
+ type: 'list',
4327
+ options: Array.from({ length: 6 }, (_, index) => ({
4328
+ id: `column${index + 1}`,
4329
+ label: `第${index + 1}列`,
4330
+ })),
4331
+ default: 'column1',
4332
+ hidden: ({ controlValues }) => (controlValues.tableVariant || 'table') === 'card-form' || (controlValues.headerCount || '6') !== '6',
4333
+ },
4334
+ ...Array.from({ length: 6 }, (_, index) => ([
4335
+ {
4336
+ id: `column${index + 1}Type`,
4337
+ label: '表单类型',
4338
+ type: 'list',
4339
+ options: TABLE_COLUMN_TYPE_OPTIONS,
4340
+ default: ['link', 'tag', 'text', 'avatar', 'datetime', 'actions'][index] || 'text',
4341
+ hidden: ({ controlValues }) => {
4342
+ if ((controlValues.tableVariant || 'table') === 'card-form') return true;
4343
+ const headerCount = controlValues.headerCount || '6';
4344
+ const activeColumn =
4345
+ controlValues[`activeColumn${headerCount}`] || 'column1';
4346
+ return Number(headerCount) < index + 1 || activeColumn !== `column${index + 1}`;
4347
+ },
4348
+ },
4349
+ ])).flat(),
4350
+ ],
4351
+
4352
+ mapProps: (cv) => ({
4353
+ tableVariant: cv.tableVariant || 'table',
4354
+ paginationMode: cv.paginationMode || 'on',
4355
+ pageSizeMode: cv.pageSizeMode || '20',
4356
+ tableSize: cv.tableSize || 'default',
4357
+ fixedColumnsMode: cv.fixedColumnsMode || 'none',
4358
+ headerCount: cv.headerCount || '5',
4359
+ column1Type: cv.column1Type || 'link',
4360
+ column2Type: cv.column2Type || 'tag',
4361
+ column3Type: cv.column3Type || 'text',
4362
+ column4Type: cv.column4Type || 'avatar',
4363
+ column5Type: cv.column5Type || 'datetime',
4364
+ column6Type: cv.column6Type || 'actions',
4365
+ }),
4366
+
4367
+ generateUsage: (_enumValues, controlValues) => buildTableUsageFromControls(controlValues),
4368
+ },
4369
+
4370
+ toast: {
4371
+ component: Toast,
4372
+ tokenMap: TOAST_TOKEN_MAP,
4373
+ jsxSource: toastJsxRaw,
4374
+
4375
+ controls: [
4376
+ {
4377
+ id: 'actions',
4378
+ label: '文字操作',
4379
+ type: 'seg',
4380
+ options: [
4381
+ { id: 'none', label: '无' },
4382
+ { id: 'single', label: '一项' },
4383
+ { id: 'dual', label: '两项' },
4384
+ ],
4385
+ default: 'none',
4386
+ },
4387
+ ],
4388
+
4389
+ mapProps: (cv, enums) => {
4390
+ const type = enums.type || 'info';
4391
+ const messages = {
4392
+ info: '补充信息或状态切换',
4393
+ success: '表单保存成功',
4394
+ warning: '可能出现的错误',
4395
+ error: '已经出现的错误',
4396
+ };
4397
+ const showClose = enums.showClose !== false;
4398
+ const noop = () => {};
4399
+ const actions =
4400
+ cv.actions === 'dual'
4401
+ ? [
4402
+ { label: '操作1', onClick: noop },
4403
+ { label: '操作2', onClick: noop },
4404
+ ]
4405
+ : cv.actions === 'single'
4406
+ ? [{ label: '操作1', onClick: noop }]
4407
+ : undefined;
4408
+ return {
4409
+ type,
4410
+ message: messages[type] || messages.info,
4411
+ bordered: enums.bordered !== false,
4412
+ showClose,
4413
+ onClose: showClose ? () => {} : undefined,
4414
+ actions,
4415
+ };
4416
+ },
4417
+
4418
+ generateUsage: (enums, cv) => {
4419
+ const type = enums.type || 'info';
4420
+ const messages = {
4421
+ info: '补充信息或状态切换',
4422
+ success: '表单保存成功',
4423
+ warning: '可能出现的错误',
4424
+ error: '已经出现的错误',
4425
+ };
4426
+ const msg = messages[type] || messages.info;
4427
+ const showClose = enums.showClose !== false;
4428
+ const bordered = enums.bordered !== false;
4429
+ const lines = [`import Toast from './components/Toast';`, ''];
4430
+ lines.push('<Toast');
4431
+ lines.push(` type="${type}"`);
4432
+ lines.push(` message="${msg}"`);
4433
+ if (cv.actions === 'single') {
4434
+ lines.push(` actions={[{ label: '操作1', onClick: () => {} }]}`);
4435
+ } else if (cv.actions === 'dual') {
4436
+ lines.push(' actions={[');
4437
+ lines.push(` { label: '操作1', onClick: () => {} },`);
4438
+ lines.push(` { label: '操作2', onClick: () => {} },`);
4439
+ lines.push(' ]}');
4440
+ }
4441
+ if (!bordered) lines.push(' bordered={false}');
4442
+ if (showClose) lines.push(' onClose={() => {}}');
4443
+ else lines.push(' showClose={false}');
4444
+ lines.push('/>');
4445
+ return lines.join('\n');
4446
+ },
4447
+ },
4448
+
4449
+ tooltip: {
4450
+ component: TooltipPreview,
4451
+ tokenMap: TOOLTIP_TOKEN_MAP,
4452
+ jsxSource: tooltipJsxRaw,
4453
+
4454
+ controls: [
4455
+ {
4456
+ id: 'mode',
4457
+ label: '展示模式',
4458
+ type: 'seg',
4459
+ options: [
4460
+ { id: 'pinned', label: '常驻' },
4461
+ { id: 'hover', label: '悬浮触发' },
4462
+ ],
4463
+ default: 'pinned',
4464
+ },
4465
+ {
4466
+ id: 'contentLength',
4467
+ label: '内容长度',
4468
+ type: 'seg',
4469
+ options: [
4470
+ { id: 'short', label: '短' },
4471
+ { id: 'medium', label: '中' },
4472
+ { id: 'long', label: '多行' },
4473
+ ],
4474
+ default: 'long',
4475
+ },
4476
+ ],
4477
+
4478
+ mapProps: (cv, enums) => {
4479
+ const contentMap = {
4480
+ short: '这是一段提示',
4481
+ medium: '提示信息:保存后无法撤销',
4482
+ long: '这是一段较长的多行提示文案。\n用于演示气泡最大宽度 280px 时的自动换行效果。',
4483
+ };
4484
+ return {
4485
+ placement: enums.placement || 'top',
4486
+ arrow: enums.arrow !== false,
4487
+ autoFlip: enums.autoFlip !== false,
4488
+ mode: cv.mode || 'pinned',
4489
+ content: contentMap[cv.contentLength] || contentMap.long,
4490
+ triggerLabel: '触发元素',
4491
+ };
4492
+ },
4493
+
4494
+ generateUsage: (enums, cv) => {
4495
+ const placement = enums.placement || 'top';
4496
+ const arrow = enums.arrow !== false;
4497
+ const autoFlip = enums.autoFlip !== false;
4498
+ const contentMap = {
4499
+ short: '这是一段提示',
4500
+ medium: '提示信息:保存后无法撤销',
4501
+ long: '这是一段较长的多行提示文案。\\n用于演示气泡最大宽度 280px 时的自动换行效果。',
4502
+ };
4503
+ const content = contentMap[cv.contentLength] || contentMap.long;
4504
+
4505
+ const lines = [`import Tooltip from './components/Tooltip';`];
4506
+ lines.push('');
4507
+ lines.push('<Tooltip');
4508
+ lines.push(` content={\`${content}\`}`);
4509
+ if (placement !== 'top') lines.push(` placement="${placement}"`);
4510
+ if (!arrow) lines.push(' arrow={false}');
4511
+ if (!autoFlip) lines.push(' autoFlip={false}');
4512
+ lines.push('>');
4513
+ lines.push(' <Button>悬浮我</Button>');
4514
+ lines.push('</Tooltip>');
4515
+ return lines.join('\n');
4516
+ },
4517
+ },
4518
+
4519
+ empty: {
4520
+ component: Empty,
4521
+ tokenMap: EMPTY_TOKEN_MAP,
4522
+ jsxSource: emptyJsxRaw,
4523
+
4524
+ // 场景示例:每种场景对应一套最自然的内容组合
4525
+ // buttonLayout 已是枚举 prop,在左侧面板直接切换,无需在 controls 重复
4526
+ controls: [
4527
+ {
4528
+ id: 'scenario',
4529
+ label: '场景示例',
4530
+ type: 'seg',
4531
+ options: [
4532
+ { id: 'default', label: '通用空状态' },
4533
+ { id: 'no-result', label: '搜索无结果' },
4534
+ { id: 'error', label: '加载失败' },
4535
+ ],
4536
+ default: 'default',
4537
+ },
4538
+ ],
4539
+
4540
+ mapProps: (cv, enums) => {
4541
+ const illustrationType = enums.illustrationType || 'no-content';
4542
+ const buttonLayout = enums.buttonLayout || 'row';
4543
+ const scenario = cv.scenario || 'default';
4544
+
4545
+ // 加载失败:链接行模式,无标题/按钮
4546
+ if (scenario === 'error') {
4547
+ return {
4548
+ illustrationType: 'failure',
4549
+ linkPrefix: '页面显示失败,',
4550
+ linkText: '刷新重试',
4551
+ };
4552
+ }
4553
+
4554
+ // 搜索无结果:有标题+描述,无操作按钮(纯说明)
4555
+ if (scenario === 'no-result') {
4556
+ return {
4557
+ illustrationType: 'no-result',
4558
+ buttonLayout,
4559
+ title: '未找到搜索结果',
4560
+ description: '请尝试更换关键词或清除筛选条件',
4561
+ };
4562
+ }
4563
+
4564
+ // 通用空状态:标题 + 描述 + 双按钮,buttonLayout 控制横排/竖排
4565
+ return {
4566
+ illustrationType,
4567
+ buttonLayout,
4568
+ title: '空状态标题',
4569
+ description: '这是一段对当前空状态的描述文本',
4570
+ primaryLabel: '一级按钮',
4571
+ secondaryLabel: buttonLayout === 'row' ? '二级按钮' : '',
4572
+ tertiaryLabel: buttonLayout === 'column' ? '文字按钮' : '',
4573
+ };
4574
+ },
4575
+
4576
+ generateUsage: (enums, cv) => {
4577
+ const illustrationType = enums.illustrationType || 'no-content';
4578
+ const buttonLayout = enums.buttonLayout || 'row';
4579
+ const scenario = cv.scenario || 'default';
4580
+
4581
+ const lines = [`import Empty from './components/Empty';`, ''];
4582
+
4583
+ if (scenario === 'error') {
4584
+ lines.push('<Empty');
4585
+ lines.push(' illustrationType="failure"');
4586
+ lines.push(' linkPrefix="页面显示失败,"');
4587
+ lines.push(' linkText="刷新重试"');
4588
+ lines.push('/>');
4589
+ return lines.join('\n');
4590
+ }
4591
+
4592
+ if (scenario === 'no-result') {
4593
+ lines.push('<Empty');
4594
+ lines.push(' illustrationType="no-result"');
4595
+ lines.push(' title="未找到搜索结果"');
4596
+ lines.push(' description="请尝试更换关键词或清除筛选条件"');
4597
+ lines.push('/>');
4598
+ return lines.join('\n');
4599
+ }
4600
+
4601
+ // 通用空状态
4602
+ lines.push('<Empty');
4603
+ if (illustrationType !== 'no-content') lines.push(` illustrationType="${illustrationType}"`);
4604
+ if (buttonLayout !== 'row') lines.push(` buttonLayout="${buttonLayout}"`);
4605
+ lines.push(' title="空状态标题"');
4606
+ lines.push(' description="这是一段对当前空状态的描述文本"');
4607
+ lines.push(' primaryLabel="一级按钮"');
4608
+ if (buttonLayout === 'row') lines.push(' secondaryLabel="二级按钮"');
4609
+ if (buttonLayout === 'column') lines.push(' tertiaryLabel="文字按钮"');
4610
+ lines.push('/>');
4611
+ return lines.join('\n');
4612
+ },
4613
+ },
4614
+
4615
+ 'full-screen-page': {
4616
+ component: FullScreenPage,
4617
+ tokenMap: FULL_SCREEN_PAGE_TOKEN_MAP,
4618
+ jsxSource: fullScreenPageJsxRaw,
4619
+ previewBgBinding: 'bg',
4620
+
4621
+ /* 预览区:去掉默认 padding,撑满布局给的高度;overflow hidden + 圆角裁边 */
4622
+ getPreviewAreaStyle: () => ({
4623
+ overflow: 'hidden',
4624
+ borderRadius: '8px',
4625
+ padding: 0,
4626
+ alignItems: 'stretch',
4627
+ }),
4628
+ /* scaler 跟随预览区高度拉伸,作为 contained FullScreenPage 的尺寸容器 */
4629
+ getPreviewScalerStyle: () => ({
4630
+ width: '100%',
4631
+ alignSelf: 'stretch',
4632
+ display: 'flex',
4633
+ flexDirection: 'column',
4634
+ }),
4635
+
4636
+ controls: [],
4637
+
4638
+ mapProps: (cv, enums) => {
4639
+ const bg = enums.bg || 'white';
4640
+
4641
+ /* 步骤卡片标题行(与 BasePageFramePattern 的 QACardHeader 对齐) */
4642
+ const StepCardHeader = ({ step, title, hint }) => (
4643
+ <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '20px 24px', flexShrink: 0 }}>
4644
+ <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
4645
+ <div style={{ width: 20, height: 20, borderRadius: 6, background: '#1C1C23', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
4646
+ <span style={{ fontSize: 12, fontWeight: 600, color: '#fff', lineHeight: 1 }}>{step}</span>
4647
+ </div>
4648
+ <span style={{ fontSize: 16, fontWeight: 600, color: '#182230', lineHeight: '22px' }}>{title}</span>
4649
+ </div>
4650
+ {hint && <span style={{ fontSize: 14, color: '#475467', lineHeight: '20px' }}>{hint}</span>}
4651
+ </div>
4652
+ );
4653
+
4654
+ /* 卡片样式:灰底=白底填充,白底=透明+灰描边 */
4655
+ const cardStyle = bg === 'grey'
4656
+ ? { background: '#FFFFFF', borderRadius: '12px', overflow: 'hidden' }
4657
+ : { background: 'transparent', border: '1px solid #E4E7EC', borderRadius: '12px', overflow: 'hidden' };
4658
+
4659
+ const demoContent = (
4660
+ <div style={{ display: 'flex', flex: 1, minHeight: 0, gap: '8px', padding: '16px' }}>
4661
+ <div style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0, ...cardStyle }}>
4662
+ <StepCardHeader step="1" title="步骤标题一" hint="辅助信息" />
4663
+ </div>
4664
+ <div style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0, ...cardStyle }}>
4665
+ <StepCardHeader step="2" title="步骤标题二" />
4666
+ </div>
4667
+ <div style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0, ...cardStyle }}>
4668
+ <StepCardHeader step="3" title="步骤标题三" />
4669
+ </div>
4670
+ </div>
4671
+ );
4672
+
4673
+ return {
4674
+ bg,
4675
+ contained: true,
4676
+ title: '创建QA对',
4677
+ onBack: () => {},
4678
+ headerActions: (
4679
+ <div style={{ display: 'flex', gap: '8px' }}>
4680
+ <Button variant="outline-black">保存草稿</Button>
4681
+ <Button variant="primary">发布</Button>
4682
+ </div>
4683
+ ),
4684
+ children: demoContent,
4685
+ };
4686
+ },
4687
+
4688
+ generateUsage: (enums, cv) => {
4689
+ const bg = enums.bg || 'white';
4690
+ const lines = [`import FullScreenPage from './components/FullScreenPage';`, ''];
4691
+ lines.push('<FullScreenPage');
4692
+ if (bg !== 'white') lines.push(` bg="${bg}"`);
4693
+ lines.push(' title="创建QA对"');
4694
+ lines.push(' onBack={() => handleBack()}');
4695
+ lines.push(' headerActions={');
4696
+ lines.push(' <>');
4697
+ lines.push(' <Button variant="outline-black">保存草稿</Button>');
4698
+ lines.push(' <Button variant="primary">发布</Button>');
4699
+ lines.push(' </>');
4700
+ lines.push(' }');
4701
+ lines.push('>');
4702
+ if (bg === 'grey') {
4703
+ lines.push(' {/* 灰底模式:页面是浅灰底,内部面板必须切换为白卡样式 */}');
4704
+ lines.push(' <div className="flex gap-4 p-6 h-full">');
4705
+ lines.push(' <div className="flex-1 bg-white rounded-xl p-6">主编辑面板</div>');
4706
+ lines.push(' <div className="w-60 bg-white rounded-xl p-6">配置侧栏面板</div>');
4707
+ lines.push(' </div>');
4708
+ } else {
4709
+ lines.push(' {/* 白底模式:页面保持一体化,内部面板用描边样式,不再叠加白卡 */}');
4710
+ lines.push(' <div className="flex gap-4 p-6 h-full">');
4711
+ lines.push(' <div className="flex-1 rounded-xl border border-border-default p-6">基础信息面板</div>');
4712
+ lines.push(' <div className="w-60 rounded-xl border border-border-default p-6">辅助配置面板</div>');
4713
+ lines.push(' </div>');
4714
+ }
4715
+ lines.push('</FullScreenPage>');
4716
+ return lines.join('\n');
4717
+ },
4718
+ },
4719
+ };