ai-chat-bot-interface 1.6.22 → 1.6.24

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ai-chat-bot-interface",
3
3
  "private": false,
4
- "version": "1.6.22",
4
+ "version": "1.6.24",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "description": "A AI chat bot interface. (private)",
package/src/App.vue CHANGED
@@ -18,11 +18,12 @@ const handleLog = (log) => {
18
18
  {
19
19
  name: '用户信息',
20
20
  value: 'personalForm',
21
- type: 'chat',
21
+ type: 'simulate',
22
22
  msg: '请为我生成一份个性化的健康的食谱。',
23
23
  },
24
24
  ]"
25
25
  :need-log="true"
26
+ post-token="CE73EA32DA06F05C3F522FE8E5E1420399E9C8433CD4932C"
26
27
  content-type="markdown"
27
28
  first-msg="减脂减重"
28
29
  @log="handleLog"
package/src/ChatUi.vue CHANGED
@@ -202,6 +202,7 @@ import AssistantReplay from './components/assistantReplay/assistantReplay.vue';
202
202
  import StoreList from './components/StoreList/StoreList.vue';
203
203
  import MarkdownViewer from './components/MarkdownPlan/MarkdownViewer.vue';
204
204
  import PersonalForm from './components/personalForm/personalForm.vue';
205
+ import processBar from './components/icons/processBar.vue';
205
206
 
