vue2-client 1.14.67 → 1.14.69
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 +1 -1
- package/src/base-client/components/common/XReport/XReport.vue +72 -1
- package/src/base-client/components/common/XReport/XReportDemo.vue +1 -2
- package/src/base-client/components/common/XReport/XReportTrGroup.vue +218 -29
- package/src/base-client/components/common/XReport/index.md +59 -0
- package/src/base-client/components/his/XList/XList.vue +155 -22
- package/src/base-client/components/his/threeTestOrders/threeTestOrders.vue +62 -1
- package/src/pages/WorkflowDetail/WorkflowPageDetail/WorkFlowHandle.vue +1 -1
package/package.json
CHANGED
|
@@ -247,7 +247,17 @@ export default {
|
|
|
247
247
|
this.$emit('updateImg', data)
|
|
248
248
|
},
|
|
249
249
|
// 导出数据,某些外部需要统一控制数据的变动
|
|
250
|
-
exportData () {
|
|
250
|
+
exportData (skipValidation = false) {
|
|
251
|
+
// 如果不跳过校验,先进行必填字段校验
|
|
252
|
+
if (!skipValidation) {
|
|
253
|
+
const validation = this.validateRequiredFields()
|
|
254
|
+
if (!validation.isValid) {
|
|
255
|
+
const errorMessages = validation.errors.map(error => error.message).join(';')
|
|
256
|
+
console.warn(`数据导出警告:存在必填字段未填写 - ${errorMessages}`)
|
|
257
|
+
// 在导出时只警告,不阻止导出
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
251
261
|
// 获取当前修改后的数据
|
|
252
262
|
let tempData
|
|
253
263
|
if (this.activeConfig === undefined || this.activeConfig === null) {
|
|
@@ -296,8 +306,68 @@ export default {
|
|
|
296
306
|
}
|
|
297
307
|
}
|
|
298
308
|
},
|
|
309
|
+
// 验证必填字段
|
|
310
|
+
validateRequiredFields () {
|
|
311
|
+
const errors = []
|
|
312
|
+
const config = this.type === 'display' ? this.originalConfig : this.activeConfig
|
|
313
|
+
|
|
314
|
+
if (!config || !config.columns) {
|
|
315
|
+
return { isValid: true, errors: [] }
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// 遍历所有配置项检查必填字段
|
|
319
|
+
config.columns.forEach((row, rowIndex) => {
|
|
320
|
+
row.forEach((cell, cellIndex) => {
|
|
321
|
+
if (cell.required && cell.dataIndex) {
|
|
322
|
+
let value
|
|
323
|
+
if (cell.dataIndex.indexOf('@@@') !== -1) {
|
|
324
|
+
// 处理深层嵌套数据
|
|
325
|
+
// const arr = cell.dataIndex.split('@@@')
|
|
326
|
+
value = config.tempData && config.tempData[cell.dataIndex]
|
|
327
|
+
} else {
|
|
328
|
+
value = config.data[cell.dataIndex]
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// 检查值是否为空
|
|
332
|
+
if (!value || (typeof value === 'string' && value.trim() === '')) {
|
|
333
|
+
const message = cell.requiredMessage || this.getDefaultRequiredMessage(cell.type)
|
|
334
|
+
errors.push({
|
|
335
|
+
dataIndex: cell.dataIndex,
|
|
336
|
+
message: message,
|
|
337
|
+
rowIndex: rowIndex,
|
|
338
|
+
cellIndex: cellIndex,
|
|
339
|
+
type: cell.type
|
|
340
|
+
})
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
})
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
return {
|
|
347
|
+
isValid: errors.length === 0,
|
|
348
|
+
errors: errors
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
// 获取默认的必填提示信息
|
|
352
|
+
getDefaultRequiredMessage (fieldType) {
|
|
353
|
+
const defaultMessages = {
|
|
354
|
+
datePicker: '请选择日期',
|
|
355
|
+
timePicker: '请选择日期时间',
|
|
356
|
+
input: '请填写此字段',
|
|
357
|
+
inputs: '请填写此字段'
|
|
358
|
+
}
|
|
359
|
+
return defaultMessages[fieldType] || '此字段为必填项'
|
|
360
|
+
},
|
|
299
361
|
// 正常的保存方法,当前修改内容会直接全部导出到外部
|
|
300
362
|
saveConfig () {
|
|
363
|
+
// 先进行必填字段校验
|
|
364
|
+
const validation = this.validateRequiredFields()
|
|
365
|
+
if (!validation.isValid) {
|
|
366
|
+
const errorMessages = validation.errors.map(error => error.message).join(';')
|
|
367
|
+
this.$message.error(`保存失败:${errorMessages}`)
|
|
368
|
+
return false
|
|
369
|
+
}
|
|
370
|
+
|
|
301
371
|
if (this.activeConfig === undefined || this.activeConfig === null) {
|
|
302
372
|
return this.originalConfig.data
|
|
303
373
|
} else {
|
|
@@ -306,6 +376,7 @@ export default {
|
|
|
306
376
|
this.changeDeepObject(this.activeConfig.data, key, this.activeConfig.tempData[key])
|
|
307
377
|
})
|
|
308
378
|
this.$emit('saveConfig', this.$refs.XReportDesign.activatedConfig)
|
|
379
|
+
return true
|
|
309
380
|
}
|
|
310
381
|
},
|
|
311
382
|
// 通过@@@分割临时变量,找到对应的key,并修改它的值
|
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
@updateImg="updateImg"
|
|
5
5
|
ref="main"
|
|
6
6
|
:use-oss-for-img="false"
|
|
7
|
-
config-name="
|
|
7
|
+
config-name="用户户内通气点火工艺流程"
|
|
8
8
|
server-name="af-system"
|
|
9
|
-
:config-data="{aaa:'data:image/png;base64,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115LdHotwujfv7+JFURlIIGUgLUeH3/8sfd7qH306dPHdO+KykECSQjWgyMNGLy9du1a7/exbfT0009X71WFIYEkxFoP37Z2wHocc8wxUb9+/VSfqDAkkARgPWhnp3CXxHpQwGP//f7775/e4kQqSCAJoO7x1ltvRR9++KH3ewjODzzwQBOca+dg5SGBeGKr5s8//7x35gqwHoMGDTJ7z0XlIYF4QLvHmjVrTGD+ySefeL/PWg8EwgwwUXlIIB7QsctmqBdffNF7xyBgPQYMGGB2DorKRAIpAtZjyZIl0dNPP212Dfpircd5550Xde7cOcUVijSRQIpAYE6r+TvvvJPofTb2OOGEE1JamcgCCcSBPXl2woQJXhuWLFgP9nyQ2pX1qGwkkH8B12rZsmXRU089lailBEjnXnDBBdGxxx6b0upEVkgg/8L69evNEdWTJk1KFJhTKWcYw4UXXqjMVREovDLwIs9oR+E2IGs1Z86caNy4caZr1xc2RO2+++7R8OHD1ZRYBCw03Qg8gDgHnfvFURh5G4MkgWwFHxxTSp588snErhU9VwxjIPZQS3txEAj3mfuNO8qkSYSCBWZrQKgzQVoigbQAcXDwDb1WSV0rAnN6rbAenPkh3HCvub+4sl988YU5T6Wmpua/Oy7pPEAoCIYZxjx8QiCBtIAP7P33348eeeSRaPHixYnei2tAYE5Luzp2i4NASKEztaXlvxmfRLcC8dvhhx9upr8ccsghptiKaJhlnOX9lUD+hrjj008/jcaOHRt9/fXXid7LB8YHedlll5kYRPjBPd+WlbaTXGgKZVsBYuH+nn322eY0riz31CiLFf0v7qDe8e677yZ6L4E5/vK1116rUaIJsS6WCysWWn2mT5+eedarzQvEDn977rnnEscdgGtFOwkvtbP7Y4fiUWvygQdRbW2t2ZmZJW1eIPi9r7/+evTwww8njjsIHGklueaaazRnNyEIhDNVkgy9YCJ+1pmtNi0QG5Tfd999iRoRwWat8Il9xoiKf2Izhr5gQXbbbbfM6yRtViCI44MPPojuv//+RIOnLQSOzMEdOHBgLvL1lYbdY+MLDySC86zTvW1SILZSfvfddycOygFBkM698sorjV8skoNAigXoLbGHDmVNmxMIHboUpkjn0sKeNCi3J9PedNNNaidpBaVYEAqGWdOmBGKPSnvwwQej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'}"
|
|
10
9
|
:show-img-in-cell="true"
|
|
11
10
|
:edit-mode="false"
|
|
12
11
|
:show-save-button="false"
|
|
@@ -37,6 +37,22 @@
|
|
|
37
37
|
{{ deserializeFunctionAndRun(cell.customFunction, configData[cell.dataIndex], config) }}
|
|
38
38
|
</template>
|
|
39
39
|
</template>
|
|
40
|
+
<template v-else-if="cell.type === 'datePicker'">
|
|
41
|
+
<template v-if="cell.customFunction === undefined">
|
|
42
|
+
{{ getDeepObject(configData, cell.dataIndex) }}
|
|
43
|
+
</template>
|
|
44
|
+
<template v-else>
|
|
45
|
+
{{ deserializeFunctionAndRun(cell.customFunction, configData[cell.dataIndex], config) }}
|
|
46
|
+
</template>
|
|
47
|
+
</template>
|
|
48
|
+
<template v-else-if="cell.type === 'timePicker'">
|
|
49
|
+
<template v-if="cell.customFunction === undefined">
|
|
50
|
+
{{ getDeepObject(configData, cell.dataIndex) }}
|
|
51
|
+
</template>
|
|
52
|
+
<template v-else>
|
|
53
|
+
{{ deserializeFunctionAndRun(cell.customFunction, configData[cell.dataIndex], config) }}
|
|
54
|
+
</template>
|
|
55
|
+
</template>
|
|
40
56
|
<template v-else-if="cell.type === 'inputs'">
|
|
41
57
|
<template v-if="cell.customFunction === undefined">
|
|
42
58
|
{{ showSubRowValue(cell) }}
|
|
@@ -131,6 +147,26 @@
|
|
|
131
147
|
}}
|
|
132
148
|
</template>
|
|
133
149
|
</template>
|
|
150
|
+
<template v-else-if="cell.type === 'datePicker'">
|
|
151
|
+
<template v-if="cell.customFunction === undefined">
|
|
152
|
+
{{ getDeepObject(configData.arr[inputColumnsDefinitionIndex], cell.dataIndex) }}
|
|
153
|
+
</template>
|
|
154
|
+
<template v-else>
|
|
155
|
+
{{
|
|
156
|
+
deserializeFunctionAndRun(cell.customFunction, configData.arr[inputColumnsDefinitionIndex][cell.dataIndex], config)
|
|
157
|
+
}}
|
|
158
|
+
</template>
|
|
159
|
+
</template>
|
|
160
|
+
<template v-else-if="cell.type === 'timePicker'">
|
|
161
|
+
<template v-if="cell.customFunction === undefined">
|
|
162
|
+
{{ getDeepObject(configData.arr[inputColumnsDefinitionIndex], cell.dataIndex) }}
|
|
163
|
+
</template>
|
|
164
|
+
<template v-else>
|
|
165
|
+
{{
|
|
166
|
+
deserializeFunctionAndRun(cell.customFunction, configData.arr[inputColumnsDefinitionIndex][cell.dataIndex], config)
|
|
167
|
+
}}
|
|
168
|
+
</template>
|
|
169
|
+
</template>
|
|
134
170
|
<template v-else-if="cell.type === 'inputs'">
|
|
135
171
|
<template v-if="cell.customFunction === undefined">
|
|
136
172
|
{{ getDeepObject(configData.arr[inputColumnsDefinitionIndex], cell.dataIndex) }}
|
|
@@ -178,39 +214,61 @@
|
|
|
178
214
|
>{{ cell.text || '确认' }}
|
|
179
215
|
</a-button>
|
|
180
216
|
</template>
|
|
217
|
+
<template v-else-if="cell.type === 'datePicker'">
|
|
218
|
+
<div>
|
|
219
|
+
<a-date-picker
|
|
220
|
+
@change="handleDatePickerChange($event, cell.dataIndex, cell)"
|
|
221
|
+
:value="formatDateValue(configData[cell.dataIndex])"
|
|
222
|
+
format="YYYY-MM-DD"
|
|
223
|
+
:style="'width:' + (cell.inputWidth ? cell.inputWidth : '100') + '%'"
|
|
224
|
+
:disabled="cell.inputReadOnly===true"
|
|
225
|
+
:class="{'required-field-error': isFieldRequired(cell) && !configData[cell.dataIndex]}"/>
|
|
226
|
+
<div v-if="isFieldRequired(cell) && !configData[cell.dataIndex]" class="required-message">
|
|
227
|
+
{{ getRequiredMessage(cell, 'datePicker') }}
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
</template>
|
|
231
|
+
<template v-else-if="cell.type === 'timePicker'">
|
|
232
|
+
<div>
|
|
233
|
+
<a-date-picker
|
|
234
|
+
@change="handleTimePickerChange($event, cell.dataIndex, cell)"
|
|
235
|
+
:value="formatDateValue(configData[cell.dataIndex])"
|
|
236
|
+
format="YYYY-MM-DD HH:mm:ss"
|
|
237
|
+
show-time
|
|
238
|
+
:style="'width:' + (cell.inputWidth ? cell.inputWidth : '100') + '%'"
|
|
239
|
+
:disabled="cell.inputReadOnly===true"
|
|
240
|
+
:class="{'required-field-error': isFieldRequired(cell) && !configData[cell.dataIndex]}"/>
|
|
241
|
+
<div v-if="isFieldRequired(cell) && !configData[cell.dataIndex]" class="required-message">
|
|
242
|
+
{{ getRequiredMessage(cell, 'timePicker') }}
|
|
243
|
+
</div>
|
|
244
|
+
</div>
|
|
245
|
+
</template>
|
|
181
246
|
<template v-else-if="cell.type === 'signature'">
|
|
182
247
|
<img :src="configData[cell.dataIndex]" alt="签名加载失败" style="max-height: 2rem">
|
|
183
248
|
<a-button v-if="!configData[cell.dataIndex]" type="dashed" >需要在手机端签名 </a-button>
|
|
184
249
|
</template>
|
|
185
250
|
<template v-else-if="cell.type === 'input'">
|
|
186
|
-
<
|
|
251
|
+
<div>
|
|
187
252
|
<template v-if="cell.dataIndex.indexOf('@@@') !== -1">
|
|
188
253
|
<a-input
|
|
189
|
-
@change="handleInputDeepChange($event, cell.dataIndex)"
|
|
254
|
+
@change="handleInputDeepChange($event, cell.dataIndex, cell)"
|
|
190
255
|
v-model="config.tempData[cell.dataIndex]"
|
|
191
256
|
:style="'width:' + (cell.inputWidth ? cell.inputWidth : '100') + '%'"
|
|
192
|
-
:disabled="true"
|
|
257
|
+
:disabled="cell.inputReadOnly === true"
|
|
258
|
+
:class="{'required-field-error': isFieldRequired(cell) && !config.tempData[cell.dataIndex]}"/>
|
|
193
259
|
</template>
|
|
194
260
|
<template v-else>
|
|
195
261
|
<a-input
|
|
262
|
+
@change="handleInputChange($event, cell.dataIndex, cell)"
|
|
196
263
|
v-model="configData[cell.dataIndex]"
|
|
197
264
|
:style="'width:' + (cell.inputWidth ? cell.inputWidth : '100') + '%'"
|
|
198
|
-
:disabled="true"
|
|
265
|
+
:disabled="cell.inputReadOnly === true"
|
|
266
|
+
:class="{'required-field-error': isFieldRequired(cell) && !configData[cell.dataIndex]}"/>
|
|
199
267
|
</template>
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
@change="handleInputDeepChange($event, cell.dataIndex)"
|
|
205
|
-
v-model="config.tempData[cell.dataIndex]"
|
|
206
|
-
:style="'width:' + (cell.inputWidth ? cell.inputWidth : '100') + '%'"/>
|
|
207
|
-
</template>
|
|
208
|
-
<template v-else>
|
|
209
|
-
<a-input
|
|
210
|
-
v-model="configData[cell.dataIndex]"
|
|
211
|
-
:style="'width:' + (cell.inputWidth ? cell.inputWidth : '100') + '%'"/>
|
|
212
|
-
</template>
|
|
213
|
-
</template>
|
|
268
|
+
<div v-if="isFieldRequired(cell) && !getInputValue(cell)" class="required-message">
|
|
269
|
+
{{ getRequiredMessage(cell, 'input') }}
|
|
270
|
+
</div>
|
|
271
|
+
</div>
|
|
214
272
|
</template>
|
|
215
273
|
<template v-else-if="cell.type === 'inputs'">
|
|
216
274
|
<template v-if="cell.inputReadOnly === true">
|
|
@@ -219,7 +277,7 @@
|
|
|
219
277
|
<span class="inputsDivItemLabel">{{ displayFormatStartText(cell.format) }}</span>
|
|
220
278
|
<template v-if="cell.dataIndex.indexOf('@@@') !== -1">
|
|
221
279
|
<a-input
|
|
222
|
-
@change="handleInputDeepChange($event, cell.dataIndex)"
|
|
280
|
+
@change="handleInputDeepChange($event, cell.dataIndex, cell)"
|
|
223
281
|
v-model="config.tempData[cell.dataIndex][index]"
|
|
224
282
|
:style="'width:' + (cell.inputWidth ? cell.inputWidth : '100') + '%'"
|
|
225
283
|
:disabled="true"/>
|
|
@@ -240,7 +298,7 @@
|
|
|
240
298
|
<span class="inputsDivItemLabel">{{ displayFormatStartText(cell.format) }}</span>
|
|
241
299
|
<template v-if="cell.dataIndex.indexOf('@@@') !== -1">
|
|
242
300
|
<a-input
|
|
243
|
-
@change="handleInputDeepChange($event, cell.dataIndex)"
|
|
301
|
+
@change="handleInputDeepChange($event, cell.dataIndex, cell)"
|
|
244
302
|
v-model="config.tempData[cell.dataIndex][index]"
|
|
245
303
|
:style="'width:' + (cell.inputWidth ? cell.inputWidth : '100') + '%'"/>
|
|
246
304
|
</template>
|
|
@@ -337,6 +395,35 @@
|
|
|
337
395
|
>{{ cell.text || '确认' }}
|
|
338
396
|
</a-button>
|
|
339
397
|
</template>
|
|
398
|
+
<template v-else-if="cell.type === 'datePicker'">
|
|
399
|
+
<div>
|
|
400
|
+
<a-date-picker
|
|
401
|
+
@change="handleArrayDatePickerChange($event, inputColumnsDefinitionIndex, cell.dataIndex, cell)"
|
|
402
|
+
:value="formatDateValue(configData.arr[inputColumnsDefinitionIndex][cell.dataIndex])"
|
|
403
|
+
format="YYYY-MM-DD"
|
|
404
|
+
:style="'width:' + (cell.inputWidth ? cell.inputWidth : '100') + '%'"
|
|
405
|
+
:disabled="cell.inputReadOnly === true"
|
|
406
|
+
:class="{'required-field-error': isFieldRequired(cell) && !configData.arr[inputColumnsDefinitionIndex][cell.dataIndex]}"/>
|
|
407
|
+
<div v-if="isFieldRequired(cell) && !configData.arr[inputColumnsDefinitionIndex][cell.dataIndex]" class="required-message">
|
|
408
|
+
{{ getRequiredMessage(cell, 'datePicker') }}
|
|
409
|
+
</div>
|
|
410
|
+
</div>
|
|
411
|
+
</template>
|
|
412
|
+
<template v-else-if="cell.type === 'timePicker'">
|
|
413
|
+
<div>
|
|
414
|
+
<a-date-picker
|
|
415
|
+
@change="handleArrayTimePickerChange($event, inputColumnsDefinitionIndex, cell.dataIndex, cell)"
|
|
416
|
+
:value="formatDateValue(configData.arr[inputColumnsDefinitionIndex][cell.dataIndex])"
|
|
417
|
+
format="YYYY-MM-DD HH:mm:ss"
|
|
418
|
+
show-time
|
|
419
|
+
:style="'width:' + (cell.inputWidth ? cell.inputWidth : '100') + '%'"
|
|
420
|
+
:disabled="cell.inputReadOnly === true"
|
|
421
|
+
:class="{'required-field-error': isFieldRequired(cell) && !configData.arr[inputColumnsDefinitionIndex][cell.dataIndex]}"/>
|
|
422
|
+
<div v-if="isFieldRequired(cell) && !configData.arr[inputColumnsDefinitionIndex][cell.dataIndex]" class="required-message">
|
|
423
|
+
{{ getRequiredMessage(cell, 'timePicker') }}
|
|
424
|
+
</div>
|
|
425
|
+
</div>
|
|
426
|
+
</template>
|
|
340
427
|
<template v-else-if="cell.type === 'signature'">
|
|
341
428
|
<img :src="configData[cell.dataIndex]" alt="签名加载失败" style="max-height: 2rem">
|
|
342
429
|
<a-button v-if="!configData[cell.dataIndex]" type="dashed" >需要在手机端签名</a-button>
|
|
@@ -345,17 +432,17 @@
|
|
|
345
432
|
{{ configData.arr[inputColumnsDefinitionIndex][cell.dataIndex] }}
|
|
346
433
|
</template>
|
|
347
434
|
<template v-else-if="cell.type === 'input'">
|
|
348
|
-
<
|
|
435
|
+
<div>
|
|
349
436
|
<a-input
|
|
437
|
+
@change="handleArrayInputChange($event, inputColumnsDefinitionIndex, cell.dataIndex, cell)"
|
|
350
438
|
v-model="configData.arr[inputColumnsDefinitionIndex][cell.dataIndex]"
|
|
351
439
|
:style="'width:' + (cell.inputWidth ? cell.inputWidth : '100') + '%'"
|
|
352
|
-
:disabled="true"
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
</template>
|
|
440
|
+
:disabled="cell.inputReadOnly === true"
|
|
441
|
+
:class="{'required-field-error': isFieldRequired(cell) && !configData.arr[inputColumnsDefinitionIndex][cell.dataIndex]}"/>
|
|
442
|
+
<div v-if="isFieldRequired(cell) && !configData.arr[inputColumnsDefinitionIndex][cell.dataIndex]" class="required-message">
|
|
443
|
+
{{ getRequiredMessage(cell, 'input') }}
|
|
444
|
+
</div>
|
|
445
|
+
</div>
|
|
359
446
|
</template>
|
|
360
447
|
<template v-else-if="cell.type === 'inputs'">
|
|
361
448
|
<template v-if="cell.inputReadOnly === true">
|
|
@@ -379,6 +466,7 @@
|
|
|
379
466
|
import Upload from '@vue2-client/base-client/components/common/Upload'
|
|
380
467
|
import { formatDate } from '@vue2-client/utils/util'
|
|
381
468
|
import { nanoid } from 'nanoid'
|
|
469
|
+
import moment from 'moment'
|
|
382
470
|
|
|
383
471
|
export default {
|
|
384
472
|
name: 'XReportTrGroup',
|
|
@@ -582,9 +670,35 @@ export default {
|
|
|
582
670
|
return result
|
|
583
671
|
},
|
|
584
672
|
// 表格中数据key含有@@@,需要手动触发更新
|
|
585
|
-
handleInputDeepChange () {
|
|
673
|
+
handleInputDeepChange (event, dataIndex, cell = null) {
|
|
674
|
+
// 如果字段必填且有值,触发重新渲染以移除错误样式
|
|
675
|
+
if (cell && this.isFieldRequired(cell) && event.target.value) {
|
|
676
|
+
this.$forceUpdate()
|
|
677
|
+
}
|
|
586
678
|
this.$forceUpdate()
|
|
587
679
|
},
|
|
680
|
+
// 处理普通input变化
|
|
681
|
+
handleInputChange (event, dataIndex, cell = null) {
|
|
682
|
+
// 如果字段必填且有值,触发重新渲染以移除错误样式
|
|
683
|
+
if (cell && this.isFieldRequired(cell) && event.target.value) {
|
|
684
|
+
this.$forceUpdate()
|
|
685
|
+
}
|
|
686
|
+
},
|
|
687
|
+
// 处理动态行中input变化
|
|
688
|
+
handleArrayInputChange (event, arrayIndex, dataIndex, cell = null) {
|
|
689
|
+
// 如果字段必填且有值,触发重新渲染以移除错误样式
|
|
690
|
+
if (cell && this.isFieldRequired(cell) && event.target.value) {
|
|
691
|
+
this.$forceUpdate()
|
|
692
|
+
}
|
|
693
|
+
},
|
|
694
|
+
// 获取input的值,用于必填校验
|
|
695
|
+
getInputValue (cell) {
|
|
696
|
+
if (cell.dataIndex.indexOf('@@@') !== -1) {
|
|
697
|
+
return this.config.tempData[cell.dataIndex]
|
|
698
|
+
} else {
|
|
699
|
+
return this.configData[cell.dataIndex]
|
|
700
|
+
}
|
|
701
|
+
},
|
|
588
702
|
// 路径中含有@@@的key,将其解析,并返回其数据
|
|
589
703
|
getDeepObject (obj, strPath) {
|
|
590
704
|
const arr = strPath.split('@@@')
|
|
@@ -610,6 +724,69 @@ export default {
|
|
|
610
724
|
}
|
|
611
725
|
this.configData = Object.assign({}, this.configData)
|
|
612
726
|
},
|
|
727
|
+
// 通用的日期时间选择器变化处理方法
|
|
728
|
+
handleDateTimePickerChange (date, dataIndex, format, arrayIndex = null, cell = null) {
|
|
729
|
+
const dateStr = date ? moment(date).format(format) : ''
|
|
730
|
+
|
|
731
|
+
if (arrayIndex !== null) {
|
|
732
|
+
// 处理动态行中的日期时间选择器
|
|
733
|
+
this.configData.arr[arrayIndex][dataIndex] = dateStr
|
|
734
|
+
} else if (dataIndex.indexOf('@@@') !== -1) {
|
|
735
|
+
// 处理深层嵌套的数据
|
|
736
|
+
this.handleInputDeepChange({ target: { value: dateStr } }, dataIndex, cell)
|
|
737
|
+
} else {
|
|
738
|
+
// 处理普通的数据
|
|
739
|
+
this.configData[dataIndex] = dateStr
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// 如果字段必填且有值,触发重新渲染以移除错误样式
|
|
743
|
+
if (cell && this.isFieldRequired(cell) && dateStr) {
|
|
744
|
+
this.$forceUpdate()
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
this.configData = Object.assign({}, this.configData)
|
|
748
|
+
},
|
|
749
|
+
// 日期选择器变化处理(YYYY-MM-DD格式)
|
|
750
|
+
handleDatePickerChange (date, dataIndex, cell = null) {
|
|
751
|
+
this.handleDateTimePickerChange(date, dataIndex, 'YYYY-MM-DD', null, cell)
|
|
752
|
+
},
|
|
753
|
+
// 时间选择器变化处理(YYYY-MM-DD HH:mm:ss格式)
|
|
754
|
+
handleTimePickerChange (date, dataIndex, cell = null) {
|
|
755
|
+
this.handleDateTimePickerChange(date, dataIndex, 'YYYY-MM-DD HH:mm:ss', null, cell)
|
|
756
|
+
},
|
|
757
|
+
// 动态行中日期选择器变化处理
|
|
758
|
+
handleArrayDatePickerChange (date, arrayIndex, dataIndex, cell = null) {
|
|
759
|
+
this.handleDateTimePickerChange(date, dataIndex, 'YYYY-MM-DD', arrayIndex, cell)
|
|
760
|
+
},
|
|
761
|
+
// 动态行中时间选择器变化处理
|
|
762
|
+
handleArrayTimePickerChange (date, arrayIndex, dataIndex, cell = null) {
|
|
763
|
+
this.handleDateTimePickerChange(date, dataIndex, 'YYYY-MM-DD HH:mm:ss', arrayIndex, cell)
|
|
764
|
+
},
|
|
765
|
+
// 判断字段是否必填
|
|
766
|
+
isFieldRequired (cell) {
|
|
767
|
+
return cell && cell.required === true
|
|
768
|
+
},
|
|
769
|
+
// 获取必填字段的提示信息
|
|
770
|
+
getRequiredMessage (cell, fieldType) {
|
|
771
|
+
if (cell.requiredMessage) {
|
|
772
|
+
return cell.requiredMessage
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// 默认提示信息
|
|
776
|
+
const defaultMessages = {
|
|
777
|
+
datePicker: '请选择日期',
|
|
778
|
+
timePicker: '请选择日期时间',
|
|
779
|
+
input: '请填写此字段'
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
return defaultMessages[fieldType] || '此字段为必填项'
|
|
783
|
+
},
|
|
784
|
+
// 格式化日期值,用于日期选择器的value
|
|
785
|
+
formatDateValue (dateStr) {
|
|
786
|
+
if (!dateStr) return null
|
|
787
|
+
const date = moment(dateStr)
|
|
788
|
+
return date.isValid() ? date : null
|
|
789
|
+
},
|
|
613
790
|
// 反序列化函数并执行
|
|
614
791
|
deserializeFunctionAndRun (functionStr, value) {
|
|
615
792
|
// eslint-disable-next-line no-eval
|
|
@@ -813,4 +990,16 @@ export default {
|
|
|
813
990
|
border-bottom: 1px solid #000;
|
|
814
991
|
padding: 8px;
|
|
815
992
|
}
|
|
993
|
+
|
|
994
|
+
.required-field-error {
|
|
995
|
+
border-color: #ff4d4f !important;
|
|
996
|
+
box-shadow: 0 0 0 2px rgba(255, 77, 79, 0.2) !important;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
.required-message {
|
|
1000
|
+
color: #ff4d4f;
|
|
1001
|
+
font-size: 12px;
|
|
1002
|
+
margin-top: 4px;
|
|
1003
|
+
line-height: 1.2;
|
|
1004
|
+
}
|
|
816
1005
|
</style>
|
|
@@ -36,9 +36,68 @@ export default {
|
|
|
36
36
|
```vue
|
|
37
37
|
<XReport :config-name="'test_tableConfig'" :activated-slot-name="'test_tableConfig_slot'"/>
|
|
38
38
|
```
|
|
39
|
+
|
|
40
|
+
## 支持的表单项目类型
|
|
41
|
+
|
|
42
|
+
| 类型 | 说明 | 输出格式 | 备注 |
|
|
43
|
+
|-----------------|------|-------|-----------|
|
|
44
|
+
| input | 文本输入框 | 字符串 | 标准文本输入 |
|
|
45
|
+
| datePicker| 日期选择器 | YYYY-MM-DD | 日期格式 |
|
|
46
|
+
| timePicker | 时间选择器 | YYYY-MM-DD HH:mm:ss | 日期时间格式 |
|
|
47
|
+
| curDateInput | 当前日期按钮 | YYYY-MM-DD HH:mm:ss | 点击获取当前时间|
|
|
48
|
+
| signature | 签名框 | 图片URL | 需要在手机端操作|
|
|
49
|
+
| images | 图片上传 | 图片数组 | 支持多图片上传|
|
|
50
|
+
|
|
51
|
+
## 配置例子
|
|
52
|
+
|
|
53
|
+
### 日期选择器配置
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"type": "datePicker",
|
|
57
|
+
"dataIndex": "selectedDate",
|
|
58
|
+
"inputWidth": "100%",
|
|
59
|
+
"inputReadOnly": false,
|
|
60
|
+
"required": true,
|
|
61
|
+
"requiredMessage": "请选择操作日期"
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 时间选择器配置
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"type": "timePicker",
|
|
69
|
+
"dataIndex": "selectedDateTime",
|
|
70
|
+
"inputWidth": "100%",
|
|
71
|
+
"inputReadOnly": false,
|
|
72
|
+
"required": true,
|
|
73
|
+
"requiredMessage": "请选择具体时间"
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 必填字段配置说明
|
|
78
|
+
|
|
79
|
+
| 参数 | 说明 | 类型 | 默认值 |
|
|
80
|
+
|-----------------|------|-------|-----------|
|
|
81
|
+
| required | 是否必填 | Boolean | false |
|
|
82
|
+
| requiredMessage| 必填提示信息 | String | 根据字段类型自动生成 |
|
|
83
|
+
|
|
84
|
+
**默认提示信息:**
|
|
85
|
+
- datePicker: "请选择日期"
|
|
86
|
+
- timePicker: "请选择日期时间"
|
|
87
|
+
- input: "请填写此字段"
|
|
88
|
+
- 其他类型: "此字段为必填项"
|
|
89
|
+
|
|
90
|
+
**必填校验特性:**
|
|
91
|
+
- 必填字段为空时会显示红色边框和错误提示
|
|
92
|
+
- 保存时会自动校验所有必填字段,校验失败会阻止保存并显示错误信息
|
|
93
|
+
- 导出数据时会进行校验提醒,但不会阻止导出
|
|
94
|
+
|
|
39
95
|
## 注意事项
|
|
40
96
|
|
|
41
97
|
> 在某些情况下,比如手机端,只需要输入表格中一部分的内容。
|
|
42
98
|
> 可以将这部分内容作为插槽,并在activatedSlotName中填写插槽名。
|
|
43
99
|
> 则会在设计页面仅展示插槽中的输入项,并在预览窗口中,根据configName中的配置,
|
|
44
100
|
> 渲染出来完整的表格
|
|
101
|
+
|
|
102
|
+
> 新增的datePicker和timePicker支持在普通行、动态行中使用,
|
|
103
|
+
> 会自动格式化为指定的日期时间格式并保存到配置数据中。支持必填校验功能。
|
|
@@ -1,28 +1,55 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="list-wrapper">
|
|
3
3
|
<a-list size="large" :data-source="data" itemLayout="horizontal" class="list-container" ref="listRef">
|
|
4
|
-
<a-list-item
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
4
|
+
<a-list-item
|
|
5
|
+
slot="renderItem"
|
|
6
|
+
slot-scope="item, index"
|
|
7
|
+
class="list-item"
|
|
8
|
+
@click="handleClick(index)"
|
|
9
|
+
@mouseenter="enableHoverOptions && handleMouseEnter(index)"
|
|
10
|
+
@mouseleave="handleMouseLeave"
|
|
11
|
+
:class="{ 'hover-active': enableHoverOptions && hoveredIndex === index }"
|
|
12
|
+
>
|
|
13
|
+
<i
|
|
14
|
+
v-if="icon"
|
|
15
|
+
class="icon-menu"
|
|
16
|
+
:style="getIconStyle(item)"
|
|
17
|
+
></i>
|
|
18
|
+
<span
|
|
19
|
+
class="item-text">
|
|
20
|
+
{{ item.number }} {{ item.name }}
|
|
21
|
+
</span>
|
|
22
|
+
|
|
23
|
+
<div v-if="button" class="button-group">
|
|
24
|
+
<a-button
|
|
25
|
+
v-for="(name, idx) in buttonNames"
|
|
26
|
+
:key="idx"
|
|
27
|
+
type="link"
|
|
28
|
+
:class="['confirm-btn', buttonMode ? 'hover-btn' : '']"
|
|
29
|
+
@click.stop="click(index, idx)"
|
|
30
|
+
>
|
|
31
|
+
<span :class="{ 'hover-active': enableHoverOptions && hoveredIndex === index }">{{ name }}</span>
|
|
32
|
+
</a-button>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<!-- 悬浮选项框 -->
|
|
36
|
+
<div
|
|
37
|
+
v-show="enableHoverOptions && hoveredIndex === index"
|
|
38
|
+
class="hover-options"
|
|
39
|
+
@mouseenter="handleOptionsEnter"
|
|
40
|
+
@mouseleave="handleOptionsLeave"
|
|
22
41
|
>
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
42
|
+
<div class="hover-options-content">
|
|
43
|
+
<div
|
|
44
|
+
v-for="(item, idx) in select_options"
|
|
45
|
+
:key="idx"
|
|
46
|
+
class="option-item"
|
|
47
|
+
@click="handleOptionClick(index, item)"
|
|
48
|
+
>
|
|
49
|
+
{{ item }}
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
26
53
|
</a-list-item>
|
|
27
54
|
</a-list>
|
|
28
55
|
</div>
|
|
@@ -42,6 +69,10 @@ export default {
|
|
|
42
69
|
fixedQueryForm: {
|
|
43
70
|
type: Object,
|
|
44
71
|
default: { condition: '1=1' }
|
|
72
|
+
},
|
|
73
|
+
enableHoverOptions: {
|
|
74
|
+
type: Boolean,
|
|
75
|
+
default: true
|
|
45
76
|
}
|
|
46
77
|
},
|
|
47
78
|
inject: ['getComponentByName'],
|
|
@@ -51,7 +82,12 @@ export default {
|
|
|
51
82
|
button: false,
|
|
52
83
|
icon: false,
|
|
53
84
|
buttonNames: [],
|
|
54
|
-
buttonMode: false
|
|
85
|
+
buttonMode: false,
|
|
86
|
+
hoveredIndex: -1,
|
|
87
|
+
isOptionsHovered: false,
|
|
88
|
+
hoverTimer: null,
|
|
89
|
+
leaveTimer: null,
|
|
90
|
+
select_options: []
|
|
55
91
|
}
|
|
56
92
|
},
|
|
57
93
|
created () {
|
|
@@ -65,6 +101,10 @@ export default {
|
|
|
65
101
|
that.icon = res.icon
|
|
66
102
|
that.buttonNames = res.buttonNames || []
|
|
67
103
|
that.buttonMode = res.buttonMode || false
|
|
104
|
+
this.enableHoverOptions = res.enableHoverOptions || false
|
|
105
|
+
if (this.enableHoverOptions) {
|
|
106
|
+
this.select_options = res.select_options
|
|
107
|
+
}
|
|
68
108
|
runLogic(res.data, param, 'af-his').then(ress => {
|
|
69
109
|
that.data = ress
|
|
70
110
|
})
|
|
@@ -88,6 +128,42 @@ export default {
|
|
|
88
128
|
runLogic(this.queryParamsName, par, 'af-his').then(res => {
|
|
89
129
|
this.data = res.data
|
|
90
130
|
})
|
|
131
|
+
},
|
|
132
|
+
handleMouseEnter (index) {
|
|
133
|
+
this.clearAllTimers()
|
|
134
|
+
this.hoveredIndex = index
|
|
135
|
+
this.isOptionsHovered = true
|
|
136
|
+
},
|
|
137
|
+
handleMouseLeave () {
|
|
138
|
+
this.clearAllTimers()
|
|
139
|
+
this.leaveTimer = setTimeout(() => {
|
|
140
|
+
this.isOptionsHovered = false
|
|
141
|
+
this.hoveredIndex = -1
|
|
142
|
+
}, 100)
|
|
143
|
+
},
|
|
144
|
+
handleOptionsEnter () {
|
|
145
|
+
this.clearAllTimers()
|
|
146
|
+
this.isOptionsHovered = true
|
|
147
|
+
},
|
|
148
|
+
handleOptionsLeave () {
|
|
149
|
+
this.clearAllTimers()
|
|
150
|
+
this.leaveTimer = setTimeout(() => {
|
|
151
|
+
this.isOptionsHovered = false
|
|
152
|
+
this.hoveredIndex = -1
|
|
153
|
+
}, 100)
|
|
154
|
+
},
|
|
155
|
+
clearAllTimers () {
|
|
156
|
+
if (this.hoverTimer) {
|
|
157
|
+
clearTimeout(this.hoverTimer)
|
|
158
|
+
this.hoverTimer = null
|
|
159
|
+
}
|
|
160
|
+
if (this.leaveTimer) {
|
|
161
|
+
clearTimeout(this.leaveTimer)
|
|
162
|
+
this.leaveTimer = null
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
handleOptionClick (index, action) {
|
|
166
|
+
this.$emit('optionClick', { data: this.data[index], action })
|
|
91
167
|
}
|
|
92
168
|
},
|
|
93
169
|
watch: {
|
|
@@ -97,6 +173,9 @@ export default {
|
|
|
97
173
|
this.refreshList(val)
|
|
98
174
|
}
|
|
99
175
|
}
|
|
176
|
+
},
|
|
177
|
+
beforeDestroy () {
|
|
178
|
+
this.clearAllTimers()
|
|
100
179
|
}
|
|
101
180
|
}
|
|
102
181
|
</script>
|
|
@@ -124,6 +203,8 @@ export default {
|
|
|
124
203
|
border: 1px solid #D9D9D9;
|
|
125
204
|
box-sizing: border-box;
|
|
126
205
|
margin-bottom: 8px !important;
|
|
206
|
+
position: relative;
|
|
207
|
+
transition: background-color 0.3s ease;
|
|
127
208
|
}
|
|
128
209
|
|
|
129
210
|
.icon-menu {
|
|
@@ -170,4 +251,56 @@ export default {
|
|
|
170
251
|
.list-wrapper::-webkit-scrollbar-track {
|
|
171
252
|
background-color: #f0f0f0;
|
|
172
253
|
}
|
|
254
|
+
|
|
255
|
+
.hover-active {
|
|
256
|
+
color: white;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.list-item.hover-active {
|
|
260
|
+
background-color: rgb(0, 87, 254) !important;
|
|
261
|
+
color: white;
|
|
262
|
+
border: 1px solid black;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.hover-options {
|
|
266
|
+
position: absolute;
|
|
267
|
+
left: 0;
|
|
268
|
+
right: 0;
|
|
269
|
+
top: 100%;
|
|
270
|
+
background: white;
|
|
271
|
+
border: 1px solid #d9d9d9;
|
|
272
|
+
border-radius: 4px;
|
|
273
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
274
|
+
z-index: 1000;
|
|
275
|
+
margin-top: 4px;
|
|
276
|
+
width: 100%;
|
|
277
|
+
box-sizing: border-box;
|
|
278
|
+
pointer-events: auto;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.hover-options-content {
|
|
282
|
+
padding: 4px 0;
|
|
283
|
+
display: flex;
|
|
284
|
+
flex-direction: column;
|
|
285
|
+
width: 100%;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.option-item {
|
|
289
|
+
padding: 8px 12px;
|
|
290
|
+
cursor: pointer;
|
|
291
|
+
transition: all 0.3s ease;
|
|
292
|
+
color: #333;
|
|
293
|
+
font-size: 14px;
|
|
294
|
+
display: flex;
|
|
295
|
+
align-items: center;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.option-item:hover {
|
|
299
|
+
background-color: #f5f5f5;
|
|
300
|
+
color: #1890ff;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.option-item:active {
|
|
304
|
+
background-color: #e6f7ff;
|
|
305
|
+
}
|
|
173
306
|
</style>
|
|
@@ -339,6 +339,65 @@ async function handleSubmit (formData) {
|
|
|
339
339
|
}
|
|
340
340
|
}
|
|
341
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
|
+
|
|
342
401
|
// 生命周期钩子
|
|
343
402
|
onMounted(() => {
|
|
344
403
|
window.addEventListener('message', (event) => {
|
|
@@ -373,7 +432,9 @@ defineExpose({
|
|
|
373
432
|
vitalSignsId.value = id
|
|
374
433
|
},
|
|
375
434
|
// 创建体温单 参数:(Object) data
|
|
376
|
-
handleSubmit
|
|
435
|
+
handleSubmit,
|
|
436
|
+
// 获取表单数据
|
|
437
|
+
mergeAndTransformData
|
|
377
438
|
})
|
|
378
439
|
</script>
|
|
379
440
|
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
<a-tab-pane key="0" tab="步骤详情">
|
|
38
38
|
<a-card :bordered="false" :loading="loadingHistory">
|
|
39
39
|
<!-- 当前步骤历史记录 -->
|
|
40
|
-
<template v-if="formCompletedDataPreview
|
|
40
|
+
<template v-if="formCompletedDataPreview">
|
|
41
41
|
<a-descriptions
|
|
42
42
|
v-show="formCompletedDataPreview.data"
|
|
43
43
|
:column="{ xxl: 4, xl: 3, lg: 3, md: 3, sm: 2, xs: 1 }"
|