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/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
- <img class="logo" :src="logo" alt="logo" style="width: 24px; height: 24px;"/>
7
- {{ name }}
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 v-if="botInfo && botInfo.onboarding_info" style="text-align: left; margin-top: 50px;">
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 :src="botInfo.icon_url" alt="icon" width="64" height="64"
24
- style="border-radius: 15px;"
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 v-for="(item, idx) in botInfo.onboarding_info.suggested_questions"
32
- :key="idx"
33
- class="board_sug"
34
- @click.stop="chatConv({code: item, text: item});"
35
- >{{ item }}</span>
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">{{ name }} <!--<span class="time">12:30</span>--></div>
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
- <p class="text" v-html="conv.content"></p>
45
- <div v-if="conv.extra.length && !isAnswering">
46
- <template v-for="(comp, idx) in conv.extra" :key="idx">
47
- <dishes-list v-if="comp.showType === 'card'" :sku-list="comp.skuList" @select="handleCardTap"/>
48
- <plan-card v-if="comp.showType === 'plan'" @select="handleCardTap({ type: 'match'})" />
49
- </template>
50
- </div>
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">User_{{ uid }} <!--<span class="time">12:30</span>--></div>
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
- <!-- <div ref="endTarget" style="height: 100px;"/>-->
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
- <div class="cui_footer">Powered by DeepSeek</div>
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 mock from './mock';
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: 'https://prodstatic.weis1606.cn/api/smartFood/Nutribite/icons/home/icon_3.png',
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
- // const endTarget = ref(null);
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
- 'https://api.coze.cn/v1/conversation/create',
156
- {'bot_id': props.botId, 'connector_id': '999'},
157
- {...chatOptions.value},
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
- `https://api.coze.cn/v3/chat?conversation_id=${conversationId.value}`,
219
- {
220
- method: 'POST',
221
- headers: {
222
- ...chatOptions.value.headers,
223
- // 'Content-Type': 'text/event-stream',
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 (strObj.hasOwnProperty('content')
289
- && strObj.hasOwnProperty('content_type')
290
- && strObj.content_type === 'text'
291
- && strObj.hasOwnProperty('type')
292
- && strObj.type === 'answer'
293
- && !strObj.hasOwnProperty('created_at')) {
294
- historyList.value[idx].content = handleText(historyList.value[idx].content + strObj.content);
295
- } else if (strObj.hasOwnProperty('type')
296
- && strObj.type === 'tool_response') {
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
- `https://api.coze.cn/v1/conversation/message/list?conversation_id=${conversationId.value}`,
316
- {order: 'asc'},
317
- {...chatOptions.value},
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(c => c.chat_id === card.chat_id && c.role === 'assistant');
365
- if(idx > -1) {
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
- `https://api.coze.cn/v1/bot/get_online_info?bot_id=${props.botId}`,
376
- {...chatOptions.value},
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(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/ig, '<a href="$2" target="_blank">[$1]</a>');
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 handleCardTap = ({type}) => {
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
- // nextTick(() => {
404
- // endTarget.value.scrollIntoView();
405
- // });
469
+ nextTick(() => {
470
+ endTarget.value.scrollIntoView();
471
+ });
406
472
  };
407
473
  </script>
408
474
 
409
475
  <style scoped lang="less">
410
- @import "./ChatUi";
476
+ @import './ChatUi';
411
477
  </style>
@@ -1,8 +1,11 @@
1
1
  @border-color: rgba(68, 83, 130, 0.25);
2
- @border-radius: 8px;
2
+ @border-radius: 10px;
3
3
  @box-max: 290px;
4
4
  @max-width: 630px;
5
- @bg-color: #F3F4F5;
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.category }}</p>
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 v-for="(info, idx) in dList.categoryList" :key="idx" :info="info"/>
8
+ <dishes-card
9
+ v-for="(info, idx) in dList.getCategoryList"
10
+ :key="idx"
11
+ :info="info"
12
+ />
9
13
  </template>
10
14
  </div>
11
15
  </div>
12
16
  </template>
13
17
  <div class="btn_group">
14
- <!-- <div class="btn btn_1" @click.stop="handleBtn('change')">換一套菜品</div>-->
15
- <div class="btn btn_2" @click.stop="handleBtn('order')">去下單</div>
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: #E6F5EB;
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>