206
207
  const chatOptions = computed(() => {
207
208
  return {
@@ -275,6 +276,10 @@ const props = defineProps({
275
276
  type: String,
276
277
  required: true,
277
278
  },
279
+ postToken: {
280
+ type: String,
281
+ default: '',
282
+ },
278
283
  showHeader: {
279
284
  type: Boolean,
280
285
  default: true,
@@ -351,6 +356,7 @@ const storage = {
351
356
  };
352
357
 
353
358
  onMounted(async () => {
359
+ localStorage.setItem('postToken', props.postToken || '');
354
360
  if (storage.getItem('conversationId')) {
355
361
  conversationId.value = storage.getItem('conversationId');
356
362
  await queryBotInfo();
@@ -359,7 +365,14 @@ onMounted(async () => {
359
365
  } else {
360
366
  await createConv();
361
367
  if (props.firstMsg) {
362
- const perMsg = `本食谱为通用版${props.firstMsg}示例,依据以下女性常规身体数据制定:
368
+ await handleFirstMsg();
369
+ }
370
+ }
371
+ scrollToEnd();
372
+ });
373
+
374
+ const handleFirstMsg = async () => {
375
+ const perMsg = `本食谱为通用版${props.firstMsg}示例,依据以下女性常规身体数据制定:
363
376
  性别:女;
364
377
  年龄:25岁;
365
378
  身高:160cm;
@@ -367,11 +380,76 @@ onMounted(async () => {
367
380
  日常运动水平:久坐;
368
381
  口味偏好及饮食禁忌:无;
369
382
  如需定制贴合个人情况的专属方案,请前往完善个人身体数据,获取个性化定制食谱。`;
370
- await chatConv([{ content: perMsg, text: perMsg }]);
371
- }
383
+
384
+ historyList.value = [
385
+ {
386
+ conversation_id: '',
387
+ bot_id: props.botId,
388
+ role: 'user',
389
+ content: perMsg,
390
+ reasoning_content: '',
391
+ status: 'ended',
392
+ extra: [],
393
+ },
394
+ ];
395
+
396
+ const res = await post('/api/cn.weis.api.Food/bdGeneralTemp', {
397
+ method: 'bdGeneralTemp',
398
+ params: [{}],
399
+ token: props.postToken,
400
+ });
401
+ if (res.errCode === 0 && res.obj) {
402
+ const resMsg = `您的身高:160cm 年龄:25 岁 体重:60kg 性别:女 日常运动水平:久坐
403
+
404
+ 根据您的身体状况,给您推荐的饮食方案如下
405
+ ——————————————————
406
+
407
+ 早餐
408
+ 热量:375kcal
409
+ 碳水:46.9g
410
+ 蛋白质:18.8g
411
+ 脂肪:12.5g
412
+
413
+ 午餐
414
+ 热量:525kcal
415
+ 碳水:65.6g
416
+ 蛋白质:26.3g
417
+ 脂肪:17.5g
418
+
419
+ 晚餐
420
+ 热量:450kcal
421
+ 碳水:56.3g
422
+ 蛋白质:22.5g
423
+ 脂肪:15g
424
+
425
+ 加餐
426
+ 热量:150kcal
427
+ 碳水:26.3g
428
+ 蛋白质:3.8g
429
+ 脂肪:3.3g
430
+
431
+ ——————————————————`;
432
+
433
+ historyList.value.push({
434
+ conversation_id: '',
435
+ bot_id: props.botId,
436
+ role: 'assistant',
437
+ content: resMsg,
438
+ reasoning_content: '',
439
+ status: 'ended',
440
+ extra: [
441
+ {
442
+ ...res.obj,
443
+ showType: 'card',
444
+ },
445
+ ],
446
+ });
447
+ Emits('log', {
448
+ turns: turns.value,
449
+ info: historyList.value[historyList.value.length - 1],
450
+ });
372
451
  }
373
- scrollToEnd();
374
- });
452
+ };
375
453
 
376
454
  const createConv = async () => {
377
455
  const res = await post(
@@ -767,6 +845,9 @@ const handleTagSel = (info) => {
767
845
  case 'call':
768
846
  Emits('call', { ...info });
769
847
  break;
848
+ case 'simulate':
849
+ simulateChat(info);
850
+ break;
770
851
  }
771
852
  };
772
853
 
@@ -784,10 +865,10 @@ const handleCardTap = ({ type }, info) => {
784
865
  ]);
785
866
  break;
786
867
  case 'personalForm':
787
- handleTagSel({
868
+ simulateChat({
788
869
  msg: '请为我生成一份个性化的健康的食谱',
789
870
  name: '用户信息',
790
- type: 'chat',
871
+ type: 'simulate',
791
872
  value: 'personalForm',
792
873
  });
793
874
  break;
@@ -815,15 +896,55 @@ const handlePlanParse = (list) => {
815
896
  };
816
897
 
817
898
  const scrollToEnd = () => {
818
- nextTick(() => {
819
- endTarget.value.scrollIntoView();
820
- });
899
+ setTimeout(() => {
900
+ nextTick(() => {
901
+ endTarget.value.scrollIntoView();
902
+ });
903
+ }, 100);
821
904
  };
822
905
 
823
906
  const handleTextNeedBtn = (str) => {
824
907
  const regExp = /#全日總熱量:(\d*?)kcal/g;
825
908
  return regExp.test(str);
826
909
  };
910
+
911
+ const simulateChat = (info) => {
912
+ console.log('== simulateChat == ', info);
913
+ historyList.value.push({
914
+ conversation_id: '',
915
+ bot_id: props.botId,
916
+ role: 'user',
917
+ content: info.msg,
918
+ reasoning_content: '',
919
+ status: 'ended',
920
+ extra: [],
921
+ });
922
+ let resInfo = {
923
+ msg: '',
924
+ extra: [],
925
+ };
926
+ switch (info.value) {
927
+ case 'personalForm':
928
+ resInfo.msg =
929
+ '请填写您的身体数据,维小饭将根据你的身体数据智能生成个性化食谱';
930
+ resInfo.extra = [
931
+ {
932
+ showType: 'personalForm',
933
+ },
934
+ ];
935
+ break;
936
+ }
937
+ historyList.value.push({
938
+ conversation_id: '',
939
+ bot_id: props.botId,
940
+ role: 'assistant',
941
+ content: resInfo.msg,
942
+ reasoning_content: '',
943
+ status: 'ended',
944
+ extra: [...resInfo.extra],
945
+ });
946
+ scrollToEnd();
947
+ };
827
948
  </script>
828
949
 
829
950
  <style scoped lang="less">
@@ -66,6 +66,10 @@
66
66
  <div class="tag">花生</div>
67
67
  <div class="tag">玉米淀粉</div>
68
68
  </div>-->
69
+ <div class="tips">
70
+ <span class="star">*</span
71
+ >一份的量偏小不便制作,因而基于3份的量来介绍,食用时取一份即可
72
+ </div>
69
73
  <div class="title">食材</div>
70
74
  <div class="text">
71
75
  {{ materialText }}
@@ -75,7 +79,7 @@
75
79
  <video :src="info.skuVideo" width="100%" controls="true" />
76
80
  </div>
77
81
  <div
78
- v-for="(item, index) in info.makeCraftList"
82
+ v-for="(item, index) in makeCraft.makeCraftList"
79
83
  :key="index"
80
84
  class="step_row"
81
85
  >
@@ -99,10 +103,11 @@
99
103
  </template>
100
104
 
101
105
  <script setup>
102
- import { computed, onMounted, ref } from 'vue';
106
+ import { computed, nextTick, onMounted, ref } from 'vue';
103
107
  import { api as viewerApi } from 'v-viewer';
104
108
  import 'viewerjs/dist/viewer.css';
105
109
  import ArrowRight from './icons/ArrowRight.vue';
110
+ import { post } from '@/utils/request.js';
106
111
 
107
112
  const props = defineProps({
108
113
  info: {
@@ -121,15 +126,43 @@ const props = defineProps({
121
126
  const isOpen = ref(props.open);
122
127
  const contentRef = ref(null); // 用于引用内容 DOM 元素
123
128
  const contentHeight = ref(0); // 存储内容的实际高度
129
+ const makeCraft = ref([]);
124
130
 
125
- onMounted(() => {
126
- getContentHeight();
131
+ onMounted(async () => {
132
+ if (isOpen.value) {
133
+ await querySkuMakeCraft();
134
+ getContentHeight();
135
+ }
127
136
  });
128
137
 
129
138
  const getContentHeight = () => {
130
- contentHeight.value = hasDetails.value ? contentRef.value.scrollHeight : 0;
139
+ nextTick(async () => {
140
+ const imgList = [];
141
+ if (makeCraft.value.hasOwnProperty('makeCraftList')) {
142
+ makeCraft.value?.makeCraftList.forEach((item) => {
143
+ const imgs = getImageList(item.craftImg);
144
+ imgList.push(...imgs);
145
+ });
146
+ }
147
+ await preloadImages(imgList);
148
+ contentHeight.value = hasDetails.value ? contentRef.value.scrollHeight : 0;
149
+ isOpen.value = true;
150
+ });
131
151
  };
132
152
 
153
+ const preloadImages = (urls) => {
154
+ const promises = [];
155
+ urls.forEach((url) => {
156
+ const img = new Image();
157
+ const promise = new Promise((resolve, reject) => {
158
+ img.onload = () => resolve(img);
159
+ img.onerror = () => reject(new Error(`加载${url}失败`));
160
+ img.src = url;
161
+ });
162
+ promises.push(promise);
163
+ });
164
+ return Promise.all(promises);
165
+ };
133
166
  // const Emits = defineEmits(['update:open']);
134
167
 
135
168
  const subStr = computed(() => {
@@ -142,14 +175,7 @@ const materialText = computed(() => {
142
175
  return (list || []).join('、');
143
176
  });
144
177
  const hasDetails = computed(() => {
145
- let sts = false;
146
- if (
147
- props.info.hasOwnProperty('makeCraftList') &&
148
- props.info.makeCraftList.length > 0
149
- ) {
150
- sts = true;
151
- }
152
- return sts;
178
+ return props.info.hasOwnProperty('cid') && props.info.cid;
153
179
  });
154
180
  const panelHeight = computed(() => {
155
181
  let h = '92px';
@@ -165,11 +191,13 @@ const previewImg = (url) => {
165
191
  });
166
192
  };
167
193
 
168
- const switchOpen = () => {
194
+ const switchOpen = async () => {
169
195
  if (!isOpen.value) {
196
+ await querySkuMakeCraft();
170
197
  getContentHeight();
198
+ } else {
199
+ isOpen.value = false;
171
200
  }
172
- isOpen.value = !isOpen.value;
173
201
  };
174
202
 
175
203
  const getImageList = (str) => {
@@ -182,6 +210,24 @@ const getImageList = (str) => {
182
210
 
183
211
  return list;
184
212
  };
213
+
214
+ const querySkuMakeCraft = async () => {
215
+ if (
216
+ !(
217
+ makeCraft.value.hasOwnProperty('makeCraftList') &&
218
+ makeCraft.value.hasOwnProperty('makeMaterialList')
219
+ )
220
+ ) {
221
+ const res = await post('/api/cn.weis.api.Food/makeCraft', {
222
+ method: 'makeCraft',
223
+ params: [{ cid: props.info.cid }],
224
+ token: localStorage.getItem('postToken'),
225
+ });
226
+ if (res.errCode === 0 && res.obj) {
227
+ makeCraft.value = { ...res.obj };
228
+ }
229
+ }
230
+ };
185
231
  </script>
186
232
 
187
233
  <style scoped lang="less">
@@ -202,6 +248,17 @@ const getImageList = (str) => {
202
248
  font-size: 18px;
203
249
  }
204
250
  }
251
+ .tips {
252
+ vertical-align: middle;
253
+ font-size: 13px;
254
+ font-weight: 400;
255
+ margin-top: 10px;
256
+ .star {
257
+ color: #ff0000;
258
+ vertical-align: middle;
259
+ margin-right: 0.4em;
260
+ }
261
+ }
205
262
  .title {
206
263
  font-weight: 600;
207
264
  font-size: 15px;
@@ -31,12 +31,12 @@
31
31
  温馨提示:维小饭AI健康食谱为日常膳食调理方案,不能替代药物治疗或专业医疗建议。如有疾病,请及时就医并遵医嘱。
32
32
  </div>
33
33
  <div v-if="isMini" class="btn_group">
34
+ <div class="btn btn_1" @click.stop="handleBtn('personalForm')">
35
+ <span class="name">私人定制个性食谱</span>
36
+ </div>
34
37
  <div class="btn btn_2" @click.stop="handleBtn('ship_order')">
35
38
  <span class="name">去订餐</span>
36
39
  </div>
37
- <div class="btn btn_1" @click.stop="handleBtn('personalForm')">
38
- <span class="name">定制个性化食谱</span>
39
- </div>
40
40
  </div>
41
41
  <div v-else class="btn_group">
42
42
  <div
@@ -83,7 +83,8 @@ const openSts = ref(false);
83
83
  const handleIsOpen = (info, category) => {
84
84
  if (openSts.value) {
85
85
  return false;
86
- } else if (category !== '01' && info.hasOwnProperty('makeCraftList')) {
86
+ } else if (category !== '01' && info.hasOwnProperty('cid') && info.cid) {
87
+ console.log('=== 6666 ===', info);
87
88
  openSts.value = true;
88
89
  return true;
89
90
  } else {
@@ -0,0 +1,115 @@
1
+ <template>
2
+ <div class="cool-progress-container">
3
+ <!-- 进度条外层容器 -->
4
+ <div class="progress-bar-wrapper">
5
+ <!-- 进度条主体(带渐变+流光) -->
6
+ <div class="progress-bar" :style="{ width: `${percentage}%` }">
7
+ <!-- 流光伪元素通过 CSS 实现,无需额外DOM -->
8
+ </div>
9
+ </div>
10
+ <!-- 进度数字(发光效果) -->
11
+ <span class="progress-text">{{ percentage }}%</span>
12
+ </div>
13
+ </template>
14
+
15
+ <script setup>
16
+ import { onMounted, ref } from 'vue';
17
+ // 模拟进度值,可替换为业务数据
18
+ const percentage = ref(0);
19
+ const runner = ref('');
20
+ onMounted(() => {
21
+ percentage.value = 0;
22
+ runFunc();
23
+ });
24
+
25
+ const runFunc = () => {
26
+ let x = 0;
27
+ let t = 100;
28
+ const max = 95;
29
+ const calcProcess = () => {
30
+ const timer = setTimeout(() => {
31
+ if (percentage.value >= max) {
32
+ console.log('== stop ==', new Date().getTime());
33
+ clearTimeout(timer);
34
+ } else {
35
+ if (percentage.value > 80) {
36
+ t = 800;
37
+ }
38
+ percentage.value = (max * (1 - Math.exp(-x / 20))).toFixed(1);
39
+ x += 0.5;
40
+ calcProcess();
41
+ }
42
+ }, t);
43
+ };
44
+ console.log('== start ==', new Date().getTime());
45
+ calcProcess();
46
+ };
47
+ </script>
48
+
49
+ <style scoped>
50
+ .cool-progress-container {
51
+ width: 100%;
52
+ max-width: 600px;
53
+ margin: 20px 0;
54
+ padding: 15px;
55
+ background: #121212; /* 暗黑底色适配酷炫风 */
56
+ border-radius: 12px;
57
+ box-shadow: 0 0 20px rgba(0, 128, 255, 0.2);
58
+ }
59
+
60
+ .progress-bar-wrapper {
61
+ height: 24px;
62
+ width: 100%;
63
+ background: rgba(255, 255, 255, 0.08);
64
+ border-radius: 12px;
65
+ overflow: hidden;
66
+ position: relative;
67
+ }
68
+
69
+ .progress-bar {
70
+ height: 100%;
71
+ border-radius: 12px;
72
+ /* 赛博朋克渐变底色 */
73
+ background: linear-gradient(90deg, #00f5ff, #7928ca, #ff0080);
74
+ position: relative;
75
+ transition: width 0.5s ease-in;
76
+ }
77
+
78
+ /* 流光动画伪元素 */
79
+ .progress-bar-wrapper::after {
80
+ content: '';
81
+ position: absolute;
82
+ top: 0;
83
+ left: -100%;
84
+ width: 50%;
85
+ height: 100%;
86
+ background: linear-gradient(
87
+ 90deg,
88
+ transparent,
89
+ rgba(255, 255, 255, 0.4),
90
+ transparent
91
+ );
92
+ transform: skewX(-20deg);
93
+ animation: light-flow 1.5s infinite linear;
94
+ }
95
+
96
+ /* 流光动画 */
97
+ @keyframes light-flow {
98
+ 0% {
99
+ left: -100%;
100
+ }
101
+ 100% {
102
+ left: 200%;
103
+ }
104
+ }
105
+
106
+ /* 发光进度文字 */
107
+ .progress-text {
108
+ display: block;
109
+ margin-top: 8px;
110
+ font-size: 16px;
111
+ font-weight: bold;
112
+ color: #00f5ff;
113
+ text-shadow: 0 0 10px #00f5ff, 0 0 20px #00f5ff;
114
+ }
115
+ </style>
package/vite.config.js CHANGED
@@ -20,5 +20,14 @@ export default defineConfig({
20
20
  },
21
21
  server: {
22
22
  host: '0.0.0.0',
23
+ hmr: true,
24
+ proxy: {
25
+ '/api': {
26
+ // target: 'https://prodapi.weis1606.cn',
27
+ target: 'https://api.weis1606.cn/',
28
+ changeOrigin: true,
29
+ rewrite: (path) => path.replace(/^\/api/, '/api'),
30
+ },
31
+ },
23
32
  },
24
33
  });