feat(V25): 补全体温单/术前核查/标本条码/审计日志/身份证校验完整功能

- 补全5个V25实体字段(匹配Flyway V25迁移)
- 创建5个Controller: NursingVitalSignsChart/SurgerySafetyCheck/SpecimenBarcode/SysAuditLog/EmpiIdVerification
- 创建5个前端页面: vitalsignschart/surgerysafetycheck/specimenbarcode/auditlog/idverification
- 修复esbmanage/registry api缺少getRegistryList导出
- 后端编译通过,前端构建通过
This commit is contained in:
2026-06-06 20:27:07 +08:00
parent dad8aa0aad
commit ec39c8b13b
21 changed files with 896 additions and 0 deletions

View File

@@ -0,0 +1,103 @@
package com.healthlink.his.web.check.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.core.domain.R;
import com.healthlink.his.check.domain.SpecimenBarcode;
import com.healthlink.his.check.service.ISpecimenBarcodeService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.Date;
/**
* 标本条码管理 Controller
*/
@RestController
@RequestMapping("/specimen-barcode")
@Slf4j
@AllArgsConstructor
public class SpecimenBarcodeController {
private final ISpecimenBarcodeService barcodeService;
@GetMapping("/page")
public R<?> getPage(
@RequestParam(value = "patientName", required = false) String patientName,
@RequestParam(value = "barcode", required = false) String barcode,
@RequestParam(value = "status", required = false) String status,
@RequestParam(value = "specimenType", required = false) String specimenType,
@RequestParam(value = "encounterId", required = false) Long encounterId,
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
LambdaQueryWrapper<SpecimenBarcode> w = new LambdaQueryWrapper<>();
w.like(StringUtils.hasText(patientName), SpecimenBarcode::getPatientName, patientName)
.eq(StringUtils.hasText(barcode), SpecimenBarcode::getBarcode, barcode)
.eq(StringUtils.hasText(status), SpecimenBarcode::getStatus, status)
.eq(StringUtils.hasText(specimenType), SpecimenBarcode::getSpecimenType, specimenType)
.eq(encounterId != null, SpecimenBarcode::getEncounterId, encounterId)
.orderByDesc(SpecimenBarcode::getCreateTime);
return R.ok(barcodeService.page(new Page<>(pageNo, pageSize), w));
}
@GetMapping("/{id}")
public R<?> getById(@PathVariable Long id) {
return R.ok(barcodeService.getById(id));
}
@GetMapping("/by-barcode/{barcode}")
public R<?> getByBarcode(@PathVariable String barcode) {
LambdaQueryWrapper<SpecimenBarcode> w = new LambdaQueryWrapper<>();
w.eq(SpecimenBarcode::getBarcode, barcode);
return R.ok(barcodeService.getOne(w));
}
@PostMapping("/add")
@Transactional(rollbackFor = Exception.class)
public R<?> add(@RequestBody SpecimenBarcode barcode) {
barcode.setStatus("COLLECTED");
barcode.setCreateTime(new Date());
barcodeService.save(barcode);
return R.ok(barcode);
}
@PutMapping("/update")
@Transactional(rollbackFor = Exception.class)
public R<?> update(@RequestBody SpecimenBarcode barcode) {
barcodeService.updateById(barcode);
return R.ok(barcode);
}
@PutMapping("/scan/{id}")
@Transactional(rollbackFor = Exception.class)
public R<?> scanConfirm(@PathVariable Long id, @RequestParam("confirmBy") String confirmBy) {
SpecimenBarcode barcode = barcodeService.getById(id);
if (barcode == null) return R.fail("条码不存在");
barcode.setStatus("SCANNED");
barcode.setScanConfirmTime(LocalDateTime.now());
barcode.setScanConfirmBy(confirmBy);
barcodeService.updateById(barcode);
return R.ok(barcode);
}
@PutMapping("/reject/{id}")
@Transactional(rollbackFor = Exception.class)
public R<?> reject(@PathVariable Long id) {
SpecimenBarcode barcode = barcodeService.getById(id);
if (barcode == null) return R.fail("条码不存在");
barcode.setStatus("REJECTED");
barcodeService.updateById(barcode);
return R.ok(barcode);
}
@DeleteMapping("/delete/{id}")
@Transactional(rollbackFor = Exception.class)
public R<?> delete(@PathVariable Long id) {
barcodeService.removeById(id);
return R.ok();
}
}

View File

@@ -0,0 +1,63 @@
package com.healthlink.his.web.empi.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.core.domain.R;
import com.healthlink.his.empi.domain.EmpiIdVerification;
import com.healthlink.his.empi.service.IEmpiIdVerificationService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.Date;
/**
* 身份证校验记录 Controller
*/
@RestController
@RequestMapping("/id-verification")
@Slf4j
@AllArgsConstructor
public class EmpiIdVerificationController {
private final IEmpiIdVerificationService verificationService;
@GetMapping("/page")
public R<?> getPage(
@RequestParam(value = "patientId", required = false) Long patientId,
@RequestParam(value = "patientName", required = false) String patientName,
@RequestParam(value = "verifyResult", required = false) String verifyResult,
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
LambdaQueryWrapper<EmpiIdVerification> w = new LambdaQueryWrapper<>();
w.eq(patientId != null, EmpiIdVerification::getPatientId, patientId)
.like(StringUtils.hasText(patientName), EmpiIdVerification::getPatientName, patientName)
.eq(StringUtils.hasText(verifyResult), EmpiIdVerification::getVerifyResult, verifyResult)
.orderByDesc(EmpiIdVerification::getCreateTime);
return R.ok(verificationService.page(new Page<>(pageNo, pageSize), w));
}
@GetMapping("/{id}")
public R<?> getById(@PathVariable Long id) {
return R.ok(verificationService.getById(id));
}
@PostMapping("/verify")
@Transactional(rollbackFor = Exception.class)
public R<?> verify(@RequestBody EmpiIdVerification verification) {
verification.setVerifyTime(LocalDateTime.now());
verification.setCreateTime(new Date());
verificationService.save(verification);
return R.ok(verification);
}
@DeleteMapping("/delete/{id}")
@Transactional(rollbackFor = Exception.class)
public R<?> delete(@PathVariable Long id) {
verificationService.removeById(id);
return R.ok();
}
}

