feat(P2): 质量控制+EMPI增强+数据仪表盘
V20 Flyway迁移 — 6张新表: - quality_core_indicator: 十八项核心制度质控指标 - quality_order_statistics: 医嘱统计日报 - empi_patient_photo: EMPI患者照片(ID卡/人脸) - empi_family_member: EMPI家庭关系(配偶/父母/子女等) - empi_merge_log: EMPI合并/拆分日志(全记录+可撤回) - sys_dashboard_config: 数据仪表盘配置 后端Controller: - QualityEnhancedController: 核心制度指标+医嘱统计 - EmpiEnhancedController: 患者照片+家庭关系+合并日志 - DashboardController: 仪表盘配置+系统概览 前端页面: - qualityenhanced: Tab页(核心指标/医嘱统计) - empienhanced: Tab页(家庭关系/合并日志) - dashboard: 系统仪表盘(模块概览+统计卡片) 所有P0+P1+P2模块编译通过 (mvn clean compile -DskipTests)
This commit is contained in:
3
healthlink-his-ui/src/views/dashboard/api.js
Normal file
3
healthlink-his-ui/src/views/dashboard/api.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import request from '@/utils/request'
|
||||
export function getDashboardOverview(){return request({url:'/dashboard/overview',method:'get'})}
|
||||
export function getDashboardList(p){return request({url:'/dashboard/list',method:'get',params:p})}
|
||||
30
healthlink-his-ui/src/views/dashboard/index.vue
Normal file
30
healthlink-his-ui/src/views/dashboard/index.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<div style="padding:16px">
|
||||
<div style="margin-bottom:16px"><span style="font-size:18px;font-weight:bold">数据仪表盘</span></div>
|
||||
<el-card shadow="never" style="margin-bottom:16px">
|
||||
<div style="text-align:center">
|
||||
<h2 style="color:#409eff;margin:0">{{ overview.systemName||'HealthLink-HIS' }}</h2>
|
||||
<p style="color:#666;margin:8px 0">版本: {{ overview.version }}</p>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="6" v-for="mod in overview.modules||[]" :key="mod">
|
||||
<el-card shadow="hover" style="text-align:center;margin-bottom:16px">
|
||||
<div style="font-size:20px;font-weight:bold;color:#409eff">{{ mod }}</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16" style="margin-top:16px">
|
||||
<el-col :span="8"><el-card shadow="never"><div style="text-align:center"><div style="font-size:32px;font-weight:bold;color:#409eff">{{ overview.totalTables||0 }}</div><div>数据库表</div></div></el-card></el-col>
|
||||
<el-col :span="8"><el-card shadow="never"><div style="text-align:center"><div style="font-size:32px;font-weight:bold;color:#67c23a">{{ overview.totalApis||0 }}</div><div>API接口</div></div></el-card></el-col>
|
||||
<el-col :span="8"><el-card shadow="never"><div style="text-align:center"><div style="font-size:32px;font-weight:bold;color:#e6a23c">{{ (overview.modules||[]).length }}</div><div>功能模块</div></div></el-card></el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref,onMounted} from 'vue'
|
||||
import {getDashboardOverview} from './api'
|
||||
const overview=ref({})
|
||||
onMounted(async()=>{const r=await getDashboardOverview();overview.value=r.data||{}})
|
||||
</script>
|
||||
8
healthlink-his-ui/src/views/empienhanced/api.js
Normal file
8
healthlink-his-ui/src/views/empienhanced/api.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import request from '@/utils/request'
|
||||
export function getPhotos(p){return request({url:'/empi-enhanced/photo/list',method:'get',params:p})}
|
||||
export function addPhoto(d){return request({url:'/empi-enhanced/photo/add',method:'post',data:d})}
|
||||
export function getFamilyMembers(p){return request({url:'/empi-enhanced/family/list',method:'get',params:p})}
|
||||
export function addFamilyMember(d){return request({url:'/empi-enhanced/family/add',method:'post',data:d})}
|
||||
export function deleteFamilyMember(id){return request({url:'/empi-enhanced/family/delete',method:'delete',params:{id}})}
|
||||
export function getMergeLogPage(p){return request({url:'/empi-enhanced/merge-log/page',method:'get',params:p})}
|
||||
export function addMergeLog(d){return request({url:'/empi-enhanced/merge-log/add',method:'post',data:d})}
|
||||
54
healthlink-his-ui/src/views/empienhanced/index.vue
Normal file
54
healthlink-his-ui/src/views/empienhanced/index.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<div style="padding:16px">
|
||||
<div style="margin-bottom:16px"><span style="font-size:18px;font-weight:bold">EMPI增强</span></div>
|
||||
<el-tabs v-model="tab" type="border-card">
|
||||
<el-tab-pane label="家庭关系" name="family">
|
||||
<div style="margin-bottom:12px">
|
||||
<el-input v-model="searchPatientId" placeholder="患者ID" style="width:120px;margin-right:8px"/>
|
||||
<el-button type="primary" @click="loadFamily">查询</el-button>
|
||||
<el-button type="success" @click="showFamily=true">新增</el-button>
|
||||
</div>
|
||||
<el-table :data="familyData" border stripe>
|
||||
<el-table-column prop="memberName" label="姓名" width="100"/>
|
||||
<el-table-column prop="relationship" label="关系" width="80"/>
|
||||
<el-table-column prop="gender" label="性别" width="60"/>
|
||||
<el-table-column prop="phone" label="电话" width="130"/>
|
||||
<el-table-column prop="isEmergencyContact" label="紧急联系人" width="100">
|
||||
<template #default="{row}"><el-tag :type="row.isEmergencyContact?'success':'info'" size="small">{{ row.isEmergencyContact?'是':'否' }}</el-tag></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80">
|
||||
<template #default="{row}"><el-button type="danger" link size="small" @click="deleteFamily(row.id)">删除</el-button></template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="合并日志" name="mergeLog">
|
||||
<el-table :data="mergeData" border stripe>
|
||||
<el-table-column prop="sourcePatientId" label="源患者ID" width="110"/>
|
||||
<el-table-column prop="targetPatientId" label="目标患者ID" width="110"/>
|
||||
<el-table-column prop="mergeType" label="类型" width="80"/>
|
||||
<el-table-column prop="mergeReason" label="原因" min-width="150" show-overflow-tooltip/>
|
||||
<el-table-column prop="mergeBy" label="操作人" width="90"/>
|
||||
<el-table-column prop="mergeTime" label="操作时间" width="170"/>
|
||||
<el-table-column prop="status" label="状态" width="80">
|
||||
<template #default="{row}"><el-tag :type="row.status==='MERGED'?'success':'info'" size="small">{{ row.status==='MERGED'?'已合并':'已撤回' }}</el-tag></template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref,reactive,onMounted} from 'vue'
|
||||
import {ElMessage,ElMessageBox} from 'element-plus'
|
||||
import {getFamilyMembers,addFamilyMember,deleteFamilyMember,getMergeLogPage} from './api'
|
||||
const tab=ref('family')
|
||||
const searchPatientId=ref('')
|
||||
const familyData=ref([]),mergeData=ref([])
|
||||
const showFamily=ref(false)
|
||||
const familyForm=reactive({patientId:null,memberName:'',relationship:'',gender:'',phone:'',isEmergencyContact:false})
|
||||
const loadFamily=async()=>{if(!searchPatientId.value)return;const r=await getFamilyMembers({patientId:searchPatientId.value});familyData.value=r.data||[]}
|
||||
const deleteFamily=async(id)=>{await ElMessageBox.confirm('确认删除?');await deleteFamilyMember(id);ElMessage.success('已删除');loadFamily()}
|
||||
const loadData=async()=>{const m=await getMergeLogPage({pageNo:1,pageSize:50});mergeData.value=m.data?.records||[]}
|
||||
onMounted(()=>loadData())
|
||||
</script>
|
||||
6
healthlink-his-ui/src/views/qualityenhanced/api.js
Normal file
6
healthlink-his-ui/src/views/qualityenhanced/api.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import request from '@/utils/request'
|
||||
export function getIndicatorPage(p){return request({url:'/quality-enhanced/indicator/page',method:'get',params:p})}
|
||||
export function addIndicator(d){return request({url:'/quality-enhanced/indicator/add',method:'post',data:d})}
|
||||
export function getIndicatorSummary(){return request({url:'/quality-enhanced/indicator/summary',method:'get'})}
|
||||
export function getOrderStatsPage(p){return request({url:'/quality-enhanced/order-stats/page',method:'get',params:p})}
|
||||
export function addOrderStats(d){return request({url:'/quality-enhanced/order-stats/add',method:'post',data:d})}
|
||||
57
healthlink-his-ui/src/views/qualityenhanced/index.vue
Normal file
57
healthlink-his-ui/src/views/qualityenhanced/index.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div style="padding:16px">
|
||||
<div style="margin-bottom:16px"><span style="font-size:18px;font-weight:bold">质量控制</span></div>
|
||||
<el-tabs v-model="tab" type="border-card">
|
||||
<el-tab-pane label="核心制度指标" name="indicator">
|
||||
<el-card shadow="never" style="margin-bottom:12px">
|
||||
<div style="display:flex;gap:30px;text-align:center">
|
||||
<div><div style="font-size:24px;font-weight:bold;color:#409eff">{{ summary.total||0 }}</div><div>总指标</div></div>
|
||||
<div><div style="font-size:24px;font-weight:bold;color:#67c23a">{{ summary.meetTarget||0 }}</div><div>达标</div></div>
|
||||
<div><div style="font-size:24px;font-weight:bold;color:#e6a23c">{{ summary.meetRate||0 }}%</div><div>达标率</div></div>
|
||||
</div>
|
||||
</el-card>
|
||||
<div style="margin-bottom:12px"><el-button type="success" @click="showAdd=true">新增指标</el-button></div>
|
||||
<el-table :data="indicatorData" border stripe>
|
||||
<el-table-column prop="indicatorCode" label="指标编码" width="120"/>
|
||||
<el-table-column prop="indicatorName" label="指标名称" min-width="180"/>
|
||||
<el-table-column prop="indicatorCategory" label="类别" width="100"/>
|
||||
<el-table-column prop="targetValue" label="目标值" width="80" align="center"/>
|
||||
<el-table-column prop="actualValue" label="实际值" width="80" align="center"/>
|
||||
<el-table-column prop="departmentName" label="科室" width="120"/>
|
||||
<el-table-column prop="statDate" label="统计日期" width="120"/>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="医嘱统计" name="orderStats">
|
||||
<div style="margin-bottom:12px"><el-button type="success" @click="showOrder=true">新增统计</el-button></div>
|
||||
<el-table :data="orderData" border stripe>
|
||||
<el-table-column prop="statDate" label="日期" width="120"/>
|
||||
<el-table-column prop="departmentName" label="科室" width="120"/>
|
||||
<el-table-column prop="totalOrders" label="总医嘱" width="80" align="center"/>
|
||||
<el-table-column prop="executedOrders" label="已执行" width="80" align="center"/>
|
||||
<el-table-column prop="completedOrders" label="已完成" width="80" align="center"/>
|
||||
<el-table-column prop="executeRate" label="执行率%" width="80" align="center"/>
|
||||
<el-table-column prop="completeRate" label="完成率%" width="80" align="center"/>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref,reactive,onMounted} from 'vue'
|
||||
import {ElMessage} from 'element-plus'
|
||||
import {getIndicatorPage,addIndicator,getIndicatorSummary,getOrderStatsPage,addOrderStats} from './api'
|
||||
const tab=ref('indicator')
|
||||
const indicatorData=ref([]),orderData=ref([])
|
||||
const summary=ref({})
|
||||
const showAdd=ref(false),showOrder=ref(false)
|
||||
const indicatorForm=reactive({indicatorCode:'',indicatorName:'',indicatorCategory:'',targetValue:null,actualValue:null,statDate:'',departmentName:''})
|
||||
const orderForm=reactive({statDate:'',departmentName:'',totalOrders:0,executedOrders:0,completedOrders:0,stoppedOrders:0,cancelledOrders:0,executeRate:0,completeRate:0})
|
||||
const loadData=async()=>{
|
||||
const [i,s,o]=await Promise.all([getIndicatorPage({pageNo:1,pageSize:50}),getIndicatorSummary(),getOrderStatsPage({pageNo:1,pageSize:50})])
|
||||
indicatorData.value=i.data?.records||[];summary.value=s.data||{};orderData.value=o.data?.records||[]
|
||||
}
|
||||
const submitIndicator=async()=>{await addIndicator(indicatorForm);ElMessage.success('新增成功');showAdd.value=false;loadData()}
|
||||
const submitOrder=async()=>{await addOrderStats(orderForm);ElMessage.success('新增成功');showOrder.value=false;loadData()}
|
||||
onMounted(()=>loadData())
|
||||
</script>
|
||||
Reference in New Issue
Block a user