vue2-client 1.14.66 → 1.14.68

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 (67) hide show
  1. package/.history/public/his/editor/editor_20250606134713.html +51 -0
  2. package/.history/src/base-client/components/common/AddressSearchCombobox/AddressSearchCombobox_20250527173925.vue +509 -0
  3. package/.history/src/base-client/components/common/AddressSearchCombobox/AddressSearchCombobox_20250527174316.vue +524 -0
  4. package/.history/src/base-client/components/common/AddressSearchCombobox/AddressSearchCombobox_20250527174419.vue +524 -0
  5. package/.history/src/base-client/components/common/AddressSearchCombobox/AddressSearchCombobox_20250527174422.vue +524 -0
  6. package/.history/src/base-client/components/common/XForm/XFormItem_20250508134122.vue +1320 -0
  7. package/.history/src/base-client/components/common/XForm/XFormItem_20250527171604.vue +1332 -0
  8. package/.history/src/base-client/components/common/XForm/XFormItem_20250527171613.vue +1331 -0
  9. package/.history/src/base-client/components/common/XForm/XFormItem_20250527171703.vue +1331 -0
  10. package/.history/src/base-client/components/common/XForm/XFormItem_20250527171720.vue +1331 -0
  11. package/.history/src/base-client/components/common/XForm/XFormItem_20250527174327.vue +1339 -0
  12. package/.history/src/base-client/components/his/XList/XList_20250609135848.vue +173 -0
  13. package/.history/src/base-client/components/his/XList/XList_20250609141026.vue +222 -0
  14. package/.history/src/base-client/components/his/XList/XList_20250609141035.vue +229 -0
  15. package/.history/src/base-client/components/his/XList/XList_20250609141103.vue +229 -0
  16. package/.history/src/base-client/components/his/XList/XList_20250609141105.vue +229 -0
  17. package/.history/src/base-client/components/his/XList/XList_20250609141334.vue +241 -0
  18. package/.history/src/base-client/components/his/XList/XList_20250609141404.vue +241 -0
  19. package/.history/src/base-client/components/his/XList/XList_20250609141406.vue +241 -0
  20. package/.history/src/base-client/components/his/XList/XList_20250609141801.vue +245 -0
  21. package/.history/src/base-client/components/his/XList/XList_20250609142033.vue +245 -0
  22. package/.history/src/base-client/components/his/XList/XList_20250609142038.vue +245 -0
  23. package/.history/src/base-client/components/his/XList/XList_20250609142435.vue +255 -0
  24. package/.history/src/base-client/components/his/XList/XList_20250609142503.vue +255 -0
  25. package/.history/src/base-client/components/his/XList/XList_20250609142504.vue +255 -0
  26. package/.history/src/base-client/components/his/XList/XList_20250609143012.vue +270 -0
  27. package/.history/src/base-client/components/his/XList/XList_20250609143044.vue +270 -0
  28. package/.history/src/base-client/components/his/XList/XList_20250609143046.vue +270 -0
  29. package/.history/src/base-client/components/his/XList/XList_20250609143210.vue +270 -0
  30. package/.history/src/base-client/components/his/XList/XList_20250609144339.vue +294 -0
  31. package/.history/src/base-client/components/his/XList/XList_20250609144410.vue +294 -0
  32. package/.history/src/base-client/components/his/XList/XList_20250609144412.vue +294 -0
  33. package/.history/src/base-client/components/his/XList/XList_20250609144647.vue +303 -0
  34. package/.history/src/base-client/components/his/XList/XList_20250609144716.vue +303 -0
  35. package/.history/src/base-client/components/his/XList/XList_20250609144729.vue +303 -0
  36. package/.history/src/base-client/components/his/XList/XList_20250609151232.vue +288 -0
  37. package/.history/src/base-client/components/his/XList/XList_20250609151247.vue +288 -0
  38. package/.history/src/base-client/components/his/XList/XList_20250609151252.vue +288 -0
  39. package/.history/src/base-client/components/his/XList/XList_20250609161220.vue +317 -0
  40. package/.history/src/base-client/components/his/XList/XList_20250609161258.vue +306 -0
  41. package/.history/src/base-client/components/his/XList/XList_20250609161319.vue +306 -0
  42. package/.history/src/base-client/components/his/XList/XList_20250609161320.vue +306 -0
  43. package/Users/objecrt/af-vue2-client/src/base-client/components/his/XShiftSchedule/XShiftSchedule.vue +36 -0
  44. package/docs/Logic/345/207/275/346/225/260/344/275/277/347/224/250/347/233/270/345/205/263.md +1 -0
  45. package/docs//345/207/275/346/225/260/344/275/277/347/224/250/347/233/270/345/205/263.md +2 -1
  46. package/package.json +1 -1
  47. package/src/base-client/components/TreeList/TreeList.vue +91 -0
  48. package/src/base-client/components/TreeList/TreeNode.vue +81 -0
  49. package/src/base-client/components/common/AmapMarker/index.js +3 -3
  50. package/src/base-client/components/common/XCardSet/XTiltle.vue +191 -0
  51. package/src/base-client/components/common/XDetailsView/index.js +3 -3
  52. package/src/base-client/components/common/XFormGroupDetails/index.js +3 -3
  53. package/src/base-client/components/his/XList/XList.vue +155 -22
  54. package/src/base-client/components/his/XTextCard/XTextCard.vue +207 -207
  55. package/src/base-client/components/his/XTreeRows/TreeNode.vue +100 -100
  56. package/src/base-client/components/his/XTreeRows/XTreeRows.vue +197 -197
  57. package/src/base-client/components/his/threeTestOrders/editor.vue +111 -111
  58. package/src/base-client/components/his/threeTestOrders/threeTestOrders.vue +76 -13
  59. package/src/components/FileImageItem/FileItem.vue +268 -23
  60. package/src/components/FileImageItem/FileItemGroup.vue +197 -0
  61. package/src/components/FileImageItem/ImageItem.vue +32 -3
  62. package/src/pages/WorkflowDetail/WorkFlowDemo.vue +36 -79
  63. package/src/pages/WorkflowDetail/WorkflowDetail.vue +16 -7
  64. package/src/pages/WorkflowDetail/WorkflowPageDetail/WorkFlowHandle.vue +74 -31
  65. package/src/pages/WorkflowDetail/WorkflowPageDetail/WorkFlowTimeline.vue +1 -1
  66. package/src/services/api/workFlow.js +3 -1
  67. package/vue.config.js +2 -2
