yt-chat-components 0.9.9-c → 0.9.9-d
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/package.json +2 -3
- package/src/YtChatView/chatWidget/chatWindow/chatMessage/index.tsx +305 -52
- package/src/YtChatView/chatWidget/chatWindow/controllers/index.ts +6 -3
- package/src/YtChatView/chatWidget/chatWindow/index.tsx +86 -34
- package/src/YtChatView/chatWidget/chatWindow/types/chatWidget/index.ts +13 -0
- package/src/YtChatView/chatWidget/index.tsx +5 -2
- package/src/YtChatView/mobileChat/index.jsx +7 -4
- package/src/YtChatView/previewDialog/index.jsx +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yt-chat-components",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.9d",
|
|
4
4
|
"main": "build/static/js/bundle.min.js",
|
|
5
5
|
"module": "build/static/js/bundle.min.js",
|
|
6
6
|
"types": "build/static/js/index.d.ts",
|
|
@@ -31,8 +31,7 @@
|
|
|
31
31
|
"typescript": "^4.9.5",
|
|
32
32
|
"uglifyjs-webpack-plugin": "^2.2.0",
|
|
33
33
|
"uuid": "^10.0.0",
|
|
34
|
-
"web-vitals": "^2.1.4"
|
|
35
|
-
"yt-chat-components": "^0.2.0"
|
|
34
|
+
"web-vitals": "^2.1.4"
|
|
36
35
|
},
|
|
37
36
|
"scripts": {
|
|
38
37
|
"start": "react-scripts start",
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
import Markdown from 'react-markdown';
|
|
3
|
-
import {
|
|
3
|
+
import {ChatMessageType, InputValueType, MessageType} from '../types/chatWidget';
|
|
4
4
|
import remarkGfm from 'remark-gfm';
|
|
5
5
|
import rehypeMathjax from 'rehype-mathjax';
|
|
6
6
|
// import './index.module.css';
|
|
7
7
|
import upFilePng from '../../../../assets/aicenter/upfile.png';
|
|
8
|
-
import { Image, message as messageTip } from 'antd';
|
|
9
|
-
import React, {useState} from 'react';
|
|
8
|
+
import {Form, Image, Input, message as messageTip, Select, DatePicker, Button} from 'antd';
|
|
9
|
+
import React, {useState, useRef} from 'react';
|
|
10
10
|
import typePdfPng from '../../../../assets/aicenter/type-pdf.png';
|
|
11
11
|
import typeWordPng from '../../../../assets/aicenter/type-word.png';
|
|
12
12
|
import typeExcelPng from '../../../../assets/aicenter/type-excel.png';
|
|
@@ -22,6 +22,8 @@ let speechSynth = window.speechSynthesis;
|
|
|
22
22
|
let utterance = null;
|
|
23
23
|
|
|
24
24
|
export default function ChatMessage({
|
|
25
|
+
type = MessageType.text,
|
|
26
|
+
rawInfo,
|
|
25
27
|
message,
|
|
26
28
|
isSend,
|
|
27
29
|
error,
|
|
@@ -29,6 +31,7 @@ export default function ChatMessage({
|
|
|
29
31
|
user_message_style,
|
|
30
32
|
bot_message_style,
|
|
31
33
|
error_message_style,
|
|
34
|
+
handleSendMessage
|
|
32
35
|
}: ChatMessageType) {
|
|
33
36
|
const parseFileName = (
|
|
34
37
|
text: string,
|
|
@@ -62,6 +65,10 @@ export default function ChatMessage({
|
|
|
62
65
|
};
|
|
63
66
|
|
|
64
67
|
const [isPlay, setIsPlay] = useState(false) // 是否正在播放文字
|
|
68
|
+
const [isSubmittingForm, setIsSubmittingForm] = useState(false) // 正在提交form
|
|
69
|
+
const [isShowFormBtns, setIsShowFormBtns] = useState(true) // 正在提交form
|
|
70
|
+
const formRef = useRef(null);
|
|
71
|
+
|
|
65
72
|
/**
|
|
66
73
|
* 根据文件URL获取文件类型
|
|
67
74
|
* @param url
|
|
@@ -130,9 +137,9 @@ export default function ChatMessage({
|
|
|
130
137
|
messageTip.error("消息框没有文字")
|
|
131
138
|
}
|
|
132
139
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
140
|
+
|
|
141
|
+
const renderUserMessage = () => {
|
|
142
|
+
return (
|
|
136
143
|
<div className="msg_userMessageBox">
|
|
137
144
|
<div style={user_message_style} className="cl-user_message">
|
|
138
145
|
{message.message}
|
|
@@ -140,62 +147,70 @@ export default function ChatMessage({
|
|
|
140
147
|
<div className="msg_messageImgBox">
|
|
141
148
|
{message.rawInfo?.files.map((item, index) => {
|
|
142
149
|
return (
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
150
|
+
<div key={item} className="msg_fileBox">
|
|
151
|
+
{parseFileName(item).isImg && (
|
|
152
|
+
<Image
|
|
153
|
+
height={40}
|
|
154
|
+
width={40}
|
|
155
|
+
className="msg_messageImg"
|
|
156
|
+
src={`${host_url}/api/v1/files/images/${item}`}
|
|
157
|
+
alt={item}
|
|
158
|
+
preview={{
|
|
159
|
+
mask: <span className="custom-mask"></span>,
|
|
160
|
+
}}
|
|
161
|
+
/>
|
|
162
|
+
)}
|
|
163
|
+
{getFileTypeByUrl(item) == 'pdf' && (
|
|
164
|
+
<img style={{ width: 40, height: 40 }} src={typePdfPng} />
|
|
165
|
+
)}
|
|
166
|
+
{getFileTypeByUrl(item) == 'excel' && (
|
|
167
|
+
<img style={{ width: 40, height: 40 }} src={typeExcelPng} />
|
|
168
|
+
)}
|
|
169
|
+
{getFileTypeByUrl(item) == 'markdown' && (
|
|
170
|
+
<img style={{ width: 40, height: 40 }} src={typeMarkdownPng} />
|
|
171
|
+
)}
|
|
172
|
+
{getFileTypeByUrl(item) == 'txt' && (
|
|
173
|
+
<img style={{ width: 40, height: 40 }} src={typeTextPng} />
|
|
174
|
+
)}
|
|
175
|
+
{getFileTypeByUrl(item) == 'word' && (
|
|
176
|
+
<img style={{ width: 40, height: 40 }} src={typeWordPng} />
|
|
177
|
+
)}
|
|
178
|
+
{getFileTypeByUrl(item) == 'mobi' && (
|
|
179
|
+
<img style={{ width: 40, height: 40 }} src={typeMobiPng} />
|
|
180
|
+
)}
|
|
181
|
+
{getFileTypeByUrl(item) == 'rpub' && (
|
|
182
|
+
<img style={{ width: 40, height: 40 }} src={typeRPubPng} />
|
|
183
|
+
)}
|
|
184
|
+
{getFileTypeByUrl(item) == 'file' && (
|
|
185
|
+
<img style={{ width: 40, height: 40 }} src={upFilePng} />
|
|
186
|
+
)}
|
|
187
|
+
<div className="msg_fileInfoBox">
|
|
188
|
+
<div className="msg_fileInfoFileName">{parseFileName(item).fileName}</div>
|
|
189
|
+
</div>
|
|
182
190
|
</div>
|
|
183
|
-
</div>
|
|
184
191
|
);
|
|
185
192
|
})}
|
|
186
193
|
</div>
|
|
187
194
|
</div>
|
|
188
|
-
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const renderErrorMessage = () => {
|
|
199
|
+
return (
|
|
189
200
|
<div style={error_message_style} className={'cl-error_message'}>
|
|
190
201
|
{message.message}
|
|
191
202
|
</div>
|
|
192
|
-
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const renderBotTextMessage = () => {
|
|
207
|
+
return (
|
|
193
208
|
<div>
|
|
194
209
|
<div style={bot_message_style} className={'cl-bot_message'}>
|
|
195
210
|
<Markdown
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
211
|
+
className={'markdown-body prose flex flex-col word-break-break-word'}
|
|
212
|
+
remarkPlugins={[remarkGfm]}
|
|
213
|
+
rehypePlugins={[rehypeMathjax]}
|
|
199
214
|
>
|
|
200
215
|
{message.message}
|
|
201
216
|
</Markdown>
|
|
@@ -205,7 +220,245 @@ export default function ChatMessage({
|
|
|
205
220
|
<img src={copyPng} onClick={()=>copyText(message.message)} />
|
|
206
221
|
</div>
|
|
207
222
|
</div>
|
|
208
|
-
|
|
223
|
+
)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const renderBotEchartMessage = () => {
|
|
227
|
+
return (
|
|
228
|
+
<div>
|
|
229
|
+
echart
|
|
230
|
+
</div>
|
|
231
|
+
)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const renderBotFormMessage = () => {
|
|
235
|
+
const { form_config } = rawInfo;
|
|
236
|
+
|
|
237
|
+
// 处理表单提交
|
|
238
|
+
const handleSubmit = (values) => {
|
|
239
|
+
console.log('表单提交数据:', values);
|
|
240
|
+
if (handleSendMessage) {
|
|
241
|
+
setIsSubmittingForm(true);
|
|
242
|
+
// TODO 提交的数据需要多一些
|
|
243
|
+
handleSendMessage(JSON.stringify(values), () => {
|
|
244
|
+
messageTip.success('表单提交成功');
|
|
245
|
+
setIsSubmittingForm(false);
|
|
246
|
+
setIsShowFormBtns(false);
|
|
247
|
+
}, InputValueType.form);
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// 处理表单取消
|
|
252
|
+
const handleCancel = () => {
|
|
253
|
+
if (formRef.current) {
|
|
254
|
+
formRef.current.resetFields();
|
|
255
|
+
}
|
|
256
|
+
if (handleSendMessage) {
|
|
257
|
+
setIsSubmittingForm(true);
|
|
258
|
+
handleSendMessage('', () => {
|
|
259
|
+
messageTip.success('取消成功');
|
|
260
|
+
setIsSubmittingForm(false);
|
|
261
|
+
setIsShowFormBtns(false);
|
|
262
|
+
}, InputValueType.form);
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
return (
|
|
267
|
+
<div className="form-container">
|
|
268
|
+
<div className="form-title">{form_config?.name || '表单'}</div>
|
|
269
|
+
<Form
|
|
270
|
+
ref={formRef}
|
|
271
|
+
layout="vertical"
|
|
272
|
+
onFinish={handleSubmit}
|
|
273
|
+
className="dynamic-form"
|
|
274
|
+
>
|
|
275
|
+
{form_config?.config?.map((item, index) => {
|
|
276
|
+
const { key, name, type, options } = item;
|
|
277
|
+
// 根据不同的表单项类型渲染不同的组件
|
|
278
|
+
switch (type) {
|
|
279
|
+
case 'text':
|
|
280
|
+
return (
|
|
281
|
+
<Form.Item
|
|
282
|
+
key={index}
|
|
283
|
+
label={name}
|
|
284
|
+
name={key}
|
|
285
|
+
rules={[{ required: true, message: `请输入${name}` }]}
|
|
286
|
+
>
|
|
287
|
+
<Input placeholder={`请输入${name}`} />
|
|
288
|
+
</Form.Item>
|
|
289
|
+
);
|
|
290
|
+
case 'date':
|
|
291
|
+
return (
|
|
292
|
+
<Form.Item
|
|
293
|
+
key={index}
|
|
294
|
+
label={name}
|
|
295
|
+
name={key}
|
|
296
|
+
rules={[{ required: true, message: `请选择${name}` }]}
|
|
297
|
+
>
|
|
298
|
+
<DatePicker style={{ width: '100%' }} placeholder={`请选择${name}`} />
|
|
299
|
+
</Form.Item>
|
|
300
|
+
);
|
|
301
|
+
case 'select':
|
|
302
|
+
return (
|
|
303
|
+
<Form.Item
|
|
304
|
+
key={index}
|
|
305
|
+
label={name}
|
|
306
|
+
name={key}
|
|
307
|
+
rules={[{ required: true, message: `请选择${name}` }]}
|
|
308
|
+
>
|
|
309
|
+
<Select
|
|
310
|
+
placeholder={`请选择${name}`}
|
|
311
|
+
style={{ width: '100%' }}
|
|
312
|
+
>
|
|
313
|
+
{options?.map((option, optIndex) => (
|
|
314
|
+
<Select.Option key={optIndex} value={option}>
|
|
315
|
+
{option}
|
|
316
|
+
</Select.Option>
|
|
317
|
+
))}
|
|
318
|
+
</Select>
|
|
319
|
+
</Form.Item>
|
|
320
|
+
);
|
|
321
|
+
default:
|
|
322
|
+
return (
|
|
323
|
+
<Form.Item
|
|
324
|
+
key={index}
|
|
325
|
+
label={name}
|
|
326
|
+
name={name}
|
|
327
|
+
>
|
|
328
|
+
<Input placeholder={`请输入${name}`} />
|
|
329
|
+
</Form.Item>
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
})}
|
|
333
|
+
|
|
334
|
+
{
|
|
335
|
+
isShowFormBtns && (
|
|
336
|
+
<div className="form-buttons">
|
|
337
|
+
<Form.Item>
|
|
338
|
+
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: '12px' }}>
|
|
339
|
+
<Button
|
|
340
|
+
loading={isSubmittingForm}
|
|
341
|
+
onClick={handleCancel}
|
|
342
|
+
>
|
|
343
|
+
取消
|
|
344
|
+
</Button>
|
|
345
|
+
<Button
|
|
346
|
+
loading={isSubmittingForm}
|
|
347
|
+
type="primary"
|
|
348
|
+
htmlType="submit"
|
|
349
|
+
style={{ backgroundColor: '#8064f6' }}
|
|
350
|
+
>
|
|
351
|
+
提交
|
|
352
|
+
</Button>
|
|
353
|
+
</div>
|
|
354
|
+
</Form.Item>
|
|
355
|
+
</div>
|
|
356
|
+
)
|
|
357
|
+
}
|
|
358
|
+
</Form>
|
|
359
|
+
|
|
360
|
+
<style jsx>{`
|
|
361
|
+
.form-container {
|
|
362
|
+
width: 100%;
|
|
363
|
+
padding: 16px;
|
|
364
|
+
border-radius: 8px;
|
|
365
|
+
background-color:rgb(244, 244, 244);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.form-title {
|
|
369
|
+
font-size: 18px;
|
|
370
|
+
font-weight: bold;
|
|
371
|
+
margin-bottom: 16px;
|
|
372
|
+
color: #333;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.dynamic-form {
|
|
376
|
+
width: 100%;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.form-select {
|
|
380
|
+
width: 100%;
|
|
381
|
+
height: 32px;
|
|
382
|
+
border: 1px solid #d9d9d9;
|
|
383
|
+
border-radius: 4px;
|
|
384
|
+
padding: 4px 11px;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
.form-buttons {
|
|
388
|
+
display: flex;
|
|
389
|
+
justify-content: flex-end;
|
|
390
|
+
margin-top: 20px;
|
|
391
|
+
gap: 12px;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.cancel-button {
|
|
395
|
+
padding: 6px 16px;
|
|
396
|
+
background-color: #f0f0f0;
|
|
397
|
+
border: 1px solid #d9d9d9;
|
|
398
|
+
border-radius: 4px;
|
|
399
|
+
cursor: pointer;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.submit-button {
|
|
403
|
+
padding: 6px 16px;
|
|
404
|
+
background-color: #8064f6;
|
|
405
|
+
color: white;
|
|
406
|
+
border: none;
|
|
407
|
+
border-radius: 4px;
|
|
408
|
+
cursor: pointer;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/* 移动端适配 */
|
|
412
|
+
@media (max-width: 768px) {
|
|
413
|
+
.form-container {
|
|
414
|
+
padding: 12px;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
.form-buttons {
|
|
418
|
+
flex-direction: column;
|
|
419
|
+
gap: 8px;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.cancel-button,
|
|
423
|
+
.submit-button {
|
|
424
|
+
width: 100%;
|
|
425
|
+
padding: 8px 0;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
`}</style>
|
|
429
|
+
</div>
|
|
430
|
+
);
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
const renderBotMessage = () => {
|
|
434
|
+
switch (type) {
|
|
435
|
+
case MessageType.echart:
|
|
436
|
+
return renderBotEchartMessage()
|
|
437
|
+
case MessageType.form:
|
|
438
|
+
return renderBotFormMessage()
|
|
439
|
+
case MessageType.text:
|
|
440
|
+
default:
|
|
441
|
+
return renderBotTextMessage()
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const render = () => {
|
|
446
|
+
if (isSend){
|
|
447
|
+
return renderUserMessage()
|
|
448
|
+
}else {
|
|
449
|
+
if (error){
|
|
450
|
+
return renderErrorMessage()
|
|
451
|
+
}else{
|
|
452
|
+
return renderBotMessage()
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return (
|
|
458
|
+
<div className={'cl-chat-message ' + (isSend ? ' cl-justify-end' : ' cl-justify-start')}>
|
|
459
|
+
{
|
|
460
|
+
render()
|
|
461
|
+
}
|
|
209
462
|
</div>
|
|
210
463
|
);
|
|
211
464
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
import axios from "axios";
|
|
3
|
+
import {InputValueType} from "../types/chatWidget";
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* 发送消息
|
|
7
|
-
* @param
|
|
8
|
-
* @param isStream
|
|
8
|
+
* @param input_value_type
|
|
9
|
+
* @param isStream
|
|
10
|
+
* @param embed_app_extend 扩展参数 * @param isStream 是否启用流式传输
|
|
9
11
|
* @param handleMessageContent 处理消息内容的回调函数
|
|
10
12
|
* @param signal 用于主动关闭请求流
|
|
11
13
|
* @param baseUrl 接口地址
|
|
@@ -20,6 +22,7 @@ import axios from "axios";
|
|
|
20
22
|
* @param additional_headers
|
|
21
23
|
*/
|
|
22
24
|
export async function sendMessage(
|
|
25
|
+
input_value_type: string = InputValueType.text,
|
|
23
26
|
embed_app_extend: object,
|
|
24
27
|
isStream: boolean,
|
|
25
28
|
handleMessageContent: Function,
|
|
@@ -38,7 +41,7 @@ export async function sendMessage(
|
|
|
38
41
|
},
|
|
39
42
|
) {
|
|
40
43
|
let data: any;
|
|
41
|
-
data = {input_type, input_value: message, output_type};
|
|
44
|
+
data = {input_value_type, input_type, input_value: message, output_type};
|
|
42
45
|
if (tweaks) {
|
|
43
46
|
data['tweaks'] = tweaks;
|
|
44
47
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
import { extractMessageFromOutput } from './utils';
|
|
3
3
|
import React, { useEffect, useRef, useState } from 'react';
|
|
4
|
-
import {
|
|
4
|
+
import {ChatMessageType, embedAppExtend, InputValueType, MessageType} from './types/chatWidget';
|
|
5
5
|
import ChatMessage from './chatMessage';
|
|
6
6
|
import { fetchUploadFile, getChatHistory, sendMessage } from './controllers';
|
|
7
7
|
import ChatMessagePlaceholder from './chatPlaceholder/index.tsx';
|
|
@@ -30,6 +30,7 @@ import btn_answer from '../../../assets/aicenter/btn_answer.png';
|
|
|
30
30
|
|
|
31
31
|
let mediaRecorder = null; // 语音对象,用于录音
|
|
32
32
|
let recognition = null; // 语音识别对象
|
|
33
|
+
const delayMessageList = []
|
|
33
34
|
|
|
34
35
|
export default function ChatWindow({
|
|
35
36
|
tags,
|
|
@@ -70,6 +71,7 @@ export default function ChatWindow({
|
|
|
70
71
|
setDropDownList = () => {},
|
|
71
72
|
dropDownList = [],
|
|
72
73
|
baseConfig = {},
|
|
74
|
+
isShowVoiceButton = true,
|
|
73
75
|
isShowUploadButton,
|
|
74
76
|
dropManUrl = '',
|
|
75
77
|
modalWidth,
|
|
@@ -112,6 +114,7 @@ export default function ChatWindow({
|
|
|
112
114
|
sessionId: string;
|
|
113
115
|
additional_headers?: { [key: string]: string };
|
|
114
116
|
baseConfig: object;
|
|
117
|
+
isShowVoiceButton: boolean;
|
|
115
118
|
isShowUploadButton: boolean;
|
|
116
119
|
dropManUrl: string;
|
|
117
120
|
modalWidth: number;
|
|
@@ -158,19 +161,57 @@ export default function ChatWindow({
|
|
|
158
161
|
}
|
|
159
162
|
};
|
|
160
163
|
|
|
164
|
+
// 处理延时队列,因为后端返回的是 token token form token token.....会导致form渲染在token前面,所以用这个方式
|
|
165
|
+
// 当处理token的时候,form的信息放到这里,然后使用 delayMessageTimer 处理这个延时队列
|
|
166
|
+
// 处理逻辑是:收到 token 的时候,重置timer,由于timer一直被重置所以不会处理 delayMessageList,直到 1.5内没有新 token 才会处理延时队列
|
|
167
|
+
const handleDelayMessage = () => {
|
|
168
|
+
if (delayMessageList.length > 0) {
|
|
169
|
+
const message = delayMessageList.shift();
|
|
170
|
+
addMessage(message);
|
|
171
|
+
handleDelayMessage();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const delayMessageTimer = (whenTimerExistsDoNothing: boolean = false) => {
|
|
176
|
+
if(window.delayMessageTimer)
|
|
177
|
+
if(whenTimerExistsDoNothing){
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
clearTimeout(window.delayMessageTimer);
|
|
181
|
+
window.delayMessageTimer = null
|
|
182
|
+
if(!window.delayMessageTimer){
|
|
183
|
+
window.delayMessageTimer = setTimeout(handleDelayMessage, 1500);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
161
187
|
// 流式输出消息,实时显示(token为流式输出内容,end为结束输出,整体输出一次)
|
|
162
188
|
const handleMessageContent = (event, data) => {
|
|
163
189
|
// console.error("event, data",event, data)
|
|
164
190
|
if (event == 'add_message' && data['sender'] == 'Machine') {
|
|
165
191
|
getHistoryList();
|
|
166
|
-
}
|
|
192
|
+
}
|
|
193
|
+
else if (event == 'token') {
|
|
167
194
|
setNowAIContent((prevState) => {
|
|
168
195
|
const newValue = prevState + data['chunk'];
|
|
169
196
|
contentRef.current = newValue
|
|
170
197
|
return newValue
|
|
171
198
|
});
|
|
172
199
|
if (lastMessage.current) lastMessage.current.scrollIntoView({ behavior: 'smooth' });
|
|
173
|
-
|
|
200
|
+
|
|
201
|
+
// 处理延时队列
|
|
202
|
+
delayMessageTimer();
|
|
203
|
+
}
|
|
204
|
+
else if (event == 'form') {
|
|
205
|
+
// 这里添加到延时队列,直接处理可能会把form渲染到token上面
|
|
206
|
+
delayMessageList.push({
|
|
207
|
+
message: "",
|
|
208
|
+
isSend: false,
|
|
209
|
+
rawInfo: data,
|
|
210
|
+
type: MessageType.form
|
|
211
|
+
})
|
|
212
|
+
delayMessageTimer(true)
|
|
213
|
+
}
|
|
214
|
+
else if (event == 'end') {
|
|
174
215
|
const res = {
|
|
175
216
|
data: data['result'],
|
|
176
217
|
};
|
|
@@ -295,7 +336,7 @@ export default function ChatWindow({
|
|
|
295
336
|
};
|
|
296
337
|
|
|
297
338
|
// 点击send发送按钮,进行消息提交发送逻辑;userMessage为传入的文本消息,优先级比input中value高
|
|
298
|
-
|
|
339
|
+
const handleSendMessage = (userMessage, callback = () => {}, input_value_type: string = InputValueType.text)=> {
|
|
299
340
|
let message = '';
|
|
300
341
|
if (value && value.trim() !== '') {
|
|
301
342
|
message = value;
|
|
@@ -307,13 +348,16 @@ export default function ChatWindow({
|
|
|
307
348
|
}
|
|
308
349
|
|
|
309
350
|
if (message && message.trim() !== '') {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
351
|
+
if(input_value_type === InputValueType.text){
|
|
352
|
+
addMessage({
|
|
353
|
+
message: message,
|
|
354
|
+
isSend: true,
|
|
355
|
+
rawInfo: { files: fileList.map((fileItem) => fileItem.fileUrl) },
|
|
356
|
+
});
|
|
357
|
+
setValue('');
|
|
358
|
+
setSendingMessage(true);
|
|
359
|
+
}
|
|
360
|
+
|
|
317
361
|
let userInfoClone = userInfo;
|
|
318
362
|
if(isEmpty(userInfoClone.code)) {
|
|
319
363
|
userInfoClone.code = sessionId;
|
|
@@ -341,6 +385,7 @@ export default function ChatWindow({
|
|
|
341
385
|
setFileList([]);
|
|
342
386
|
handleScroll();
|
|
343
387
|
sendMessage(
|
|
388
|
+
input_value_type,
|
|
344
389
|
embedAppExtend,
|
|
345
390
|
isStream,
|
|
346
391
|
handleMessageContent,
|
|
@@ -659,7 +704,7 @@ export default function ChatWindow({
|
|
|
659
704
|
return (
|
|
660
705
|
<div className={'cl-tips-wrapper'}>
|
|
661
706
|
<img className="drop-man-img" src={dropManUrl}/>
|
|
662
|
-
<div className="drop-down-title"><strong>Hi,</strong><br/><strong>欢迎使用{window_title}!</strong><br/><div>您可以这样问我:</div></div>
|
|
707
|
+
<div className="drop-down-title"><strong>Hi,</strong><br/><strong>欢迎使用{window_title}!</strong><br/><div style={{fontSize:'14px'}}>您可以这样问我:</div></div>
|
|
663
708
|
<div className={'cl-drop-down-mobile'}>
|
|
664
709
|
<div className="drop-down-list-mobile">
|
|
665
710
|
{dropDownList.map(({backgroundImg, title}) => (
|
|
@@ -668,7 +713,7 @@ export default function ChatWindow({
|
|
|
668
713
|
key={title}
|
|
669
714
|
onClick={() => {
|
|
670
715
|
setDropDownList(undefined);
|
|
671
|
-
|
|
716
|
+
handleSendMessage(title);
|
|
672
717
|
}}>
|
|
673
718
|
<Typography.Paragraph
|
|
674
719
|
className="drop-down-item-title-mobile"
|
|
@@ -706,7 +751,7 @@ export default function ChatWindow({
|
|
|
706
751
|
className="drop-down-item-bottom-button"
|
|
707
752
|
onClick={() => {
|
|
708
753
|
setDropDownList(undefined);
|
|
709
|
-
|
|
754
|
+
handleSendMessage(title);
|
|
710
755
|
}}
|
|
711
756
|
>
|
|
712
757
|
<img src={btn_answer}/>
|
|
@@ -732,7 +777,7 @@ export default function ChatWindow({
|
|
|
732
777
|
key={title}
|
|
733
778
|
onClick={() => {
|
|
734
779
|
setDropDownList(undefined);
|
|
735
|
-
|
|
780
|
+
handleSendMessage(title);
|
|
736
781
|
}}>
|
|
737
782
|
<Typography.Paragraph
|
|
738
783
|
className="drop-down-item-title"
|
|
@@ -746,7 +791,7 @@ export default function ChatWindow({
|
|
|
746
791
|
className="drop-down-item-bottom-button"
|
|
747
792
|
onClick={() => {
|
|
748
793
|
setDropDownList(undefined);
|
|
749
|
-
|
|
794
|
+
handleSendMessage(title);
|
|
750
795
|
}}
|
|
751
796
|
>
|
|
752
797
|
<img src={btn_answer}/>
|
|
@@ -799,10 +844,13 @@ export default function ChatWindow({
|
|
|
799
844
|
user_message_style={user_message_style}
|
|
800
845
|
error_message_style={error_message_style}
|
|
801
846
|
key={index}
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
847
|
+
host_url={hostUrl}
|
|
848
|
+
message={message}
|
|
849
|
+
isSend={message.isSend}
|
|
850
|
+
error={message.error}
|
|
851
|
+
type={message.type}
|
|
852
|
+
rawInfo={message.rawInfo}
|
|
853
|
+
handleSendMessage={handleSendMessage}
|
|
806
854
|
/>
|
|
807
855
|
))}
|
|
808
856
|
{sendingMessage && (nowAIContent?
|
|
@@ -814,6 +862,7 @@ export default function ChatWindow({
|
|
|
814
862
|
host_url={hostUrl}
|
|
815
863
|
message={{ message: nowAIContent}}
|
|
816
864
|
isSend={false}
|
|
865
|
+
handleSendMessage={handleSendMessage}
|
|
817
866
|
/>:<ChatMessagePlaceholder bot_message_style={bot_message_style} />
|
|
818
867
|
)}
|
|
819
868
|
<div ref={lastMessage}></div>
|
|
@@ -831,7 +880,7 @@ export default function ChatWindow({
|
|
|
831
880
|
key={index}
|
|
832
881
|
className="w_tagItemBox"
|
|
833
882
|
onClick={() => {
|
|
834
|
-
|
|
883
|
+
handleSendMessage(item?.name);
|
|
835
884
|
setFileList([]);
|
|
836
885
|
}}
|
|
837
886
|
>
|
|
@@ -922,7 +971,7 @@ export default function ChatWindow({
|
|
|
922
971
|
value={value}
|
|
923
972
|
onChange={(e) => setValue(e.target.value)}
|
|
924
973
|
onKeyDown={(e) => {
|
|
925
|
-
if (e.key === 'Enter' && !e.shiftKey)
|
|
974
|
+
if (e.key === 'Enter' && !e.shiftKey) handleSendMessage();
|
|
926
975
|
}}
|
|
927
976
|
disabled={sendingMessage}
|
|
928
977
|
placeholder={
|
|
@@ -934,15 +983,18 @@ export default function ChatWindow({
|
|
|
934
983
|
ref={inputRef}
|
|
935
984
|
className="cl-input-element"
|
|
936
985
|
/>
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
986
|
+
{
|
|
987
|
+
isShowVoiceButton &&
|
|
988
|
+
<Tooltip title={recordState ? "点击结束录音" : "点击开始录音"}>
|
|
989
|
+
<div
|
|
990
|
+
className="w_send_voice_box"
|
|
991
|
+
style={sendingMessage ? { cursor: 'not-allowed' } : {}}
|
|
992
|
+
onClick={startRecord}
|
|
993
|
+
>
|
|
994
|
+
<img src={recordState ? soundWavePng : luyinPng} style={{ width: 23 }} className={recordState ? "w_recordIng" : ''}></img>
|
|
995
|
+
</div>
|
|
996
|
+
</Tooltip>
|
|
997
|
+
}
|
|
946
998
|
{/*<Tooltip title="支持PDF / Word / Excel / Markdown / txt / mobi / rpub">*/}
|
|
947
999
|
{
|
|
948
1000
|
isShowUploadButton &&
|
|
@@ -973,13 +1025,13 @@ export default function ChatWindow({
|
|
|
973
1025
|
abortControllerRef.current.abort('disconnect');
|
|
974
1026
|
abortControllerRef.current = new AbortController();
|
|
975
1027
|
}else{
|
|
976
|
-
|
|
1028
|
+
handleSendMessage()
|
|
977
1029
|
}
|
|
978
1030
|
}}
|
|
979
1031
|
>
|
|
980
1032
|
|
|
981
|
-
|
|
982
|
-
|
|
1033
|
+
<img src={sendingMessage ? stopmessagePng : sendmessagePng} style={{width: 55}}/>
|
|
1034
|
+
</button>
|
|
983
1035
|
</Tooltip>
|
|
984
1036
|
</div>
|
|
985
1037
|
</div>
|
|
@@ -11,8 +11,21 @@ export type ChatMessageType = {
|
|
|
11
11
|
user_message_style?: React.CSSProperties;
|
|
12
12
|
error_message_style?: React.CSSProperties;
|
|
13
13
|
rawInfo?: any;
|
|
14
|
+
type?: MessageType,
|
|
15
|
+
handleSendMessage?: (message: string, callback?: Function) => void;
|
|
14
16
|
};
|
|
15
17
|
|
|
18
|
+
export enum MessageType {
|
|
19
|
+
text,
|
|
20
|
+
form,
|
|
21
|
+
echart
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const InputValueType = {
|
|
25
|
+
text: "text",
|
|
26
|
+
form : "form",
|
|
27
|
+
}
|
|
28
|
+
|
|
16
29
|
export type ChatMessagePlaceholderType = {
|
|
17
30
|
bot_message_style?: React.CSSProperties;
|
|
18
31
|
};
|
|
@@ -37,6 +37,7 @@ export default function ChatWidget({
|
|
|
37
37
|
setDropDownList,
|
|
38
38
|
dropDownList,
|
|
39
39
|
baseConfig,
|
|
40
|
+
isShowVoiceButton,
|
|
40
41
|
isShowUploadButton,
|
|
41
42
|
dropManUrl,
|
|
42
43
|
modalWidth,
|
|
@@ -75,6 +76,7 @@ export default function ChatWidget({
|
|
|
75
76
|
session_id?: string;
|
|
76
77
|
start_open?: boolean;
|
|
77
78
|
baseConfig:object;
|
|
79
|
+
isShowVoiceButton:boolean;
|
|
78
80
|
isShowUploadButton:boolean;
|
|
79
81
|
dropManUrl: string;
|
|
80
82
|
modalWidth: number;
|
|
@@ -497,14 +499,14 @@ video {
|
|
|
497
499
|
margin-top: 45px;
|
|
498
500
|
font-family: PingFangSC, PingFang SC;
|
|
499
501
|
font-weight: 500;
|
|
500
|
-
font-size:
|
|
502
|
+
font-size: 18px;
|
|
501
503
|
color: #333333;
|
|
502
504
|
line-height: 28px;
|
|
503
505
|
text-align: left;
|
|
504
506
|
font-style: normal;
|
|
505
507
|
> div {
|
|
506
508
|
margin-top: 15px;
|
|
507
|
-
font-size:
|
|
509
|
+
font-size: 12px;
|
|
508
510
|
color: # 999;
|
|
509
511
|
}
|
|
510
512
|
}
|
|
@@ -2572,6 +2574,7 @@ input::-ms-input-placeholder { /* Microsoft Edge */
|
|
|
2572
2574
|
setDropDownList={setDropDownList}
|
|
2573
2575
|
dropDownList={dropDownList}
|
|
2574
2576
|
baseConfig={baseConfig}
|
|
2577
|
+
isShowVoiceButton={isShowVoiceButton}
|
|
2575
2578
|
isShowUploadButton={isShowUploadButton}
|
|
2576
2579
|
dropManUrl={dropManUrl}
|
|
2577
2580
|
modalWidth={modalWidth}
|
|
@@ -487,6 +487,7 @@ export class MobileChatPage extends React.Component {
|
|
|
487
487
|
logoFontSize = '26px',
|
|
488
488
|
isTitleSideIcon = false,// 是否将左侧历史顶部icon移到中间顶部左侧蓝点位置
|
|
489
489
|
modalIndex,
|
|
490
|
+
isShowVoiceButton,
|
|
490
491
|
isShowUploadButton,
|
|
491
492
|
dropManUrl,
|
|
492
493
|
modalWidth,
|
|
@@ -528,7 +529,7 @@ export class MobileChatPage extends React.Component {
|
|
|
528
529
|
onClose={() => this.setState({drawerVisible: false})}
|
|
529
530
|
open={this.state.drawerVisible}
|
|
530
531
|
>
|
|
531
|
-
<div className="drawer-history-header" style={{padding: '54px 14px 14px 14px', background: 'linear-gradient( 71deg, #E1E9FF 0%, #FFFFFF 100%)', position: "relative"}}>
|
|
532
|
+
<div className="drawer-history-header" style={{marginTop:0, padding: '54px 14px 14px 14px', background: 'linear-gradient( 71deg, #E1E9FF 0%, #FFFFFF 100%)', position: "relative"}}>
|
|
532
533
|
<img src={icon_history_headerbg} style={{position: 'absolute', right: 0, bottom: 0, width: '40%', zIndex:0}}/>
|
|
533
534
|
<div className="drawer-history-header-title" style={{display: 'flex', flexDirection: 'row', alignItems: 'center'}}>
|
|
534
535
|
<img style={{width: 35}} src={agentUrl}/>
|
|
@@ -674,6 +675,7 @@ export class MobileChatPage extends React.Component {
|
|
|
674
675
|
setDropDownList={(list) => this.setState({dropDownList: list})}
|
|
675
676
|
dropDownList={this.state.dropDownList}
|
|
676
677
|
baseConfig={{isTitleSideIcon, logoWidth, agentUrl}}
|
|
678
|
+
isShowVoiceButton={isShowVoiceButton}
|
|
677
679
|
isShowUploadButton={isShowUploadButton}
|
|
678
680
|
dropManUrl={character || dropManUrl}
|
|
679
681
|
modalWidth={modalWidth}
|
|
@@ -765,6 +767,7 @@ export class MobileChatPage extends React.Component {
|
|
|
765
767
|
background: '#fff',
|
|
766
768
|
borderRadius: 22,
|
|
767
769
|
opacity: 0.7,
|
|
770
|
+
width:'100%',
|
|
768
771
|
height: '45vw',
|
|
769
772
|
boxSizing: 'border-box',
|
|
770
773
|
padding: '18px',
|
|
@@ -784,10 +787,10 @@ export class MobileChatPage extends React.Component {
|
|
|
784
787
|
<img
|
|
785
788
|
src={item.icon}
|
|
786
789
|
alt={item.name}
|
|
787
|
-
style={{width: '
|
|
790
|
+
style={{width: '40px', height: '40px'}}
|
|
788
791
|
/>
|
|
789
|
-
<div style={{marginTop: '12px', color: '#333333', fontWeight: "bold", fontSize: '
|
|
790
|
-
<div style={{marginTop: '8px', color: '#333333', fontSize: '
|
|
792
|
+
<div style={{marginTop: '12px', color: '#333333', fontWeight: "bold", fontSize: '14px', textAlign: "left"}}>{item.name}</div>
|
|
793
|
+
<div style={{marginTop: '8px', color: '#333333', fontSize: '10px', textAlign: "left"}}>{item.content}</div>
|
|
791
794
|
</div>
|
|
792
795
|
))}
|
|
793
796
|
</div>
|
|
@@ -453,7 +453,7 @@ export class ToolDialog extends React.Component {
|
|
|
453
453
|
{
|
|
454
454
|
isShowSideLeft && <div className="p_toolLeft">
|
|
455
455
|
<div className="p_toolLogo">
|
|
456
|
-
<img className="p_logoImg" style={{ width: logoWidth }} src={agentUrl}/>
|
|
456
|
+
<img className="p_logoImg" style={{ width: logoWidth, borderRadius: 15 }} src={agentUrl}/>
|
|
457
457
|
<div className="p_logoText" style={{ fontSize: logoFontSize }}>{agentName}</div>
|
|
458
458
|
</div>
|
|
459
459
|
<div className="p_historyDialog">
|