|
|
@@ -36,7 +36,13 @@
|
|
|
|
|
|
<!-- 列表 -->
|
|
|
<ContentWrap v-if="!visible">
|
|
|
- <TabBarBtn @add="openForm()" @export="handleExport" @del="handleDelete" />
|
|
|
+ <TabBarBtn @add="openForm()" @export="handleExport" @del="handleDelete">
|
|
|
+ <template #next>
|
|
|
+ <el-button type="warning" plain @click="handleSingleExport">
|
|
|
+ <Icon icon="ep:download" class="mr-5px" /> 单人导出
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </TabBarBtn>
|
|
|
<Table2
|
|
|
selection
|
|
|
@selectionChange="handleSelectionChange"
|
|
|
@@ -67,6 +73,8 @@ import Form from './Form.vue'
|
|
|
import Export from './Export.vue'
|
|
|
import Detail from './Detail.vue'
|
|
|
import { useUserStore } from '@/store/modules/user'
|
|
|
+import * as XLSX from 'xlsx-js-style'
|
|
|
+import { formatTime } from '@/utils'
|
|
|
defineOptions({ name: 'VitalSign' })
|
|
|
const userStore = useUserStore()
|
|
|
const message = useMessage() // 消息弹窗
|
|
|
@@ -152,6 +160,149 @@ const handleDelete = async () => {
|
|
|
} catch {}
|
|
|
}
|
|
|
|
|
|
+/** 去除指标值中的偏高/偏低标记,只保留原始数值(如 36.5(偏高) => 36.5) */
|
|
|
+const stripMetric = (val: any) => {
|
|
|
+ if (val === null || val === undefined || val === '') return ''
|
|
|
+ const str = String(val)
|
|
|
+ const indexEn = str.indexOf('(')
|
|
|
+ const indexCn = str.indexOf('(')
|
|
|
+ let index = -1
|
|
|
+ if (indexEn !== -1 && indexCn !== -1) {
|
|
|
+ index = Math.min(indexEn, indexCn)
|
|
|
+ } else {
|
|
|
+ index = indexEn !== -1 ? indexEn : indexCn
|
|
|
+ }
|
|
|
+ return index === -1 ? str : str.substring(0, index).trim()
|
|
|
+}
|
|
|
+
|
|
|
+/** 单人导出 */
|
|
|
+const handleSingleExport = async () => {
|
|
|
+ if (!queryParams.elderName) {
|
|
|
+ message.warning('请先输入长者姓名')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const wb = XLSX.utils.book_new()
|
|
|
+ const headers = ['日期', '时间', '体温', '脉搏', '呼吸', '血压', '血氧', '测量人']
|
|
|
+ const colCount = headers.length
|
|
|
+
|
|
|
+ // 查询该长者所有数据
|
|
|
+ const data = await getVitalSignsPage({ ...queryParams, pageNo: 1, pageSize: 99999 })
|
|
|
+ const records: Recordable[] = Array.isArray(data?.list) ? data.list : []
|
|
|
+ if (records.length === 0) {
|
|
|
+ message.warning(`未查询到长者「${queryParams.elderName}」的生命体征数据,无法导出`)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 标题行
|
|
|
+ const titleRow: string[] = new Array(colCount).fill('')
|
|
|
+ titleRow[0] = '生命体征'
|
|
|
+
|
|
|
+ // 基本信息行:姓名 / 床号
|
|
|
+ const first = records[0]
|
|
|
+ const infoItems = [
|
|
|
+ { label: '姓名:', value: first.elderName || '', start: 0, span: 3 },
|
|
|
+ { label: '床号:', value: first.bedName || '', start: 4, span: 4 }
|
|
|
+ ]
|
|
|
+ const infoRow: string[] = new Array(colCount).fill('')
|
|
|
+ infoItems.forEach((item) => {
|
|
|
+ infoRow[item.start] = item.label + item.value
|
|
|
+ })
|
|
|
+
|
|
|
+ // 数据行
|
|
|
+ const dataRows = records.map((row: any) => [
|
|
|
+ row.measuringDate ? formatTime(row.measuringDate, 'yyyy-MM-dd') : '',
|
|
|
+ row.measuringTime || '',
|
|
|
+ stripMetric(row.temperature),
|
|
|
+ stripMetric(row.pulse),
|
|
|
+ stripMetric(row.respiratoryRate),
|
|
|
+ [stripMetric(row.systolicPressure), stripMetric(row.diastolicPressure)].filter(Boolean).join('/'),
|
|
|
+ stripMetric(row.bloodOxygenSaturation),
|
|
|
+ row.measuringName || ''
|
|
|
+ ])
|
|
|
+
|
|
|
+ // 护士签名行
|
|
|
+ const nurseSignRow: string[] = new Array(colCount).fill('')
|
|
|
+ nurseSignRow[0] = '护士签名:'
|
|
|
+
|
|
|
+ const ws = XLSX.utils.aoa_to_sheet([titleRow, infoRow, headers, ...dataRows, nurseSignRow])
|
|
|
+
|
|
|
+ // 合并单元格
|
|
|
+ const nurseSignRowIndex = 3 + dataRows.length
|
|
|
+ ws['!merges'] = [
|
|
|
+ { s: { r: 0, c: 0 }, e: { r: 0, c: colCount - 1 } },
|
|
|
+ ...infoItems.map((item) => ({ s: { r: 1, c: item.start }, e: { r: 1, c: item.start + item.span - 1 } })),
|
|
|
+ { s: { r: nurseSignRowIndex, c: 0 }, e: { r: nurseSignRowIndex, c: colCount - 1 } }
|
|
|
+ ]
|
|
|
+
|
|
|
+ // 列宽
|
|
|
+ ws['!cols'] = [
|
|
|
+ { wch: 14 },
|
|
|
+ { wch: 12 },
|
|
|
+ { wch: 10 },
|
|
|
+ { wch: 10 },
|
|
|
+ { wch: 10 },
|
|
|
+ { wch: 12 },
|
|
|
+ { wch: 10 },
|
|
|
+ { wch: 12 }
|
|
|
+ ]
|
|
|
+
|
|
|
+ // 行高
|
|
|
+ ws['!rows'] = [
|
|
|
+ { hpt: 38 },
|
|
|
+ { hpt: 28 },
|
|
|
+ { hpt: 32 },
|
|
|
+ ...dataRows.map(() => ({ hpt: 28 })),
|
|
|
+ { hpt: 34 }
|
|
|
+ ]
|
|
|
+
|
|
|
+ const range = XLSX.utils.decode_range(ws['!ref'] || '')
|
|
|
+ const headerFill = {
|
|
|
+ patternType: 'solid',
|
|
|
+ fgColor: { rgb: 'FFD9D9D9' }
|
|
|
+ }
|
|
|
+ const border = {
|
|
|
+ top: { style: 'thin', color: { rgb: 'FF000000' } },
|
|
|
+ bottom: { style: 'thin', color: { rgb: 'FF000000' } },
|
|
|
+ left: { style: 'thin', color: { rgb: 'FF000000' } },
|
|
|
+ right: { style: 'thin', color: { rgb: 'FF000000' } }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (let R = range.s.r; R <= range.e.r; R++) {
|
|
|
+ for (let C = range.s.c; C <= range.e.c; C++) {
|
|
|
+ const cellRef = XLSX.utils.encode_cell({ r: R, c: C })
|
|
|
+ if (!ws[cellRef]) {
|
|
|
+ ws[cellRef] = { v: '', t: 's' }
|
|
|
+ }
|
|
|
+ const cell = ws[cellRef]
|
|
|
+ cell.s = cell.s || {}
|
|
|
+ cell.s.alignment = { vertical: 'center', horizontal: 'center', wrapText: true }
|
|
|
+
|
|
|
+ if (R === 0) {
|
|
|
+ cell.s.border = border
|
|
|
+ cell.s.font = { bold: true, sz: 18, name: '宋体' }
|
|
|
+ } else if (R === 1) {
|
|
|
+ cell.s.border = border
|
|
|
+ cell.s.alignment = { vertical: 'center', horizontal: 'left', wrapText: true }
|
|
|
+ cell.s.font = { bold: true, sz: 11, name: '宋体' }
|
|
|
+ } else if (R === 2) {
|
|
|
+ cell.s.border = border
|
|
|
+ cell.s.fill = headerFill
|
|
|
+ cell.s.font = { bold: true, sz: 11, name: '宋体' }
|
|
|
+ } else if (R === nurseSignRowIndex) {
|
|
|
+ // 护士签名行:位于表格下方,无边框,左对齐,留空手签
|
|
|
+ cell.s.alignment = { vertical: 'center', horizontal: 'left' }
|
|
|
+ cell.s.font = { bold: true, sz: 11, name: '宋体' }
|
|
|
+ } else {
|
|
|
+ cell.s.border = border
|
|
|
+ cell.s.font = { sz: 11, name: '宋体' }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ XLSX.utils.book_append_sheet(wb, ws, '生命体征')
|
|
|
+ XLSX.writeFile(wb, `生命体征_${queryParams.elderName}.xlsx`)
|
|
|
+}
|
|
|
+
|
|
|
/** 初始化 **/
|
|
|
onMounted(() => {
|
|
|
getList()
|