@baishuyun/chat-sdk 0.0.8 → 0.0.10

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 (78) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/CHANGELOG.md +16 -0
  3. package/dist/chat-sdk.js +20855 -19610
  4. package/dist/chat-sdk.js.map +1 -1
  5. package/dist/chat-sdk.umd.cjs +188 -188
  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 +16 -6
  10. package/src/components/biz-comp/FakeBotMsg.tsx +6 -1
  11. package/src/components/biz-comp/chat-client.tsx +25 -5
  12. package/src/components/biz-comp/conversation.tsx +24 -45
  13. package/src/components/biz-comp/dash-widget-icon.tsx +17 -0
  14. package/src/components/biz-comp/field-icon.tsx +7 -2
  15. package/src/components/biz-comp/highlight-msg.tsx +3 -0
  16. package/src/components/biz-comp/markdown-part.tsx +42 -18
  17. package/src/components/biz-comp/message-content.tsx +1 -1
  18. package/src/components/biz-comp/messages.tsx +5 -4
  19. package/src/components/biz-comp/multi-modal-input/clear-btn.tsx +1 -0
  20. package/src/components/biz-comp/multi-modal-input/index.tsx +7 -3
  21. package/src/components/biz-comp/preview-message-wrapper.tsx +5 -1
  22. package/src/components/biz-comp/suggestions.tsx +8 -6
  23. package/src/components/bs-ui/border-color-animation.tsx +4 -1
  24. package/src/components/bs-ui/bs-icons.tsx +165 -0
  25. package/src/components/bs-ui/card-field.tsx +23 -0
  26. package/src/components/bs-ui/card.tsx +8 -10
  27. package/src/components/bs-ui/chat-area-header.tsx +14 -3
  28. package/src/components/bs-ui/collapsible-txt-msg.tsx +31 -18
  29. package/src/components/bs-ui/fields-generating-indicator.tsx +76 -31
  30. package/src/components/bs-ui/fields-previewer.tsx +26 -3
  31. package/src/components/bs-ui/form-info-editor.tsx +21 -7
  32. package/src/components/bs-ui/formula-tag.tsx +32 -0
  33. package/src/components/bs-ui/icon-btn.tsx +7 -2
  34. package/src/components/bs-ui/primary-confirm-btn.tsx +14 -7
  35. package/src/components/bs-ui/primary-entry-btn.tsx +14 -4
  36. package/src/components/bs-ui/scroll-to-bottom-btn.tsx +28 -0
  37. package/src/components/bs-ui/split-line.tsx +9 -0
  38. package/src/components/web-comp/fields-previewer-web-component.ts +1 -1
  39. package/src/const/index.ts +26 -2
  40. package/src/hooks/use-chat-preference.ts +9 -0
  41. package/src/hooks/use-draggable.ts +154 -0
  42. package/src/hooks/use-frame-mode.ts +1 -0
  43. package/src/hooks/use-msg-status-broadcast.ts +10 -0
  44. package/src/hooks/use-scroll-to-bottom.ts +127 -0
  45. package/src/index.tsx +2 -4
  46. package/src/lib/utils.ts +22 -0
  47. package/src/plugins/form-filling-plugin/index.ts +1 -1
  48. package/src/plugins/mcp-form-builder-plugin/components/create-form-confirm.tsx +1 -1
  49. package/src/plugins/mcp-form-builder-plugin/index.ts +6 -0
  50. package/src/plugins/report-query-plugin/components/avatar.tsx +12 -0
  51. package/src/plugins/report-query-plugin/components/query-entry-btn.tsx +17 -0
  52. package/src/plugins/report-query-plugin/components/query-msg-part.tsx +57 -0
  53. package/src/plugins/report-query-plugin/components/query-opening-lines.tsx +30 -0
  54. package/src/plugins/report-query-plugin/components/result-cards/DataTableCard.tsx +68 -0
  55. package/src/plugins/report-query-plugin/components/result-cards/DataTableFields.tsx +46 -0
  56. package/src/plugins/report-query-plugin/components/result-cards/FilterCondition.tsx +374 -0
  57. package/src/plugins/report-query-plugin/components/result-cards/FormulaField.tsx +24 -0
  58. package/src/plugins/report-query-plugin/const.ts +1 -0
  59. package/src/plugins/report-query-plugin/index.ts +100 -0
  60. package/src/plugins/report-query-plugin/types.ts +16 -0
  61. package/src/plugins/report-query-plugin/utils/field-enhance.ts +39 -0
  62. package/src/plugins/report-query-plugin/utils/get-field-group.ts +691 -0
  63. package/src/plugins/report-query-plugin/utils/get-field-icon.ts +26 -0
  64. package/src/plugins/report-query-plugin/utils/get-group-rule.ts +21 -0
  65. package/src/plugins/report-query-plugin/utils.tsx +379 -0
  66. package/src/sdk.impl.tsx +7 -0
  67. package/src/store/index.ts +2 -1
  68. package/src/stories/CardField.stories.tsx +87 -0
  69. package/src/stories/DashWidgetIcon.stories.tsx +132 -0
  70. package/src/stories/FormulaField.stories.tsx +145 -0
  71. package/src/stories/FormulaTag.stories.tsx +68 -0
  72. package/src/stories/IconBtn.stories.tsx +14 -8
  73. package/src/stories/{PrimaryConfirmBtn.stories.ts → PrimaryConfirmBtn.stories.tsx} +16 -0
  74. package/src/stories/ScrollToBottom.stories.tsx +24 -0
  75. package/src/stories/SplitLine.stories.tsx +40 -0
  76. package/src/stories/fields-generating.stories.tsx +19 -0
  77. package/src/style.css +5 -2
  78. package/tailwind.config.js +19 -20
