ai-chat-bot-interface 1.6.7 → 1.6.8

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.8",
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,152 @@ 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((item) => item.material);
121
+ return (list || []).join('、');
122
+ });
123
+ const hasDetails = computed(() => {
124
+ let sts = false;
125
+ if (
126
+ props.info.hasOwnProperty('makeCraftList') &&
127
+ props.info.makeCraftList.length > 0
128
+ ) {
129
+ sts = true;
130
+ }
131
+ return sts;
132
+ });
38
133
 
39
134
  const previewImg = (url) => {
40
135
  viewerApi({
41
136
  images: [url],
42
137
  });
43
138
  };
139
+
140
+ const switchOpen = () => {
141
+ isOpen.value = !isOpen.value;
142
+ };
44
143
  </script>
45
144
 
46
145
  <style scoped lang="less">
47
146
  @import '../assets/styles/public';
48
147
 
49
148
  .dishes {
149
+ &_details {
150
+ padding: 1px 10px;
151
+
152
+ .title {
153
+ font-weight: 600;
154
+ font-size: 15px;
155
+ color: #000;
156
+ margin: 15px 0;
157
+ }
158
+ .text {
159
+ font-weight: 400;
160
+ font-size: 13px;
161
+ color: #000;
162
+ }
163
+ .step {
164
+ &_row {
165
+ margin-bottom: 15px;
166
+ }
167
+ &_item {
168
+ .flexrss();
169
+ .num {
170
+ width: 4em;
171
+ color: #666;
172
+ font-size: 13px;
173
+ }
174
+ .desc {
175
+ flex: 1;
176
+ font-size: 13px;
177
+ color: #000;
178
+ }
179
+ }
180
+ }
181
+ .line {
182
+ margin: 5px 0;
183
+ border-bottom: 1px solid #fff;
184
+ }
185
+ .data {
186
+ &_row {
187
+ .flexrsc();
188
+ gap: 10px;
189
+ }
190
+ &_item {
191
+ .flexrsc();
192
+ flex: 1;
193
+ gap: 10px;
194
+ line-height: 26px;
195
+ .label {
196
+ width: 72px;
197
+ font-weight: 400;
198
+ font-size: 13px;
199
+ color: #666;
200
+ }
201
+ .content {
202
+ flex: 1;
203
+ width: 60px;
204
+ font-weight: 400;
205
+ font-size: 13px;
206
+ color: #000;
207
+ }
208
+ }
209
+ }
210
+ .tag {
211
+ padding: 0 15px;
212
+ background-color: #fff;
213
+ border-radius: 15px;
214
+ line-height: 30px;
215
+ font-weight: 400;
216
+ font-size: 13px;
217
+ color: #000;
218
+ &_box {
219
+ .flexrsc();
220
+ gap: 10px;
221
+ }
222
+ }
223
+ .collapse_btn {
224
+ width: 100px;
225
+ height: 32px;
226
+ line-height: 32px;
227
+ text-align: center;
228
+ border-radius: 10px 10px 0 0;
229
+ background-color: #e2e2e2;
230
+ color: #999;
231
+ margin: 20px auto 0;
232
+ }
233
+ }
234
+ &_box {
235
+ transition: height 100ms ease-in-out;
236
+ height: 92px;
237
+ background-color: #ededed;
238
+ border-radius: 10px;
239
+ margin: 10px 0;
240
+ overflow: hidden;
241
+ &.is_open {
242
+ //height: auto;
243
+ }
244
+ }
50
245
  &_wrap {
51
246
  display: grid;
52
247
  grid-template-columns: 72px 1fr;
@@ -54,7 +249,6 @@ const previewImg = (url) => {
54
249
  background-color: @bg-color;
55
250
  border-radius: 10px;
56
251
  padding: 10px;
57
- margin: 10px 0;
58
252
 
59
253
  .img {
60
254
  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>
@@ -27,7 +30,11 @@
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
@@ -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
  };
@@ -285,22 +308,13 @@ const selectTag = (item, type) => {
285
308
  </div>
286
309
  <div v-if="dialogInfo.key === 'sport'" class="sport_wrap">
287
310
  <div
288
- :class="{ row: true, selected: dialogInfo.sport === '久坐' }"
289
- @click.stop="dialogInfo.sport = '久坐'"
311
+ v-for="item in sportList"
312
+ :key="item.key"
313
+ :class="{ row: true, selected: dialogInfo.sport === item.name }"
314
+ @click.stop="dialogInfo.sport = item.name"
290
315
  >
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>
316
+ <div class="name">{{ item.name }}</div>
317
+ <div class="sub">{{ item.sub }}</div>
304
318
  </div>
305
319
  </div>
306
320
  <div v-if="dialogInfo.key === 'taste'" class="taste_wrap">
@@ -455,17 +469,37 @@ const selectTag = (item, type) => {
455
469
 
456
470
  .row {
457
471
  transition: 100ms ease-in-out;
458
- font-size: 15px;
459
- font-weight: 400;
460
- text-align: center;
461
472
  height: 58px;
462
- line-height: 58px;
463
473
  border-top: 1px solid #fff;
464
474
  border-bottom: 1px solid #fff;
475
+ .name {
476
+ font-size: 15px;
477
+ font-weight: 400;
478
+ text-align: center;
479
+ line-height: 28px;
480
+ }
481
+ .sub {
482
+ font-size: 13px;
483
+ font-weight: 400;
484
+ text-align: center;
485
+ line-height: 18px;
486
+ }
465
487
 
466
488
  &.selected {
467
- font-size: 20px;
468
- font-weight: 600;
489
+ .name {
490
+ color: #039938;
491
+ font-size: 16px;
492
+ font-weight: 600;
493
+ text-align: center;
494
+ line-height: 32px;
495
+ }
496
+ .sub {
497
+ color: #039938;
498
+ font-size: 12px;
499
+ font-weight: 400;
500
+ text-align: center;
501
+ line-height: 16px;
502
+ }
469
503
  color: #039938;
470
504
  border-color: #d9d9d9;
471
505
  }