Files
onboard/resources/js/Pages/Statistic/Index.vue
brusnitsyn 52a80ccd3b nothing
2026-02-20 17:28:16 +09:00

280 lines
8.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
import {NDataTable, NFlex, NText, NDatePicker, NBadge, NIcon, NPopover, NTag, NSpace} from 'naive-ui'
import AppLayout from "../../Layouts/AppLayout.vue";
import {h, ref} from "vue";
import DatePickerQuery from "../../Components/DatePickerQuery.vue";
import {Link, usePage} from "@inertiajs/vue3";
import {TbAlertCircle, TbEye} from "vue-icons-plus/tb";
import ModalUnwantedEvents from "./Components/ModalUnwantedEvents.vue";
import ModalObservablePatients from "./Components/ModalObservablePatients.vue";
const props = defineProps({
data: {
type: Object,
default: []
},
isHeadOrAdmin: {
type: Boolean
},
date: {
type: [Number, Array]
},
isOneDay: {
type: Boolean
}
})
const columns = ref([
{
title: 'Отделение',
key: 'department',
width: 240,
titleAlign: 'center',
colSpan: (row) => row.colspan,
render(row) {
if (row.isGroupHeader) {
return h(NFlex, {
align: "center",
justify: "center"
}, h(NText, { style: 'font-weight: 600;' }, row.groupName))
}
if (row.isTotalRow) {
return row.department
}
// Получаем текущие query параметры
const { url } = usePage()
const currentUrl = new URL(url, window.location.origin)
const searchParams = currentUrl.searchParams
// Берем startAt и endAt из текущего URL
const propsStartAt = Array.isArray(props.date) ? props.date[0] : props.date
const propsEndAt = Array.isArray(props.date) ? props.date[1] : props.date
const startAt = searchParams.get('startAt') ?? propsStartAt
const endAt = searchParams.get('endAt') ?? propsEndAt
const linkData = {}
if (startAt)
linkData.startAt = startAt
if (endAt)
linkData.endAt = endAt
return h(NFlex, {align: 'center', justify: 'start'}, [
h(NBadge, {
dot: true,
type: row.isReportToday ? 'success' : 'error'
}),
h(Link, {
href: `/report`,
data: linkData,
class: 'underline decoration-dashed'
}, row.department),
h(NSpace, {align: 'center', size: 'small'}, [
h(NPopover, {
trigger: 'hover',
}, {
trigger: h(NTag, {
round: true,
size: 'small',
bordered: false,
class: 'cursor-pointer!',
onClick: () => onShowUnwantedEventsModal(row.department_id)
}, {
icon: h(NIcon, { }, {
default: () => h(TbAlertCircle)
}),
default: row.countUnwanted
}),
default: 'Нежелательные события'
}),
h(NPopover, {
trigger: 'hover',
}, {
trigger: h(NTag, {
round: true,
size: 'small',
bordered: false,
class: 'cursor-pointer!',
onClick: () => onShowObservablePatientsModal(row.department_id)
}, {
icon: h(NIcon, { }, {
default: () => h(TbEye)
}),
default: row.countObservable
}),
default: 'Пациенты на контроле'
}),
])
])
}
},
{
title: 'Кол-во коек',
key: 'beds',
width: 60,
titleAlign: 'center',
align: 'center',
},
{
title: 'Поступило',
key: 'recipients',
titleAlign: 'center',
children: [
{
title: 'Всего',
key: 'recipients.all',
width: 60,
titleAlign: 'center',
align: 'center',
},
{
title: 'План',
key: 'recipients.plan',
width: 60,
titleAlign: 'center',
align: 'center',
},
{
title: 'Экстр',
key: 'recipients.emergency',
width: 60,
titleAlign: 'center',
align: 'center',
},
{
title: 'Перевод',
key: 'recipients.transferred',
width: 84,
titleAlign: 'center',
align: 'center',
},
]
},
{
title: 'Выбыло',
key: 'outcome',
width: 84,
titleAlign: 'center',
align: 'center',
},
{
title: 'Состоит',
key: 'consist',
width: 84,
titleAlign: 'center',
align: 'center',
},
{
title: 'Ср. койко-день',
key: 'averageBedDays',
width: 44,
titleAlign: 'center',
align: 'center',
},
{
title: '% загруженности',
key: 'percentLoadedBeds',
width: 84,
titleAlign: 'center',
align: 'center',
},
{
title: 'Операции',
key: 'surgical',
titleAlign: 'center',
children: [
{
title: 'Э',
key: 'surgical.emergency',
width: 60,
titleAlign: 'center',
align: 'center',
},
{
title: 'П',
key: 'surgical.plan',
width: 60,
titleAlign: 'center',
align: 'center',
},
]
},
{
title: 'Умерло',
key: 'deceased',
width: 84,
titleAlign: 'center',
align: 'center',
},
{
title: 'Мед. персонал',
key: 'countStaff',
width: 84,
titleAlign: 'center',
align: 'center',
},
])
const currentDepartmentId = ref(null)
const showUnwantedEventsModal = ref(false)
const showObservablePatientsModal = ref(false)
const onShowUnwantedEventsModal = (departmentId) => {
currentDepartmentId.value = departmentId
showUnwantedEventsModal.value = true
}
const onShowObservablePatientsModal = (departmentId) => {
currentDepartmentId.value = departmentId
showObservablePatientsModal.value = true
}
const rowProps = (row) => {
if (row.isGroupHeader) return {
style: `--n-merged-td-color: var(--n-merged-th-color)`
}
if (row.isTotalRow) return {
style: `--n-merged-td-color: var(--n-merged-th-color);`
}
}
const rowClassName = (row) => {
if (row.isTotalRow)
return `total-row`
}
</script>
<template>
<AppLayout>
<template #headerExtra>
<DatePickerQuery :is-head-or-admin="isHeadOrAdmin" :date="date" :is-one-day="isOneDay" />
</template>
<NDataTable :columns="columns"
:data="data"
size="small"
:single-line="false"
striped
min-height="calc(100vh - 48px - 70px)"
max-height="calc(100vh - 48px - 70px)"
:row-props="rowProps"
:row-class-name="rowClassName"
>
</NDataTable>
<ModalUnwantedEvents v-model:open="showUnwantedEventsModal" :start-at="date[0]" :end-at="date[1]" :department-id="currentDepartmentId" />
<ModalObservablePatients v-model:open="showObservablePatientsModal" :start-at="date[0]" :end-at="date[1]" :department-id="currentDepartmentId" />
</AppLayout>
</template>
<style scoped>
:deep(.n-data-table-th),
:deep(.n-data-table-td) {
font-size: var(--n-font-size);
}
:deep(.total-row td) {
--n-td-text-color: var(--n-th-icon-color-active);
font-weight: 500;
}
</style>