@baishuyun/chat-sdk 0.0.16 → 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +178 -115
- package/CHANGELOG.md +16 -0
- package/dist/chat-sdk.js +37309 -44637
- package/dist/chat-sdk.js.map +1 -1
- package/dist/chat-sdk.umd.cjs +218 -295
- package/dist/chat-sdk.umd.cjs.map +1 -1
- package/dist/index.css +1 -1
- package/package.json +4 -4
- package/src/chat.tsx +19 -2
- package/src/components/biz-comp/FieldChecker.tsx +49 -7
- package/src/components/biz-comp/FieldCheckerListMsg.tsx +101 -22
- package/src/components/biz-comp/chat-client.tsx +8 -3
- package/src/components/biz-comp/error-msg.tsx +10 -0
- package/src/components/biz-comp/messages.tsx +10 -0
- package/src/components/biz-comp/multi-modal-input/clear-btn.tsx +3 -1
- package/src/components/biz-comp/multi-modal-input/index.tsx +80 -43
- package/src/components/biz-comp/multi-modal-input/prompt-input.tsx +13 -10
- package/src/components/biz-comp/preview-message-wrapper.tsx +4 -4
- package/src/components/biz-comp/preview-message.tsx +26 -2
- package/src/components/biz-comp/suggestions.tsx +5 -1
- package/src/components/bs-ui/attachment-part-group.tsx +5 -2
- package/src/components/bs-ui/attachment-part.tsx +14 -9
- package/src/components/bs-ui/attachments-previewer.tsx +6 -3
- package/src/components/bs-ui/base-button.tsx +20 -5
- package/src/components/bs-ui/border-animation.tsx +107 -0
- package/src/components/bs-ui/bs-icons.tsx +35 -0
- package/src/components/bs-ui/card.tsx +4 -3
- package/src/components/bs-ui/chat-area-header.tsx +7 -3
- package/src/components/bs-ui/confirm-dialog.tsx +17 -11
- package/src/components/bs-ui/fields-design-info-table.tsx +160 -0
- package/src/components/bs-ui/fields-previewer.tsx +18 -8
- package/src/components/bs-ui/form-info-editor.tsx +2 -42
- package/src/components/bs-ui/generate-animation.tsx +7 -5
- package/src/components/bs-ui/img-part.tsx +4 -2
- package/src/components/bs-ui/line-checker.tsx +19 -5
- package/src/components/bs-ui/previewer-header.tsx +48 -4
- package/src/components/bs-ui/primary-entry-btn.tsx +2 -1
- package/src/components/bs-ui/square-checker.tsx +30 -5
- package/src/components/bs-ui/tooltip.tsx +1 -1
- package/src/components/ui/dialog.tsx +1 -1
- package/src/components/ui/tooltip.tsx +1 -1
- package/src/const/ui.ts +42 -0
- package/src/hooks/use-frame-mode.ts +15 -0
- package/src/index.tsx +0 -1
- package/src/lib/parse-design-doc.ts +60 -0
- package/src/lib/utils.ts +79 -2
- package/src/plugins/form-builder-base-plugin/const.ts +3 -0
- package/src/plugins/form-builder-plugin/components/create-form-confirm.tsx +12 -1
- package/src/plugins/form-builder-plugin/components/design-doc-part.tsx +19 -0
- package/src/plugins/form-builder-plugin/components/design-info.tsx +47 -0
- package/src/plugins/form-builder-plugin/components/entry-btn.tsx +10 -2
- package/src/plugins/form-builder-plugin/components/fields-part.tsx +78 -0
- package/src/plugins/form-builder-plugin/components/follow-up.tsx +21 -6
- package/src/plugins/form-builder-plugin/components/msg-part.tsx +20 -145
- package/src/plugins/form-builder-plugin/components/opening-lines.tsx +11 -6
- package/src/plugins/form-builder-plugin/components/suggestion-part.tsx +62 -0
- package/src/plugins/form-builder-plugin/hooks/index.tsx +50 -0
- package/src/plugins/form-builder-plugin/index.ts +91 -14
- package/src/plugins/form-builder-plugin/types.ts +22 -2
- package/src/plugins/form-builder-plugin/utils/get-render-strategy.ts +27 -0
- package/src/plugins/form-builder-plugin/utils/index.ts +75 -41
- package/src/plugins/form-filling-plugin/components/FormFillingOpeningLines.tsx +4 -0
- package/src/plugins/form-filling-plugin/components/batch-fill-part.tsx +17 -0
- package/src/plugins/form-filling-plugin/components/batch-generator-action.tsx +50 -35
- package/src/plugins/form-filling-plugin/components/entry-btn.tsx +1 -1
- package/src/plugins/form-filling-plugin/components/first-batch-generating-animation.tsx +11 -0
- package/src/plugins/form-filling-plugin/components/generated-data-counter.tsx +17 -0
- package/src/plugins/form-filling-plugin/components/msg-part.tsx +39 -122
- package/src/plugins/form-filling-plugin/components/non-first-batch-generating-animation.tsx +18 -0
- package/src/plugins/form-filling-plugin/components/single-fill-part.tsx +42 -0
- package/src/plugins/form-filling-plugin/hooks/use-conversation-id-in-ctx.ts +13 -0
- package/src/plugins/form-filling-plugin/hooks/use-fields-data.ts +110 -0
- package/src/plugins/form-filling-plugin/index.ts +62 -42
- package/src/plugins/form-filling-plugin/types.ts +21 -1
- package/src/plugins/report-query-plugin/components/query-msg-part.tsx +25 -18
- package/src/plugins/report-query-plugin/components/result-cards/CreatedSourceMsg.tsx +36 -0
- package/src/plugins/report-query-plugin/components/result-cards/DataTableCard.tsx +15 -2
- package/src/plugins/report-query-plugin/const.ts +22 -0
- package/src/plugins/report-query-plugin/index.ts +41 -5
- package/src/plugins/report-query-plugin/types.ts +6 -0
- package/src/sdk.impl.tsx +4 -0
- package/src/store/index.ts +11 -1
- package/src/stories/BorderAnimation.stories.tsx +116 -0
- package/src/stories/FormInfoEditor.stories.tsx +19 -28
- package/src/stories/PreviewerHeader.stories.tsx +24 -0
- package/src/stories/fields-design-info-table.stories.tsx +203 -0
- package/src/style.css +25 -0
- package/src/plugins/form-builder-plugin/hooks/index.ts +0 -0
- package/src/plugins/general-model-form-builder-plugin/components/confirmer.tsx +0 -90
- package/src/plugins/general-model-form-builder-plugin/components/ghost-evt-dispatcher.tsx +0 -69
- package/src/plugins/general-model-form-builder-plugin/components/msg-part.tsx +0 -147
- package/src/plugins/general-model-form-builder-plugin/components/new-confirmer.tsx +0 -191
- package/src/plugins/general-model-form-builder-plugin/const.ts +0 -3
- package/src/plugins/general-model-form-builder-plugin/index.ts +0 -20
- package/src/plugins/general-model-form-builder-plugin/types.ts +0 -1
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { BorderColorAnimation } from '@/components/bs-ui/border-color-animation';
|
|
2
|
-
import { GenerateAnimation } from '@/components/bs-ui/generate-animation';
|
|
3
1
|
import { PrimaryConfirmBtn } from '@/components/bs-ui/primary-confirm-btn';
|
|
4
2
|
import { TextUIPart } from 'ai';
|
|
5
3
|
import { memo, useCallback, useEffect, useRef, useState } from 'react';
|
|
@@ -11,7 +9,11 @@ import { DATA_COUNT_PER_BATCH, FILLING_EVENTS, PLUGIN_NAME } from '../const';
|
|
|
11
9
|
import { useEvtBus } from '@/hooks/use-evt-bus';
|
|
12
10
|
import { ChatMessage, UseChatHelpers } from '@baishuyun/types';
|
|
13
11
|
import { type Field } from '@/plugins/form-builder-base-plugin/types';
|
|
14
|
-
import {
|
|
12
|
+
import { GeneratedDataCounter } from './generated-data-counter';
|
|
13
|
+
import { FirstBatchGeneratingAnimation } from './first-batch-generating-animation';
|
|
14
|
+
import { NonFirstBatchGeneratingAnimation } from './non-first-batch-generating-animation';
|
|
15
|
+
import { usePlugin } from '@/hooks/use-plugin';
|
|
16
|
+
import { useFakeGlobalLoadingMessage } from '@/hooks/use-frame-mode';
|
|
15
17
|
|
|
16
18
|
type FilledField = {
|
|
17
19
|
fieldName: string;
|
|
@@ -23,7 +25,6 @@ type FilledField = {
|
|
|
23
25
|
|
|
24
26
|
const useFillingEvtEmitter = (id: string) => {
|
|
25
27
|
const evtBus = useEvtBus();
|
|
26
|
-
|
|
27
28
|
return useCallback((f: FilledField) => {
|
|
28
29
|
const normalizedValue = valueNormalizer(f.value as string | string[]);
|
|
29
30
|
|
|
@@ -93,30 +94,52 @@ const BaseBatchGeneratorAction = ({
|
|
|
93
94
|
id,
|
|
94
95
|
fields,
|
|
95
96
|
}: BatchGeneratorActionProps) => {
|
|
97
|
+
const pluginInst = usePlugin<FormFillingPlugin>(PLUGIN_NAME);
|
|
98
|
+
|
|
96
99
|
const [streamingState, setStreamingState] = useState(status === 'streaming');
|
|
97
100
|
|
|
101
|
+
const [totalCount, setTotalCount] = useState(pluginInst?.getCount(id) || 0);
|
|
102
|
+
|
|
103
|
+
const [next, setNext] = useState(totalCount > 0);
|
|
104
|
+
|
|
98
105
|
const evtBus = useEvtBus();
|
|
99
106
|
|
|
107
|
+
|
|
108
|
+
const getRowNum = useCallback(() => {
|
|
109
|
+
if (!fields || fields.length === 0) {
|
|
110
|
+
return 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const lastField = fields[fields.length - 1];
|
|
114
|
+
return lastField.row || 0;
|
|
115
|
+
}, [fields]);
|
|
116
|
+
|
|
100
117
|
useEffect(() => {
|
|
101
118
|
if (status === 'ready' || status === 'error') {
|
|
102
119
|
setStreamingState(false);
|
|
103
120
|
}
|
|
104
121
|
}, [status]);
|
|
105
122
|
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
if (!pluginInst) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (streamingState) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const newRows = getRowNum();
|
|
133
|
+
pluginInst.setCount(id, newRows);
|
|
134
|
+
|
|
135
|
+
setTotalCount(pluginInst.getCount(id) || 0);
|
|
136
|
+
}, [streamingState, getRowNum, pluginInst, id]);
|
|
137
|
+
|
|
106
138
|
const formFillingCtx: FormFillingPluginCtx | null = usePluginCtx<
|
|
107
139
|
FormFillingPluginCtx,
|
|
108
140
|
FormFillingPlugin
|
|
109
141
|
>(PLUGIN_NAME);
|
|
110
142
|
|
|
111
|
-
const getRowNum = () => {
|
|
112
|
-
if (!fields || fields.length === 0) {
|
|
113
|
-
return 0;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const lastField = fields[fields.length - 1];
|
|
117
|
-
return lastField.row || 0;
|
|
118
|
-
};
|
|
119
|
-
|
|
120
143
|
const handleCheckChange = useCallback(
|
|
121
144
|
(checked: boolean) => {
|
|
122
145
|
evtBus.emit('form-filling-fields-checked-update', {
|
|
@@ -132,20 +155,19 @@ const BaseBatchGeneratorAction = ({
|
|
|
132
155
|
if (!streamingState) {
|
|
133
156
|
return (
|
|
134
157
|
<div className="rounded-[10px] border border-[var(--e0e0e0,#E0E0E0)] w-full flex items-center flex-col gap-[10px] p-[4px] pb-[20px]">
|
|
135
|
-
<
|
|
136
|
-
<CircleChecker defaultChecked onCheckedChange={handleCheckChange}>
|
|
137
|
-
已生成{getRowNum()}条数据
|
|
138
|
-
</CircleChecker>
|
|
139
|
-
</div>
|
|
158
|
+
<GeneratedDataCounter count={totalCount} handleCheckChange={handleCheckChange} />
|
|
140
159
|
|
|
141
160
|
<PrimaryConfirmBtn
|
|
142
161
|
text={`继续生成${DATA_COUNT_PER_BATCH}条`}
|
|
143
162
|
className="w-[300px]"
|
|
163
|
+
light
|
|
144
164
|
onClick={() => {
|
|
145
165
|
if (status === 'streaming' || streamingState) {
|
|
146
166
|
return;
|
|
147
167
|
}
|
|
148
168
|
|
|
169
|
+
setNext(true);
|
|
170
|
+
|
|
149
171
|
setStreamingState(true);
|
|
150
172
|
|
|
151
173
|
const formStructure = Array.from(formFillingCtx?.fieldMap.values() || []);
|
|
@@ -169,22 +191,12 @@ const BaseBatchGeneratorAction = ({
|
|
|
169
191
|
);
|
|
170
192
|
}
|
|
171
193
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
bgInset={1000}
|
|
179
|
-
borderWidth={1.5}
|
|
180
|
-
style={{
|
|
181
|
-
display: 'flex',
|
|
182
|
-
alignItems: 'center',
|
|
183
|
-
}}
|
|
184
|
-
>
|
|
185
|
-
<GenerateAnimation>数据生成中,可点击下方按钮暂停</GenerateAnimation>
|
|
186
|
-
</BorderColorAnimation>
|
|
187
|
-
);
|
|
194
|
+
const isFirstBatch = totalCount <= DATA_COUNT_PER_BATCH && !next;
|
|
195
|
+
if (isFirstBatch) {
|
|
196
|
+
return <FirstBatchGeneratingAnimation />;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return <NonFirstBatchGeneratingAnimation />;
|
|
188
200
|
};
|
|
189
201
|
|
|
190
202
|
const PartParser = ({ parts, ...rest }: BatchGeneratorActionProps) => {
|
|
@@ -194,9 +206,12 @@ const PartParser = ({ parts, ...rest }: BatchGeneratorActionProps) => {
|
|
|
194
206
|
|
|
195
207
|
const emit = useFillingEvtEmitter(rest.id);
|
|
196
208
|
|
|
209
|
+
const loadingMsg = useFakeGlobalLoadingMessage();
|
|
210
|
+
|
|
197
211
|
usePartParser(lastPart, (all, latestField) => {
|
|
198
212
|
setFields(all);
|
|
199
213
|
emit(latestField);
|
|
214
|
+
loadingMsg.clearGlobalFakeLoadingMessage();
|
|
200
215
|
});
|
|
201
216
|
|
|
202
217
|
if (!lastPart) {
|
|
@@ -218,7 +233,7 @@ export const BatchGeneratorAction = memo(PartParser, (prevProps, nextProps) => {
|
|
|
218
233
|
prevProps.parts?.length === nextProps.parts?.length &&
|
|
219
234
|
prevLastPart?.text === lastPart?.text &&
|
|
220
235
|
prevProps.status === nextProps.status &&
|
|
221
|
-
prevProps.sendMessage === nextProps.sendMessage &&
|
|
236
|
+
// prevProps.sendMessage === nextProps.sendMessage &&
|
|
222
237
|
prevProps.id === nextProps.id
|
|
223
238
|
);
|
|
224
239
|
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import BorderAnimation from '@/components/bs-ui/border-animation';
|
|
2
|
+
import { GenerateAnimation } from '@/components/bs-ui/generate-animation';
|
|
3
|
+
import { memo } from 'react';
|
|
4
|
+
|
|
5
|
+
export const FirstBatchGeneratingAnimation = memo(() => {
|
|
6
|
+
return (
|
|
7
|
+
<BorderAnimation borderRadius={4} height={40} className="flex items-center px-[10px]">
|
|
8
|
+
<GenerateAnimation>数据生成中,可点击下方按钮暂停</GenerateAnimation>
|
|
9
|
+
</BorderAnimation>
|
|
10
|
+
);
|
|
11
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { CircleChecker } from '@/components/bs-ui/circle-checker';
|
|
2
|
+
|
|
3
|
+
export const GeneratedDataCounter = ({
|
|
4
|
+
count,
|
|
5
|
+
handleCheckChange,
|
|
6
|
+
}: {
|
|
7
|
+
count: number;
|
|
8
|
+
handleCheckChange: (checked: boolean) => void;
|
|
9
|
+
}) => {
|
|
10
|
+
return (
|
|
11
|
+
<div className="flex items-center w-full py-[10px] px-[16px] gap-[6px]">
|
|
12
|
+
<CircleChecker defaultChecked onCheckedChange={handleCheckChange}>
|
|
13
|
+
已生成{count}条数据
|
|
14
|
+
</CircleChecker>
|
|
15
|
+
</div>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
@@ -1,141 +1,58 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
1
|
import { MarkdownMsgpart } from '@/components/biz-comp/markdown-part';
|
|
3
|
-
import { MessageContent } from '@/components/biz-comp/message-content';
|
|
4
2
|
import { usePluginCtx } from '@/hooks/use-plugin-ctx';
|
|
5
|
-
|
|
6
|
-
import { GetFieldJsonInfo, IsFieldsJsonPart, NeedToAppendFieldsSaveConfirm } from '@/lib/utils';
|
|
7
3
|
import { MsgPartCompProps } from '@baishuyun/types';
|
|
8
4
|
import { TextUIPart } from 'ai';
|
|
9
|
-
import { FILLING_EVENTS, PLUGIN_NAME } from '../const';
|
|
10
|
-
import { FormFillingPluginCtx } from '../types';
|
|
11
|
-
import { useEvtBus } from '@/hooks/use-evt-bus';
|
|
12
|
-
import { FormFillingPlugin } from '..';
|
|
13
|
-
import { valueNormalizer } from '../utils';
|
|
14
|
-
import { FieldValueCheckerProps } from '@/components/biz-comp/FieldValueChecker';
|
|
15
|
-
import { FieldValueCheckerListMsg } from '@/components/biz-comp/FieldCheckerListMsg';
|
|
16
|
-
import { PrimaryConfirmBtn } from '@/components/bs-ui/primary-confirm-btn';
|
|
17
|
-
import { BatchGeneratorAction } from './batch-generator-action';
|
|
18
|
-
import { useEvt } from '@/hooks/use-evt';
|
|
19
|
-
|
|
20
|
-
export const FillMessagePart = (props: MsgPartCompProps) => {
|
|
21
|
-
const { part: p, id, sendMessage, setMessages, status } = props;
|
|
22
|
-
|
|
23
|
-
const part = p as TextUIPart;
|
|
24
5
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
6
|
+
import { PLUGIN_NAME } from '../const';
|
|
7
|
+
import { FillPartRenderStrategy, FillPartRenderStrategyMap, FormFillingPluginCtx, IFieldsData } from '../types';
|
|
8
|
+
import { FormFillingPlugin } from '..';
|
|
9
|
+
import { useConversationIdInCtx } from '../hooks/use-conversation-id-in-ctx';
|
|
10
|
+
import { useFieldsData } from '../hooks/use-fields-data';
|
|
11
|
+
import { BatchFillPart } from './batch-fill-part';
|
|
12
|
+
import { SingleFillPart } from './single-fill-part';
|
|
13
|
+
|
|
14
|
+
const RENDER_STRATEGY_COMPONENT_MAP: FillPartRenderStrategyMap = {
|
|
15
|
+
batch: BatchFillPart,
|
|
16
|
+
single: SingleFillPart,
|
|
17
|
+
markdown: MarkdownMsgpart,
|
|
18
|
+
unknown: (_) => null,
|
|
19
|
+
};
|
|
32
20
|
|
|
33
|
-
|
|
34
|
-
|
|
21
|
+
const getRenderStrategy = (ctx: FormFillingPluginCtx | null, fieldsData: IFieldsData, part: any): FillPartRenderStrategy => {
|
|
22
|
+
if (fieldsData.fieldsParts.length && ctx && ctx.fillMode !== 'batch') {
|
|
23
|
+
return 'single';
|
|
35
24
|
}
|
|
36
25
|
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
if (ctx?.fillMode === 'batch' && fieldsData.fieldsParts.length) {
|
|
27
|
+
return 'batch';
|
|
28
|
+
}
|
|
39
29
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
});
|
|
30
|
+
if (part.text !== undefined && part.text.trim() !== '') {
|
|
31
|
+
return 'markdown';
|
|
32
|
+
}
|
|
44
33
|
|
|
45
|
-
|
|
34
|
+
return 'unknown';
|
|
35
|
+
}
|
|
46
36
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const fieldPropsArray: Array<FieldValueCheckerProps | null> = fieldsParts.map((p) => {
|
|
50
|
-
const field = GetFieldJsonInfo(p);
|
|
51
|
-
if (
|
|
52
|
-
!formFillingCtx.fieldMap?.has(field.fieldName) &&
|
|
53
|
-
!formFillingCtx.subFieldsNameMap?.has(field.fieldName)
|
|
54
|
-
) {
|
|
55
|
-
console.warn('unregistered field:', field.fieldName, formFillingCtx.fieldMap);
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
37
|
+
export const FillMessagePart = (props: MsgPartCompProps) => {
|
|
38
|
+
const { part: p, id } = props;
|
|
58
39
|
|
|
59
|
-
|
|
60
|
-
return {
|
|
61
|
-
field: {
|
|
62
|
-
label: field.label,
|
|
63
|
-
widget: {
|
|
64
|
-
widgetName: field.fieldName,
|
|
65
|
-
type: field.fieldType,
|
|
66
|
-
value: field.value,
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
onLoaded: (f: any) => {
|
|
70
|
-
f.widget.value = normalizedValue;
|
|
71
|
-
formFillingCtx.valueMap.set(f.widget?.widgetName, normalizedValue);
|
|
72
|
-
evtBus.emit(FILLING_EVENTS.FIELD_FILLED_UPDATE, {
|
|
73
|
-
field: f,
|
|
74
|
-
checked: true,
|
|
75
|
-
});
|
|
76
|
-
},
|
|
77
|
-
onChange: (check: boolean, f: any, parentFieldName?: string, row?: number) => {
|
|
78
|
-
if (check) {
|
|
79
|
-
formFillingCtx.valueMap.set(f.widget?.widgetName, normalizedValue);
|
|
80
|
-
}
|
|
81
|
-
evtBus.emit(FILLING_EVENTS.FIELD_FILLED_UPDATE, {
|
|
82
|
-
field: f,
|
|
83
|
-
checked: check,
|
|
84
|
-
parentFieldName,
|
|
85
|
-
rowIndex: row,
|
|
86
|
-
});
|
|
87
|
-
},
|
|
88
|
-
};
|
|
89
|
-
});
|
|
40
|
+
const part = p as TextUIPart;
|
|
90
41
|
|
|
91
|
-
|
|
42
|
+
const formFillingCtx = usePluginCtx<FormFillingPluginCtx, FormFillingPlugin>(PLUGIN_NAME);
|
|
92
43
|
|
|
93
|
-
|
|
94
|
-
<MessageContent className="gap-0 py-0" compact>
|
|
95
|
-
{status === 'ready' ? `已为你填写${filled.length}个字段` : null}
|
|
96
|
-
<FieldValueCheckerListMsg
|
|
97
|
-
props={filled}
|
|
98
|
-
readonly={confirmed}
|
|
99
|
-
streaming={status === 'streaming'}
|
|
100
|
-
>
|
|
101
|
-
{status === 'ready' ? (
|
|
102
|
-
<PrimaryConfirmBtn
|
|
103
|
-
className="mt-[20px] mb-[10px]"
|
|
104
|
-
disabled={confirmed}
|
|
105
|
-
onClick={() => {
|
|
106
|
-
setConfirmed(true);
|
|
107
|
-
}}
|
|
108
|
-
>
|
|
109
|
-
{confirmed ? '已确认' : '确认'}
|
|
110
|
-
</PrimaryConfirmBtn>
|
|
111
|
-
) : null}
|
|
112
|
-
</FieldValueCheckerListMsg>
|
|
113
|
-
{status === 'ready'
|
|
114
|
-
? '请选择需要的字段添加到表单。你可以补充描述后重新生成,表单中已添加字段在下次生成时保留。'
|
|
115
|
-
: null}
|
|
116
|
-
</MessageContent>
|
|
117
|
-
);
|
|
118
|
-
}
|
|
44
|
+
useConversationIdInCtx(formFillingCtx, id);
|
|
119
45
|
|
|
120
|
-
|
|
121
|
-
return (
|
|
122
|
-
<BatchGeneratorAction
|
|
123
|
-
id={id}
|
|
124
|
-
setMessages={setMessages}
|
|
125
|
-
status={status}
|
|
126
|
-
parts={fieldsParts}
|
|
127
|
-
sendMessage={sendMessage}
|
|
128
|
-
/>
|
|
129
|
-
);
|
|
130
|
-
}
|
|
46
|
+
const fieldsData = useFieldsData(part, formFillingCtx);
|
|
131
47
|
|
|
132
|
-
|
|
133
|
-
return <PrimaryConfirmBtn>保存</PrimaryConfirmBtn>;
|
|
134
|
-
}
|
|
48
|
+
const renderStrategy = getRenderStrategy(formFillingCtx, fieldsData, part);
|
|
135
49
|
|
|
136
|
-
|
|
137
|
-
return <MarkdownMsgpart {...props} />;
|
|
138
|
-
}
|
|
50
|
+
const Component = RENDER_STRATEGY_COMPONENT_MAP[renderStrategy];
|
|
139
51
|
|
|
140
|
-
return
|
|
52
|
+
return (
|
|
53
|
+
<Component
|
|
54
|
+
{...props}
|
|
55
|
+
{...fieldsData}
|
|
56
|
+
/>
|
|
57
|
+
)
|
|
141
58
|
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { GenerateAnimation } from '@/components/bs-ui/generate-animation';
|
|
2
|
+
import { PrimaryConfirmBtn } from '@/components/bs-ui/primary-confirm-btn';
|
|
3
|
+
import { DATA_COUNT_PER_BATCH } from '../const';
|
|
4
|
+
import { memo } from 'react';
|
|
5
|
+
import BorderAnimation from '@/components/bs-ui/border-animation';
|
|
6
|
+
|
|
7
|
+
export const NonFirstBatchGeneratingAnimation = memo(() => {
|
|
8
|
+
return (
|
|
9
|
+
<BorderAnimation borderRadius={10} height={110}>
|
|
10
|
+
<div className="flex flex-col gap-[10px] w-full pt-[4px]">
|
|
11
|
+
<GenerateAnimation className='ml-[16px] py-[10px]'>继续生成中,可点击下方暂停按钮暂停</GenerateAnimation>
|
|
12
|
+
<PrimaryConfirmBtn
|
|
13
|
+
className="w-[300px] mx-auto"
|
|
14
|
+
text={`继续生成${DATA_COUNT_PER_BATCH}条`} disabled />
|
|
15
|
+
</div>
|
|
16
|
+
</BorderAnimation>
|
|
17
|
+
);
|
|
18
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { MsgPartCompProps } from "@baishuyun/types";
|
|
2
|
+
import { IFieldsData } from "../types";
|
|
3
|
+
import { MessageContent } from "@/components/biz-comp/message-content";
|
|
4
|
+
import { FieldValueCheckerListMsg } from "@/components/biz-comp/FieldCheckerListMsg";
|
|
5
|
+
import { PrimaryConfirmBtn } from "@/components/bs-ui/primary-confirm-btn";
|
|
6
|
+
import { useState } from "react";
|
|
7
|
+
|
|
8
|
+
export const SingleFillPart: React.FC<MsgPartCompProps & IFieldsData> = (props) => {
|
|
9
|
+
const { status, filledFields} = props;
|
|
10
|
+
const [confirmed, setConfirmed] = useState(false);
|
|
11
|
+
|
|
12
|
+
const prefixTips = status === 'ready' ? `已为你填写${filledFields.length}个字段` : null;
|
|
13
|
+
const suffixTips = status === 'ready'
|
|
14
|
+
? '请选择需要的字段添加到表单。你可以补充描述后重新生成,表单中已添加字段在下次生成时保留。'
|
|
15
|
+
: null;
|
|
16
|
+
|
|
17
|
+
const confirmedText = confirmed ? '已确认' : '确认';
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<MessageContent className="gap-0 py-0" compact>
|
|
21
|
+
{prefixTips}
|
|
22
|
+
<FieldValueCheckerListMsg
|
|
23
|
+
props={filledFields}
|
|
24
|
+
readonly={confirmed}
|
|
25
|
+
streaming={status === 'streaming'}
|
|
26
|
+
>
|
|
27
|
+
{status === 'ready' ? (
|
|
28
|
+
<PrimaryConfirmBtn
|
|
29
|
+
className="mt-[20px] mb-[10px]"
|
|
30
|
+
disabled={confirmed}
|
|
31
|
+
onClick={() => {
|
|
32
|
+
setConfirmed(true);
|
|
33
|
+
}}
|
|
34
|
+
>
|
|
35
|
+
{confirmedText}
|
|
36
|
+
</PrimaryConfirmBtn>
|
|
37
|
+
) : null}
|
|
38
|
+
</FieldValueCheckerListMsg>
|
|
39
|
+
{suffixTips}
|
|
40
|
+
</MessageContent>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { FormFillingPluginCtx } from '../types';
|
|
3
|
+
|
|
4
|
+
export const useConversationIdInCtx = (
|
|
5
|
+
ctx: FormFillingPluginCtx | null,
|
|
6
|
+
conversationId?: string
|
|
7
|
+
) => {
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
if (ctx && conversationId) {
|
|
10
|
+
ctx.conversationId = conversationId;
|
|
11
|
+
}
|
|
12
|
+
}, [ctx, conversationId]);
|
|
13
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { TextUIPart } from 'ai';
|
|
2
|
+
import { FormFillingPluginCtx, IFieldsData } from '../types';
|
|
3
|
+
import { GetFieldJsonInfo, IsFieldsJsonPart } from '@/lib/utils';
|
|
4
|
+
import { FieldValueCheckerProps } from '@/components/biz-comp/FieldValueChecker';
|
|
5
|
+
import { valueNormalizer } from '../utils';
|
|
6
|
+
import { useEvtBus } from '@/hooks/use-evt-bus';
|
|
7
|
+
import { FILLING_EVENTS } from '../const';
|
|
8
|
+
import { IEvtBus } from '@baishuyun/types';
|
|
9
|
+
import { useMemo } from 'react';
|
|
10
|
+
|
|
11
|
+
// 解析消息中的字段数据,生成表单填写组件所需的数据结构
|
|
12
|
+
const extractFieldParts = (part: TextUIPart): TextUIPart[] => {
|
|
13
|
+
if (Array.isArray(part) && part.length > 0) return part;
|
|
14
|
+
if (IsFieldsJsonPart(part)) return [part];
|
|
15
|
+
return [];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// 判断是否为单条填写模式
|
|
19
|
+
const isSingleFillMode = (parts: TextUIPart[], ctx: FormFillingPluginCtx | null): boolean => {
|
|
20
|
+
return parts.length > 0 && ctx !== null && ctx.fillMode !== 'batch';
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// 原问题:onLoaded/onChange 在 render 中定义,导致子组件每次都会收到新的函数引用
|
|
24
|
+
// 改进:若性能敏感,应使用 useMemo 或拆分为独立组件
|
|
25
|
+
|
|
26
|
+
interface FieldConfig {
|
|
27
|
+
field: FieldValueCheckerProps['field'];
|
|
28
|
+
onLoaded: (f: any) => void;
|
|
29
|
+
onChange: FieldValueCheckerProps['onChange'];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const createFieldConfig = (
|
|
33
|
+
part: TextUIPart,
|
|
34
|
+
ctx: FormFillingPluginCtx,
|
|
35
|
+
evtBus: IEvtBus
|
|
36
|
+
): FieldConfig | null => {
|
|
37
|
+
const field = GetFieldJsonInfo(part);
|
|
38
|
+
if (!field) return null;
|
|
39
|
+
|
|
40
|
+
const isRegistered =
|
|
41
|
+
ctx.fieldMap?.has(field.fieldName) || ctx.subFieldsNameMap?.has(field.fieldName);
|
|
42
|
+
|
|
43
|
+
if (!isRegistered) {
|
|
44
|
+
console.warn('[useFieldsData] Unregistered field skipped:', field.fieldName);
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const normalizedValue = valueNormalizer(field.value as string | string[]);
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
field: {
|
|
52
|
+
label: field.label,
|
|
53
|
+
widget: {
|
|
54
|
+
widgetName: field.fieldName,
|
|
55
|
+
type: field.fieldType,
|
|
56
|
+
value: field.value,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
onLoaded: (instance) => {
|
|
60
|
+
instance.widget.value = normalizedValue;
|
|
61
|
+
ctx.valueMap.set(instance.widget?.widgetName, normalizedValue);
|
|
62
|
+
evtBus.emit(FILLING_EVENTS.FIELD_FILLED_UPDATE, {
|
|
63
|
+
field: instance,
|
|
64
|
+
checked: true,
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
onChange: (checked, instance, parentFieldName, rowIndex) => {
|
|
68
|
+
if (checked) {
|
|
69
|
+
ctx.valueMap.set(instance.widget?.widgetName, normalizedValue);
|
|
70
|
+
}
|
|
71
|
+
evtBus.emit(FILLING_EVENTS.FIELD_FILLED_UPDATE, {
|
|
72
|
+
field: instance,
|
|
73
|
+
checked,
|
|
74
|
+
parentFieldName,
|
|
75
|
+
rowIndex,
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const useFieldsData = (
|
|
82
|
+
part: TextUIPart,
|
|
83
|
+
formFillingCtx: FormFillingPluginCtx | null
|
|
84
|
+
): IFieldsData => {
|
|
85
|
+
const evtBus = useEvtBus();
|
|
86
|
+
|
|
87
|
+
const rawFieldParts = useMemo(() => extractFieldParts(part), [part]);
|
|
88
|
+
|
|
89
|
+
const isSingleMode = useMemo(
|
|
90
|
+
() => isSingleFillMode(rawFieldParts, formFillingCtx),
|
|
91
|
+
[rawFieldParts, formFillingCtx]
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const fieldConfigs = useMemo(() => {
|
|
95
|
+
if (!isSingleMode || !formFillingCtx) return [];
|
|
96
|
+
|
|
97
|
+
return rawFieldParts
|
|
98
|
+
.map((p) => createFieldConfig(p, formFillingCtx, evtBus))
|
|
99
|
+
.filter((config): config is FieldConfig => config !== null);
|
|
100
|
+
}, [rawFieldParts, formFillingCtx, evtBus, isSingleMode]);
|
|
101
|
+
|
|
102
|
+
return useMemo(
|
|
103
|
+
() => ({
|
|
104
|
+
fieldsParts: rawFieldParts,
|
|
105
|
+
fieldPropsArray: fieldConfigs,
|
|
106
|
+
filledFields: fieldConfigs.filter(Boolean),
|
|
107
|
+
}),
|
|
108
|
+
[rawFieldParts, fieldConfigs]
|
|
109
|
+
);
|
|
110
|
+
};
|