ai-chat-bot-interface 1.7.7 → 1.7.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/assets/index-DQulfwi9.css +9 -0
  2. package/dist/assets/index-Dkddqe_3.js +123 -0
  3. package/{index.html → dist/index.html} +2 -1
  4. package/dist/src/index.d.ts +0 -0
  5. package/package.json +26 -5
  6. package/.prettierignore +0 -5
  7. package/.prettierrc.cjs +0 -37
  8. package/.vscode/extensions.json +0 -3
  9. package/index.js +0 -11
  10. package/src/App.vue +0 -39
  11. package/src/ChatUi.less +0 -301
  12. package/src/ChatUi.vue +0 -1087
  13. package/src/assets/styles/public.less +0 -152
  14. package/src/assets/vue.svg +0 -1
  15. package/src/components/DishesCard.vue +0 -369
  16. package/src/components/DishesList.vue +0 -207
  17. package/src/components/MarkdownPlan/MarkdownViewer.vue +0 -34
  18. package/src/components/OperateModule.less +0 -186
  19. package/src/components/OperateModule.vue +0 -392
  20. package/src/components/PlanCard.vue +0 -114
  21. package/src/components/StoreList/StoreCard.vue +0 -72
  22. package/src/components/StoreList/StoreList.vue +0 -27
  23. package/src/components/StoreList/mock.js +0 -411
  24. package/src/components/assistantReplay/assistantReplay.vue +0 -78
  25. package/src/components/icons/ArrowDown.vue +0 -26
  26. package/src/components/icons/ArrowRight.vue +0 -19
  27. package/src/components/icons/BackIcon.vue +0 -19
  28. package/src/components/icons/ClearIcon.vue +0 -18
  29. package/src/components/icons/CloseIcon.vue +0 -17
  30. package/src/components/icons/NewSessionIcon.vue +0 -20
  31. package/src/components/icons/OkIcon.vue +0 -26
  32. package/src/components/icons/SendIcon.vue +0 -22
  33. package/src/components/icons/ThinkingIcon.vue +0 -28
  34. package/src/components/icons/addIcon.vue +0 -18
  35. package/src/components/icons/cameraIcon.vue +0 -22
  36. package/src/components/icons/closeBorderIcon.vue +0 -35
  37. package/src/components/icons/fileIcon.vue +0 -18
  38. package/src/components/icons/loadingIcon.vue +0 -76
  39. package/src/components/icons/loadingIcon2.vue +0 -88
  40. package/src/components/icons/newChat.vue +0 -18
  41. package/src/components/icons/pictureIcon.vue +0 -22
  42. package/src/components/icons/processBar.vue +0 -115
  43. package/src/components/icons/progressRing.vue +0 -63
  44. package/src/components/icons/sendLoadingIcon.vue +0 -35
  45. package/src/components/icons/tagIcon.vue +0 -18
  46. package/src/components/imgeList.vue +0 -63
  47. package/src/components/personalForm/personalForm.vue +0 -634
  48. package/src/components/popup/popup.vue +0 -178
  49. package/src/main.js +0 -26
  50. package/src/style.css +0 -4
  51. package/src/utils/imagesViewer.js +0 -8
  52. package/src/utils/request.js +0 -52
  53. package/src/utils/tools.js +0 -20
  54. package/vite.config.js +0 -33
  55. /package/{public → dist}/vite.svg +0 -0
