yt-chat-components 0.9.9 → 1.0.0

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.
@@ -19,8 +19,6 @@ H
19
19
  src/chatWidget/index.tsx,3/6/364385cedcce4c06de1901392ffeeac0caef0f3c
20
20
  =
21
21
 
22
- j
23
- :src/YtChatView/chatWidget/chatWindow/chatMessage/index.tsx,2/e/2e9f70bf32b414323ca1647e2b6f26e1533ee2fc
24
22
  e
25
23
  5src/YtChatView/chatWidget/chatWindow/index.module.css,d/7/d747cbed4201192dfa83a1a51345b020a050b647
26
24
  U
@@ -34,6 +32,4 @@ U
34
32
  A
35
33
  webpack.config.js,d/5/d5595158cc48f9bf3e51b06f6e6805a8fd2d6262
36
34
  S
37
- #src/chatWidget/chatWindow/index.tsx,6/c/6c024c1d0ad64656b9d4b0695ec3c49c0454addf
38
- =
39
-
35
+ #src/chatWidget/chatWindow/index.tsx,6/c/6c024c1d0ad64656b9d4b0695ec3c49c0454addf
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yt-chat-components",
3
- "version": "0.9.9",
3
+ "version": "1.0.0",
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",
package/public/index.html CHANGED
@@ -3,7 +3,8 @@
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Document</title>
6
+ <title>校园智多星</title>
7
+ <script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
7
8
  </head>
8
9
  <body style="width: 100vw; height: 100vh; position: relative; margin: unset ">
9
10
  <yt-chat
@@ -78,4 +79,30 @@
78
79
  <!-- is-show-upload-button=false-->
79
80
  <!--/>-->
80
81
  <!--</body>-->
82
+
83
+ <!--fd432d45-51fa-47cb-9ca9-a545385c618c de12e244-d603-4651-999f-25e22bdceeeb-->
84
+ <!--校园智多星sceneId=de12e244-d603-4651-999f-25e22bdceeeb appId=-->
85
+ <!--<body style="width: 100vw; height: 100vh; margin:0 ">-->
86
+ <!--<yt-page-chat-mobile-->
87
+ <!-- title="移动聊天"-->
88
+ <!-- host-url="https://ai-api.yuntu.cn"-->
89
+ <!-- app-id="a8e7ebd8-9bf7-499b-9e4a-c235078a0910"-->
90
+ <!-- user-info='{"id": "123", "name": "测试校长", "code":"1606451129" }'-->
91
+ <!-- tags='[{"name":"今天天气怎么样?"},{"name":"明天天气怎么样?"},{"name":"昨天天气怎么样?"}]'-->
92
+ <!-- box-style='{"height":"100%"}'-->
93
+ <!-- scene-id="de12e244-d603-4651-999f-25e22bdceeeb"-->
94
+ <!-- is-show-side-right=true-->
95
+ <!-- is-show-side-left=true-->
96
+ <!-- is-show-login=true-->
97
+ <!-- is-login=true-->
98
+ <!-- dialog-index="999999999"-->
99
+ <!-- agent-url="https://trans-from-yuntu-resourse.oss-cn-beijing.aliyuncs.com/smartSchool/appCreator/school/ccit/user/xc//image/ebfaf4da-c1d9-46fb-a0b1-f159e95cffc2_AI招生咨询小助手.png"-->
100
+ <!-- agent-name="校园智多星"-->
101
+ <!-- logo-width="42px"-->
102
+ <!-- logo-font-size="26px"-->
103
+ <!-- is-title-side-icon=true-->
104
+ <!-- is-show-upload-button=false-->
105
+ <!-- drop-man-url="https://trans-from-yuntu-resourse.oss-cn-beijing.aliyuncs.com/smartSchool/appCreator/ai/ai_man01.png"-->
106
+ <!--/>-->
107
+ <!--</body>-->
81
108
  </html>
@@ -1,12 +1,12 @@
1
1
  // @ts-nocheck
2
2
  import Markdown from 'react-markdown';
3
- import { ChatMessageType } from '../types/chatWidget';
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
- return (
134
- <div className={'cl-chat-message ' + (isSend ? ' cl-justify-end' : ' cl-justify-start')}>
135
- {isSend ? (
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
- <div key={item} className="msg_fileBox">
144
- {parseFileName(item).isImg && (
145
- <Image
146
- height={40}
147
- width={40}
148
- className="msg_messageImg"
149
- src={`${host_url}/api/v1/files/images/${item}`}
150
- alt={item}
151
- preview={{
152
- mask: <span className="custom-mask"></span>,
153
- }}
154
- />
155
- )}
156
- {getFileTypeByUrl(item) == 'pdf' && (
157
- <img style={{ width: 40, height: 40 }} src={typePdfPng} />
158
- )}
159
- {getFileTypeByUrl(item) == 'excel' && (
160
- <img style={{ width: 40, height: 40 }} src={typeExcelPng} />
161
- )}
162
- {getFileTypeByUrl(item) == 'markdown' && (
163
- <img style={{ width: 40, height: 40 }} src={typeMarkdownPng} />
164
- )}
165
- {getFileTypeByUrl(item) == 'txt' && (
166
- <img style={{ width: 40, height: 40 }} src={typeTextPng} />
167
- )}
168
- {getFileTypeByUrl(item) == 'word' && (
169
- <img style={{ width: 40, height: 40 }} src={typeWordPng} />
170
- )}
171
- {getFileTypeByUrl(item) == 'mobi' && (
172
- <img style={{ width: 40, height: 40 }} src={typeMobiPng} />
173
- )}
174
- {getFileTypeByUrl(item) == 'rpub' && (
175
- <img style={{ width: 40, height: 40 }} src={typeRPubPng} />
176
- )}
177
- {getFileTypeByUrl(item) == 'file' && (
178
- <img style={{ width: 40, height: 40 }} src={upFilePng} />
179
- )}
180
- <div className="msg_fileInfoBox">
181
- <div className="msg_fileInfoFileName">{parseFileName(item).fileName}</div>
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
- ) : error ? (
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
- className={'markdown-body prose flex flex-col word-break-break-word'}
197
- remarkPlugins={[remarkGfm]}
198
- rehypePlugins={[rehypeMathjax]}
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 embed_app_extend 扩展参数
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
  }