vue2-client 1.22.3 → 1.22.4

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/.idea/.name +1 -0
  3. package/.idea/MarsCodeWorkspaceAppSettings.xml +8 -0
  4. package/.idea/deployment.xml +14 -0
  5. package/.idea/gradle.xml +7 -0
  6. package/.idea/inspectionProfiles/Project_Default.xml +6 -0
  7. package/.idea/libraries/contour_plot.xml +9 -0
  8. package/.idea/material_theme_project_new.xml +18 -0
  9. package/.idea/misc.xml +87 -5
  10. package/package.json +1 -1
  11. package/src/base-client/components/common/HIS/HForm/HForm.vue +1186 -1186
  12. package/src/base-client/components/common/XMarkdownViewer/demo.vue +102 -102
  13. package/src/base-client/components/his/HAi/HAi.vue +1177 -1177
  14. package/src/base-client/components/his/XTransfer/index.md +327 -327
  15. package/src/base-client/plugins/GetLoginInfoService.js +4 -4
  16. package/src/utils/login.js +11 -11
  17. package/.history/.eslintrc_20260521171150.js +0 -74
  18. package/.history/.eslintrc_20260521171213.js +0 -74
  19. package/.history/src/base-client/components/common/HIS/HAddNativeForm/HAddNativeForm_20260601154443.vue +0 -726
  20. package/.history/src/base-client/components/common/HIS/HAddNativeForm/HAddNativeForm_20260601154700.vue +0 -478
  21. package/.history/src/base-client/components/common/HIS/HButtons/HButtons_20260512175435.vue +0 -706
  22. package/.history/src/base-client/components/common/HIS/HButtons/HButtons_20260512175450.vue +0 -694
  23. package/.history/src/base-client/components/common/HIS/HButtons/HButtons_20260611152602.vue +0 -755
  24. package/.history/src/base-client/components/common/HIS/HForm/HForm_20260513145941.vue +0 -524
  25. package/.history/src/base-client/components/common/HIS/HForm/HForm_20260513153133.vue +0 -731
  26. package/.history/src/base-client/components/common/HIS/HForm/HForm_20260513160316.vue +0 -525
  27. package/.history/src/base-client/components/common/HIS/HForm/HForm_20260601144150.vue +0 -1046
  28. package/.history/src/base-client/components/common/HIS/HFormTable/HFormTable_20260310142713.vue +0 -512
  29. package/.history/src/base-client/components/common/HIS/HFormTable/HFormTable_20260310145118.vue +0 -511
  30. package/.history/src/base-client/components/common/HIS/HFormTable/HFormTable_20260311094834.vue +0 -696
  31. package/.history/src/base-client/components/common/HIS/HFormTable/HFormTable_20260320143028.vue +0 -693
  32. package/.history/src/base-client/components/common/HIS/HFormTable/HFormTable_20260409101450.vue +0 -677
  33. package/.history/src/base-client/components/common/HIS/HTab/HTab_20260508164645.vue +0 -758
  34. package/.history/src/base-client/components/common/HIS/HTab/HTab_20260508164714.vue +0 -693
  35. package/.history/src/base-client/components/common/HIS/HTab/HTab_20260508171651.vue +0 -716
  36. package/.history/src/base-client/components/common/HIS/HTab/HTab_20260509133717.vue +0 -695
  37. package/.history/src/base-client/components/common/HIS/HTab/HTab_20260509171115.vue +0 -664
  38. package/.history/src/base-client/components/common/XAddNativeForm/XAddNativeForm_20260513140637.vue +0 -1455
  39. package/.history/src/base-client/components/common/XAddNativeForm/XAddNativeForm_20260513140935.vue +0 -1441
  40. package/.history/src/base-client/components/common/XAddNativeForm/XAddNativeForm_20260513150818.vue +0 -1441
  41. package/.history/src/base-client/components/common/XAddNativeForm/XAddNativeForm_20260513153119.vue +0 -1442
  42. package/.history/src/base-client/components/common/XAddNativeForm/XAddNativeForm_20260513153126.vue +0 -1486
  43. package/.history/src/base-client/components/common/XForm/XFormItem_20260513140854.vue +0 -1607
  44. package/.history/src/base-client/components/common/XMarkdownViewer/XMarkdownViewer_20260519140403.vue +0 -643
  45. package/.history/src/base-client/components/common/XMarkdownViewer/XMarkdownViewer_20260519140829.vue +0 -628
  46. package/.history/src/base-client/components/common/XMarkdownViewer/demo_20260519142824.vue +0 -104
  47. package/.history/src/base-client/components/common/XMarkdownViewer/demo_20260519143155.vue +0 -102
  48. package/.history/src/base-client/components/common/XReportGrid/XReport_20260309171231.vue +0 -1241
  49. package/.history/src/base-client/components/common/XReportGrid/XReport_20260309171441.vue +0 -1223
  50. package/.history/src/base-client/components/his/HAi/HAi_20260612174826.vue +0 -472
  51. package/.history/src/base-client/components/his/HAi/HAi_20260612175839.vue +0 -538
  52. package/.history/src/base-client/components/his/HAi/HAi_20260615103331.vue +0 -650
  53. package/.history/src/base-client/components/his/XHDescriptions/XHDescriptions_20260424134504.vue +0 -1469
  54. package/.history/src/base-client/components/his/XSidebar/XSidebar_20260610171133.vue +0 -788
  55. package/.history/src/base-client/components/his/XSidebar/XSidebar_20260610171151.vue +0 -780
  56. package/.history/src/base-client/components/his/XTransfer/XTransfer_20260511170841.vue +0 -585
  57. package/.history/src/base-client/components/his/XTransfer/XTransfer_20260511171138.vue +0 -787
  58. package/.history/src/base-client/components/his/XTransfer/XTransfer_20260512141830.vue +0 -739
  59. package/.history/src/components/STable/index_20260409155138.js +0 -806
  60. package/.history/src/components/STable/index_20260409155218.js +0 -814
  61. package/.history/src/expression/core/Expression_20260305164427.js +0 -1371
  62. package/.history/src/expression/core/Expression_20260305170258.js +0 -1358
  63. package/.history/src/expression/core/Program_20260305111830.js +0 -944
  64. package/.history/src/expression/core/Program_20260305112041.js +0 -931
  65. package/.history/src/logic/LogicRunner_20260304154306.js +0 -170
  66. package/.history/src/logic/LogicRunner_20260304155553.js +0 -112
  67. package/.history/src/logic/LogicRunner_20260305105834.js +0 -112
  68. package/.history/src/logic/LogicRunner_20260305112718.js +0 -129
  69. package/.history/src/logic/LogicRunner_20260305182436.js +0 -133
  70. package/.history/src/logic/LogicRunner_20260306151301.js +0 -213
  71. package/.history/src/logic/LogicRunner_20260306152419.js +0 -213
  72. package/.history/src/logic/plugins/common/DateTools_20260305154159.js +0 -61
  73. package/.history/src/logic/plugins/common/DateTools_20260305154217.js +0 -44
  74. package/.history/src/logic/plugins/common/DateTools_20260305161014.js +0 -44
  75. package/.history/src/logic/plugins/common/HttpTools_20260305164352.js +0 -80
  76. package/.history/src/logic/plugins/common/HttpTools_20260305170258.js +0 -75
  77. package/.history/src/logic/plugins/common/HttpTools_20260305171634.js +0 -75
  78. package/.history/src/logic/plugins/common/HttpTools_20260306152419.js +0 -72
  79. package/.history/src/services/api/restTools_20260427142149.js +0 -245
  80. package/.history/src/services/api/restTools_20260427142853.js +0 -230
  81. package/.history/src/services/api/restTools_20260519135558.js +0 -230
  82. package/.history/src/services/api/restTools_20260519140825.js +0 -230
  83. package/.history/src/services/api/restTools_20260519151223.js +0 -230
  84. package/.history/src/utils/indexedDB_20260306150918.js +0 -593
  85. package/.history/src/utils/indexedDB_20260306151301.js +0 -586
  86. package/.idea/codeStyles/Project.xml +0 -62
  87. package/.idea/codeStyles/codeStyleConfig.xml +0 -5
  88. package/preview-input-box.html +0 -180