package/src/ChatUi.vue DELETED
@@ -1,1087 +0,0 @@
1
- <template>
2
- <div class="cui_box">
3
- <div class="cui_wrap">
4
- <div v-if="showHeader" class="cui_header">
5
- <div class="title" @click.stop="queryHistoryList">
6
- <div class="back" @click.stop="handleBack">
7
- <back-icon />
8
- </div>
9
- <div class="name_box">
10
- <div class="name">{{ name }}</div>
11
- <div v-if="nameSub" class="name_sub">{{ nameSub }}</div>
12
- </div>
13
- </div>
14
- <div class="btn_group">
15
- <button class="btn" @click.stop="createConv">
16
- <new-chat class="icon" />
17
- <span style="font-size: 14px">{{ finalDefMsg.newChat }}</span>
18
- </button>
19
- <template v-show="false">
20
- <button class="btn">
21
- <clear-icon />
22
- </button>
23
- <button class="btn">
24
- <close-icon />
25
- </button>
26
- </template>
27
- </div>
28
- </div>
29
- <div class="cui_content">
30
- <div
31
- v-if="botInfo && botInfo.onboarding_info"
32
- style="text-align: left; margin-top: 50px"
33
- >
34
- <div style="text-align: center">
35
- <img
36
- :src="botInfo.icon_url"
37
- alt="icon"
38
- width="64"
39
- height="64"
40
- style="border-radius: 15px"
41
- />
42
- <p class="board_name">{{ botInfo.name }}</p>
43
- </div>
44
-
45
- <div class="board_desc">{{ botInfo.onboarding_info.prologue }}</div>
46
- <div class="flexcss">
47
- <span
48
- v-for="(item, idx) in botInfo.onboarding_info.suggested_questions"
49
- :key="idx"
50
- class="board_sug"
51
- @click.stop="chatConv([{ content: item, text: item }])"
52
- >{{ item }}</span
53
- >
54
- </div>
55
- </div>
56
- <template v-for="(conv, index) in historyList" :key="index">
57
- <div v-if="conv.role === 'assistant'" class="replay role_sys">
58
- <div class="replay_content">
59
- <div class="name">
60
- <img class="avatar" :src="logo" alt="avatar" />
61
- {{ name }}
62
- <!--<span class="time">12:30</span>-->
63
- </div>
64
- <div class="box">
65
- <template v-if="conv.reasoning_content">
66
- <div v-if="conv.status === 'thinking'" class="think_status">
67
- <thinking-icon class="icon" />
68
- {{ finalDefMsg.thinking }}
69
- </div>
70
- <div v-else class="think_status">
71
- <ok-icon class="icon" />
72
- {{ finalDefMsg.thinkCompleted }}
73
- </div>
74
-
75
- <p class="think_text">
76
- {{ conv.reasoning_content }}
77
- </p>
78
- </template>
79
- <template v-if="conv.content">
80
- <assistant-replay
81
- v-if="contentTye === 'text'"
82
- :content="conv.content"
83
- />
84
- <markdown-viewer
85
- v-else-if="contentTye === 'markdown'"
86
- :content="conv.content"
87
- />
88
- <template v-if="!isAnswering">
89
- <div v-if="conv.extra.length">
90
- <template v-for="(comp, idx) in conv.extra" :key="idx">
91
- <!-- <p
92
- v-if="comp.showType === 'plan' && comp.planParse"
93
- class="text"
94
- style="margin-top: 1em"
95
- >
96
- {{ comp.planParse }}
97
- </p>-->
98
- <dishes-list
99
- v-if="
100
- finalCardList.dishes && comp.showType === 'card'
101
- "
102
- :sku-list="comp.skuList"
103
- :is-mini="!showHeader"
104
- :def-msg="finalDefMsg"
105
- :channel="channel"
106
- @select="(data) => handleCardTap(data, comp)"
107
- />
108
- <plan-card
109
- v-if="finalCardList.plan && comp.showType === 'plan'"
110
- :info="comp"
111
- :def-msg="finalDefMsg"
112
- @select="handleCardTap({ type: 'match' }, comp)"
113
- />
114
- <store-list
115
- v-if="
116
- finalCardList.store && comp.showType === 'store'
117
- "
118
- :list="comp.storeList"
119
- @select="handleStoreSel"
120
- />
121
- <personal-form
122
- v-if="
123
- finalCardList.personalForm &&
124
- comp.showType === 'personalForm'
125
- "
126
- :query="firstMsg"
127
- @submit="chatConv"
128
- />
129
- <div v-if="comp.showType === 'contactKF'">
130
- 电话热线:<a href="tel:4006681606">400 6681 606</a>
131
- <img
132
- src="https://prodstatic.weis1606.cn/api/smartFood/mealPlan/contact_kf.png"
133
- alt=""
134
- width="100%"
135
- style="margin-top: 10px"
136
- />
137
- </div>
138
- </template>
139
- </div>
140
- <div v-else-if="handleTextNeedBtn(conv.content)">
141
- <div
142
- class="cui_btn cui_btn_2"
143
- @click.stop="handleCardTap({ type: 'match' }, {})"
144
- >
145
- {{ finalDefMsg.useSolution }}
146
- </div>
147
- </div>
148
- </template>
149
- </template>
150
- <div
151
- v-if="isAnswering && index === historyList.length - 1"
152
- class="loading_row"
153
- >
154
- Ai努力生成中...
155
- <loading-icon2 />
156
- </div>
157
- </div>
158
- </div>
159
- </div>
160
- <div v-else class="replay role_user">
161
- <div class="replay_content">
162
- <div class="name">
163
- 健康用户
164
- <!--<span class="time">12:30</span>-->
165
- <img class="avatar" :src="avatar" alt="avatar" />
166
- </div>
167
- <div class="box">
168
- <p class="text" v-html="conv.content" />
169
- <div v-if="conv.extra.length">
170
- <imge-list :list="conv.extra" />
171
- </div>
172
- </div>
173
- </div>
174
- </div>
175
- </template>
176
- <div ref="endTarget" style="height: 100px" />
177
- </div>
178
- <operate-module
179
- v-model="inputText"
180
- :new-chat="!showHeader"
181
- :token="token"
182
- :tag-list="tagList"
183
- :def-msg="finalDefMsg"
184
- :is-req="isReq"
185
- @send="chatConv"
186
- @tag="handleTagSel"
187
- @call="handleCall"
188
- />
189
- </div>
190
- </div>
191
- </template>
192
-
193
- <script setup>
194
- import { computed, nextTick, onMounted, provide, ref } from 'vue';
195
- import ClearIcon from './components/icons/ClearIcon.vue';
196
- import CloseIcon from './components/icons/CloseIcon.vue';
197
- import NewSessionIcon from './components/icons/NewSessionIcon.vue';
198
- import SendIcon from './components/icons/SendIcon.vue';
199
- import { get, post } from './utils/request';
200
- import DishesCard from './components/DishesCard.vue';
201
- import DishesList from './components/DishesList.vue';
202
- import PlanCard from './components/PlanCard.vue';
203
- import OperateModule from './components/OperateModule.vue';
204
- import BackIcon from './components/icons/BackIcon.vue';
205
- import NewChat from './components/icons/newChat.vue';
206
- import LoadingIcon from './components/icons/loadingIcon.vue';
207
- import LoadingIcon2 from './components/icons/loadingIcon2.vue';
208
- import ImgeList from './components/imgeList.vue';
209
- import ThinkingIcon from './components/icons/ThinkingIcon.vue';
210
- import OkIcon from './components/icons/OkIcon.vue';
211
- import AssistantReplay from './components/assistantReplay/assistantReplay.vue';
212
- import StoreList from './components/StoreList/StoreList.vue';
213
- import MarkdownViewer from './components/MarkdownPlan/MarkdownViewer.vue';
214
- import PersonalForm from './components/personalForm/personalForm.vue';
215
- import processBar from './components/icons/processBar.vue';
216
-
217
- const chatOptions = computed(() => {
218
- return {
219
- headers: {
220
- Authorization: `Bearer ${props.token}`,
221
- 'Content-Type': 'application/json',
222
- },
223
- };
224
- });
225
- const conversationId = ref('');
226
- const isAnswering = ref(false);
227
- const isReq = ref('00');
228
- const historyList = ref([]);
229
- const isFirst = ref(false);
230
- const turns = ref(1);
231
-
232
- const msgObj = {
233
- placeholder: '發消息⋯',
234
- fileText: '請根據我上傳的體檢報告為我生成飲食方案',
235
- uploadingTips: '文件正在上傳,請稍候...',
236
- matchText: '請用以上方案為我配餐',
237
- matchContent: '請用以上方案為我配餐',
238
- energy: '熱量',
239
- protein: '蛋白質',
240
- fat: '脂肪',
241
- carbonwater: '碳水',
242
- newChat: '新對話',
243
- thinking: '思考中...',
244
- thinkCompleted: '思考完成',
245
- useSolution: '用該方案配餐',
246
- aiTips: '【内容由AI生成,仅供参考】',
247
- };
248
- const defCardList = {
249
- store: true,
250
- dishes: true,
251
- plan: true,
252
- personalForm: false,
253
- };
254
-
255
- const props = defineProps({
256
- logo: {
257
- type: String,
258
- default: 'https://prodstatic.weis1606.cn/api/smartFood/icon/weis_logo.png',
259
- },
260
- avatar: {
261
- type: String,
262
- default: 'https://prodstatic.weis1606.cn/api/smartFood/icon/def_gray.svg',
263
- },
264
- name: {
265
- type: String,
266
- default: 'Weis Bot',
267
- },
268
- nameSub: {
269
- type: String,
270
- default: 'nutribite.com',
271
- },
272
- botId: {
273
- type: String,
274
- required: true,
275
- },
276
- uid: {
277
- type: String,
278
- required: true,
279
- default: 'user',
280
- },
281
- channel: {
282
- type: String,
283
- default: 'web',
284
- },
285
- token: {
286
- type: String,
287
- required: true,
288
- },
289
- postToken: {
290
- type: String,
291
- default: '',
292
- },
293
- showHeader: {
294
- type: Boolean,
295
- default: true,
296
- },
297
- tagList: {
298
- type: Array,
299
- default: () => [
300
- { name: '人工客服', value: 'kefu', type: 'chat', msg: '人工客服' },
301
- { name: '查看菜单', value: 'menu', type: 'call', msg: '查看菜单' },
302
- { name: '体检报告', value: 'exam', type: 'upload', msg: '体检报告' },
303
- ],
304
- },
305
- defMsg: {
306
- type: Object,
307
- default: () => ({}),
308
- },
309
- contentTye: {
310
- type: String,
311
- default: 'text',
312
- },
313
- cardList: {
314
- type: Object,
315
- default: () => ({
316
- store: true,
317
- dishes: true,
318
- plan: true,
319
- personalForm: false,
320
- }),
321
- },
322
- storage: {
323
- type: String,
324
- default: 'sessionStorage',
325
- },
326
- needLog: {
327
- type: Boolean,
328
- default: false,
329
- },
330
- firstMsg: {
331
- type: String,
332
- default: '',
333
- },
334
- });
335
-
336
- const Emits = defineEmits(['call', 'log']);
337
-
338
- const finalDefMsg = computed(() => {
339
- return {
340
- ...msgObj,
341
- ...props.defMsg,
342
- };
343
- });
344
- const finalCardList = computed(() => {
345
- return {
346
- ...defCardList,
347
- ...props.cardList,
348
- };
349
- });
350
-
351
- const endTarget = ref(null);
352
- const inputText = ref('');
353
- const botInfo = ref({});
354
-
355
- const storage = {
356
- setItem: (key, value) => {
357
- props.storage === 'sessionStorage'
358
- ? sessionStorage.setItem(key, value)
359
- : localStorage.setItem(key, value);
360
- },
361
- getItem: (key) => {
362
- return props.storage === 'sessionStorage'
363
- ? sessionStorage.getItem(key)
364
- : localStorage.getItem(key);
365
- },
366
- };
367
-
368
- onMounted(async () => {
369
- localStorage.setItem('postToken', props.postToken || '');
370
- if (storage.getItem('conversationId')) {
371
- conversationId.value = storage.getItem('conversationId');
372
- await queryBotInfo();
373
- await queryHistoryList();
374
- isFirst.value = false;
375
- } else {
376
- await createConv();
377
- if (props.firstMsg) {
378
- await handleFirstMsg();
379
- }
380
- }
381
- scrollToEnd();
382
- });
383
-
384
- const handleFirstMsg = async () => {
385
- const presetList = [
386
- {
387
- params: {
388
- planType: '03', // 03保持,01减肥
389
- profileInfo: {
390
- birthday: '20010101',
391
- motion: '01',
392
- sex: '2',
393
- weight: '50',
394
- height: '160',
395
- },
396
- },
397
- age: 25,
398
- msg: `本食谱为均衡营养版${props.firstMsg}的食疗调养方案示例,依据以下女性常规身体数据制定:
399
- 性别:女;
400
- 年龄:25岁;
401
- 身高:160cm;
402
- 体重:50kg;
403
- 日常运动水平:久坐;
404
- 口味偏好及饮食禁忌:无;
405
- 如需定制贴合个人情况的专属方案,请前往完善个人身体数据,获取个性化定制食谱。`,
406
- },
407
- {
408
- params: {
409
- planType: '03',
410
- profileInfo: {
411
- birthday: '20010101',
412
- motion: '01',
413
- sex: '1',
414
- weight: '65',
415
- height: '170',
416
- },
417
- },
418
- age: 25,
419
- msg: `本食谱为均衡营养版${props.firstMsg}的食疗调养方案示例,依据以下男性常规身体数据制定:
420
- 性别:男;
421
- 年龄:25岁;
422
- 身高:170cm;
423
- 体重:65kg;
424
- 日常运动水平:久坐;
425
- 口味偏好及饮食禁忌:无;
426
- 如需定制贴合个人情况的专属方案,请前往完善个人身体数据,获取个性化定制食谱。`,
427
- },
428
- {
429
- params: {
430
- planType: '01',
431
- profileInfo: {
432
- birthday: '19910101',
433
- motion: '01',
434
- sex: '2',
435
- weight: '65',
436
- height: '160',
437
- },
438
- },
439
- age: 35,
440
- msg: `本食谱为控卡版${props.firstMsg}的食疗调养方案示例,依据以下女性常规身体数据制定:
441
- 性别:女;
442
- 年龄:35岁;
443
- 身高:160cm;
444
- 体重:65kg;
445
- 日常运动水平:久坐;
446
- 口味偏好及饮食禁忌:无;
447
- 如需定制贴合个人情况的专属方案,请前往完善个人身体数据,获取个性化定制食谱。`,
448
- },
449
- {
450
- params: {
451
- planType: '01',
452
- profileInfo: {
453
- birthday: '19910101',
454
- motion: '01',
455
- sex: '1',
456
- weight: '75',
457
- height: '170',
458
- },
459
- },
460
- age: 35,
461
- msg: `本食谱为控卡版${props.firstMsg}的食疗调养方案示例,依据以下男性常规身体数据制定:
462
- 性别:男;
463
- 年龄:35岁;
464
- 身高:170cm;
465
- 体重:75kg;
466
- 日常运动水平:久坐;
467
- 口味偏好及饮食禁忌:无;
468
- 如需定制贴合个人情况的专属方案,请前往完善个人身体数据,获取个性化定制食谱。`,
469
- },
470
- ];
471
- const categoryMap = {
472
- '01': '早餐',
473
- '02': '午餐',
474
- '03': '晚餐',
475
- '04': '加餐',
476
- };
477
- let p = localStorage.getItem('preset_point') || 0;
478
- p++;
479
- if (p >= presetList.length) {
480
- p = 0;
481
- }
482
-
483
- localStorage.setItem('preset_point', p);
484
- const curInfo = presetList[p];
485
- const curMsg = `帮我定制${props.firstMsg}的⻝疗调养⽅案`;
486
- const perMsg = curMsg;
487
-
488
- historyList.value = [
489
- {
490
- conversation_id: '',
491
- bot_id: props.botId,
492
- role: 'user',
493
- content: perMsg,
494
- reasoning_content: '',
495
- status: 'ended',
496
- extra: [],
497
- },
498
- ];
499
-
500
- const res = await post('/api/cn.weis.api.Food/bdGeneralTemp', {
501
- method: 'bdGeneralTemp',
502
- params: [{ ...curInfo.params }],
503
- token: props.postToken,
504
- });
505
-
506
- if (res.errCode === 0 && res.obj) {
507
- const cateMap = {};
508
- (res.obj.planDetails || []).forEach((item) => {
509
- cateMap[item.category] = {
510
- energy: `热量:${item.totalKcal}kcal`,
511
- carbonWater: `碳水:${item.carbohydrateTotal}g`,
512
- protein: `蛋白质:${item.proteinTotal}g`,
513
- fat: `脂肪:${item.fatTotal}g`,
514
- };
515
- });
516
-
517
- // const resMsg = `您的身高:${curInfo.params.profileInfo.height}cm 年龄:${
518
- // curInfo.age
519
- // } 岁 体重:${curInfo.params.profileInfo.weight}kg 性别:${
520
- // curInfo.params.profileInfo.sex === '2' ? '女' : '男'
521
- // } 日常运动水平:久坐
522
- // 根据您的身体状况,给您推荐的饮食方案如下
523
-
524
- const resMsg = `我将为您出通用的食谱建议,如您有定制化健康诉求,请点击下方“私人订制个性食谱”来让我结合您的个人情况制定个性化配餐计划
525
-
526
-
527
- | 早餐 | | 午餐 |
528
- |------------|----------|----|
529
- | ${cateMap['01'].energy} |&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; | ${cateMap['02'].energy} |
530
- | ${cateMap['01'].carbonWater} |&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; | ${cateMap['02'].carbonWater} |
531
- | ${cateMap['01'].protein} |&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; | ${cateMap['02'].protein} |
532
- | ${cateMap['01'].fat} |&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; | ${cateMap['02'].fat} |
533
-
534
- | 晚餐 | | 加餐 |
535
- |------------|----------|----|
536
- | ${cateMap['03'].energy} |&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; | ${cateMap['04'].energy} |
537
- | ${cateMap['03'].carbonWater} |&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; | ${cateMap['04'].carbonWater} |
538
- | ${cateMap['03'].protein} |&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; | ${cateMap['04'].protein} |
539
- | ${cateMap['03'].fat} |&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; | ${cateMap['04'].fat} |`;
540
-
541
- historyList.value.push({
542
- conversation_id: '',
543
- bot_id: props.botId,
544
- role: 'assistant',
545
- content: resMsg,
546
- reasoning_content: '',
547
- status: 'ended',
548
- extra: [
549
- {
550
- ...res.obj,
551
- showType: 'card',
552
- },
553
- ],
554
- });
555
- Emits('log', {
556
- turns: turns.value,
557
- info: {
558
- ...historyList.value[historyList.value.length - 1],
559
- question: {
560
- content: perMsg,
561
- role: 'user',
562
- },
563
- },
564
- });
565
- }
566
- };
567
-
568
- const createConv = async () => {
569
- const res = await post(
570
- 'https://api.coze.cn/v1/conversation/create',
571
- { bot_id: props.botId, connector_id: '999' },
572
- { ...chatOptions.value },
573
- );
574
- isFirst.value = true;
575
- turns.value = 0;
576
- console.log(res);
577
- if (res.code === 0 && res.data) {
578
- conversationId.value = res.data.id || '';
579
- storage.setItem('conversationId', conversationId.value);
580
- historyList.value = [];
581
- await queryBotInfo();
582
- // await messageCreate();
583
- }
584
- };
585
- // const messageCreate = async () => {
586
- // console.log(chatOptions.value);
587
- // const res = await post(
588
- // `https://api.coze.cn/v1/conversation/message/create?conversation_id=${conversationId.value}`,
589
- // { "role":"user","content":`#userid_${props.uid}`,"content_type":"text"},
590
- // {...chatOptions.value});
591
- // };
592
- /*const messageCreate = async () => {
593
- const res = await fetch(
594
- `https://api.coze.cn/v3/chat?conversation_id=${conversationId.value}`,
595
- {
596
- method: 'POST',
597
- headers: {
598
- ...chatOptions.value.headers,
599
- // 'Content-Type': 'text/event-stream',
600
- },
601
- body: JSON.stringify({
602
- bot_id: props.botId,
603
- user_id: props.uid,
604
- stream: true,
605
- connector_id: '999',
606
- additional_messages: [
607
- {
608
- 'role': 'user',
609
- 'content_type': 'text',
610
- 'content': `#userid_${props.uid}`,
611
-
612
- }],
613
- }),
614
- credentials: 'same-origin', // 默认同源策略
615
- mode: 'cors', // 默认跨域模式
616
- cache: 'default', // 默认缓存策略
617
- },
618
- );
619
- };*/
620
-
621
- const chatConv = async (data) => {
622
- isReq.value = '01';
623
- let isInCode = Array.isArray(data); // data.hasOwnProperty('text') && data.hasOwnProperty('code');
624
- const botId = props.botId;
625
- if (!(!isAnswering.value && (inputText.value || isInCode))) {
626
- return;
627
- }
628
- const inText = inputText.value;
629
- console.log('== user send ==', isInCode, data, inText);
630
- isAnswering.value = true;
631
-
632
- const additional_messages = [];
633
- const uObj = {
634
- conversation_id: conversationId.value,
635
- bot_id: botId,
636
- role: 'user',
637
- content: '',
638
- reasoning_content: '',
639
- status: 'ended',
640
- extra: [],
641
- };
642
- const markStr = `chat_${new Date().getTime()};`;
643
- data.forEach((item) => {
644
- if (item.hasOwnProperty('content') && item.content) {
645
- if (item.hasOwnProperty('type') && item.type === 'object_string') {
646
- console.log('=== item ====', item);
647
- // botId = '7474884145253023795';
648
- additional_messages.push({
649
- content: JSON.stringify(
650
- item.content.map((con) => ({
651
- type: con.type,
652
- file_id: con.file_id,
653
- })),
654
- ),
655
- content_type: 'object_string',
656
- role: 'user',
657
- meta_data: {
658
- chat_group: markStr,
659
- },
660
- });
661
- uObj.extra = item.content.map((con) => ({
662
- ...con,
663
- showType: con.type,
664
- }));
665
- } else {
666
- uObj.content = item.text;
667
- additional_messages.push({
668
- content: item.content,
669
- content_type: 'text',
670
- role: 'user',
671
- meta_data: {
672
- chat_group: markStr,
673
- },
674
- });
675
- }
676
- }
677
- });
678
- historyList.value.push(uObj);
679
- isReq.value = '02';
680
- // if (props.needLog) {
681
- // Emits('log', {
682
- // turns: turns.value,
683
- // info: uObj,
684
- // });
685
- // }
686
- const res = await fetch(
687
- `https://api.coze.cn/v3/chat?conversation_id=${conversationId.value}`,
688
- {
689
- method: 'POST',
690
- headers: {
691
- ...chatOptions.value.headers,
692
- // 'Content-Type': 'text/event-stream',
693
- },
694
- body: JSON.stringify({
695
- bot_id: botId,
696
- user_id: props.uid,
697
- stream: true,
698
- connector_id: '999',
699
- additional_messages /*[
700
- {
701
- content: '[{"type":"image","file_id":"7475569020654436390"}]',
702
- content_type: 'object_string',
703
- role: 'user',
704
- },
705
- {
706
- role: 'user',
707
- content_type: 'text',
708
- content: isInCode ? data.code : inText, // '配餐1600kcal,身高173,体重60kg,生成一天的套餐',
709
- },
710
- ]*/,
711
- custom_variables: {
712
- uid: props.uid,
713
- },
714
- }),
715
- credentials: 'same-origin', // 默认同源策略
716
- mode: 'cors', // 默认跨域模式
717
- cache: 'default', // 默认缓存策略
718
- },
719
- );
720
- isReq.value = '03';
721
- historyList.value.push({
722
- conversation_id: '',
723
- bot_id: '',
724
- role: 'assistant',
725
- content: '',
726
- reasoning_content: '',
727
- status: 'answering',
728
- extra: [],
729
- });
730
- scrollToEnd();
731
- inputText.value = '';
732
- const idx = historyList.value.length - 1;
733
- const reader = res.body.getReader();
734
- const decoder = new TextDecoder('utf-8');
735
- let buffer = '';
736
-
737
- const handlePersonalForm = () => {
738
- console.log('======= End ======', historyList.value, isFirst.value);
739
- if (isFirst.value) {
740
- if (
741
- historyList.value[idx].extra.findIndex((item) =>
742
- item.hasOwnProperty('showType'),
743
- ) === -1
744
- ) {
745
- historyList.value[idx].extra.push({ showType: 'personalForm' });
746
- }
747
- isFirst.value = false;
748
- console.log('===============', historyList.value[idx]);
749
- }
750
- };
751
-
752
- // 逐块读取数据
753
- while (true) {
754
- const { done, value } = await reader.read();
755
- if (done) {
756
- console.log('Stream has ended.');
757
- isAnswering.value = false;
758
- isReq.value = '00';
759
- historyList.value[idx].status = 'ended';
760
- handlePersonalForm();
761
- if (props.needLog) {
762
- Emits('log', {
763
- turns: turns.value,
764
- info: {
765
- ...historyList.value[idx],
766
- question: {
767
- ...uObj,
768
- },
769
- },
770
- });
771
- turns.value += 1;
772
- }
773
- scrollToEnd();
774
- break;
775
- }
776
-
777
- // 解码数据块
778
- const chunk = decoder.decode(value, { stream: true });
779
- buffer += chunk;
780
-
781
- // 处理数据块
782
- const lines = buffer.split('\n');
783
- buffer = lines.pop(); // 保留未处理的片段
784
-
785
- lines.forEach((line) => {
786
- if (line.trim().length > 0 && line.trim().startsWith('data:')) {
787
- const str = line.replace(/^data:\s*/, '');
788
- const strObj = JSON.parse(str);
789
- console.log('Received:', strObj);
790
- isAnswering.value = true;
791
- isReq.value = '04';
792
- if (!historyList.value[idx].bot_id && strObj.bot_id) {
793
- historyList.value[idx].bot_id = strObj.bot_id;
794
- }
795
- if (!historyList.value[idx].conversation_id && strObj.conversation_id) {
796
- historyList.value[idx].conversation_id = strObj.conversation_id;
797
- }
798
- if (
799
- strObj.hasOwnProperty('reasoning_content') &&
800
- strObj.reasoning_content &&
801
- strObj.type === 'answer'
802
- ) {
803
- historyList.value[idx].status = 'thinking';
804
- historyList.value[idx].reasoning_content += strObj.reasoning_content;
805
- scrollToEnd();
806
- } else if (
807
- strObj.hasOwnProperty('content') &&
808
- strObj.hasOwnProperty('content_type') &&
809
- strObj.content_type === 'text' &&
810
- strObj.hasOwnProperty('type') &&
811
- strObj.type === 'answer' &&
812
- !strObj.hasOwnProperty('created_at')
813
- ) {
814
- historyList.value[idx].status = 'answering';
815
- historyList.value[idx].content = handleText(
816
- historyList.value[idx].content + strObj.content,
817
- );
818
- scrollToEnd();
819
- } else if (
820
- strObj.hasOwnProperty('type') &&
821
- strObj.type === 'tool_response'
822
- ) {
823
- const extraObj = JSON.parse(strObj.content);
824
- if (extraObj.hasOwnProperty('showType')) {
825
- historyList.value[idx].extra.push({ ...extraObj });
826
- } else if (extraObj.hasOwnProperty('response_for_model')) {
827
- const modelObj = JSON.parse(extraObj.response_for_model);
828
- if (modelObj.hasOwnProperty('showType')) {
829
- historyList.value[idx].extra.push({ ...modelObj });
830
- }
831
- }
832
- scrollToEnd();
833
- }
834
- }
835
- });
836
- }
837
- console.log('====', historyList.value);
838
- };
839
-
840
- const queryHistoryList = async () => {
841
- const res = await post(
842
- `https://api.coze.cn/v1/conversation/message/list?conversation_id=${conversationId.value}`,
843
- { order: 'asc' },
844
- { ...chatOptions.value },
845
- );
846
- if (res.code === 0 && res.data && res.data.length) {
847
- historyList.value = [];
848
- const cardList = [];
849
- const userFileList = [];
850
- res.data.forEach((row) => {
851
- if (row.hasOwnProperty('content_type')) {
852
- if (row.content_type === 'text') {
853
- historyList.value.push({
854
- chat_id: row.chat_id,
855
- conversation_id: row.conversation_id,
856
- bot_id: row.bot_id,
857
- role: row.role,
858
- meta_data: row.meta_data,
859
- content: handleText(row.content),
860
- reasoning_content: row.reasoning_content,
861
- status: 'ended',
862
- extra: [],
863
- });
864
- } else if (row.content_type === 'card' && row.role === 'assistant') {
865
- try {
866
- const cardObj = JSON.parse(row.content);
867
- if (cardObj.hasOwnProperty('showType')) {
868
- cardList.push({
869
- chat_id: row.chat_id,
870
- conversation_id: row.conversation_id,
871
- bot_id: row.bot_id,
872
- role: row.role,
873
- extra: [{ ...cardObj }],
874
- });
875
- } else if (cardObj.hasOwnProperty('response_for_model')) {
876
- const modelObj = JSON.parse(cardObj.response_for_model);
877
- if (modelObj.hasOwnProperty('showType')) {
878
- cardList.push({
879
- chat_id: row.chat_id,
880
- conversation_id: row.conversation_id,
881
- bot_id: row.bot_id,
882
- role: row.role,
883
- extra: [{ ...modelObj }],
884
- });
885
- }
886
- }
887
- } catch (e) {
888
- console.log('== 解析错误 sys==');
889
- }
890
- } else if (
891
- row.content_type === 'object_string' &&
892
- row.role === 'user'
893
- ) {
894
- try {
895
- const strObj = JSON.parse(row.content);
896
- cardList.push({
897
- chat_id: row.chat_id,
898
- conversation_id: row.conversation_id,
899
- bot_id: row.bot_id,
900
- role: row.role,
901
- meta_data: row.meta_data,
902
- extra: strObj.map((item) => ({ ...item, showType: item.type })),
903
- });
904
- } catch (e) {
905
- console.log('== 解析错误 user==');
906
- }
907
- }
908
- }
909
- });
910
- cardList.forEach((card) => {
911
- const idx =
912
- card.role === 'user'
913
- ? historyList.value.findIndex(
914
- (c) =>
915
- c.meta_data.chat_group === card.meta_data.chat_group &&
916
- c.role === card.role,
917
- )
918
- : historyList.value.findIndex(
919
- (c) => c.chat_id === card.chat_id && c.role === card.role,
920
- );
921
- if (idx > -1) {
922
- historyList.value[idx].extra = [...card.extra];
923
- }
924
- });
925
- }
926
-
927
- console.log('== history ==', historyList.value, finalCardList.value);
928
- };
929
-
930
- const queryBotInfo = async () => {
931
- const res = await get(
932
- `https://api.coze.cn/v1/bot/get_online_info?bot_id=${props.botId}`,
933
- { ...chatOptions.value },
934
- );
935
- console.log(res);
936
- if (res.code === 0 && res.data) {
937
- botInfo.value = { ...res.data };
938
- }
939
- };
940
-
941
- const handleText = (str) => {
942
- return str;
943
- // console.log(str);
944
- // let txt = '';
945
- //
946
- // return str.replaceAll(
947
- // /\[([^\]]+)\]\((https?:\/\/[^)]+)\)/gi,
948
- // '<a href="$2" target="_blank">[$1]</a>',
949
- // );
950
- };
951
- const handleBack = () => {
952
- Emits('call', { type: 'back' });
953
- };
954
- const handleStoreSel = (info) => {
955
- chatConv([{ content: info.content, text: info.text }]);
956
- };
957
- const handleTagSel = (info) => {
958
- console.log(info);
959
- switch (info.type) {
960
- case 'chat':
961
- chatConv([{ content: info.msg, text: info.msg }]);
962
- break;
963
- case 'call':
964
- Emits('call', { ...info });
965
- break;
966
- case 'simulate':
967
- simulateChat(info);
968
- break;
969
- }
970
- };
971
-
972
- const handleCardTap = ({ type }, info) => {
973
- switch (type) {
974
- case 'change':
975
- chatConv([{ content: '換一套菜品', text: '換一套菜品' }]);
976
- break;
977
- case 'match':
978
- chatConv([
979
- {
980
- content: finalDefMsg.value.matchContent,
981
- text: finalDefMsg.value.matchText,
982
- },
983
- ]);
984
- break;
985
- case 'personalForm':
986
- simulateChat({
987
- msg: '请为我生成一份个性化的健康的食谱',
988
- name: '用户信息',
989
- type: 'simulate',
990
- value: 'personalForm',
991
- });
992
- break;
993
- default:
994
- Emits('call', { type, info });
995
- }
996
- };
997
- const handleCall = (data) => {
998
- switch (data.type) {
999
- case 'new_chat':
1000
- createConv();
1001
- break;
1002
- default:
1003
- Emits('call', data);
1004
-
1005
- break;
1006
- }
1007
- };
1008
-
1009
- const handlePlanParse = (list) => {
1010
- const pIdx = list.findIndex((p) => p.showType === 'plan');
1011
- return pIdx > -1
1012
- ? { show: true, text: list[pIdx].planParse }
1013
- : { show: false };
1014
- };
1015
-
1016
- const scrollToEnd = () => {
1017
- setTimeout(() => {
1018
- nextTick(() => {
1019
- endTarget.value.scrollIntoView();
1020
- });
1021
- }, 100);
1022
- };
1023
-
1024
- provide('triggerScrollToEnd', scrollToEnd);
1025
-
1026
- const handleTextNeedBtn = (str) => {
1027
- const regExp = /#全日總熱量:(\d*?)kcal/g;
1028
- return regExp.test(str);
1029
- };
1030
-
1031
- const simulateChat = (info) => {
1032
- console.log('== simulateChat == ', info);
1033
- historyList.value.push({
1034
- conversation_id: '',
1035
- bot_id: props.botId,
1036
- role: 'user',
1037
- content: info.msg,
1038
- reasoning_content: '',
1039
- status: 'ended',
1040
- extra: [],
1041
- });
1042
- let resInfo = {
1043
- msg: '',
1044
- extra: [],
1045
- };
1046
- switch (info.value) {
1047
- case 'personalForm':
1048
- resInfo.msg =
1049
- '请填写您的身体数据,维小饭将根据你的身体数据智能生成个性化食谱';
1050
- resInfo.extra = [
1051
- {
1052
- showType: 'personalForm',
1053
- },
1054
- ];
1055
- break;
1056
- case 'contactKF':
1057
- resInfo.msg = '联系专业营养师团队帮你快速实现科学配比的健康餐营养搭配。';
1058
- resInfo.extra = [{ showType: 'contactKF' }];
1059
- break;
1060
- }
1061
- historyList.value.push({
1062
- conversation_id: '',
1063
- bot_id: props.botId,
1064
- role: 'assistant',
1065
- content: resInfo.msg,
1066
- reasoning_content: '',
1067
- status: 'ended',
1068
- extra: [...resInfo.extra],
1069
- });
1070
- turns.value += 1;
1071
- Emits('log', {
1072
- turns: turns.value,
1073
- info: {
1074
- ...historyList.value[historyList.value.length - 1],
1075
- question: {
1076
- content: info.msg,
1077
- role: 'user',
1078
- },
1079
- },
1080
- });
1081
- scrollToEnd();
1082
- };
1083
- </script>
1084
-
1085
- <style scoped lang="less">
1086
- @import './ChatUi';
1087
- </style>