소스 검색

Merge branch 'master' of http://47.107.245.0:3000/xiongxing/kyj-yanglao-web-new

chenjun 4 주 전
부모
커밋
970f3de5db
1개의 변경된 파일과 152개의 추가작업 그리고 1개의 파일을 삭제
  1. 152 1
      src/views/elderly/assess/vital-sign/index.vue

+ 152 - 1
src/views/elderly/assess/vital-sign/index.vue

@@ -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()