@@ -1,650 +0,0 @@
1
- <template>
2
- <div class="ai-consultation-outer">
3
- <div class="ai-consultation-container">
4
- <div class="chat-area">
5
- <!-- 对话区域 -->
6
- <div class="dialog-area">
7
- <template v-for="(item, index) in displayItems">
8
- <div
9
- v-if="item.type === 'divider'"
10
- :key="`divider-${index}`"
11
- class="dialog-divider"
12
- >
13
- <span class="dialog-divider-text">{{ item.text }}</span>
14
- </div>
15
- <div
16
- v-else
17
- :key="`msg-${index}`"
18
- :class="['dialog-message', item.type]"
19
- >
20
- <div class="message-text" v-html="renderMd(item.content)" />
21
- <span v-if="isAiStreaming && index === displayItems.length - 1 && item.type === 'ai'" class="typing-cursor">▍</span>
22
- </div>
23
- </template>
24
- </div>
25
- </div>
26
-
27
- <div class="bottom-input-wrapper">
28
- <div class="input-container">
29
- <div class="input-textarea-area">
30
- <a-textarea
31
- ref="consultTextarea"
32
- v-model="doctorInputText"
33
- placeholder="医生补充信息或提问..."
34
- class="bottom-input"
35
- :rows="1"
36
- resize="none"
37
- @input="handleInput"
38
- @keydown="handleKeydown"
39
- ></a-textarea>
40
- </div>
41
-
42
- <input
43
- ref="uploadInput"
44
- type="file"
45
- style="display: none"
46
- @change="handleFileChange"
47
- multiple
48
- accept="image/*,.pdf,.doc,.docx"
49
- />
50
-
51
- <div class="input-controls">
52
- <div class="input-controls-left">
53
- <a-button class="upload-btn" @click="triggerUpload">
54
- <a-icon type="plus" class="upload-icon" />
55
- <span class="upload-text">上传(报告、图片)</span>
56
- </a-button>
57
- </div>
58
-
59
- <div class="input-controls-right">
60
- <span class="enter-tip">Enter</span>
61
- <a-button
62
- type="primary"
63
- shape="circle"
64
- class="send-btn"
65
- @click="handleSendMessage"
66
- :disabled="!doctorInputText.trim()"
67
- >
68
- <span class="send-icon">↑</span>
69
- </a-button>
70
- </div>
71
- </div>
72
- </div>
73
- </div>
74
- </div>
75
- </div>
76
- </template>
77
-
78
- <script>
79
- import { startEventStreamPOST } from '@vue2-client/services/api/restTools'
80
- import { marked } from 'marked'
81
- import moment from 'moment/moment'
82
- import { mapState } from 'vuex'
83
-
84
- export default {
85
- name: 'AIConsultation',
86
- data() {
87
- return {
88
- currentDialogId: null,
89
- doctorInputText: '',
90
- isPlaying: false,
91
- isRecording: false,
92
- patientComplaint: '',
93
- doctorQuestions: [],
94
- examinationOrder: null,
95
- aiDiagnosis: null,
96
- dialogMessages: [],
97
- currentStreamConnection: null,
98
- isAiStreaming: false,
99
- resizeTimeout: null
100
- }
101
- },
102
- computed: {
103
- ...mapState('account', {currUser: 'user'}),
104
- // 把 dialogMessages 与日期分隔符合并成最终渲染列表
105
- displayItems() {
106
- const items = []
107
- let lastDateKey = ''
108
- this.dialogMessages.forEach(msg => {
109
- const dateKey = this.getDateKey(msg.timestamp)
110
- if (dateKey && dateKey !== lastDateKey) {
111
- items.push({ type: 'divider', text: this.getDateLabel(msg.timestamp) })
112
- lastDateKey = dateKey
113
- }
114
- items.push(msg)
115
- })
116
- return items
117
- }
118
- },
119
- mounted() {
120
- this.initTestData()
121
- this.$nextTick(() => {
122
- this.autoResizeTextarea()
123
- })
124
- },
125
- beforeDestroy() {
126
- // 组件销毁时关闭流式连接
127
- if (this.currentStreamConnection) {
128
- this.currentStreamConnection()
129
- this.currentStreamConnection = null
130
- }
131
- if (this.resizeTimeout) {
132
- clearTimeout(this.resizeTimeout)
133
- }
134
- },
135
- methods: {
136
- autoResizeTextarea() {
137
- this.$nextTick(() => {
138
- const textarea = this.$refs.consultTextarea?.$el || this.$refs.consultTextarea
139
- if (!textarea) return
140
-
141
- textarea.style.height = 'auto'
142
- const scrollHeight = textarea.scrollHeight
143
- textarea.style.height = `${Math.min(Math.max(scrollHeight, 120), 250)}px`
144
- })
145
- },
146
-
147
- handleInput() {
148
- if (this.resizeTimeout) clearTimeout(this.resizeTimeout)
149
- this.resizeTimeout = setTimeout(() => {
150
- this.autoResizeTextarea()
151
- }, 16)
152
- },
153
-
154
- renderMd(text) {
155
- if (!text) return ''
156
- try {
157
- return marked.parse(text, { breaks: true, gfm: true, headerIds: false, mangle: false })
158
- } catch {
159
- return text
160
- }
161
- },
162
-
163
- handleKeydown(event) {
164
- if (event.key === 'Enter' && !event.shiftKey) {
165
- event.preventDefault()
166
- this.handleSendMessage()
167
- }
168
- },
169
-
170
- initDialog() {
171
- if (!this.currentDialogId) {
172
- this.currentDialogId = moment().format('YYYYMMDDHHmmss')
173
- }
174
- },
175
-
176
- clearDialog() {
177
- this.currentDialogId = moment().format('YYYYMMDDHHmmss')
178
- this.dialogMessages = []
179
- },
180
-
181
- handleSendMessage() {
182
- if (!this.doctorInputText.trim()) return
183
-
184
- // 确保有dialogId
185
- if (!this.currentDialogId) {
186
- this.initDialog()
187
- }
188
-
189
- const messageContent = this.doctorInputText.trim()
190
- const doctorMessage = {
191
- type: 'user',
192
- content: messageContent,
193
- time: new Date().toLocaleTimeString(),
194
- timestamp: Date.now()
195
- }
196
- this.dialogMessages.push(doctorMessage)
197
- this.scrollToBottom()
198
-
199
- this.doctorInputText = ''
200
- this.autoResizeTextarea()
201
-
202
- const chatId = this.currentDialogId
203
- let aiContent = ''
204
- const aiMsgIndex = this.dialogMessages.length
205
- this.dialogMessages.push({
206
- type: 'ai',
207
- content: '',
208
- time: new Date().toLocaleTimeString(),
209
- timestamp: Date.now()
210
- })
211
- this.scrollToBottom()
212
-
213
- this.isAiStreaming = true
214
-
215
- this.currentStreamConnection = startEventStreamPOST(
216
- '/his-web/api/af-his/ai/chat/stream',
217
- { userMessage: messageContent, chatId, model: '', prompt: '', useContext: true, user: this.currUser.operInfo },
218
- {'Content-Type': 'application/json'},
219
- (data, type) => {
220
- if (type === 'additionalInfo' || type === 'sourceInfo') return
221
-
222
- let textChunk = ''
223
- if (typeof data === 'object' && data !== null) {
224
- const choices = data.choices
225
- if (Array.isArray(choices) && choices.length > 0) {
226
- const delta = choices[0].delta
227
-
228
- // 优先取 delta.content(标准 OpenAI 格式)
229
- if (delta && typeof delta.content === 'string' && delta.content) {
230
- textChunk = delta.content
231
- }
232
-
233
- // 兼容百川模型的 thinking 字段
234
- if (!textChunk && delta && delta.thinking) {
235
- const thinking = delta.thinking
236
- // 取 summary 描述
237
- if (thinking.summary && typeof thinking.summary === 'string') {
238
- textChunk = `[思考中] ${thinking.summary}`
239
- }
240
- // 取 reasoning steps
241
- if (Array.isArray(thinking.steps)) {
242
- const activeStep = thinking.steps.find(s => s.status === 'in_progress')
243
- if (activeStep && activeStep.label) {
244
- textChunk = `[思考中] ${activeStep.label}`
245
- }
246
- }
247
- }
248
-
249
- // 检测模型拒答
250
- const finishReason = choices[0].finish_reason
251
- if (finishReason === 'refuse_answer') {
252
- const refuseTip = '\n\n> ⚠️ 很抱歉,当前问题我无法回答,请尝试调整提问方式或联系管理员。'
253
- if (!this.dialogMessages[aiMsgIndex].content.includes('无法回答')) {
254
- this.dialogMessages[aiMsgIndex].content += refuseTip
255
- this.isAiStreaming = false
256
- this.scrollToBottom()
257
- }
258
- }
259
- }
260
- } else if (typeof data === 'string') {
261
- textChunk = data
262
- }
263
-
264
- if (textChunk) {
265
- aiContent += textChunk
266
- this.dialogMessages[aiMsgIndex].content = aiContent
267
- this.scrollToBottom()
268
- }
269
- },
270
- (error) => {
271
- this.isAiStreaming = false
272
- this.dialogMessages[aiMsgIndex].content += `\n\n[错误: ${error.message || '请求失败'}]`
273
- this.scrollToBottom()
274
- },
275
- () => {
276
- this.isAiStreaming = false
277
- this.scrollToBottom()
278
- }
279
- )
280
- },
281
-
282
- setPatientComplaint(complaint) {
283
- this.patientComplaint = complaint
284
- },
285
-
286
- scrollToBottom(delay = 0) {
287
- this.$nextTick(() => {
288
- setTimeout(() => {
289
- const chatArea = document.querySelector('.chat-area')
290
- if (chatArea) {
291
- chatArea.scrollTop = chatArea.scrollHeight
292
- }
293
- }, delay)
294
- })
295
- },
296
-
297
- reset() {
298
- if (this.currentStreamConnection) {
299
- this.currentStreamConnection()
300
- this.currentStreamConnection = null
301
- }
302
- this.isAiStreaming = false
303
- this.patientComplaint = ''
304
- this.doctorQuestions = []
305
- this.examinationOrder = null
306
- this.aiDiagnosis = null
307
- this.doctorInputText = ''
308
- this.isPlaying = false
309
- this.isRecording = false
310
- this.dialogMessages = []
311
- this.$nextTick(() => {
312
- this.autoResizeTextarea()
313
- const chatArea = document.querySelector('.chat-area')
314
- if (chatArea) {
315
- chatArea.scrollTop = 0
316
- }
317
- })
318
- },
319
-
320
- triggerUpload() {
321
- this.$refs.uploadInput.click()
322
- },
323
-
324
- handleFileChange(event) {
325
- const files = Array.from(event.target.files || [])
326
- if (!files.length) return
327
-
328
- this.dialogMessages.push({
329
- type: 'user',
330
- content: `已上传文件:${files.map(f => f.name).join('、')}`,
331
- time: new Date().toLocaleTimeString(),
332
- timestamp: Date.now()
333
- })
334
- this.scrollToBottom()
335
- event.target.value = ''
336
- },
337
-
338
- // 初始化测试数据:两天内容(昨天 + 今天),不调用接口
339
- initTestData() {
340
- const now = moment()
341
- const today = now.clone().startOf('day')
342
- const yesterday = today.clone().subtract(1, 'day')
343
-
344
- this.dialogMessages = [
345
- // 昨天 - 上午
346
- {
347
- type: 'user',
348
- content: '患者男,65岁,主诉反复胸闷、心悸2年,加重伴乏力1周。既往有高血压病史10年,长期口服氨氯地平。',
349
- time: '09:12',
350
- timestamp: yesterday.clone().hour(9).minute(12).valueOf()
351
- },
352
- {
353
- type: 'ai',
354
- content: '**初步分析:**\n\n1. 患者老年男性,有高血压病史,症状提示可能存在 **冠心病、心律失常** 或 **心力衰竭**\n2. 建议完善以下检查:\n - 心电图(ECG)\n - 心脏彩超\n - 动态心电图(Holter)\n - 血脂、血糖、BNP\n\n3. 注意鉴别:\n - 甲状腺功能异常\n - 电解质紊乱\n - 药物影响',
355
- time: '09:13',
356
- timestamp: yesterday.clone().hour(9).minute(13).valueOf()
357
- },
358
- {
359
- type: 'user',
360
- content: '心电图提示:窦性心律,ST段压低,T波倒置。心脏彩超:左室舒张功能减退,EF 55%。',
361
- time: '10:25',
362
- timestamp: yesterday.clone().hour(10).minute(25).valueOf()
363
- },
364
- {
365
- type: 'ai',
366
- content: '**进一步分析:**\n\n根据检查结果:\n- ST段压低、T波倒置:考虑 **心肌缺血**\n- 左室舒张功能减退:提示 **舒张性心衰(HFrEF 临界)**\n- EF 55% 处于正常下限\n\n**建议:**\n1. 加用 **阿司匹林** 抗血小板治疗\n2. 加用 **他汀类** 调脂稳斑\n3. 考虑 **ACEI/ARB** 控制血压、逆转心肌重构\n4. 必要时行 **冠脉造影** 明确冠脉病变\n5. 密切监测血压、心率',
367
- time: '10:27',
368
- timestamp: yesterday.clone().hour(10).minute(27).valueOf()
369
- },
370
- // 昨天 - 下午
371
- {
372
- type: 'user',
373
- content: '冠脉造影结果:LAD 中段狭窄 70%,LCX 远端狭窄 40%。',
374
- time: '15:40',
375
- timestamp: yesterday.clone().hour(15).minute(40).valueOf()
376
- },
377
- {
378
- type: 'ai',
379
- content: '**冠脉病变评估:**\n\n- **LAD 中段 70% 狭窄**:临界病变,建议 PCI 干预\n- **LCX 远端 40% 狭窄**:轻度,可药物保守治疗\n\n**治疗方案:**\n1. **LAD 行 PCI + 支架植入**\n2. 术后双联抗血小板治疗(阿司匹林 + 氯吡格雷)至少 12 个月\n3. 强化他汀治疗至 LDL < 1.4 mmol/L\n4. 控制血压 < 130/80 mmHg\n5. 心肺康复、规律运动',
380
- time: '15:42',
381
- timestamp: yesterday.clone().hour(15).minute(42).valueOf()
382
- },
383
- // 今天 - 上午
384
- {
385
- type: 'user',
386
- content: 'PCI 术后第 1 天,患者诉胸闷较前明显好转,无胸痛,复查心电图未见明显 ST-T 改变。',
387
- time: '08:30',
388
- timestamp: today.clone().hour(8).minute(30).valueOf()
389
- },
390
- {
391
- type: 'ai',
392
- content: '**术后评估:**\n\n术后恢复良好,症状改善明显。\n\n**今日医嘱:**\n1. 继续双联抗血小板治疗\n2. 监测心电图、心肌酶变化\n3. 观察穿刺点有无出血、血肿\n4. 鼓励早期下床活动\n5. 出院前评估:\n - 心脏彩超复查\n - 24小时动态心电图\n - 出院带药及健康宣教',
393
- time: '08:31',
394
- timestamp: today.clone().hour(8).minute(31).valueOf()
395
- },
396
- {
397
- type: 'user',
398
- content: '好的,麻烦帮我整理一下完整的出院小结模板。',
399
- time: '09:45',
400
- timestamp: today.clone().hour(9).minute(45).valueOf()
401
- }
402
- ]
403
- },
404
-
405
- // 获取时间戳的日期键(YYYY-MM-DD)
406
- getDateKey(timestamp) {
407
- if (!timestamp) return ''
408
- return moment(timestamp).format('YYYY-MM-DD')
409
- },
410
-
411
- // 解析日期键为本地日期对象
412
- parseDateKey(dateKey) {
413
- return moment(dateKey, 'YYYY-MM-DD')
414
- },
415
-
416
- // 把日期戳格式化为"今天 / 昨天 / 具体日期"
417
- getDateLabel(timestamp) {
418
- if (!timestamp) return ''
419
- const m = moment(timestamp)
420
- const today = moment().startOf('day')
421
- const yesterday = today.clone().subtract(1, 'day')
422
- const target = m.clone().startOf('day')
423
-
424
- if (target.isSame(today, 'day')) return '今天'
425
- if (target.isSame(yesterday, 'day')) return '昨天'
426
- // 跨年显示年份
427
- if (target.year() !== today.year()) {
428
- return m.format('YYYY年M月D日')
429
- }
430
- return m.format('M月D日')
431
- }
432
- }
433
- }
434
- </script>
435
-
436
- <style scoped>
437
- .ai-consultation-outer {
438
- box-sizing: border-box;
439
- margin: 0;
440
- padding: 0;
441
- width: 100%;
442
- height: 100%;
443
- }
444
-
445
- .ai-consultation-container {
446
- width: 100%;
447
- height: 100%;
448
- display: flex;
449
- flex-direction: column;
450
- background: #FFFFFF;
451
- border: 1px solid #E5E9F0;
452
- }
453
-
454
- /* 对话区域容器:初始高度 240px(无内容时),最大高度 690px(内容超出滚动) */
455
- .chat-area {
456
- flex: 0 1 auto;
457
- overflow-y: auto;
458
- padding: 16px;
459
- background: #fff;
460
- /* 初始高度:没有内容时也保留这块区域 */
461
- min-height: 240px;
462
- /* 最大高度:内容超过则出现滚动条 */
463
- max-height: 690px;
464
- box-sizing: border-box;
465
- }
466
-
467
- /* 消息列表:跟随 chat-area 高度 */
468
- .dialog-area {
469
- display: flex;
470
- flex-direction: column;
471
- min-height: 208px; /* 240(chat-area) - 16*2(padding) = 208 */
472
- }
473
-
474
- /* 日期分隔符 */
475
- .dialog-divider {
476
- display: flex;
477
- align-items: center;
478
- justify-content: center;
479
- margin: 8px 0 16px;
480
- }
481
-
482
- .dialog-divider::before,
483
- .dialog-divider::after {
484
- content: '';
485
- flex: 1;
486
- height: 1px;
487
- background: #E5E9F0;
488
- }
489
-
490
- .dialog-divider-text {
491
- padding: 2px 12px;
492
- font-size: 12px;
493
- color: #8C8C8C;
494
- background: #F5F7FA;
495
- border-radius: 10px;
496
- margin: 0 12px;
497
- white-space: nowrap;
498
- }
499
-
500
- /* 底部输入框区域 */
501
- .bottom-input-wrapper {
502
- flex-shrink: 0;
503
- padding: 12px 16px;
504
- background: #fff;
505
- }
506
-
507
- .input-container {
508
- width: 782px;
509
- margin: 0 auto;
510
- padding: 12px;
511
- display: flex;
512
- flex-direction: column;
513
- border: 1px solid #E5E9F0;
514
- border-radius: 6px;
515
- background: #FFFFFF;
516
- box-sizing: border-box;
517
- }
518
-
519
- .input-textarea-area {
520
- flex: 1 1 auto;
521
- min-height: 0;
522
- margin-bottom: 8px;
523
- }
524
-
525
- .bottom-input {
526
- width: 100%;
527
- height: 100%;
528
- border: none !important;
529
- background: transparent;
530
- padding: 0;
531
- font-family: 'Source Han Sans', 'Microsoft YaHei', sans-serif;
532
- font-size: 16px;
533
- color: #313131;
534
- resize: none;
535
- outline: none;
536
- box-shadow: none !important;
537
- }
538
-
539
- .bottom-input:focus,
540
- .bottom-input:hover {
541
- border: none !important;
542
- box-shadow: none !important;
543
- }
544
-
545
- .bottom-input::placeholder {
546
- color: #999;
547
- }
548
-
549
- .input-controls {
550
- flex-shrink: 0;
551
- display: flex;
552
- align-items: center;
553
- justify-content: space-between;
554
- padding-top: 0;
555
- border-top: none;
556
- }
557
-
558
- .input-controls-left {
559
- display: flex;
560
- align-items: center;
561
- gap: 8px;
562
- }
563
-
564
- .input-controls-right {
565
- display: flex;
566
- align-items: center;
567
- gap: 8px;
568
- }
569
-
570
- .enter-tip {
571
- font-family: 'Inter', sans-serif;
572
- font-size: 14px;
573
- font-weight: 500;
574
- color: #9E9E9E;
575
- padding: 4px 8px;
576
- }
577
-
578
- .send-btn {
579
- width: 30px;
580
- height: 30px;
581
- background: #0057FE;
582
- border: none;
583
- border-radius: 50%;
584
- color: #fff;
585
- font-size: 14px;
586
- cursor: pointer;
587
- display: flex;
588
- align-items: center;
589
- justify-content: center;
590
- padding: 0;
591
- }
592
-
593
- .send-btn:hover {
594
- background: #0040cc;
595
- }
596
-
597
- .send-icon {
598
- font-size: 14px;
599
- font-weight: bold;
600
- }
601
-
602
- /* 对话区域样式 */
603
- .dialog-message {
604
- display: flex;
605
- margin-bottom: 16px;
606
- align-items: flex-start;
607
- }
608
-
609
- .dialog-message:last-child {
610
- margin-bottom: 0;
611
- }
612
-
613
- .dialog-message.ai {
614
- flex-direction: row;
615
- }
616
-
617
- .dialog-message.user {
618
- flex-direction: row-reverse;
619
- }
620
-
621
- .dialog-message.user .message-text {
622
- text-align: right;
623
- }
624
-
625
- .message-text {
626
- opacity: 1;
627
- font-family: Source Han Sans;
628
- font-size: 16px;
629
- font-weight: normal;
630
- line-height: normal;
631
- text-align: justify;
632
- letter-spacing: 0em;
633
- font-feature-settings: "kern" on;
634
- max-width: 100%;
635
- color: #313131;
636
- }
637
-
638
- .typing-cursor {
639
- display: inline-block;
640
- margin-left: 2px;
641
- color: #0057FE;
642
- font-size: 18px;
643
- animation: blink 1s step-end infinite;
644
- }
645
-
646
- @keyframes blink {
647
- 0%, 100% { opacity: 1; }
648
- 50% { opacity: 0; }
649
- }
650
- </style>