@@ -0,0 +1,374 @@
1
+ import type {
2
+ FilterMethod,
3
+ FilterTypeRelationMap,
4
+ IFilterField,
5
+ IQueryFilter,
6
+ MethodLabelMap,
7
+ } from '@baishuyun/types';
8
+ import type { ReactNode } from 'react';
9
+
10
+ /**
11
+ * 方法到中文描述的默认映射
12
+ */
13
+ const DEFAULT_METHOD_LABELS: MethodLabelMap = {
14
+ eq: '等于',
15
+ ne: '不等于',
16
+ in: '等于任意一个',
17
+ nin: '不等于任意一个',
18
+ like: '包含',
19
+ unlike: '不包含',
20
+ empty: '为空',
21
+ not_empty: '不为空',
22
+ gt: '大于',
23
+ lt: '小于',
24
+ gte: '大于等于',
25
+ lte: '小于等于',
26
+ range: '范围',
27
+ formula: '动态',
28
+ all: '同时包含',
29
+ };
30
+
31
+ /**
32
+ * 类型到关系映射的定义
33
+ * 某些类型对同一方法有不同的中文描述
34
+ */
35
+ const TYPE_METHOD_OVERRIDES: FilterTypeRelationMap = {
36
+ user: {
37
+ in: '等于任意一个',
38
+ ne: '不等于',
39
+ eq: '等于',
40
+ nin: '不等于任意一个',
41
+ empty: '为空',
42
+ not_empty: '不为空',
43
+ },
44
+ flowstate: {
45
+ ne: '不等于',
46
+ eq: '等于',
47
+ empty: '为空',
48
+ },
49
+ _id: {
50
+ eq: '等于',
51
+ ne: '不等于',
52
+ in: '等于任意一个',
53
+ nin: '不等于任意一个',
54
+ like: '包含',
55
+ unlike: '不包含',
56
+ empty: '为空',
57
+ not_empty: '不为空',
58
+ },
59
+ updateTime: {
60
+ eq: '等于',
61
+ ne: '不等于',
62
+ gte: '大于等于',
63
+ lte: '小于等于',
64
+ formula: '动态',
65
+ range: '范围',
66
+ empty: '为空',
67
+ not_empty: '不为空',
68
+ },
69
+ createTime: {
70
+ eq: '等于',
71
+ ne: '不等于',
72
+ gte: '大于等于',
73
+ lte: '小于等于',
74
+ formula: '动态',
75
+ range: '范围',
76
+ empty: '为空',
77
+ not_empty: '不为空',
78
+ },
79
+ creator: {
80
+ in: '等于任意一个',
81
+ ne: '不等于',
82
+ eq: '等于',
83
+ nin: '不等于任意一个',
84
+ empty: '为空',
85
+ not_empty: '不为空',
86
+ },
87
+ assist_outtime: {
88
+ gt: '大于',
89
+ lt: '小于',
90
+ range: '范围',
91
+ empty: '为空',
92
+ not_empty: '不为空',
93
+ },
94
+ assist_protime: {
95
+ gt: '大于',
96
+ lt: '小于',
97
+ range: '范围',
98
+ empty: '为空',
99
+ not_empty: '不为空',
100
+ },
101
+ assist_prostatus: {
102
+ ne: '不等于',
103
+ eq: '等于',
104
+ empty: '为空',
105
+ },
106
+ assist_flowid: {
107
+ like: '包含',
108
+ },
109
+ flow_chargers: {
110
+ in: '等于任意一个',
111
+ ne: '不等于',
112
+ eq: '等于',
113
+ nin: '不等于任意一个',
114
+ empty: '为空',
115
+ not_empty: '不为空',
116
+ },
117
+ flowdecision: {
118
+ in: '等于任意一个',
119
+ },
120
+ deptgroup: {
121
+ in: '包含任意一个',
122
+ all: '同时包含',
123
+ eq: '等于',
124
+ empty: '为空',
125
+ not_empty: '不为空',
126
+ },
127
+ usergroup: {
128
+ in: '包含任意一个',
129
+ all: '同时包含',
130
+ eq: '等于',
131
+ empty: '为空',
132
+ not_empty: '不为空',
133
+ },
134
+ dept: {
135
+ in: '等于任意一个',
136
+ ne: '不等于',
137
+ eq: '等于',
138
+ nin: '不等于任意一个',
139
+ empty: '为空',
140
+ not_empty: '不为空',
141
+ },
142
+ text: {
143
+ eq: '等于',
144
+ ne: '不等于',
145
+ in: '等于任意一个',
146
+ nin: '不等于任意一个',
147
+ like: '包含',
148
+ unlike: '不包含',
149
+ empty: '为空',
150
+ not_empty: '不为空',
151
+ },
152
+ signature: {
153
+ empty: '为空',
154
+ not_empty: '不为空',
155
+ },
156
+ upload: {
157
+ empty: '为空',
158
+ not_empty: '不为空',
159
+ },
160
+ image: {
161
+ empty: '为空',
162
+ not_empty: '不为空',
163
+ },
164
+ location: {
165
+ nin: '不包含',
166
+ all: '包含',
167
+ empty: '为空',
168
+ not_empty: '不为空',
169
+ },
170
+ // 单选类型
171
+ radiogroup: {
172
+ eq: '等于',
173
+ ne: '不等于',
174
+ in: '等于任意一个',
175
+ nin: '不等于任意一个',
176
+ empty: '为空',
177
+ not_empty: '不为空',
178
+ },
179
+ // 多选类型
180
+ checkboxgroup: {
181
+ in: '包含任意一个',
182
+ all: '同时包含',
183
+ eq: '等于',
184
+ empty: '为空',
185
+ not_empty: '不为空',
186
+ },
187
+ // 数字类型
188
+ number: {
189
+ eq: '等于',
190
+ ne: '不等于',
191
+ gt: '大于',
192
+ lt: '小于',
193
+ gte: '大于等于',
194
+ lte: '小于等于',
195
+ range: '范围',
196
+ empty: '为空',
197
+ not_empty: '不为空',
198
+ },
199
+ // 日期时间类型
200
+ datetime: {
201
+ eq: '等于',
202
+ ne: '不等于',
203
+ gte: '大于等于',
204
+ lte: '小于等于',
205
+ formula: '动态',
206
+ range: '范围',
207
+ empty: '为空',
208
+ not_empty: '不为空',
209
+ },
210
+ // 日期类型
211
+ date: {
212
+ eq: '等于',
213
+ ne: '不等于',
214
+ gte: '大于等于',
215
+ lte: '小于等于',
216
+ formula: '动态',
217
+ range: '范围',
218
+ empty: '为空',
219
+ not_empty: '不为空',
220
+ },
221
+ // 下拉选择类型(与单选类似)
222
+ select: {
223
+ eq: '等于',
224
+ ne: '不等于',
225
+ in: '等于任意一个',
226
+ nin: '不等于任意一个',
227
+ empty: '为空',
228
+ not_empty: '不为空',
229
+ },
230
+ };
231
+
232
+ /**
233
+ * 获取方法的中文描述
234
+ * @param method - 过滤方法
235
+ * @param type - 字段类型
236
+ * @returns 方法的中文描述
237
+ */
238
+ const getMethodLabel = (method: FilterMethod, type: string): string => {
239
+ // 优先使用类型特定的方法描述
240
+ const typeOverrides = TYPE_METHOD_OVERRIDES[type];
241
+ if (typeOverrides && typeOverrides[method]) {
242
+ return typeOverrides[method]!;
243
+ }
244
+ // 回退到默认描述
245
+ return DEFAULT_METHOD_LABELS[method] || method;
246
+ };
247
+
248
+ /**
249
+ * 格式化过滤值为可读字符串
250
+ * @param value - 过滤值数组
251
+ * @param method - 过滤方法
252
+ * @returns 格式化后的值字符串
253
+ */
254
+ const formatValue = (value: IFilterField['value'], method: FilterMethod): string => {
255
+ // 为空/不为空 不需要显示值
256
+ if (method === 'empty' || method === 'not_empty') {
257
+ return '';
258
+ }
259
+
260
+ if (!value || value.length === 0) {
261
+ return '';
262
+ }
263
+
264
+ // 范围类型特殊处理
265
+ if (method === 'range' && value.length === 2) {
266
+ return `${value[0]} 至 ${value[1]}`;
267
+ }
268
+
269
+ // 多值用逗号连接
270
+ if (value.length > 1) {
271
+ return `[${value.map((v: string | number | boolean | null) => String(v)).join(', ')}]`;
272
+ }
273
+
274
+ return String(value[0]);
275
+ };
276
+
277
+ const renderMethodLabel = (methodLabel: string): ReactNode => {
278
+ if (methodLabel.endsWith('一个')) {
279
+ const prefix = methodLabel.slice(0, -1);
280
+ const tail = methodLabel.slice(-1);
281
+ return (
282
+ <>
283
+ <span className="text-[#666]">{prefix}</span>
284
+ <span className="text-[#999]">{tail}</span>
285
+ </>
286
+ );
287
+ }
288
+
289
+ return <span className="text-[#666]">{methodLabel}</span>;
290
+ };
291
+
292
+ /**
293
+ * 根据方法类型生成更自然的人类可读描述模板
294
+ * @param method - 过滤方法
295
+ * @returns 描述模板结构
296
+ */
297
+ const getHumanReadableTemplate = (method: FilterMethod): { prefix: string; suffix: string } => {
298
+ switch (method) {
299
+ case 'range':
300
+ return { prefix: '在', suffix: '之间' };
301
+ case 'gt':
302
+ case 'gte':
303
+ case 'lt':
304
+ case 'lte':
305
+ return { prefix: '', suffix: '' };
306
+ case 'like':
307
+ case 'unlike':
308
+ return { prefix: '', suffix: '' };
309
+ case 'in':
310
+ case 'nin':
311
+ return { prefix: '', suffix: '' };
312
+ case 'all':
313
+ return { prefix: '', suffix: '' };
314
+ case 'empty':
315
+ case 'not_empty':
316
+ return { prefix: '', suffix: '' };
317
+ default:
318
+ return { prefix: '', suffix: '' };
319
+ }
320
+ };
321
+
322
+ /**
323
+ * 将单个过滤字段转换为人类可读描述
324
+ * @param field - 过滤字段
325
+ * @returns 人类可读的条件描述
326
+ */
327
+ const filterToDesc = (field: IFilterField): ReactNode => {
328
+ const { title, method, value, type } = field;
329
+ const methodLabel = getMethodLabel(method, type);
330
+ const valueStr = formatValue(value, method);
331
+ const { prefix, suffix } = getHumanReadableTemplate(method);
332
+
333
+ // 为空/不为空 不需要显示值
334
+ if (method === 'empty' || method === 'not_empty') {
335
+ return (
336
+ <span className="leading-[normal]">
337
+ <span className="text-[#030303]">{title}</span>
338
+ <span className="text-[#4d609f]">&nbsp;</span>
339
+ {renderMethodLabel(methodLabel)}
340
+ </span>
341
+ );
342
+ }
343
+
344
+ return (
345
+ <span className="leading-[normal]">
346
+ <span className="text-[#030303]">{title}</span>
347
+ <span className="text-[#4d609f]">&nbsp;</span>
348
+ {prefix && <span className="text-[#666]">{prefix}</span>}
349
+ {renderMethodLabel(methodLabel)}
350
+ <span className="text-[#4d609f]">&nbsp;</span>
351
+ <span className="text-[#0265ff]">{valueStr}</span>
352
+ {suffix && <span className="text-[#666]">&nbsp;{suffix}</span>}
353
+ </span>
354
+ );
355
+ };
356
+
357
+ export const FilterCondition = ({ conds, rel }: IQueryFilter): ReactNode => {
358
+ if (!conds || conds.length === 0) {
359
+ return null;
360
+ }
361
+
362
+ const relLabel = rel === 'or' ? '或' : '且';
363
+
364
+ return (
365
+ <span className="font-['Alibaba_PuHuiTi_2.0:55_Regular',sans-serif] text-[14px] text-[#030303] whitespace-pre-wrap">
366
+ {conds.map((field, index) => (
367
+ <span key={`${field.name}-${index}`} className="leading-[normal]">
368
+ {filterToDesc(field)}
369
+ {index < conds.length - 1 && <span className="text-[#999]">&nbsp;{relLabel}&nbsp;</span>}
370
+ </span>
371
+ ))}
372
+ </span>
373
+ );
374
+ };
@@ -0,0 +1,24 @@
1
+ import { FormulasIcon } from '@/components/bs-ui/bs-icons';
2
+ import { FormulaTag } from '@/components/bs-ui/formula-tag';
3
+ import { cn } from '@/lib/utils';
4
+ import type { FormulaOp, IQueryResultFormula } from '@baishuyun/types';
5
+
6
+ export interface FormulaFieldProps {
7
+ field: IQueryResultFormula;
8
+ className?: string;
9
+ opType?: FormulaOp;
10
+ }
11
+
12
+ export const FormulaField = ({ field, className, opType }: FormulaFieldProps) => {
13
+ return (
14
+ <div className={cn('flex items-center justify-between', className)}>
15
+ <div className="flex min-w-0 items-center gap-[6px]">
16
+ <span className="shrink-0 text-[#0265FF]">
17
+ <FormulasIcon />
18
+ </span>
19
+ <span className="truncate text-sm leading-normal text-[#030303]">{field.title}</span>
20
+ </div>
21
+ {opType && <FormulaTag type={opType} />}
22
+ </div>
23
+ );
24
+ };
@@ -0,0 +1 @@
1
+ export const PLUGIN_NAME = 'ReportQueryPlugin';
@@ -0,0 +1,100 @@
1
+ import {
2
+ ChatAreaPlugin,
3
+ IDashForm,
4
+ PluginCustomComponent,
5
+ PluginHooks,
6
+ PluginRegistryEntry,
7
+ } from '@baishuyun/types';
8
+ import { IReportQueryContext } from './types';
9
+ import { PLUGIN_NAME } from './const';
10
+ import { QueryOpeningLines } from './components/query-opening-lines';
11
+ import { QueryEntryBtn } from './components/query-entry-btn';
12
+ import { QueryAvatar } from './components/avatar';
13
+ import { QueryMsgPart } from './components/query-msg-part';
14
+ import { queryResult2createParams, queryResult2previewerProps } from './utils';
15
+
16
+ export class ReportQueryPlugin extends ChatAreaPlugin<IReportQueryContext> {
17
+ public pluginName: string = PLUGIN_NAME;
18
+
19
+ public customComponents?: Partial<PluginCustomComponent> | undefined = {
20
+ OpeningLines: QueryOpeningLines,
21
+ EntryButton: QueryEntryBtn,
22
+ AvatarAndName: QueryAvatar,
23
+ MessagePart: QueryMsgPart,
24
+ };
25
+
26
+ public lifecycleHooks?: PluginHooks | undefined = {
27
+ onBeforeChatRender: () => {
28
+ return {
29
+ appendLoadingIndicator: true,
30
+ };
31
+ },
32
+ onBeforeMessageSend: (ctx) => {
33
+ return {
34
+ ...ctx,
35
+ options: {
36
+ body: {
37
+ appId: this.Context.appInfo?.appId,
38
+ },
39
+ },
40
+ };
41
+ },
42
+ };
43
+
44
+ public getPreviewerProps = (entryId: string) => {
45
+ if (!this.Context.queryResult) {
46
+ return null;
47
+ }
48
+
49
+ return queryResult2previewerProps(this.Context.queryResult, entryId);
50
+ };
51
+
52
+ public getCreateParams = (entryId: string) => {
53
+ if (!this.Context.queryResult) {
54
+ return null;
55
+ }
56
+
57
+ return queryResult2createParams(this.Context.queryResult, entryId);
58
+ };
59
+
60
+ public setAppId = (appId: string) => {
61
+ if (!this.Context.appInfo) {
62
+ this.Context.appInfo = {
63
+ appId,
64
+ };
65
+ } else {
66
+ this.Context.appInfo.appId = appId;
67
+ }
68
+ };
69
+
70
+ public setForms = (forms: Array<IDashForm>) => {
71
+ if (!forms || forms.length === 0) {
72
+ return;
73
+ }
74
+
75
+ const formMap = new Map<string, IDashForm>();
76
+ forms.forEach((form) => {
77
+ formMap.set(form.entryId, form);
78
+ });
79
+ if (!this.Context.appInfo) {
80
+ this.Context.appInfo = {
81
+ appId: '',
82
+ };
83
+ }
84
+ this.Context.appInfo.formMap = formMap;
85
+
86
+ console.log('forms received in plugin:', formMap);
87
+ };
88
+
89
+ public constructor(ctx: IReportQueryContext) {
90
+ super(ctx);
91
+ this.Context = ctx;
92
+ }
93
+ }
94
+
95
+ export const ReportQueryPluginEntry: PluginRegistryEntry<IReportQueryContext> = {
96
+ Plugin: ReportQueryPlugin,
97
+ createContext: (): IReportQueryContext => {
98
+ return {};
99
+ },
100
+ };
@@ -0,0 +1,16 @@
1
+ import { IDashForm, IQueryResult } from '@baishuyun/types';
2
+
3
+ export interface AppInfo {
4
+ appId: string;
5
+ formMap?: Map<string, IDashForm>;
6
+ }
7
+
8
+ export interface FormInfo {
9
+ name: string;
10
+ }
11
+
12
+ export interface IReportQueryContext {
13
+ appInfo?: AppInfo;
14
+
15
+ queryResult?: IQueryResult;
16
+ }
@@ -0,0 +1,39 @@
1
+ import {
2
+ FieldType,
3
+ IDashWidgetType,
4
+ IQueryResultMetric,
5
+ IQueryResultXField,
6
+ } from '@baishuyun/types';
7
+ import { GetXFieldGroupStr } from './get-field-group';
8
+ import { getFieldIcon } from './get-field-icon';
9
+ import { getGroupRule } from './get-group-rule';
10
+
11
+ export const XFieldEnhance = (
12
+ field: IQueryResultXField,
13
+ widgetType: IDashWidgetType,
14
+ index: number
15
+ ) => {
16
+ return {
17
+ ...field,
18
+ id: index,
19
+ icon: getFieldIcon(field.type as FieldType),
20
+ group: GetXFieldGroupStr(widgetType, field),
21
+ groupRule: getGroupRule(field.type as FieldType, widgetType),
22
+ };
23
+ };
24
+
25
+ export const MetricEnhance = (
26
+ field: IQueryResultMetric,
27
+ widgetType: IDashWidgetType,
28
+ index: number
29
+ ) => {
30
+ return {
31
+ ...field,
32
+ id: index,
33
+ icon: getFieldIcon(field.type as FieldType),
34
+ group: GetXFieldGroupStr(widgetType, {
35
+ type: field.type,
36
+ name: field.name || field.title,
37
+ }),
38
+ };
39
+ };