View File

@@ -0,0 +1,83 @@
package com.healthlink.his.web.inpatientmanage.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.core.domain.R;
import com.healthlink.his.nursing.domain.NursingVitalSignsChart;
import com.healthlink.his.nursing.service.INursingVitalSignsChartService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.util.Date;
import java.util.List;
/**
* 体温单数据(三测单) Controller
*/
@RestController
@RequestMapping("/vital-signs-chart")
@Slf4j
@AllArgsConstructor
public class NursingVitalSignsChartController {
private final INursingVitalSignsChartService chartService;
@GetMapping("/page")
public R<?> getPage(
@RequestParam(value = "patientId", required = false) Long patientId,
@RequestParam(value = "encounterId", required = false) Long encounterId,
@RequestParam(value = "startDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate,
@RequestParam(value = "endDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate,
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
LambdaQueryWrapper<NursingVitalSignsChart> w = new LambdaQueryWrapper<>();
w.eq(patientId != null, NursingVitalSignsChart::getPatientId, patientId)
.eq(encounterId != null, NursingVitalSignsChart::getEncounterId, encounterId)
.ge(startDate != null, NursingVitalSignsChart::getRecordDate, startDate)
.le(endDate != null, NursingVitalSignsChart::getRecordDate, endDate)
.orderByDesc(NursingVitalSignsChart::getRecordDate)
.orderByDesc(NursingVitalSignsChart::getRecordHour);
return R.ok(chartService.page(new Page<>(pageNo, pageSize), w));
}
@GetMapping("/list")
public R<?> getList(
@RequestParam("encounterId") Long encounterId,
@RequestParam(value = "startDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate,
@RequestParam(value = "endDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) {
LambdaQueryWrapper<NursingVitalSignsChart> w = new LambdaQueryWrapper<>();
w.eq(NursingVitalSignsChart::getEncounterId, encounterId)
.ge(startDate != null, NursingVitalSignsChart::getRecordDate, startDate)
.le(endDate != null, NursingVitalSignsChart::getRecordDate, endDate)
.orderByAsc(NursingVitalSignsChart::getRecordDate)
.orderByAsc(NursingVitalSignsChart::getRecordHour);
return R.ok(chartService.list(w));
}
@PostMapping("/add")
@Transactional(rollbackFor = Exception.class)
public R<?> add(@RequestBody NursingVitalSignsChart chart) {
chart.setCreateTime(new Date());
chartService.save(chart);
return R.ok(chart);
}
@PutMapping("/update")
@Transactional(rollbackFor = Exception.class)
public R<?> update(@RequestBody NursingVitalSignsChart chart) {
chartService.updateById(chart);
return R.ok(chart);
}
@DeleteMapping("/delete/{id}")
@Transactional(rollbackFor = Exception.class)
public R<?> delete(@PathVariable Long id) {
chartService.removeById(id);
return R.ok();
}
}

View File

@@ -0,0 +1,76 @@
package com.healthlink.his.web.surgicalschedule.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.core.domain.R;
import com.healthlink.his.surgicalschedule.domain.SurgerySafetyCheck;
import com.healthlink.his.surgicalschedule.service.ISurgerySafetyCheckService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
/**
* 术前安全核查 Controller (WS/T 313)
*/
@RestController
@RequestMapping("/surgery-safety-check")
@Slf4j
@AllArgsConstructor
public class SurgerySafetyCheckController {
private final ISurgerySafetyCheckService safetyCheckService;
@GetMapping("/page")
public R<?> getPage(
@RequestParam(value = "patientName", required = false) String patientName,
@RequestParam(value = "encounterId", required = false) Long encounterId,
@RequestParam(value = "checkPhase", required = false) String checkPhase,
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
LambdaQueryWrapper<SurgerySafetyCheck> w = new LambdaQueryWrapper<>();
w.like(StringUtils.hasText(patientName), SurgerySafetyCheck::getPatientName, patientName)
.eq(encounterId != null, SurgerySafetyCheck::getEncounterId, encounterId)
.eq(StringUtils.hasText(checkPhase), SurgerySafetyCheck::getCheckPhase, checkPhase)
.orderByDesc(SurgerySafetyCheck::getCreateTime);
return R.ok(safetyCheckService.page(new Page<>(pageNo, pageSize), w));
}
@GetMapping("/list")
public R<?> getList(@RequestParam("encounterId") Long encounterId) {
LambdaQueryWrapper<SurgerySafetyCheck> w = new LambdaQueryWrapper<>();
w.eq(SurgerySafetyCheck::getEncounterId, encounterId)
.orderByAsc(SurgerySafetyCheck::getCheckPhase);
return R.ok(safetyCheckService.list(w));
}
@GetMapping("/{id}")
public R<?> getById(@PathVariable Long id) {
return R.ok(safetyCheckService.getById(id));
}
@PostMapping("/add")
@Transactional(rollbackFor = Exception.class)
public R<?> add(@RequestBody SurgerySafetyCheck check) {
check.setCreateTime(new Date());
safetyCheckService.save(check);
return R.ok(check);
}
@PutMapping("/update")
@Transactional(rollbackFor = Exception.class)
public R<?> update(@RequestBody SurgerySafetyCheck check) {
safetyCheckService.updateById(check);
return R.ok(check);
}
@DeleteMapping("/delete/{id}")
@Transactional(rollbackFor = Exception.class)
public R<?> delete(@PathVariable Long id) {
safetyCheckService.removeById(id);
return R.ok();
}
}

View File

@@ -0,0 +1,62 @@
package com.healthlink.his.web.system.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.core.domain.R;
import com.healthlink.his.sys.domain.SysAuditLog;
import com.healthlink.his.sys.service.ISysAuditLogService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
/**
* 审计日志 Controller
*/
@RestController
@RequestMapping("/audit-log")
@Slf4j
@AllArgsConstructor
public class SysAuditLogController {
private final ISysAuditLogService auditLogService;
@GetMapping("/page")
public R<?> getPage(
@RequestParam(value = "userName", required = false) String userName,
@RequestParam(value = "module", required = false) String module,
@RequestParam(value = "action", required = false) String action,
@RequestParam(value = "result", required = false) String result,
@RequestParam(value = "userId", required = false) Long userId,
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
LambdaQueryWrapper<SysAuditLog> w = new LambdaQueryWrapper<>();
w.like(StringUtils.hasText(userName), SysAuditLog::getUserName, userName)
.eq(StringUtils.hasText(module), SysAuditLog::getModule, module)
.eq(StringUtils.hasText(action), SysAuditLog::getAction, action)
.eq(StringUtils.hasText(result), SysAuditLog::getResult, result)
.eq(userId != null, SysAuditLog::getUserId, userId)
.orderByDesc(SysAuditLog::getCreateTime);
return R.ok(auditLogService.page(new Page<>(pageNo, pageSize), w));
}
@GetMapping("/{id}")
public R<?> getById(@PathVariable Long id) {
return R.ok(auditLogService.getById(id));
}
@PostMapping("/record")
public R<?> record(@RequestBody SysAuditLog log) {
log.setCreateTime(new Date());
auditLogService.save(log);
return R.ok(log);
}
@DeleteMapping("/delete/{id}")
public R<?> delete(@PathVariable Long id) {
auditLogService.removeById(id);
return R.ok();
}
}

View File

@@ -5,10 +5,26 @@ import com.core.common.core.domain.HisBaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 标本条码管理
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("specimen_barcode")
public class SpecimenBarcode extends HisBaseEntity {
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
private Long encounterId;
private Long patientId;
private String patientName;
private String barcode;
private String specimenType;
private String specimenName;
private LocalDateTime collectionTime;
private String collectorName;
private LocalDateTime scanConfirmTime;
private String scanConfirmBy;
private String status;
}

View File

@@ -5,10 +5,24 @@ import com.core.common.core.domain.HisBaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* 身份证校验记录
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("empi_id_verification")
public class EmpiIdVerification extends HisBaseEntity {
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
private Long patientId;
private String idCard;
private String patientName;
private LocalDate birthDate;
private String gender;
private String verifyResult;
private String verifySource;
private LocalDateTime verifyTime;
}

View File

@@ -5,10 +5,34 @@ import com.core.common.core.domain.HisBaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
* 体温单数据(三测单)
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("nursing_vital_signs_chart")
public class NursingVitalSignsChart extends HisBaseEntity {
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
private Long encounterId;
private Long patientId;
private String patientName;
private LocalDate recordDate;
private Integer recordHour;
private BigDecimal temperature;
private Integer pulse;
private Integer respiration;
private Integer systolicBp;
private Integer diastolicBp;
private BigDecimal heightCm;
private BigDecimal weightKg;
private Integer painScore;
private String consciousLevel;
private Integer inputMl;
private Integer outputMl;
private Integer stoolCount;
private String nurseName;
}

View File

@@ -5,10 +5,32 @@ import com.core.common.core.domain.HisBaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 术前安全核查(WS/T 313)
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("surgery_safety_check")
public class SurgerySafetyCheck extends HisBaseEntity {
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
private Long encounterId;
private Long patientId;
private String patientName;
private String surgeryName;
private String checkPhase;
private LocalDateTime checkTime;
private String checkItems;
private Boolean patientIdConfirmed;
private Boolean surgerySiteConfirmed;
private Boolean procedureTypeConfirmed;
private Boolean allergyConfirmed;
private Boolean vitalSignsBaseline;
private Boolean criticalResultConfirmed;
private Boolean checklistCompleted;
private String anesthesiologistName;
private String surgeonName;
private String nurseName;
}

View File

@@ -5,10 +5,24 @@ import com.core.common.core.domain.HisBaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 审计日志(所有接口调用可追溯)
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("sys_audit_log")
public class SysAuditLog extends HisBaseEntity {
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
private Long userId;
private String userName;
private String module;
private String action;
private String method;
private String url;
private String ip;
private String params;
private String result;
private String errorMsg;
private Integer durationMs;
}

View File

@@ -0,0 +1,5 @@
import request from '@/utils/request'
export function getPage(p){return request({url:'/audit-log/page',method:'get',params:p})}
export function getById(id){return request({url:'/audit-log/'+id,method:'get'})}
export function record(d){return request({url:'/audit-log/record',method:'post',data:d})}
export function del(id){return request({url:'/audit-log/delete/'+id,method:'delete'})}

View File

@@ -0,0 +1,63 @@
<template>
<div style="padding:16px">
<div style="margin-bottom:16px"><span style="font-size:18px;font-weight:bold">审计日志</span></div>
<div style="margin-bottom:12px;display:flex;gap:8px;flex-wrap:wrap">
<el-input v-model="q.userName" placeholder="用户名" clearable style="width:120px"/>
<el-input v-model="q.module" placeholder="模块" clearable style="width:120px"/>
<el-input v-model="q.action" placeholder="操作" clearable style="width:120px"/>
<el-select v-model="q.result" placeholder="结果" clearable style="width:100px">
<el-option label="成功" value="SUCCESS"/>
<el-option label="失败" value="FAIL"/>
</el-select>
<el-button type="primary" @click="loadData">查询</el-button>
</div>
<el-table :data="tableData" border stripe>
<el-table-column prop="userName" label="用户" width="100"/>
<el-table-column prop="module" label="模块" width="120"/>
<el-table-column prop="action" label="操作" width="100"/>
<el-table-column prop="method" label="方法" width="70" align="center"/>
<el-table-column prop="url" label="URL" min-width="200" show-overflow-tooltip/>
<el-table-column prop="ip" label="IP" width="120"/>
<el-table-column prop="result" label="结果" width="80" align="center">
<template #default="{row}">
<el-tag v-if="row.result==='SUCCESS'" type="success" size="small">成功</el-tag>
<el-tag v-else type="danger" size="small">失败</el-tag>
</template>
</el-table-column>
<el-table-column prop="durationMs" label="耗时ms" width="80" align="center"/>
<el-table-column prop="createTime" label="时间" width="170"/>
<el-table-column label="操作" width="80">
<template #default="{row}">
<el-popconfirm title="确定删除?" @confirm="delItem(row.id)">
<template #reference><el-button type="danger" link size="small">删除</el-button></template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<el-pagination style="margin-top:12px;justify-content:flex-end" v-model:current-page="q.pageNo" v-model:page-size="q.pageSize" :total="total" layout="total,prev,pager,next" @current-change="loadData"/>
<el-dialog v-model="detailVisible" title="审计日志详情" width="600px">
<el-descriptions :column="2" border>
<el-descriptions-item label="用户">{{ detail.userName }}</el-descriptions-item>
<el-descriptions-item label="模块">{{ detail.module }}</el-descriptions-item>
<el-descriptions-item label="操作">{{ detail.action }}</el-descriptions-item>
<el-descriptions-item label="方法">{{ detail.method }}</el-descriptions-item>
<el-descriptions-item label="URL" :span="2">{{ detail.url }}</el-descriptions-item>
<el-descriptions-item label="IP">{{ detail.ip }}</el-descriptions-item>
<el-descriptions-item label="耗时">{{ detail.durationMs }}ms</el-descriptions-item>
<el-descriptions-item label="参数" :span="2">{{ detail.params }}</el-descriptions-item>
<el-descriptions-item label="错误" :span="2">{{ detail.errorMsg }}</el-descriptions-item>
</el-descriptions>
</el-dialog>
</div>
</template>
<script setup>
import {ref,onMounted} from 'vue'
import {ElMessage} from 'element-plus'
import {getPage,del} from './api'
const tableData=ref([]);const total=ref(0)
const q=ref({pageNo:1,pageSize:20,userName:'',module:'',action:'',result:''})
const detailVisible=ref(false);const detail=ref({})
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
const delItem=async(id)=>{await del(id);ElMessage.success('已删除');loadData()}
onMounted(()=>loadData())
</script>

View File

@@ -4,3 +4,4 @@ export function addRegistry(data) { return request({ url: '/esb/registry/add', m
export function updateRegistry(data) { return request({ url: '/esb/registry/update', method: 'put', data }) }
export function deleteRegistry(id) { return request({ url: '/esb/registry/delete', method: 'delete', params: { id } }) }
export function updateRegistryStatus(id, status) { return request({ url: '/esb/registry/status', method: 'put', params: { id, status } }) }
export function getRegistryList(params) { return request({ url: "/esb/registry/list", method: "get", params }) }

View File

@@ -0,0 +1,4 @@
import request from '@/utils/request'
export function getPage(p){return request({url:'/id-verification/page',method:'get',params:p})}
export function verify(d){return request({url:'/id-verification/verify',method:'post',data:d})}
export function del(id){return request({url:'/id-verification/delete/'+id,method:'delete'})}

View File

@@ -0,0 +1,78 @@
<template>
<div style="padding:16px">
<div style="margin-bottom:16px"><span style="font-size:18px;font-weight:bold">身份证校验记录</span></div>
<div style="margin-bottom:12px;display:flex;gap:8px;flex-wrap:wrap">
<el-input v-model="q.patientName" placeholder="患者姓名" clearable style="width:140px"/>
<el-select v-model="q.verifyResult" placeholder="校验结果" clearable style="width:120px">
<el-option label="通过" value="PASS"/>
<el-option label="失败" value="FAIL"/>
<el-option label="待验" value="PENDING"/>
</el-select>
<el-button type="primary" @click="loadData">查询</el-button>
<el-button type="success" @click="openVerify">新增校验</el-button>
</div>
<el-table :data="tableData" border stripe>
<el-table-column prop="patientName" label="患者" width="100"/>
<el-table-column prop="idCard" label="身份证号" width="180"/>
<el-table-column prop="birthDate" label="出生日期" width="110"/>
<el-table-column prop="gender" label="性别" width="60" align="center"/>
<el-table-column prop="verifyResult" label="结果" width="80" align="center">
<template #default="{row}">
<el-tag v-if="row.verifyResult==='PASS'" type="success" size="small">通过</el-tag>
<el-tag v-else-if="row.verifyResult==='FAIL'" type="danger" size="small">失败</el-tag>
<el-tag v-else type="info" size="small">待验</el-tag>
</template>
</el-table-column>
<el-table-column prop="verifySource" label="校验来源" width="100"/>
<el-table-column prop="verifyTime" label="校验时间" width="170"/>
<el-table-column label="操作" width="80">
<template #default="{row}">
<el-popconfirm title="确定删除?" @confirm="delItem(row.id)">
<template #reference><el-button type="danger" link size="small">删除</el-button></template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<el-pagination style="margin-top:12px;justify-content:flex-end" v-model:current-page="q.pageNo" v-model:page-size="q.pageSize" :total="total" layout="total,prev,pager,next" @current-change="loadData"/>
<el-dialog v-model="dlgVisible" title="新增身份证校验" width="500px">
<el-form :model="form" label-width="80px">
<el-form-item label="患者ID"><el-input v-model.number="form.patientId"/></el-form-item>
<el-form-item label="患者姓名"><el-input v-model="form.patientName"/></el-form-item>
<el-form-item label="身份证号"><el-input v-model="form.idCard"/></el-form-item>
<el-form-item label="出生日期"><el-date-picker v-model="form.birthDate" type="date"/></el-form-item>
<el-form-item label="性别">
<el-select v-model="form.gender">
<el-option label="男" value="男"/>
<el-option label="女" value="女"/>
</el-select>
</el-form-item>
<el-form-item label="校验结果">
<el-select v-model="form.verifyResult">
<el-option label="通过" value="PASS"/>
<el-option label="失败" value="FAIL"/>
<el-option label="待验" value="PENDING"/>
</el-select>
</el-form-item>
<el-form-item label="校验来源"><el-input v-model="form.verifySource"/></el-form-item>
</el-form>
<template #footer>
<el-button @click="dlgVisible=false">取消</el-button>
<el-button type="primary" @click="saveData">保存</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import {ref,onMounted} from 'vue'
import {ElMessage} from 'element-plus'
import {getPage,verify,del} from './api'
const tableData=ref([]);const total=ref(0)
const q=ref({pageNo:1,pageSize:20,patientName:'',verifyResult:''})
const dlgVisible=ref(false)
const form=ref({patientId:null,patientName:'',idCard:'',birthDate:null,gender:'',verifyResult:'PENDING',verifySource:''})
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
const openVerify=()=>{form.value={patientId:null,patientName:'',idCard:'',birthDate:null,gender:'',verifyResult:'PENDING',verifySource:''};dlgVisible.value=true}
const saveData=async()=>{await verify(form.value);ElMessage.success('校验完成');dlgVisible.value=false;loadData()}
const delItem=async(id)=>{await del(id);ElMessage.success('已删除');loadData()}
onMounted(()=>loadData())
</script>

View File

@@ -0,0 +1,9 @@
import request from '@/utils/request'
export function getPage(p){return request({url:'/specimen-barcode/page',method:'get',params:p})}
export function getById(id){return request({url:'/specimen-barcode/'+id,method:'get'})}
export function getByBarcode(bc){return request({url:'/specimen-barcode/by-barcode/'+bc,method:'get'})}
export function add(d){return request({url:'/specimen-barcode/add',method:'post',data:d})}
export function scanConfirm(id,confirmBy){return request({url:'/specimen-barcode/scan/'+id,method:'put',params:{confirmBy}})}
export function reject(id){return request({url:'/specimen-barcode/reject/'+id,method:'put'})}
export function del(id){return request({url:'/specimen-barcode/delete/'+id,method:'delete'})}
export function update(d){return request({url:"/specimen-barcode/update",method:"put",data:d})}

View File

@@ -0,0 +1,79 @@
<template>
<div style="padding:16px">
<div style="margin-bottom:16px"><span style="font-size:18px;font-weight:bold">标本条码管理</span></div>
<div style="margin-bottom:12px;display:flex;gap:8px;flex-wrap:wrap">
<el-input v-model="q.patientName" placeholder="患者姓名" clearable style="width:140px"/>
<el-input v-model="q.barcode" placeholder="条码号" clearable style="width:140px"/>
<el-select v-model="q.status" placeholder="状态" clearable style="width:120px">
<el-option label="已采集" value="COLLECTED"/>
<el-option label="已扫码" value="SCANNED"/>
<el-option label="已拒收" value="REJECTED"/>
</el-select>
<el-input v-model="scanInput" placeholder="扫码(回车确认)" style="width:180px" @keyup.enter="doScan"/>
<el-button type="primary" @click="loadData">查询</el-button>
<el-button type="success" @click="openAdd">新增条码</el-button>
</div>
<el-table :data="tableData" border stripe>
<el-table-column prop="barcode" label="条码" width="140"/>
<el-table-column prop="patientName" label="患者" width="100"/>
<el-table-column prop="specimenType" label="标本类型" width="100"/>
<el-table-column prop="specimenName" label="标本名称" width="120"/>
<el-table-column prop="collectionTime" label="采集时间" width="170"/>
<el-table-column prop="collectorName" label="采集人" width="80"/>
<el-table-column prop="status" label="状态" width="90">
<template #default="{row}">
<el-tag v-if="row.status==='COLLECTED'" type="info" size="small">已采集</el-tag>
<el-tag v-else-if="row.status==='SCANNED'" type="success" size="small">已扫码</el-tag>
<el-tag v-else type="danger" size="small">已拒收</el-tag>
</template>
</el-table-column>
<el-table-column prop="scanConfirmBy" label="扫码人" width="80"/>
<el-table-column label="操作" width="200">
<template #default="{row}">
<el-button v-if="row.status==='COLLECTED'" type="success" link size="small" @click="doScanConfirm(row)">确认扫码</el-button>
<el-button v-if="row.status==='COLLECTED'" type="warning" link size="small" @click="doReject(row)">拒收</el-button>
<el-button type="primary" link size="small" @click="openEdit(row)">编辑</el-button>
<el-popconfirm title="确定删除?" @confirm="delItem(row.id)">
<template #reference><el-button type="danger" link size="small">删除</el-button></template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<el-pagination style="margin-top:12px;justify-content:flex-end" v-model:current-page="q.pageNo" v-model:page-size="q.pageSize" :total="total" layout="total,prev,pager,next" @current-change="loadData"/>
<el-dialog v-model="dlgVisible" :title="isEdit?'编辑条码':'新增条码'" width="500px">
<el-form :model="form" label-width="80px">
<el-form-item label="患者ID"><el-input v-model.number="form.patientId"/></el-form-item>
<el-form-item label="就诊ID"><el-input v-model.number="form.encounterId"/></el-form-item>
<el-form-item label="患者姓名"><el-input v-model="form.patientName"/></el-form-item>
<el-form-item label="条码号"><el-input v-model="form.barcode"/></el-form-item>
<el-form-item label="标本类型"><el-input v-model="form.specimenType"/></el-form-item>
<el-form-item label="标本名称"><el-input v-model="form.specimenName"/></el-form-item>
<el-form-item label="采集人"><el-input v-model="form.collectorName"/></el-form-item>
</el-form>
<template #footer>
<el-button @click="dlgVisible=false">取消</el-button>
<el-button type="primary" @click="saveData">保存</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import {ref,onMounted} from 'vue'
import {ElMessage,ElMessageBox} from 'element-plus'
import {getPage,add,update,scanConfirm,reject,del} from './api'
const tableData=ref([]);const total=ref(0)
const q=ref({pageNo:1,pageSize:20,patientName:'',barcode:'',status:''})
const scanInput=ref('')
const dlgVisible=ref(false);const isEdit=ref(false)
const defaultForm=()=>({patientId:null,encounterId:null,patientName:'',barcode:'',specimenType:'',specimenName:'',collectorName:''})
const form=ref(defaultForm())
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
const openAdd=()=>{isEdit.value=false;form.value=defaultForm();dlgVisible.value=true}
const openEdit=(row)=>{isEdit.value=true;form.value={...row};dlgVisible.value=true}
const saveData=async()=>{if(isEdit.value){await update(form.value)}else{await add(form.value)}ElMessage.success('保存成功');dlgVisible.value=false;loadData()}
const doScan=async()=>{if(!scanInput.value)return;const r=await getPage({barcode:scanInput.value,pageNo:1,pageSize:1});if(r.data?.records?.length){ElMessage.success('条码找到: '+r.data.records[0].patientName)}else{ElMessage.warning('条码不存在')};scanInput.value=''}
const doScanConfirm=async(row)=>{const {value}=await ElMessageBox.prompt('确认扫码人','确认扫码');if(value){await scanConfirm(row.id,value);ElMessage.success('扫码确认成功');loadData()}}
const doReject=async(row)=>{await ElMessageBox.confirm('确定拒收该标本?');await reject(row.id);ElMessage.success('已拒收');loadData()}
const delItem=async(id)=>{await del(id);ElMessage.success('已删除');loadData()}
onMounted(()=>loadData())
</script>

View File

@@ -0,0 +1,7 @@
import request from '@/utils/request'
export function getPage(p){return request({url:'/surgery-safety-check/page',method:'get',params:p})}
export function getList(p){return request({url:'/surgery-safety-check/list',method:'get',params:p})}
export function getById(id){return request({url:'/surgery-safety-check/'+id,method:'get'})}
export function add(d){return request({url:'/surgery-safety-check/add',method:'post',data:d})}
export function update(d){return request({url:'/surgery-safety-check/update',method:'put',data:d})}
export function del(id){return request({url:'/surgery-safety-check/delete/'+id,method:'delete'})}

View File

@@ -0,0 +1,93 @@
<template>
<div style="padding:16px">
<div style="margin-bottom:16px"><span style="font-size:18px;font-weight:bold">术前安全核查</span></div>
<div style="margin-bottom:12px;display:flex;gap:8px;flex-wrap:wrap">
<el-input v-model="q.patientName" placeholder="患者姓名" clearable style="width:140px"/>
<el-select v-model="q.checkPhase" placeholder="核查阶段" clearable style="width:140px">
<el-option label="麻醉前" value="ANESTHESIA_BEFORE"/>
<el-option label="手术前" value="SURGERY_BEFORE"/>
<el-option label="离室前" value="EXIT"/>
</el-select>
<el-button type="primary" @click="loadData">查询</el-button>
<el-button type="success" @click="openAdd">新增核查</el-button>
</div>
<el-table :data="tableData" border stripe>
<el-table-column prop="patientName" label="患者" width="100"/>
<el-table-column prop="surgeryName" label="手术名称" width="160"/>
<el-table-column prop="checkPhase" label="阶段" width="90">
<template #default="{row}">
<el-tag v-if="row.checkPhase==='ANESTHESIA_BEFORE'" type="warning" size="small">麻醉前</el-tag>
<el-tag v-else-if="row.checkPhase==='SURGERY_BEFORE'" type="primary" size="small">手术前</el-tag>
<el-tag v-else type="success" size="small">离室前</el-tag>
</template>
</el-table-column>
<el-table-column prop="checkTime" label="核查时间" width="170"/>
<el-table-column prop="surgeonName" label="主刀" width="80"/>
<el-table-column prop="anesthesiologistName" label="麻醉" width="80"/>
<el-table-column prop="nurseName" label="护士" width="80"/>
<el-table-column label="核查项" width="200">
<template #default="{row}">
<el-tag v-if="row.patientIdConfirmed" type="success" size="small">身份</el-tag>
<el-tag v-if="row.surgerySiteConfirmed" type="success" size="small">术野</el-tag>
<el-tag v-if="row.procedureTypeConfirmed" type="success" size="small">术式</el-tag>
<el-tag v-if="row.allergyConfirmed" type="success" size="small">过敏</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="140">
<template #default="{row}">
<el-button type="primary" link size="small" @click="openEdit(row)">编辑</el-button>
<el-popconfirm title="确定删除?" @confirm="delItem(row.id)">
<template #reference><el-button type="danger" link size="small">删除</el-button></template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<el-pagination style="margin-top:12px;justify-content:flex-end" v-model:current-page="q.pageNo" v-model:page-size="q.pageSize" :total="total" layout="total,prev,pager,next" @current-change="loadData"/>
<el-dialog v-model="dlgVisible" :title="isEdit?'编辑核查':'新增核查'" width="650px">
<el-form :model="form" label-width="90px">
<el-form-item label="患者ID"><el-input v-model.number="form.patientId"/></el-form-item>
<el-form-item label="就诊ID"><el-input v-model.number="form.encounterId"/></el-form-item>
<el-form-item label="患者姓名"><el-input v-model="form.patientName"/></el-form-item>
<el-form-item label="手术名称"><el-input v-model="form.surgeryName"/></el-form-item>
<el-form-item label="核查阶段">
<el-select v-model="form.checkPhase">
<el-option label="麻醉前" value="ANESTHESIA_BEFORE"/>
<el-option label="手术前" value="SURGERY_BEFORE"/>
<el-option label="离室前" value="EXIT"/>
</el-select>
</el-form-item>
<el-form-item label="核查时间"><el-date-picker v-model="form.checkTime" type="datetime"/></el-form-item>
<el-form-item label="身份确认"><el-switch v-model="form.patientIdConfirmed"/></el-form-item>
<el-form-item label="术野确认"><el-switch v-model="form.surgerySiteConfirmed"/></el-form-item>
<el-form-item label="术式确认"><el-switch v-model="form.procedureTypeConfirmed"/></el-form-item>
<el-form-item label="过敏确认"><el-switch v-model="form.allergyConfirmed"/></el-form-item>
<el-form-item label="生命体征"><el-switch v-model="form.vitalSignsBaseline"/></el-form-item>
<el-form-item label="危急值"><el-switch v-model="form.criticalResultConfirmed"/></el-form-item>
<el-form-item label="主刀医生"><el-input v-model="form.surgeonName"/></el-form-item>
<el-form-item label="麻醉医生"><el-input v-model="form.anesthesiologistName"/></el-form-item>
<el-form-item label="护士"><el-input v-model="form.nurseName"/></el-form-item>
<el-form-item label="核查备注"><el-input v-model="form.checkItems" type="textarea" :rows="3"/></el-form-item>
</el-form>
<template #footer>
<el-button @click="dlgVisible=false">取消</el-button>
<el-button type="primary" @click="saveData">保存</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import {ref,onMounted} from 'vue'
import {ElMessage} from 'element-plus'
import {getPage,add,update,del} from './api'
const tableData=ref([]);const total=ref(0)
const q=ref({pageNo:1,pageSize:20,patientName:'',checkPhase:''})
const dlgVisible=ref(false);const isEdit=ref(false)
const defaultForm=()=>({patientId:null,encounterId:null,patientName:'',surgeryName:'',checkPhase:'ANESTHESIA_BEFORE',checkTime:null,checkItems:'',patientIdConfirmed:false,surgerySiteConfirmed:false,procedureTypeConfirmed:false,allergyConfirmed:false,vitalSignsBaseline:false,criticalResultConfirmed:false,surgeonName:'',anesthesiologistName:'',nurseName:''})
const form=ref(defaultForm())
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
const openAdd=()=>{isEdit.value=false;form.value=defaultForm();dlgVisible.value=true}
const openEdit=(row)=>{isEdit.value=true;form.value={...row};dlgVisible.value=true}
const saveData=async()=>{if(isEdit.value){await update(form.value)}else{await add(form.value)}ElMessage.success('保存成功');dlgVisible.value=false;loadData()}
const delItem=async(id)=>{await del(id);ElMessage.success('已删除');loadData()}
onMounted(()=>loadData())
</script>

View File

@@ -0,0 +1,6 @@
import request from '@/utils/request'
export function getChartPage(p){return request({url:'/vital-signs-chart/page',method:'get',params:p})}
export function getChartList(p){return request({url:'/vital-signs-chart/list',method:'get',params:p})}
export function addChart(d){return request({url:'/vital-signs-chart/add',method:'post',data:d})}
export function updateChart(d){return request({url:'/vital-signs-chart/update',method:'put',data:d})}
export function deleteChart(id){return request({url:'/vital-signs-chart/delete/'+id,method:'delete'})}

View File

@@ -0,0 +1,74 @@
<template>
<div style="padding:16px">
<div style="margin-bottom:16px"><span style="font-size:18px;font-weight:bold">体温单数据管理</span></div>
<div style="margin-bottom:12px;display:flex;gap:8px;flex-wrap:wrap">
<el-input v-model="q.patientName" placeholder="患者姓名" clearable style="width:140px"/>
<el-input v-model="q.patientId" placeholder="患者ID" clearable style="width:120px"/>
<el-date-picker v-model="q.dateRange" type="daterange" range-separator="~" start-placeholder="开始日期" end-placeholder="结束日期" style="width:240px"/>
<el-button type="primary" @click="loadData">查询</el-button>
<el-button type="success" @click="openAdd">新增记录</el-button>
</div>
<el-table :data="tableData" border stripe>
<el-table-column prop="patientName" label="患者" width="100"/>
<el-table-column prop="recordDate" label="日期" width="110"/>
<el-table-column prop="recordHour" label="时" width="50" align="center"/>
<el-table-column prop="temperature" label="体温℃" width="70" align="center"/>
<el-table-column prop="pulse" label="脉搏" width="60" align="center"/>
<el-table-column prop="respiration" label="呼吸" width="60" align="center"/>
<el-table-column label="血压" width="100" align="center">
<template #default="{row}">{{ row.systolicBp }}/{{ row.diastolicBp }}</template>
</el-table-column>
<el-table-column prop="painScore" label="疼痛" width="60" align="center"/>
<el-table-column prop="consciousLevel" label="意识" width="70"/>
<el-table-column prop="inputMl" label="入量ml" width="70" align="center"/>
<el-table-column prop="outputMl" label="出量ml" width="70" align="center"/>
<el-table-column prop="nurseName" label="护士" width="80"/>
<el-table-column label="操作" width="140">
<template #default="{row}">
<el-button type="primary" link size="small" @click="openEdit(row)">编辑</el-button>
<el-popconfirm title="确定删除?" @confirm="del(row.id)">
<template #reference><el-button type="danger" link size="small">删除</el-button></template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<el-pagination style="margin-top:12px;justify-content:flex-end" v-model:current-page="q.pageNo" v-model:page-size="q.pageSize" :total="total" layout="total,prev,pager,next" @current-change="loadData"/>
<el-dialog v-model="dlgVisible" :title="isEdit?'编辑体温单':'新增体温单'" width="600px">
<el-form :model="form" label-width="80px">
<el-form-item label="患者ID"><el-input v-model.number="form.patientId"/></el-form-item>
<el-form-item label="就诊ID"><el-input v-model.number="form.encounterId"/></el-form-item>
<el-form-item label="患者姓名"><el-input v-model="form.patientName"/></el-form-item>
<el-form-item label="记录日期"><el-date-picker v-model="form.recordDate" type="date"/></el-form-item>
<el-form-item label="记录小时"><el-input-number v-model="form.recordHour" :min="0" :max="23"/></el-form-item>
<el-form-item label="体温℃"><el-input-number v-model="form.temperature" :min="35" :max="42" :step="0.1"/></el-form-item>
<el-form-item label="脉搏"><el-input-number v-model="form.pulse" :min="40" :max="180"/></el-form-item>
<el-form-item label="呼吸"><el-input-number v-model="form.respiration" :min="8" :max="50"/></el-form-item>
<el-form-item label="收缩压"><el-input-number v-model="form.systolicBp" :min="60" :max="250"/></el-form-item>
<el-form-item label="舒张压"><el-input-number v-model="form.diastolicBp" :min="30" :max="150"/></el-form-item>
<el-form-item label="疼痛评分"><el-input-number v-model="form.painScore" :min="0" :max="10"/></el-form-item>
<el-form-item label="入量ml"><el-input-number v-model="form.inputMl" :min="0"/></el-form-item>
<el-form-item label="出量ml"><el-input-number v-model="form.outputMl" :min="0"/></el-form-item>
<el-form-item label="护士"><el-input v-model="form.nurseName"/></el-form-item>
</el-form>
<template #footer>
<el-button @click="dlgVisible=false">取消</el-button>
<el-button type="primary" @click="saveData">保存</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import {ref,onMounted} from 'vue'
import {ElMessage} from 'element-plus'
import {getChartPage,addChart,updateChart,deleteChart} from './api'
const tableData=ref([]);const total=ref(0)
const q=ref({pageNo:1,pageSize:20,patientName:'',patientId:'',dateRange:null})
const dlgVisible=ref(false);const isEdit=ref(false)
const form=ref({patientId:null,encounterId:null,patientName:'',recordDate:null,recordHour:8,temperature:36.5,pulse:72,respiration:18,systolicBp:120,diastolicBp:80,painScore:0,inputMl:0,outputMl:0,nurseName:''})
const loadData=async()=>{const r=await getChartPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
const openAdd=()=>{isEdit.value=false;form.value={patientId:null,encounterId:null,patientName:'',recordDate:null,recordHour:8,temperature:36.5,pulse:72,respiration:18,systolicBp:120,diastolicBp:80,painScore:0,inputMl:0,outputMl:0,nurseName:''};dlgVisible.value=true}
const openEdit=(row)=>{isEdit.value=true;form.value={...row};dlgVisible.value=true}
const saveData=async()=>{if(isEdit.value){await updateChart(form.value)}else{await addChart(form.value)}ElMessage.success('保存成功');dlgVisible.value=false;loadData()}
const del=async(id)=>{await deleteChart(id);ElMessage.success('已删除');loadData()}
onMounted(()=>loadData())
</script>