ai-chat-bot-interface 1.5.9 → 1.6.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/package.json +2 -1
- package/src/App.vue +2 -2
- package/src/ChatUi.vue +147 -115
- package/src/components/icons/ArrowDown.vue +26 -0
- package/src/components/icons/ArrowRight.vue +19 -0
- package/src/components/personalForm/personalForm.vue +508 -0
- package/src/components/popup/popup.vue +178 -0
- package/src/main.js +1 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-chat-bot-interface",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.6.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"description": "A AI chat bot interface. (private)",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"markdown-it": "^14.1.0",
|
|
21
21
|
"v-viewer": "^3.0.21",
|
|
22
|
+
"vant": "^4.9.21",
|
|
22
23
|
"vue": "^3.5.13"
|
|
23
24
|
},
|
|
24
25
|
"devDependencies": {
|
package/src/App.vue
CHANGED
|
@@ -5,8 +5,8 @@ import ChatUi from './ChatUi.vue';
|
|
|
5
5
|
<template>
|
|
6
6
|
<div style="width: 100vw; height: 100vh">
|
|
7
7
|
<chat-ui
|
|
8
|
-
bot-id="
|
|
9
|
-
token="
|
|
8
|
+
bot-id="7572460298389962778"
|
|
9
|
+
token="sat_cSmj7ZyuePbkEpOKR7F259YuIfp2fjX9rJ3Om2O0gB9JzrGjAk8pSL269rbu8kzr"
|
|
10
10
|
uid="262598"
|
|
11
11
|
:def-msg="{ placeholder: '发消息...' }"
|
|
12
12
|
:show-header="true"
|
package/src/ChatUi.vue
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
<div v-if="showHeader" class="cui_header">
|
|
5
5
|
<div class="title" @click.stop="queryHistoryList">
|
|
6
6
|
<div class="back" @click.stop="handleBack">
|
|
7
|
-
<back-icon/>
|
|
7
|
+
<back-icon />
|
|
8
8
|
</div>
|
|
9
9
|
<div class="name_box">
|
|
10
10
|
<div class="name">{{ name }}</div>
|
|
11
|
-
<div v-if="nameSub" class="name_sub">{{ nameSub}}</div>
|
|
11
|
+
<div v-if="nameSub" class="name_sub">{{ nameSub }}</div>
|
|
12
12
|
</div>
|
|
13
13
|
</div>
|
|
14
14
|
<div class="btn_group">
|
|
@@ -18,26 +18,26 @@
|
|
|
18
18
|
</button>
|
|
19
19
|
<template v-show="false">
|
|
20
20
|
<button class="btn">
|
|
21
|
-
<clear-icon/>
|
|
21
|
+
<clear-icon />
|
|
22
22
|
</button>
|
|
23
23
|
<button class="btn">
|
|
24
|
-
<close-icon/>
|
|
24
|
+
<close-icon />
|
|
25
25
|
</button>
|
|
26
26
|
</template>
|
|
27
27
|
</div>
|
|
28
28
|
</div>
|
|
29
29
|
<div class="cui_content">
|
|
30
30
|
<div
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
v-if="botInfo && botInfo.onboarding_info"
|
|
32
|
+
style="text-align: left; margin-top: 50px"
|
|
33
33
|
>
|
|
34
34
|
<div style="text-align: center">
|
|
35
35
|
<img
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
:src="botInfo.icon_url"
|
|
37
|
+
alt="icon"
|
|
38
|
+
width="64"
|
|
39
|
+
height="64"
|
|
40
|
+
style="border-radius: 15px"
|
|
41
41
|
/>
|
|
42
42
|
<p class="board_name">{{ botInfo.name }}</p>
|
|
43
43
|
</div>
|
|
@@ -45,11 +45,11 @@
|
|
|
45
45
|
<div class="board_desc">{{ botInfo.onboarding_info.prologue }}</div>
|
|
46
46
|
<div class="flexcss">
|
|
47
47
|
<span
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
v-for="(item, idx) in botInfo.onboarding_info.suggested_questions"
|
|
49
|
+
:key="idx"
|
|
50
|
+
class="board_sug"
|
|
51
|
+
@click.stop="chatConv([{ content: item, text: item }])"
|
|
52
|
+
>{{ item }}</span
|
|
53
53
|
>
|
|
54
54
|
</div>
|
|
55
55
|
</div>
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
<div v-if="conv.role === 'assistant'" class="replay role_sys">
|
|
58
58
|
<div class="replay_content">
|
|
59
59
|
<div class="name">
|
|
60
|
-
<img class="avatar" :src="logo" alt="avatar"/>
|
|
60
|
+
<img class="avatar" :src="logo" alt="avatar" />
|
|
61
61
|
{{ name }}
|
|
62
62
|
<!--<span class="time">12:30</span>-->
|
|
63
63
|
</div>
|
|
@@ -77,8 +77,14 @@
|
|
|
77
77
|
</p>
|
|
78
78
|
</template>
|
|
79
79
|
<template v-if="conv.content">
|
|
80
|
-
<assistant-replay
|
|
81
|
-
|
|
80
|
+
<assistant-replay
|
|
81
|
+
v-if="contentTye === 'text'"
|
|
82
|
+
:content="conv.content"
|
|
83
|
+
/>
|
|
84
|
+
<markdown-viewer
|
|
85
|
+
v-else-if="contentTye === 'markdown'"
|
|
86
|
+
:content="conv.content"
|
|
87
|
+
/>
|
|
82
88
|
<template v-if="!isAnswering">
|
|
83
89
|
<div v-if="conv.extra.length">
|
|
84
90
|
<template v-for="(comp, idx) in conv.extra" :key="idx">
|
|
@@ -90,7 +96,9 @@
|
|
|
90
96
|
{{ comp.planParse }}
|
|
91
97
|
</p>-->
|
|
92
98
|
<dishes-list
|
|
93
|
-
v-if="
|
|
99
|
+
v-if="
|
|
100
|
+
finalCardList.dishes && comp.showType === 'card'
|
|
101
|
+
"
|
|
94
102
|
:sku-list="comp.skuList"
|
|
95
103
|
:is-mini="!showHeader"
|
|
96
104
|
:def-msg="finalDefMsg"
|
|
@@ -104,21 +112,34 @@
|
|
|
104
112
|
@select="handleCardTap({ type: 'match' }, comp)"
|
|
105
113
|
/>
|
|
106
114
|
<store-list
|
|
107
|
-
v-if="
|
|
115
|
+
v-if="
|
|
116
|
+
finalCardList.store && comp.showType === 'store'
|
|
117
|
+
"
|
|
108
118
|
:list="comp.storeList"
|
|
109
119
|
@select="handleStoreSel"
|
|
110
120
|
/>
|
|
121
|
+
<personal-form
|
|
122
|
+
v-if="
|
|
123
|
+
finalCardList.personalForm &&
|
|
124
|
+
comp.showType === 'personalForm'
|
|
125
|
+
"
|
|
126
|
+
@submit="chatConv"
|
|
127
|
+
/>
|
|
111
128
|
</template>
|
|
112
129
|
</div>
|
|
113
130
|
<div v-else-if="handleTextNeedBtn(conv.content)">
|
|
114
131
|
<div
|
|
115
132
|
class="cui_btn cui_btn_2"
|
|
116
133
|
@click.stop="handleCardTap({ type: 'match' }, {})"
|
|
117
|
-
>
|
|
134
|
+
>
|
|
135
|
+
{{ finalDefMsg.useSolution }}
|
|
136
|
+
</div>
|
|
118
137
|
</div>
|
|
119
138
|
</template>
|
|
120
139
|
</template>
|
|
121
|
-
<loading-icon2
|
|
140
|
+
<loading-icon2
|
|
141
|
+
v-if="isAnswering && index === historyList.length - 1"
|
|
142
|
+
/>
|
|
122
143
|
</div>
|
|
123
144
|
</div>
|
|
124
145
|
</div>
|
|
@@ -127,18 +148,18 @@
|
|
|
127
148
|
<div class="name">
|
|
128
149
|
User_{{ uid }}
|
|
129
150
|
<!--<span class="time">12:30</span>-->
|
|
130
|
-
<img class="avatar" :src="avatar" alt="avatar"/>
|
|
151
|
+
<img class="avatar" :src="avatar" alt="avatar" />
|
|
131
152
|
</div>
|
|
132
153
|
<div class="box">
|
|
133
154
|
<p class="text" v-html="conv.content" />
|
|
134
155
|
<div v-if="conv.extra.length">
|
|
135
|
-
<imge-list :list="conv.extra"/>
|
|
156
|
+
<imge-list :list="conv.extra" />
|
|
136
157
|
</div>
|
|
137
158
|
</div>
|
|
138
159
|
</div>
|
|
139
160
|
</div>
|
|
140
161
|
</template>
|
|
141
|
-
<div ref="endTarget" style="height: 100px"/>
|
|
162
|
+
<div ref="endTarget" style="height: 100px" />
|
|
142
163
|
</div>
|
|
143
164
|
<operate-module
|
|
144
165
|
v-model="inputText"
|
|
@@ -156,12 +177,12 @@
|
|
|
156
177
|
</template>
|
|
157
178
|
|
|
158
179
|
<script setup>
|
|
159
|
-
import {computed, nextTick, onMounted, ref} from 'vue';
|
|
180
|
+
import { computed, nextTick, onMounted, ref } from 'vue';
|
|
160
181
|
import ClearIcon from './components/icons/ClearIcon.vue';
|
|
161
182
|
import CloseIcon from './components/icons/CloseIcon.vue';
|
|
162
183
|
import NewSessionIcon from './components/icons/NewSessionIcon.vue';
|
|
163
184
|
import SendIcon from './components/icons/SendIcon.vue';
|
|
164
|
-
import {get, post} from './utils/request';
|
|
185
|
+
import { get, post } from './utils/request';
|
|
165
186
|
import DishesCard from './components/DishesCard.vue';
|
|
166
187
|
import DishesList from './components/DishesList.vue';
|
|
167
188
|
import PlanCard from './components/PlanCard.vue';
|
|
@@ -175,8 +196,8 @@ import ThinkingIcon from './components/icons/ThinkingIcon.vue';
|
|
|
175
196
|
import OkIcon from './components/icons/OkIcon.vue';
|
|
176
197
|
import AssistantReplay from './components/assistantReplay/assistantReplay.vue';
|
|
177
198
|
import StoreList from './components/StoreList/StoreList.vue';
|
|
178
|
-
import MarkdownViewer from
|
|
179
|
-
|
|
199
|
+
import MarkdownViewer from './components/MarkdownPlan/MarkdownViewer.vue';
|
|
200
|
+
import PersonalForm from './components/personalForm/personalForm.vue';
|
|
180
201
|
|
|
181
202
|
const chatOptions = computed(() => {
|
|
182
203
|
return {
|
|
@@ -190,6 +211,7 @@ const conversationId = ref('');
|
|
|
190
211
|
const isAnswering = ref(false);
|
|
191
212
|
const isReq = ref('00');
|
|
192
213
|
const historyList = ref([]);
|
|
214
|
+
const isFirst = ref(false);
|
|
193
215
|
|
|
194
216
|
const msgObj = {
|
|
195
217
|
placeholder: '發消息⋯',
|
|
@@ -205,13 +227,14 @@ const msgObj = {
|
|
|
205
227
|
thinking: '思考中...',
|
|
206
228
|
thinkCompleted: '思考完成',
|
|
207
229
|
useSolution: '用該方案配餐',
|
|
208
|
-
aiTips: '【内容由AI生成,仅供参考】'
|
|
230
|
+
aiTips: '【内容由AI生成,仅供参考】',
|
|
209
231
|
};
|
|
210
232
|
const defCardList = {
|
|
211
233
|
store: true,
|
|
212
234
|
dishes: true,
|
|
213
235
|
plan: true,
|
|
214
|
-
|
|
236
|
+
personalForm: false,
|
|
237
|
+
};
|
|
215
238
|
|
|
216
239
|
const props = defineProps({
|
|
217
240
|
logo: {
|
|
@@ -254,9 +277,9 @@ const props = defineProps({
|
|
|
254
277
|
tagList: {
|
|
255
278
|
type: Array,
|
|
256
279
|
default: () => [
|
|
257
|
-
{name: '人工客服', value: 'kefu', type: 'chat', msg: '人工客服'},
|
|
258
|
-
{name: '查看菜单', value: 'menu', type: 'call', msg: '查看菜单'},
|
|
259
|
-
{name: '体检报告', value: 'exam', type: 'upload', msg: '体检报告'},
|
|
280
|
+
{ name: '人工客服', value: 'kefu', type: 'chat', msg: '人工客服' },
|
|
281
|
+
{ name: '查看菜单', value: 'menu', type: 'call', msg: '查看菜单' },
|
|
282
|
+
{ name: '体检报告', value: 'exam', type: 'upload', msg: '体检报告' },
|
|
260
283
|
],
|
|
261
284
|
},
|
|
262
285
|
defMsg: {
|
|
@@ -265,7 +288,7 @@ const props = defineProps({
|
|
|
265
288
|
},
|
|
266
289
|
contentTye: {
|
|
267
290
|
type: String,
|
|
268
|
-
default: 'text'
|
|
291
|
+
default: 'text',
|
|
269
292
|
},
|
|
270
293
|
cardList: {
|
|
271
294
|
type: Object,
|
|
@@ -273,12 +296,12 @@ const props = defineProps({
|
|
|
273
296
|
store: true,
|
|
274
297
|
dishes: true,
|
|
275
298
|
plan: true,
|
|
276
|
-
})
|
|
299
|
+
}),
|
|
277
300
|
},
|
|
278
301
|
storage: {
|
|
279
302
|
type: String,
|
|
280
|
-
default: 'sessionStorage'
|
|
281
|
-
}
|
|
303
|
+
default: 'sessionStorage',
|
|
304
|
+
},
|
|
282
305
|
});
|
|
283
306
|
|
|
284
307
|
const Emits = defineEmits(['call']);
|
|
@@ -292,9 +315,9 @@ const finalDefMsg = computed(() => {
|
|
|
292
315
|
const finalCardList = computed(() => {
|
|
293
316
|
return {
|
|
294
317
|
...defCardList,
|
|
295
|
-
...props.cardList
|
|
296
|
-
}
|
|
297
|
-
})
|
|
318
|
+
...props.cardList,
|
|
319
|
+
};
|
|
320
|
+
});
|
|
298
321
|
|
|
299
322
|
const endTarget = ref(null);
|
|
300
323
|
const inputText = ref('');
|
|
@@ -303,22 +326,22 @@ const botInfo = ref({});
|
|
|
303
326
|
const storage = {
|
|
304
327
|
setItem: (key, value) => {
|
|
305
328
|
props.storage === 'sessionStorage'
|
|
306
|
-
|
|
307
|
-
|
|
329
|
+
? sessionStorage.setItem(key, value)
|
|
330
|
+
: localStorage.setItem(key, value);
|
|
308
331
|
},
|
|
309
332
|
getItem: (key) => {
|
|
310
333
|
return props.storage === 'sessionStorage'
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
334
|
+
? sessionStorage.getItem(key)
|
|
335
|
+
: localStorage.getItem(key);
|
|
336
|
+
},
|
|
337
|
+
};
|
|
316
338
|
|
|
317
339
|
onMounted(async () => {
|
|
318
340
|
if (storage.getItem('conversationId')) {
|
|
319
341
|
conversationId.value = storage.getItem('conversationId');
|
|
320
342
|
await queryBotInfo();
|
|
321
343
|
await queryHistoryList();
|
|
344
|
+
isFirst.value = false;
|
|
322
345
|
} else {
|
|
323
346
|
await createConv();
|
|
324
347
|
}
|
|
@@ -326,10 +349,11 @@ onMounted(async () => {
|
|
|
326
349
|
});
|
|
327
350
|
|
|
328
351
|
const createConv = async () => {
|
|
352
|
+
isFirst.value = true;
|
|
329
353
|
const res = await post(
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
354
|
+
'https://api.coze.cn/v1/conversation/create',
|
|
355
|
+
{ bot_id: props.botId, connector_id: '999' },
|
|
356
|
+
{ ...chatOptions.value },
|
|
333
357
|
);
|
|
334
358
|
console.log(res);
|
|
335
359
|
if (res.code === 0 && res.data) {
|
|
@@ -405,10 +429,10 @@ const chatConv = async (data) => {
|
|
|
405
429
|
// botId = '7474884145253023795';
|
|
406
430
|
additional_messages.push({
|
|
407
431
|
content: JSON.stringify(
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
432
|
+
item.content.map((con) => ({
|
|
433
|
+
type: con.type,
|
|
434
|
+
file_id: con.file_id,
|
|
435
|
+
})),
|
|
412
436
|
),
|
|
413
437
|
content_type: 'object_string',
|
|
414
438
|
role: 'user',
|
|
@@ -434,21 +458,21 @@ const chatConv = async (data) => {
|
|
|
434
458
|
}
|
|
435
459
|
});
|
|
436
460
|
historyList.value.push(uObj);
|
|
437
|
-
isReq.value = '02'
|
|
461
|
+
isReq.value = '02';
|
|
438
462
|
const res = await fetch(
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
463
|
+
`https://api.coze.cn/v3/chat?conversation_id=${conversationId.value}`,
|
|
464
|
+
{
|
|
465
|
+
method: 'POST',
|
|
466
|
+
headers: {
|
|
467
|
+
...chatOptions.value.headers,
|
|
468
|
+
// 'Content-Type': 'text/event-stream',
|
|
469
|
+
},
|
|
470
|
+
body: JSON.stringify({
|
|
471
|
+
bot_id: botId,
|
|
472
|
+
user_id: props.uid,
|
|
473
|
+
stream: true,
|
|
474
|
+
connector_id: '999',
|
|
475
|
+
additional_messages /*[
|
|
452
476
|
{
|
|
453
477
|
content: '[{"type":"image","file_id":"7475569020654436390"}]',
|
|
454
478
|
content_type: 'object_string',
|
|
@@ -460,14 +484,14 @@ const chatConv = async (data) => {
|
|
|
460
484
|
content: isInCode ? data.code : inText, // '配餐1600kcal,身高173,体重60kg,生成一天的套餐',
|
|
461
485
|
},
|
|
462
486
|
]*/,
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
487
|
+
custom_variables: {
|
|
488
|
+
uid: props.uid,
|
|
489
|
+
},
|
|
490
|
+
}),
|
|
491
|
+
credentials: 'same-origin', // 默认同源策略
|
|
492
|
+
mode: 'cors', // 默认跨域模式
|
|
493
|
+
cache: 'default', // 默认缓存策略
|
|
494
|
+
},
|
|
471
495
|
);
|
|
472
496
|
isReq.value = '03';
|
|
473
497
|
historyList.value.push({
|
|
@@ -485,20 +509,30 @@ const chatConv = async (data) => {
|
|
|
485
509
|
const reader = res.body.getReader();
|
|
486
510
|
const decoder = new TextDecoder('utf-8');
|
|
487
511
|
let buffer = '';
|
|
512
|
+
|
|
513
|
+
const handlePersonalForm = () => {
|
|
514
|
+
console.log('======= End ======', historyList.value);
|
|
515
|
+
if (isFirst.value) {
|
|
516
|
+
historyList.value[idx].extra.push({ showType: 'personalForm' });
|
|
517
|
+
isFirst.value = false;
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
|
|
488
521
|
// 逐块读取数据
|
|
489
522
|
while (true) {
|
|
490
|
-
const {done, value} = await reader.read();
|
|
523
|
+
const { done, value } = await reader.read();
|
|
491
524
|
if (done) {
|
|
492
525
|
console.log('Stream has ended.');
|
|
493
526
|
isAnswering.value = false;
|
|
494
527
|
isReq.value = '00';
|
|
495
528
|
historyList.value[idx].status = 'ended';
|
|
529
|
+
handlePersonalForm();
|
|
496
530
|
scrollToEnd();
|
|
497
531
|
break;
|
|
498
532
|
}
|
|
499
533
|
|
|
500
534
|
// 解码数据块
|
|
501
|
-
const chunk = decoder.decode(value, {stream: true});
|
|
535
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
502
536
|
buffer += chunk;
|
|
503
537
|
|
|
504
538
|
// 处理数据块
|
|
@@ -536,20 +570,20 @@ const chatConv = async (data) => {
|
|
|
536
570
|
) {
|
|
537
571
|
historyList.value[idx].status = 'answering';
|
|
538
572
|
historyList.value[idx].content = handleText(
|
|
539
|
-
|
|
573
|
+
historyList.value[idx].content + strObj.content,
|
|
540
574
|
);
|
|
541
575
|
scrollToEnd();
|
|
542
576
|
} else if (
|
|
543
|
-
|
|
544
|
-
|
|
577
|
+
strObj.hasOwnProperty('type') &&
|
|
578
|
+
strObj.type === 'tool_response'
|
|
545
579
|
) {
|
|
546
580
|
const extraObj = JSON.parse(strObj.content);
|
|
547
581
|
if (extraObj.hasOwnProperty('showType')) {
|
|
548
|
-
historyList.value[idx].extra.push({...extraObj});
|
|
582
|
+
historyList.value[idx].extra.push({ ...extraObj });
|
|
549
583
|
} else if (extraObj.hasOwnProperty('response_for_model')) {
|
|
550
584
|
const modelObj = JSON.parse(extraObj.response_for_model);
|
|
551
585
|
if (modelObj.hasOwnProperty('showType')) {
|
|
552
|
-
historyList.value[idx].extra.push({...modelObj});
|
|
586
|
+
historyList.value[idx].extra.push({ ...modelObj });
|
|
553
587
|
}
|
|
554
588
|
}
|
|
555
589
|
scrollToEnd();
|
|
@@ -562,9 +596,9 @@ const chatConv = async (data) => {
|
|
|
562
596
|
|
|
563
597
|
const queryHistoryList = async () => {
|
|
564
598
|
const res = await post(
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
599
|
+
`https://api.coze.cn/v1/conversation/message/list?conversation_id=${conversationId.value}`,
|
|
600
|
+
{ order: 'asc' },
|
|
601
|
+
{ ...chatOptions.value },
|
|
568
602
|
);
|
|
569
603
|
if (res.code === 0 && res.data && res.data.length) {
|
|
570
604
|
historyList.value = [];
|
|
@@ -593,7 +627,7 @@ const queryHistoryList = async () => {
|
|
|
593
627
|
conversation_id: row.conversation_id,
|
|
594
628
|
bot_id: row.bot_id,
|
|
595
629
|
role: row.role,
|
|
596
|
-
extra: [{...cardObj}],
|
|
630
|
+
extra: [{ ...cardObj }],
|
|
597
631
|
});
|
|
598
632
|
} else if (cardObj.hasOwnProperty('response_for_model')) {
|
|
599
633
|
const modelObj = JSON.parse(cardObj.response_for_model);
|
|
@@ -603,7 +637,7 @@ const queryHistoryList = async () => {
|
|
|
603
637
|
conversation_id: row.conversation_id,
|
|
604
638
|
bot_id: row.bot_id,
|
|
605
639
|
role: row.role,
|
|
606
|
-
extra: [{...modelObj}],
|
|
640
|
+
extra: [{ ...modelObj }],
|
|
607
641
|
});
|
|
608
642
|
}
|
|
609
643
|
}
|
|
@@ -611,8 +645,8 @@ const queryHistoryList = async () => {
|
|
|
611
645
|
console.log('== 解析错误 sys==');
|
|
612
646
|
}
|
|
613
647
|
} else if (
|
|
614
|
-
|
|
615
|
-
|
|
648
|
+
row.content_type === 'object_string' &&
|
|
649
|
+
row.role === 'user'
|
|
616
650
|
) {
|
|
617
651
|
try {
|
|
618
652
|
const strObj = JSON.parse(row.content);
|
|
@@ -622,7 +656,7 @@ const queryHistoryList = async () => {
|
|
|
622
656
|
bot_id: row.bot_id,
|
|
623
657
|
role: row.role,
|
|
624
658
|
meta_data: row.meta_data,
|
|
625
|
-
extra: strObj.map((item) => ({...item, showType: item.type})),
|
|
659
|
+
extra: strObj.map((item) => ({ ...item, showType: item.type })),
|
|
626
660
|
});
|
|
627
661
|
} catch (e) {
|
|
628
662
|
console.log('== 解析错误 user==');
|
|
@@ -632,15 +666,15 @@ const queryHistoryList = async () => {
|
|
|
632
666
|
});
|
|
633
667
|
cardList.forEach((card) => {
|
|
634
668
|
const idx =
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
669
|
+
card.role === 'user'
|
|
670
|
+
? historyList.value.findIndex(
|
|
671
|
+
(c) =>
|
|
672
|
+
c.meta_data.chat_group === card.meta_data.chat_group &&
|
|
673
|
+
c.role === card.role,
|
|
674
|
+
)
|
|
675
|
+
: historyList.value.findIndex(
|
|
676
|
+
(c) => c.chat_id === card.chat_id && c.role === card.role,
|
|
677
|
+
);
|
|
644
678
|
if (idx > -1) {
|
|
645
679
|
historyList.value[idx].extra = [...card.extra];
|
|
646
680
|
}
|
|
@@ -652,12 +686,12 @@ const queryHistoryList = async () => {
|
|
|
652
686
|
|
|
653
687
|
const queryBotInfo = async () => {
|
|
654
688
|
const res = await get(
|
|
655
|
-
|
|
656
|
-
|
|
689
|
+
`https://api.coze.cn/v1/bot/get_online_info?bot_id=${props.botId}`,
|
|
690
|
+
{ ...chatOptions.value },
|
|
657
691
|
);
|
|
658
692
|
console.log(res);
|
|
659
693
|
if (res.code === 0 && res.data) {
|
|
660
|
-
botInfo.value = {...res.data};
|
|
694
|
+
botInfo.value = { ...res.data };
|
|
661
695
|
}
|
|
662
696
|
};
|
|
663
697
|
|
|
@@ -672,7 +706,7 @@ const handleText = (str) => {
|
|
|
672
706
|
// );
|
|
673
707
|
};
|
|
674
708
|
const handleBack = () => {
|
|
675
|
-
Emits('call', {type: 'back'});
|
|
709
|
+
Emits('call', { type: 'back' });
|
|
676
710
|
};
|
|
677
711
|
const handleStoreSel = (info) => {
|
|
678
712
|
chatConv([{ content: info.content, text: info.text }]);
|
|
@@ -681,18 +715,18 @@ const handleTagSel = (info) => {
|
|
|
681
715
|
console.log(info);
|
|
682
716
|
switch (info.type) {
|
|
683
717
|
case 'chat':
|
|
684
|
-
chatConv([{content: info.msg, text: info.msg}]);
|
|
718
|
+
chatConv([{ content: info.msg, text: info.msg }]);
|
|
685
719
|
break;
|
|
686
720
|
case 'call':
|
|
687
|
-
Emits('call', {...info});
|
|
721
|
+
Emits('call', { ...info });
|
|
688
722
|
break;
|
|
689
723
|
}
|
|
690
724
|
};
|
|
691
725
|
|
|
692
|
-
const handleCardTap = ({type}, info) => {
|
|
726
|
+
const handleCardTap = ({ type }, info) => {
|
|
693
727
|
switch (type) {
|
|
694
728
|
case 'change':
|
|
695
|
-
chatConv([{content: '換一套菜品', text: '換一套菜品'}]);
|
|
729
|
+
chatConv([{ content: '換一套菜品', text: '換一套菜品' }]);
|
|
696
730
|
break;
|
|
697
731
|
case 'match':
|
|
698
732
|
chatConv([
|
|
@@ -703,7 +737,7 @@ const handleCardTap = ({type}, info) => {
|
|
|
703
737
|
]);
|
|
704
738
|
break;
|
|
705
739
|
default:
|
|
706
|
-
Emits('call', {type, info});
|
|
740
|
+
Emits('call', { type, info });
|
|
707
741
|
}
|
|
708
742
|
};
|
|
709
743
|
const handleCall = (data) => {
|
|
@@ -721,8 +755,8 @@ const handleCall = (data) => {
|
|
|
721
755
|
const handlePlanParse = (list) => {
|
|
722
756
|
const pIdx = list.findIndex((p) => p.showType === 'plan');
|
|
723
757
|
return pIdx > -1
|
|
724
|
-
|
|
725
|
-
|
|
758
|
+
? { show: true, text: list[pIdx].planParse }
|
|
759
|
+
: { show: false };
|
|
726
760
|
};
|
|
727
761
|
|
|
728
762
|
const scrollToEnd = () => {
|
|
@@ -731,8 +765,6 @@ const scrollToEnd = () => {
|
|
|
731
765
|
});
|
|
732
766
|
};
|
|
733
767
|
|
|
734
|
-
|
|
735
|
-
|
|
736
768
|
const handleTextNeedBtn = (str) => {
|
|
737
769
|
const regExp = /#全日總熱量:(\d*?)kcal/g;
|
|
738
770
|
return regExp.test(str);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<svg
|
|
3
|
+
width="1em"
|
|
4
|
+
height="1em"
|
|
5
|
+
viewBox="0 0 44 44"
|
|
6
|
+
version="1.1"
|
|
7
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
8
|
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
9
|
+
>
|
|
10
|
+
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
11
|
+
<g transform="translate(44, 0) rotate(90)">
|
|
12
|
+
<polyline
|
|
13
|
+
stroke="currentColor"
|
|
14
|
+
stroke-width="4"
|
|
15
|
+
stroke-linecap="round"
|
|
16
|
+
stroke-linejoin="round"
|
|
17
|
+
points="16 10 28 22 16 34"
|
|
18
|
+
/>
|
|
19
|
+
</g>
|
|
20
|
+
</g>
|
|
21
|
+
</svg>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<script setup lang="ts"></script>
|
|
25
|
+
|
|
26
|
+
<style scoped></style>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<svg
|
|
3
|
+
width="1em"
|
|
4
|
+
height="1em"
|
|
5
|
+
viewBox="0 0 25 24"
|
|
6
|
+
fill="none"
|
|
7
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
8
|
+
>
|
|
9
|
+
<path
|
|
10
|
+
d="M10.3193 16.5L14.8193 12L10.3193 7.5"
|
|
11
|
+
stroke="currentColor"
|
|
12
|
+
stroke-linecap="round"
|
|
13
|
+
/>
|
|
14
|
+
</svg>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<script setup></script>
|
|
18
|
+
|
|
19
|
+
<style scoped></style>
|
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed, onMounted, ref } from 'vue';
|
|
3
|
+
import ArrowRight from '../components/icons/ArrowRight.vue';
|
|
4
|
+
import Popup from '../components/popup/popup.vue';
|
|
5
|
+
import { showToast } from 'vant';
|
|
6
|
+
|
|
7
|
+
const showPopup = ref(false);
|
|
8
|
+
|
|
9
|
+
const dialogInfo = ref({
|
|
10
|
+
key: 'sex',
|
|
11
|
+
sex: '',
|
|
12
|
+
age: '',
|
|
13
|
+
height: '',
|
|
14
|
+
weight: '',
|
|
15
|
+
sport: '',
|
|
16
|
+
taste: [],
|
|
17
|
+
taboo: [],
|
|
18
|
+
title: '标题',
|
|
19
|
+
unit: '',
|
|
20
|
+
dialogHeight: '50vh',
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const tasteList = [
|
|
24
|
+
'不吃辣',
|
|
25
|
+
'不吃葱',
|
|
26
|
+
'不吃姜',
|
|
27
|
+
'不吃蒜',
|
|
28
|
+
'不吃糖',
|
|
29
|
+
'不吃鱼',
|
|
30
|
+
'不吃虾',
|
|
31
|
+
'不吃牛肉',
|
|
32
|
+
'不吃鸡肉',
|
|
33
|
+
'不吃猪肉',
|
|
34
|
+
];
|
|
35
|
+
const tabooList = ['高GI', '高饱和脂肪酸', '高嘌呤', '高盐', '高胆固醇'];
|
|
36
|
+
|
|
37
|
+
const pfForm = ref([
|
|
38
|
+
{ key: 'sex', label: '性别', value: '', placeholder: '请输选择' },
|
|
39
|
+
{
|
|
40
|
+
key: 'age',
|
|
41
|
+
label: '年龄',
|
|
42
|
+
value: '',
|
|
43
|
+
placeholder: '请输入年龄',
|
|
44
|
+
unit: '岁',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
key: 'height',
|
|
48
|
+
label: '身高',
|
|
49
|
+
value: '',
|
|
50
|
+
placeholder: '请输入身高',
|
|
51
|
+
unit: 'cm',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
key: 'weight',
|
|
55
|
+
label: '体重',
|
|
56
|
+
value: '',
|
|
57
|
+
placeholder: '请输入体重',
|
|
58
|
+
unit: 'kg',
|
|
59
|
+
},
|
|
60
|
+
{ key: 'sport', label: '日常运动水平', value: '', placeholder: '请输入' },
|
|
61
|
+
{
|
|
62
|
+
key: 'taste',
|
|
63
|
+
label: '口味偏好及饮食禁忌',
|
|
64
|
+
value: '',
|
|
65
|
+
taste: [],
|
|
66
|
+
taboo: [],
|
|
67
|
+
placeholder: '请输入',
|
|
68
|
+
},
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
const Emits = defineEmits(['submit']);
|
|
72
|
+
|
|
73
|
+
onMounted(() => {
|
|
74
|
+
const form = localStorage.getItem('personalInfo');
|
|
75
|
+
if (form) {
|
|
76
|
+
pfForm.value = JSON.parse(form);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const isValid = computed(() => {
|
|
81
|
+
return pfForm.value
|
|
82
|
+
.filter((item) => item.key !== 'taste')
|
|
83
|
+
.every((item) => item.value !== '');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const handleSelect = (item) => {
|
|
87
|
+
dialogInfo.value.key = item.key;
|
|
88
|
+
|
|
89
|
+
switch (item.key) {
|
|
90
|
+
case 'sex':
|
|
91
|
+
dialogInfo.value.title = '选择性别';
|
|
92
|
+
dialogInfo.value.dialogHeight = '300px';
|
|
93
|
+
dialogInfo.value.sex = item.value;
|
|
94
|
+
break;
|
|
95
|
+
case 'age':
|
|
96
|
+
dialogInfo.value.title = '设置年龄';
|
|
97
|
+
dialogInfo.value.dialogHeight = '240px';
|
|
98
|
+
dialogInfo.value.unit = item.unit;
|
|
99
|
+
dialogInfo.value.age = item.value;
|
|
100
|
+
break;
|
|
101
|
+
case 'height':
|
|
102
|
+
dialogInfo.value.title = '设置身高';
|
|
103
|
+
dialogInfo.value.dialogHeight = '240px';
|
|
104
|
+
dialogInfo.value.unit = item.unit;
|
|
105
|
+
dialogInfo.value.height = item.value;
|
|
106
|
+
break;
|
|
107
|
+
case 'weight':
|
|
108
|
+
dialogInfo.value.title = '设置体重';
|
|
109
|
+
dialogInfo.value.dialogHeight = '240px';
|
|
110
|
+
dialogInfo.value.unit = item.unit;
|
|
111
|
+
dialogInfo.value.weight = item.value;
|
|
112
|
+
break;
|
|
113
|
+
case 'sport':
|
|
114
|
+
dialogInfo.value.title = '日常运动水平';
|
|
115
|
+
dialogInfo.value.dialogHeight = '360px';
|
|
116
|
+
dialogInfo.value.unit = '';
|
|
117
|
+
dialogInfo.value.sport = item.value;
|
|
118
|
+
break;
|
|
119
|
+
case 'taste':
|
|
120
|
+
dialogInfo.value.title = '口味偏好&饮食禁忌';
|
|
121
|
+
dialogInfo.value.dialogHeight = '500px';
|
|
122
|
+
dialogInfo.value.unit = '';
|
|
123
|
+
dialogInfo.value.taste = [...item.taste];
|
|
124
|
+
dialogInfo.value.taboo = [...item.taboo];
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
console.log(dialogInfo.value, item);
|
|
128
|
+
showPopup.value = true;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const handleConfirm = () => {
|
|
132
|
+
switch (dialogInfo.value.key) {
|
|
133
|
+
case 'sex':
|
|
134
|
+
if (!dialogInfo.value.sex) {
|
|
135
|
+
showToast('请选择性别');
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
pfForm.value.find((item) => item.key === 'sex').value =
|
|
139
|
+
dialogInfo.value.sex;
|
|
140
|
+
break;
|
|
141
|
+
case 'age':
|
|
142
|
+
if (!dialogInfo.value.age) {
|
|
143
|
+
showToast('请输入年龄');
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
pfForm.value.find((item) => item.key === 'age').value =
|
|
147
|
+
dialogInfo.value.age;
|
|
148
|
+
break;
|
|
149
|
+
case 'height':
|
|
150
|
+
if (!dialogInfo.value.height) {
|
|
151
|
+
showToast('请输入身高');
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
pfForm.value.find((item) => item.key === 'height').value =
|
|
155
|
+
dialogInfo.value.height;
|
|
156
|
+
break;
|
|
157
|
+
case 'weight':
|
|
158
|
+
if (!dialogInfo.value.weight) {
|
|
159
|
+
showToast('请输入体重');
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
pfForm.value.find((item) => item.key === 'weight').value =
|
|
163
|
+
dialogInfo.value.weight;
|
|
164
|
+
break;
|
|
165
|
+
case 'sport':
|
|
166
|
+
if (!dialogInfo.value.sport) {
|
|
167
|
+
showToast('请选择日常运动水平');
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
pfForm.value.find((item) => item.key === 'sport').value =
|
|
171
|
+
dialogInfo.value.sport;
|
|
172
|
+
break;
|
|
173
|
+
case 'taste':
|
|
174
|
+
pfForm.value.find((item) => item.key === 'taste').taste =
|
|
175
|
+
dialogInfo.value.taste;
|
|
176
|
+
pfForm.value.find((item) => item.key === 'taste').taboo =
|
|
177
|
+
dialogInfo.value.taboo;
|
|
178
|
+
pfForm.value.find((item) => item.key === 'taste').value = [
|
|
179
|
+
...dialogInfo.value.taste,
|
|
180
|
+
...dialogInfo.value.taboo,
|
|
181
|
+
].join('、');
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
showPopup.value = false;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const handleSubmit = () => {
|
|
189
|
+
if (!isValid.value) {
|
|
190
|
+
showToast('请填写完整信息');
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
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}${
|
|
197
|
+
pfForm.value.find((item) => item.key === 'age').unit || ''
|
|
198
|
+
};
|
|
199
|
+
身高:${pfForm.value.find((item) => item.key === 'height').value}${
|
|
200
|
+
pfForm.value.find((item) => item.key === 'height').unit || ''
|
|
201
|
+
};
|
|
202
|
+
体重:${pfForm.value.find((item) => item.key === 'weight').value}${
|
|
203
|
+
pfForm.value.find((item) => item.key === 'weight').unit || ''
|
|
204
|
+
};
|
|
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
|
+
`;
|
|
214
|
+
console.log(msg);
|
|
215
|
+
Emits('submit', [{ content: msg, text: msg }]);
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const selectTag = (item, type) => {
|
|
219
|
+
if (dialogInfo.value[type].includes(item)) {
|
|
220
|
+
dialogInfo.value[type] = dialogInfo.value[type].filter((i) => i !== item);
|
|
221
|
+
} else {
|
|
222
|
+
dialogInfo.value[type].push(item);
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
</script>
|
|
226
|
+
|
|
227
|
+
<template>
|
|
228
|
+
<div class="pf_wrap">
|
|
229
|
+
<div class="pf_form">
|
|
230
|
+
<div v-for="item in pfForm" :key="item.key" class="pf_row">
|
|
231
|
+
<div class="title">{{ item.label }}</div>
|
|
232
|
+
<div class="content" @click="handleSelect(item)">
|
|
233
|
+
<span
|
|
234
|
+
class="text_row"
|
|
235
|
+
:style="{ color: item.value ? '#000' : '#75759d' }"
|
|
236
|
+
>{{
|
|
237
|
+
item.value ? `${item.value} ${item.unit || ''}` : item.placeholder
|
|
238
|
+
}}</span
|
|
239
|
+
>
|
|
240
|
+
<arrow-right class="arrow" />
|
|
241
|
+
</div>
|
|
242
|
+
</div>
|
|
243
|
+
</div>
|
|
244
|
+
<div
|
|
245
|
+
class="pf_btn"
|
|
246
|
+
:class="{ disabled: !isValid }"
|
|
247
|
+
@click.stop="handleSubmit"
|
|
248
|
+
>
|
|
249
|
+
确定
|
|
250
|
+
</div>
|
|
251
|
+
</div>
|
|
252
|
+
|
|
253
|
+
<popup
|
|
254
|
+
v-model:show="showPopup"
|
|
255
|
+
:title="dialogInfo.title"
|
|
256
|
+
:height="dialogInfo.dialogHeight"
|
|
257
|
+
@confirm="handleConfirm"
|
|
258
|
+
>
|
|
259
|
+
<template #default>
|
|
260
|
+
<div v-if="dialogInfo.key === 'sex'" class="sex_wrap">
|
|
261
|
+
<div
|
|
262
|
+
:class="{ sex: true, selected: dialogInfo.sex === '男' }"
|
|
263
|
+
@click="dialogInfo.sex = '男'"
|
|
264
|
+
>
|
|
265
|
+
男
|
|
266
|
+
</div>
|
|
267
|
+
<div
|
|
268
|
+
:class="{ sex: true, selected: dialogInfo.sex === '女' }"
|
|
269
|
+
@click="dialogInfo.sex = '女'"
|
|
270
|
+
>
|
|
271
|
+
女
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
<div
|
|
275
|
+
v-if="['age', 'height', 'weight'].includes(dialogInfo.key)"
|
|
276
|
+
class="input_wrap"
|
|
277
|
+
>
|
|
278
|
+
<input
|
|
279
|
+
v-model="dialogInfo[dialogInfo.key]"
|
|
280
|
+
class="input"
|
|
281
|
+
type="text"
|
|
282
|
+
placeholder="请输入"
|
|
283
|
+
/>
|
|
284
|
+
<div class="unit">{{ dialogInfo.unit }}</div>
|
|
285
|
+
</div>
|
|
286
|
+
<div v-if="dialogInfo.key === 'sport'" class="sport_wrap">
|
|
287
|
+
<div
|
|
288
|
+
:class="{ row: true, selected: dialogInfo.sport === '久坐' }"
|
|
289
|
+
@click.stop="dialogInfo.sport = '久坐'"
|
|
290
|
+
>
|
|
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>
|
|
304
|
+
</div>
|
|
305
|
+
</div>
|
|
306
|
+
<div v-if="dialogInfo.key === 'taste'" class="taste_wrap">
|
|
307
|
+
<div class="title">口味偏好</div>
|
|
308
|
+
<div class="box">
|
|
309
|
+
<div
|
|
310
|
+
:class="{ tag: true, selected: dialogInfo.taste.includes(item) }"
|
|
311
|
+
v-for="item in tasteList"
|
|
312
|
+
:key="item"
|
|
313
|
+
@click.stop="selectTag(item, 'taste')"
|
|
314
|
+
>
|
|
315
|
+
{{ item }}
|
|
316
|
+
</div>
|
|
317
|
+
</div>
|
|
318
|
+
<div class="line"></div>
|
|
319
|
+
<div class="title">饮食禁忌</div>
|
|
320
|
+
<div class="box">
|
|
321
|
+
<div
|
|
322
|
+
:class="{ tag: true, selected: dialogInfo.taboo.includes(item) }"
|
|
323
|
+
v-for="item in tabooList"
|
|
324
|
+
:key="item"
|
|
325
|
+
@click.stop="selectTag(item, 'taboo')"
|
|
326
|
+
>
|
|
327
|
+
{{ item }}
|
|
328
|
+
</div>
|
|
329
|
+
</div>
|
|
330
|
+
</div>
|
|
331
|
+
</template>
|
|
332
|
+
</popup>
|
|
333
|
+
</template>
|
|
334
|
+
|
|
335
|
+
<style scoped lang="less">
|
|
336
|
+
.pf {
|
|
337
|
+
&_wrap {
|
|
338
|
+
width: 100%;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
&_form {
|
|
342
|
+
padding: 0 15px;
|
|
343
|
+
background: #fff;
|
|
344
|
+
border-radius: 8px;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
&_row {
|
|
348
|
+
display: flex;
|
|
349
|
+
flex-direction: row;
|
|
350
|
+
align-items: center;
|
|
351
|
+
justify-content: space-between;
|
|
352
|
+
border-bottom: 1px solid #dadada;
|
|
353
|
+
height: 54px;
|
|
354
|
+
line-height: 54px;
|
|
355
|
+
|
|
356
|
+
&:last-child {
|
|
357
|
+
border-bottom: none;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.title {
|
|
361
|
+
font-size: 14px;
|
|
362
|
+
font-weight: 600;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.content {
|
|
366
|
+
display: flex;
|
|
367
|
+
flex-direction: row;
|
|
368
|
+
align-items: center;
|
|
369
|
+
justify-content: flex-end;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.arrow {
|
|
373
|
+
color: #000;
|
|
374
|
+
font-size: 22px;
|
|
375
|
+
}
|
|
376
|
+
.text_row {
|
|
377
|
+
display: inline-block;
|
|
378
|
+
font-size: 14px;
|
|
379
|
+
text-align: right;
|
|
380
|
+
width: 160px;
|
|
381
|
+
text-overflow: ellipsis;
|
|
382
|
+
white-space: nowrap;
|
|
383
|
+
overflow: hidden;
|
|
384
|
+
}
|
|
385
|
+
.input {
|
|
386
|
+
font-size: 14px;
|
|
387
|
+
text-align: right;
|
|
388
|
+
outline: none;
|
|
389
|
+
border: none;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
&_btn {
|
|
394
|
+
cursor: pointer;
|
|
395
|
+
height: 40px;
|
|
396
|
+
line-height: 40px;
|
|
397
|
+
border-radius: 20px;
|
|
398
|
+
text-align: center;
|
|
399
|
+
font-size: 14px;
|
|
400
|
+
font-weight: 600;
|
|
401
|
+
color: #000;
|
|
402
|
+
background: #00dc4e;
|
|
403
|
+
margin-top: 15px;
|
|
404
|
+
|
|
405
|
+
&.disabled {
|
|
406
|
+
color: #fff;
|
|
407
|
+
background: #d9d9d9;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
.sex {
|
|
412
|
+
&_wrap {
|
|
413
|
+
padding: 50px 0;
|
|
414
|
+
.sex {
|
|
415
|
+
transition: 100ms ease-in-out;
|
|
416
|
+
font-size: 15px;
|
|
417
|
+
font-weight: 400;
|
|
418
|
+
text-align: center;
|
|
419
|
+
height: 58px;
|
|
420
|
+
line-height: 58px;
|
|
421
|
+
border-top: 1px solid #fff;
|
|
422
|
+
border-bottom: 1px solid #fff;
|
|
423
|
+
&.selected {
|
|
424
|
+
font-size: 20px;
|
|
425
|
+
font-weight: 600;
|
|
426
|
+
color: #039938;
|
|
427
|
+
border-color: #d9d9d9;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
.input {
|
|
433
|
+
&_wrap {
|
|
434
|
+
text-align: center;
|
|
435
|
+
padding: 20px 0;
|
|
436
|
+
.input {
|
|
437
|
+
height: 50px;
|
|
438
|
+
line-height: 50px;
|
|
439
|
+
font-size: 36px;
|
|
440
|
+
text-align: center;
|
|
441
|
+
width: 200px;
|
|
442
|
+
outline: none;
|
|
443
|
+
border: none;
|
|
444
|
+
}
|
|
445
|
+
.unit {
|
|
446
|
+
font-size: 15px;
|
|
447
|
+
font-weight: 400;
|
|
448
|
+
line-height: 22px;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
.sport {
|
|
453
|
+
&_wrap {
|
|
454
|
+
padding: 20px 0;
|
|
455
|
+
|
|
456
|
+
.row {
|
|
457
|
+
transition: 100ms ease-in-out;
|
|
458
|
+
font-size: 15px;
|
|
459
|
+
font-weight: 400;
|
|
460
|
+
text-align: center;
|
|
461
|
+
height: 58px;
|
|
462
|
+
line-height: 58px;
|
|
463
|
+
border-top: 1px solid #fff;
|
|
464
|
+
border-bottom: 1px solid #fff;
|
|
465
|
+
|
|
466
|
+
&.selected {
|
|
467
|
+
font-size: 20px;
|
|
468
|
+
font-weight: 600;
|
|
469
|
+
color: #039938;
|
|
470
|
+
border-color: #d9d9d9;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
.taste {
|
|
476
|
+
&_wrap {
|
|
477
|
+
padding: 20px;
|
|
478
|
+
.title {
|
|
479
|
+
font-size: 15px;
|
|
480
|
+
font-weight: 600;
|
|
481
|
+
line-height: 32px;
|
|
482
|
+
margin-bottom: 16px;
|
|
483
|
+
}
|
|
484
|
+
.box {
|
|
485
|
+
display: inline-flex;
|
|
486
|
+
flex-wrap: wrap;
|
|
487
|
+
gap: 10px;
|
|
488
|
+
}
|
|
489
|
+
.line {
|
|
490
|
+
border-top: 1px solid #e9e9e9;
|
|
491
|
+
margin: 25px 0;
|
|
492
|
+
}
|
|
493
|
+
.tag {
|
|
494
|
+
height: 32px;
|
|
495
|
+
line-height: 32px;
|
|
496
|
+
padding: 0 15px;
|
|
497
|
+
background-color: #f3f4f5;
|
|
498
|
+
border-radius: 16px;
|
|
499
|
+
font-size: 14px;
|
|
500
|
+
text-align: center;
|
|
501
|
+
&.selected {
|
|
502
|
+
color: #fff;
|
|
503
|
+
background-color: #000;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
</style>
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed, ref, watch } from 'vue';
|
|
3
|
+
import ArrowDown from '../components/icons/ArrowDown.vue';
|
|
4
|
+
|
|
5
|
+
const props = defineProps({
|
|
6
|
+
show: {
|
|
7
|
+
type: Boolean,
|
|
8
|
+
default: false,
|
|
9
|
+
},
|
|
10
|
+
height: {
|
|
11
|
+
type: String,
|
|
12
|
+
default: '50vh',
|
|
13
|
+
},
|
|
14
|
+
title: {
|
|
15
|
+
type: String,
|
|
16
|
+
default: '',
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
const emit = defineEmits(['update:show', 'confirm', 'close']);
|
|
20
|
+
|
|
21
|
+
const visibleEle = ref(false);
|
|
22
|
+
const bottomPosi = ref(`-${props.height}`);
|
|
23
|
+
const animationInfo = ref({
|
|
24
|
+
delay: 300,
|
|
25
|
+
bottom: `-${props.height}`,
|
|
26
|
+
opacity: 0,
|
|
27
|
+
});
|
|
28
|
+
const handleAnimation = (type) => {
|
|
29
|
+
if (type) {
|
|
30
|
+
animationInfo.value.bottom = '0';
|
|
31
|
+
animationInfo.value.opacity = 0.5;
|
|
32
|
+
} else {
|
|
33
|
+
animationInfo.value.bottom = `-${props.height}`;
|
|
34
|
+
animationInfo.value.opacity = 0;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const handleConfirm = () => {
|
|
38
|
+
emit('confirm', {});
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
watch(
|
|
42
|
+
() => props.show,
|
|
43
|
+
(newVal, oldValue) => {
|
|
44
|
+
if (newVal) {
|
|
45
|
+
bottomPosi.value = `-${props.height}`;
|
|
46
|
+
visibleEle.value = true;
|
|
47
|
+
setTimeout(() => {
|
|
48
|
+
handleAnimation(true);
|
|
49
|
+
}, 0);
|
|
50
|
+
} else {
|
|
51
|
+
handleAnimation(false);
|
|
52
|
+
setTimeout(() => {
|
|
53
|
+
visibleEle.value = false;
|
|
54
|
+
}, 300);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
);
|
|
58
|
+
const handleClose = () => {
|
|
59
|
+
emit('update:show', false);
|
|
60
|
+
handleAnimation(false);
|
|
61
|
+
setTimeout(() => {
|
|
62
|
+
visibleEle.value = false;
|
|
63
|
+
}, 300);
|
|
64
|
+
};
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<template>
|
|
68
|
+
<div
|
|
69
|
+
v-if="visibleEle"
|
|
70
|
+
class="pup_mask"
|
|
71
|
+
:style="{
|
|
72
|
+
backgroundColor: `rgba(0, 0, 0, ${animationInfo.opacity})`,
|
|
73
|
+
}"
|
|
74
|
+
@click.stop="handleClose"
|
|
75
|
+
>
|
|
76
|
+
<div
|
|
77
|
+
class="pup_wrap"
|
|
78
|
+
:style="{ height: height, bottom: animationInfo.bottom }"
|
|
79
|
+
@click.stop="() => {}"
|
|
80
|
+
>
|
|
81
|
+
<div class="pup_header">
|
|
82
|
+
<div class="left">
|
|
83
|
+
<div class="icon_down" @click.stop="handleClose">
|
|
84
|
+
<arrow-down />
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
<div class="title">{{ title }}</div>
|
|
88
|
+
<div class="right">
|
|
89
|
+
<div class="btn" @click="handleConfirm">确定</div>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
<div class="pup_content" :style="{ height: `calc(height - 56px)` }">
|
|
93
|
+
<slot />
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
</template>
|
|
98
|
+
|
|
99
|
+
<style scoped lang="less">
|
|
100
|
+
@delay: 200ms;
|
|
101
|
+
@headerHeight: 56px;
|
|
102
|
+
.pup {
|
|
103
|
+
&_mask {
|
|
104
|
+
transition: background-color @delay ease-in-out;
|
|
105
|
+
width: 100vw;
|
|
106
|
+
height: 100vh;
|
|
107
|
+
position: fixed;
|
|
108
|
+
top: 0;
|
|
109
|
+
left: 0;
|
|
110
|
+
z-index: 899;
|
|
111
|
+
}
|
|
112
|
+
&_wrap {
|
|
113
|
+
transition: bottom @delay ease-in-out;
|
|
114
|
+
width: 100%;
|
|
115
|
+
height: 50vh;
|
|
116
|
+
background: #fff;
|
|
117
|
+
border-radius: 16px 16px 0 0;
|
|
118
|
+
overflow: hidden;
|
|
119
|
+
position: absolute;
|
|
120
|
+
bottom: 0;
|
|
121
|
+
left: 0;
|
|
122
|
+
z-index: 900;
|
|
123
|
+
}
|
|
124
|
+
&_header {
|
|
125
|
+
display: flex;
|
|
126
|
+
flex-direction: row;
|
|
127
|
+
align-items: center;
|
|
128
|
+
justify-content: space-between;
|
|
129
|
+
height: @headerHeight;
|
|
130
|
+
line-height: @headerHeight;
|
|
131
|
+
background-color: #fff;
|
|
132
|
+
position: relative;
|
|
133
|
+
|
|
134
|
+
.icon_down {
|
|
135
|
+
padding: 0 16px;
|
|
136
|
+
font-size: 24px;
|
|
137
|
+
}
|
|
138
|
+
.title {
|
|
139
|
+
width: 100%;
|
|
140
|
+
text-align: center;
|
|
141
|
+
font-size: 16px;
|
|
142
|
+
font-weight: 600;
|
|
143
|
+
}
|
|
144
|
+
.left {
|
|
145
|
+
height: @headerHeight;
|
|
146
|
+
display: flex;
|
|
147
|
+
flex-direction: row;
|
|
148
|
+
align-items: center;
|
|
149
|
+
justify-content: flex-start;
|
|
150
|
+
position: absolute;
|
|
151
|
+
left: 0;
|
|
152
|
+
top: 0;
|
|
153
|
+
z-index: 5;
|
|
154
|
+
}
|
|
155
|
+
.right {
|
|
156
|
+
height: @headerHeight;
|
|
157
|
+
display: flex;
|
|
158
|
+
flex-direction: row;
|
|
159
|
+
align-items: center;
|
|
160
|
+
justify-content: flex-end;
|
|
161
|
+
position: absolute;
|
|
162
|
+
right: 0;
|
|
163
|
+
top: 0;
|
|
164
|
+
z-index: 5;
|
|
165
|
+
}
|
|
166
|
+
.btn {
|
|
167
|
+
cursor: pointer;
|
|
168
|
+
padding: 0 16px;
|
|
169
|
+
height: 32px;
|
|
170
|
+
line-height: 32px;
|
|
171
|
+
margin-right: 16px;
|
|
172
|
+
text-align: center;
|
|
173
|
+
border-radius: 16px;
|
|
174
|
+
background-color: #00dc4e;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
</style>
|