ai-chat-bot-interface 1.3.1 → 1.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ai-chat-bot-interface",
3
3
  "private": false,
4
- "version": "1.3.1",
4
+ "version": "1.4.0",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "description": "A AI chat bot interface. (private)",
@@ -17,6 +17,7 @@
17
17
  "bot"
18
18
  ],
19
19
  "dependencies": {
20
+ "v-viewer": "^3.0.21",
20
21
  "vue": "^3.5.13"
21
22
  },
22
23
  "devDependencies": {
package/src/App.vue CHANGED
@@ -5,9 +5,10 @@ import ChatUi from './ChatUi.vue';
5
5
  <template>
6
6
  <div style="width: 100vw; height: 100vh">
7
7
  <chat-ui
8
- bot-id="7471093458661720104"
9
- token="pat_2yVcSFJUZB6c9Kdcv9iktIVFeGRuzyK3bAJY6GcqKrdGxTdjKMd1iDB09ipJ6YX8"
8
+ bot-id="555"
9
+ token="pat_88888"
10
10
  uid="262598"
11
+ :def-msg="{ placeholder: '发消息...' }"
11
12
  :show-header="true"
12
13
  />
13
14
  </div>
package/src/ChatUi.vue CHANGED
@@ -75,7 +75,7 @@
75
75
  </p>
76
76
  </template>
77
77
  <template v-if="conv.content">
78
- <p class="text" v-html="conv.content"></p>
78
+ <assistant-replay :content="conv.content" />
79
79
  <template v-if="!isAnswering">
80
80
  <div v-if="conv.extra.length">
81
81
  <template v-for="(comp, idx) in conv.extra" :key="idx">
@@ -95,6 +95,7 @@
95
95
  <plan-card
96
96
  v-if="comp.showType === 'plan'"
97
97
  :info="comp"
98
+ :def-msg="finalDefMsg"
98
99
  @select="handleCardTap({ type: 'match' }, comp)"
99
100
  />
100
101
  </template>
@@ -136,6 +137,7 @@
136
137
  :new-chat="!showHeader"
137
138
  :token="token"
138
139
  :tag-list="tagList"
140
+ :def-msg="finalDefMsg"
139
141
  @send="chatConv"
140
142
  @tag="handleTagSel"
141
143
  @call="handleCall"
@@ -162,6 +164,7 @@ import LoadingIcon2 from './components/icons/loadingIcon2.vue';
162
164
  import ImgeList from './components/imgeList.vue';
163
165
  import ThinkingIcon from './components/icons/ThinkingIcon.vue';
164
166
  import OkIcon from './components/icons/OkIcon.vue';
167
+ import AssistantReplay from './components/assistantReplay/assistantReplay.vue';
165
168
 
166
169
  const chatOptions = computed(() => {
167
170
  return {
@@ -175,6 +178,14 @@ const conversationId = ref('');
175
178
  const isAnswering = ref(false);
176
179
  const historyList = ref([]);
177
180
 
181
+ const msgObj = {
182
+ placeholder: '發消息⋯',
183
+ fileText: '請根據我上傳的體檢報告為我生成飲食方案',
184
+ uploadingTips: '文件正在上傳,請稍候...',
185
+ matchText: '請用以上方案為我配餐',
186
+ matchContent: '請用以上方案為我配餐',
187
+ };
188
+
178
189
  const props = defineProps({
179
190
  logo: {
180
191
  type: String,
@@ -213,10 +224,21 @@ const props = defineProps({
213
224
  { name: '体检报告', value: 'exam', type: 'upload', msg: '体检报告' },
214
225
  ],
215
226
  },
227
+ defMsg: {
228
+ type: Object,
229
+ default: () => ({}),
230
+ },
216
231
  });
217
232
 
218
233
  const Emits = defineEmits(['call']);
219
234
 
235
+ const finalDefMsg = computed(() => {
236
+ return {
237
+ ...msgObj,
238
+ ...props.defMsg,
239
+ };
240
+ });
241
+
220
242
  const endTarget = ref(null);
221
243
  const inputText = ref('');
222
244
  const botInfo = ref({});
@@ -285,7 +307,7 @@ const createConv = async () => {
285
307
 
286
308
  const chatConv = async (data) => {
287
309
  let isInCode = Array.isArray(data); // data.hasOwnProperty('text') && data.hasOwnProperty('code');
288
- let botId = props.botId;
310
+ const botId = props.botId;
289
311
  if (!(!isAnswering.value && (inputText.value || isInCode))) {
290
312
  return;
291
313
  }
@@ -308,7 +330,7 @@ const chatConv = async (data) => {
308
330
  if (item.hasOwnProperty('content') && item.content) {
309
331
  if (item.hasOwnProperty('type') && item.type === 'object_string') {
310
332
  console.log('=== item ====', item);
311
- botId = '7474884145253023795';
333
+ // botId = '7474884145253023795';
312
334
  additional_messages.push({
313
335
  content: JSON.stringify(
314
336
  item.content.map((con) => ({
@@ -565,11 +587,14 @@ const queryBotInfo = async () => {
565
587
  };
566
588
 
567
589
  const handleText = (str) => {
590
+ return str;
568
591
  // console.log(str);
569
- return str.replaceAll(
570
- /\[([^\]]+)\]\((https?:\/\/[^)]+)\)/gi,
571
- '<a href="$2" target="_blank">[$1]</a>',
572
- );
592
+ // let txt = '';
593
+ //
594
+ // return str.replaceAll(
595
+ // /\[([^\]]+)\]\((https?:\/\/[^)]+)\)/gi,
596
+ // '<a href="$2" target="_blank">[$1]</a>',
597
+ // );
573
598
  };
574
599
  const handleBack = () => {
575
600
  Emits('call', { type: 'back' });
@@ -593,7 +618,10 @@ const handleCardTap = ({ type }, info) => {
593
618
  break;
594
619
  case 'match':
595
620
  chatConv([
596
- { content: '請用以上方案為我配餐', text: '請用以上方案為我配餐' },
621
+ {
622
+ content: finalDefMsg.value.matchContent,
623
+ text: finalDefMsg.value.matchText,
624
+ },
597
625
  ]);
598
626
  break;
599
627
  default:
@@ -52,7 +52,7 @@
52
52
  ref="txtEle"
53
53
  class="input"
54
54
  rows="1"
55
- placeholder="發消息⋯"
55
+ :placeholder="defMsg.placeholder"
56
56
  @input="handleInput"
57
57
  @keyup.enter="sendMsg"
58
58
  />
@@ -140,6 +140,10 @@ const props = defineProps({
140
140
  type: Boolean,
141
141
  default: false,
142
142
  },
143
+ defMsg: {
144
+ type: Object,
145
+ required: true,
146
+ },
143
147
  });
144
148
  const Emits = defineEmits(['update:modelValue', 'send', 'tag', 'call']);
145
149
 
@@ -194,7 +198,7 @@ const sendMsg = () => {
194
198
  uFileList.value.findIndex((f) => f.percent < 100) > -1
195
199
  ) {
196
200
  Emits('call', {
197
- message: '文件正在上傳,請稍候...',
201
+ message: props.defMsg.uploadingTips,
198
202
  reason: 'is_uploading',
199
203
  value: 'toast',
200
204
  type: 'call',
@@ -245,7 +249,7 @@ const handleUpload = async (idx) => {
245
249
  const file = uFileList.value[idx].file;
246
250
  console.log('========', file);
247
251
  if (!textValue.value) {
248
- textValue.value = '請根據我上傳的體檢報告為我生成飲食方案';
252
+ textValue.value = props.defMsg.fileText;
249
253
  }
250
254
  const formDate = new FormData();
251
255
  formDate.append('file', file);
@@ -46,6 +46,10 @@ const props = defineProps({
46
46
  type: Object,
47
47
  required: true,
48
48
  },
49
+ defMsg: {
50
+ type: Object,
51
+ required: true,
52
+ },
49
53
  });
50
54
  const Emits = defineEmits(['select']);
51
55
  const handleSel = () => {
@@ -0,0 +1,92 @@
1
+ <template>
2
+ <p class="text">
3
+ <template v-for="(item, idx) in msgList" :key="idx">
4
+ <template v-if="item.type === 'text'">{{ item.content }}</template>
5
+ <template v-if="item.type === 'link'">
6
+ <a class="link" @click.stop="previewImg(item)">{{ item.content }}</a>
7
+ </template>
8
+ </template>
9
+ </p>
10
+ </template>
11
+
12
+ <script setup>
13
+ import { computed, ref } from 'vue';
14
+ import { api as viewerApi } from 'v-viewer';
15
+
16
+ const props = defineProps({
17
+ content: {
18
+ type: String,
19
+ default: '',
20
+ },
21
+ });
22
+
23
+ const images = ref([]);
24
+ const msgList = computed(() => {
25
+ const regEx = /\[([^\]]+)]\((https?:\/\/[^)]+)\)/gi;
26
+ const matches = [...props.content.matchAll(regEx)];
27
+ let remaining = props.content;
28
+ let pointer = 0;
29
+ const result = [];
30
+ if (matches.length) {
31
+ matches.forEach((match, index) => {
32
+ result.push({
33
+ type: 'text',
34
+ content: match.input.slice(pointer, match.index).replace(regEx, ''),
35
+ });
36
+ result.push({
37
+ type: 'link',
38
+ string: match[0],
39
+ ...JSON.parse(match[0].replace(regEx, '{"content":"[$1]","url":"$2"}')),
40
+ });
41
+ remaining = match.input.slice(match.index).replace(match[0], '');
42
+ pointer = match.index;
43
+ });
44
+ if (remaining.length) {
45
+ result.push({
46
+ type: 'text',
47
+ content: remaining,
48
+ });
49
+ }
50
+ } else {
51
+ result.push({
52
+ type: 'text',
53
+ content: remaining,
54
+ });
55
+ }
56
+ return result;
57
+ });
58
+
59
+ const previewImg = ({ url }) => {
60
+ images.value = [url];
61
+ viewerApi({
62
+ images: images.value,
63
+ options: {
64
+ navbar: false,
65
+ toolbar: {
66
+ flipHorizontal: false,
67
+ flipVertical: false,
68
+ next: false,
69
+ prev: false,
70
+ play: false,
71
+ reset: true,
72
+ oneToOne: true,
73
+ rotateLeft: true,
74
+ rotateRight: true,
75
+ zoomIn: true,
76
+ zoomOut: true,
77
+ },
78
+ },
79
+ });
80
+ };
81
+ </script>
82
+
83
+ <style scoped lang="less">
84
+ .text {
85
+ word-break: break-all;
86
+ white-space: pre-wrap;
87
+ margin: 0;
88
+ }
89
+ .link {
90
+ color: #039938;
91
+ }
92
+ </style>
package/src/main.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createApp } from 'vue';
2
2
  import './style.css';
3
3
  import App from './App.vue';
4
+ import 'viewerjs/dist/viewer.css';
4
5
 
5
6
  createApp(App).mount('#app');