@baishuyun/chat-sdk 0.0.7 → 0.0.9

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 (50) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/CHANGELOG.md +16 -0
  3. package/dist/chat-sdk.js +18430 -17466
  4. package/dist/chat-sdk.js.map +1 -1
  5. package/dist/chat-sdk.umd.cjs +177 -177
  6. package/dist/chat-sdk.umd.cjs.map +1 -1
  7. package/dist/index.css +1 -1
  8. package/package.json +4 -4
  9. package/src/chat.tsx +7 -6
  10. package/src/components/biz-comp/chat-client.tsx +24 -5
  11. package/src/components/biz-comp/markdown-part.tsx +42 -18
  12. package/src/components/biz-comp/message-content.tsx +1 -1
  13. package/src/components/biz-comp/preview-message-wrapper.tsx +5 -1
  14. package/src/components/biz-comp/suggestions.tsx +3 -1
  15. package/src/components/bs-ui/bs-icons.tsx +95 -0
  16. package/src/components/bs-ui/card-field.tsx +23 -0
  17. package/src/components/bs-ui/chat-area-header.tsx +9 -0
  18. package/src/components/bs-ui/collapsible-txt-msg.tsx +13 -15
  19. package/src/components/bs-ui/fields-generating-indicator.tsx +2 -2
  20. package/src/components/bs-ui/fields-previewer.tsx +22 -3
  21. package/src/components/bs-ui/formula-tag.tsx +32 -0
  22. package/src/components/bs-ui/split-line.tsx +9 -0
  23. package/src/components/web-comp/fields-previewer-web-component.ts +1 -1
  24. package/src/hooks/use-chat-preference.ts +9 -0
  25. package/src/hooks/use-draggable.ts +154 -0
  26. package/src/hooks/use-msg-status-broadcast.ts +10 -0
  27. package/src/index.tsx +2 -4
  28. package/src/lib/utils.ts +22 -0
  29. package/src/plugins/form-filling-plugin/index.ts +1 -1
  30. package/src/plugins/mcp-form-builder-plugin/index.ts +5 -0
  31. package/src/plugins/report-query-plugin/components/avatar.tsx +12 -0
  32. package/src/plugins/report-query-plugin/components/query-entry-btn.tsx +17 -0
  33. package/src/plugins/report-query-plugin/components/query-msg-part.tsx +30 -0
  34. package/src/plugins/report-query-plugin/components/query-opening-lines.tsx +30 -0
  35. package/src/plugins/report-query-plugin/components/result-cards/DataTableCard.tsx +59 -0
  36. package/src/plugins/report-query-plugin/components/result-cards/DataTableFields.tsx +46 -0
  37. package/src/plugins/report-query-plugin/components/result-cards/FilterCondition.tsx +374 -0
  38. package/src/plugins/report-query-plugin/components/result-cards/FormulaField.tsx +24 -0
  39. package/src/plugins/report-query-plugin/const.ts +1 -0
  40. package/src/plugins/report-query-plugin/index.ts +96 -0
  41. package/src/plugins/report-query-plugin/types.ts +16 -0
  42. package/src/plugins/report-query-plugin/utils.tsx +389 -0
  43. package/src/sdk.impl.tsx +7 -0
  44. package/src/store/index.ts +2 -1
  45. package/src/stories/CardField.stories.tsx +87 -0
  46. package/src/stories/FormulaField.stories.tsx +145 -0
  47. package/src/stories/FormulaTag.stories.tsx +68 -0
  48. package/src/stories/SplitLine.stories.tsx +40 -0
  49. package/src/style.css +5 -2
  50. package/tailwind.config.js +19 -20
