* оптимизировал запросы выдачи пациентов, сохранения снапшотов

* доработал страницу отчета дежурного
* переделал "действия" над пациентом
* подключил виджеты на странице отчета дежурного
This commit is contained in:
brusnitsyn
2026-05-08 17:04:56 +09:00
parent 6cf1ffbb2b
commit 90e0d04dfd
17 changed files with 818 additions and 292 deletions

View File

@@ -1,9 +1,20 @@
<script setup>
import AppLayout from "../../Layouts/AppLayout.vue";
import {useReportStore} from "../../Stores/report.js";
import {h, onMounted, ref, watch} from "vue";
import {computed, h, onMounted, ref, watch} from "vue";
import {useAuthStore} from "../../Stores/auth.js";
import {NCard, NFlex, NNumberAnimation, NStatistic, NTabPane, NTabs, NTag} from "naive-ui";
import {
NFormItem,
NFlex,
NInputNumber,
NStatistic,
NTabPane,
NTabs,
NTag,
NNumberAnimation,
NSkeleton,
NRow, NCol, NDivider, NButton
} from "naive-ui";
import DatePickerQuery from "../../Components/DatePickerQuery.vue";
import AppPanel from "../../Components/AppPanel.vue";
import AppContainer from "../../Components/AppContainer.vue";
@@ -17,6 +28,8 @@ import ActionsColumn from "./Components/DataTableColumns/ActionsColumn.vue";
import OperationsColumn from "./Components/DataTableColumns/OperationsColumn.vue";
import ReportWidget from "./Components/ReportWidget.vue";
import OperationInfoModal from "./Components/OperationInfoModal.vue";
import {usePatientColumns} from "../../Composables/usePatientColumns.js";
import {router} from "@inertiajs/vue3";
const props = defineProps({
department: {
@@ -32,41 +45,8 @@ const props = defineProps({
default: []
},
patients: {
type: Object,
default: () => ({})
},
inDepartmentHistories: {
type: Object,
default: { data: [] }
},
plannedHistories: {
type: Object,
default: { data: [] }
},
emergencyHistories: {
type: Object,
default: { data: [] }
},
recipientHistories: {
type: Object,
default: { data: [] }
},
dischargedHistories: {
type: Object,
default: { data: [] }
},
deceasedHistories: {
type: Object,
default: { data: [] }
},
transferredHistories: {
type: Object,
default: { data: [] }
},
reanimationHistories: {
type: Object,
default: { data: [] }
type: Array,
default: () => ([])
},
dates: {
type: Array,
@@ -81,69 +61,50 @@ const loading = ref(false)
const operationsInModal = ref(null)
const showOperationsModal = ref(false)
const columns = [
{
title: 'ФИО',
key: 'full_name',
width: 280
},
{
title: 'Возраст',
key: 'birth_date',
render: (row) => formatDistanceStrict(new Date(row.birth_date), new Date(), { locale: ru }),
width: 75,
},
{
title: 'Д/р',
key: 'birth_date',
minWidth: 94,
maxWidth: 100,
width: 94,
resizable: false,
render: (row) => format(new Date(row.birth_date), 'dd.MM.yyyy')
},
{
title: 'Д/п',
key: 'latest_migration.ingoing_date',
minWidth: 134,
maxWidth: 144,
width: 134,
resizable: false,
render: (row) => format(new Date(row.latest_migration.ingoing_date), 'dd.MM.yyyy HH:mm')
},
{
title: 'Диагноз',
key: 'latest_migration.diagnosis_code',
width: 75,
resizable: false,
render: (row) => h(TooltipColumn, { triggerText: row.latest_migration.diagnosis_code, contentText: row.latest_migration.diagnosis_name })
},
{
title: 'Операции',
key: 'latest_migration.operations',
width: 140,
className: 'relative',
render: (row) => h(OperationsColumn, { operations: row.latest_migration.operations, onClick: (operations) => onShowOperationsModal(operations) })
},
{
title: '',
key: 'actions',
align: 'end',
render: (row) => {
return h(
ActionsColumn,
{
row: row,
isMis: true
}
)
const patientsByGroup = computed(() => {
const groups = {
urgent: [],
planned: [],
deceased: [],
in_department: [],
recipient: [],
discharged: [],
transferred: [],
};
for (const p of props.patients.data) {
// Группировка по срочности
if (p.patient_urgency === 'urgent') groups.urgent.push(p);
else if (p.patient_urgency === 'planned') groups.planned.push(p);
// Группировка по статусу (дублирование нужно, если один пациент может быть в двух таблицах)
if (groups.hasOwnProperty(p.patient_status)) {
groups[p.patient_status].push(p);
}
}
]
const onShowOperationsModal = (operations) => {
operationsInModal.value = operations
showOperationsModal.value = true
return groups;
})
const {
planOrEmergencyColumns, observableColumns, reanimationColumns, dischargedColumns,
deceasedColumns, transferredColumns,
} = usePatientColumns({
onAddObservable: (row) => {
console.log(row)
},
onShowOperationModal: (operations) => {
operationsInModal.value = operations
showOperationsModal.value = true
}
})
const submit = () => {
router.post('/duty/report/save', {}, {
onSuccess: () => {
alert('Сохранено')
}
})
}
const syncPageProps = () => reportStore.initializeFromPage(props)
@@ -173,24 +134,90 @@ watch(() => props, (newProps) => {
</NTag>
<DatePickerQuery :date="dates" :is-head-or-admin="true" class="text-lg!" />
</NFlex>
<NDivider dashed class="my-4! mt-3!" />
<NRow class="grow space-x-2">
<NCol :span="3">
<AppPanel no-padding>
<div class="flex flex-col items-center justify-center text-center py-2">
<NStatistic label="Коек">
<template #default>
<NSkeleton v-if="reportStore.isLoadReportInfo" round class="w-[70px]! mt-2 h-[29px]!" />
<NNumberAnimation v-else :from="0" :to="reportStore.reportInfo?.department?.beds" />
</template>
</NStatistic>
</div>
</AppPanel>
</NCol>
<NCol :span="3">
<AppPanel no-padding>
<div class="flex flex-col items-center justify-center text-center py-2">
<NStatistic label="Загруженность">
<template #default>
<NNumberAnimation :from="0" :to="reportStore.reportInfo?.department?.percentLoadedBeds" />
<span>%</span>
</template>
</NStatistic>
</div>
</AppPanel>
</NCol>
<NCol :span="3">
<AppPanel no-padding>
<div class="flex flex-col items-center justify-center text-center py-2">
<NStatistic label="Состоит">
<NNumberAnimation :from="0" :to="patientsByGroup.in_department.length + patientsByGroup.recipient.length" />
</NStatistic>
</div>
</AppPanel>
</NCol>
<NCol :span="3">
<AppPanel no-padding>
<div class="flex flex-col items-center justify-center text-center py-2">
<NStatistic label="Поступило">
<NNumberAnimation :from="0" :to="patientsByGroup.recipient.length" />
</NStatistic>
</div>
</AppPanel>
</NCol>
<NCol :span="3">
<AppPanel no-padding>
<div class="flex flex-col items-center justify-center text-center py-2">
<NStatistic label="Выбыло">
<NNumberAnimation :from="0" :to="patientsByGroup.deceased.length + patientsByGroup.discharged.length" />
</NStatistic>
</div>
</AppPanel>
</NCol>
</NRow>
</AppPanel>
<div class="grid grid-cols-[1fr_auto] gap-x-2">
<AppPanel>
<NFlex justify="space-between" align="center">
<NFlex align="center" :wrap="false">
<NFormItem label="Поступило" :show-feedback="false">
<NInputNumber :min="0" />
</NFormItem>
<NFormItem label="Выбыло" :show-feedback="false">
<NInputNumber :min="0" />
</NFormItem>
<NFormItem label="Состоит" :show-feedback="false">
<NInputNumber :min="0" />
</NFormItem>
<NFormItem label="Мед. персонал" :show-feedback="false">
<NInputNumber :min="0" />
</NFormItem>
</NFlex>
</AppPanel>
<NFlex :wrap="false" :size="8">
<ReportWidget :to="deceasedHistories.data.length">
<ReportWidget :to="patientsByGroup.deceased.length">
Умерло
</ReportWidget>
<ReportWidget :to="deceasedHistories.data.length">
<ReportWidget :to="patientsByGroup.deceased.length">
<NSpace vertical :size="0">
<div>Летальность</div>
<div>%</div>
</NSpace>
</ReportWidget>
<ReportWidget :to="deceasedHistories.data.length">
<ReportWidget :to="patientsByGroup.deceased.length">
<NSpace vertical :size="0">
<div>Операций</div>
<div>Э / П</div>
@@ -202,28 +229,28 @@ watch(() => props, (newProps) => {
<NTabs type="segment">
<NTabPane name="mis" tab="МИС">
<PatientTypeSection>
<PatientTypeSectionItem label="Планово" :counter="plannedHistories.data.length">
<PatientDataTable :data="plannedHistories.data" :columns="columns" />
<PatientTypeSectionItem label="Планово" :counter="patientsByGroup.planned.length">
<PatientDataTable :data="patientsByGroup.planned" :columns="planOrEmergencyColumns" />
</PatientTypeSectionItem>
<PatientTypeSectionItem label="Экстренно" :counter="emergencyHistories.data.length">
<PatientDataTable :data="emergencyHistories.data" :columns="columns" />
<PatientTypeSectionItem label="Экстренно" :counter="patientsByGroup.urgent.length">
<PatientDataTable :data="patientsByGroup.urgent" :columns="planOrEmergencyColumns" />
</PatientTypeSectionItem>
<PatientTypeSectionItem label="Находятся на контроле" :counter="recipientHistories.data.length">
<PatientDataTable :data="recipientHistories.data" :columns="columns" />
<PatientTypeSectionItem label="Находятся на контроле" :counter="patientsByGroup.recipient.length">
<PatientDataTable :data="patientsByGroup.recipient" :columns="observableColumns" />
</PatientTypeSectionItem>
<PatientTypeSectionItem label="Находятся в реанимации" :counter="reanimationHistories.data.length">
<PatientDataTable :data="reanimationHistories.data" :columns="columns" />
<PatientTypeSectionItem label="Находятся в реанимации" :counter="patientsByGroup.recipient.length">
<PatientDataTable :data="patientsByGroup.recipient" :columns="reanimationColumns" />
</PatientTypeSectionItem>
<PatientTypeSectionItem label="Выбывшие" :counter="dischargedHistories.data.length + deceasedHistories.data.length">
<PatientTypeSectionItem label="Выбывшие" :counter="patientsByGroup.discharged.length + patientsByGroup.deceased.length">
<NTabs type="segment" animated>
<NTabPane name="1" :tab="`Выписанные (${dischargedHistories.data.length})`">
<PatientDataTable :data="dischargedHistories.data" :columns="columns" />
<NTabPane name="1" :tab="`Выписанные (${patientsByGroup.discharged.length})`">
<PatientDataTable :data="patientsByGroup.discharged" :columns="dischargedColumns" />
</NTabPane>
<NTabPane name="2" :tab="`Умершие (${deceasedHistories.data.length})`">
<PatientDataTable :data="deceasedHistories.data" :columns="columns" />
<NTabPane name="2" :tab="`Умершие (${patientsByGroup.deceased.length})`">
<PatientDataTable :data="patientsByGroup.deceased" :columns="deceasedColumns" />
</NTabPane>
<NTabPane name="3" :tab="`Переведенные (${transferredHistories.data.length})`">
<PatientDataTable :data="transferredHistories.data" :columns="columns" />
<NTabPane name="3" :tab="`Переведенные (${patientsByGroup.transferred.length})`">
<PatientDataTable :data="patientsByGroup.transferred" :columns="transferredColumns" />
</NTabPane>
</NTabs>
</PatientTypeSectionItem>
@@ -231,25 +258,28 @@ watch(() => props, (newProps) => {
</NTabPane>
<NTabPane name="nurse" tab="Мед. сестра">
<PatientTypeSection>
<PatientTypeSectionItem label="Планово" :counter="plannedHistories.length">
<PatientDataTable :data="plannedHistories" :columns="columns" />
<PatientTypeSectionItem label="Планово" :counter="patientsByGroup.planned.length">
<PatientDataTable :data="patientsByGroup.planned" :columns="planOrEmergencyColumns" />
</PatientTypeSectionItem>
<PatientTypeSectionItem label="Экстренно" :counter="emergencyHistories.length">
<PatientDataTable :data="emergencyHistories" :columns="columns" />
<PatientTypeSectionItem label="Экстренно" :counter="patientsByGroup.urgent.length">
<PatientDataTable :data="patientsByGroup.urgent" :columns="planOrEmergencyColumns" />
</PatientTypeSectionItem>
<PatientTypeSectionItem label="Поступившие" :counter="recipientHistories.length">
<PatientDataTable :data="recipientHistories" :columns="columns" />
<PatientTypeSectionItem label="Находятся на контроле" :counter="patientsByGroup.recipient.length">
<PatientDataTable :data="patientsByGroup.recipient" :columns="observableColumns" />
</PatientTypeSectionItem>
<PatientTypeSectionItem label="Выбывшие" :counter="recipientHistories.length">
<PatientTypeSectionItem label="Находятся в реанимации" :counter="patientsByGroup.recipient.length">
<PatientDataTable :data="patientsByGroup.recipient" :columns="reanimationColumns" />
</PatientTypeSectionItem>
<PatientTypeSectionItem label="Выбывшие" :counter="patientsByGroup.discharged.length + patientsByGroup.deceased.length">
<NTabs type="segment" animated>
<NTabPane name="1" tab="Выписанные">
<PatientDataTable :data="recipientHistories" :columns="columns" />
<NTabPane name="1" :tab="`Выписанные (${patientsByGroup.discharged.length})`">
<PatientDataTable :data="patientsByGroup.discharged" :columns="dischargedColumns" />
</NTabPane>
<NTabPane name="2" tab="Умершие">
<PatientDataTable :data="recipientHistories" :columns="columns" />
<NTabPane name="2" :tab="`Умершие (${patientsByGroup.deceased.length})`">
<PatientDataTable :data="patientsByGroup.deceased" :columns="deceasedColumns" />
</NTabPane>
<NTabPane name="3" tab="Переведенные">
<PatientDataTable :data="recipientHistories" :columns="columns" />
<NTabPane name="3" :tab="`Переведенные (${patientsByGroup.transferred.length})`">
<PatientDataTable :data="patientsByGroup.transferred" :columns="transferredColumns" />
</NTabPane>
</NTabs>
</PatientTypeSectionItem>
@@ -257,6 +287,9 @@ watch(() => props, (newProps) => {
</NTabPane>
</NTabs>
</AppPanel>
<NButton secondary size="large" @click="submit" :loading="loading">
Сохранить отчет
</NButton>
</AppContainer>
</AppLayout>
<OperationInfoModal :operations="operationsInModal" v-model:show="showOperationsModal" />