feat(mrhomepage): 死亡病例讨论记录

This commit is contained in:
2026-06-17 14:50:25 +08:00
parent f1a8fafb72
commit 84c0f6a43d
5 changed files with 388 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
package com.healthlink.his.web.mrhomepage.appservice;
import com.healthlink.his.mrhomepage.domain.MrDeathDiscussion;
import java.util.List;
public interface IDeathDiscussionAppService {
MrDeathDiscussion saveDiscussion(MrDeathDiscussion discussion);
List<MrDeathDiscussion> getDiscussions(Long homepageId);
List<MrDeathDiscussion> checkDeadline();
}

View File

@@ -0,0 +1,64 @@
package com.healthlink.his.web.mrhomepage.appservice.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.healthlink.his.mrhomepage.domain.MrDeathDiscussion;
import com.healthlink.his.mrhomepage.service.IMrDeathDiscussionService;
import com.healthlink.his.web.mrhomepage.appservice.IDeathDiscussionAppService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
@Service
public class DeathDiscussionAppServiceImpl implements IDeathDiscussionAppService {
@Resource
private IMrDeathDiscussionService mrDeathDiscussionService;
@Override
@Transactional
public MrDeathDiscussion saveDiscussion(MrDeathDiscussion discussion) {
if (discussion.getId() == null) {
if (discussion.getDeathDate() != null) {
Calendar cal = Calendar.getInstance();
cal.setTime(discussion.getDeathDate());
cal.add(Calendar.DAY_OF_MONTH, 7);
discussion.setDeadlineDate(cal.getTime());
}
discussion.setIsOverdue(false);
discussion.setStatus(0);
}
mrDeathDiscussionService.saveOrUpdate(discussion);
return discussion;
}
@Override
public List<MrDeathDiscussion> getDiscussions(Long homepageId) {
return mrDeathDiscussionService.list(
new LambdaQueryWrapper<MrDeathDiscussion>()
.eq(MrDeathDiscussion::getEncounterId, homepageId)
.orderByDesc(MrDeathDiscussion::getDiscussionDate)
);
}
@Override
public List<MrDeathDiscussion> checkDeadline() {
Date now = new Date();
List<MrDeathDiscussion> overdue = mrDeathDiscussionService.list(
new LambdaQueryWrapper<MrDeathDiscussion>()
.le(MrDeathDiscussion::getDeadlineDate, now)
.eq(MrDeathDiscussion::getIsOverdue, false)
.eq(MrDeathDiscussion::getStatus, 0)
);
for (MrDeathDiscussion d : overdue) {
d.setIsOverdue(true);
}
if (!overdue.isEmpty()) {
mrDeathDiscussionService.updateBatchById(overdue);
}
return overdue;
}
}

View File

@@ -0,0 +1,42 @@
package com.healthlink.his.web.mrhomepage.controller;
import com.core.common.core.domain.R;
import com.healthlink.his.mrhomepage.domain.MrDeathDiscussion;
import com.healthlink.his.web.mrhomepage.appservice.IDeathDiscussionAppService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/v1/mr-homepage/death-discussion")
@Tag(name = "死亡病例讨论记录")
public class DeathDiscussionController {
@Resource
private IDeathDiscussionAppService deathDiscussionAppService;
@PostMapping("/save")
@PreAuthorize("hasAuthority('inpatient:mrhomepage:edit')")
@Operation(summary = "保存死亡病例讨论记录")
public R<MrDeathDiscussion> saveDiscussion(@RequestBody MrDeathDiscussion discussion) {
return R.ok(deathDiscussionAppService.saveDiscussion(discussion));
}
@GetMapping("/list/{homepageId}")
@PreAuthorize("hasAuthority('inpatient:mrhomepage:list')")
@Operation(summary = "查询死亡病例讨论记录列表")
public R<List<MrDeathDiscussion>> getDiscussions(@PathVariable Long homepageId) {
return R.ok(deathDiscussionAppService.getDiscussions(homepageId));
}
@GetMapping("/pending-deadline")
@PreAuthorize("hasAuthority('inpatient:mrhomepage:list')")
@Operation(summary = "查询7日到期未讨论记录")
public R<List<MrDeathDiscussion>> checkDeadline() {
return R.ok(deathDiscussionAppService.checkDeadline());
}
}

View File

