vue2-client 1.14.55 → 1.14.57

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.
@@ -2,35 +2,57 @@
2
2
  <div>
3
3
  <div class="chat-container" v-if="loading">
4
4
  <!-- 对话展示区域 -->
5
- <div class="chat-content">
5
+ <div class="chat-content" ref="chatContent">
6
6
  <div
7
7
  v-for="(message, index) in messages"
8
8
  :key="index"
9
9
  :class="['chat-message', message.type]"
10
10
  >
11
11
  <span class="chat-avatar">{{ message.type === 'user' ? '👤' : '🤖' }}</span>
12
- <div
13
- class="chat-text markdown-body"
14
- v-if="message.text"
15
- v-html="renderMarkdown(message.text)"
16
- ></div>
17
- <div class="chat-text" v-else-if="message.state === 'loading'">
18
- <a-spin :spinning="true"/>
12
+ <div class="chat-text-wrapper">
13
+ <div
14
+ class="chat-text markdown-body"
15
+ v-if="message.text"
16
+ v-html="renderMarkdown(message.text)"
17
+ ></div>
18
+ <div class="chat-text" v-else-if="message.state === 'loading'">
19
+ <a-spin :spinning="true"/>
20
+ </div>
21
+ <a-button
22
+ v-if="message.sourceInfo"
23
+ type="link"
24
+ class="source-info-btn"
25
+ @click="showSourceInfo(message.sourceInfo)"
26
+ >
27
+ 查看源信息
28
+ </a-button>
19
29
  </div>
20
30
  </div>
21
31
  </div>
22
32
 
23
33
  <!-- 输入框和发送按钮 -->
24
- <div class="chat-input">
25
- <a-input
26
- v-model="inputMessage"
27
- placeholder="Type your message..."
28
- @pressEnter="sendMessage"
29
- />
30
- <a-button type="primary" @click="sendMessage">发送</a-button>
34
+ <div class="chat-input-wrapper">
35
+ <div class="chat-input">
36
+ <a-input
37
+ v-model="inputMessage"
38
+ placeholder="Type your message..."
39
+ @pressEnter="sendMessage"
40
+ />
41
+ <a-button type="primary" @click="sendMessage">发送</a-button>
42
+ </div>
31
43
  </div>
32
44
  </div>
33
45
  <a-spin :spinning="!loading"/>
46
+
47
+ <!-- 源信息弹窗 -->
48
+ <a-modal
49
+ v-model="sourceInfoVisible"
50
+ title="源信息"
51
+ :footer="null"
52
+ width="600px"
53
+ >
54
+ <div class="source-info-content markdown-body" v-html="renderMarkdown(currentSourceInfo)"></div>
55
+ </a-modal>
34
56
  </div>
35
57
  </template>
36
58
 
@@ -118,25 +140,32 @@ export default {
118
140
  messages: [ // 消息列表
119
141
  { type: 'bot', text: '你好! 今天有什么能帮到您的?' },
120
142
  ],
143
+ sourceInfoVisible: false,
144
+ currentSourceInfo: '',
121
145
  }
122
146
  },
