ai-chat-bot-interface 0.0.1 → 1.0.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/package.json +3 -2
- package/src/App.vue +9 -21
- package/src/ChatUi.less +217 -0
- package/src/ChatUi.vue +357 -4
- package/src/assets/styles/public.less +149 -0
- package/src/components/DishesCard.vue +63 -0
- package/src/components/DishesList.vue +80 -0
- package/src/components/icons/ClearIcon.vue +11 -0
- package/src/components/icons/CloseIcon.vue +10 -0
- package/src/components/icons/NewSessionIcon.vue +13 -0
- package/src/components/icons/SendIcon.vue +11 -0
- package/src/mock.js +162 -0
- package/src/utils/request.js +54 -0
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-chat-bot-interface",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.0
|
|
4
|
+
"version": "1.0.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
7
|
-
"description": "A AI chat bot interface",
|
|
7
|
+
"description": "A AI chat bot interface. (private)",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"dev": "vite",
|
|
10
10
|
"build": "vite build",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@vitejs/plugin-vue": "^5.2.1",
|
|
23
|
+
"less": "^4.2.2",
|
|
23
24
|
"vite": "^6.1.0"
|
|
24
25
|
},
|
|
25
26
|
"author": "lin_26",
|
package/src/App.vue
CHANGED
|
@@ -1,30 +1,18 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import
|
|
2
|
+
import ChatUi from './ChatUi.vue'
|
|
3
3
|
</script>
|
|
4
4
|
|
|
5
5
|
<template>
|
|
6
|
-
<div>
|
|
7
|
-
<
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
<img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
|
|
12
|
-
</a>
|
|
6
|
+
<div style="width: 100vw; height: 100vh;">
|
|
7
|
+
<chat-ui bot-id="123132132"
|
|
8
|
+
token="pat_123"
|
|
9
|
+
uid="5555"
|
|
10
|
+
/>
|
|
13
11
|
</div>
|
|
14
|
-
<HelloWorld msg="Vite + Vue" />
|
|
15
12
|
</template>
|
|
16
13
|
|
|
17
|
-
<style
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
padding: 1.5em;
|
|
21
|
-
will-change: filter;
|
|
22
|
-
transition: filter 300ms;
|
|
23
|
-
}
|
|
24
|
-
.logo:hover {
|
|
25
|
-
filter: drop-shadow(0 0 2em #646cffaa);
|
|
26
|
-
}
|
|
27
|
-
.logo.vue:hover {
|
|
28
|
-
filter: drop-shadow(0 0 2em #42b883aa);
|
|
14
|
+
<style>
|
|
15
|
+
#app {
|
|
16
|
+
padding: 0;
|
|
29
17
|
}
|
|
30
18
|
</style>
|
package/src/ChatUi.less
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
|
|
2
|
+
@import './assets/styles/public';
|
|
3
|
+
|
|
4
|
+
.btn {
|
|
5
|
+
background-color: #fff;
|
|
6
|
+
padding: 8px;
|
|
7
|
+
outline: none;
|
|
8
|
+
|
|
9
|
+
&:hover {
|
|
10
|
+
border-color: transparent;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
.board {
|
|
14
|
+
&_name {
|
|
15
|
+
font-size: 20px;
|
|
16
|
+
font-weight: 500;
|
|
17
|
+
margin: 10px 0;
|
|
18
|
+
}
|
|
19
|
+
&_desc {
|
|
20
|
+
display: inline-block;
|
|
21
|
+
font-size: 14px;
|
|
22
|
+
text-align: left;
|
|
23
|
+
padding: 12px 16px;
|
|
24
|
+
background-color: #fff;
|
|
25
|
+
border-radius: 8px;
|
|
26
|
+
margin-bottom: 10px;
|
|
27
|
+
}
|
|
28
|
+
&_sug {
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
margin-bottom: 8px;
|
|
31
|
+
padding: 6px 16px;
|
|
32
|
+
border-radius: 8px;
|
|
33
|
+
border: 1px solid rgba(68, 83, 130, 0.25);
|
|
34
|
+
&:hover {
|
|
35
|
+
background-color: #EAEAEA;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
.cui {
|
|
40
|
+
&_box {
|
|
41
|
+
width: 100%;
|
|
42
|
+
height: 100%;
|
|
43
|
+
background-color: @bg-color;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
&_wrap {
|
|
47
|
+
display: flex;
|
|
48
|
+
flex-direction: column;
|
|
49
|
+
justify-content: flex-start;
|
|
50
|
+
align-items: center;
|
|
51
|
+
width: 100%;
|
|
52
|
+
max-width: @max-width;
|
|
53
|
+
height: 100%;
|
|
54
|
+
margin: 0 auto;
|
|
55
|
+
background-color: @bg-color;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
&_header {
|
|
59
|
+
display: flex;
|
|
60
|
+
flex-direction: row;
|
|
61
|
+
justify-content: space-between;
|
|
62
|
+
align-items: center;
|
|
63
|
+
box-sizing: border-box;
|
|
64
|
+
padding: 0 16px;
|
|
65
|
+
height: 56px;
|
|
66
|
+
width: 100%;
|
|
67
|
+
line-height: 56px;
|
|
68
|
+
background-color: @bg-color;
|
|
69
|
+
border-bottom: 1px solid #1d1c2314;
|
|
70
|
+
|
|
71
|
+
.title {
|
|
72
|
+
.flexrsc();
|
|
73
|
+
|
|
74
|
+
.logo {
|
|
75
|
+
width: 24px;
|
|
76
|
+
height: 24px;
|
|
77
|
+
margin-right: 16px;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.btn {
|
|
82
|
+
font-size: 16px;
|
|
83
|
+
background-color: @bg-color;
|
|
84
|
+
|
|
85
|
+
&_group {
|
|
86
|
+
.flexrec()
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
&_content {
|
|
92
|
+
flex: 1;
|
|
93
|
+
width: 100%;
|
|
94
|
+
padding: 0 20px;
|
|
95
|
+
box-sizing: border-box;
|
|
96
|
+
background-color: @bg-color;
|
|
97
|
+
overflow-y: auto;
|
|
98
|
+
|
|
99
|
+
.replay {
|
|
100
|
+
.flexrss();
|
|
101
|
+
margin: 20px 0;
|
|
102
|
+
|
|
103
|
+
.name {
|
|
104
|
+
width: 100%;
|
|
105
|
+
text-align: left;
|
|
106
|
+
font-size: 14px;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.time {
|
|
110
|
+
font-size: 12px;
|
|
111
|
+
color: #999;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.avatar {
|
|
115
|
+
width: 32px;
|
|
116
|
+
height: 32px;
|
|
117
|
+
margin: 0 12px 0 0;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.box {
|
|
121
|
+
color: #000;
|
|
122
|
+
font-size: 14px;
|
|
123
|
+
font-weight: 400;
|
|
124
|
+
text-align: left;
|
|
125
|
+
background-color: #fff;
|
|
126
|
+
padding: 12px 16px;
|
|
127
|
+
border-radius: @border-radius;
|
|
128
|
+
|
|
129
|
+
/deep/ .text {
|
|
130
|
+
white-space: pre-wrap;
|
|
131
|
+
|
|
132
|
+
a {
|
|
133
|
+
color: #039938;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.recm {
|
|
140
|
+
cursor: pointer;
|
|
141
|
+
padding: 6px 16px;
|
|
142
|
+
border: 1px solid @border-color;
|
|
143
|
+
border-radius: @border-radius;
|
|
144
|
+
margin: 8px 0;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
&_content {
|
|
148
|
+
.flexcss();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
&.role_user {
|
|
152
|
+
.flexres();
|
|
153
|
+
|
|
154
|
+
.avatar {
|
|
155
|
+
margin: 0 0 0 12px;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.name {
|
|
159
|
+
text-align: right;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.box {
|
|
163
|
+
background-color: #EAEAEA;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
&_operate {
|
|
170
|
+
.flexrsc();
|
|
171
|
+
width: 100%;
|
|
172
|
+
padding: 10px 16px;
|
|
173
|
+
box-sizing: border-box;
|
|
174
|
+
|
|
175
|
+
.btn {
|
|
176
|
+
font-size: 16px;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.input {
|
|
180
|
+
flex: 1;
|
|
181
|
+
height: 38px;
|
|
182
|
+
line-height: 38px;
|
|
183
|
+
border: none;
|
|
184
|
+
font-size: 16px;
|
|
185
|
+
background-color: @bg-color;
|
|
186
|
+
|
|
187
|
+
&:focus-visible {
|
|
188
|
+
outline: none;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
&_group {
|
|
192
|
+
.flexrec();
|
|
193
|
+
flex: 1;
|
|
194
|
+
margin-left: 10px;
|
|
195
|
+
box-sizing: border-box;
|
|
196
|
+
height: 48px;
|
|
197
|
+
padding: 4px 16px;
|
|
198
|
+
border-radius: 24px;
|
|
199
|
+
border: 1px solid @border-color;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.send {
|
|
204
|
+
.flexrcc();
|
|
205
|
+
width: 38px;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
&_footer {
|
|
210
|
+
width: 100%;
|
|
211
|
+
height: 32px;
|
|
212
|
+
line-height: 32px;
|
|
213
|
+
color: #1d1c2359;
|
|
214
|
+
background-color: #2e2e380a;
|
|
215
|
+
font-size: 12px;
|
|
216
|
+
}
|
|
217
|
+
}
|
package/src/ChatUi.vue
CHANGED
|
@@ -1,10 +1,363 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div class="cui_box">
|
|
3
|
+
<div class="cui_wrap">
|
|
4
|
+
<div class="cui_header">
|
|
5
|
+
<div class="title" @click.stop="queryHistoryList">
|
|
6
|
+
<img class="logo" :src="logo" alt="logo" style="width: 24px; height: 24px;"/>
|
|
7
|
+
{{ name }} {{ botId }}
|
|
8
|
+
</div>
|
|
9
|
+
<div class="btn_group">
|
|
10
|
+
<button class="btn">
|
|
11
|
+
<clear-icon/>
|
|
12
|
+
</button>
|
|
13
|
+
<button class="btn">
|
|
14
|
+
<close-icon/>
|
|
15
|
+
</button>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
<div class="cui_content">
|
|
19
|
+
<div v-if="botInfo && botInfo.onboarding_info" style="text-align: left; margin-top: 50px;">
|
|
20
|
+
<div style="text-align: center">
|
|
21
|
+
<img :src="botInfo.icon_url" alt="icon" width="64" height="64"
|
|
22
|
+
style="border-radius: 15px;"
|
|
23
|
+
/>
|
|
24
|
+
<p class="board_name">{{ botInfo.name }}</p>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<div class="board_desc">{{ botInfo.onboarding_info.prologue }}</div>
|
|
28
|
+
<div class="flexcss">
|
|
29
|
+
<span v-for="(item, idx) in botInfo.onboarding_info.suggested_questions"
|
|
30
|
+
:key="idx"
|
|
31
|
+
class="board_sug"
|
|
32
|
+
@click.stop="chatConv({code: item, text: item});"
|
|
33
|
+
>{{ item }}</span>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
<template v-for="(conv, index) in historyList" :key="index">
|
|
37
|
+
<div v-if="conv.role === 'assistant'" class="replay role_sys">
|
|
38
|
+
<img class="avatar" :src="logo" alt="avatar">
|
|
39
|
+
<div class="replay_content">
|
|
40
|
+
<div class="name">{{ name }} <!--<span class="time">12:30</span>--></div>
|
|
41
|
+
<div class="box">
|
|
42
|
+
<p class="text" v-html="conv.content"></p>
|
|
43
|
+
<div v-if="conv.extra.length && !isAnswering">
|
|
44
|
+
<template v-for="(comp, idx) in conv.extra" :key="idx">
|
|
45
|
+
<dishes-list :sku-list="comp.content.skuList" @select="handleCardTap"/>
|
|
46
|
+
</template>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
<div v-else class="replay role_user">
|
|
52
|
+
<div class="replay_content">
|
|
53
|
+
|
|
54
|
+
<div class="name">User_{{ uid }} <!--<span class="time">12:30</span>--></div>
|
|
55
|
+
<div class="box">
|
|
56
|
+
<p class="text" v-html="conv.content"></p>
|
|
57
|
+
<div v-if="conv.extra.length && !isAnswering">
|
|
58
|
+
<template v-for="(comp, idx) in conv.extra" :key="idx">
|
|
59
|
+
<dishes-list :sku-list="comp.content.skuList"
|
|
60
|
+
@select="handleCardTap"
|
|
61
|
+
/>
|
|
62
|
+
</template>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
<img class="avatar" :src="avatar" alt="avatar">
|
|
67
|
+
</div>
|
|
68
|
+
</template>
|
|
69
|
+
<div ref="endTarget" style="height: 100px;" />
|
|
70
|
+
</div>
|
|
71
|
+
<div class="cui_operate">
|
|
72
|
+
<button class="btn" @click.stop="createConv">
|
|
73
|
+
<new-session-icon/>
|
|
74
|
+
</button>
|
|
75
|
+
<div class="input_group">
|
|
76
|
+
<input v-model="inputText" class="input" @keyup.enter="chatConv"/>
|
|
77
|
+
<div class="send" @click.stop="chatConv">
|
|
78
|
+
<send-icon :style="{ color: !isAnswering && inputText ? '#333': '#ccc'}"/>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
<div class="cui_footer">Powered by Coze</div>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
3
86
|
</template>
|
|
4
87
|
|
|
5
88
|
<script setup>
|
|
6
|
-
import {ref} from 'vue';
|
|
7
|
-
|
|
89
|
+
import {computed, nextTick, onMounted, ref} from 'vue';
|
|
90
|
+
import ClearIcon from './components/icons/ClearIcon.vue';
|
|
91
|
+
import CloseIcon from './components/icons/CloseIcon.vue';
|
|
92
|
+
import NewSessionIcon from './components/icons/NewSessionIcon.vue';
|
|
93
|
+
import SendIcon from './components/icons/SendIcon.vue';
|
|
94
|
+
import mock from './mock';
|
|
95
|
+
import {get, post} from './utils/request';
|
|
96
|
+
import DishesCard from './components/DishesCard.vue';
|
|
97
|
+
import DishesList from './components/DishesList.vue';
|
|
98
|
+
|
|
99
|
+
const chatOptions = computed(() => {
|
|
100
|
+
return {
|
|
101
|
+
headers: {
|
|
102
|
+
Authorization: `Bearer ${props.token}`,
|
|
103
|
+
'Content-Type': 'application/json',
|
|
104
|
+
},
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
const conversationId = ref('');
|
|
108
|
+
const isAnswering = ref(false);
|
|
109
|
+
const historyList = ref([]);
|
|
110
|
+
|
|
111
|
+
const props = defineProps({
|
|
112
|
+
logo: {
|
|
113
|
+
type: String,
|
|
114
|
+
default: 'https://prodstatic.weis1606.cn/api/smartFood/icon/weis_logo.png',
|
|
115
|
+
},
|
|
116
|
+
avatar: {
|
|
117
|
+
type: String,
|
|
118
|
+
default: 'https://prodstatic.weis1606.cn/api/smartFood/Nutribite/icons/home/icon_3.png',
|
|
119
|
+
},
|
|
120
|
+
name: {
|
|
121
|
+
type: String,
|
|
122
|
+
default: 'Weis Bot',
|
|
123
|
+
},
|
|
124
|
+
botId: {
|
|
125
|
+
type: String,
|
|
126
|
+
required: true,
|
|
127
|
+
},
|
|
128
|
+
uid: {
|
|
129
|
+
type: String,
|
|
130
|
+
required: true,
|
|
131
|
+
default: 'user',
|
|
132
|
+
},
|
|
133
|
+
token: {
|
|
134
|
+
type: String,
|
|
135
|
+
required: true
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
});
|
|
139
|
+
const endTarget = ref(null);
|
|
140
|
+
const inputText = ref('');
|
|
141
|
+
const botInfo = ref({});
|
|
142
|
+
|
|
143
|
+
onMounted(async () => {
|
|
144
|
+
if (localStorage.getItem('conversationId')) {
|
|
145
|
+
conversationId.value = localStorage.getItem('conversationId');
|
|
146
|
+
await queryBotInfo();
|
|
147
|
+
await queryHistoryList();
|
|
148
|
+
} else {
|
|
149
|
+
await createConv();
|
|
150
|
+
}
|
|
151
|
+
scrollToEnd();
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const createConv = async () => {
|
|
155
|
+
const res = await post(
|
|
156
|
+
'https://api.coze.cn/v1/conversation/create',
|
|
157
|
+
{'bot_id': props.botId, 'connector_id': '999'},
|
|
158
|
+
{...chatOptions.value},
|
|
159
|
+
);
|
|
160
|
+
console.log(res);
|
|
161
|
+
if (res.code === 0 && res.data) {
|
|
162
|
+
conversationId.value = res.data.id || '';
|
|
163
|
+
localStorage.setItem('conversationId', conversationId.value);
|
|
164
|
+
historyList.value = [];
|
|
165
|
+
await queryBotInfo();
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
// const messageCreate = async () => {
|
|
169
|
+
// console.log(chatOptions.value);
|
|
170
|
+
// const res = await post(
|
|
171
|
+
// `https://api.coze.cn/v1/conversation/message/create?conversation_id=${conversationId.value}`,
|
|
172
|
+
// { "role":"user","content":`#userid_${props.uid}`,"content_type":"text"},
|
|
173
|
+
// {...chatOptions.value});
|
|
174
|
+
// };
|
|
175
|
+
const messageCreate = async () => {
|
|
176
|
+
const res = await fetch(
|
|
177
|
+
`https://api.coze.cn/v3/chat?conversation_id=${conversationId.value}`,
|
|
178
|
+
{
|
|
179
|
+
method: 'POST',
|
|
180
|
+
headers: {
|
|
181
|
+
...chatOptions.value.headers,
|
|
182
|
+
// 'Content-Type': 'text/event-stream',
|
|
183
|
+
},
|
|
184
|
+
body: JSON.stringify({
|
|
185
|
+
bot_id: props.botId,
|
|
186
|
+
user_id: props.uid,
|
|
187
|
+
stream: true,
|
|
188
|
+
connector_id: '999',
|
|
189
|
+
additional_messages: [
|
|
190
|
+
{
|
|
191
|
+
'role': 'user',
|
|
192
|
+
'content_type': 'text',
|
|
193
|
+
'content': `#userid_${props.uid}`,
|
|
194
|
+
}],
|
|
195
|
+
}),
|
|
196
|
+
credentials: 'same-origin', // 默认同源策略
|
|
197
|
+
mode: 'cors', // 默认跨域模式
|
|
198
|
+
cache: 'default', // 默认缓存策略
|
|
199
|
+
},
|
|
200
|
+
);
|
|
201
|
+
};
|
|
202
|
+
const chatConv = async (data) => {
|
|
203
|
+
console.log('== user send ==');
|
|
204
|
+
let txt = '';
|
|
205
|
+
if (!(!isAnswering.value && (inputText || data.code))) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
historyList.value.push({
|
|
209
|
+
conversation_id: conversationId.value,
|
|
210
|
+
bot_id: props.botId,
|
|
211
|
+
role: 'user',
|
|
212
|
+
content: data.text ? data.text : inputText.value,
|
|
213
|
+
status: 'ended',
|
|
214
|
+
extra: [],
|
|
215
|
+
});
|
|
216
|
+
const res = await fetch(
|
|
217
|
+
`https://api.coze.cn/v3/chat?conversation_id=${conversationId.value}`,
|
|
218
|
+
{
|
|
219
|
+
method: 'POST',
|
|
220
|
+
headers: {
|
|
221
|
+
...chatOptions.value.headers,
|
|
222
|
+
// 'Content-Type': 'text/event-stream',
|
|
223
|
+
},
|
|
224
|
+
body: JSON.stringify({
|
|
225
|
+
bot_id: props.botId,
|
|
226
|
+
user_id: props.uid,
|
|
227
|
+
stream: true,
|
|
228
|
+
connector_id: '999',
|
|
229
|
+
additional_messages: [
|
|
230
|
+
{
|
|
231
|
+
'role': 'user',
|
|
232
|
+
'content_type': 'text',
|
|
233
|
+
'content': data.code ? data.code : inputText.value, // '配餐1600kcal,身高173,体重60kg,生成一天的套餐',
|
|
234
|
+
}],
|
|
235
|
+
}),
|
|
236
|
+
credentials: 'same-origin', // 默认同源策略
|
|
237
|
+
mode: 'cors', // 默认跨域模式
|
|
238
|
+
cache: 'default', // 默认缓存策略
|
|
239
|
+
},
|
|
240
|
+
);
|
|
241
|
+
historyList.value.push({
|
|
242
|
+
conversation_id: '',
|
|
243
|
+
bot_id: '',
|
|
244
|
+
role: 'assistant',
|
|
245
|
+
content: '',
|
|
246
|
+
status: 'answering',
|
|
247
|
+
extra: [],
|
|
248
|
+
});
|
|
249
|
+
scrollToEnd();
|
|
250
|
+
inputText.value = '';
|
|
251
|
+
const idx = historyList.value.length - 1;
|
|
252
|
+
const reader = res.body.getReader();
|
|
253
|
+
const decoder = new TextDecoder('utf-8');
|
|
254
|
+
let buffer = '';
|
|
255
|
+
// 逐块读取数据
|
|
256
|
+
while (true) {
|
|
257
|
+
const {done, value} = await reader.read();
|
|
258
|
+
if (done) {
|
|
259
|
+
console.log('Stream has ended.');
|
|
260
|
+
isAnswering.value = false;
|
|
261
|
+
historyList.value[idx].status = 'ended';
|
|
262
|
+
scrollToEnd();
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// 解码数据块
|
|
267
|
+
const chunk = decoder.decode(value, {stream: true});
|
|
268
|
+
buffer += chunk;
|
|
269
|
+
|
|
270
|
+
// 处理数据块
|
|
271
|
+
const lines = buffer.split('\n');
|
|
272
|
+
buffer = lines.pop(); // 保留未处理的片段
|
|
273
|
+
|
|
274
|
+
lines.forEach(line => {
|
|
275
|
+
if (line.trim().length > 0 && line.trim().startsWith('data:')) {
|
|
276
|
+
const str = line.replace(/^data:\s*/, '');
|
|
277
|
+
const strObj = JSON.parse(str);
|
|
278
|
+
console.log('Received:', strObj);
|
|
279
|
+
isAnswering.value = true;
|
|
280
|
+
historyList.value[idx].status = 'answering';
|
|
281
|
+
if (!historyList.value[idx].bot_id && strObj.bot_id) {
|
|
282
|
+
historyList.value[idx].bot_id = strObj.bot_id;
|
|
283
|
+
}
|
|
284
|
+
if (!historyList.value[idx].conversation_id && strObj.conversation_id) {
|
|
285
|
+
historyList.value[idx].conversation_id = strObj.conversation_id;
|
|
286
|
+
}
|
|
287
|
+
if (strObj.hasOwnProperty('content')
|
|
288
|
+
&& strObj.hasOwnProperty('content_type')
|
|
289
|
+
&& strObj.content_type === 'text'
|
|
290
|
+
&& strObj.hasOwnProperty('type')
|
|
291
|
+
&& strObj.type === 'answer') {
|
|
292
|
+
txt += JSON.parse(str).content;
|
|
293
|
+
historyList.value[idx].content = handleText(historyList.value[idx].content + JSON.parse(str).content);
|
|
294
|
+
} else if (strObj.hasOwnProperty('type')
|
|
295
|
+
&& strObj.type === 'tool_response') {
|
|
296
|
+
const extraObj = JSON.parse(strObj.content);
|
|
297
|
+
historyList.value[idx].extra.push({
|
|
298
|
+
type: strObj.type,
|
|
299
|
+
content: extraObj,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
console.log('====', historyList.value);
|
|
306
|
+
console.log('== txt ==', txt);
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const queryHistoryList = async () => {
|
|
310
|
+
const res = await post(
|
|
311
|
+
`https://api.coze.cn/v1/conversation/message/list?conversation_id=${conversationId.value}`,
|
|
312
|
+
{order: 'asc'},
|
|
313
|
+
{...chatOptions.value},
|
|
314
|
+
);
|
|
315
|
+
if (res.code === 0 && res.data && res.data.length) {
|
|
316
|
+
historyList.value = [];
|
|
317
|
+
res.data.forEach(row => {
|
|
318
|
+
historyList.value.push({
|
|
319
|
+
conversation_id: row.conversation_id,
|
|
320
|
+
bot_id: row.bot_id,
|
|
321
|
+
role: row.role,
|
|
322
|
+
content: row.content,
|
|
323
|
+
status: 'ended',
|
|
324
|
+
extra: [],
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
const queryBotInfo = async () => {
|
|
331
|
+
const res = await get(
|
|
332
|
+
`https://api.coze.cn/v1/bot/get_online_info?bot_id=${props.botId}`,
|
|
333
|
+
{...chatOptions.value},
|
|
334
|
+
);
|
|
335
|
+
console.log(res);
|
|
336
|
+
if (res.code === 0 && res.data) {
|
|
337
|
+
botInfo.value = {...res.data};
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
const handleText = (str) => {
|
|
342
|
+
// console.log(str);
|
|
343
|
+
return str.replaceAll(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/ig, '<a href="$2" target="_blank">[$1]</a>');
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
const handleCardTap = ({type}) => {
|
|
347
|
+
switch (type) {
|
|
348
|
+
case 'change':
|
|
349
|
+
chatConv({code: '換一套菜品', text: '換一套菜品'});
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
const scrollToEnd = () => {
|
|
355
|
+
nextTick(() => {
|
|
356
|
+
endTarget.value.scrollIntoView();
|
|
357
|
+
})
|
|
358
|
+
}
|
|
8
359
|
</script>
|
|
9
360
|
|
|
10
|
-
<style scoped
|
|
361
|
+
<style scoped lang="less">
|
|
362
|
+
@import "./ChatUi";
|
|
363
|
+
</style>
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
@border-color: rgba(68, 83, 130, 0.25);
|
|
2
|
+
@border-radius: 8px;
|
|
3
|
+
@box-max: 290px;
|
|
4
|
+
@max-width: 630px;
|
|
5
|
+
@bg-color: #F3F4F5;
|
|
6
|
+
|
|
7
|
+
.flexr {
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-direction: row;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.flexc {
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
}
|
|
16
|
+
.flexrss {
|
|
17
|
+
.flexr();
|
|
18
|
+
justify-content: flex-start;
|
|
19
|
+
align-items: flex-start;
|
|
20
|
+
}
|
|
21
|
+
.flexrsc {
|
|
22
|
+
.flexr();
|
|
23
|
+
justify-content: flex-start;
|
|
24
|
+
align-items: center;
|
|
25
|
+
}
|
|
26
|
+
.flexrse {
|
|
27
|
+
.flexr();
|
|
28
|
+
justify-content: flex-start;
|
|
29
|
+
align-items: flex-end;
|
|
30
|
+
}
|
|
31
|
+
.flexrsst {
|
|
32
|
+
.flexr();
|
|
33
|
+
justify-content: flex-start;
|
|
34
|
+
align-items: stretch;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.flexrbs {
|
|
38
|
+
.flexr();
|
|
39
|
+
justify-content: space-between;
|
|
40
|
+
align-items: flex-start;
|
|
41
|
+
}
|
|
42
|
+
.flexrbc {
|
|
43
|
+
.flexr();
|
|
44
|
+
justify-content: space-between;
|
|
45
|
+
align-items: center;
|
|
46
|
+
}
|
|
47
|
+
.flexrac {
|
|
48
|
+
.flexr();
|
|
49
|
+
justify-content: space-around;
|
|
50
|
+
align-items: center;
|
|
51
|
+
}
|
|
52
|
+
.flexrbe {
|
|
53
|
+
.flexr();
|
|
54
|
+
justify-content: space-between;
|
|
55
|
+
align-items: flex-end;
|
|
56
|
+
}
|
|
57
|
+
.flexrcs {
|
|
58
|
+
.flexr();
|
|
59
|
+
justify-content: center;
|
|
60
|
+
align-items: flex-start;
|
|
61
|
+
}
|
|
62
|
+
.flexrcc {
|
|
63
|
+
.flexr();
|
|
64
|
+
justify-content: center;
|
|
65
|
+
align-items: center;
|
|
66
|
+
}
|
|
67
|
+
.flexrce {
|
|
68
|
+
.flexr();
|
|
69
|
+
justify-content: center;
|
|
70
|
+
align-items: flex-end;
|
|
71
|
+
}
|
|
72
|
+
.flexres {
|
|
73
|
+
.flexr();
|
|
74
|
+
justify-content: flex-end;
|
|
75
|
+
align-items: flex-start;
|
|
76
|
+
}
|
|
77
|
+
.flexrec {
|
|
78
|
+
.flexr();
|
|
79
|
+
justify-content: flex-end;
|
|
80
|
+
align-items: center;
|
|
81
|
+
}
|
|
82
|
+
.flexree {
|
|
83
|
+
.flexr();
|
|
84
|
+
justify-content: flex-end;
|
|
85
|
+
align-items: flex-end;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.flexcss {
|
|
89
|
+
.flexc();
|
|
90
|
+
justify-content: flex-start;
|
|
91
|
+
align-items: flex-start;
|
|
92
|
+
}
|
|
93
|
+
.flexcsc {
|
|
94
|
+
.flexc();
|
|
95
|
+
justify-content: flex-start;
|
|
96
|
+
align-items: center;
|
|
97
|
+
}
|
|
98
|
+
.flexcse {
|
|
99
|
+
.flexc();
|
|
100
|
+
justify-content: flex-start;
|
|
101
|
+
align-items: flex-end;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.flexccs {
|
|
105
|
+
.flexc();
|
|
106
|
+
justify-content: center;
|
|
107
|
+
align-items: flex-start;
|
|
108
|
+
}
|
|
109
|
+
.flexccc {
|
|
110
|
+
.flexc();
|
|
111
|
+
justify-content: center;
|
|
112
|
+
align-items: center;
|
|
113
|
+
}
|
|
114
|
+
.flexcce {
|
|
115
|
+
.flexc();
|
|
116
|
+
justify-content: center;
|
|
117
|
+
align-items: flex-end;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.flexcbs {
|
|
121
|
+
.flexc();
|
|
122
|
+
justify-content: space-between;
|
|
123
|
+
align-items: flex-start;
|
|
124
|
+
}
|
|
125
|
+
.flexcbc {
|
|
126
|
+
.flexc();
|
|
127
|
+
justify-content: space-between;
|
|
128
|
+
align-items: center;
|
|
129
|
+
}
|
|
130
|
+
.flexcbe {
|
|
131
|
+
.flexc();
|
|
132
|
+
justify-content: space-between;
|
|
133
|
+
align-items: flex-end;
|
|
134
|
+
}
|
|
135
|
+
.flexces {
|
|
136
|
+
.flexc();
|
|
137
|
+
justify-content: flex-end;
|
|
138
|
+
align-items: flex-start;
|
|
139
|
+
}
|
|
140
|
+
.flexcec {
|
|
141
|
+
.flexc();
|
|
142
|
+
justify-content: flex-end;
|
|
143
|
+
align-items: center;
|
|
144
|
+
}
|
|
145
|
+
.flexcee {
|
|
146
|
+
.flexc();
|
|
147
|
+
justify-content: flex-end;
|
|
148
|
+
align-items: flex-end;
|
|
149
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="dishes_wrap" @click.stop="testFunc">
|
|
3
|
+
<img class="img" :src="info.primaryImgUrl" alt="img" />
|
|
4
|
+
<div>
|
|
5
|
+
<div class="name">
|
|
6
|
+
<span>{{info.skuname}}</span>
|
|
7
|
+
<span>×1</span>
|
|
8
|
+
</div>
|
|
9
|
+
<div class="sub">{{subStr}}</div>
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script setup>
|
|
15
|
+
import {computed} from 'vue';
|
|
16
|
+
|
|
17
|
+
const props = defineProps({
|
|
18
|
+
info: {
|
|
19
|
+
type: Object,
|
|
20
|
+
required: true
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const subStr = computed(() => {
|
|
25
|
+
return `热量${props.info.energy}kcal,蛋白质${props.info.protein}g,脂肪${props.info.fat}g,碳水${props.info.carbonwater}g`
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<style scoped lang="less">
|
|
31
|
+
@import 'src/assets/styles/public';
|
|
32
|
+
|
|
33
|
+
.dishes {
|
|
34
|
+
&_wrap {
|
|
35
|
+
display: grid;
|
|
36
|
+
grid-template-columns: 72px 1fr;
|
|
37
|
+
grid-column-gap: 10px;
|
|
38
|
+
background-color: @bg-color;
|
|
39
|
+
border-radius: 10px;
|
|
40
|
+
padding: 10px;
|
|
41
|
+
margin: 10px 0;
|
|
42
|
+
|
|
43
|
+
.img {
|
|
44
|
+
width: 100%;
|
|
45
|
+
border-radius: 8px;
|
|
46
|
+
}
|
|
47
|
+
.name {
|
|
48
|
+
.flexrbs();
|
|
49
|
+
font-weight: 600;
|
|
50
|
+
font-size: 14px;
|
|
51
|
+
color: #000;
|
|
52
|
+
line-height: 16px;
|
|
53
|
+
}
|
|
54
|
+
.sub {
|
|
55
|
+
margin-top: 10px;
|
|
56
|
+
font-weight: 400;
|
|
57
|
+
font-size: 10px;
|
|
58
|
+
color: #666;
|
|
59
|
+
line-height: 12px;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
</style>
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="dl_wrap">
|
|
3
|
+
<template v-for="cate in skuList" :key="cate.category">
|
|
4
|
+
<div v-if="cate.list && cate.list.length">
|
|
5
|
+
<p class="title">{{ cate.category }}</p>
|
|
6
|
+
<div>
|
|
7
|
+
<template v-for="dList in cate.list" :key="dList.id">
|
|
8
|
+
<dishes-card v-for="(info, idx) in dList.categoryList" :key="idx" :info="info"/>
|
|
9
|
+
</template>
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
13
|
+
<div class="btn_group">
|
|
14
|
+
<div class="btn btn_1" @click.stop="handleBtn('change')">換一套菜品</div>
|
|
15
|
+
<div class="btn btn_2" @click.stop="handleBtn('order')">去下單</div>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script setup>
|
|
23
|
+
import DishesCard from './DishesCard.vue';
|
|
24
|
+
|
|
25
|
+
const props = defineProps({
|
|
26
|
+
skuList: {
|
|
27
|
+
type: Array,
|
|
28
|
+
required: true,
|
|
29
|
+
default: () => ([]),
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
const Emits = defineEmits(['select']);
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
const handleBtn = (type) => {
|
|
36
|
+
Emits('select', {type});
|
|
37
|
+
};
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<style scoped lang="less">
|
|
41
|
+
@import 'src/assets/styles/public';
|
|
42
|
+
|
|
43
|
+
.dl {
|
|
44
|
+
&_wrap {
|
|
45
|
+
max-width: 320px;
|
|
46
|
+
|
|
47
|
+
.title {
|
|
48
|
+
font-size: 16px;
|
|
49
|
+
font-weight: bold;
|
|
50
|
+
margin: 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.btn {
|
|
54
|
+
cursor: pointer;
|
|
55
|
+
width: 100%;
|
|
56
|
+
height: 38px;
|
|
57
|
+
line-height: 38px;
|
|
58
|
+
background-color: #E6F5EB;
|
|
59
|
+
border-radius: 19px;
|
|
60
|
+
text-align: center;
|
|
61
|
+
font-weight: 600;
|
|
62
|
+
font-size: 13px;
|
|
63
|
+
color: #039938;
|
|
64
|
+
|
|
65
|
+
&_2 {
|
|
66
|
+
color: #fff;
|
|
67
|
+
background-color: #039938;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
&_group {
|
|
71
|
+
display: grid;
|
|
72
|
+
grid-template-columns: 1fr 1fr;
|
|
73
|
+
grid-column-gap: 10px;
|
|
74
|
+
margin-top: 20px;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
</style>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<svg class="icon-icon icon-icon-coz_broom " width="1em" height="1em" viewBox="0 0 24 24" fill="currentColor"
|
|
3
|
+
xmlns="http://www.w3.org/2000/svg">
|
|
4
|
+
<path
|
|
5
|
+
d="M9 2V6L4 6C2.89543 6 2 6.89543 2 8V21C2 22.1046 2.89543 23 4 23H20C21.1046 23 22 22.1046 22 21V8C22 6.89543 21.1046 6 20 6L15 6V2C15 0.895431 14.1046 0 13 0H11C9.89543 0 9 0.895431 9 2ZM13 2V8H20V12H4V8H11V2H13ZM4 14H20V21H9V18C9 17.4477 8.55228 17 8 17C7.44772 17 7 17.4477 7 18V21H4L4 14Z"></path>
|
|
6
|
+
</svg>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script></script>
|
|
10
|
+
|
|
11
|
+
<style scoped></style>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<svg width="1em" height="1em" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<path
|
|
4
|
+
d="M4.96977 17.7929C4.57925 18.1834 4.57925 18.8166 4.96977 19.2071C5.3603 19.5976 5.99346 19.5976 6.38399 19.2071L12.1769 13.4142L17.9698 19.2071C18.3603 19.5976 18.9935 19.5976 19.384 19.2071C19.7745 18.8166 19.7745 18.1834 19.384 17.7929L13.5911 12L19.384 6.20711C19.7745 5.81658 19.7745 5.18342 19.384 4.79289C18.9935 4.40237 18.3603 4.40237 17.9698 4.79289L12.1769 10.5858L6.38399 4.79289C5.99347 4.40237 5.3603 4.40237 4.96978 4.79289C4.57925 5.18342 4.57925 5.81658 4.96978 6.20711L10.7627 12L4.96977 17.7929Z"></path>
|
|
5
|
+
</svg>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script></script>
|
|
9
|
+
|
|
10
|
+
<style scoped></style>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<svg width="1em" height="1em" viewBox="0 0 24 24"
|
|
3
|
+
fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
|
4
|
+
<path
|
|
5
|
+
d="M6.94025 19.5916L6.23607 21H12C12.4316 21 12.856 20.9696 13.2714 20.9109C13.5046 20.9691 13.7487 21 14 21H15V22C15 22.1951 15.0186 22.3858 15.0542 22.5705C14.0845 22.8501 13.0597 23 12 23H4.61803C3.87465 23 3.39116 22.2177 3.72361 21.5528L4.48405 20.0319C2.3399 18.0247 1 15.1688 1 12C1 5.92487 5.92487 1 12 1C18.0751 1 23 5.92487 23 12C23 13.0597 22.8501 14.0845 22.5705 15.0542C22.3858 15.0186 22.1951 15 22 15H21V14C21 13.7487 20.9691 13.5046 20.9109 13.2714C20.9696 12.856 21 12.4316 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 14.5928 4.09379 16.9269 5.85088 18.5718L6.94025 19.5916Z"></path>
|
|
6
|
+
<path
|
|
7
|
+
d="M8 8C7.44772 8 7 8.44772 7 9 7 9.55229 7.44772 10 8 10H15C15.5523 10 16 9.55229 16 9 16 8.44772 15.5523 8 15 8H8zM7 14C7 13.4477 7.44772 13 8 13H12C12.5523 13 13 13.4477 13 14 13 14.5523 12.5523 15 12 15H8C7.44772 15 7 14.5523 7 14zM18 13C18.5523 13 19 13.4477 19 14V17H22C22.5523 17 23 17.4477 23 18 23 18.5523 22.5523 19 22 19H19V22C19 22.5523 18.5523 23 18 23 17.4477 23 17 22.5523 17 22V19H14C13.4477 19 13 18.5523 13 18 13 17.4477 13.4477 17 14 17H17V14C17 13.4477 17.4477 13 18 13z"></path>
|
|
8
|
+
</svg>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script></script>
|
|
12
|
+
|
|
13
|
+
<style scoped></style>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<svg width="1em" height="1em" viewBox="0 0 24 24"
|
|
3
|
+
fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
|
4
|
+
<path
|
|
5
|
+
d="M21.4159 13.3153C21.8961 13.0536 22.1965 12.5506 22.1965 11.9998C22.1965 11.449 21.8961 10.9483 21.4159 10.6865L3.99982 1.25701C3.53551 1.00437 2.98699 1.01575 2.53179 1.2866C2.07659 1.55744 1.80347 2.03768 1.80347 2.57027L3.725 10.0755L11.8947 11.2715C12.363 11.2715 12.7414 11.5969 12.7414 11.9998C12.7414 12.4026 12.363 12.7281 11.8947 12.7281C7.2906 13.4031 4.56846 13.799 3.72825 13.9159L1.80347 21.4293C1.80347 21.9619 2.07659 22.4421 2.53179 22.7153C2.98699 22.9861 3.53551 22.9975 3.99982 22.7448L21.4159 13.3153Z"></path>
|
|
6
|
+
</svg>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script></script>
|
|
10
|
+
|
|
11
|
+
<style scoped></style>
|
package/src/mock.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
export default [
|
|
2
|
+
{
|
|
3
|
+
"conversation_id": "7472317677525188619",
|
|
4
|
+
"bot_id": "7468975471649194018",
|
|
5
|
+
"role": "assistant",
|
|
6
|
+
"content": "您好,我是Nutribite",
|
|
7
|
+
"status": "ended",
|
|
8
|
+
"extra": []
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
conversation_id: '7472317677525188619',
|
|
12
|
+
bot_id: '7468975471649194018',
|
|
13
|
+
role: `user_555`,
|
|
14
|
+
content: '配餐1600kcal,身高173,体重60kg,生成一天的套餐',
|
|
15
|
+
status: 'ended',
|
|
16
|
+
extra: [],
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"conversation_id": "7472317677525188619",
|
|
20
|
+
"bot_id": "7468975471649194018",
|
|
21
|
+
"role": "assistant",
|
|
22
|
+
"content": "为你生成的一天1600kcal套餐如下:\n- **早餐(约450kcal)**:\n - 火腿汁烧豆腐,能量82kcal,蛋白质8.7g,脂肪3.1g,碳水化合物4.7g,<a href=\"https://prodstatic.weis1606.cn/api/diet/2024122015441833302.jpg?x-oss-process=image/resize,h_750\" target=\"_blank\">[点击查看]</a>\n - 五彩鸡块,能量156kcal,蛋白质16.5g,脂肪6.8g,碳水化合物7.2g,<a href=\"https://prodstatic.weis1606.cn/api/diet/2024120910310309135.jpg?x-oss-process=image/resize,h_750\" target=\"_blank\">[点击查看]</a>\n - 素炒云南小瓜,能量39kcal,蛋白质1.7g,脂肪1.3g,碳水化合物5g,<a href=\"https://prodstatic.weis1606.cn/api/diet/2025010318161342225.jpg?x-oss-process=image/resize,h_750\" target=\"_blank\">[点击查看]</a>\n - 牛肉焖饭,能量178kcal,蛋白质7.6g,脂肪2.4g,碳水化合物31.6g,<a href=\"https://prodstatic.weis1606.cn/api/diet/2025010318062384767.jpg?x-oss-process=image/resize,h_750\" target=\"_blank\">[点击查看]</a>\n- **午餐(约540kcal)**:\n - 粟米蒸水蛋,能量117kcal,蛋白质9.9g,脂肪6.6g,碳水化合物4.5g,<a href=\"https://prodstatic.weis1606.cn/api/diet/2025010318155462227.jpg?x-oss-process=image/resize,h_750\" target=\"_blank\">[点击查看]</a>\n - 豉油鸡扒,能量174kcal,蛋白质26.2g,脂肪6.8g,碳水化合物1.9g,<a href=\"https://prodstatic.weis1606.cn/api/diet/2025010318254931832.png?x-oss-process=image/resize,h_750\" target=\"_blank\">[点击查看]</a>\n - 清炒菠菜,能量49kcal,蛋白质4g,脂肪1.5g,碳水化合物4.9g,<a href=\"https://prodstatic.weis1606.cn/api/diet/2025010317554302323.jpg?x-oss-process=image/resize,h_750\" target=\"_blank\">[点击查看]</a>\n - 红米飯,能量182kcal,蛋白质3.4g,脂肪0.1g,碳水化合物41.9g,<a href=\"https://prodstatic.weis1606.cn/api/diet/2025010318002606875.jpg?x-oss-process=image/resize,h_750\" target=\"_blank\">[点击查看]</a>\n- **晚餐(约450kcal)**:\n - 番茄大虾面,能量430kcal,蛋白质26.3g,脂肪5.6g,碳水化合物68.5g,<a href=\"https://prodstatic.weis1606.cn/api/diet/2024092513400322999.jpg?x-oss-process=image/resize,h_750\" target=\"_blank\">[点击查看]</a> \n\n这份套餐营养较为均衡,包含了蛋白质、碳水化合物、脂肪、维生素和矿物质等多种营养成分,希望能满足你的需求。 为你生成的一天1600kcal套餐如下:\n- **早餐(约450kcal)**:\n - 火腿汁烧豆腐,能量82kcal,蛋白质8.7g,脂肪3.1g,碳水化合物4.7g,<a href=\"https://prodstatic.weis1606.cn/api/diet/2024122015441833302.jpg?x-oss-process=image/resize,h_750\" target=\"_blank\">[点击查看]</a>\n - 五彩鸡块,能量156kcal,蛋白质16.5g,脂肪6.8g,碳水化合物7.2g,<a href=\"https://prodstatic.weis1606.cn/api/diet/2024120910310309135.jpg?x-oss-process=image/resize,h_750\" target=\"_blank\">[点击查看]</a>\n - 素炒云南小瓜,能量39kcal,蛋白质1.7g,脂肪1.3g,碳水化合物5g,<a href=\"https://prodstatic.weis1606.cn/api/diet/2025010318161342225.jpg?x-oss-process=image/resize,h_750\" target=\"_blank\">[点击查看]</a>\n - 牛肉焖饭,能量178kcal,蛋白质7.6g,脂肪2.4g,碳水化合物31.6g,<a href=\"https://prodstatic.weis1606.cn/api/diet/2025010318062384767.jpg?x-oss-process=image/resize,h_750\" target=\"_blank\">[点击查看]</a>\n- **午餐(约540kcal)**:\n - 粟米蒸水蛋,能量117kcal,蛋白质9.9g,脂肪6.6g,碳水化合物4.5g,<a href=\"https://prodstatic.weis1606.cn/api/diet/2025010318155462227.jpg?x-oss-process=image/resize,h_750\" target=\"_blank\">[点击查看]</a>\n - 豉油鸡扒,能量174kcal,蛋白质26.2g,脂肪6.8g,碳水化合物1.9g,<a href=\"https://prodstatic.weis1606.cn/api/diet/2025010318254931832.png?x-oss-process=image/resize,h_750\" target=\"_blank\">[点击查看]</a>\n - 清炒菠菜,能量49kcal,蛋白质4g,脂肪1.5g,碳水化合物4.9g,<a href=\"https://prodstatic.weis1606.cn/api/diet/2025010317554302323.jpg?x-oss-process=image/resize,h_750\" target=\"_blank\">[点击查看]</a>\n - 红米飯,能量182kcal,蛋白质3.4g,脂肪0.1g,碳水化合物41.9g,<a href=\"https://prodstatic.weis1606.cn/api/diet/2025010318002606875.jpg?x-oss-process=image/resize,h_750\" target=\"_blank\">[点击查看]</a>\n- **晚餐(约450kcal)**:\n - 番茄大虾面,能量430kcal,蛋白质26.3g,脂肪5.6g,碳水化合物68.5g,<a href=\"https://prodstatic.weis1606.cn/api/diet/2024092513400322999.jpg?x-oss-process=image/resize,h_750\" target=\"_blank\">[点击查看]</a> \n\n这份套餐营养较为均衡,包含了蛋白质、碳水化合物、脂肪、维生素和矿物质等多种营养成分,希望能满足你的需求。 ",
|
|
23
|
+
"status": "ended",
|
|
24
|
+
"extra": [
|
|
25
|
+
{
|
|
26
|
+
"type": "tool_response",
|
|
27
|
+
"content": {
|
|
28
|
+
"code": 0,
|
|
29
|
+
"skuList": [
|
|
30
|
+
{
|
|
31
|
+
"category": "早餐",
|
|
32
|
+
"totalKcal": 450,
|
|
33
|
+
"sequentialDays": 1,
|
|
34
|
+
"list": [
|
|
35
|
+
{
|
|
36
|
+
"id": 3,
|
|
37
|
+
"name": "五彩雞塊饭",
|
|
38
|
+
"categoryList": [
|
|
39
|
+
{
|
|
40
|
+
"energy": 82,
|
|
41
|
+
"skuname": "火腿汁燒豆腐",
|
|
42
|
+
"skunameEn": "Braised Tofu in Ham Sauce",
|
|
43
|
+
"primaryImgUrl": "https://prodstatic.weis1606.cn/api/diet/2024122015441833302.jpg?x-oss-process=image/resize,h_750",
|
|
44
|
+
"protein": 8.7,
|
|
45
|
+
"fat": 3.1,
|
|
46
|
+
"carbonwater": 4.7
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"fat": 6.8,
|
|
50
|
+
"carbonwater": 7.2,
|
|
51
|
+
"energy": 156,
|
|
52
|
+
"skuname": "五彩雞塊",
|
|
53
|
+
"skunameEn": "Five-Colored Chicken Cubes",
|
|
54
|
+
"primaryImgUrl": "https://prodstatic.weis1606.cn/api/diet/2024120910310309135.jpg?x-oss-process=image/resize,h_750",
|
|
55
|
+
"protein": 16.5
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"skuname": "素炒雲南小瓜",
|
|
59
|
+
"skunameEn": "Stir-Fried Yunnan Zucchini",
|
|
60
|
+
"primaryImgUrl": "https://prodstatic.weis1606.cn/api/diet/2025010318161342225.jpg?x-oss-process=image/resize,h_750",
|
|
61
|
+
"protein": 1.7,
|
|
62
|
+
"fat": 1.3,
|
|
63
|
+
"carbonwater": 5,
|
|
64
|
+
"energy": 39
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"carbonwater": 31.6,
|
|
68
|
+
"energy": 178,
|
|
69
|
+
"skuname": "牛肉燜飯",
|
|
70
|
+
"skunameEn": "Stewed Rice with Braised Beef",
|
|
71
|
+
"primaryImgUrl": "https://prodstatic.weis1606.cn/api/diet/2025010318062384767.jpg?x-oss-process=image/resize,h_750",
|
|
72
|
+
"protein": 7.6,
|
|
73
|
+
"fat": 2.4
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"category": "午餐",
|
|
81
|
+
"totalKcal": 540,
|
|
82
|
+
"sequentialDays": 1,
|
|
83
|
+
"list": [
|
|
84
|
+
{
|
|
85
|
+
"categoryList": [
|
|
86
|
+
{
|
|
87
|
+
"carbonwater": 4.5,
|
|
88
|
+
"energy": 117,
|
|
89
|
+
"skuname": "粟米蒸水蛋",
|
|
90
|
+
"skunameEn": "Steamed Eggs with Corn",
|
|
91
|
+
"primaryImgUrl": "https://prodstatic.weis1606.cn/api/diet/2025010318155462227.jpg?x-oss-process=image/resize,h_750",
|
|
92
|
+
"protein": 9.9,
|
|
93
|
+
"fat": 6.6
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"energy": 174,
|
|
97
|
+
"skuname": "豉油雞扒",
|
|
98
|
+
"skunameEn": "Grilled Chicken Steak with Soy Sauce",
|
|
99
|
+
"primaryImgUrl": "https://prodstatic.weis1606.cn/api/diet/2025010318254931832.png?x-oss-process=image/resize,h_750",
|
|
100
|
+
"protein": 26.2,
|
|
101
|
+
"fat": 6.8,
|
|
102
|
+
"carbonwater": 1.9
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"carbonwater": 4.9,
|
|
106
|
+
"energy": 49,
|
|
107
|
+
"skuname": "清炒菠菜",
|
|
108
|
+
"skunameEn": "Stir-Fried Spinach",
|
|
109
|
+
"primaryImgUrl": "https://prodstatic.weis1606.cn/api/diet/2025010317554302323.jpg?x-oss-process=image/resize,h_750",
|
|
110
|
+
"protein": 4,
|
|
111
|
+
"fat": 1.5
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"energy": 182,
|
|
115
|
+
"skuname": "紅米飯",
|
|
116
|
+
"skunameEn": "Red rice",
|
|
117
|
+
"primaryImgUrl": "https://prodstatic.weis1606.cn/api/diet/2025010318002606875.jpg?x-oss-process=image/resize,h_750",
|
|
118
|
+
"protein": 3.4,
|
|
119
|
+
"fat": 0.1,
|
|
120
|
+
"carbonwater": 41.9
|
|
121
|
+
}
|
|
122
|
+
],
|
|
123
|
+
"id": 4,
|
|
124
|
+
"name": "豉油雞扒饭"
|
|
125
|
+
}
|
|
126
|
+
]
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"sequentialDays": 1,
|
|
130
|
+
"list": [
|
|
131
|
+
{
|
|
132
|
+
"id": 5,
|
|
133
|
+
"name": "番茄大蝦麵",
|
|
134
|
+
"categoryList": [
|
|
135
|
+
{
|
|
136
|
+
"energy": 430,
|
|
137
|
+
"skuname": "番茄大蝦麵",
|
|
138
|
+
"skunameEn": "Tomato Prawn Noodles",
|
|
139
|
+
"primaryImgUrl": "https://prodstatic.weis1606.cn/api/diet/2024092513400322999.jpg?x-oss-process=image/resize,h_750",
|
|
140
|
+
"protein": 26.3,
|
|
141
|
+
"fat": 5.6,
|
|
142
|
+
"carbonwater": 68.5
|
|
143
|
+
}
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
],
|
|
147
|
+
"category": "晚餐",
|
|
148
|
+
"totalKcal": 450
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"totalKcal": 360,
|
|
152
|
+
"sequentialDays": 1,
|
|
153
|
+
"list": [],
|
|
154
|
+
"category": "加餐"
|
|
155
|
+
}
|
|
156
|
+
],
|
|
157
|
+
"msg": "success"
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
]
|
|
161
|
+
}
|
|
162
|
+
]
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// 封装 fetch 函数
|
|
2
|
+
function customFetch(url, options = {}) {
|
|
3
|
+
// 默认配置
|
|
4
|
+
const defaultOptions = {
|
|
5
|
+
method: 'GET', // 默认为 GET 请求
|
|
6
|
+
headers: {
|
|
7
|
+
'Content-Type': 'application/json',
|
|
8
|
+
'Accept': 'application/json'
|
|
9
|
+
},
|
|
10
|
+
body: null, // 默认没有请求体
|
|
11
|
+
credentials: 'same-origin', // 默认同源策略
|
|
12
|
+
mode: 'cors', // 默认跨域模式
|
|
13
|
+
cache: 'default' // 默认缓存策略
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// 合并用户传入的选项和默认选项
|
|
17
|
+
const finalOptions = { ...defaultOptions, ...options };
|
|
18
|
+
|
|
19
|
+
console.log('====finalOptions====', finalOptions);
|
|
20
|
+
// 如果是 POST 请求,确保有 body
|
|
21
|
+
if (finalOptions.method.toUpperCase() === 'POST' && !finalOptions.body) {
|
|
22
|
+
throw new Error('POST request must have a body');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// 发送 fetch 请求
|
|
26
|
+
return fetch(url, finalOptions)
|
|
27
|
+
.then(response => {
|
|
28
|
+
if (!response.ok) {
|
|
29
|
+
// 如果响应状态码不是 2xx,抛出错误
|
|
30
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
31
|
+
}
|
|
32
|
+
return response.json(); // 默认解析为 JSON
|
|
33
|
+
})
|
|
34
|
+
.catch(error => {
|
|
35
|
+
console.error('Fetch error:', error);
|
|
36
|
+
throw error; // 向外抛出错误
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 使用封装的 fetch 函数发送 GET 请求
|
|
41
|
+
export function get(url, options = {}) {
|
|
42
|
+
return customFetch(url, { ...options, method: 'GET' });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 使用封装的 fetch 函数发送 POST 请求
|
|
46
|
+
export function post(url, data={}, options = {}) {
|
|
47
|
+
return customFetch(url, {
|
|
48
|
+
...options,
|
|
49
|
+
method: 'POST',
|
|
50
|
+
body: JSON.stringify(data)
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|