vue2-client 1.22.3 → 1.22.5

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 (90) 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/pages/login/Login.vue +9 -7
  17. package/src/pages/login/LoginV3.vue +9 -7
  18. package/src/utils/login.js +8 -19
  19. package/.history/.eslintrc_20260521171150.js +0 -74
  20. package/.history/.eslintrc_20260521171213.js +0 -74
  21. package/.history/src/base-client/components/common/HIS/HAddNativeForm/HAddNativeForm_20260601154443.vue +0 -726
  22. package/.history/src/base-client/components/common/HIS/HAddNativeForm/HAddNativeForm_20260601154700.vue +0 -478
  23. package/.history/src/base-client/components/common/HIS/HButtons/HButtons_20260512175435.vue +0 -706
  24. package/.history/src/base-client/components/common/HIS/HButtons/HButtons_20260512175450.vue +0 -694
  25. package/.history/src/base-client/components/common/HIS/HButtons/HButtons_20260611152602.vue +0 -755
  26. package/.history/src/base-client/components/common/HIS/HForm/HForm_20260513145941.vue +0 -524
  27. package/.history/src/base-client/components/common/HIS/HForm/HForm_20260513153133.vue +0 -731
  28. package/.history/src/base-client/components/common/HIS/HForm/HForm_20260513160316.vue +0 -525
  29. package/.history/src/base-client/components/common/HIS/HForm/HForm_20260601144150.vue +0 -1046
  30. package/.history/src/base-client/components/common/HIS/HFormTable/HFormTable_20260310142713.vue +0 -512
  31. package/.history/src/base-client/components/common/HIS/HFormTable/HFormTable_20260310145118.vue +0 -511
  32. package/.history/src/base-client/components/common/HIS/HFormTable/HFormTable_20260311094834.vue +0 -696
  33. package/.history/src/base-client/components/common/HIS/HFormTable/HFormTable_20260320143028.vue +0 -693
  34. package/.history/src/base-client/components/common/HIS/HFormTable/HFormTable_20260409101450.vue +0 -677
  35. package/.history/src/base-client/components/common/HIS/HTab/HTab_20260508164645.vue +0 -758
  36. package/.history/src/base-client/components/common/HIS/HTab/HTab_20260508164714.vue +0 -693
  37. package/.history/src/base-client/components/common/HIS/HTab/HTab_20260508171651.vue +0 -716
  38. package/.history/src/base-client/components/common/HIS/HTab/HTab_20260509133717.vue +0 -695
  39. package/.history/src/base-client/components/common/HIS/HTab/HTab_20260509171115.vue +0 -664
  40. package/.history/src/base-client/components/common/XAddNativeForm/XAddNativeForm_20260513140637.vue +0 -1455
  41. package/.history/src/base-client/components/common/XAddNativeForm/XAddNativeForm_20260513140935.vue +0 -1441
  42. package/.history/src/base-client/components/common/XAddNativeForm/XAddNativeForm_20260513150818.vue +0 -1441
  43. package/.history/src/base-client/components/common/XAddNativeForm/XAddNativeForm_20260513153119.vue +0 -1442
  44. package/.history/src/base-client/components/common/XAddNativeForm/XAddNativeForm_20260513153126.vue +0 -1486
  45. package/.history/src/base-client/components/common/XForm/XFormItem_20260513140854.vue +0 -1607
  46. package/.history/src/base-client/components/common/XMarkdownViewer/XMarkdownViewer_20260519140403.vue +0 -643
  47. package/.history/src/base-client/components/common/XMarkdownViewer/XMarkdownViewer_20260519140829.vue +0 -628
  48. package/.history/src/base-client/components/common/XMarkdownViewer/demo_20260519142824.vue +0 -104
  49. package/.history/src/base-client/components/common/XMarkdownViewer/demo_20260519143155.vue +0 -102
  50. package/.history/src/base-client/components/common/XReportGrid/XReport_20260309171231.vue +0 -1241
  51. package/.history/src/base-client/components/common/XReportGrid/XReport_20260309171441.vue +0 -1223
  52. package/.history/src/base-client/components/his/HAi/HAi_20260612174826.vue +0 -472
  53. package/.history/src/base-client/components/his/HAi/HAi_20260612175839.vue +0 -538
  54. package/.history/src/base-client/components/his/HAi/HAi_20260615103331.vue +0 -650
  55. package/.history/src/base-client/components/his/XHDescriptions/XHDescriptions_20260424134504.vue +0 -1469
  56. package/.history/src/base-client/components/his/XSidebar/XSidebar_20260610171133.vue +0 -788
  57. package/.history/src/base-client/components/his/XSidebar/XSidebar_20260610171151.vue +0 -780
  58. package/.history/src/base-client/components/his/XTransfer/XTransfer_20260511170841.vue +0 -585
  59. package/.history/src/base-client/components/his/XTransfer/XTransfer_20260511171138.vue +0 -787
  60. package/.history/src/base-client/components/his/XTransfer/XTransfer_20260512141830.vue +0 -739
  61. package/.history/src/components/STable/index_20260409155138.js +0 -806
  62. package/.history/src/components/STable/index_20260409155218.js +0 -814
  63. package/.history/src/expression/core/Expression_20260305164427.js +0 -1371
  64. package/.history/src/expression/core/Expression_20260305170258.js +0 -1358
  65. package/.history/src/expression/core/Program_20260305111830.js +0 -944
  66. package/.history/src/expression/core/Program_20260305112041.js +0 -931
  67. package/.history/src/logic/LogicRunner_20260304154306.js +0 -170
  68. package/.history/src/logic/LogicRunner_20260304155553.js +0 -112
  69. package/.history/src/logic/LogicRunner_20260305105834.js +0 -112
  70. package/.history/src/logic/LogicRunner_20260305112718.js +0 -129
  71. package/.history/src/logic/LogicRunner_20260305182436.js +0 -133
  72. package/.history/src/logic/LogicRunner_20260306151301.js +0 -213
  73. package/.history/src/logic/LogicRunner_20260306152419.js +0 -213
  74. package/.history/src/logic/plugins/common/DateTools_20260305154159.js +0 -61
  75. package/.history/src/logic/plugins/common/DateTools_20260305154217.js +0 -44
  76. package/.history/src/logic/plugins/common/DateTools_20260305161014.js +0 -44
  77. package/.history/src/logic/plugins/common/HttpTools_20260305164352.js +0 -80
  78. package/.history/src/logic/plugins/common/HttpTools_20260305170258.js +0 -75
  79. package/.history/src/logic/plugins/common/HttpTools_20260305171634.js +0 -75
  80. package/.history/src/logic/plugins/common/HttpTools_20260306152419.js +0 -72
  81. package/.history/src/services/api/restTools_20260427142149.js +0 -245
  82. package/.history/src/services/api/restTools_20260427142853.js +0 -230
  83. package/.history/src/services/api/restTools_20260519135558.js +0 -230
  84. package/.history/src/services/api/restTools_20260519140825.js +0 -230
  85. package/.history/src/services/api/restTools_20260519151223.js +0 -230
  86. package/.history/src/utils/indexedDB_20260306150918.js +0 -593
  87. package/.history/src/utils/indexedDB_20260306151301.js +0 -586
  88. package/.idea/codeStyles/Project.xml +0 -62
  89. package/.idea/codeStyles/codeStyleConfig.xml +0 -5
  90. 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>