ai-chat-bot-interface 1.6.7 → 1.6.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.
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.7",
4
+ "version": "1.6.9",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "description": "A AI chat bot interface. (private)",
@@ -1,23 +1,91 @@
1
1
  <template>
2
- <div class="dishes_wrap" @click.stop="">
3
- <img
4
- class="img"
5
- :src="info.primaryImgUrl"
6
- alt="img"
7
- @click.stop="previewImg(info.primaryImgUrl)"
8
- />
9
- <div>
10
- <div class="name">
11
- <span>{{ info.skuname }}</span>
12
- <span>×1</span>
2
+ <div
3
+ :class="{ dishes_box: true, is_open: isOpen }"
4
+ :style="{ height: isOpen ? contentHeight + 92 + 'px' : '92px' }"
5
+ >
6
+ <div class="dishes_wrap" @click.stop="switchOpen">
7
+ <img
8
+ class="img"
9
+ :src="info.primaryImgUrl"
10
+ alt="img"
11
+ @click.stop="previewImg(info.primaryImgUrl)"
12
+ />
13
+ <div>
14
+ <div class="name">
15
+ <span>{{ info.skuname }}{{ contentHeight }}</span>
16
+ <span>×1</span>
17
+ </div>
18
+ <div class="sub">{{ subStr }}</div>
13
19
  </div>
14
- <div class="sub">{{ subStr }}</div>
20
+ </div>
21
+ <div v-if="hasDetails" class="dishes_details" ref="contentRef">
22
+ <!-- <div class="title">营养数据</div>
23
+ <div class="data_row">
24
+ <div class="data_item">
25
+ <div class="label">热量</div>
26
+ <div class="content">5555kcal</div>
27
+ </div>
28
+ </div>
29
+ <div class="line"></div>
30
+ <div class="data_row">
31
+ <div class="data_item">
32
+ <div class="label">蛋白质</div>
33
+ <div class="content">55g</div>
34
+ </div>
35
+ <div class="data_item">
36
+ <div class="label">膳食纤维</div>
37
+ <div class="content">55g</div>
38
+ </div>
39
+ </div>
40
+ <div class="data_row">
41
+ <div class="data_item">
42
+ <div class="label">碳水</div>
43
+ <div class="content">55g</div>
44
+ </div>
45
+ <div class="data_item">
46
+ <div class="label">盐量</div>
47
+ <div class="content">55g</div>
48
+ </div>
49
+ </div>
50
+ <div class="data_row">
51
+ <div class="data_item">
52
+ <div class="label">脂肪</div>
53
+ <div class="content">55g</div>
54
+ </div>
55
+ <div class="data_item">
56
+ <div class="label">食物多样性</div>
57
+ <div class="content">55g</div>
58
+ </div>
59
+ </div>
60
+
61
+ <div class="title">过敏原</div>
62
+ <div class="tag_box">
63
+ <div class="tag">花生</div>
64
+ <div class="tag">玉米淀粉</div>
65
+ </div>-->
66
+ <div class="title">食材</div>
67
+ <div class="text">
68
+ {{ materialText }}
69
+ </div>
70
+ <div class="title">制作工艺</div>
71
+ <div
72
+ v-for="(item, index) in info.makeCraftList"
73
+ :key="index"
74
+ class="step_row"
75
+ >
76
+ <div class="step_item">
77
+ <div class="num">第{{ index + 1 }}步</div>
78
+ <div class="desc">{{ item.content }}</div>
79
+ </div>
80
+ </div>
81
+
82
+ <div class="collapse_btn" @click.stop="isOpen = false">收起</div>
15
83
  </div>
16
84
  </div>
17
85
  </template>
18
86
 
19
87
  <script setup>
20
- import { computed } from 'vue';
88
+ import { computed, onMounted, ref } from 'vue';
21
89
  import { api as viewerApi } from 'v-viewer';
22
90
  import 'viewerjs/dist/viewer.css';
23
91
 
@@ -28,25 +96,154 @@ const props = defineProps({
28
96
  },
29
97
  defMsg: {
30
98
  type: Object,
31
- default: () => ({})
32
- }
99
+ default: () => ({}),
100
+ },
101
+ open: {
102
+ type: Boolean,
103
+ default: false,
104
+ },
33
105
  });