@@ -0,0 +1,389 @@
1
+ import {
2
+ IQueryResult,
3
+ IQueryResultPreviewerProps,
4
+ IQueryResultCreateParams,
5
+ IDashComponent,
6
+ IDashComponentField,
7
+ IDashComponentFilterCond,
8
+ IFilterField,
9
+ IQueryResultField,
10
+ } from '@baishuyun/types';
11
+
12
+ /** 生成唯一的 widget ID */
13
+ const generateWidgetId = (): string => {
14
+ return `_widget_${Date.now()}`;
15
+ };
16
+
17
+ /** 生成唯一的 entry ID */
18
+ const generateEntryId = (): string => {
19
+ const chars = 'abcdef0123456789';
20
+ let result = '';
21
+ for (let i = 0; i < 24; i++) {
22
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
23
+ }
24
+ return result;
25
+ };
26
+
27
+ /** 获取字段图标 */
28
+ const getFieldIcon = (type: string): string => {
29
+ const iconMap: Record<string, string> = {
30
+ text: 'icon-widget-text',
31
+ textarea: 'icon-widget-textarea',
32
+ number: 'icon-widget-number',
33
+ datetime: 'icon-widget-datetime',
34
+ date: 'icon-widget-date',
35
+ time: 'icon-widget-time',
36
+ radiogroup: 'icon-widget-radiogroup',
37
+ checkboxgroup: 'icon-widget-checkboxgroup',
38
+ user: 'icon-widget-user',
39
+ usergroup: 'icon-widget-usergroup',
40
+ dept: 'icon-widget-dept',
41
+ deptgroup: 'icon-widget-deptgroup',
42
+ upload: 'icon-widget-upload',
43
+ image: 'icon-widget-image',
44
+ location: 'icon-widget-location',
45
+ select: 'icon-widget-select',
46
+ combo: 'icon-widget-combo',
47
+ signature: 'icon-widget-signature',
48
+ };
49
+ return iconMap[type] || 'icon-widget-text';
50
+ };
51
+
52
+ /** 将 IQueryResultField 转换为 IDashComponentField */
53
+ const convertField = (
54
+ field: IQueryResultField,
55
+ formId: string,
56
+ index: number
57
+ ): IDashComponentField => {
58
+ return {
59
+ id: field.id ?? index,
60
+ name: field.name,
61
+ text: field.title,
62
+ type: field.type,
63
+ title: field.title,
64
+ form: formId,
65
+ isEditable: field.isEditable ?? false,
66
+ icon: getFieldIcon(field.type),
67
+ group: field.group || 'dimension',
68
+ formula: '',
69
+ tag: `f_${Date.now() + index}`,
70
+ };
71
+ };
72
+
73
+ /** 将 IFilterField 转换为 IDashComponentFilterCond */
74
+ const convertFilterField = (filter: IFilterField, formId: string): IDashComponentFilterCond => {
75
+ return {
76
+ title: filter.title,
77
+ name: filter.name,
78
+ type: filter.type,
79
+ entryId: formId,
80
+ form: formId,
81
+ field: filter.name,
82
+ method: filter.method,
83
+ value: filter.value ?? [],
84
+ hasEmpty: filter.method !== 'not_empty',
85
+ fieldvalueMode: 'widget',
86
+ fieldvalueName: '',
87
+ };
88
+ };
89
+
90
+ const createLabelField = (formId: string): IDashComponentField => {
91
+ return {
92
+ name: 'label',
93
+ text: '标题',
94
+ type: 'textarea',
95
+ title: '标题',
96
+ form: formId,
97
+ isEditable: true,
98
+ icon: getFieldIcon('textarea'),
99
+ group: 'dimension',
100
+ formula: '',
101
+ tag: `f_${Date.now()}`,
102
+ };
103
+ };
104
+
105
+ /** 创建默认样式配置 */
106
+ const createDefaultStyles = () => ({
107
+ theme: {
108
+ name: 'preset_style_light_1',
109
+ highLightColor: '#0265FF',
110
+ palette: 'preset_1',
111
+ gradation: 'preset_map_1',
112
+ },
113
+ background: {
114
+ type: 'color',
115
+ color: '#F4F6F9',
116
+ },
117
+ widget: {
118
+ theme: {
119
+ color: '#E9E9E9',
120
+ icon: 1,
121
+ cardName: 'preset_metric_1',
122
+ },
123
+ background: {
124
+ type: 'color',
125
+ color: '#FFFFFF',
126
+ },
127
+ fontSetting: {
128
+ titleColor: '#1F2D3D',
129
+ headColor: '#5E6D82',
130
+ textColor: '#1F2D3D',
131
+ textAlign: 'default',
132
+ },
133
+ },
134
+ });
135
+
136
+ /** 创建默认的 attrList 配置 */
137
+ const createDefaultAttrList = () => ({
138
+ title: {
139
+ show: false,
140
+ text: '标题内容',
141
+ textStyle: {
142
+ color: '#333333',
143
+ fontStyle: 'normal',
144
+ fontSize: 16,
145
+ fontWeight: 'normal',
146
+ },
147
+ left: 'left',
148
+ borderColor: 'transparent',
149
+ borderWidth: 2,
150
+ padding: 5,
151
+ onBorderColor: false,
152
+ borderRadius: 0,
153
+ backgroundColor: 'transparent',
154
+ },
155
+ orient: 'vertical',
156
+ legend: {
157
+ textStyle: {
158
+ fontStyle: 'normal',
159
+ fontSize: 10,
160
+ fontWeight: 'normal',
161
+ },
162
+ itemWidth: 12,
163
+ itemHeight: 12,
164
+ position: 'bottom',
165
+ type: 'plain',
166
+ padding: 10,
167
+ },
168
+ itemStyle: {
169
+ opacity: 0.7,
170
+ borderWidth: 2,
171
+ },
172
+ tooltip: {
173
+ show: true,
174
+ triggerOn: 'mousemove',
175
+ position: '0',
176
+ seriesName: '',
177
+ formatter: '1',
178
+ opacity: 1,
179
+ textStyle: {
180
+ color: '#FFFFFF',
181
+ fontStyle: 'normal',
182
+ fontSize: 14,
183
+ fontWeight: 'normal',
184
+ },
185
+ },
186
+ label: {
187
+ fontStyle: 'normal',
188
+ fontWeight: 'normal',
189
+ fontSize: 10,
190
+ position: 'top',
191
+ formatter: '1',
192
+ },
193
+ });
194
+
195
+ /** 将 IQueryResult 转换为 IQueryResultPreviewerProps */
196
+ export const queryResult2previewerProps = (
197
+ result: IQueryResult,
198
+ entryId: string
199
+ ): IQueryResultPreviewerProps => {
200
+ const appId = result.appID || '';
201
+ const widgetId = generateWidgetId();
202
+ const formId = result.formIds?.[0] || '';
203
+ const componentName = result.title || '未命名数据表';
204
+
205
+ // 转换字段
206
+ const fields: IDashComponentField[] =
207
+ result.fields?.map((field, index) => convertField(field, formId, index)) || [];
208
+
209
+ // 转换过滤条件
210
+ const filterConds: IDashComponentFilterCond[] =
211
+ result.filter?.conds?.map((cond) => convertFilterField(cond, formId)) || [];
212
+ const filterRel = result.filter?.rel || 'and';
213
+
214
+ // 创建组件配置
215
+ const component: IDashComponent = {
216
+ title: componentName,
217
+ forms: result.formIds || [],
218
+ type: 'data_table',
219
+ xFields: [],
220
+ metrics: [],
221
+ hasExport: false,
222
+ widgetId,
223
+ palette: 'default',
224
+ chart_label: { enable: true },
225
+ width: 30,
226
+ height: 20,
227
+ posX: 0,
228
+ posY: 0,
229
+ fields,
230
+ formulas: result.formulas || [],
231
+ widgetNameAlias: widgetId,
232
+ yFields: [],
233
+ permission: 'all',
234
+ triggers: [],
235
+ filter: {
236
+ cond: filterConds,
237
+ rel: filterRel,
238
+ },
239
+ defaultSort: [],
240
+ reportAuth: [],
241
+ enableRowEdit: false,
242
+ attrList: createDefaultAttrList(),
243
+ batchmenu: { enable: true, set: [] },
244
+ itemSeleable: true,
245
+ itemMarekerable: true,
246
+ mapScale: 14,
247
+ itemDataable: true,
248
+ scopefields: [],
249
+ menu: { enable: true, set: [] },
250
+ onSwitchButton: false,
251
+ BtnName: '点击操作',
252
+ FunctionBtn: 'itemManualData',
253
+ dryRun: true,
254
+ DataMode: false,
255
+ mapfields: [],
256
+ tFields: [],
257
+ bgColorFilter: [],
258
+ cardConfig: { widget_id: '{}' },
259
+ cardConfigFields: [],
260
+ selectFields: [],
261
+ mapFields: [],
262
+ selectArg: {},
263
+ thresholds: [],
264
+ guideline: [],
265
+ treeFields: {},
266
+ batchmenuAuth: {},
267
+ };
268
+
269
+ const components: Record<string, IDashComponent> = {
270
+ [widgetId]: component,
271
+ };
272
+
273
+ const styles = createDefaultStyles();
274
+
275
+ // 创建 entry 配置(用于 allComponentDash)
276
+ const entry = {
277
+ appId,
278
+ entryId,
279
+ hasLazyLoad: true,
280
+ styles,
281
+ autoLoad: true,
282
+ rel: filterRel,
283
+ cacheFilter: 0,
284
+ isMobileFilteringButton: 0 as const,
285
+ showFilter: 0 as const,
286
+ components,
287
+ mode: 'preview' as const,
288
+ dryRun: true,
289
+ name: result.title || '未命名分析报表',
290
+ };
291
+
292
+ return {
293
+ appId,
294
+ entryId,
295
+ hasLazyLoad: true,
296
+ styles,
297
+ autoLoad: true,
298
+ rel: 'and',
299
+ cacheFilter: 0,
300
+ isMobileFilteringButton: 1,
301
+ showFilter: 0,
302
+ components,
303
+ mode: 'preview',
304
+ showType: 'popup',
305
+ name: result.title || '未命名分析报表',
306
+ allComponentDash: {
307
+ entry,
308
+ },
309
+ };
310
+ };
311
+
312
+ export const queryResult2createParams = (
313
+ result: IQueryResult,
314
+ entryId: string
315
+ ): IQueryResultCreateParams => {
316
+ const appId = result.appID || '';
317
+ const widgetId = generateWidgetId();
318
+ const formId = result.formIds?.[0] || '';
319
+ const componentName = result.title || '未命名数据表';
320
+
321
+ const labelField = createLabelField(formId);
322
+ const fields: IDashComponentField[] = [labelField].concat(
323
+ result.fields?.map((field, index) => convertField(field, formId, index)) || []
324
+ );
325
+
326
+ const filterConds: IDashComponentFilterCond[] =
327
+ result.filter?.conds?.map((cond) => convertFilterField(cond, formId)) || [];
328
+ const filterRel = result.filter?.rel || 'and';
329
+
330
+ const widget: IDashComponent = {
331
+ title: componentName,
332
+ forms: result.formIds || [],
333
+ type: 'data_table',
334
+ xFields: [],
335
+ metrics: [],
336
+ hasExport: false,
337
+ widgetId,
338
+ palette: 'default',
339
+ chart_label: { enable: true },
340
+ width: 30,
341
+ height: 20,
342
+ posX: 0,
343
+ posY: 0,
344
+ fields,
345
+ formulas: result.formulas || [],
346
+ widgetNameAlias: widgetId,
347
+ yFields: [],
348
+ permission: 'all',
349
+ triggers: [],
350
+ filter: {
351
+ cond: filterConds,
352
+ rel: filterRel,
353
+ },
354
+ defaultSort: [],
355
+ reportAuth: [],
356
+ enableRowEdit: false,
357
+ attrList: createDefaultAttrList(),
358
+ batchmenu: { enable: true, set: [] },
359
+ itemSeleable: true,
360
+ itemMarekerable: true,
361
+ mapScale: 14,
362
+ itemDataable: true,
363
+ scopefields: [],
364
+ menu: { enable: true, set: [] },
365
+ onSwitchButton: false,
366
+ BtnName: '点击操作',
367
+ FunctionBtn: 'itemManualData',
368
+ DataMode: false,
369
+ mapfields: [],
370
+ tFields: [],
371
+ bgColorFilter: [],
372
+ cardConfig: { widget_id: {} },
373
+ cardConfigFields: [],
374
+ selectFields: [],
375
+ mapFields: [],
376
+ selectArg: {},
377
+ thresholds: [],
378
+ guideline: [],
379
+ treeFields: {},
380
+ };
381
+
382
+ return {
383
+ appId,
384
+ entryId,
385
+ widget,
386
+ datahelpers_remove: [],
387
+ datahelpers: {},
388
+ };
389
+ };
package/src/sdk.impl.tsx CHANGED
@@ -117,6 +117,13 @@ export class ChatSDK implements IChatSDK {
117
117
  };
