vue2-client 1.22.19 → 1.22.21

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.
Files changed (88) hide show
  1. package/.env.his +19 -19
  2. package/.env.message +19 -19
  3. package/.history/.eslintrc_20260521171150.js +74 -0
  4. package/.history/.eslintrc_20260521171213.js +74 -0
  5. package/.history/src/base-client/components/common/HIS/HAddNativeForm/HAddNativeForm_20260601154443.vue +726 -0
  6. package/.history/src/base-client/components/common/HIS/HAddNativeForm/HAddNativeForm_20260601154700.vue +478 -0
  7. package/.history/src/base-client/components/common/HIS/HButtons/HButtons_20260512175435.vue +706 -0
  8. package/.history/src/base-client/components/common/HIS/HButtons/HButtons_20260512175450.vue +694 -0
  9. package/.history/src/base-client/components/common/HIS/HButtons/HButtons_20260611152602.vue +755 -0
  10. package/.history/src/base-client/components/common/HIS/HForm/HForm_20260513145941.vue +524 -0
  11. package/.history/src/base-client/components/common/HIS/HForm/HForm_20260513153133.vue +731 -0
  12. package/.history/src/base-client/components/common/HIS/HForm/HForm_20260513160316.vue +525 -0
  13. package/.history/src/base-client/components/common/HIS/HForm/HForm_20260601144150.vue +1046 -0
  14. package/.history/src/base-client/components/common/HIS/HFormTable/HFormTable_20260310142713.vue +512 -0
  15. package/.history/src/base-client/components/common/HIS/HFormTable/HFormTable_20260310145118.vue +511 -0
  16. package/.history/src/base-client/components/common/HIS/HFormTable/HFormTable_20260311094834.vue +696 -0
  17. package/.history/src/base-client/components/common/HIS/HFormTable/HFormTable_20260320143028.vue +693 -0
  18. package/.history/src/base-client/components/common/HIS/HFormTable/HFormTable_20260409101450.vue +677 -0
  19. package/.history/src/base-client/components/common/HIS/HTab/HTab_20260625173703.vue +767 -0
  20. package/.history/src/base-client/components/common/HIS/HTab/HTab_20260626112425.vue +803 -0
  21. package/.history/src/base-client/components/common/XAddNativeForm/XAddNativeForm_20260513140637.vue +1455 -0
  22. package/.history/src/base-client/components/common/XAddNativeForm/XAddNativeForm_20260513140935.vue +1441 -0
  23. package/.history/src/base-client/components/common/XAddNativeForm/XAddNativeForm_20260513150818.vue +1441 -0
  24. package/.history/src/base-client/components/common/XAddNativeForm/XAddNativeForm_20260513153119.vue +1442 -0
  25. package/.history/src/base-client/components/common/XAddNativeForm/XAddNativeForm_20260513153126.vue +1486 -0
  26. package/.history/src/base-client/components/common/XForm/XFormItem_20260513140854.vue +1607 -0
  27. package/.history/src/base-client/components/common/XMarkdownViewer/XMarkdownViewer_20260519140403.vue +643 -0
  28. package/.history/src/base-client/components/common/XMarkdownViewer/XMarkdownViewer_20260519140829.vue +628 -0
  29. package/.history/src/base-client/components/common/XMarkdownViewer/demo_20260519142824.vue +104 -0
  30. package/.history/src/base-client/components/common/XMarkdownViewer/demo_20260519143155.vue +102 -0
  31. package/.history/src/base-client/components/common/XReportGrid/XReport_20260309171231.vue +1241 -0
  32. package/.history/src/base-client/components/common/XReportGrid/XReport_20260309171441.vue +1223 -0
  33. package/.history/src/base-client/components/his/HAi/HAi_20260612174826.vue +472 -0
  34. package/.history/src/base-client/components/his/HAi/HAi_20260612175839.vue +538 -0
  35. package/.history/src/base-client/components/his/HAi/HAi_20260615103331.vue +650 -0
  36. package/.history/src/base-client/components/his/HAi/HAi_20260618140214.vue +1396 -0
  37. package/.history/src/base-client/components/his/HAi/HAi_20260618140556.vue +1396 -0
  38. package/.history/src/base-client/components/his/XHDescriptions/XHDescriptions_20260424134504.vue +1469 -0
  39. package/.history/src/base-client/components/his/XSidebar/XSidebar_20260610171133.vue +788 -0
  40. package/.history/src/base-client/components/his/XSidebar/XSidebar_20260610171151.vue +780 -0
  41. package/.history/src/base-client/components/his/XTransfer/XTransfer_20260511170841.vue +585 -0
  42. package/.history/src/base-client/components/his/XTransfer/XTransfer_20260511171138.vue +787 -0
  43. package/.history/src/base-client/components/his/XTransfer/XTransfer_20260512141830.vue +739 -0
  44. package/.history/src/components/STable/index_20260409155138.js +806 -0
  45. package/.history/src/components/STable/index_20260409155218.js +814 -0
  46. package/.history/src/expression/core/Expression_20260305164427.js +1371 -0
  47. package/.history/src/expression/core/Expression_20260305170258.js +1358 -0
  48. package/.history/src/expression/core/Program_20260305111830.js +944 -0
  49. package/.history/src/expression/core/Program_20260305112041.js +931 -0
  50. package/.history/src/logic/LogicRunner_20260304154306.js +170 -0
  51. package/.history/src/logic/LogicRunner_20260304155553.js +112 -0
  52. package/.history/src/logic/LogicRunner_20260305105834.js +112 -0
  53. package/.history/src/logic/LogicRunner_20260305112718.js +129 -0
  54. package/.history/src/logic/LogicRunner_20260305182436.js +133 -0
  55. package/.history/src/logic/LogicRunner_20260306151301.js +213 -0
  56. package/.history/src/logic/LogicRunner_20260306152419.js +213 -0
  57. package/.history/src/logic/plugins/common/DateTools_20260305154159.js +61 -0
  58. package/.history/src/logic/plugins/common/DateTools_20260305154217.js +44 -0
  59. package/.history/src/logic/plugins/common/DateTools_20260305161014.js +44 -0
  60. package/.history/src/logic/plugins/common/HttpTools_20260305164352.js +80 -0
  61. package/.history/src/logic/plugins/common/HttpTools_20260305170258.js +75 -0
  62. package/.history/src/logic/plugins/common/HttpTools_20260305171634.js +75 -0
  63. package/.history/src/logic/plugins/common/HttpTools_20260306152419.js +72 -0
  64. package/.history/src/services/api/restTools_20260427142149.js +245 -0
  65. package/.history/src/services/api/restTools_20260427142853.js +230 -0
  66. package/.history/src/services/api/restTools_20260519135558.js +230 -0
  67. package/.history/src/services/api/restTools_20260519140825.js +230 -0
  68. package/.history/src/services/api/restTools_20260519151223.js +230 -0
  69. package/.history/src/utils/indexedDB_20260306150918.js +593 -0
  70. package/.history/src/utils/indexedDB_20260306151301.js +586 -0
  71. package/.idea/af-vue2-client.iml +4 -7
  72. package/.idea/codeStyles/Project.xml +62 -0
  73. package/.idea/codeStyles/codeStyleConfig.xml +5 -0
  74. package/.idea/misc.xml +6 -0
  75. package/CHANGELOG.md +837 -837
  76. package/package.json +117 -117
  77. package/preview-input-box.html +180 -0
  78. package/src/base-client/components/common/HIS/HForm/HForm.vue +1186 -1186
  79. package/src/base-client/components/common/HIS/HTab/HTab.vue +790 -757
  80. package/src/base-client/components/common/ImagePreviewModal/index.js +3 -3
  81. package/src/base-client/components/common/XMarkdownViewer/demo.vue +102 -102
  82. package/src/base-client/components/his/HAi/HAi.vue +159 -30
  83. package/src/base-client/components/his/XTransfer/index.md +327 -327
  84. package/src/components/MarkdownEditor/demo.vue +300 -300
  85. package/src/components/MarkdownEditor/index.vue +590 -590
  86. package/src/router/async/router.map.js +141 -141
  87. package/src/services/api/restTools.js +1 -1
  88. package/.idea/inspectionProfiles/Project_Default.xml +0 -6