106
+ const isOpen = ref(props.open);
107
+ const contentRef = ref(null); // 用于引用内容 DOM 元素
108
+ const contentHeight = ref(0); // 存储内容的实际高度
109
+
110
+ onMounted(() => {
111
+ contentHeight.value = hasDetails.value ? contentRef.value.scrollHeight : 0;
112
+ });
113
+
114
+ // const Emits = defineEmits(['update:open']);
34
115
 
35
116
  const subStr = computed(() => {
36
117
  return `${props.defMsg.energy}${props.info.energy}kcal,${props.defMsg.protein}${props.info.protein}g,${props.defMsg.fat}${props.info.fat}g,${props.defMsg.carbonwater}${props.info.carbonwater}g`;
37
118
  });
119
+ const materialText = computed(() => {
120
+ const list = props.info?.makeMaterialList?.map(
121
+ (item) => item.material + item.usage + 'g',
122
+ );
123
+ return (list || []).join('、');
124
+ });
125
+ const hasDetails = computed(() => {
126
+ let sts = false;
127
+ if (
128
+ props.info.hasOwnProperty('makeCraftList') &&
129
+ props.info.makeCraftList.length > 0
130
+ ) {
131
+ sts = true;
132
+ }
133
+ return sts;
134
+ });
38
135
 
39
136
  const previewImg = (url) => {
40
137
  viewerApi({
41
138
  images: [url],
42
139
  });
43
140
  };
141
+
142
+ const switchOpen = () => {
143
+ isOpen.value = !isOpen.value;
144
+ };
44
145
  </script>
45
146
 
46
147
  <style scoped lang="less">
47
148
  @import '../assets/styles/public';
48
149
 