123
147
  methods: {
124
148
  renderMarkdown (text) {
125
- const markdownPatterns = [
126
- /(^|\s)(#{1,6})\s/, // 标题 (# 到 ######)
127
- /(\*\*|__)(.*?)\1/, // 加粗 (** 或 __)
128
- /(\*|_)(.*?)\1/, // 斜体 (* 或 _)
129
- /\[([^\]]+)\]\([^\)]+\)/, // 链接 [text](url)
130
- /`{1,3}[^`]+`{1,3}/, // 行内代码 (`text`)
131
- /\n{2,}([^\n]+)\n{2,}/, // 段落之间的换行
132
- /\n{3,}([^\n]+)\n{3,}/, // 多行代码块
133
- /[-\*\+]\s+/, // 无序列表 (- 或 * 或 +)
134
- /\d+\.\s+/, // 有序列表 (1. 2. 或 3.)
135
- ]
136
- if (markdownPatterns.some(pattern => pattern.test(text))) {
137
- return marked(text)
138
- } else {
139
- return text
149
+ if (!text) return ''
150
+
151
+ // 预处理文本,处理特殊字符
152
+ const processedText = text
153
+
154
+ try {
155
+ // 配置 marked 选项
156
+ marked.setOptions({
157
+ breaks: true, // 支持换行符
158
+ gfm: true, // 启用 GitHub 风格的 Markdown
159
+ headerIds: false, // 禁用标题 ID
160
+ mangle: false, // 禁用标题 ID 混淆
161
+ sanitize: false // 允许 HTML 标签
162
+ })
163
+ const rendered = marked(processedText)
164
+ this.scrollToBottom()
165
+ return rendered
166
+ } catch (error) {
167
+ console.error('Markdown 渲染错误:', error)
168
+ return processedText
140
169
  }
141
170
  },
142
171
  setAddtionalInfo (params) {
@@ -147,6 +176,14 @@ export default {
147
176
  this.loading = true
148
177
  this.serviceName = params.serviceName || process.env.VUE_APP_SERVICE_NAME
149
178
  },
179
+ scrollToBottom () {
180
+ this.$nextTick(() => {
181
+ const chatContent = this.$refs.chatContent
182
+ if (chatContent) {
183
+ chatContent.scrollTop = chatContent.scrollHeight
184
+ }
185
+ })
186
+ },
150
187
  async sendMessage () {
151
188
  if (this.renderConfig.type === 'AI') {
152
189
  // 验证 chatId 和 aiModel
@@ -159,11 +196,12 @@ export default {
159
196
 
160
197
  // 添加用户消息
161
198
  this.messages.push({ type: 'user', text: this.inputMessage })
199
+ this.scrollToBottom()
162
200
 
163
201
  // 清空输入框
164
202
  const userMessage = this.inputMessage
165
203
  this.inputMessage = ''
166
- // 模拟机器人的回复
204
+
167
205
  // 通过logic获取消息
168
206
  if (this.renderConfig.logicName) {
169
207
  const response = await runLogic(this.renderConfig.logicName, {
@@ -197,13 +235,18 @@ export default {
197
235
  console.error('JSON解析失败:', e)
198
236
  }
199
237
  }
200
- console.log(response.value.response)
201
- this.messages.push({ type: 'bot', text: response.value.response })
238
+ console.log(response.value)
239
+ this.messages.push({
240
+ type: 'bot',
241
+ text: response.value,
242
+ sourceInfo: response.sourceInfo || ''
243
+ })
202
244
  } else if (this.renderConfig.api) {
203
245
  // 通过api获取消息
204
- this.messages.push({ type: 'bot', state: 'loading' })
246
+ this.messages.push({ type: 'bot', state: 'loading', sourceInfo: '' })
205
247
  this.messages[this.messages.length - 1].text = await postByServiceName(this.renderConfig.api, {
206
248
  userMessage,
249
+ additionalInfo: this.additionalInfo,
207
250
  model: this.renderConfig.aiModel,
208
251
  chatId: this.renderConfig.chatId || moment().format('YYYYMMDDHHmmss'),
209
252
  prompt: this.prompt
@@ -211,17 +254,61 @@ export default {
211
254
  this.$forceUpdate()
212
255
  } else if (this.renderConfig.streamApi) {
213
256
  // 通过流式api 获取消息
214
- this.messages.push({ type: 'bot', state: 'loading' })
257
+ this.messages.push({ type: 'bot', state: 'loading', sourceInfo: '' })
258
+
259
+ // 如果存在之前的连接,先关闭它
260
+ if (this.stopConnection) {
261
+ this.stopConnection()
262
+ }
263
+
264
+ let currentMessage = ''
215
265
  this.stopConnection = startEventStreamPOST(
216
266
  this.renderConfig.streamApi,
217
267
  {
218
268
  userMessage,
219
269
  model: this.renderConfig.aiModel,
220
270
  chatId: this.renderConfig.chatId || moment().format('YYYYMMDDHHmmss'),
221
- prompt: this.prompt
222
- }, {},
223
- (data) => {
224
- this.messages[this.messages.length - 1].text = `${this.messages[this.messages.length - 1]?.text || ''}${data}`
271
+ prompt: this.prompt,
272
+ additionalInfo: this.additionalInfo
273
+ },
274
+ {},
275
+ (data, type) => {
276
+ // 处理 additionalInfo 事件
277
+ if (type === 'additionalInfo') {
278
+ console.log('收到 additionalInfo:', data)
279
+ if (data) {
280
+ // 更新 additionalInfo
281
+ this.additionalInfo = {
282
+ ...this.additionalInfo,
283
+ ...data
284
+ }
285
+ // 触发全局数据更新
286
+ this.setGlobalData?.(this.additionalInfo)
287
+ }
288
+ return
289
+ }
290
+
291
+ if (type === 'sourceInfo') {
292
+ console.log('收到 sourceInfo:', data)
293
+ if (data) {
294
+ // 更新 sourceInfo
295
+ this.messages[this.messages.length - 1].sourceInfo = data.replace(/<br\s*\/?>/gi, '\n')
296
+ }
297
+ return
298
+ }
299
+ // 处理普通消息
300
+ if (this.messages[this.messages.length - 1].state === 'loading') {
301
+ this.messages[this.messages.length - 1].state = undefined
302
+ }
303
+ currentMessage += data
304
+ this.messages[this.messages.length - 1].text = currentMessage.replace(/<br\s*\/?>/gi, '\n')
305
+ this.renderMarkdown(this.messages[this.messages.length - 1].text)
306
+ this.$forceUpdate()
307
+ },
308
+ (error) => {
309
+ console.error('流式请求错误:', error)
310
+ this.messages[this.messages.length - 1].state = undefined
311
+ this.messages[this.messages.length - 1].text = '抱歉,消息发送失败,请重试。'
225
312
  this.$forceUpdate()
226
313
  }
227
314
  )
@@ -238,7 +325,14 @@ export default {
238
325
  text: msg.content
239
326
  })
240
327
  })
241
- }
328
+ this.scrollToBottom()
329
+ },
330
+ showSourceInfo (sourceInfo) {
331
+ this.currentSourceInfo = typeof sourceInfo === 'string'
332
+ ? sourceInfo
333
+ : JSON.stringify(sourceInfo, null, 2)
334
+ this.sourceInfoVisible = true
335
+ },
242
336
  },
243
337
  mounted () {
244
338
  this.setAddtionalInfo(this.getMixinData())
@@ -247,6 +341,20 @@ export default {
247
341
  if (this.stopConnection) {
248
342
  this.stopConnection()
249
343
  }
344
+ },
345
+ watch: {
346
+ messages: {
347
+ handler () {
348
+ this.scrollToBottom()
349
+ },
350
+ deep: true
351
+ },
352
+ 'messages.$[].text': {
353
+ handler () {
354
+ this.scrollToBottom()
355
+ },
356
+ deep: true
357
+ }
250
358
  }
251
359
  }
252
360
  </script>
@@ -255,10 +363,11 @@ export default {
255
363
  .chat-container {
256
364
  display: flex;
257
365
  flex-direction: column;
258
- height: 500px;
366
+ height: 600px; /* 设置最小高度 */
259
367
  border: 1px solid #d9d9d9;
260
368
  border-radius: 4px;
261
369
  overflow: hidden;
370
+ position: relative;
262
371
  }
263
372
 
264
373
  .chat-content {
@@ -266,6 +375,7 @@ export default {
266
375
  padding: 16px;
267
376
  overflow-y: auto;
268
377
  background: #f5f5f5;
378
+ padding-bottom: 60px; /* 为底部输入框留出空间 */
269
379
  }
270
380
 
271
381
  .chat-message {
@@ -286,8 +396,13 @@ export default {
286
396
  margin: 0 8px;
287
397
  }
288
398
 
289
- .chat-text {
399
+ .chat-text-wrapper {
400
+ display: flex;
401
+ flex-direction: column;
290
402
  max-width: 70%;
403
+ }
404
+
405
+ .chat-text {
291
406
  padding: 8px 12px;
292
407
  border-radius: 4px;
293
408
  background-color: #fff;
@@ -298,11 +413,18 @@ export default {
298
413
  background-color: #e6f7ff;
299
414
  }
300
415
 
301
- .chat-input {
302
- display: flex;
303
- padding: 8px;
416
+ .chat-input-wrapper {
417
+ position: absolute;
418
+ bottom: 0;
419
+ left: 0;
420
+ right: 0;
304
421
  background: #fff;
305
422
  border-top: 1px solid #d9d9d9;
423
+ padding: 8px;
424
+ }
425
+
426
+ .chat-input {
427
+ display: flex;
306
428
  gap: 10px;
307
429
  }
308
430
 
@@ -357,4 +479,98 @@ code {
357
479
  padding-left: 2em;
358
480
  margin-bottom: 16px;
359
481
  }
482
+
483
+ /* 按钮样式 */
484
+ .markdown-body :deep(button) {
485
+ display: inline-block;
486
+ padding: 4px 15px;
487
+ font-size: 14px;
488
+ font-weight: 400;
489
+ line-height: 1.5715;
490
+ text-align: center;
491
+ white-space: nowrap;
492
+ background: #fff;
493
+ border: 1px solid #d9d9d9;
494
+ border-radius: 2px;
495
+ cursor: pointer;
496
+ transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
497
+ user-select: none;
498
+ touch-action: manipulation;
499
+ margin: 4px 8px 4px 0;
500
+ }
501
+
502
+ .markdown-body :deep(button:hover) {
503
+ color: #40a9ff;
504
+ border-color: #40a9ff;
505
+ background: #fff;
506
+ }
507
+
508
+ .markdown-body :deep(button:active) {
509
+ color: #096dd9;
510
+ border-color: #096dd9;
511
+ background: #fff;
512
+ }
513
+
514
+ /* Markdown 按钮样式 */
515
+ .markdown-button {
516
+ display: inline-block;
517
+ padding: 4px 15px;
518
+ font-size: 14px;
519
+ font-weight: 400;
520
+ line-height: 1.5715;
521
+ text-align: center;
522
+ white-space: nowrap;
523
+ background: #fff;
524
+ border: 1px solid #d9d9d9;
525
+ border-radius: 2px;
526
+ cursor: pointer;
527
+ transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
528
+ user-select: none;
529
+ touch-action: manipulation;
530
+ margin: 4px 8px 4px 0;
531
+ }
532
+
533
+ .markdown-button:hover {
534
+ color: #40a9ff;
535
+ border-color: #40a9ff;
536
+ background: #fff;
537
+ }
538
+
539
+ .markdown-button:active {
540
+ color: #096dd9;
541
+ border-color: #096dd9;
542
+ background: #fff;
543
+ }
544
+
545
+ .source-info-btn {
546
+ font-size: 12px;
547
+ padding: 0;
548
+ margin-top: 4px;
549
+ align-self: flex-start;
550
+ }
551
+
552
+ .source-info-content {
553
+ background-color: #f5f5f5;
554
+ padding: 16px;
555
+ border-radius: 4px;
556
+ max-height: 400px;
557
+ overflow-y: auto;
558
+ white-space: pre-wrap;
559
+ word-break: break-all;
560
+ }
561
+
562
+ .source-info-content :deep(pre) {
563
+ background-color: #f6f8fa;
564
+ border-radius: 6px;
565
+ padding: 16px;
566
+ overflow: auto;
567
+ margin: 0;
568
+ }
569
+
570
+ .source-info-content :deep(code) {
571
+ background-color: #f6f8fa;
572
+ border-radius: 3px;
573
+ padding: 0.2em 0.4em;
574
+ font-size: 85%;
575
+ }
360
576
  </style>
@@ -6,6 +6,7 @@
6
6
  :labelCol="labelCol"
7
7
  :flex="attr.flex">
8
8
  <a-form-model-item
9
+ v-bind="bindOther"
9
10
  :rules="rules"
10
11
  :ref="attr.model"
11
12
  :label="showLabel?attr.name:undefined"
@@ -82,6 +83,7 @@
82
83
  :labelCol="labelCol"
83
84
  :flex="attr.flex">
84
85
  <a-form-model-item
86
+ v-bind="bindOther"
85
87
  :rules="rules"
86
88
  v-if="!attr.showMode || mode === '查询' || attr.showMode === 'select' "
87
89
  :ref="attr.model"
@@ -190,6 +192,7 @@
190
192
  </a-select>
191
193
  </a-form-model-item>
192
194
  <a-form-model-item
195
+ v-bind="bindOther"
193
196
  :rules="rules"
194
197
  v-else-if="attr.showMode === 'radioGroup'"
195
198
  :ref="attr.model"
@@ -202,6 +205,7 @@
202
205
  </a-radio-group>
203
206
  </a-form-model-item>
204
207
  <a-form-model-item
208
+ v-bind="bindOther"
205
209
  v-else-if="attr.showMode === 'clickChange' && option.length > 0"
206
210
  :ref="attr.model"
207
211
  :label="showLabel?attr.name:undefined"
@@ -215,6 +219,7 @@
215
219
  :labelCol="labelCol"
216
220
  :flex="attr.flex">
217
221
  <a-form-model-item
222
+ v-bind="bindOther"
218
223
  :rules="rules"
219
224
  v-if="!attr.showMode || mode === '查询' || attr.showMode === 'select' "
220
225
  :ref="attr.model"
@@ -287,6 +292,7 @@
287
292
  </a-select>
288
293
  </a-form-model-item>
289
294
  <a-form-model-item
295
+ v-bind="bindOther"
290
296
  :rules="rules"
291
297
  v-else
292
298
  :ref="attr.model"
@@ -305,6 +311,7 @@
305
311
  :labelCol="labelCol"
306
312
  :flex="attr.flex">
307
313
  <a-form-model-item
314
+ v-bind="bindOther"
308
315
  :rules="rules"
309
316
  v-if="!attr.showMode || attr.type === 'radio' "
310
317
  :ref="attr.model"
@@ -336,6 +343,7 @@
336
343
  </a-radio-group>
337
344
  </a-form-model-item>
338
345
  <a-form-model-item
346
+ v-bind="bindOther"
339
347
  :rules="rules"
340
348
  v-else-if="attr.showMode === 'radioGroup'"
341
349
  :ref="attr.model"
@@ -348,6 +356,7 @@
348
356
  </a-radio-group>
349
357
  </a-form-model-item>
350
358
  <a-form-model-item
359
+ v-bind="bindOther"
351
360
  :rules="rules"
352
361
  v-else-if="attr.showMode === 'clickChange' && option.length > 0"
353
362
  :ref="attr.model"
@@ -362,6 +371,7 @@
362
371
  :labelCol="labelCol"
363
372
  :flex="attr.flex">
364
373
  <a-form-model-item
374
+ v-bind="bindOther"
365
375
  :rules="rules"
366
376
  :ref="attr.model"
367
377
  :label="showLabel?attr.name:undefined"
@@ -383,6 +393,7 @@
383
393
  :flex="attr.flex">
384
394
  <!-- :style="layout === 'inline'?{width:'calc(100% - 60px)'}:{}"-->
385
395
  <a-form-model-item
396
+ v-bind="bindOther"
386
397
  :rules="rules"
387
398
  :ref="attr.model"
388
399
  :label="showLabel?attr.name:undefined"
@@ -401,6 +412,7 @@
401
412
  :labelCol="labelCol"
402
413
  :flex="attr.flex">
403
414
  <a-form-model-item
415
+ v-bind="bindOther"
404
416
  :rules="rules"
405
417
  :ref="attr.model"
406
418
  :label="showLabel?attr.name:undefined"
@@ -420,6 +432,7 @@
420
432
  :labelCol="labelCol"
421
433
  :flex="attr.flex">
422
434
  <a-form-model-item
435
+ v-bind="bindOther"
423
436
  :rules="rules"
424
437
  :ref="attr.model"
425
438
  :label="showLabel?attr.name:undefined"
@@ -440,6 +453,7 @@
440
453
  :occupyCol="attr.occupyCol"
441
454
  :flex="attr.flex">
442
455
  <a-form-model-item
456
+ v-bind="bindOther"
443
457
  :rules="rules"
444
458
  :ref="attr.model"
445
459
  :label="showLabel?attr.name:undefined"
@@ -463,6 +477,7 @@
463
477
  :labelCol="labelCol"
464
478
  :flex="attr.flex">
465
479
  <a-form-model-item
480
+ v-bind="bindOther"
466
481
  :rules="rules"
467
482
  :ref="attr.model"
468
483
  :label="showLabel?attr.name:undefined"
@@ -490,6 +505,7 @@
490
505
  :labelCol="labelCol"
491
506
  :flex="attr.flex">
492
507
  <a-form-model-item
508
+ v-bind="bindOther"
493
509
  :rules="rules"
494
510
  :ref="attr.model"
495
511
  :label="showLabel?attr.name:undefined"
@@ -551,6 +567,7 @@
551
567
  :labelCol="labelCol"
552
568
  :flex="attr.flex">
553
569
  <a-form-model-item
570
+ v-bind="bindOther"
554
571
  :rules="rules"
555
572
  :ref="attr.model"
556
573
  :label="showLabel?attr.name:undefined"
@@ -574,6 +591,7 @@
574
591
  :labelCol="labelCol"
575
592
  :flex="attr.flex">
576
593
  <a-form-model-item
594
+ v-bind="bindOther"
577
595
  :rules="rules"
578
596
  :ref="attr.model"
579
597
  :label="showLabel?attr.name:undefined"
@@ -596,6 +614,7 @@
596
614
  :labelCol="labelCol"
597
615
  :flex="attr.flex">
598
616
  <a-form-model-item
617
+ v-bind="bindOther"
599
618
  :rules="rules"
600
619
  :ref="attr.model"
601
620
  :label="showLabel?attr.name:undefined"
@@ -636,6 +655,7 @@
636
655
  :labelCol="labelCol"
637
656
  :flex="attr.flex">
638
657
  <a-form-model-item
658
+ v-bind="bindOther"
639
659
  :rules="rules"
640
660
  :ref="attr.model"
641
661
  :label="showLabel?attr.name:undefined"
@@ -714,7 +734,8 @@ export default {
714
734
  // 行选择器浮层是否显示
715
735
  rowChoosePopoverVisible: false,
716
736
  // 行选择器CRUD固定查询值
717
- rowChooseFixedQueryValue: undefined
737
+ rowChooseFixedQueryValue: undefined,
738
+ bindOther: {}
718
739
  }
719
740
  },
720
741
  props: {