@@ -4,18 +4,20 @@
4
4
  <a-row class="box">
5
5
  病历号&nbsp;&nbsp;<a-input v-model="vitalSignsId" style="width: 160px"></a-input>
6
6
  <a-button-group style="margin-left: 20px;">
7
- <a-button v-if="showCreateButton"
8
- plain
9
- type="primary"
10
- @click="showVitalSignsModal('create')"
11
- :loading="loading"
12
- :disabled="!editorReady">创建体温单</a-button>
13
- <a-button v-if="showUpdateButton"
14
- plain
15
- type="primary"
16
- @click="showVitalSignsModal('update')"
17
- :loading="loading"
18
- :disabled="!editorReady">更新体温单</a-button>
7
+ <a-button
8
+ v-if="showCreateButton"
9
+ plain
10
+ type="primary"
11
+ @click="showVitalSignsModal('create')"
12
+ :loading="loading"
13
+ :disabled="!editorReady">创建体温单</a-button>
14
+ <a-button
15
+ v-if="showUpdateButton"
16
+ plain
17
+ type="primary"
18
+ @click="showVitalSignsModal('update')"
19
+ :loading="loading"
20
+ :disabled="!editorReady">更新体温单</a-button>
19
21
  </a-button-group>
20
22
  <a-button-group v-if="showClearButton" style="margin: 0 20px;">