49
150
  .dishes {
151
+ &_details {
152
+ padding: 1px 10px;
153
+
154
+ .title {
155
+ font-weight: 600;
156
+ font-size: 15px;
157
+ color: #000;
158
+ margin: 15px 0;
159
+ }
160
+ .text {
161
+ font-weight: 400;
162
+ font-size: 13px;
163
+ color: #000;
164
+ }
165
+ .step {
166
+ &_row {
167
+ margin-bottom: 15px;
168
+ }
169
+ &_item {
170
+ .flexrss();
171
+ .num {
172
+ width: 4em;
173
+ color: #666;
174
+ font-size: 13px;
175
+ }
176
+ .desc {
177
+ flex: 1;
178
+ font-size: 13px;
179
+ color: #000;
180
+ }
181
+ }
182
+ }
183
+ .line {
184
+ margin: 5px 0;
185
+ border-bottom: 1px solid #fff;
186
+ }
187
+ .data {
188
+ &_row {
189
+ .flexrsc();
190
+ gap: 10px;
191
+ }
192
+ &_item {
193
+ .flexrsc();
194
+ flex: 1;
195
+ gap: 10px;
196
+ line-height: 26px;
197
+ .label {
198
+ width: 72px;
199
+ font-weight: 400;
200
+ font-size: 13px;
201
+ color: #666;
202
+ }
203
+ .content {
204
+ flex: 1;
205
+ width: 60px;
206
+ font-weight: 400;
207
+ font-size: 13px;
208
+ color: #000;
209
+ }
210
+ }
211
+ }
212
+ .tag {
213
+ padding: 0 15px;
214
+ background-color: #fff;
215
+ border-radius: 15px;
216
+ line-height: 30px;
217
+ font-weight: 400;
218
+ font-size: 13px;
219
+ color: #000;
220
+ &_box {
221
+ .flexrsc();
222
+ gap: 10px;
223
+ }
224
+ }
225
+ .collapse_btn {
226
+ width: 100px;
227
+ height: 32px;
228
+ line-height: 32px;
229
+ text-align: center;
230
+ border-radius: 10px 10px 0 0;
231
+ background-color: #e2e2e2;
232
+ color: #999;
233
+ margin: 20px auto 0;
234
+ }
235
+ }
236
+ &_box {
237
+ transition: height 100ms ease-in-out;
238
+ height: 92px;
239
+ background-color: #ededed;
240
+ border-radius: 10px;
241
+ margin: 10px 0;
242
+ overflow: hidden;
243
+ &.is_open {
244
+ //height: auto;
245
+ }
246
+ }
50
247
  &_wrap {
51
248
  display: grid;
52
249
  grid-template-columns: 72px 1fr;
@@ -54,7 +251,6 @@ const previewImg = (url) => {
54
251
  background-color: @bg-color;
55
252
  border-radius: 10px;
56
253
  padding: 10px;
57
- margin: 10px 0;
58
254
 
59
255
  .img {
60
256
  cursor: pointer;
@@ -8,7 +8,9 @@
8
8
  </div>
9
9
  </div>
10
10
  <div v-if="cate.list && cate.list.length" style="padding: 15px 0">
11
- <p class="title">{{ cate.categoryType }} {{ categoryEnergy(cate) }}kcal</p>
11
+ <p class="title">
12
+ {{ cate.categoryType }} {{ categoryEnergy(cate) }}kcal
13
+ </p>
12
14
  <div>
13
15
  <template v-for="dList in cate.list" :key="dList.id">
14
16
  <dishes-card
@@ -16,6 +18,7 @@
16
18
  :key="idx"
17
19
  :info="info"
18
20
  :def-msg="defMsg"
21
+ :open="handleIsOpen(info, cate.category)"
19
22
  />
20
23
  </template>
21
24
  </div>
@@ -23,11 +26,15 @@
23
26
  </template>
24
27
  <div v-if="isMini" class="btn_group">
25
28
  <div class="btn btn_2" @click.stop="handleBtn('ship_order')">
26
- <span class="name">立即下单</span>
29
+ <span class="name">去订餐</span>
27
30
  </div>
28
31
  </div>
29
32
  <div v-else class="btn_group">
30
- <div v-if="channel !== 'hft'" class="btn btn_1" @click.stop="handleBtn('ship_order')">
33
+ <div
34
+ v-if="channel !== 'hft'"
35
+ class="btn btn_1"
36
+ @click.stop="handleBtn('ship_order')"
37
+ >
31
38
  <span class="name">配送下單</span>
32
39
  <span class="sub">(直送到府)</span>
33
40
  </div>
@@ -41,6 +48,7 @@
41
48
 
42
49
  <script setup>
43
50
  import DishesCard from './DishesCard.vue';
51
+ import { ref } from 'vue';
44
52
 
45
53
  const props = defineProps({
46
54
  skuList: {
@@ -54,23 +62,34 @@ const props = defineProps({
54
62
  },
55
63
  channel: {
56
64
  type: String,
57
- default: 'web'
65
+ default: 'web',
58
66
  },
59
67
  defMsg: {
60
68
  type: Object,
61
- default: () => ({})
62
- }
69
+ default: () => ({}),
70
+ },
63
71
  });
64
72
  const Emits = defineEmits(['select']);
73
+ const openSts = ref(false);
74
+ const handleIsOpen = (info, category) => {
75
+ if (openSts.value) {
76
+ return false;
77
+ } else if (category !== '01' && info.hasOwnProperty('makeCraftList')) {
78
+ openSts.value = true;
79
+ return true;
80
+ } else {
81
+ return false;
82
+ }
83
+ };
65
84
  const categoryEnergy = (cate) => {
66
85
  let energy = 0;
67
- (cate?.list || []).forEach(list => {
68
- (list?.getCategoryList ||[]).forEach(item => {
86
+ (cate?.list || []).forEach((list) => {
87
+ (list?.getCategoryList || []).forEach((item) => {
69
88
  energy += item.energy || 0;
70
- })
71
- })
72
- return energy
73
- }
89
+ });
90
+ });
91
+ return energy;
92
+ };
74
93
  const showDate = (idx) => {
75
94
  return (
76
95
  props.skuList
@@ -24,7 +24,7 @@
24
24
  width: fit-content;
25
25
  white-space: nowrap;
26
26
  font-weight: 400;
27
- font-size: 12px;
27
+ font-size: 14px;
28
28
  color: #333;
29
29
  line-height: 16px;
30
30
  text-align: left;
@@ -33,7 +33,7 @@
33
33
  border: 1px solid #d0d0d0;
34
34
  }
35
35
  .icon {
36
- font-size: 16px;
36
+ font-size: 18px;
37
37
  color: @primary-color;
38
38
  }
39
39
  }
@@ -20,6 +20,33 @@ const dialogInfo = ref({
20
20
  dialogHeight: '50vh',
21
21
  });
22
22
 
23
+ const sportList = [
24
+ {
25
+ key: 'rest',
26
+ name: '休息状态',
27
+ sub: '卧床或者坐式休息',
28
+ },
29
+ {
30
+ key: 'sedentary',
31
+ name: '久坐',
32
+ sub: '坐式工作',
33
+ },
34
+ {
35
+ key: 'light',
36
+ name: '轻体力',
37
+ sub: '站立或需要走动的工作',
38
+ },
39
+ {
40
+ key: 'medium',
41
+ name: '中体力',
42
+ sub: '走动多的工作',
43
+ },
44
+ {
45
+ key: 'heavy',
46
+ name: '重体力',
47
+ sub: '高强度体力工作',
48
+ },
49
+ ];
23
50
  const tasteList = [
24
51
  '不吃辣',
25
52
  '不吃葱',
@@ -112,7 +139,7 @@ const handleSelect = (item) => {
112
139
  break;
113
140
  case 'sport':
114
141
  dialogInfo.value.title = '日常运动水平';
115
- dialogInfo.value.dialogHeight = '360px';
142
+ dialogInfo.value.dialogHeight = '420px';
116
143
  dialogInfo.value.unit = '';
117
144
  dialogInfo.value.sport = item.value;
118
145
  break;
@@ -191,26 +218,22 @@ const handleSubmit = () => {
191
218
  return;
192
219
  }
193
220
  localStorage.setItem('personalInfo', JSON.stringify(pfForm.value));
194
- const msg = `
195
- 性别:${pfForm.value.find((item) => item.key === 'sex').value}
196
- 年龄:${pfForm.value.find((item) => item.key === 'age').value}${
221
+ const msg = `性别:${pfForm.value.find((item) => item.key === 'sex').value};
222
+ 年龄:${pfForm.value.find((item) => item.key === 'age').value}${
197
223
  pfForm.value.find((item) => item.key === 'age').unit || ''
198
224
  };
199
- 身高:${pfForm.value.find((item) => item.key === 'height').value}${
225
+ 身高:${pfForm.value.find((item) => item.key === 'height').value}${
200
226
  pfForm.value.find((item) => item.key === 'height').unit || ''
201
227
  };
202
- 体重:${pfForm.value.find((item) => item.key === 'weight').value}${
228
+ 体重:${pfForm.value.find((item) => item.key === 'weight').value}${
203
229
  pfForm.value.find((item) => item.key === 'weight').unit || ''
204
230
  };
205
- 日常运动水平:${
206
- pfForm.value.find((item) => item.key === 'sport').value
207
- };
208
- 口味偏好及饮食禁忌:${
209
- pfForm.value.find((item) => item.key === 'taste').value
210
- ? pfForm.value.find((item) => item.key === 'taste').value
211
- : '没有饮食禁忌'
212
- };
213
- `;
231
+ 日常运动水平:${pfForm.value.find((item) => item.key === 'sport').value};
232
+ 口味偏好及饮食禁忌:${
233
+ pfForm.value.find((item) => item.key === 'taste').value
234
+ ? pfForm.value.find((item) => item.key === 'taste').value
235
+ : '没有饮食禁忌'
236
+ };`;
214
237
  console.log(msg);
215
238
  Emits('submit', [{ content: msg, text: msg }]);
216
239
  };
@@ -222,6 +245,16 @@ const selectTag = (item, type) => {
222
245
  dialogInfo.value[type].push(item);
223
246
  }
224
247
  };
248
+
249
+ const selectSport = (item) => {
250
+ dialogInfo.value.sport = item.name;
251
+ handleConfirm();
252
+ };
253
+
254
+ const selectSex = (item) => {
255
+ dialogInfo.value.sex = item;
256
+ handleConfirm();
257
+ };
225
258
  </script>
226
259
 
227
260
  <template>
@@ -246,7 +279,7 @@ const selectTag = (item, type) => {
246
279
  :class="{ disabled: !isValid }"
247
280
  @click.stop="handleSubmit"
248
281
  >
249
- 确定
282
+ 按身体数据领取7天食谱
250
283
  </div>
251
284
  </div>
252
285
 
@@ -260,13 +293,13 @@ const selectTag = (item, type) => {
260
293
  <div v-if="dialogInfo.key === 'sex'" class="sex_wrap">
261
294
  <div
262
295
  :class="{ sex: true, selected: dialogInfo.sex === '男' }"
263
- @click="dialogInfo.sex = '男'"
296
+ @click.stop="selectSex('男')"
264
297
  >
265
298
 
266
299
  </div>
267
300
  <div
268
301
  :class="{ sex: true, selected: dialogInfo.sex === '女' }"
269
- @click="dialogInfo.sex = '女'"
302
+ @click.stop="selectSex('女')"
270
303
  >
271
304
 
272
305
  </div>
@@ -279,28 +312,20 @@ const selectTag = (item, type) => {
279
312
  v-model="dialogInfo[dialogInfo.key]"
280
313
  class="input"
281
314
  type="text"
315
+ @keyup.enter="handleConfirm"
282
316
  placeholder="请输入"
283
317
  />
284
318
  <div class="unit">{{ dialogInfo.unit }}</div>
285
319
  </div>
286
320
  <div v-if="dialogInfo.key === 'sport'" class="sport_wrap">
287
321
  <div
288
- :class="{ row: true, selected: dialogInfo.sport === '久坐' }"
289
- @click.stop="dialogInfo.sport = '久坐'"
322
+ v-for="item in sportList"
323
+ :key="item.key"
324
+ :class="{ row: true, selected: dialogInfo.sport === item.name }"
325
+ @click.stop="selectSport(item)"
290
326
  >
291
- <div class="name">久坐</div>
292
- </div>
293
- <div
294
- :class="{ row: true, selected: dialogInfo.sport === '轻体力' }"
295
- @click.stop="dialogInfo.sport = '轻体力'"
296
- >
297
- <div class="name">轻体力</div>
298
- </div>
299
- <div
300
- :class="{ row: true, selected: dialogInfo.sport === '中体力' }"
301
- @click.stop="dialogInfo.sport = '中体力'"
302
- >
303
- <div class="name">中体力</div>
327
+ <div class="name">{{ item.name }}</div>
328
+ <div class="sub">{{ item.sub }}</div>
304
329
  </div>
305
330
  </div>
306
331
  <div v-if="dialogInfo.key === 'taste'" class="taste_wrap">
@@ -346,6 +371,7 @@ const selectTag = (item, type) => {
346
371
 
347
372
  &_row {
348
373
  display: flex;
374
+ gap: 6px;
349
375
  flex-direction: row;
350
376
  align-items: center;
351
377
  justify-content: space-between;
@@ -377,7 +403,7 @@ const selectTag = (item, type) => {
377
403
  display: inline-block;
378
404
  font-size: 14px;
379
405
  text-align: right;
380
- width: 160px;
406
+ width: 156px;
381
407
  text-overflow: ellipsis;
382
408
  white-space: nowrap;
383
409
  overflow: hidden;
@@ -455,17 +481,37 @@ const selectTag = (item, type) => {
455
481
 
456
482
  .row {
457
483
  transition: 100ms ease-in-out;
458
- font-size: 15px;
459
- font-weight: 400;
460
- text-align: center;
461
484
  height: 58px;
462
- line-height: 58px;
463
485
  border-top: 1px solid #fff;
464
486
  border-bottom: 1px solid #fff;
487
+ .name {
488
+ font-size: 15px;
489
+ font-weight: 400;
490
+ text-align: center;
491
+ line-height: 28px;
492
+ }
493
+ .sub {
494
+ font-size: 13px;
495
+ font-weight: 400;
496
+ text-align: center;
497
+ line-height: 18px;
498
+ }
465
499
 
466
500
  &.selected {
467
- font-size: 20px;
468
- font-weight: 600;
501
+ .name {
502
+ color: #039938;
503
+ font-size: 16px;
504
+ font-weight: 600;
505
+ text-align: center;
506
+ line-height: 32px;
507
+ }
508
+ .sub {
509
+ color: #039938;
510
+ font-size: 12px;
511
+ font-weight: 400;
512
+ text-align: center;
513
+ line-height: 16px;
514
+ }
469
515
  color: #039938;
470
516
  border-color: #d9d9d9;
471
517
  }