vue2-client 1.19.42 → 1.19.44

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,6 +1,6 @@
1
1
  {
2
2
  "name": "vue2-client",
3
- "version": "1.19.42",
3
+ "version": "1.19.44",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "serve": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint",
@@ -106,7 +106,10 @@ export default {
106
106
  previewVisible: false,
107
107
  previewImage: '',
108
108
  previewImageName: '',
109
- showScanner: false
109
+ showScanner: false,
110
+ uploadQueue: [],
111
+ isUploading: false,
112
+ uploadControllers: new Map()
110
113
  }
111
114
  },
112
115
  props: {
@@ -159,10 +162,10 @@ export default {
159
162
  }
160
163
  },
161
164
  computed: {
162
- ...mapState('account', { currUser: 'user' }),
165
+ ...mapState('account', {currUser: 'user'}),
163
166
  ...mapState('setting', ['compatible'])
164
167
  },
165
- created () {
168
+ created() {
166
169
  const list = this.model.type === 'file' ? [...this.files] : [...this.images]
167
170
  if (this.model.useType) {
168
171
  // {uid,url,name,f_use_type?,f_form_type?}
@@ -180,7 +183,7 @@ export default {
180
183
  }
181
184
  },
182
185
  methods: {
183
- async handlePreview (file) {
186
+ async handlePreview(file) {
184
187
  if (this.isImageFile(file)) {
185
188
  // 图片文件:预览
186
189
  this.previewImage = file.url
@@ -192,7 +195,7 @@ export default {
192
195
  }
193
196
  },
194
197
 
195
- async handleDownload (file) {
198
+ async handleDownload(file) {
196
199
  const link = document.createElement('a')
197
200
  link.href = file.url
198
201
  link.download = file.name || 'download'
@@ -202,13 +205,13 @@ export default {
202
205
  document.body.removeChild(link)
203
206
  },
204
207
 
205
- isImageFile (file) {
208
+ isImageFile(file) {
206
209
  const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']
207
210
  const fileName = file.name ? file.name.toLowerCase() : ''
208
211
  return imageExtensions.some(ext => fileName.endsWith(ext))
209
212
  },
210
213
 
211
- confirmPhoto (img) {
214
+ confirmPhoto(img) {
212
215
  const fileName = `GaoPaiYi_${moment().format('YYYYMMDDHHmmss')}.png`
213
216
  const file = base64ToFile(img, fileName)
214
217
  file.uid = fileName
@@ -218,21 +221,50 @@ export default {
218
221
  this.uploadFiles(info)
219
222
  },
220
223
 
221
- useScannerUpload (e) {
224
+ useScannerUpload(e) {
222
225
  // 打开高拍仪
223
226
  this.showScanner = true
224
227
  },
225
228
 
226
- handleCancelScanner () {
229
+ handleCancelScanner() {
227
230
  // 关闭高拍仪弹窗
228
231
  this.showScanner = false
229
232
  },
233
+ uploadFiles(info) {
234
+ // 添加到上传队列
235
+ this.uploadQueue.push(info)
236
+ // 如果当前没有上传任务,开始处理队列
237
+ if (!this.isUploading) {
238
+ this.processUploadQueue()
239
+ }
240
+ },
241
+
242
+ // 处理上传队列
243
+ async processUploadQueue() {
244
+ if (this.uploadQueue.length === 0) {
245
+ this.isUploading = false
246
+ return
247
+ }
248
+ this.isUploading = true
249
+ // 从队列中取出第一个任务
250
+ const info = this.uploadQueue.shift()
251
+ await this.singleFileUpload(info)
252
+ // 继续处理队列中的下一个任务
253
+ this.processUploadQueue()
254
+ },
230
255
 
231
- uploadFiles (info) {
256
+ // 单个文件上传
257
+ async singleFileUpload(info) {
258
+ // 检查文件数量限制
232
259
  if (this.uploadedFileList.length >= this.model.acceptCount) {
233
260
  this.$message.error(`当前表单限制仅可上传 ${this.model.acceptCount} 个文件`)
234
261
  return
235
262
  }
263
+
264
+ // 创建取消控制器
265
+ const controller = new AbortController()
266
+ this.uploadControllers.set(info.file.uid, controller)
267
+
236
268
  // 初始化文件信息
237
269
  const fileInfo = {
238
270
  uid: info.file.uid,
@@ -241,66 +273,118 @@ export default {
241
273
  response: '',
242
274
  url: '',
243
275
  }
244
- // 放入上传列表中,以便于显示上传进度
245
- this.uploadedFileList.push(fileInfo)
246
- // 组装上传数据
247
- const headers = {
248
- 'Content-Type': 'multipart/form-data',
249
- }
250
- const formData = new FormData()
251
- formData.append('avatar', info.file)
252
- formData.append('resUploadMode', this.model.resUploadMode ?? 'server')
253
- if (this.model.pathKey) {
254
- formData.append('pathKey', this.model.pathKey ?? 'Default')
255
- }
256
- // formData.append('stockAlias', this.model.stockAlias)
257
- formData.append('formType', this.model.type)
258
- formData.append('useType', this.model.useType ?? 'Default')
259
- formData.append('resUploadStock', this.model.resUploadStock ?? 1)
260
- formData.append('filename', info.file.name)
261
- formData.append('filesize', (info.file.size / 1024 / 1024).toFixed(4))
262
- formData.append('f_operator', this.currUser ? this.currUser.username : '')
263
276
 
264
- upload(formData, this.serviceName, { headers, timeout: 600 * 1000 }).then(res => {
265
- // 根据服务端返回的结果判断成功与否,设置文件条目的状态
277
+ // 使用不可变更新,避免状态冲突
278
+ this.uploadedFileList = [...this.uploadedFileList, fileInfo]
279
+
280
+ try {
281
+ // 组装上传数据
282
+ const headers = {
283
+ 'Content-Type': 'multipart/form-data',
284
+ }
285
+ const formData = new FormData()
286
+ formData.append('avatar', info.file)
287
+ formData.append('resUploadMode', this.model.resUploadMode ?? 'server')
288
+ if (this.model.pathKey) {
289
+ formData.append('pathKey', this.model.pathKey ?? 'Default')
290
+ }
291
+ formData.append('formType', this.model.type)
292
+ formData.append('useType', this.model.useType ?? 'Default')
293
+ formData.append('resUploadStock', this.model.resUploadStock ?? 1)
294
+ formData.append('filename', info.file.name)
295
+ formData.append('filesize', (info.file.size / 1024 / 1024).toFixed(4))
296
+ formData.append('f_operator', this.currUser ? this.currUser.username : '')
297
+
298
+ const res = await upload(formData, this.serviceName, {
299
+ headers,
300
+ timeout: 600 * 1000,
301
+ signal: controller.signal // 添加取消信号
302
+ })
303
+
304
+ // 根据服务端返回的结果判断成功与否
266
305
  if (res.success || res.id) {
267
- fileInfo.status = 'done'
268
- let dataObj
269
- if (res.id) {
270
- dataObj = res
271
- } else if (typeof res.data === 'string') {
272
- dataObj = JSON.parse(res.data)
273
- } else {
274
- dataObj = res.data
275
- }
276
- fileInfo.response = dataObj
277
- fileInfo.id = dataObj.id
278
- // 如果新上传的图片是V4,需要携带serviceName前缀,但是同时也要回显老系统图片
279
- if (this.imgPrefix) {
280
- fileInfo.url = this.imgPrefix + dataObj.f_downloadpath
281
- } else {
282
- fileInfo.url = dataObj.f_downloadpath
283
- }
284
- if (this.outerContainerIndex !== undefined) {
285
- this.$emit('setFiles', this.uploadedFileList.filter(item => item.status === 'done'), this.outerContainerIndex)
286
- } else {
287
- this.$emit('setFiles', this.uploadedFileList.filter(item => item.status === 'done').map(item => item.id))
288
- this.$emit('setFilesAllInfo', this.uploadedFileList.filter(item => item.status === 'done').map(item => item))
289
- }
306
+ this.updateFileStatus(info.file.uid, 'done', res)
290
307
  this.$message.success('上传成功!')
291
308
  } else {
292
- fileInfo.status = 'error'
293
- fileInfo.response = res.data
309
+ this.updateFileStatus(info.file.uid, 'error', res.data)
310
+ this.$message.error('上传失败!')
311
+ }
312
+ } catch (error) {
313
+ if (error.name !== 'AbortError') {
314
+ // 非取消错误才显示错误信息
315
+ this.updateFileStatus(info.file.uid, 'error', error)
294
316
  this.$message.error('上传失败!')
295
317
  }
296
- }).catch((e) => {
297
- fileInfo.status = 'error'
298
- fileInfo.response = e
318
+ } finally {
319
+ // 清理取消控制器
320
+ this.uploadControllers.delete(info.file.uid)
321
+ }
322
+ },
323
+
324
+ // 更新文件状态
325
+ updateFileStatus(uid, status, response) {
326
+ this.uploadedFileList = this.uploadedFileList.map(item => {
327
+ if (item.uid === uid) {
328
+ const updatedItem = {
329
+ ...item,
330
+ status,
331
+ response
332
+ }
333
+
334
+ if (status === 'done') {
335
+ let dataObj
336
+ if (response.id) {
337
+ dataObj = response
338
+ } else if (typeof response.data === 'string') {
339
+ dataObj = JSON.parse(response.data)
340
+ } else {
341
+ dataObj = response.data
342
+ }
343
+
344
+ updatedItem.response = dataObj
345
+ updatedItem.id = dataObj.id
346
+ // 如果新上传的图片是V4,需要携带serviceName前缀
347
+ if (this.imgPrefix) {
348
+ updatedItem.url = this.imgPrefix + dataObj.f_downloadpath
349
+ } else {
350
+ updatedItem.url = dataObj.f_downloadpath
351
+ }
352
+ }
353
+
354
+ return updatedItem
355
+ }
356
+ return item
299
357
  })
358
+
359
+ this.emitFilesUpdate()
360
+ },
361
+
362
+ // 触发文件更新事件
363
+ emitFilesUpdate() {
364
+ const doneFiles = this.uploadedFileList.filter(item => item.status === 'done')
365
+
366
+ if (this.outerContainerIndex !== undefined) {
367
+ this.$emit('setFiles', doneFiles, this.outerContainerIndex)
368
+ } else {
369
+ this.$emit('setFiles', doneFiles.map(item => item.id))
370
+ this.$emit('setFilesAllInfo', doneFiles)
371
+ }
372
+ },
373
+
374
+ // 取消上传(可选功能)
375
+ cancelUpload(uid) {
376
+ const controller = this.uploadControllers.get(uid)
377
+ if (controller) {
378
+ controller.abort()
379
+
380
+ // 从上传列表中移除
381
+ this.uploadedFileList = this.uploadedFileList.filter(item => item.uid !== uid)
382
+ this.emitFilesUpdate()
383
+ }
300
384
  },
301
385
 
302
386
  // 删除文件
303
- deleteFileItem (file) {
387
+ deleteFileItem(file) {
304
388
  const that = this
305
389
  this.$confirm({
306
390
  title: '提醒',
@@ -309,35 +393,38 @@ export default {
309
393
  okType: 'danger',
310
394
  cancelText: '取消',
311
395
  zIndex: 2000,
312
- onOk () {
396
+ onOk() {
397
+ // 如果文件正在上传,先取消上传
398
+ if (file.status === 'uploading') {
399
+ that.cancelUpload(file.uid)
400
+ }
401
+
313
402
  // 阳春博能工单信息V4页面有时会删除V3照片, V3照片不能通过这个请求删除, 已经在外层进行删除处理
314
403
  if (file.id && !file.version) {
315
404
  switch (that.compatible) {
316
405
  case 'V3':
317
- fileDelete({ id: file.id, f_state: '删除' })
406
+ fileDelete({id: file.id, f_state: '删除'})
318
407
  .then(res => {
319
408
  }).catch(e => {
320
409
  })
321
410
  break
322
411
  case 'V4':
323
- fileDeleteV4({ id: file.id, resDeleteMode: 'server' })
412
+ fileDeleteV4({id: file.id, resDeleteMode: 'server'})
324
413
  .then(res => {
325
414
  }).catch(e => {
326
415
  })
327
416
  break
328
417
  }
329
418
  }
330
- // 找到当前文件所在列表的索引
331
- const index = that.uploadedFileList.indexOf(file)
332
419
  // 从列表中移除该文件
333
- that.uploadedFileList.splice(index, 1)
420
+ that.uploadedFileList = that.uploadedFileList.filter(item => item.uid !== file.uid)
334
421
  if (that.outerContainerIndex !== undefined) {
335
422
  that.$emit('setFiles', that.uploadedFileList.filter(item => item.status === 'done'), that.outerContainerIndex)
336
423
  } else {
337
424
  that.$emit('setFiles', that.uploadedFileList.filter(item => item.status === 'done').map(item => item.id))
338
425
  }
339
426
  },
340
- onCancel () {
427
+ onCancel() {
341
428
  console.log('取消删除')
342
429
  },
343
430
  })
@@ -67,6 +67,7 @@
67
67
  <div class="workflow-content">
68
68
  <div data-step="1" class="workflow-section">
69
69
  <PreliminaryDiagnosis
70
+ :key="componentKey"
70
71
  ref="preliminaryDiagnosisRef"
71
72
  :patientComplaint="patientComplaint"
72
73
  @question-asked="handleQuestionAsked"
@@ -184,6 +185,9 @@ const doctorQuestions = ref([])
184
185
  const examinationOrder = ref(null)
185
186
  const aiDiagnosis = ref(null)
186
187
 
188
+ // 添加重置key来强制重新渲染子组件
189
+ const componentKey = ref(0)
190
+
187
191
  const workflowSteps = ref([
188
192
  {
189
193
  key: 'preliminary',
@@ -401,9 +405,60 @@ const setPatientComplaint = (complaint) => {
401
405
  patientComplaint.value = complaint
402
406
  }
403
407
 
408
+ // 重置方法 - 将组件恢复到初始状态
409
+ const reset = () => {
410
+ console.log('重置方法被调用了 - 开始重置')
411
+
412
+ // 重置所有状态到初始值
413
+ currentStep.value = 1
414
+ selectedStep.value = 1
415
+ patientComplaint.value = ''
416
+ doctorQuestions.value = []
417
+ examinationOrder.value = null
418
+ aiDiagnosis.value = null
419
+ doctorInputText.value = ''
420
+ isPlaying.value = false
421
+ isRecording.value = false
422
+
423
+ // 递增组件key来强制重新渲染子组件
424
+ componentKey.value += 1
425
+
426
+ // 重置工作流步骤
427
+ workflowSteps.value = [
428
+ { key: 'preliminary', title: '初步诊断', completed: false },
429
+ { key: 'examination', title: '检查检验', completed: false },
430
+ { key: 'diagnosis', title: '诊断建议', completed: false },
431
+ { key: 'prescription', title: '医嘱建议', completed: false }
432
+ ]
433
+
434
+ console.log('状态重置完成:', {
435
+ currentStep: currentStep.value,
436
+ selectedStep: selectedStep.value,
437
+ hasPatientComplaint: !!patientComplaint.value,
438
+ doctorQuestionsCount: doctorQuestions.value.length,
439
+ componentKey: componentKey.value,
440
+ workflowSteps: workflowSteps.value
441
+ })
442
+
443
+ // 使用nextTick确保DOM更新
444
+ nextTick(() => {
445
+ // 重新初始化文本框
446
+ autoResizeTextarea()
447
+
448
+ // 滚动到顶部
449
+ const chatArea = document.querySelector('.chat-area')
450
+ if (chatArea) {
451
+ chatArea.scrollTop = 0
452
+ }
453
+
454
+ console.log('重置完成 - DOM已更新')
455
+ })
456
+ }
457
+
404
458
  // 暴露方法和子组件给父组件使用
405
459
  defineExpose({
406
460
  setPatientComplaint,
461
+ reset,
407
462
  getPreliminaryDiagnosis: () => preliminaryDiagnosisRef.value,
408
463
  getExaminationOrder: () => examinationOrderRef.value,
409
464
  getDiagnosisSuggestion: () => diagnosisSuggestionRef.value,
@@ -68,6 +68,9 @@
68
68
  <script setup>
69
69
  import { ref, computed, watch, onMounted } from 'vue'
70
70
 
71
+ // AI 服务配置 - 暂时使用完整地址(代理配置待修复)
72
+ const AI_API_URL = 'http://192.168.50.67:30556/predict'
73
+
71
74
  // Props
72
75
  const props = defineProps({
73
76
  patientComplaint: {
@@ -118,16 +121,13 @@ const generatePreliminaryDiagnosis = (complaint) => {
118
121
  const fetchAISuggestions = async () => {
119
122
  try {
120
123
  const requestData = {
121
- 'known_facts': [
122
- '症状特征.胸痛.起病时间=今天',
123
- '伴随表现.呼吸困难.是否=是'
124
- ],
124
+ 'known_facts': props.patientComplaint ? [props.patientComplaint] : [],
125
125
  'top_n': 5,
126
126
  'step_id': 0,
127
127
  'asked_count': 0
128
128
  }
129
129
 
130
- const response = await fetch('http://192.168.50.67:30556/predict', {
130
+ const response = await fetch(AI_API_URL, {
131
131
  method: 'POST',
132
132
  headers: {
133
133
  'Content-Type': 'application/json',
@@ -113,10 +113,10 @@
113
113
  <a-spin tip="加载中,马上好" :spinning="loading">
114
114
  <iframe
115
115
  id="x-editor"
116
- src="/his/editor/editor.html"
116
+ :src="iframeSrc"
117
117
  v-bind="objectOfAttrs"
118
118
  @load="onload"
119
- style="height: calc(-200px - 3rem + 100vh);">
119
+ :style="iframeStyle">
120
120
  </iframe>
121
121
  </a-spin>
122
122
  </div>
@@ -145,6 +145,12 @@ import { initDiagnosisDropdown } from './diagnosisAutocomplete'
145
145
 
146
146
  export default {
147
147
  name: 'XHisEditor',
148
+ props: {
149
+ hideEditorToolbar: {
150
+ type: Boolean,
151
+ default: true
152
+ }
153
+ },
148
154
  computed: {
149
155
  resListCp () {
150
156
  const list = this.resList
@@ -158,12 +164,38 @@ export default {
158
164
  // 过滤字段列表 - 提取重复逻辑
159
165
  filterFields () {
160
166
  return this.fieldFilter ? this.fieldFilter.split(',').map(f => f.trim()).filter(f => f) : []
167
+ },
168
+ // iframe样式
169
+ iframeStyle () {
170
+ const baseStyle = {
171
+ height: 'calc(-200px - 3rem + 100vh)'
172
+ }
173
+
174
+ return baseStyle
175
+ },
176
+ // iframe src
177
+ iframeSrc () {
178
+ let src = '/his/editor/editor.html'
179
+
180
+ if (this.hideEditorToolbar) {
181
+ // 使用最直接的参数名,确保编辑器能识别
182
+ src += '?toolbar=false&menubar=false&hideToolbar=true&hideMenubar=true'
183
+ console.log('iframe URL设置为隐藏工具栏:', src)
184
+ } else {
185
+ src += '?toolbar=true&menubar=true'
186
+ console.log('iframe URL设置为显示工具栏:', src)
187
+ }
188
+
189
+ return src
161
190
  }
162
191
  },
163
192
  created() {
164
193
  // 初始化字段过滤器
165
194
  this.fieldFilter = this.$attrs.fieldFilter
166
195
  },
196
+ mounted() {
197
+ // 组件挂载时不需要隐藏,等待iframe onload事件
198
+ },
167
199
  beforeDestroy() {
168
200
  // 清理防抖定时器
169
201
  if (this.debounceTimer) {
@@ -420,11 +452,16 @@ export default {
420
452
  this.editorRef = e.target.contentWindow.editor
421
453
  this.$emit('init', {})
422
454
 
455
+ // 隐藏工具栏
456
+ if (this.hideEditorToolbar) {
457
+ setTimeout(() => this.hideToolbar(), 500)
458
+ }
459
+
423
460
  // 如果配置了字段过滤,延迟初始化监听器
424
461
  if (this.fieldFilter && this.fieldFilter.trim()) {
425
462
  setTimeout(() => {
426
463
  this.setupInputEndListener()
427
- }, 1000) // 等待数据完全加载
464
+ }, 1000)
428
465
  }
429
466
  }
430
467
  },
@@ -478,6 +515,7 @@ export default {
478
515
  this.loadResList()
479
516
  }
480
517
  },
518
+
481
519
  // 加载文档
482
520
  loadFile (fileUrl, bindObject, currResData) {
483
521
  this.loading = true
@@ -524,7 +562,6 @@ export default {
524
562
  },
525
563
  // 加载文档列表
526
564
  loadResList () {
527
- console.log("getTemplateListLogic=======",this.logicExtraParams)
528
565
  runLogic('getTemplateListLogic', this.logicExtraParams, this.serviceName).then(res => {
529
566
  this.resList = res
530
567
  this.toggleResId = this.resId
@@ -906,6 +943,34 @@ export default {
906
943
  handleExportMenuClick (e) {
907
944
  const key = e.key
908
945
  this.editorRef.execCommand(key)
946
+ },
947
+ // 隐藏工具栏
948
+ hideToolbar () {
949
+ if (!this.hideEditorToolbar) return
950
+
951
+ try {
952
+ // 通过iframe访问文档
953
+ const iframe = document.getElementById('x-editor')
954
+ if (!iframe || !iframe.contentDocument) return
955
+
956
+ const doc = iframe.contentDocument
957
+
958
+ // 注入CSS样式隐藏工具栏并调整布局
959
+ const style = doc.createElement('style')
960
+ style.textContent = `
961
+ #_toolbar { display: none !important; }
962
+ #_tool-tab { display: none !important; }
963
+ .tool-bar { display: none !important; }
964
+ #_main {
965
+ top: 0px !important;
966
+ height: 100% !important;
967
+ }
968
+ `
969
+ doc.head.appendChild(style)
970
+
971
+ } catch (error) {
972
+ // 静默失败
973
+ }
909
974
  }
910
975
  }
911
976
  }
@@ -966,4 +1031,10 @@ export default {
966
1031
  background-color: rgb(0, 87, 254) !important;
967
1032
  }
968
1033
  }
1034
+
1035
+ /* 隐藏工具栏的样式 - 当iframe不可访问时使用 */
1036
+ :deep(#x-editor.hide-toolbar) {
1037
+ /* 当工具栏隐藏时,可以在这里调整iframe的样式 */
1038
+ margin-top: -50px; /* 向上移动以减少空白 */
1039
+ }
969
1040
  </style>
package/vue.config.js CHANGED
@@ -133,6 +133,13 @@ module.exports = {
133
133
  target: OSSServerDev,
134
134
  pathRewrite: { '^/oss': '/' },
135
135
  changeOrigin: true
136
+ },
137
+ '/hai-api': {
138
+ target: 'http://192.168.50.67:30556',
139
+ pathRewrite: { '^/hai-api': '' },
140
+ changeOrigin: true,
141
+ secure: false,
142
+ ws: false
136
143
  }
137
144
  },
138
145
  client: {