@@ -11,3 +11,6 @@ export function getQualityResults(homepageId) { return request({ url: '/api/v1/m
export function submitHqmsReport(homepageId, reportType) { return request({ url: '/api/v1/mr-homepage/hqms/report', method: 'post', params: { homepageId, reportType } }) }
export function getHqmsReportStatus(homepageId) { return request({ url: '/api/v1/mr-homepage/hqms/status/' + homepageId, method: 'get' }) }
export function listHqmsReports(homepageId) { return request({ url: '/api/v1/mr-homepage/hqms/list/' + homepageId, method: 'get' }) }
export function saveDeathDiscussion(data) { return request({ url: '/api/v1/mr-homepage/death-discussion/save', method: 'post', data }) }
export function listDeathDiscussions(homepageId) { return request({ url: '/api/v1/mr-homepage/death-discussion/list/' + homepageId, method: 'get' }) }
export function getPendingDeadline() { return request({ url: '/api/v1/mr-homepage/death-discussion/pending-deadline', method: 'get' }) }

View File

@@ -0,0 +1,265 @@
<template>
<div class="app-container">
<el-card shadow="never">
<template #header>
<div class="card-header">
<span>死亡病例讨论记录</span>
<div>
<el-input
v-model="homepageId"
placeholder="请输入病案首页ID"
style="width: 220px; margin-right: 12px"
@keyup.enter="loadDiscussions"
/>
<el-button type="primary" :loading="loading" @click="loadDiscussions">
查询
</el-button>
<el-button type="warning" :loading="deadlineLoading" @click="checkDeadline" style="margin-left: 8px">
到期检查
</el-button>
<el-button type="success" @click="showAddDialog" style="margin-left: 8px">
新增讨论
</el-button>
</div>
</div>
</template>
<el-alert
v-if="pendingDeadline.length > 0"
:title="'有 ' + pendingDeadline.length + ' 条死亡病例已超过7日期限未讨论'"
type="warning"
show-icon
:closable="false"
style="margin-bottom: 16px"
>
<template #default>
<div v-for="item in pendingDeadline" :key="item.id" style="margin-top: 4px; font-size: 13px">
患者{{ item.patientName }} | 死亡日期{{ item.deathDate }} | 截止日期{{ item.deadlineDate }}
</div>
</template>
</el-alert>
<el-table :data="discussions" stripe border style="width: 100%">
<el-table-column prop="patientName" label="患者姓名" width="120" />
<el-table-column prop="deathDate" label="死亡日期" width="170" />
<el-table-column prop="discussionDate" label="讨论日期" width="170" />
<el-table-column prop="deadlineDate" label="讨论截止" width="170">
<template #default="scope">
<span :style="{ color: scope.row.isOverdue ? '#f56c6c' : '' }">
{{ scope.row.deadlineDate }}
</span>
</template>
</el-table-column>
<el-table-column prop="hostDoctorName" label="主持人" width="120" />
<el-table-column prop="participants" label="参加人员" min-width="180" show-overflow-tooltip />
<el-table-column prop="discussionConclusion" label="讨论结论" min-width="200" show-overflow-tooltip />
<el-table-column prop="status" label="状态" width="100">
<template #default="scope">
<el-tag :type="scope.row.status === 1 ? 'success' : (scope.row.isOverdue ? 'danger' : 'warning')" size="small">
{{ scope.row.status === 1 ? '已完成' : (scope.row.isOverdue ? '已逾期' : '待讨论') }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="100" fixed="right">
<template #default="scope">
<el-button
v-if="scope.row.status === 0"
type="primary"
link
size="small"
@click="showEditDialog(scope.row)"
>
编辑
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<el-dialog v-model="dialogVisible" :title="isEdit ? '编辑讨论记录' : '新增讨论记录'" width="700px">
<el-form ref="formRef" :model="form" :rules="rules" label-width="110px">
<el-form-item label="病案首页ID" prop="encounterId">
<el-input-number v-model="form.encounterId" :min="1" style="width: 100%" />
</el-form-item>
<el-form-item label="患者姓名" prop="patientName">
<el-input v-model="form.patientName" placeholder="请输入患者姓名" />
</el-form-item>
<el-form-item label="患者ID" prop="patientId">
<el-input-number v-model="form.patientId" :min="1" style="width: 100%" />
</el-form-item>
<el-form-item label="死亡日期" prop="deathDate">
<el-date-picker
v-model="form.deathDate"
type="datetime"
placeholder="选择死亡日期"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="讨论日期" prop="discussionDate">
<el-date-picker
v-model="form.discussionDate"
type="datetime"
placeholder="选择讨论日期"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="主持人" prop="hostDoctorName">
<el-input v-model="form.hostDoctorName" placeholder="请输入主持人姓名" />
</el-form-item>
<el-form-item label="主持人职称">
<el-input v-model="form.hostTitle" placeholder="请输入主持人职称" />
</el-form-item>
<el-form-item label="参加人员">
<el-input v-model="form.participants" placeholder="请输入参加人员(逗号分隔)" />
</el-form-item>
<el-form-item label="讨论结论">
<el-input v-model="form.discussionConclusion" type="textarea" :rows="3" placeholder="请输入讨论结论" />
</el-form-item>
<el-form-item label="改进措施">
<el-input v-model="form.improvementMeasures" type="textarea" :rows="3" placeholder="请输入改进措施" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">
保存
</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
import { saveDeathDiscussion, listDeathDiscussions, getPendingDeadline } from '@/api/mrhomepage'
import { ElMessage } from 'element-plus'
const homepageId = ref('')
const loading = ref(false)
const deadlineLoading = ref(false)
const discussions = ref([])
const pendingDeadline = ref([])
const dialogVisible = ref(false)
const isEdit = ref(false)
const submitLoading = ref(false)
const formRef = ref(null)
const form = reactive({
id: null,
encounterId: null,
patientId: null,
patientName: '',
deathDate: null,
discussionDate: null,
hostDoctorName: '',
hostTitle: '',
participants: '',
discussionConclusion: '',
improvementMeasures: ''
})
const rules = {
encounterId: [{ required: true, message: '请输入病案首页ID', trigger: 'blur' }],
patientName: [{ required: true, message: '请输入患者姓名', trigger: 'blur' }],
patientId: [{ required: true, message: '请输入患者ID', trigger: 'blur' }],
deathDate: [{ required: true, message: '请选择死亡日期', trigger: 'change' }],
discussionDate: [{ required: true, message: '请选择讨论日期', trigger: 'change' }],
hostDoctorName: [{ required: true, message: '请输入主持人', trigger: 'blur' }]
}
const resetForm = () => {
form.id = null
form.encounterId = homepageId.value ? Number(homepageId.value) : null
form.patientId = null
form.patientName = ''
form.deathDate = null
form.discussionDate = null
form.hostDoctorName = ''
form.hostTitle = ''
form.participants = ''
form.discussionConclusion = ''
form.improvementMeasures = ''
}
const showAddDialog = () => {
isEdit.value = false
resetForm()
dialogVisible.value = true
}
const showEditDialog = (row) => {
isEdit.value = true
Object.assign(form, {
id: row.id,
encounterId: row.encounterId,
patientId: row.patientId,
patientName: row.patientName,
deathDate: row.deathDate,
discussionDate: row.discussionDate,
hostDoctorName: row.hostDoctorName,
hostTitle: row.hostTitle,
participants: row.participants,
discussionConclusion: row.discussionConclusion,
improvementMeasures: row.improvementMeasures
})
dialogVisible.value = true
}
const loadDiscussions = async () => {
if (!homepageId.value) {
ElMessage.warning('请输入病案首页ID')
return
}
loading.value = true
try {
discussions.value = await listDeathDiscussions(homepageId.value)
} catch (e) {
ElMessage.error('查询失败: ' + (e.message || '未知错误'))
} finally {
loading.value = false
}
}
const checkDeadline = async () => {
deadlineLoading.value = true
try {
pendingDeadline.value = await getPendingDeadline()
if (pendingDeadline.value.length === 0) {
ElMessage.success('暂无到期记录')
} else {
ElMessage.warning('发现 ' + pendingDeadline.value.length + ' 条到期记录')
}
} catch (e) {
ElMessage.error('检查失败: ' + (e.message || '未知错误'))
} finally {
deadlineLoading.value = false
}
}
const handleSubmit = async () => {
if (!formRef.value) return
await formRef.value.validate()
submitLoading.value = true
try {
await saveDeathDiscussion({ ...form })
ElMessage.success('保存成功')
dialogVisible.value = false
if (homepageId.value) {
loadDiscussions()
}
} catch (e) {
ElMessage.error('保存失败: ' + (e.message || '未知错误'))
} finally {
submitLoading.value = false
}
}
</script>
<style scoped>
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>