* изменил таблицы в основном отчете
* изменил метод сохранения пациентов основного отчета
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="max-w-6xl mx-auto mt-6 mb-4 w-full flex flex-col gap-y-4">
|
||||
<div class="max-w-6xl mx-auto mt-6 mb-4 w-full flex flex-col gap-y-2">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -61,7 +61,7 @@ watch(() => [props.minH, props.maxH], ([minH, maxH]) => {
|
||||
|
||||
<template>
|
||||
<NFormItem :show-label="hasHeaderInOutside" :label="header" :show-feedback="hasFeedback" :feedback="feedback">
|
||||
<NCard :class="noPadding ? 'no-padding h-full' : 'h-full'">
|
||||
<NCard v-bind="$attrs" :class="noPadding ? 'no-padding h-full' : 'h-full'">
|
||||
<template v-if="!hasHeaderInOutside && header" #header>
|
||||
{{ header }}
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
<script setup>
|
||||
import {NSpace, NDropdown, NButton, NIcon} from 'naive-ui'
|
||||
import {TbExternalLink, TbEyePlus, TbClick} from "vue-icons-plus/tb"
|
||||
import {computed, h} from "vue";
|
||||
const props = defineProps({
|
||||
row: Object,
|
||||
isMis: Boolean,
|
||||
isAddObservable: Boolean
|
||||
})
|
||||
|
||||
const renderIcon = (icon) => {
|
||||
return () => h(
|
||||
NIcon,
|
||||
{},
|
||||
{
|
||||
default: () => h(icon)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const actions = computed(() => ([
|
||||
{
|
||||
label: 'Просмотреть в МИС',
|
||||
key: 'mis',
|
||||
icon: renderIcon(TbExternalLink),
|
||||
show: props.isMis
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
key: 'd1',
|
||||
show: props.isMis && props.isAddObservable
|
||||
},
|
||||
{
|
||||
label: 'Добавить в наблюдение',
|
||||
key: 'add-observable',
|
||||
icon: renderIcon(TbEyePlus),
|
||||
show: props.isAddObservable
|
||||
},
|
||||
]))
|
||||
|
||||
const onOpenMis = () => {
|
||||
const id = props.row.id
|
||||
window.open(`https://stationar.amurzdrav.ru/prod/statist/edit/card/${id}`, '_blank')
|
||||
}
|
||||
|
||||
const onSelectOption = (key, option) => {
|
||||
switch (key) {
|
||||
case 'mis': onOpenMis()
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NSpace size="small" justify="end">
|
||||
<NDropdown size="small" trigger="click" placement="bottom-end" :options="actions" @select="onSelectOption">
|
||||
<NButton secondary size="tiny">
|
||||
<template #icon>
|
||||
<TbClick />
|
||||
</template>
|
||||
Действия
|
||||
</NButton>
|
||||
</NDropdown>
|
||||
</NSpace>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,16 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
index: {
|
||||
type: [String, Number],
|
||||
default: null
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>{{index}}</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,24 @@
|
||||
<script setup>
|
||||
import { NTooltip } from 'naive-ui'
|
||||
import {computed} from "vue";
|
||||
const props = defineProps({
|
||||
operations: Array
|
||||
})
|
||||
|
||||
const firstOperation = computed(() => props.operations[0])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NTooltip v-if="operations.length" :arrow="false">
|
||||
<template #trigger>
|
||||
<div class="absolute inset-0 p-2 pt-2.5">
|
||||
{{ firstOperation?.code_service }}
|
||||
</div>
|
||||
</template>
|
||||
{{ firstOperation?.name_service }}
|
||||
</NTooltip>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,22 @@
|
||||
<script setup>
|
||||
import {NTooltip} from 'naive-ui'
|
||||
const props = defineProps({
|
||||
triggerText: String,
|
||||
contentText: String
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NTooltip trigger="hover" :arrow="false">
|
||||
<template #trigger>
|
||||
<div>
|
||||
{{ triggerText }}
|
||||
</div>
|
||||
</template>
|
||||
{{ contentText }}
|
||||
</NTooltip>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
86
resources/js/Pages/Report/Components/PatientDataTable.vue
Normal file
86
resources/js/Pages/Report/Components/PatientDataTable.vue
Normal file
@@ -0,0 +1,86 @@
|
||||
<script setup>
|
||||
import {NDataTable, NSpace, NInput, NButton, NFlex} from "naive-ui";
|
||||
import {TbSearch} from 'vue-icons-plus/tb'
|
||||
import {computed, h, ref} from "vue";
|
||||
import IndexColumn from "./DataTableColumns/IndexColumn.vue";
|
||||
|
||||
const props = defineProps({
|
||||
columns: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
data: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
loading: {
|
||||
type: Boolean
|
||||
},
|
||||
})
|
||||
|
||||
const patients = ref([...props.data])
|
||||
const tableColumns = computed(() => {
|
||||
const baseColumns = [...props.columns]
|
||||
|
||||
const numericColumn = {
|
||||
title: '',
|
||||
key: 'index',
|
||||
minWidth: 50,
|
||||
maxWidth: 60,
|
||||
width: 50,
|
||||
resizable: false,
|
||||
render: (row, i) => h(IndexColumn, { index: i + 1 })
|
||||
}
|
||||
|
||||
baseColumns.unshift(numericColumn)
|
||||
|
||||
return baseColumns
|
||||
})
|
||||
const searchArg = ref(null)
|
||||
|
||||
const findPatient = (arg) => {
|
||||
patients.value = patients.value.find(itm => itm.full_name === arg)
|
||||
}
|
||||
|
||||
const rowProps = (row) => {
|
||||
const style = []
|
||||
|
||||
if (row.admitted_today) {
|
||||
style.push('--n-merged-td-color: #047857')
|
||||
}
|
||||
|
||||
return {
|
||||
style: style,
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NSpace vertical>
|
||||
<NFlex :wrap="false">
|
||||
<NInput v-model:value="searchArg" placeholder="Поиск пациента" @input="value => findPatient(value)" />
|
||||
<NButton secondary @click="findPatient(searchArg)">
|
||||
<template #icon>
|
||||
<TbSearch />
|
||||
</template>
|
||||
Найти
|
||||
</NButton>
|
||||
</NFlex>
|
||||
<NDataTable :columns="tableColumns"
|
||||
:data="patients"
|
||||
table-layout="fixed"
|
||||
max-height="280"
|
||||
min-height="280"
|
||||
:loading="loading"
|
||||
size="small"
|
||||
:row-props="rowProps"
|
||||
/>
|
||||
</NSpace>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.n-data-table-th),
|
||||
:deep(.n-data-table-td) {
|
||||
font-size: var(--n-font-size);
|
||||
}
|
||||
</style>
|
||||
16
resources/js/Pages/Report/Components/PatientSection.vue
Normal file
16
resources/js/Pages/Report/Components/PatientSection.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<script setup>
|
||||
import { NCollapse } from 'naive-ui'
|
||||
import {ref} from "vue";
|
||||
|
||||
const openedCollapsible = ref()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NCollapse v-model:expanded-names="openedCollapsible">
|
||||
<slot />
|
||||
</NCollapse>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
27
resources/js/Pages/Report/Components/PatientSectionItem.vue
Normal file
27
resources/js/Pages/Report/Components/PatientSectionItem.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<script setup>
|
||||
import { NCollapseItem } from 'naive-ui'
|
||||
import {computed} from "vue";
|
||||
const props = defineProps({
|
||||
label: {
|
||||
type: String
|
||||
},
|
||||
counter: {
|
||||
type: [String, Number],
|
||||
default: null
|
||||
}
|
||||
})
|
||||
|
||||
const counterText = computed(() => props.counter ? `(${props.counter})` : '')
|
||||
|
||||
const header = computed(() => `${props.label} ${counterText.value}`)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NCollapseItem :title="header">
|
||||
<slot />
|
||||
</NCollapseItem>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
26
resources/js/Pages/Report/Components/ReportWidget.vue
Normal file
26
resources/js/Pages/Report/Components/ReportWidget.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<script setup>
|
||||
import AppPanel from "../../../Components/AppPanel.vue";
|
||||
import {NNumberAnimation, NStatistic} from "naive-ui";
|
||||
|
||||
const props = defineProps({
|
||||
to: Number,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AppPanel class="min-w-[120px] min-h-[100px] max-h-[102px] h-full"
|
||||
style="--n-padding-top: 0; --n-padding-bottom: 0; --n-padding-left: 8px; --n-padding-right: 8px;">
|
||||
<div class="w-full h-full flex flex items-center justify-center">
|
||||
<NStatistic class="text-center">
|
||||
<NNumberAnimation :from="0" :to="to" />
|
||||
<template #label>
|
||||
<slot />
|
||||
</template>
|
||||
</NStatistic>
|
||||
</div>
|
||||
</AppPanel>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,10 +1,21 @@
|
||||
<script setup>
|
||||
import AppLayout from "../../Layouts/AppLayout.vue";
|
||||
import ReportForm from "./Components/ReportForm.vue";
|
||||
import {useReportStore} from "../../Stores/report.js";
|
||||
import {computed, onMounted, watch} from "vue";
|
||||
import {h, onMounted, ref, watch} from "vue";
|
||||
import {useAuthStore} from "../../Stores/auth.js";
|
||||
import StatisticRecipientPlanOfYear from "../../Layouts/Components/Statistic/StatisticRecipientPlanOfYear.vue";
|
||||
import {NCard, NFlex, NNumberAnimation, NStatistic, NTabPane, NTabs, NTag} from "naive-ui";
|
||||
import DatePickerQuery from "../../Components/DatePickerQuery.vue";
|
||||
import AppPanel from "../../Components/AppPanel.vue";
|
||||
import AppContainer from "../../Components/AppContainer.vue";
|
||||
import PatientTypeSection from "./Components/PatientSection.vue";
|
||||
import PatientTypeSectionItem from "./Components/PatientSectionItem.vue";
|
||||
import PatientDataTable from "./Components/PatientDataTable.vue";
|
||||
import {format, formatDistanceStrict} from 'date-fns';
|
||||
import { ru } from 'date-fns/locale';
|
||||
import TooltipColumn from "./Components/DataTableColumns/TooltipColumn.vue";
|
||||
import ActionsColumn from "./Components/DataTableColumns/ActionsColumn.vue";
|
||||
import OperationsColumn from "./Components/DataTableColumns/OperationsColumn.vue";
|
||||
import ReportWidget from "./Components/ReportWidget.vue";
|
||||
|
||||
const props = defineProps({
|
||||
department: {
|
||||
@@ -19,46 +30,221 @@ const props = defineProps({
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
dates: {
|
||||
type: Object,
|
||||
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: [] }
|
||||
},
|
||||
dates: {
|
||||
type: Array,
|
||||
default: []
|
||||
}
|
||||
})
|
||||
|
||||
const reportStore = useReportStore()
|
||||
const authStore = useAuthStore()
|
||||
const userDepartment = authStore.userDepartment
|
||||
const loading = 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 })
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
key: 'actions',
|
||||
align: 'end',
|
||||
render: (row) => {
|
||||
return h(
|
||||
ActionsColumn,
|
||||
{
|
||||
row: row,
|
||||
isMis: true
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const syncPageProps = () => reportStore.initializeFromPage(props)
|
||||
|
||||
onMounted(syncPageProps)
|
||||
|
||||
// reportStore.getReportInfo()
|
||||
|
||||
const mode = computed(() => {
|
||||
if (authStore.isHeadOfDepartment)
|
||||
return 'readonly'
|
||||
return 'fillable'
|
||||
})
|
||||
|
||||
watch(() => props, (newProps) => {
|
||||
reportStore.initializeFromPage(newProps)
|
||||
}, {
|
||||
deep: true,
|
||||
immediate: true
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AppLayout>
|
||||
<template #headerSuffix>
|
||||
<StatisticRecipientPlanOfYear :plan="reportStore.reportInfo.department.recipientPlanOfYear" :progress="reportStore.reportInfo.department.progressPlanOfYear" />
|
||||
</template>
|
||||
<ReportForm :mode />
|
||||
<!-- <template #headerSuffix>-->
|
||||
<!-- <StatisticRecipientPlanOfYear :plan="reportStore.reportInfo.department.recipientPlanOfYear" :progress="reportStore.reportInfo.department.progressPlanOfYear" />-->
|
||||
<!-- </template>-->
|
||||
<!-- <ReportForm :mode />-->
|
||||
|
||||
<AppContainer>
|
||||
<AppPanel>
|
||||
<NFlex justify="space-between" align="center">
|
||||
<NTag type="info" :bordered="false">
|
||||
{{ userDepartment.name_full }}
|
||||
</NTag>
|
||||
<DatePickerQuery :date="dates" :is-head-or-admin="true" class="text-lg!" />
|
||||
</NFlex>
|
||||
</AppPanel>
|
||||
<div class="grid grid-cols-[1fr_auto] gap-x-2">
|
||||
<AppPanel>
|
||||
<NFlex justify="space-between" align="center">
|
||||
|
||||
</NFlex>
|
||||
</AppPanel>
|
||||
<NFlex :wrap="false" :size="8">
|
||||
<ReportWidget :to="deceasedHistories.data.length">
|
||||
Умерло
|
||||
</ReportWidget>
|
||||
<ReportWidget :to="deceasedHistories.data.length">
|
||||
<NSpace vertical :size="0">
|
||||
<div>Летальность</div>
|
||||
<div>%</div>
|
||||
</NSpace>
|
||||
</ReportWidget>
|
||||
<ReportWidget :to="deceasedHistories.data.length">
|
||||
<NSpace vertical :size="0">
|
||||
<div>Операций</div>
|
||||
<div>Э / П</div>
|
||||
</NSpace>
|
||||
</ReportWidget>
|
||||
</NFlex>
|
||||
</div>
|
||||
<AppPanel>
|
||||
<NTabs type="segment">
|
||||
<NTabPane name="mis" tab="МИС">
|
||||
<PatientTypeSection>
|
||||
<PatientTypeSectionItem label="Планово" :counter="plannedHistories.data.length">
|
||||
<PatientDataTable :data="plannedHistories.data" :columns="columns" />
|
||||
</PatientTypeSectionItem>
|
||||
<PatientTypeSectionItem label="Экстренно" :counter="emergencyHistories.data.length">
|
||||
<PatientDataTable :data="emergencyHistories.data" :columns="columns" />
|
||||
</PatientTypeSectionItem>
|
||||
<PatientTypeSectionItem label="Находятся на контроле" :counter="recipientHistories.data.length">
|
||||
<PatientDataTable :data="recipientHistories.data" :columns="columns" />
|
||||
</PatientTypeSectionItem>
|
||||
<PatientTypeSectionItem label="Находятся в реанимации" :counter="recipientHistories.data.length">
|
||||
<PatientDataTable :data="recipientHistories.data" :columns="columns" />
|
||||
</PatientTypeSectionItem>
|
||||
<PatientTypeSectionItem label="Выбывшие" :counter="dischargedHistories.data.length + deceasedHistories.data.length">
|
||||
<NTabs type="segment" animated>
|
||||
<NTabPane name="1" :tab="`Выписанные (${dischargedHistories.data.length})`">
|
||||
<PatientDataTable :data="dischargedHistories.data" :columns="columns" />
|
||||
</NTabPane>
|
||||
<NTabPane name="2" :tab="`Умершие (${deceasedHistories.data.length})`">
|
||||
<PatientDataTable :data="deceasedHistories.data" :columns="columns" />
|
||||
</NTabPane>
|
||||
<NTabPane name="3" :tab="`Переведенные (${transferredHistories.data.length})`">
|
||||
<PatientDataTable :data="transferredHistories.data" :columns="columns" />
|
||||
</NTabPane>
|
||||
</NTabs>
|
||||
</PatientTypeSectionItem>
|
||||
</PatientTypeSection>
|
||||
</NTabPane>
|
||||
<NTabPane name="nurse" tab="Мед. сестра">
|
||||
<PatientTypeSection>
|
||||
<PatientTypeSectionItem label="Планово" :counter="plannedHistories.length">
|
||||
<PatientDataTable :data="plannedHistories" :columns="columns" />
|
||||
</PatientTypeSectionItem>
|
||||
<PatientTypeSectionItem label="Экстренно" :counter="emergencyHistories.length">
|
||||
<PatientDataTable :data="emergencyHistories" :columns="columns" />
|
||||
</PatientTypeSectionItem>
|
||||
<PatientTypeSectionItem label="Поступившие" :counter="recipientHistories.length">
|
||||
<PatientDataTable :data="recipientHistories" :columns="columns" />
|
||||
</PatientTypeSectionItem>
|
||||
<PatientTypeSectionItem label="Выбывшие" :counter="recipientHistories.length">
|
||||
<NTabs type="segment" animated>
|
||||
<NTabPane name="1" tab="Выписанные">
|
||||
<PatientDataTable :data="recipientHistories" :columns="columns" />
|
||||
</NTabPane>
|
||||
<NTabPane name="2" tab="Умершие">
|
||||
<PatientDataTable :data="recipientHistories" :columns="columns" />
|
||||
</NTabPane>
|
||||
<NTabPane name="3" tab="Переведенные">
|
||||
<PatientDataTable :data="recipientHistories" :columns="columns" />
|
||||
</NTabPane>
|
||||
</NTabs>
|
||||
</PatientTypeSectionItem>
|
||||
</PatientTypeSection>
|
||||
</NTabPane>
|
||||
</NTabs>
|
||||
</AppPanel>
|
||||
</AppContainer>
|
||||
</AppLayout>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user