21
23
  <a-button plain type="primary" @click="execCommand('print')" :loading="loading" :disabled="!editorReady">打印</a-button>
@@ -337,6 +339,65 @@ async function handleSubmit (formData) {
337
339
  }
338
340
  }
339
341
 
342
+ // 处理表单数据
343
+ function mergeAndTransformData (records, patientInfo, id, date, operateDate) {
344
+ // 提取records中的数据
345
+ const temperatureList = records.map(record => record.temperature).join(',')
346
+ const sphygmusList = records.map(record => record.sphygmus).join(',')
347
+ const breathList = records.map(record => record.breath).join(',')
348
+ const heartList = records.map(record => record.heart).join(',')
349
+ const notesList = records.map(record => record.notes).join(',')
350
+ const painList = records.map(record => record.pain).join(',')
351
+ const data1List = records.map(record => {
352
+ const [systolic, diastolic] = record.data1.split('|')
353
+ return `${systolic}/${diastolic}`
354
+ }).join(',')
355
+ const data2List = records.map(record => record.data2).join(',')
356
+ const data3List = records.map(record => record.data3).join(',')
357
+ const data4List = records.map(record => record.data4).join(',')
358
+ const data5List = records.map(record => record.data5).join(',')
359
+ const data6List = records.map(record => record.data6).join(',')
360
+ const data7List = records.map(record => record.data7).join(',')
361
+ const tempTypeList = records.map(record => record.temptype).join(',')
362
+
363
+ // 提取patientInfo中的数据
364
+ const name = patientInfo.name || ''
365
+ const dept = patientInfo.dept || ''
366
+ const bed = patientInfo.bed || ''
367
+ const inDate = patientInfo.indate || ''
368
+ const diag = patientInfo.diag || ''
369
+
370
+ // 生成合并后的对象
371
+ const mergedData = {
372
+ bed: bed.toString(),
373
+ sphygmus: sphygmusList,
374
+ notes: notesList,
375
+ medicalNo: id, // 示例中的固定值
376
+ diag: diag,
377
+ type: 'normal', // 示例中的固定值
378
+ breath: breathList,
379
+ temperature: temperatureList,
380
+ id: id, // 示例中的固定值
381
+ tempType: tempTypeList,
382
+ data7: data7List,
383
+ pain: painList,
384
+ data6: data6List,
385
+ data5: data5List,
386
+ data4: data4List,
387
+ data3: data3List,
388
+ data2: data2List,
389
+ inDate: inDate.split(' ')[0], // 只取日期部分
390
+ data1: data1List,
391
+ dept: dept,
392
+ heart: heartList,
393
+ labels: '血压(mmHg)|入水量(ml)|出水量(ml)|大便(次)|小便(次)|身高(cm)|体重(kg)', // 示例中的固定值
394
+ name: name,
395
+ begin: date, // 只取日期部分
396
+ operateDate: operateDate // 只取日期部分
397
+ }
398
+ return mergedData
399
+ }
400
+
340
401
  // 生命周期钩子