@@ -1,3 +1,3 @@
1
- import ImagePreviewModal from './ImagePreviewModal'
2
-
3
- export default ImagePreviewModal
1
+ import ImagePreviewModal from './ImagePreviewModal'
2
+
3
+ export default ImagePreviewModal
@@ -1,102 +1,102 @@
1
- <template>
2
- <div style="padding: 16px">
3
- <h2>XMarkdownViewer Demo</h2>
4
-
5
- <div style="margin: 12px 0; padding: 12px; background: #f5f5f5; border-radius: 4px">
6
- <h4 style="margin: 0 0 8px 0">流式 AI 调用示例</h4>
7
- <div style="margin-bottom: 8px">
8
- <input
9
- v-model="userMessage"
10
- type="text"
11
- placeholder="输入消息"
12
- style="width: 300px; padding: 6px 8px; border: 1px solid #ddd; border-radius: 4px"
13
- @keyup.enter="sendMessage"
14
- >
15
- <button
16
- type="button"
17
- style="margin-left: 8px; padding: 6px 16px; background: #1890ff; color: #fff; border: none; border-radius: 4px; cursor: pointer"
18
- @click="sendMessage"
19
- >
20
- 发送
21
- </button>
22
- <button
23
- v-if="isStreaming"
24
- type="button"
25
- style="margin-left: 8px; padding: 6px 16px; background: #ff4d4f; color: #fff; border: none; border-radius: 4px; cursor: pointer"
26
- @click="stopMessage"
27
- >
28
- 停止
29
- </button>
30
- </div>
31
- <div v-if="isStreaming" style="color: #1890ff; font-size: 12px">AI 正在思考中...</div>
32
- </div>
33
-
34
- <div style="margin: 8px 0; color: #666; font-size: 12px">
35
- <span v-if="fileName">当前文件:{{ fileName }}({{ md.length }} 字符)</span>
36
- <span v-else-if="md">当前内容:流式响应({{ md.length }} 字符)</span>
37
- <span v-else>请输入消息并点击发送</span>
38
- </div>
39
-
40
- <x-markdown-viewer ref="viewer" :markdown="md" />
41
- </div>
42
- </template>
43
-
44
- <script>
45
- import XMarkdownViewer from './XMarkdownViewer.vue'
46
-
47
- export default {
48
- name: 'XMarkdownViewerDemo',
49
- components: {
50
- XMarkdownViewer
51
- },
52
- data () {
53
- return {
54
- md: '',
55
- fileName: '',
56
- userMessage: '',
57
- isStreaming: false,
58
- currentStream: null
59
- }
60
- },
61
- methods: {
62
- async sendMessage () {
63
- const message = this.userMessage.trim()
64
- if (!message) return
65
-
66
- const viewer = this.$refs.viewer
67
- if (!viewer || typeof viewer.callAiStream !== 'function') {
68
- console.warn('[Demo] viewer 不存在或没有 callAiStream 方法')
69
- return
70
- }
71
-
72
- // 开始时显示提示
73
- viewer.receiveMarkdownString('### 思考中,请稍后...\n')
74
- this.isStreaming = true
75
-
76
- // 调用流式接口
77
- const stream = viewer.callAiStream(message, {
78
- onChunk: (text) => {
79
- viewer.receiveMarkdownString('### 思考中,请稍后...\n\n' + viewer.getStreamContent())
80
- },
81
- onEnd: (fullContent) => {
82
- viewer.receiveMarkdownString(fullContent)
83
- this.isStreaming = false
84
- },
85
- onError: (error) => {
86
- viewer.receiveMarkdownString('### 抱歉,发生了错误\n\n' + error)
87
- this.isStreaming = false
88
- }
89
- })
90
-
91
- this.currentStream = stream
92
- },
93
- stopMessage () {
94
- if (this.currentStream && typeof this.currentStream.stop === 'function') {
95
- this.currentStream.stop()
96
- this.isStreaming = false
97
- }
98
- }
99
- }
100
- }
101
- </script>
102
-
1
+ <template>
2
+ <div style="padding: 16px">
3
+ <h2>XMarkdownViewer Demo</h2>
4
+
5
+ <div style="margin: 12px 0; padding: 12px; background: #f5f5f5; border-radius: 4px">
6
+ <h4 style="margin: 0 0 8px 0">流式 AI 调用示例</h4>
7
+ <div style="margin-bottom: 8px">
8
+ <input
9
+ v-model="userMessage"
10
+ type="text"
11
+ placeholder="输入消息"
12
+ style="width: 300px; padding: 6px 8px; border: 1px solid #ddd; border-radius: 4px"
13
+ @keyup.enter="sendMessage"
14
+ >
15
+ <button
16
+ type="button"
17
+ style="margin-left: 8px; padding: 6px 16px; background: #1890ff; color: #fff; border: none; border-radius: 4px; cursor: pointer"
18
+ @click="sendMessage"
19
+ >
20
+ 发送
21
+ </button>
22
+ <button
23
+ v-if="isStreaming"
24
+ type="button"
25
+ style="margin-left: 8px; padding: 6px 16px; background: #ff4d4f; color: #fff; border: none; border-radius: 4px; cursor: pointer"
26
+ @click="stopMessage"
27
+ >
28
+ 停止
29
+ </button>
30
+ </div>
31
+ <div v-if="isStreaming" style="color: #1890ff; font-size: 12px">AI 正在思考中...</div>
32
+ </div>
33
+
34
+ <div style="margin: 8px 0; color: #666; font-size: 12px">
35
+ <span v-if="fileName">当前文件:{{ fileName }}({{ md.length }} 字符)</span>
36
+ <span v-else-if="md">当前内容:流式响应({{ md.length }} 字符)</span>
37
+ <span v-else>请输入消息并点击发送</span>
38
+ </div>
39
+
40
+ <x-markdown-viewer ref="viewer" :markdown="md" />
41
+ </div>
42
+ </template>
43
+
44
+ <script>
45
+ import XMarkdownViewer from './XMarkdownViewer.vue'
46
+
47
+ export default {
48
+ name: 'XMarkdownViewerDemo',
49
+ components: {
50
+ XMarkdownViewer
51
+ },
52
+ data () {
53
+ return {
54
+ md: '',
55
+ fileName: '',
56
+ userMessage: '',
57
+ isStreaming: false,
58
+ currentStream: null
59
+ }
60
+ },
61
+ methods: {
62
+ async sendMessage () {
63
+ const message = this.userMessage.trim()
64
+ if (!message) return
65
+
66
+ const viewer = this.$refs.viewer
67
+ if (!viewer || typeof viewer.callAiStream !== 'function') {
68
+ console.warn('[Demo] viewer 不存在或没有 callAiStream 方法')
69
+ return
70
+ }
71
+
72
+ // 开始时显示提示
73
+ viewer.receiveMarkdownString('### 思考中,请稍后...\n')
74
+ this.isStreaming = true
75
+
76
+ // 调用流式接口
77
+ const stream = viewer.callAiStream(message, {
78
+ onChunk: (text) => {
79
+ viewer.receiveMarkdownString('### 思考中,请稍后...\n\n' + viewer.getStreamContent())
80
+ },
81
+ onEnd: (fullContent) => {
82
+ viewer.receiveMarkdownString(fullContent)
83
+ this.isStreaming = false
84
+ },
85
+ onError: (error) => {
86
+ viewer.receiveMarkdownString('### 抱歉,发生了错误\n\n' + error)
87
+ this.isStreaming = false
88
+ }
89
+ })
90
+
91
+ this.currentStream = stream
92
+ },
93
+ stopMessage () {
94
+ if (this.currentStream && typeof this.currentStream.stop === 'function') {
95
+ this.currentStream.stop()
96
+ this.isStreaming = false
97
+ }
98
+ }
99
+ }
100
+ }
101
+ </script>
102
+
@@ -8,7 +8,7 @@
8
8
  v-for="(navItem, navIndex) in navMessages"