118
118
  }
119
119
 
120
+ public setUserId(userId: string) {
121
+ this.options.chatApiHeaders = {
122
+ ...this.options.chatApiHeaders,
123
+ 'X-User-Id': userId,
124
+ };
125
+ }
126
+
120
127
  public getChatStatus() {
121
128
  return this.Store.getState().chatStatus;
122
129
  }
@@ -104,8 +104,9 @@ export const setupStoreSubscriptions = (store: ChatStore, option: IChatSDKOption
104
104
  (status) => {
105
105
  const eventBus = store.getState().eventBus;
106
106
  const visible = status !== 'hide';
107
- eventBus.emit('chat-area-visibility-change', { visible });
108
107
  option.onChatStatusChange?.(status);
108
+ // @deprecated, will be removed in future versions
109
+ eventBus.emit('chat-area-visibility-change', { visible, status });
109
110
  console.log('chat status changed:', status);
110
111
  }
111
112
  );
@@ -0,0 +1,87 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+
3
+ import { CardField } from '@/components/bs-ui/card-field';
4
+
5
+ const meta: Meta<typeof CardField> = {
6
+ title: 'Example/Chat/CardField',
7
+ component: CardField,
8
+ tags: ['autodocs'],
9
+ parameters: {
10
+ layout: 'centered',
11
+ },
12
+ args: {},
13
+ decorators: [
14
+ (Story) => (
15
+ <div className="w-[400px]">
16
+ <Story />
17
+ </div>
18
+ ),
19
+ ],
20
+ };
21
+
22
+ export default meta;
23
+ type Story = StoryObj<typeof CardField>;
24
+
25
+ // 基础用法
26
+ export const Base: Story = {
27
+ args: {
28
+ label: '维度(行)',
29
+ children: '仓库、产品名称、产品编码、日期(年-月)',
30
+ },
31
+ };
32
+
33
+ // label 过长省略展示
34
+ export const LongLabel: Story = {
35
+ args: {
36
+ label: '这是一个非常长的标签文字需要省略展示',
37
+ children: '内容区域',
38
+ },
39
+ };
40
+
41
+ // 无 children 时显示「无」
42
+ export const NoChildren: Story = {
43
+ args: {
44
+ label: '空字段',
45
+ },
46
+ };
47
+
48
+ // children 为空字符串
49
+ export const EmptyChildren: Story = {
50
+ args: {
51
+ label: '字段名称',
52
+ children: '',
53
+ },
54
+ };
55
+
56
+ // children 内容较长换行展示
57
+ export const LongContent: Story = {
58
+ args: {
59
+ label: '详细描述',
60
+ children:
61
+ '这是一段很长的内容,用于测试换行效果。内容区域支持自动换行,确保文字不会溢出容器,保持良好的阅读体验。',
62
+ },
63
+ };
64
+
65
+ // 多个 CardField 组合使用
66
+ export const MultipleFields: Story = {
67
+ args: {
68
+ label: '维度(行)',
69
+ children: '仓库、产品名称、产品编码、日期(年-月)',
70
+ },
71
+ render: () => (
72
+ <div className="flex flex-col gap-2">
73
+ <CardField label="维度(行)">仓库、产品名称、产品编码、日期(年-月)</CardField>
74
+ <CardField label="维度(列)">产品类别</CardField>
75
+ <CardField label="指标">销售额、销售量、库存量</CardField>
76
+ <CardField label="筛选条件" />
77
+ </div>
78
+ ),
79
+ };
80
+
81
+ // children 为自定义 ReactNode
82
+ export const CustomChildren: Story = {
83
+ args: {
84
+ label: '状态',
85
+ children: <span className="text-green-500 font-medium">已完成</span>,
86
+ },
87
+ };
@@ -0,0 +1,145 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+
3
+ import { FormulaField } from '@/plugins/report-query-plugin/components/result-cards/FormulaField';
4
+
5
+ const meta: Meta<typeof FormulaField> = {
6
+ title: 'Example/Chat/FormulaField',
7
+ component: FormulaField,
8
+ tags: ['autodocs'],
9
+ parameters: {
10
+ layout: 'centered',
11
+ },
12
+ decorators: [
13
+ (Story) => (
14
+ <div style={{ width: 308 }}>
15
+ <Story />
16
+ </div>
17
+ ),
18
+ ],
19
+ args: {
20
+ field: {
21
+ name: '_formula_1770204131633',
22
+ title: '活动数量',
23
+ formula: 'COUNT($_widget_1545113120234)',
24
+ },
25
+ },
26
+ } satisfies Meta<typeof FormulaField>;
27
+
28
+ export default meta;
29
+
30
+ type Story = StoryObj<typeof meta>;
31
+
32
+ /** COUNT → 计数 */
33
+ export const Count: Story = {};
34
+
35
+ /** SUM → 求和 */
36
+ export const Sum: Story = {
37
+ args: {
38
+ field: {
39
+ name: '_formula_1770204131634',
40
+ title: '总销售金额',
41
+ formula: 'SUM($_widget_1766566822319)',
42
+ },
43
+ },
44
+ };
45
+
46
+ /** MAX → 最大值 */
47
+ export const Max: Story = {
48
+ args: {
49
+ field: {
50
+ name: '_formula_1770204131635',
51
+ title: '最高库存',
52
+ formula: 'MAX($_widget_1751887349089)',
53
+ },
54
+ },
55
+ };
56
+
57
+ /** MIN → 最小值 */
58
+ export const Min: Story = {
59
+ args: {
60
+ field: {
61
+ name: '_formula_1770204131636',
62
+ title: '最低库存',
63
+ formula: 'MIN($_widget_1751887349089)',
64
+ },
65
+ },
66
+ };
67
+
68
+ /** AVERAGE → 平均 */
69
+ export const Average: Story = {
70
+ args: {
71
+ field: {
72
+ name: '_formula_1770204131637',
73
+ title: '平均单价',
74
+ formula: 'AVERAGE($_widget_1766566822319)',
75
+ },
76
+ },
77
+ };
78
+
79
+ /** COUNTDISTINCT → 去重计数 */
80
+ export const CountDistinct: Story = {
81
+ args: {
82
+ field: {
83
+ name: '_formula_1770204131638',
84
+ title: '产品种类数',
85
+ formula: 'COUNTDISTINCT($_widget_1545113120234)',
86
+ },
87
+ },
88
+ };
89
+
90
+ /** 算术表达式(无聚合函数) → 不显示 tag */
91
+ export const ArithmeticExpression: Story = {
92
+ args: {
93
+ field: {
94
+ name: '_formula_1770204131639',
95
+ title: '利润计算',
96
+ formula: '$_widget_1751887349089 - $_widget_1766566822319',
97
+ },
98
+ },
99
+ };
100
+
101
+ /** 长标题截断 */
102
+ export const LongTitle: Story = {
103
+ args: {
104
+ field: {
105
+ name: '_formula_1770204131640',
106
+ title: '这是一个非常长的公式字段名称需要被截断显示',
107
+ formula: 'COUNT($_widget_1545113120234)',
108
+ },
109
+ },
110
+ };
111
+
112
+ /** 多条公式一览 */
113
+ export const MultipleFormulas: Story = {
114
+ render: () => (
115
+ <div className="flex flex-col gap-3">
116
+ <FormulaField
117
+ field={{ name: '_formula_1', title: '活动数量', formula: 'COUNT($_widget_1545113120234)' }}
118
+ />
119
+ <FormulaField
120
+ field={{
121
+ name: '_formula_2',
122
+ title: '同意参与活动数量',
123
+ formula: 'COUNT($_widget_1551492482126)',
124
+ }}
125
+ />
126
+ <FormulaField
127
+ field={{
128
+ name: '_formula_3',
129
+ title: '需要货品支持数量',
130
+ formula: 'COUNT($_widget_1566611350781)',
131
+ }}
132
+ />
133
+ <FormulaField
134
+ field={{ name: '_formula_4', title: '总金额', formula: 'SUM($_widget_1766566822319)' }}
135
+ />
136
+ <FormulaField
137
+ field={{
138
+ name: '_formula_5',
139
+ title: '利润',
140
+ formula: '$_widget_1751887349089 - $_widget_1766566822319',
141
+ }}
142
+ />
143
+ </div>
144
+ ),
145
+ };
@@ -0,0 +1,68 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+
3
+ import { FormulaTag } from '@/components/bs-ui/formula-tag';
4
+ import { type FormulaOp, FORMULA_OP_LABEL } from '@baishuyun/types';
5
+
6
+ const meta = {
7
+ title: 'Example/Chat/FormulaTag',
8
+ component: FormulaTag,
9
+ tags: ['autodocs'],
10
+ parameters: {
11
+ layout: 'centered',
12
+ },
13
+ argTypes: {
14
+ type: {
15
+ control: 'select',
16
+ options: Object.keys(FORMULA_OP_LABEL) as FormulaOp[],
17
+ description: '统计方式',
18
+ },
19
+ },
20
+ args: {
21
+ type: 'sum',
22
+ },
23
+ } satisfies Meta<typeof FormulaTag>;
24
+
25
+ export default meta;
26
+
27
+ type Story = StoryObj<typeof meta>;
28
+
29
+ /** 求和 – #3767F1 */
30
+ export const Sum: Story = {
31
+ args: { type: 'sum' },
32
+ };
33
+
34
+ /** 最大值 – #F06648 */
35
+ export const Max: Story = {
36
+ args: { type: 'max' },
37
+ };
38
+
39
+ /** 最小值 – #4ACC8C */
40
+ export const Min: Story = {
41
+ args: { type: 'min' },
42
+ };
43
+
44
+ /** 平均 – #4AC0EC */
45
+ export const Average: Story = {
46
+ args: { type: 'average' },
47
+ };
48
+
49
+ /** 计数 – #9362CE */
50
+ export const Count: Story = {
51
+ args: { type: 'count' },
52
+ };
53
+
54
+ /** 去重计数 – #F168B6 */
55
+ export const CountDistinct: Story = {
56
+ args: { type: 'countDistinct' },
57
+ };
58
+
59
+ /** 所有统计方式一览 */
60
+ export const AllTypes: Story = {
61
+ render: () => (
62
+ <div className="flex flex-wrap items-center gap-2">
63
+ {(Object.keys(FORMULA_OP_LABEL) as FormulaOp[]).map((op) => (
64
+ <FormulaTag key={op} type={op} />
65
+ ))}
66
+ </div>
67
+ ),
68
+ };