341
402
  onMounted(() => {
342
403
  window.addEventListener('message', (event) => {
@@ -371,7 +432,9 @@ defineExpose({
371
432
  vitalSignsId.value = id
372
433
  },
373
434
  // 创建体温单 参数:(Object) data
374
- handleSubmit
435
+ handleSubmit,
436
+ // 获取表单数据
437
+ mergeAndTransformData
375
438
  })
376
439
  </script>
377
440
 
@@ -1,23 +1,59 @@
1
1
  <template>
2
- <div>
3
- <!-- 文件 -->
4
- <div v-for="file in files" :key="file.id">
5
- <a class="file-item">
6
- <span class="file-action" @click="preview(file.url)">
7
- <a-icon type="link" />{{ file.name }}
8
- </span>
9
- <span class="file-action" @click="preview(file.url)">
10
- <a-icon type="eye" />预览
11
- </span>
12
- <a v-if="downloadable" class="file-action" @click="download(file)"><a-icon type="download" />下载</a>
13
- </a>
2
+ <div class="file-list">
3
+ <div class="file-grid">
4
+ <div v-for="file in files" :key="file.id" class="file-item">
5
+ <div class="file-info">
6
+ <div class="file-icon-wrapper" :class="getFileTypeClass(file.name)">
7
+ <a-icon :type="getFileIcon(file.name)" class="file-icon" />
8
+ </div>
9
+ <div class="file-content">
10
+ <div class="file-header">
11
+ <span class="file-name">{{ file.name }}</span>
12
+ <span class="file-type">{{ getFileType(file.name) }}</span>
13
+ </div>
14
+ <div class="file-footer">
15
+ <span class="file-size" v-if="file.size">{{ formatFileSize(file.size) }}</span>
16
+ <span class="file-time" v-if="file.upload_time">{{ file.upload_time }}</span>
17
+ </div>
18
+ </div>
19
+ </div>
20
+ <div class="file-actions">
21
+ <a-tooltip title="预览文件">
22
+ <a-button type="link" class="action-btn preview-btn" @click="handlePreview(file.url)">
23
+ <a-icon type="eye" />
24
+ </a-button>
25
+ </a-tooltip>
26
+ <a-tooltip v-if="downloadable" title="下载文件">
27
+ <a-button type="link" class="action-btn download-btn" @click="download(file)">
28
+ <a-icon type="download" />
29
+ </a-button>
30
+ </a-tooltip>
31
+ </div>
32
+ </div>
14
33
  </div>
34
+ <!-- 文件预览弹窗 -->
35
+ <a-modal
36
+ v-model="previewVisible"
37
+ :footer="null"
38
+ :dialog-style="{ top: '20px' }"
39
+ width="85%"
40
+ :z-index="1001"
41
+ title="文件预览"
42
+ @cancel="handlePreviewClose"
43
+ >
44
+ <file-preview :path="previewPath" />
45
+ </a-modal>
15
46
  </div>
16
47
  </template>
17
48
 
18
49
  <script>
50
+ import FilePreview from '@vue2-client/components/FilePreview'
51
+
19
52
  export default {
20
53
  name: 'FileImageItem',
54
+ components: {
55
+ FilePreview
56
+ },
21
57
  props: {
22
58
  files: {
23
59
  type: Array,
@@ -29,32 +65,241 @@ export default {
29
65
  default: true
30
66
  }
31
67
  },
68
+ data () {
69
+ return {
70
+ previewVisible: false,
71
+ previewPath: ''
72
+ }
73
+ },
32
74
  methods: {
33
- preview (path) {
34
- this.$emit('preview', path)
75
+ handlePreview (path) {
76
+ this.previewPath = path
77
+ this.previewVisible = true
78
+ },
79
+ handlePreviewClose () {
80
+ this.previewVisible = false
81
+ this.previewPath = ''
35
82
  },
36
- // 文件下载
37
83
  download (file) {
38
84
  const a = document.createElement('a')
39
85
  a.href = file.url
40
86
  a.download = file.name
41
87
  a.click()
88
+ },
89
+ formatFileSize (size) {
90
+ if (!size) return ''
91
+ // 如果小于1M,转换为KB显示
92
+ if (size < 1) {
93
+ const kbSize = size * 1024
94
+ return `${kbSize.toFixed(1)} KB`
95
+ }
96
+ // 大于等于1M,保持M为单位
97
+ return `${size.toFixed(1)} MB`
98
+ },
99
+ getFileType (filename) {
100
+ const ext = filename.split('.').pop().toLowerCase()
101
+ const typeMap = {
102
+ pdf: 'PDF文档',
103
+ doc: 'Word文档',
104
+ docx: 'Word文档',
105
+ xls: 'Excel表格',
106
+ xlsx: 'Excel表格',
107
+ ppt: 'PPT演示',
108
+ pptx: 'PPT演示',
109
+ jpg: '图片',
110
+ jpeg: '图片',
111
+ png: '图片',
112
+ gif: '图片',
113
+ txt: '文本文件',
114
+ zip: '压缩文件',
115
+ rar: '压缩文件'
116
+ }
117
+ return typeMap[ext] || '文件'
118
+ },
119
+ getFileIcon (filename) {
120
+ const ext = filename.split('.').pop().toLowerCase()
121
+ const iconMap = {
122
+ pdf: 'file-pdf',
123
+ doc: 'file-word',
124
+ docx: 'file-word',
125
+ xls: 'file-excel',
126
+ xlsx: 'file-excel',
127
+ ppt: 'file-ppt',
128
+ pptx: 'file-ppt',
129
+ jpg: 'file-image',
130
+ jpeg: 'file-image',
131
+ png: 'file-image',
132
+ gif: 'file-image',
133
+ txt: 'file-text',
134
+ zip: 'file-zip',
135
+ rar: 'file-zip'
136
+ }
137
+ return iconMap[ext] || 'file'
138
+ },
139
+ getFileTypeClass (filename) {
140
+ const ext = filename.split('.').pop().toLowerCase()
141
+ return `file-type-${ext}`
42
142
  }
43
143
  }
44
144
  }
45
145
  </script>
46
146
 
47
147
  <style lang="less" scoped>
48
- .file-item {
49
- .file-action {
50
- padding: 3px 4px;
51
- color: #1890ff;
148
+ .file-list {
149
+ .file-grid {
150
+ display: grid;
151
+ grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
152
+ gap: 16px;
153
+ }
154
+
155
+ .file-item {
156
+ display: flex;
157
+ align-items: center;
158
+ justify-content: space-between;
159
+ padding: 16px;
160
+ background: #fff;
161
+ border-radius: 12px;
162
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
163
+ border: 1px solid rgba(0, 0, 0, 0.06);
164
+
52
165
  &:hover {
53
- background: #e6f7ff;
166
+ transform: translateY(-2px);
167
+ border-color: @primary-2;
168
+ background: rgba(24, 144, 255, 0.02);
169
+ }
170
+
171
+ .file-info {
172
+ display: flex;
173
+ align-items: center;
174
+ flex: 1;
175
+ min-width: 0;
176
+
177
+ .file-icon-wrapper {
178
+ display: flex;
179
+ align-items: center;
180
+ justify-content: center;
181
+ width: 48px;
182
+ height: 48px;
183
+ margin-right: 16px;
184
+ border-radius: 12px;
185
+ transition: all 0.3s;
186
+
187
+ &.file-type-pdf {
188
+ background: linear-gradient(135deg, #ff4d4f, #ff7875);
189
+ }
190
+ &.file-type-doc, &.file-type-docx {
191
+ background: linear-gradient(135deg, #1890ff, #69c0ff);
192
+ }
193
+ &.file-type-xls, &.file-type-xlsx {
194
+ background: linear-gradient(135deg, #52c41a, #95de64);
195
+ }
196
+ &.file-type-ppt, &.file-type-pptx {
197
+ background: linear-gradient(135deg, #fa8c16, #ffc069);
198
+ }
199
+ &.file-type-jpg, &.file-type-jpeg, &.file-type-png, &.file-type-gif {
200
+ background: linear-gradient(135deg, #722ed1, #b37feb);
201
+ }
202
+ &.file-type-txt {
203
+ background: linear-gradient(135deg, #13c2c2, #5cdbd3);
204
+ }
205
+ &.file-type-zip, &.file-type-rar {
206
+ background: linear-gradient(135deg, #eb2f96, #ff85c0);
207
+ }
208
+
209
+ .file-icon {
210
+ font-size: 24px;
211
+ color: #fff;
212
+ }
213
+ }
214
+
215
+ .file-content {
216
+ display: flex;
217
+ flex-direction: column;
218
+ min-width: 0;
219
+ flex: 1;
220
+
221
+ .file-header {
222
+ display: flex;
223
+ align-items: center;
224
+ margin-bottom: 4px;
225
+
226
+ .file-name {
227
+ color: rgba(0, 0, 0, 0.85);
228
+ font-size: 15px;
229
+ font-weight: 500;
230
+ overflow: hidden;
231
+ text-overflow: ellipsis;
232
+ white-space: nowrap;
233
+ margin-right: 8px;
234
+ width: 0;
235
+ flex: 1;
236
+ }
237
+
238
+ .file-type {
239
+ color: rgba(0, 0, 0, 0.45);
240
+ font-size: 12px;
241
+ padding: 2px 6px;
242
+ background: rgba(0, 0, 0, 0.04);
243
+ border-radius: 4px;
244
+ }
245
+ }
246
+
247
+ .file-footer {
248
+ display: flex;
249
+ align-items: center;
250
+ color: rgba(0, 0, 0, 0.45);
251
+ font-size: 12px;
252
+
253
+ .file-size {
254
+ margin-right: 12px;
255
+ }
256
+
257
+ .file-time {
258
+ position: relative;
259
+ padding-left: 12px;
260
+
261
+ &::before {
262
+ content: '';
263
+ position: absolute;
264
+ left: 0;
265
+ top: 50%;
266
+ transform: translateY(-50%);
267
+ width: 4px;
268
+ height: 4px;
269
+ background: rgba(0, 0, 0, 0.25);
270
+ border-radius: 50%;
271
+ }
272
+ }
273
+ }
274
+ }
275
+ }
276
+
277
+ .file-actions {
278
+ display: flex;
279
+ align-items: center;
280
+ margin-left: 16px;
281
+
282
+ .action-btn {
283
+ width: 36px;
284
+ height: 36px;
285
+ padding: 0;
286
+ margin-left: 8px;
287
+ border-radius: 8px;
288
+ color: rgba(0, 0, 0, 0.45);
289
+ background: transparent;
290
+ transition: all 0.3s;
291
+
292
+ &:hover {
293
+ color: #fff;
294
+ background: @primary-color;
295
+ transform: scale(1.05);
296
+ }
297
+
298
+ .anticon {
299
+ font-size: 16px;
300
+ }
301
+ }
54
302
  }
55
- }
56
- .anticon {
57
- margin-right: 3px;
58
303
  }
59
304
  }
60
305
  </style>
@@ -0,0 +1,197 @@
1
+ <template>
2
+ <div class="file-item-group">
3
+ <!-- 搜索区域 -->
4
+ <div class="search-area">
5
+ <a-form-model ref="searchForm" :model="formData" layout="inline">
6
+ <a-form-model-item prop="dateRange">
7
+ <a-range-picker
8
+ v-model="formData.dateRange"
9
+ :ranges="dateRanges"
10
+ :show-time="{ format: 'HH:mm' }"
11
+ format="YYYY-MM-DD HH:mm"
12
+ value-format="YYYY-MM-DD HH:mm:ss"
13
+ :placeholder="['开始时间', '结束时间']"
14
+ @change="handleDateChange"
15
+ />
16
+ </a-form-model-item>
17
+ <a-form-model-item>
18
+ <a-button type="primary" :loading="loading" @click="handleSearch">
19
+ <a-icon type="search" />查询
20
+ </a-button>
21
+ </a-form-model-item>
22
+ </a-form-model>
23
+ </div>
24
+
25
+ <!-- 文件列表区域 -->
26
+ <template v-if="Object.keys(groupedFiles).length > 0">
27
+ <div class="file-list-container">
28
+ <a-spin :spinning="loading">
29
+ <template v-for="(group, date) in groupedFiles">
30
+ <div :key="date" class="file-group">
31
+ <div class="group-header">
32
+ <a-icon type="calendar" />
33
+ <span class="date-label">{{ formatDate(date) }}</span>
34
+ <span class="file-count">({{ group.length }}个文件)</span>
35
+ </div>
36
+ <file-item
37
+ :files="group"
38
+ :downloadable="downloadable"
39
+ />
40
+ </div>
41
+ </template>
42
+ </a-spin>
43
+ </div>
44
+ </template>
45
+ <template v-else>
46
+ <a-empty></a-empty>
47
+ </template>
48
+ </div>
49
+ </template>
50
+
51
+ <script>
52
+ import FileItem from './FileItem.vue'
53
+
54
+ export default {
55
+ name: 'FileItemGroup',
56
+ components: {
57
+ FileItem
58
+ },
59
+ props: {
60
+ downloadable: {
61
+ type: Boolean,
62
+ default: true
63
+ }
64
+ },
65
+ data () {
66
+ const now = new Date()
67
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
68
+ const yesterday = new Date(today)
69
+ yesterday.setDate(yesterday.getDate() - 1)
70
+ const last7Days = new Date(today)
71
+ last7Days.setDate(last7Days.getDate() - 6)
72
+ const last30Days = new Date(today)
73
+ last30Days.setDate(last30Days.getDate() - 29)
74
+
75
+ return {
76
+ loading: false,
77
+ formData: {
78
+ dateRange: []
79
+ },
80
+ files: [],
81
+ dateRanges: {
82
+ 今天: [today, new Date(today.getTime() + 24 * 60 * 60 * 1000 - 1)],
83
+ 昨天: [yesterday, new Date(yesterday.getTime() + 24 * 60 * 60 * 1000 - 1)],
84
+ 最近7天: [last7Days, new Date(today.getTime() + 24 * 60 * 60 * 1000 - 1)],
85
+ 最近30天: [last30Days, new Date(today.getTime() + 24 * 60 * 60 * 1000 - 1)]
86
+ }
87
+ }
88
+ },
89
+ mounted () {
90
+ this.handleSearch()
91
+ },
92
+ computed: {
93
+ groupedFiles () {
94
+ const groups = {}
95
+ this.files.forEach(file => {
96
+ const date = file.upload_time.split(' ')[0]
97
+ if (!groups[date]) {
98
+ groups[date] = []
99
+ }
100
+ groups[date].push(file)
101
+ })
102
+ // 按日期降序排序
103
+ return Object.fromEntries(
104
+ Object.entries(groups).sort(([a], [b]) => b.localeCompare(a))
105
+ )
106
+ }
107
+ },
108
+ methods: {
109
+ handleDateChange (dates) {
110
+ this.formData.dateRange = dates
111
+ },
112
+ handleSearch () {
113
+ this.$refs.searchForm.validate(async valid => {
114
+ if (valid) {
115
+ this.loading = true
116
+ let queryDate
117
+ if (this.formData.dateRange && this.formData.dateRange.length > 0) {
118
+ queryDate = this.formData.dateRange
119
+ } else {
120
+ queryDate = undefined
121
+ }
122
+ const callback = (res) => {
123
+ this.files = res.files
124
+ this.loading = false
125
+ }
126
+ const errorCallback = (e) => {
127
+ this.$message.error('获取文件列表失败:' + e.getMessage())
128
+ this.loading = false
129
+ }
130
+ this.$emit('search', queryDate, callback, errorCallback)
131
+ } else {
132
+ this.$message.error('请检查日期选择是否正确')
133
+ }
134
+ })
135
+ },
136
+ formatDate (date) {
137
+ const today = new Date().toISOString().split('T')[0]
138
+ const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString().split('T')[0]
139
+
140
+ if (date === today) {
141
+ return '今天'
142
+ } else if (date === yesterday) {
143
+ return '昨天'
144
+ }
145
+
146
+ const [year, month, day] = date.split('-')
147
+ return `${year}年${month}月${day}日`
148
+ }
149
+ }
150
+ }
151
+ </script>
152
+
153
+ <style lang="less" scoped>
154
+ .file-item-group {
155
+ .search-area {
156
+ display: flex;
157
+ gap: 16px;
158
+ margin-bottom: 24px;
159
+ padding: 16px;
160
+ background: #fff;
161
+ border-radius: 8px;
162
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
163
+ }
164
+
165
+ .file-list-container {
166
+ .file-group {
167
+ margin-bottom: 24px;
168
+
169
+ .group-header {
170
+ display: flex;
171
+ align-items: center;
172
+ margin-bottom: 16px;
173
+ padding: 8px 16px;
174
+ background: #fafafa;
175
+ border-radius: 6px;
176
+
177
+ .anticon {
178
+ margin-right: 8px;
179
+ color: @primary-color;
180
+ }
181
+
182
+ .date-label {
183
+ font-size: 15px;
184
+ font-weight: 500;
185
+ color: rgba(0, 0, 0, 0.85);
186
+ }
187
+
188
+ .file-count {
189
+ margin-left: 8px;
190
+ color: rgba(0, 0, 0, 0.45);
191
+ font-size: 13px;
192
+ }
193
+ }
194
+ }
195
+ }
196
+ }
197
+ </style>
@@ -4,15 +4,32 @@
4
4
  <span v-for="img in images" :key="img.id" class="picture-card" :style="{ width: cardSize, height: cardSize }">
5
5
  <img :src="img.url" :alt="img.name" />
6
6
  <span class="picture-action" :style="{ width: actionSize, height: actionSize }">
7
- <a-icon type="eye" class="picture-preview-icon" @click="preview(img.url)" />
7
+ <a-icon type="eye" class="picture-preview-icon" @click="handlePreview(img.url)" />
8
8
  </span>
9
9
  </span>
10
+ <!-- 图片预览弹窗 -->
11
+ <a-modal
12
+ v-model="previewVisible"
13
+ :footer="null"
14
+ :dialog-style="{ top: '20px' }"
15
+ width="85%"
16
+ :z-index="1001"
17
+ title="图片预览"
18
+ @cancel="handlePreviewClose"
19
+ >
20
+ <file-preview :path="previewPath" />
21
+ </a-modal>
10
22
  </div>
11
23
  </template>
12
24
 
13
25
  <script>
26
+ import FilePreview from '@vue2-client/components/FilePreview'
27
+
14
28
  export default {
15
29
  name: 'ImageItem',
30
+ components: {
31
+ FilePreview
32
+ },
16
33
  props: {
17
34
  images: {
18
35
  type: Array,
@@ -23,6 +40,12 @@ export default {
23
40
  default: 150
24
41
  }
25
42
  },
43
+ data () {
44
+ return {
45
+ previewVisible: false,
46
+ previewPath: ''
47
+ }
48
+ },
26
49
  computed: {
27
50
  cardSize: function () {
28
51
  return this.width + 'px'
@@ -32,8 +55,13 @@ export default {
32
55
  }
33
56
  },
34
57
  methods: {
35
- preview (path) {
36
- this.$emit('preview', path)
58
+ handlePreview (path) {
59
+ this.previewPath = path
60
+ this.previewVisible = true
61
+ },
62
+ handlePreviewClose () {
63
+ this.previewVisible = false
64
+ this.previewPath = ''
37
65
  }
38
66
  }
39
67
  }
@@ -51,6 +79,7 @@ export default {
51
79
  img {
52
80
  width: 100%;
53
81
  height: 100%;
82
+ object-fit: cover;
54
83
  }
55
84
  .picture-action {
56
85
  position: absolute;