9
9
  :key="`nav-${navIndex}`"
10
10
  class="nav-dot"
11
- :class="{ active: activeNavIndex === navIndex }"
11
+ :class="{ active: activeNavIndex === navItem.index }"
12
12
  :title="navItem.content"
13
13
  @click="scrollToMessage(navItem.index)"
14
14
  />
@@ -184,6 +184,7 @@ const BAICHUAN_API_KEY = 'sk-c6368da98129d38b74d7df85a2be8414'
184
184
 
185
185
  export default {
186
186
  name: 'AIConsultation',
187
+ emits: ['favorite', 'delete'],
187
188
  data() {
188
189
  return {
189
190
  currentDialogId: null,
@@ -203,7 +204,8 @@ export default {
203
204
  previewImageUrl: '',
204
205
  activeNavIndex: -1,
205
206
  uploading: false,
206
- uploadProgress: 0
207
+ uploadProgress: 0,
208
+ editingMessage: null
207
209
  }
208
210
  },
209
211
  computed: {
@@ -248,34 +250,131 @@ export default {
248
250
  copyMessage(item) {
249
251
  // 复制消息内容
250
252
  const text = item.content || ''
251
- navigator.clipboard.writeText(text).then(() => {
253
+ if (!text) {
254
+ this.$message.warning('无内容可复制')
255
+ return
256
+ }
257
+ // 兼容方式复制文本
258
+ const textarea = document.createElement('textarea')
259
+ textarea.value = text
260
+ textarea.style.position = 'fixed'
261
+ textarea.style.opacity = '0'
262
+ document.body.appendChild(textarea)
263
+ textarea.select()
264
+ try {
265
+ document.execCommand('copy')
252
266
  this.$message.success('已复制')
253
- }).catch(() => {
267
+ } catch {
254
268
  this.$message.error('复制失败')
255
- })
269
+ } finally {
270
+ document.body.removeChild(textarea)
271
+ }
256
272
  },
257
273
 
258
274
  editMessage(item) {
259
- // 修改消息 - 将消息内容填充到输入框
260
- this.doctorInputText = item.content || ''
275
+ const group = this.getNeighborGroup(item)
276
+ const userItem = group.find(msg => msg.type === 'user')
277
+ if (!userItem) {
278
+ this.$message.warning('未找到对应的用户消息')
279
+ return
280
+ }
281
+ this.editingMessage = userItem
282
+ this.doctorInputText = userItem.content
283
+ this.autoResizeTextarea()
261
284
  },
262
285
 
263
286
  favoriteMessage(item) {
264
- // 收藏消息
265
- this.$message.success('已收藏')
287
+ const group = this.getNeighborGroup(item)
288
+ this.$emit('favorite', group)
266
289
  },
267
290
 
268
291
  deleteMessage(item) {
269
- // 删除消息
270
- const index = this.dialogMessages.indexOf(item)
271
- if (index > -1) {
272
- this.dialogMessages.splice(index, 1)
292
+ const group = this.getNeighborGroup(item)
293
+ this.$emit('delete', group)
294
+ if (!group.length) return
295
+ const removeIds = new Set(group.map(msg => msg.messageId).filter(Boolean))
296
+ const removeObjects = new Set(group)
297
+ this.dialogMessages = this.dialogMessages.filter(msg => {
298
+ if (removeObjects.has(msg)) {
299
+ if (msg.attachments) {
300
+ msg.attachments.forEach(file => {
301
+ if (file.url && file.url.startsWith('blob:')) {
302
+ URL.revokeObjectURL(file.url)
303
+ }
304
+ })
305
+ }
306
+ return false
307
+ }
308
+ if (msg.messageId && removeIds.has(msg.messageId)) {
309
+ return false
310
+ }
311
+ return true
312
+ })
313
+ },
314
+
315
+ getNeighborGroup(item) {
316
+ const idx = this.dialogMessages.findIndex(msg => msg === item || (msg.messageId && msg.messageId === item.messageId))
317
+ if (idx === -1) return [item]
318
+
319
+ const current = this.dialogMessages[idx]
320
+ if (current.type === 'ai') {
321
+ for (let i = idx - 1; i >= 0; i--) {
322
+ if (this.dialogMessages[i].type === 'user') {
323
+ return [this.dialogMessages[i], current]
324
+ }
325
+ }
326
+ return [current]
327
+ } else {
328
+ for (let i = idx + 1; i < this.dialogMessages.length; i++) {
329
+ if (this.dialogMessages[i].type === 'ai') {
330
+ return [current, this.dialogMessages[i]]
331
+ }
332
+ }
333
+ return [current]
273
334
  }
274
335
  },
275
336
 
276
337
  regenerateAiResponse(item) {
277
- // 重新生成AI回复
278
- this.$message.info('重新生成中...')
338
+ if (this.isAiStreaming) {
339
+ this.$message.warning('AI 正在生成中,请稍候')
340
+ return
341
+ }
342
+
343
+ const group = this.getNeighborGroup(item)
344
+ const aiItem = group.find(msg => msg.type === 'ai') || item
345
+ const userItem = group.find(msg => msg.type === 'user')
346
+
347
+ const aiIndex = this.dialogMessages.findIndex(
348
+ msg => msg === aiItem || (msg.messageId && msg.messageId === aiItem.messageId)
349
+ )
350
+ if (aiIndex === -1) {
351
+ this.$message.warning('未找到需要刷新的 AI 消息')
352
+ return
353
+ }
354
+
355
+ if (this.currentStreamConnection) {
356
+ this.currentStreamConnection()
357
+ this.currentStreamConnection = null
358
+ }
359
+
360
+ const messageContent = userItem ? userItem.content : ''
361
+ const chatId = this.currentDialogId || moment().format('YYYYMMDDHHmmss')
362
+ this.currentDialogId = chatId
363
+
364
+ this.dialogMessages[aiIndex].content = ''
365
+ this.scrollToBottom()
366
+
367
+ const requestBody = {
368
+ userMessage: messageContent,
369
+ chatId,
370
+ model: '',
371
+ prompt: '',
372
+ useContext: true,
373
+ user: this.currUser.operInfo,
374
+ files: []
375
+ }
376
+
377
+ this.startStream(aiIndex, undefined, requestBody)
279
378
  },
280
379
 
281
380
  autoResizeTextarea() {
@@ -331,6 +430,22 @@ export default {
331
430
  return
332
431
  }
333
432
 
433
+ // 如果是编辑后发送,先删除旧的那组消息
434
+ if (this.editingMessage) {
435
+ const oldGroup = this.getNeighborGroup(this.editingMessage)
436
+ oldGroup.forEach(msg => {
437
+ if (msg.attachments) {
438
+ msg.attachments.forEach(file => {
439
+ if (file.url && file.url.startsWith('blob:')) {
440
+ URL.revokeObjectURL(file.url)
441
+ }
442
+ })
443
+ }
444
+ })
445
+ this.dialogMessages = this.dialogMessages.filter(msg => !oldGroup.includes(msg))
446
+ this.editingMessage = null
447
+ }
448
+
334
449
  // 确保有dialogId
335
450
  if (!this.currentDialogId) {
336
451
  this.initDialog()
@@ -344,6 +459,7 @@ export default {
344
459
  .filter(f => f.file_id)
345
460
  .map(f => ({ file_id: f.file_id }))
346
461
 
462
+ const userMsgIndex = this.dialogMessages.length
347
463
  const doctorMessage = {
348
464
  type: 'user',
349
465
  content: messageContent,
@@ -359,8 +475,6 @@ export default {
359
475
  this.autoResizeTextarea()
360
476
 
361
477
  const chatId = this.currentDialogId
362
- let aiContent = ''
363
- const aiMsgIndex = this.dialogMessages.length
364
478
  this.dialogMessages.push({
365
479
  type: 'ai',
366
480
  content: '',
@@ -369,8 +483,6 @@ export default {
369
483
  })
370
484
  this.scrollToBottom()
371
485
 
372
- this.isAiStreaming = true
373
-
374
486
  const requestBody = {
375
487
  userMessage: messageContent,
376
488
  chatId,
@@ -381,12 +493,31 @@ export default {
381
493
  files: fileIds
382
494
  }
383
495
 
496
+ const aiMsgIndex = this.dialogMessages.length - 1
497
+ this.startStream(aiMsgIndex, userMsgIndex, requestBody)
498
+ },
499
+
500
+ startStream(aiMsgIndex, userMsgIndex, requestBody) {
501
+ this.isAiStreaming = true
502
+
384
503
  this.currentStreamConnection = startEventStreamPOST(
385
504
  '/his-web/api/af-his/ai/chat/stream',
386
505
  requestBody,
387
506
  {'Content-Type': 'application/json'},
388
507
  (data, type) => {
389
- if (type === 'additionalInfo' || type === 'sourceInfo') return
508
+ if (type === 'additionalInfo' || type === 'sourceInfo' || type === 'message_id') {
509
+ if (type === 'message_id') {
510
+ const userMessageId = data.userMessageId || ''
511
+ const aiMessageId = data.aiMessageId || ''
512
+ if (userMessageId && userMsgIndex !== undefined && this.dialogMessages[userMsgIndex]) {
513
+ this.dialogMessages[userMsgIndex].messageId = userMessageId
514
+ }
515
+ if (aiMessageId && this.dialogMessages[aiMsgIndex]) {
516
+ this.dialogMessages[aiMsgIndex].messageId = aiMessageId
517
+ }
518
+ }
519
+ return
520
+ }
390
521
 
391
522
  let textChunk = ''
392
523
  if (typeof data === 'object' && data !== null) {
@@ -394,12 +525,10 @@ export default {
394
525
  if (Array.isArray(choices) && choices.length > 0) {
395
526
  const delta = choices[0].delta
396
527
 
397
- // 优先取 delta.content(标准 OpenAI 格式)
398
528
  if (delta && typeof delta.content === 'string' && delta.content) {
399
529
  textChunk = delta.content
400
530
  }
401
531
 
402
- // 兼容百川模型的 thinking 字段
403
532
  if (!textChunk && delta && delta.thinking) {
404
533
  const thinking = delta.thinking
405
534
  if (thinking.summary && typeof thinking.summary === 'string') {
@@ -413,7 +542,6 @@ export default {
413
542
  }
414
543
  }
415
544
 
416
- // 检测模型拒答
417
545
  const finishReason = choices[0].finish_reason
418
546
  if (finishReason === 'refuse_answer') {
419
547
  const refuseTip = '\n\n> ⚠️ 很抱歉,当前问题我无法回答,请尝试调整提问方式或联系管理员。'
@@ -429,8 +557,7 @@ export default {
429
557
  }
430
558
 
431
559
  if (textChunk) {
432
- aiContent += textChunk
433
- this.dialogMessages[aiMsgIndex].content = aiContent
560
+ this.dialogMessages[aiMsgIndex].content += textChunk
434
561
  this.scrollToBottom()
435
562
  }
436
563
  },
@@ -441,8 +568,7 @@ export default {
441
568
  },
442
569
  () => {
443
570
  this.isAiStreaming = false
444
- // 如果 AI 消息为空(模型拒答或无响应),显示拒答提示
445
- if (!aiContent && this.dialogMessages[aiMsgIndex]) {
571
+ if (!this.dialogMessages[aiMsgIndex].content) {
446
572
  this.dialogMessages[aiMsgIndex].content = '很抱歉,当前问题我无法回答,请尝试调整提问方式或联系管理员。'
447
573
  }
448
574
  this.scrollToBottom()
@@ -483,6 +609,7 @@ export default {
483
609
  },
484
610
 
485
611
  scrollToMessage(displayIndex) {
612
+ this.activeNavIndex = displayIndex
486
613
  this.$nextTick(() => {
487
614
  const messages = document.querySelectorAll('.dialog-area > div')
488
615
  const target = messages[displayIndex]
@@ -587,6 +714,7 @@ export default {
587
714
  if (!msg) return null
588
715
 
589
716
  const message = {
717
+ messageId: msg.id || '',
590
718
  type: msg.type === 'assistant' ? 'ai' : (msg.type || 'user'),
591
719
  content: msg.content || '',
592
720
  time: msg.time || '',
@@ -858,17 +986,18 @@ export default {
858
986
  height: 8px;
859
987
  border-radius: 6px;
860
988
  opacity: 1;
861
- background: #3362DA;
989
+ background: #94979E;
862
990
  cursor: pointer;
863
- transition: transform 0.2s;
991
+ transition: transform 0.2s, background 0.2s;
864
992
  }
865
993
 
866
994
  .nav-dot:hover {
867
995
  transform: scale(1.2);
996
+ background: #0057FE;
868
997
  }
869
998
 
870
999
  .nav-dot.active {
871
- background: #3362DA;
1000
+ background: #0057FE;
872
1001
  transform: scale(1.3);
873
1002
  }
874
1003