ai-chat-bot-interface 1.0.8 → 1.1.0
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 -26
- package/src/ChatUi.vue +184 -118
- package/src/assets/styles/public.less +5 -2
- package/src/components/DishesCard.vue +8 -9
- package/src/components/DishesList.vue +16 -12
- package/src/components/OperateModule.vue +175 -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/components/HelloWorld.vue +0 -43
- package/src/mock.js +0 -162
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
|
-
|
|
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 />
|
|
51
87
|
</div>
|
|
52
88
|
</div>
|
|
53
89
|
</div>
|
|
54
90
|
<div v-else class="replay role_user">
|
|
55
91
|
<div class="replay_content">
|
|
56
|
-
<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>
|
|
57
97
|
<div class="box">
|
|
58
98
|
<p class="text" v-html="conv.content"></p>
|
|
59
99
|
</div>
|
|
60
100
|
</div>
|
|
61
|
-
<img class="avatar" :src="avatar" alt="avatar">
|
|
62
101
|
</div>
|
|
63
102
|
</template>
|
|
64
|
-
|
|
65
|
-
</div>
|
|
66
|
-
<div class="cui_operate">
|
|
67
|
-
<button class="btn" @click.stop="createConv">
|
|
68
|
-
<new-session-icon/>
|
|
69
|
-
</button>
|
|
70
|
-
<div class="input_group">
|
|
71
|
-
<input v-model="inputText" class="input" @keyup.enter="chatConv"/>
|
|
72
|
-
<div class="send" @click.stop="chatConv">
|
|
73
|
-
<send-icon :style="{ color: !isAnswering && inputText ? '#333': '#ccc'}"/>
|
|
74
|
-
</div>
|
|
75
|
-
</div>
|
|
103
|
+
<div ref="endTarget" style="height: 100px" />
|
|
76
104
|
</div>
|
|
77
|
-
<
|
|
105
|
+
<operate-module
|
|
106
|
+
v-model="inputText"
|
|
107
|
+
@send="chatConv"
|
|
108
|
+
@tag="handleTagSel"
|
|
109
|
+
/>
|
|
78
110
|
</div>
|
|
79
111
|
</div>
|
|
80
|
-
|
|
81
112
|
</template>
|
|
82
113
|
|
|
83
114
|
<script setup>
|
|
84
|
-
import {computed, nextTick, onMounted, ref} from 'vue';
|
|
115
|
+
import { computed, nextTick, onMounted, ref } from 'vue';
|
|
85
116
|
import ClearIcon from './components/icons/ClearIcon.vue';
|
|
86
117
|
import CloseIcon from './components/icons/CloseIcon.vue';
|
|
87
118
|
import NewSessionIcon from './components/icons/NewSessionIcon.vue';
|
|
88
119
|
import SendIcon from './components/icons/SendIcon.vue';
|
|
89
|
-
import
|
|
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) {
|
|
@@ -162,6 +197,7 @@ const createConv = async () => {
|
|
|
162
197
|
localStorage.setItem('conversationId', conversationId.value);
|
|
163
198
|
historyList.value = [];
|
|
164
199
|
await queryBotInfo();
|
|
200
|
+
// await messageCreate();
|
|
165
201
|
}
|
|
166
202
|
};
|
|
167
203
|
// const messageCreate = async () => {
|
|
@@ -171,7 +207,7 @@ const createConv = async () => {
|
|
|
171
207
|
// { "role":"user","content":`#userid_${props.uid}`,"content_type":"text"},
|
|
172
208
|
// {...chatOptions.value});
|
|
173
209
|
// };
|
|
174
|
-
const messageCreate = async () => {
|
|
210
|
+
/*const messageCreate = async () => {
|
|
175
211
|
const res = await fetch(
|
|
176
212
|
`https://api.coze.cn/v3/chat?conversation_id=${conversationId.value}`,
|
|
177
213
|
{
|
|
@@ -190,6 +226,7 @@ const messageCreate = async () => {
|
|
|
190
226
|
'role': 'user',
|
|
191
227
|
'content_type': 'text',
|
|
192
228
|
'content': `#userid_${props.uid}`,
|
|
229
|
+
|
|
193
230
|
}],
|
|
194
231
|
}),
|
|
195
232
|
credentials: 'same-origin', // 默认同源策略
|
|
@@ -197,7 +234,7 @@ const messageCreate = async () => {
|
|
|
197
234
|
cache: 'default', // 默认缓存策略
|
|
198
235
|
},
|
|
199
236
|
);
|
|
200
|
-
}
|
|
237
|
+
};*/
|
|
201
238
|
const chatConv = async (data) => {
|
|
202
239
|
let isInCode = data.hasOwnProperty('text') && data.hasOwnProperty('code');
|
|
203
240
|
if (!(!isAnswering.value && (inputText.value || isInCode))) {
|
|
@@ -215,29 +252,33 @@ const chatConv = async (data) => {
|
|
|
215
252
|
extra: [],
|
|
216
253
|
});
|
|
217
254
|
const res = await fetch(
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
},
|
|
225
|
-
body: JSON.stringify({
|
|
226
|
-
bot_id: props.botId,
|
|
227
|
-
user_id: props.uid,
|
|
228
|
-
stream: true,
|
|
229
|
-
connector_id: '999',
|
|
230
|
-
additional_messages: [
|
|
231
|
-
{
|
|
232
|
-
'role': 'user',
|
|
233
|
-
'content_type': 'text',
|
|
234
|
-
'content': isInCode ? data.code : inText, // '配餐1600kcal,身高173,体重60kg,生成一天的套餐',
|
|
235
|
-
}],
|
|
236
|
-
}),
|
|
237
|
-
credentials: 'same-origin', // 默认同源策略
|
|
238
|
-
mode: 'cors', // 默认跨域模式
|
|
239
|
-
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',
|
|
240
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
|
+
},
|
|
241
282
|
);
|
|
242
283
|
historyList.value.push({
|
|
243
284
|
conversation_id: '',
|
|
@@ -255,7 +296,7 @@ const chatConv = async (data) => {
|
|
|
255
296
|
let buffer = '';
|
|
256
297
|
// 逐块读取数据
|
|
257
298
|
while (true) {
|
|
258
|
-
const {done, value} = await reader.read();
|
|
299
|
+
const { done, value } = await reader.read();
|
|
259
300
|
if (done) {
|
|
260
301
|
console.log('Stream has ended.');
|
|
261
302
|
isAnswering.value = false;
|
|
@@ -265,14 +306,14 @@ const chatConv = async (data) => {
|
|
|
265
306
|
}
|
|
266
307
|
|
|
267
308
|
// 解码数据块
|
|
268
|
-
const chunk = decoder.decode(value, {stream: true});
|
|
309
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
269
310
|
buffer += chunk;
|
|
270
311
|
|
|
271
312
|
// 处理数据块
|
|
272
313
|
const lines = buffer.split('\n');
|
|
273
314
|
buffer = lines.pop(); // 保留未处理的片段
|
|
274
315
|
|
|
275
|
-
lines.forEach(line => {
|
|
316
|
+
lines.forEach((line) => {
|
|
276
317
|
if (line.trim().length > 0 && line.trim().startsWith('data:')) {
|
|
277
318
|
const str = line.replace(/^data:\s*/, '');
|
|
278
319
|
const strObj = JSON.parse(str);
|
|
@@ -285,22 +326,28 @@ const chatConv = async (data) => {
|
|
|
285
326
|
if (!historyList.value[idx].conversation_id && strObj.conversation_id) {
|
|
286
327
|
historyList.value[idx].conversation_id = strObj.conversation_id;
|
|
287
328
|
}
|
|
288
|
-
if (
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
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
|
+
) {
|
|
297
344
|
const extraObj = JSON.parse(strObj.content);
|
|
298
|
-
if(extraObj.hasOwnProperty('showType')) {
|
|
299
|
-
historyList.value[idx].extra.push({...extraObj});
|
|
300
|
-
}else if(extraObj.hasOwnProperty('response_for_model')){
|
|
345
|
+
if (extraObj.hasOwnProperty('showType')) {
|
|
346
|
+
historyList.value[idx].extra.push({ ...extraObj });
|
|
347
|
+
} else if (extraObj.hasOwnProperty('response_for_model')) {
|
|
301
348
|
const modelObj = JSON.parse(extraObj.response_for_model);
|
|
302
|
-
if(modelObj.hasOwnProperty('showType')) {
|
|
303
|
-
historyList.value[idx].extra.push({...modelObj});
|
|
349
|
+
if (modelObj.hasOwnProperty('showType')) {
|
|
350
|
+
historyList.value[idx].extra.push({ ...modelObj });
|
|
304
351
|
}
|
|
305
352
|
}
|
|
306
353
|
}
|
|
@@ -312,16 +359,16 @@ const chatConv = async (data) => {
|
|
|
312
359
|
|
|
313
360
|
const queryHistoryList = async () => {
|
|
314
361
|
const res = await post(
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
362
|
+
`https://api.coze.cn/v1/conversation/message/list?conversation_id=${conversationId.value}`,
|
|
363
|
+
{ order: 'asc' },
|
|
364
|
+
{ ...chatOptions.value },
|
|
318
365
|
);
|
|
319
366
|
if (res.code === 0 && res.data && res.data.length) {
|
|
320
367
|
historyList.value = [];
|
|
321
368
|
const cardList = [];
|
|
322
|
-
res.data.forEach(row => {
|
|
323
|
-
if(row.hasOwnProperty('content_type')) {
|
|
324
|
-
if(row.content_type === 'text') {
|
|
369
|
+
res.data.forEach((row) => {
|
|
370
|
+
if (row.hasOwnProperty('content_type')) {
|
|
371
|
+
if (row.content_type === 'text') {
|
|
325
372
|
historyList.value.push({
|
|
326
373
|
chat_id: row.chat_id,
|
|
327
374
|
conversation_id: row.conversation_id,
|
|
@@ -331,81 +378,100 @@ const queryHistoryList = async () => {
|
|
|
331
378
|
status: 'ended',
|
|
332
379
|
extra: [],
|
|
333
380
|
});
|
|
334
|
-
}else if(row.content_type === 'card' && row.role === 'assistant') {
|
|
381
|
+
} else if (row.content_type === 'card' && row.role === 'assistant') {
|
|
335
382
|
try {
|
|
336
383
|
const cardObj = JSON.parse(row.content);
|
|
337
|
-
if(cardObj.hasOwnProperty('showType')) {
|
|
384
|
+
if (cardObj.hasOwnProperty('showType')) {
|
|
338
385
|
cardList.push({
|
|
339
386
|
chat_id: row.chat_id,
|
|
340
387
|
conversation_id: row.conversation_id,
|
|
341
388
|
bot_id: row.bot_id,
|
|
342
389
|
role: row.role,
|
|
343
|
-
extra:[{...cardObj}]
|
|
344
|
-
})
|
|
345
|
-
}else if(cardObj.hasOwnProperty('response_for_model')){
|
|
390
|
+
extra: [{ ...cardObj }],
|
|
391
|
+
});
|
|
392
|
+
} else if (cardObj.hasOwnProperty('response_for_model')) {
|
|
346
393
|
const modelObj = JSON.parse(cardObj.response_for_model);
|
|
347
|
-
if(modelObj.hasOwnProperty('showType')) {
|
|
394
|
+
if (modelObj.hasOwnProperty('showType')) {
|
|
348
395
|
cardList.push({
|
|
349
396
|
chat_id: row.chat_id,
|
|
350
397
|
conversation_id: row.conversation_id,
|
|
351
398
|
bot_id: row.bot_id,
|
|
352
399
|
role: row.role,
|
|
353
|
-
extra:[{...modelObj}]
|
|
354
|
-
})
|
|
400
|
+
extra: [{ ...modelObj }],
|
|
401
|
+
});
|
|
355
402
|
}
|
|
356
403
|
}
|
|
357
|
-
}catch (e) {
|
|
404
|
+
} catch (e) {
|
|
358
405
|
console.log('== 解析错误 ==');
|
|
359
406
|
}
|
|
360
407
|
}
|
|
361
408
|
}
|
|
362
409
|
});
|
|
363
|
-
cardList.forEach(card => {
|
|
364
|
-
const idx = historyList.value.findIndex(
|
|
365
|
-
|
|
410
|
+
cardList.forEach((card) => {
|
|
411
|
+
const idx = historyList.value.findIndex(
|
|
412
|
+
(c) => c.chat_id === card.chat_id && c.role === 'assistant',
|
|
413
|
+
);
|
|
414
|
+
if (idx > -1) {
|
|
366
415
|
historyList.value[idx].extra = [...card.extra];
|
|
367
416
|
}
|
|
368
|
-
})
|
|
417
|
+
});
|
|
369
418
|
console.log(historyList.value, cardList);
|
|
370
419
|
}
|
|
371
420
|
};
|
|
372
421
|
|
|
373
422
|
const queryBotInfo = async () => {
|
|
374
423
|
const res = await get(
|
|
375
|
-
|
|
376
|
-
|
|
424
|
+
`https://api.coze.cn/v1/bot/get_online_info?bot_id=${props.botId}`,
|
|
425
|
+
{ ...chatOptions.value },
|
|
377
426
|
);
|
|
378
427
|
console.log(res);
|
|
379
428
|
if (res.code === 0 && res.data) {
|
|
380
|
-
botInfo.value = {...res.data};
|
|
429
|
+
botInfo.value = { ...res.data };
|
|
381
430
|
}
|
|
382
431
|
};
|
|
383
432
|
|
|
384
433
|
const handleText = (str) => {
|
|
385
434
|
// console.log(str);
|
|
386
|
-
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' });
|
|
387
442
|
};
|
|
388
443
|
|
|
389
|
-
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) => {
|
|
390
456
|
switch (type) {
|
|
391
457
|
case 'change':
|
|
392
|
-
chatConv({code: '換一套菜品', text: '換一套菜品'});
|
|
458
|
+
chatConv({ code: '換一套菜品', text: '換一套菜品' });
|
|
393
459
|
break;
|
|
394
460
|
case 'match':
|
|
395
|
-
chatConv({code: '請用以上方案為我配餐', text: '請用以上方案為我配餐'});
|
|
461
|
+
chatConv({ code: '請用以上方案為我配餐', text: '請用以上方案為我配餐' });
|
|
396
462
|
break;
|
|
397
463
|
default:
|
|
398
|
-
Emits('call', { type });
|
|
464
|
+
Emits('call', { type, info });
|
|
399
465
|
}
|
|
400
466
|
};
|
|
401
467
|
|
|
402
468
|
const scrollToEnd = () => {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
469
|
+
nextTick(() => {
|
|
470
|
+
endTarget.value.scrollIntoView();
|
|
471
|
+
});
|
|
406
472
|
};
|
|
407
473
|
</script>
|
|
408
474
|
|
|
409
475
|
<style scoped lang="less">
|
|
410
|
-
@import
|
|
476
|
+
@import './ChatUi';
|
|
411
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">
|
|
@@ -2,21 +2,27 @@
|
|
|
2
2
|
<div class="dl_wrap">
|
|
3
3
|
<template v-for="cate in skuList" :key="cate.category">
|
|
4
4
|
<div v-if="cate.list && cate.list.length">
|
|
5
|
-
<p class="title">{{ cate.
|
|
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
|
-
|
|
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;
|
|
@@ -69,12 +74,11 @@ const handleBtn = (type) => {
|
|
|
69
74
|
|
|
70
75
|
&_group {
|
|
71
76
|
display: grid;
|
|
72
|
-
grid-template-columns: 1fr;
|
|
77
|
+
grid-template-columns: 1fr 1fr;
|
|
73
78
|
grid-column-gap: 10px;
|
|
74
79
|
margin-top: 20px;
|
|
75
80
|
}
|
|
76
81
|
}
|
|
77
82
|
}
|
|
78
83
|
}
|
|
79
|
-
|
|
80
84
|
</style>
|