feat(emr): 优化病历修改留痕功能并移除医保模拟服务

- 新增分页查询修改留痕(含患者信息)功能,支持按患者、医生、操作人、病历类型筛选
- 在EmrRevisionController中移除权限校验注解,简化访问控制
- 重构病历修改留痕前端界面,采用树形结构展示病历与修订版本关系
- 添加表格列最小宽度限制和溢出省略显示,优化表格组件样式
- 更新医保配置地址从本地到云端服务器
- 移除医保模拟服务相关代码和数据库迁移文件
- 修复临床路径表缺少基础实体字段问题
This commit is contained in:
2026-06-23 15:45:06 +08:00
parent b53b6abc9a
commit 92708b386a
19 changed files with 559 additions and 319 deletions

View File

@@ -15,6 +15,21 @@ $vxe-row-height: 40px;
$vxe-header-height: 40px;
$vxe-radius: 4px;
// === 全局列宽限制:防止文字竖排 ===
.vxe-table {
// 表头和表体列最小宽度
.vxe-header--column,
.vxe-body--column {
min-width: 80px;
}
// 列内容不换行,超长显示省略号
.vxe-cell {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
// === 全局覆盖 ===
.vxe-table {
font-family: 'HarmonyOS Sans', 'Helvetica Neue', Helvetica, 'PingFang SC',

View File

@@ -12,6 +12,8 @@
max-height="650"
:data="ePrescribingDetailList"
border
:column-config="{ minWidth: 100 }"
show-overflow-tooltip
>
<vxe-column
title="处方号"

View File

@@ -15,6 +15,8 @@
max-height="650"
:data="prescriptionInfoList"
border
:column-config="{ minWidth: 100 }"
show-overflow-tooltip
>
<vxe-column
title="医保处方编号"
@@ -215,6 +217,8 @@
max-height="650"
:data="rxdrugdetailList"
border
:column-config="{ minWidth: 100 }"
show-overflow-tooltip
>
<vxe-column
title="医疗目录编码"
@@ -453,6 +457,8 @@
max-height="650"
:data="mdtrtinfoList"
border
:column-config="{ minWidth: 100 }"
show-overflow-tooltip
>
<vxe-column
title="定点医疗机构名称"
@@ -733,6 +739,8 @@
max-height="650"
:data="discinfoList"
border
:column-config="{ minWidth: 100 }"
show-overflow-tooltip
>
<vxe-column
title="诊断类别"

View File

@@ -1,5 +1,5 @@
import request from '@/utils/request'
export function getRevisionPage(p){return request({url:'/emr/revision/page',method:'get',params:p})}
export function getRevisionPage(p){return request({url:'/emr/revision/page-with-patient',method:'get',params:p})}
export function getRevisionList(emrId){return request({url:'/emr/revision/list/'+emrId,method:'get'})}
export function recordRevision(d){return request({url:'/emr/revision/record',method:'post',data:d})}
export function compareRevisions(id1,id2){return request({url:'/emr/revision/compare',method:'get',params:{revisionId1:id1,revisionId2:id2}})}

View File

@@ -3,82 +3,102 @@
<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.emrId"
placeholder="病历ID"
clearable
style="width:120px"
/>
<el-input
v-model="q.operatorName"
placeholder="操作人"
clearable
style="width:120px"
/>
<el-button
type="primary"
@click="loadData"
>
查询
</el-button>
</div>
<el-card shadow="never" style="margin-bottom:16px">
<el-form inline>
<el-form-item label="患者">
<el-input v-model="q.patientName" placeholder="患者姓名" clearable style="width:130px" />
</el-form-item>
<el-form-item label="医生">
<el-input v-model="q.doctorName" placeholder="医生姓名" clearable style="width:130px" />
</el-form-item>
<el-form-item label="操作人">
<el-input v-model="q.operatorName" placeholder="操作人" clearable style="width:120px" />
</el-form-item>
<el-form-item label="病历类型">
<el-select v-model="q.emrType" placeholder="全部" clearable style="width:130px">
<el-option label="入院记录" value="ADMISSION" />
<el-option label="首次病程" value="FIRST_COURSE" />
<el-option label="日常病程" value="DAILY_COURSE" />
<el-option label="出院记录" value="DISCHARGE" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery">搜索</el-button>
<el-button @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-table
:data="tableData"
v-loading="loading"
:data="groupedData"
row-key="emrId"
border
stripe
default-expand-all
:tree-props="{ children: 'revisions' }"
>
<el-table-column
prop="emrId"
label="病历ID"
width="100"
/>
<el-table-column
prop="revisionNumber"
label="版本"
width="70"
align="center"
>
<el-table-column prop="emrTitle" label="病历标题" min-width="200">
<template #default="{row}">
<el-tag size="small">
V{{ row.revisionNumber }}
</el-tag>
<template v-if="row.isGroup">
<span style="font-weight:bold">{{ row.emrTitle || '病历 #' + row.emrId }}</span>
<el-tag size="small" style="margin-left:8px">{{ row.emrType }}</el-tag>
</template>
<template v-else>
<span style="color:#909399">V{{ row.revisionNumber }}</span>
</template>
</template>
</el-table-column>
<el-table-column
prop="operatorName"
label="操作人"
width="100"
/>
<el-table-column
prop="operationType"
label="操作类型"
width="100"
/>
<el-table-column
prop="diffContent"
label="变更内容"
min-width="200"
show-overflow-tooltip
/>
<el-table-column
prop="createTime"
label="修改时间"
width="170"
/>
<el-table-column
label="操作"
width="100"
>
<el-table-column prop="patientName" label="患者" width="100">
<template #default="{row}">
<el-button
type="primary"
link
size="small"
@click="viewDetail(row)"
>
查看
</el-button>
<template v-if="row.isGroup">
{{ row.patientName || '-' }}
<span v-if="row.patientGender" style="color:#909399;margin-left:4px">({{ row.patientGender }})</span>
</template>
</template>
</el-table-column>
<el-table-column prop="doctorName" label="主治医生" width="100">
<template #default="{row}">
<template v-if="row.isGroup">{{ row.doctorName || '-' }}</template>
</template>
</el-table-column>
<el-table-column prop="departmentName" label="科室" width="100">
<template #default="{row}">
<template v-if="row.isGroup">{{ row.departmentName || '-' }}</template>
</template>
</el-table-column>
<el-table-column prop="encounterNo" label="就诊号" width="120">
<template #default="{row}">
<template v-if="row.isGroup">{{ row.encounterNo || '-' }}</template>
</template>
</el-table-column>
<el-table-column prop="operationType" label="操作类型" width="100">
<template #default="{row}">
<template v-if="!row.isGroup">
<el-tag :type="opTypeMap[row.operationType]?.type || 'info'" size="small">
{{ opTypeMap[row.operationType]?.label || row.operationType }}
</el-tag>
</template>
</template>
</el-table-column>
<el-table-column prop="operatorName" label="操作人" width="90">
<template #default="{row}">
<template v-if="!row.isGroup">{{ row.operatorName }}</template>
</template>
</el-table-column>
<el-table-column prop="diffContent" label="变更内容" min-width="200" show-overflow-tooltip>
<template #default="{row}">
<template v-if="!row.isGroup">{{ row.diffContent }}</template>
</template>
</el-table-column>
<el-table-column prop="createTime" label="时间" width="170">
<template #default="{row}">
<template v-if="!row.isGroup">{{ row.createTime }}</template>
</template>
</el-table-column>
<el-table-column label="操作" width="80">
<template #default="{row}">
<template v-if="!row.isGroup">
<el-button type="primary" link size="small" @click="viewDetail(row)">详情</el-button>
</template>
</template>
</el-table-column>
</el-table>
@@ -87,73 +107,105 @@
v-model:page-size="q.pageSize"
style="margin-top:12px;justify-content:flex-end"
:total="total"
layout="total,prev,pager,next"
layout="total, sizes, prev, pager, next"
@size-change="loadData"
@current-change="loadData"
/>
<el-dialog
v-model="detailVisible"
title="修订详情"
width="700px"
>
<el-descriptions
:column="2"
border
>
<el-descriptions-item label="版本">
V{{ detail.revisionNumber }}
</el-descriptions-item>
<el-descriptions-item label="操作人">
{{ detail.operatorName }}
</el-descriptions-item>
<el-descriptions-item label="操作类型">
{{ detail.operationType }}
</el-descriptions-item>
<el-descriptions-item label="时间">
{{ detail.createTime }}
</el-descriptions-item>
<el-dialog v-model="detailVisible" title="修订详情" width="700px">
<el-descriptions :column="2" border>
<el-descriptions-item label="版本">V{{ detail.revisionNumber }}</el-descriptions-item>
<el-descriptions-item label="操作人">{{ detail.operatorName }}</el-descriptions-item>
<el-descriptions-item label="操作类型">{{ detail.operationType }}</el-descriptions-item>
<el-descriptions-item label="时间">{{ detail.createTime }}</el-descriptions-item>
</el-descriptions>
<div style="margin-top:12px">
<div style="font-weight:bold;margin-bottom:8px">
变更内容:
</div>
<div style="font-weight:bold;margin-bottom:8px">变更内容:</div>
<pre style="background:#f5f7fa;padding:12px;border-radius:4px;max-height:300px;overflow:auto">{{ detail.diffContent }}</pre>
</div>
<div style="margin-top:12px">
<div style="font-weight:bold;margin-bottom:8px">
内容快照:
</div>
<div style="font-weight:bold;margin-bottom:8px">内容快照:</div>
<pre style="background:#f5f7fa;padding:12px;border-radius:4px;max-height:300px;overflow:auto">{{ detail.snapshotContent }}</pre>
</div>
</el-dialog>
</div>
</template>
<script setup>
import {ref,onMounted} from 'vue'
import {useRoute} from 'vue-router'
import {getRevisionPage} from './api'
import {ElMessage} from 'element-plus'
const route=useRoute()
const tableData=ref([]);const total=ref(0)
const q=ref({pageNo:1,pageSize: 10,emrId:null,operatorName:''})
const detailVisible=ref(false);const detail=ref({})
const loadData=async()=>{
try{
// 清理空参数
const params={pageNo:q.value.pageNo,pageSize:q.value.pageSize}
if(q.value.emrId) params.emrId=q.value.emrId
if(q.value.operatorName) params.operatorName=q.value.operatorName
const r=await getRevisionPage(params)
console.log('修订历史响应:',r)
tableData.value=r.data?.records||r.data||[]
total.value=r.data?.total||tableData.value.length
}catch(e){
console.error('加载失败:',e)
import { ref, computed, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import { getRevisionPage } from './api'
import { ElMessage } from 'element-plus'
const route = useRoute()
const loading = ref(false)
const rawData = ref([])
const total = ref(0)
const q = ref({ pageNo: 1, pageSize: 10, patientName: '', doctorName: '', operatorName: '', emrType: '', emrId: null })
const detailVisible = ref(false)
const detail = ref({})
const opTypeMap = {
CREATE: { label: '创建', type: 'success' },
EDIT: { label: '编辑', type: 'warning' },
APPROVE: { label: '审批', type: '' },
SIGN: { label: '签名', type: 'info' }
}
const groupedData = computed(() => {
const map = new Map()
for (const row of rawData.value) {
const key = row.emrId
if (!map.has(key)) {
map.set(key, {
emrId: key,
emrTitle: row.emrTitle,
emrType: row.emrType,
patientName: row.patientName,
patientGender: row.patientGender,
doctorName: row.doctorName,
departmentName: row.departmentName,
encounterNo: row.encounterNo,
isGroup: true,
revisions: []
})
}
map.get(key).revisions.push({ ...row, isGroup: false })
}
return Array.from(map.values())
})
const loadData = async () => {
loading.value = true
try {
const params = { pageNo: q.value.pageNo, pageSize: q.value.pageSize }
if (q.value.patientName) params.patientName = q.value.patientName
if (q.value.doctorName) params.doctorName = q.value.doctorName
if (q.value.operatorName) params.operatorName = q.value.operatorName
if (q.value.emrType) params.emrType = q.value.emrType
if (q.value.emrId) params.emrId = q.value.emrId
const r = await getRevisionPage(params)
rawData.value = r.data?.records || []
total.value = r.data?.total || 0
} catch {
ElMessage.error('加载失败')
} finally {
loading.value = false
}
}
const viewDetail=(row)=>{detail.value=row;detailVisible.value=true}
onMounted(()=>{
if(route.query.emrId){q.value.emrId=route.query.emrId}
const handleQuery = () => { q.value.pageNo = 1; loadData() }
const resetQuery = () => {
q.value.patientName = ''
q.value.doctorName = ''
q.value.operatorName = ''
q.value.emrType = ''
q.value.emrId = null
q.value.pageNo = 1
loadData()
}
const viewDetail = (row) => { detail.value = row; detailVisible.value = true }
onMounted(() => {
if (route.query.emrId) q.value.emrId = Number(route.query.emrId)
loadData()
})
</script>