ai-chat-bot-interface 1.0.9 → 1.1.1
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/.prettierignore +5 -0
- package/.prettierrc.cjs +37 -0
- package/index.html +1 -1
- package/package.json +5 -3
- package/src/App.vue +4 -12
- package/src/ChatUi.less +45 -24
- package/src/ChatUi.vue +166 -105
- package/src/assets/styles/public.less +5 -2
- package/src/components/DishesCard.vue +8 -9
- package/src/components/DishesList.vue +14 -10
- package/src/components/OperateModule.vue +176 -0
- package/src/components/PlanCard.vue +5 -6
- package/src/components/icons/BackIcon.vue +19 -0
- package/src/components/icons/ClearIcon.vue +10 -3
- package/src/components/icons/CloseIcon.vue +9 -2
- package/src/components/icons/NewSessionIcon.vue +11 -4
- package/src/components/icons/SendIcon.vue +14 -3
- package/src/components/icons/loadingIcon.vue +76 -0
- package/src/components/icons/loadingIcon2.vue +88 -0
- package/src/components/icons/newChat.vue +18 -0
- package/src/components/icons/tagIcon.vue +18 -0
- package/src/main.js +4 -4
- package/src/style.css +4 -79
- package/src/utils/request.js +15 -17
- package/vite.config.js +20 -3
- package/src/mock.js +0 -0
package/src/ChatUi.vue
CHANGED
|
@@ -3,94 +3,129 @@
|
|
|
3
3
|
<div class="cui_wrap">
|
|
4
4
|
<div class="cui_header">
|
|
5
5
|
<div class="title" @click.stop="queryHistoryList">
|
|
6
|
-
<
|
|
7
|
-
|
|
6
|
+
<div class="back" @click.stop="handleBack">
|
|
7
|
+
<back-icon />
|
|
8
|
+
</div>
|
|
9
|
+
<img
|
|
10
|
+
class="logo"
|
|
11
|
+
:src="logo"
|
|
12
|
+
alt="logo"
|
|
13
|
+
style="width: 24px; height: 24px"
|
|
14
|
+
/>
|
|
15
|
+
<div class="name_box">
|
|
16
|
+
<div class="name">{{ name }}</div>
|
|
17
|
+
<div class="name_sub">nutribite.com</div>
|
|
18
|
+
</div>
|
|
8
19
|
</div>
|
|
9
20
|
<div class="btn_group">
|
|
21
|
+
<button class="btn" @click.stop="createConv">
|
|
22
|
+
<new-chat class="icon" />
|
|
23
|
+
</button>
|
|
10
24
|
<template v-show="false">
|
|
11
25
|
<button class="btn">
|
|
12
|
-
<clear-icon/>
|
|
26
|
+
<clear-icon />
|
|
13
27
|
</button>
|
|
14
28
|
<button class="btn">
|
|
15
|
-
<close-icon/>
|
|
29
|
+
<close-icon />
|
|
16
30
|
</button>
|
|
17
31
|
</template>
|
|
18
32
|
</div>
|
|
19
33
|
</div>
|
|
20
34
|
<div class="cui_content">
|
|
21
|
-
<div
|
|
35
|
+
<div
|
|
36
|
+
v-if="botInfo && botInfo.onboarding_info"
|
|
37
|
+
style="text-align: left; margin-top: 50px"
|
|
38
|
+
>
|
|
22
39
|
<div style="text-align: center">
|
|
23
|
-
<img
|
|
24
|
-
|
|
40
|
+
<img
|
|
41
|
+
:src="botInfo.icon_url"
|
|
42
|
+
alt="icon"
|
|
43
|
+
width="64"
|
|
44
|
+
height="64"
|
|
45
|
+
style="border-radius: 15px"
|
|
25
46
|
/>
|
|
26
47
|
<p class="board_name">{{ botInfo.name }}</p>
|
|
27
48
|
</div>
|
|
28
49
|
|
|
29
50
|
<div class="board_desc">{{ botInfo.onboarding_info.prologue }}</div>
|
|
30
51
|
<div class="flexcss">
|
|
31
|
-
<span
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
52
|
+
<span
|
|
53
|
+
v-for="(item, idx) in botInfo.onboarding_info.suggested_questions"
|
|
54
|
+
:key="idx"
|
|
55
|
+
class="board_sug"
|
|
56
|
+
@click.stop="chatConv({ code: item, text: item })"
|
|
57
|
+
>{{ item }}</span
|
|
58
|
+
>
|
|
36
59
|
</div>
|
|
37
60
|
</div>
|
|
38
61
|
<template v-for="(conv, index) in historyList" :key="index">
|
|
39
62
|
<div v-if="conv.role === 'assistant'" class="replay role_sys">
|
|
40
|
-
<img class="avatar" :src="logo" alt="avatar">
|
|
41
63
|
<div class="replay_content">
|
|
42
|
-
<div class="name">
|
|
64
|
+
<div class="name">
|
|
65
|
+
<img class="avatar" :src="logo" alt="avatar" />
|
|
66
|
+
{{ name }}
|
|
67
|
+
<!--<span class="time">12:30</span>-->
|
|
68
|
+
</div>
|
|
43
69
|
<div class="box">
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
<
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
70
|
+
<template v-if="conv.content">
|
|
71
|
+
<p class="text" v-html="conv.content"></p>
|
|
72
|
+
<div v-if="conv.extra.length && !isAnswering">
|
|
73
|
+
<template v-for="(comp, idx) in conv.extra" :key="idx">
|
|
74
|
+
<dishes-list
|
|
75
|
+
v-if="comp.showType === 'card'"
|
|
76
|
+
:sku-list="comp.skuList"
|
|
77
|
+
@select="(data) => handleCardTap(data, comp)"
|
|
78
|
+
/>
|
|
79
|
+
<plan-card
|
|
80
|
+
v-if="comp.showType === 'plan'"
|
|
81
|
+
@select="handleCardTap({ type: 'match' }, comp)"
|
|
82
|
+
/>
|
|
83
|
+
</template>
|
|
84
|
+
</div>
|
|
85
|
+
</template>
|
|
86
|
+
<loading-icon2 v-else />
|
|
52
87
|
</div>
|
|
53
88
|
</div>
|
|
54
89
|
</div>
|
|
55
90
|
<div v-else class="replay role_user">
|
|
56
91
|
<div class="replay_content">
|
|
57
|
-
<div class="name">
|
|
92
|
+
<div class="name">
|
|
93
|
+
User_{{ uid }}
|
|
94
|
+
<!--<span class="time">12:30</span>-->
|
|
95
|
+
<img class="avatar" :src="avatar" alt="avatar" />
|
|
96
|
+
</div>
|
|
58
97
|
<div class="box">
|
|
59
98
|
<p class="text" v-html="conv.content"></p>
|
|
60
99
|
</div>
|
|
61
100
|
</div>
|
|
62
|
-
<img class="avatar" :src="avatar" alt="avatar">
|
|
63
101
|
</div>
|
|
64
102
|
</template>
|
|
65
|
-
<div ref="endTarget" style="height: 100px
|
|
103
|
+
<div ref="endTarget" style="height: 100px" />
|
|
66
104
|
</div>
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
<input v-model="inputText" class="input" @keyup.enter="chatConv"/>
|
|
73
|
-
<div class="send" @click.stop="chatConv">
|
|
74
|
-
<send-icon :style="{ color: !isAnswering && inputText ? '#333': '#ccc'}"/>
|
|
75
|
-
</div>
|
|
76
|
-
</div>
|
|
77
|
-
</div>
|
|
78
|
-
<div class="cui_footer">Powered by DeepSeek</div>
|
|
105
|
+
<operate-module
|
|
106
|
+
v-model="inputText"
|
|
107
|
+
@send="chatConv"
|
|
108
|
+
@tag="handleTagSel"
|
|
109
|
+
/>
|
|
79
110
|
</div>
|
|
80
111
|
</div>
|
|
81
|
-
|
|
82
112
|
</template>
|
|
83
113
|
|
|
84
114
|
<script setup>
|
|
85
|
-
import {computed, nextTick, onMounted, ref} from 'vue';
|
|
115
|
+
import { computed, nextTick, onMounted, ref } from 'vue';
|
|
86
116
|
import ClearIcon from './components/icons/ClearIcon.vue';
|
|
87
117
|
import CloseIcon from './components/icons/CloseIcon.vue';
|
|
88
118
|
import NewSessionIcon from './components/icons/NewSessionIcon.vue';
|
|
89
119
|
import SendIcon from './components/icons/SendIcon.vue';
|
|
90
|
-
import {get, post} from './utils/request';
|
|
120
|
+
import { get, post } from './utils/request';
|
|
91
121
|
import DishesCard from './components/DishesCard.vue';
|
|
92
122
|
import DishesList from './components/DishesList.vue';
|
|
93
123
|
import PlanCard from './components/PlanCard.vue';
|
|
124
|
+
import OperateModule from './components/OperateModule.vue';
|
|
125
|
+
import BackIcon from './components/icons/BackIcon.vue';
|
|
126
|
+
import NewChat from './components/icons/newChat.vue';
|
|
127
|
+
import LoadingIcon from './components/icons/loadingIcon.vue';
|
|
128
|
+
import LoadingIcon2 from './components/icons/loadingIcon2.vue';
|
|
94
129
|
|
|
95
130
|
const chatOptions = computed(() => {
|
|
96
131
|
return {
|
|
@@ -111,7 +146,8 @@ const props = defineProps({
|
|
|
111
146
|
},
|
|
112
147
|
avatar: {
|
|
113
148
|
type: String,
|
|
114
|
-
default:
|
|
149
|
+
default:
|
|
150
|
+
'https://prodstatic.weis1606.cn/api/smartFood/Nutribite/icons/home/icon_3.png',
|
|
115
151
|
},
|
|
116
152
|
name: {
|
|
117
153
|
type: String,
|
|
@@ -130,12 +166,11 @@ const props = defineProps({
|
|
|
130
166
|
type: String,
|
|
131
167
|
required: true,
|
|
132
168
|
},
|
|
133
|
-
|
|
134
169
|
});
|
|
135
170
|
|
|
136
171
|
const Emits = defineEmits(['call']);
|
|
137
172
|
|
|
138
|
-
|
|
173
|
+
const endTarget = ref(null);
|
|
139
174
|
const inputText = ref('');
|
|
140
175
|
const botInfo = ref({});
|
|
141
176
|
|
|
@@ -152,9 +187,9 @@ onMounted(async () => {
|
|
|
152
187
|
|
|
153
188
|
const createConv = async () => {
|
|
154
189
|
const res = await post(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
190
|
+
'https://api.coze.cn/v1/conversation/create',
|
|
191
|
+
{ bot_id: props.botId, connector_id: '999' },
|
|
192
|
+
{ ...chatOptions.value },
|
|
158
193
|
);
|
|
159
194
|
console.log(res);
|
|
160
195
|
if (res.code === 0 && res.data) {
|
|
@@ -217,32 +252,33 @@ const chatConv = async (data) => {
|
|
|
217
252
|
extra: [],
|
|
218
253
|
});
|
|
219
254
|
const res = await fetch(
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
},
|
|
227
|
-
body: JSON.stringify({
|
|
228
|
-
bot_id: props.botId,
|
|
229
|
-
user_id: props.uid,
|
|
230
|
-
stream: true,
|
|
231
|
-
connector_id: '999',
|
|
232
|
-
additional_messages: [
|
|
233
|
-
{
|
|
234
|
-
'role': 'user',
|
|
235
|
-
'content_type': 'text',
|
|
236
|
-
'content': isInCode ? data.code : inText, // '配餐1600kcal,身高173,体重60kg,生成一天的套餐',
|
|
237
|
-
}],
|
|
238
|
-
custom_variables: {
|
|
239
|
-
'uid': props.uid,
|
|
240
|
-
},
|
|
241
|
-
}),
|
|
242
|
-
credentials: 'same-origin', // 默认同源策略
|
|
243
|
-
mode: 'cors', // 默认跨域模式
|
|
244
|
-
cache: 'default', // 默认缓存策略
|
|
255
|
+
`https://api.coze.cn/v3/chat?conversation_id=${conversationId.value}`,
|
|
256
|
+
{
|
|
257
|
+
method: 'POST',
|
|
258
|
+
headers: {
|
|
259
|
+
...chatOptions.value.headers,
|
|
260
|
+
// 'Content-Type': 'text/event-stream',
|
|
245
261
|
},
|
|
262
|
+
body: JSON.stringify({
|
|
263
|
+
bot_id: props.botId,
|
|
264
|
+
user_id: props.uid,
|
|
265
|
+
stream: true,
|
|
266
|
+
connector_id: '999',
|
|
267
|
+
additional_messages: [
|
|
268
|
+
{
|
|
269
|
+
role: 'user',
|
|
270
|
+
content_type: 'text',
|
|
271
|
+
content: isInCode ? data.code : inText, // '配餐1600kcal,身高173,体重60kg,生成一天的套餐',
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
custom_variables: {
|
|
275
|
+
uid: props.uid,
|
|
276
|
+
},
|
|
277
|
+
}),
|
|
278
|
+
credentials: 'same-origin', // 默认同源策略
|
|
279
|
+
mode: 'cors', // 默认跨域模式
|
|
280
|
+
cache: 'default', // 默认缓存策略
|
|
281
|
+
},
|
|
246
282
|
);
|
|
247
283
|
historyList.value.push({
|
|
248
284
|
conversation_id: '',
|
|
@@ -260,7 +296,7 @@ const chatConv = async (data) => {
|
|
|
260
296
|
let buffer = '';
|
|
261
297
|
// 逐块读取数据
|
|
262
298
|
while (true) {
|
|
263
|
-
const {done, value} = await reader.read();
|
|
299
|
+
const { done, value } = await reader.read();
|
|
264
300
|
if (done) {
|
|
265
301
|
console.log('Stream has ended.');
|
|
266
302
|
isAnswering.value = false;
|
|
@@ -270,14 +306,14 @@ const chatConv = async (data) => {
|
|
|
270
306
|
}
|
|
271
307
|
|
|
272
308
|
// 解码数据块
|
|
273
|
-
const chunk = decoder.decode(value, {stream: true});
|
|
309
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
274
310
|
buffer += chunk;
|
|
275
311
|
|
|
276
312
|
// 处理数据块
|
|
277
313
|
const lines = buffer.split('\n');
|
|
278
314
|
buffer = lines.pop(); // 保留未处理的片段
|
|
279
315
|
|
|
280
|
-
lines.forEach(line => {
|
|
316
|
+
lines.forEach((line) => {
|
|
281
317
|
if (line.trim().length > 0 && line.trim().startsWith('data:')) {
|
|
282
318
|
const str = line.replace(/^data:\s*/, '');
|
|
283
319
|
const strObj = JSON.parse(str);
|
|
@@ -290,22 +326,28 @@ const chatConv = async (data) => {
|
|
|
290
326
|
if (!historyList.value[idx].conversation_id && strObj.conversation_id) {
|
|
291
327
|
historyList.value[idx].conversation_id = strObj.conversation_id;
|
|
292
328
|
}
|
|
293
|
-
if (
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
329
|
+
if (
|
|
330
|
+
strObj.hasOwnProperty('content') &&
|
|
331
|
+
strObj.hasOwnProperty('content_type') &&
|
|
332
|
+
strObj.content_type === 'text' &&
|
|
333
|
+
strObj.hasOwnProperty('type') &&
|
|
334
|
+
strObj.type === 'answer' &&
|
|
335
|
+
!strObj.hasOwnProperty('created_at')
|
|
336
|
+
) {
|
|
337
|
+
historyList.value[idx].content = handleText(
|
|
338
|
+
historyList.value[idx].content + strObj.content,
|
|
339
|
+
);
|
|
340
|
+
} else if (
|
|
341
|
+
strObj.hasOwnProperty('type') &&
|
|
342
|
+
strObj.type === 'tool_response'
|
|
343
|
+
) {
|
|
302
344
|
const extraObj = JSON.parse(strObj.content);
|
|
303
345
|
if (extraObj.hasOwnProperty('showType')) {
|
|
304
|
-
historyList.value[idx].extra.push({...extraObj});
|
|
346
|
+
historyList.value[idx].extra.push({ ...extraObj });
|
|
305
347
|
} else if (extraObj.hasOwnProperty('response_for_model')) {
|
|
306
348
|
const modelObj = JSON.parse(extraObj.response_for_model);
|
|
307
349
|
if (modelObj.hasOwnProperty('showType')) {
|
|
308
|
-
historyList.value[idx].extra.push({...modelObj});
|
|
350
|
+
historyList.value[idx].extra.push({ ...modelObj });
|
|
309
351
|
}
|
|
310
352
|
}
|
|
311
353
|
}
|
|
@@ -317,14 +359,14 @@ const chatConv = async (data) => {
|
|
|
317
359
|
|
|
318
360
|
const queryHistoryList = async () => {
|
|
319
361
|
const res = await post(
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
362
|
+
`https://api.coze.cn/v1/conversation/message/list?conversation_id=${conversationId.value}`,
|
|
363
|
+
{ order: 'asc' },
|
|
364
|
+
{ ...chatOptions.value },
|
|
323
365
|
);
|
|
324
366
|
if (res.code === 0 && res.data && res.data.length) {
|
|
325
367
|
historyList.value = [];
|
|
326
368
|
const cardList = [];
|
|
327
|
-
res.data.forEach(row => {
|
|
369
|
+
res.data.forEach((row) => {
|
|
328
370
|
if (row.hasOwnProperty('content_type')) {
|
|
329
371
|
if (row.content_type === 'text') {
|
|
330
372
|
historyList.value.push({
|
|
@@ -345,7 +387,7 @@ const queryHistoryList = async () => {
|
|
|
345
387
|
conversation_id: row.conversation_id,
|
|
346
388
|
bot_id: row.bot_id,
|
|
347
389
|
role: row.role,
|
|
348
|
-
extra: [{...cardObj}],
|
|
390
|
+
extra: [{ ...cardObj }],
|
|
349
391
|
});
|
|
350
392
|
} else if (cardObj.hasOwnProperty('response_for_model')) {
|
|
351
393
|
const modelObj = JSON.parse(cardObj.response_for_model);
|
|
@@ -355,7 +397,7 @@ const queryHistoryList = async () => {
|
|
|
355
397
|
conversation_id: row.conversation_id,
|
|
356
398
|
bot_id: row.bot_id,
|
|
357
399
|
role: row.role,
|
|
358
|
-
extra: [{...modelObj}],
|
|
400
|
+
extra: [{ ...modelObj }],
|
|
359
401
|
});
|
|
360
402
|
}
|
|
361
403
|
}
|
|
@@ -365,8 +407,10 @@ const queryHistoryList = async () => {
|
|
|
365
407
|
}
|
|
366
408
|
}
|
|
367
409
|
});
|
|
368
|
-
cardList.forEach(card => {
|
|
369
|
-
const idx = historyList.value.findIndex(
|
|
410
|
+
cardList.forEach((card) => {
|
|
411
|
+
const idx = historyList.value.findIndex(
|
|
412
|
+
(c) => c.chat_id === card.chat_id && c.role === 'assistant',
|
|
413
|
+
);
|
|
370
414
|
if (idx > -1) {
|
|
371
415
|
historyList.value[idx].extra = [...card.extra];
|
|
372
416
|
}
|
|
@@ -377,40 +421,57 @@ const queryHistoryList = async () => {
|
|
|
377
421
|
|
|
378
422
|
const queryBotInfo = async () => {
|
|
379
423
|
const res = await get(
|
|
380
|
-
|
|
381
|
-
|
|
424
|
+
`https://api.coze.cn/v1/bot/get_online_info?bot_id=${props.botId}`,
|
|
425
|
+
{ ...chatOptions.value },
|
|
382
426
|
);
|
|
383
427
|
console.log(res);
|
|
384
428
|
if (res.code === 0 && res.data) {
|
|
385
|
-
botInfo.value = {...res.data};
|
|
429
|
+
botInfo.value = { ...res.data };
|
|
386
430
|
}
|
|
387
431
|
};
|
|
388
432
|
|
|
389
433
|
const handleText = (str) => {
|
|
390
434
|
// console.log(str);
|
|
391
|
-
return str.replaceAll(
|
|
435
|
+
return str.replaceAll(
|
|
436
|
+
/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/gi,
|
|
437
|
+
'<a href="$2" target="_blank">[$1]</a>',
|
|
438
|
+
);
|
|
439
|
+
};
|
|
440
|
+
const handleBack = () => {
|
|
441
|
+
Emits('call', { type: 'back' });
|
|
392
442
|
};
|
|
393
443
|
|
|
394
|
-
const
|
|
444
|
+
const handleTagSel = (info) => {
|
|
445
|
+
switch (info.type) {
|
|
446
|
+
case 'chat':
|
|
447
|
+
chatConv({ code: info.msg, text: info.msg });
|
|
448
|
+
break;
|
|
449
|
+
case 'call':
|
|
450
|
+
Emits('call', { ...info });
|
|
451
|
+
break;
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
const handleCardTap = ({ type }, info) => {
|
|
395
456
|
switch (type) {
|
|
396
457
|
case 'change':
|
|
397
|
-
chatConv({code: '換一套菜品', text: '換一套菜品'});
|
|
458
|
+
chatConv({ code: '換一套菜品', text: '換一套菜品' });
|
|
398
459
|
break;
|
|
399
460
|
case 'match':
|
|
400
|
-
chatConv({code: '請用以上方案為我配餐', text: '請用以上方案為我配餐'});
|
|
461
|
+
chatConv({ code: '請用以上方案為我配餐', text: '請用以上方案為我配餐' });
|
|
401
462
|
break;
|
|
402
463
|
default:
|
|
403
|
-
Emits('call', {type, info});
|
|
464
|
+
Emits('call', { type, info });
|
|
404
465
|
}
|
|
405
466
|
};
|
|
406
467
|
|
|
407
468
|
const scrollToEnd = () => {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
469
|
+
nextTick(() => {
|
|
470
|
+
endTarget.value.scrollIntoView();
|
|
471
|
+
});
|
|
411
472
|
};
|
|
412
473
|
</script>
|
|
413
474
|
|
|
414
475
|
<style scoped lang="less">
|
|
415
|
-
@import
|
|
476
|
+
@import './ChatUi';
|
|
416
477
|
</style>
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
@border-color: rgba(68, 83, 130, 0.25);
|
|
2
|
-
@border-radius:
|
|
2
|
+
@border-radius: 10px;
|
|
3
3
|
@box-max: 290px;
|
|
4
4
|
@max-width: 630px;
|
|
5
|
-
@bg-color: #
|
|
5
|
+
@bg-color: #fff;
|
|
6
|
+
@sys-bg: #f3f4f5;
|
|
7
|
+
@user-bg: #28d465;
|
|
8
|
+
@primary-color: #039938;
|
|
6
9
|
|
|
7
10
|
.flexr {
|
|
8
11
|
display: flex;
|
|
@@ -3,28 +3,27 @@
|
|
|
3
3
|
<img class="img" :src="info.primaryImgUrl" alt="img" />
|
|
4
4
|
<div>
|
|
5
5
|
<div class="name">
|
|
6
|
-
<span>{{info.skuname}}</span>
|
|
6
|
+
<span>{{ info.skuname }}</span>
|
|
7
7
|
<span>×1</span>
|
|
8
8
|
</div>
|
|
9
|
-
<div class="sub">{{subStr}}</div>
|
|
9
|
+
<div class="sub">{{ subStr }}</div>
|
|
10
10
|
</div>
|
|
11
11
|
</div>
|
|
12
12
|
</template>
|
|
13
13
|
|
|
14
14
|
<script setup>
|
|
15
|
-
import {computed} from 'vue';
|
|
15
|
+
import { computed } from 'vue';
|
|
16
16
|
|
|
17
17
|
const props = defineProps({
|
|
18
18
|
info: {
|
|
19
19
|
type: Object,
|
|
20
|
-
required: true
|
|
21
|
-
}
|
|
22
|
-
})
|
|
20
|
+
required: true,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
23
|
|
|
24
24
|
const subStr = computed(() => {
|
|
25
|
-
return `热量${props.info.energy}kcal,蛋白质${props.info.protein}g,脂肪${props.info.fat}g,碳水${props.info.carbonwater}g
|
|
26
|
-
})
|
|
27
|
-
|
|
25
|
+
return `热量${props.info.energy}kcal,蛋白质${props.info.protein}g,脂肪${props.info.fat}g,碳水${props.info.carbonwater}g`;
|
|
26
|
+
});
|
|
28
27
|
</script>
|
|
29
28
|
|
|
30
29
|
<style scoped lang="less">
|
|
@@ -5,18 +5,24 @@
|
|
|
5
5
|
<p class="title">{{ cate.categoryType }}</p>
|
|
6
6
|
<div>
|
|
7
7
|
<template v-for="dList in cate.list" :key="dList.id">
|
|
8
|
-
<dishes-card
|
|
8
|
+
<dishes-card
|
|
9
|
+
v-for="(info, idx) in dList.getCategoryList"
|
|
10
|
+
:key="idx"
|
|
11
|
+
:info="info"
|
|
12
|
+
/>
|
|
9
13
|
</template>
|
|
10
14
|
</div>
|
|
11
15
|
</div>
|
|
12
16
|
</template>
|
|
13
17
|
<div class="btn_group">
|
|
14
|
-
<div class="btn btn_1" @click.stop="handleBtn('ship_order')"
|
|
15
|
-
|
|
18
|
+
<div class="btn btn_1" @click.stop="handleBtn('ship_order')">
|
|
19
|
+
配送下单
|
|
20
|
+
</div>
|
|
21
|
+
<div class="btn btn_2" @click.stop="handleBtn('pick_order')">
|
|
22
|
+
自取下单
|
|
23
|
+
</div>
|
|
16
24
|
</div>
|
|
17
25
|
</div>
|
|
18
|
-
|
|
19
|
-
|
|
20
26
|
</template>
|
|
21
27
|
|
|
22
28
|
<script setup>
|
|
@@ -26,14 +32,13 @@ const props = defineProps({
|
|
|
26
32
|
skuList: {
|
|
27
33
|
type: Array,
|
|
28
34
|
required: true,
|
|
29
|
-
default: () =>
|
|
35
|
+
default: () => [],
|
|
30
36
|
},
|
|
31
37
|
});
|
|
32
38
|
const Emits = defineEmits(['select']);
|
|
33
39
|
|
|
34
|
-
|
|
35
40
|
const handleBtn = (type) => {
|
|
36
|
-
Emits('select', {type});
|
|
41
|
+
Emits('select', { type });
|
|
37
42
|
};
|
|
38
43
|
</script>
|
|
39
44
|
|
|
@@ -55,7 +60,7 @@ const handleBtn = (type) => {
|
|
|
55
60
|
width: 100%;
|
|
56
61
|
height: 38px;
|
|
57
62
|
line-height: 38px;
|
|
58
|
-
background-color: #
|
|
63
|
+
background-color: #e6f5eb;
|
|
59
64
|
border-radius: 19px;
|
|
60
65
|
text-align: center;
|
|
61
66
|
font-weight: 600;
|
|
@@ -76,5 +81,4 @@ const handleBtn = (type) => {
|
|
|
76
81
|
}
|
|
77
82
|
}
|
|
78
83
|
}
|
|
79
|
-
|
|
80
84
|
</style>
|