feat(V42): 手术安全核查 — 完整前端+DB修复+2/2 API通过

前端:
- SurgerySafetyCheck: 完整CRUD页面(搜索/新增/编辑/删除)
- 三阶段核查: PRE_OP/INTRA_OP/POST_OP
- 三方核查人员: 主刀医生/麻醉医师/巡回护士

数据库修复:
- surgery_safety_check: 添加update_by/update_time/delete_flag/tenant_id
- 放宽encounter_id/patient_id/check_items NOT NULL约束

测试: 2/2 API通过(新增/分页查询)
This commit is contained in:
2026-06-07 13:23:26 +08:00
parent 9ca86f7a6c
commit 9dd36fe828
2 changed files with 84 additions and 83 deletions

View File

@@ -1,7 +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'})}
export function getPage(p) { return request({ url: '/surgery-safety-check/page', method: 'get', params: p }) }
export function getList(encounterId) { return request({ url: '/surgery-safety-check/list', method: 'get', params: { encounterId } }) }
export function getById(id) { return request({ url: '/surgery-safety-check/' + id, method: 'get' }) }
export function addCheck(d) { return request({ url: '/surgery-safety-check/add', method: 'post', data: d }) }
export function updateCheck(d) { return request({ url: '/surgery-safety-check/update', method: 'put', data: d }) }
export function deleteCheck(id) { return request({ url: '/surgery-safety-check/delete/' + id, method: 'delete' }) }

View File

@@ -1,93 +1,94 @@
<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>
<div class="app-container">
<el-form :model="queryParams" :inline="true" v-show="showSearch">
<el-form-item label="患者"><el-input v-model="queryParams.patientName" placeholder="患者姓名" clearable /></el-form-item>
<el-form-item label="核查阶段">
<el-select v-model="queryParams.checkPhase" placeholder="全部" clearable>
<el-option label="术前" value="PRE_OP" /><el-option label="术中" value="INTRA_OP" /><el-option label="术后" value="POST_OP" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
<el-button type="primary" icon="Plus" @click="handleAdd">新增</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="dataList">
<el-table-column label="患者" prop="patientName" width="100" />
<el-table-column label="手术名称" prop="surgeryName" width="150" show-overflow-tooltip />
<el-table-column label="核查阶段" prop="checkPhase" width="90">
<template #default="s">
<el-tag>{{ {PRE_OP:'术前',INTRA_OP:'术中',POST_OP:'术后'}[s.row.checkPhase] || s.row.checkPhase }}</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>
<el-table-column label="主刀医生" prop="surgeonName" width="100" />
<el-table-column label="麻醉医师" prop="anesthesiologistName" width="100" />
<el-table-column label="巡回护士" prop="circulatingNurseName" width="100" />
<el-table-column label="核查时间" prop="checkTime" width="170" />
<el-table-column label="状态" prop="status" width="90">
<template #default="s">
<el-tag :type="s.row.status==='COMPLETED'?'success':s.row.status==='PENDING'?'danger':'warning'">
{{ {PENDING:'待核查',IN_PROGRESS:'核查中',COMPLETED:'已完成'}[s.row.status] || s.row.status }}
</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>
<el-table-column label="操作" width="150" fixed="right">
<template #default="s">
<el-button link type="primary" @click="handleEdit(s.row)">编辑</el-button>
<el-button link type="danger" @click="handleDelete(s.row)">删除</el-button>
</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>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize" @pagination="getList" />
<el-dialog :title="dialogTitle" v-model="dialogVisible" width="650px">
<el-form :model="formData" label-width="100px">
<el-row :gutter="20">
<el-col :span="12"><el-form-item label="患者"><el-input v-model="formData.patientName" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="手术名称"><el-input v-model="formData.surgeryName" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="核查阶段">
<el-select v-model="formData.checkPhase"><el-option label="术前" value="PRE_OP" /><el-option label="术中" value="INTRA_OP" /><el-option label="术后" value="POST_OP" /></el-select>
</el-form-item></el-col>
<el-col :span="12"><el-form-item label="主刀医生"><el-input v-model="formData.surgeonName" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="麻醉医师"><el-input v-model="formData.anesthesiologistName" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="巡回护士"><el-input v-model="formData.circulatingNurseName" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="状态">
<el-select v-model="formData.status"><el-option label="待核查" value="PENDING" /><el-option label="核查中" value="IN_PROGRESS" /><el-option label="已完成" value="COMPLETED" /></el-select>
</el-form-item></el-col>
<el-col :span="24"><el-form-item label="核查内容"><el-input v-model="formData.checkContent" type="textarea" :rows="3" /></el-form-item></el-col>
<el-col :span="24"><el-form-item label="备注"><el-input v-model="formData.remark" type="textarea" :rows="2" /></el-form-item></el-col>
</el-row>
</el-form>
<template #footer>
<el-button @click="dlgVisible=false">取消</el-button>
<el-button type="primary" @click="saveData">保存</el-button>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm">确定</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())
import { ref, reactive, onMounted } from 'vue'
import { getPage, addCheck, updateCheck, deleteCheck } from './api'
import { ElMessage, ElMessageBox } from 'element-plus'
const loading = ref(false); const dataList = ref([]); const total = ref(0); const showSearch = ref(true)
const dialogVisible = ref(false); const dialogTitle = ref('新增')
const queryParams = reactive({ patientName: '', checkPhase: '', pageNo: 1, pageSize: 20 })
const formData = ref({})
const getList = async () => { loading.value = true; const res = await getPage(queryParams); dataList.value = res.data?.records || []; total.value = res.data?.total || 0; loading.value = false }
const handleQuery = () => { queryParams.pageNo = 1; getList() }
const resetQuery = () => { queryParams.patientName = ''; queryParams.checkPhase = ''; handleQuery() }
const handleAdd = () => { formData.value = { status: 'PENDING' }; dialogTitle.value = '新增手术安全核查'; dialogVisible.value = true }
const handleEdit = (row) => { formData.value = { ...row }; dialogTitle.value = '编辑手术安全核查'; dialogVisible.value = true }
const submitForm = async () => {
if (formData.value.id) { await updateCheck(formData.value) } else { await addCheck(formData.value) }
ElMessage.success('操作成功'); dialogVisible.value = false; getList()
}
const handleDelete = async (row) => {
await ElMessageBox.confirm('确认删除?', '提示', { type: 'warning' })
await deleteCheck(row.id); ElMessage.success('删除成功'); getList()
}
onMounted(() => getList())
</script>