nothing
This commit is contained in:
13
resources/js/Components/AppContainer.vue
Normal file
13
resources/js/Components/AppContainer.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="max-w-6xl mx-auto mt-6 mb-4 w-full">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
65
resources/js/Components/AppPanel.vue
Normal file
65
resources/js/Components/AppPanel.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
<script setup>
|
||||
import { NFormItem, NCard, NScrollbar } from 'naive-ui'
|
||||
import {computed, ref, watch} from "vue";
|
||||
const props = defineProps({
|
||||
header: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
feedback: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
minH: {
|
||||
type: [String, Number],
|
||||
default: null
|
||||
},
|
||||
maxH: {
|
||||
type: [String, Number],
|
||||
default: null
|
||||
},
|
||||
})
|
||||
|
||||
const hasHeader = computed(() => props.header.trim().length > 0)
|
||||
const hasFeedback = computed(() => props.feedback.trim().length > 0)
|
||||
const hasMinH = computed(() => props.minH.trim().length > 0 || Number.isInteger(props.minH))
|
||||
const hasMaxH = computed(() => props.maxH.trim().length > 0 || Number.isInteger(props.maxH))
|
||||
|
||||
const styles = ref([])
|
||||
|
||||
watch(() => [props.minH, props.maxH], ([minH, maxH]) => {
|
||||
const sizeStyles = []
|
||||
|
||||
if (minH === null) return
|
||||
if (minH.trim().length > 0) {
|
||||
sizeStyles.push(`min-height: ${minH};`)
|
||||
} else if (Number.isInteger(minH)) {
|
||||
sizeStyles.push(`min-height: ${minH}px;`)
|
||||
}
|
||||
|
||||
if (maxH === null) return
|
||||
if (maxH.trim().length > 0) {
|
||||
sizeStyles.push(`max-height: ${maxH};`)
|
||||
} else if (Number.isInteger(maxH)) {
|
||||
sizeStyles.push(`max-height: ${maxH}px;`)
|
||||
}
|
||||
|
||||
styles.value = styles.value.concat(sizeStyles)
|
||||
}, {
|
||||
immediate: true
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NFormItem :show-label="hasHeader" :label="header" :show-feedback="hasFeedback" :feedback="feedback">
|
||||
<NCard>
|
||||
<NScrollbar :style="styles">
|
||||
<slot />
|
||||
</NScrollbar>
|
||||
</NCard>
|
||||
</NFormItem>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -6,7 +6,7 @@ import {router, useForm} from "@inertiajs/vue3";
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const userOptions = computed(() => {
|
||||
return authStore.availableRoles.map(itm => {
|
||||
return authStore.availableRoles?.map(itm => {
|
||||
return {
|
||||
label: itm.name,
|
||||
value: itm.role_id
|
||||
@@ -15,7 +15,7 @@ const userOptions = computed(() => {
|
||||
})
|
||||
|
||||
const formRole = useForm({
|
||||
role_id: authStore.user.role.role_id
|
||||
role_id: authStore.user.role?.role_id
|
||||
})
|
||||
const onChangeRole = (roleId) => {
|
||||
formRole.post('/user/role/change', {
|
||||
|
||||
47
resources/js/Pages/Admin/Index.vue
Normal file
47
resources/js/Pages/Admin/Index.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<script setup>
|
||||
import {NH1, NFlex, NSpace, NP} from 'naive-ui'
|
||||
import {TbUsers} from "vue-icons-plus/tb";
|
||||
import AppLayout from "../../Layouts/AppLayout.vue";
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
import {computed} from "vue";
|
||||
import {format} from "date-fns";
|
||||
import {useNow} from "@vueuse/core";
|
||||
import {ru} from "date-fns/locale";
|
||||
import StartButton from "../../Components/StartButton.vue";
|
||||
|
||||
const currentDate = computed(() => {
|
||||
const formatted = format(useNow().value, 'PPPPpp', {
|
||||
locale: ru
|
||||
})
|
||||
|
||||
return formatted.charAt(0).toUpperCase() + formatted.slice(1)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AppLayout>
|
||||
<div class="flex flex-col justify-start items-center mt-12">
|
||||
<NFlex vertical align="center" justify="center" class="max-w-xl w-full">
|
||||
<NSpace vertical align="center">
|
||||
<NH1 class="mb-0!">
|
||||
Панель администратора
|
||||
</NH1>
|
||||
<NP class="mb-4!">
|
||||
{{ currentDate }}
|
||||
</NP>
|
||||
</NSpace>
|
||||
|
||||
<StartButton title="Учетные записи"
|
||||
description="Создание и редактирование учетных записей"
|
||||
href="/admin/users"
|
||||
:tag="Link"
|
||||
:icon="TbUsers"
|
||||
/>
|
||||
</NFlex>
|
||||
</div>
|
||||
</AppLayout>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
61
resources/js/Pages/Admin/Users/Create.vue
Normal file
61
resources/js/Pages/Admin/Users/Create.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<script setup>
|
||||
import {NButton, NFlex, NGi, NGrid, NH2, NForm, NFormItem, NInput} from "naive-ui";
|
||||
import AppLayout from "../../../Layouts/AppLayout.vue";
|
||||
import AppContainer from "../../../Components/AppContainer.vue";
|
||||
import AppPanel from "../../../Components/AppPanel.vue";
|
||||
import {useForm} from "@inertiajs/vue3";
|
||||
import {ref} from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
departments: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
roles: {
|
||||
type: Array,
|
||||
default: []
|
||||
}
|
||||
})
|
||||
|
||||
const form = ref({
|
||||
'name': '',
|
||||
'login': '',
|
||||
'password': '',
|
||||
'is_active': true,
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AppLayout>
|
||||
<template #header>
|
||||
<NFlex align="center" justify="space-between" class="max-w-6xl mx-auto mt-6 mb-4 w-full">
|
||||
<NH2>
|
||||
Создание нового пользователя
|
||||
</NH2>
|
||||
</NFlex>
|
||||
</template>
|
||||
<AppContainer>
|
||||
<AppPanel>
|
||||
<NForm v-model:model="form">
|
||||
<NFormItem label="Имя">
|
||||
<NInput v-model:value="form.name" />
|
||||
</NFormItem>
|
||||
<NFormItem label="Логин">
|
||||
<NInput v-model:value="form.login" />
|
||||
</NFormItem>
|
||||
<NFormItem label="Логин">
|
||||
<NInput v-model:value="form.password" />
|
||||
</NFormItem>
|
||||
<NFormItem label="Логин">
|
||||
<NInput v-model:value="form.password" />
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
</AppPanel>
|
||||
</AppContainer>
|
||||
</AppLayout>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
81
resources/js/Pages/Admin/Users/Index.vue
Normal file
81
resources/js/Pages/Admin/Users/Index.vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<script setup>
|
||||
import AppLayout from "../../../Layouts/AppLayout.vue";
|
||||
import {NFlex, NH2, NSpace, NButton, NDataTable} from 'naive-ui'
|
||||
import {h, ref} from "vue";
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
|
||||
const props = defineProps({
|
||||
users: {
|
||||
type: Array,
|
||||
default: []
|
||||
}
|
||||
})
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: 'Имя',
|
||||
key: 'name'
|
||||
},
|
||||
{
|
||||
title: 'Логин',
|
||||
key: 'login'
|
||||
},
|
||||
{
|
||||
title: 'Активен',
|
||||
key: 'is_active',
|
||||
render: (row) => {
|
||||
return row.is_active ? 'Да' : 'Нет'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Дата создания',
|
||||
key: 'created_at'
|
||||
},
|
||||
{
|
||||
title: 'Дата изменения',
|
||||
key: 'updated_at'
|
||||
},
|
||||
{
|
||||
title: 'Действия',
|
||||
key: 'actions',
|
||||
render: (row) => {
|
||||
return h(NButton, {
|
||||
text: true,
|
||||
size: 'small',
|
||||
tag: Link,
|
||||
href: `/admin/users/${row.id}`
|
||||
}, 'Редактировать')
|
||||
}
|
||||
},
|
||||
])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AppLayout>
|
||||
<template #header>
|
||||
<NFlex align="center" justify="space-between" class="max-w-6xl mx-auto mt-6 mb-4 w-full">
|
||||
<NH2>
|
||||
Учетные записи
|
||||
</NH2>
|
||||
<NSpace>
|
||||
<NButton :tag="Link" href="/admin/users/new" type="primary">
|
||||
Создать учетную запись
|
||||
</NButton>
|
||||
</NSpace>
|
||||
</NFlex>
|
||||
</template>
|
||||
<NDataTable class="max-w-6xl mx-auto mb-4 w-full" :columns="columns" :data="users" />
|
||||
</AppLayout>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.n-h) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
:deep(.n-data-table-th),
|
||||
:deep(.n-data-table-td) {
|
||||
font-size: var(--n-font-size);
|
||||
}
|
||||
|
||||
</style>
|
||||
120
resources/js/Pages/Admin/Users/User.vue
Normal file
120
resources/js/Pages/Admin/Users/User.vue
Normal file
@@ -0,0 +1,120 @@
|
||||
<script setup>
|
||||
import {NButton, NDataTable, NScrollbar, NList, NListItem, NGrid, NGi, NFlex, NH2, NSpace} from "naive-ui";
|
||||
import AppLayout from "../../../Layouts/AppLayout.vue";
|
||||
import AppPanel from "../../../Components/AppPanel.vue";
|
||||
import AppContainer from "../../../Components/AppContainer.vue";
|
||||
|
||||
const props = defineProps({
|
||||
userData: {
|
||||
type: Object,
|
||||
default: {}
|
||||
},
|
||||
roles: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
departments: {
|
||||
type: Array,
|
||||
default: []
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AppLayout>
|
||||
<template #header>
|
||||
<NFlex align="center" justify="space-between" class="max-w-6xl mx-auto mt-6 mb-4 w-full">
|
||||
<NH2>
|
||||
{{ userData.name }}
|
||||
</NH2>
|
||||
<NSpace>
|
||||
<NButton type="primary">
|
||||
Создать учетную запись
|
||||
</NButton>
|
||||
</NSpace>
|
||||
</NFlex>
|
||||
</template>
|
||||
<AppContainer>
|
||||
<NGrid cols="2" x-gap="12" y-gap="12">
|
||||
<NGi>
|
||||
<AppPanel header="Информация о пользователе" min-h="148px" max-h="148px">
|
||||
<NSpace vertical>
|
||||
<NFlex align="center" justify="space-between">
|
||||
<div>
|
||||
Имя
|
||||
</div>
|
||||
<div>
|
||||
{{ userData.name }}
|
||||
</div>
|
||||
</NFlex>
|
||||
<NFlex align="center" justify="space-between">
|
||||
<div>
|
||||
Логин
|
||||
</div>
|
||||
<div>
|
||||
{{ userData.login }}
|
||||
</div>
|
||||
</NFlex>
|
||||
<NFlex align="center" justify="space-between">
|
||||
<div>
|
||||
Активен
|
||||
</div>
|
||||
<div>
|
||||
{{ userData.is_active ? 'Да' : 'Нет' }}
|
||||
</div>
|
||||
</NFlex>
|
||||
<NFlex align="center" justify="space-between">
|
||||
<div>
|
||||
Дата создания
|
||||
</div>
|
||||
<div>
|
||||
{{ userData.created_at }}
|
||||
</div>
|
||||
</NFlex>
|
||||
<NFlex align="center" justify="space-between">
|
||||
<div>
|
||||
Дата обновления
|
||||
</div>
|
||||
<div>
|
||||
{{ userData.updated_at }}
|
||||
</div>
|
||||
</NFlex>
|
||||
</NSpace>
|
||||
</AppPanel>
|
||||
</NGi>
|
||||
<NGi>
|
||||
|
||||
</NGi>
|
||||
<NGi>
|
||||
<AppPanel :header="`Доступные отделения (${departments.length})`" min-h="148px" max-h="148px">
|
||||
<NList>
|
||||
<NListItem v-for="department in departments">
|
||||
{{ department.name_full }}
|
||||
</NListItem>
|
||||
</NList>
|
||||
</AppPanel>
|
||||
</NGi>
|
||||
<NGi>
|
||||
<AppPanel :header="`Доступные роли (${roles.length})`" min-h="148px" max-h="148px">
|
||||
<NList>
|
||||
<NListItem v-for="role in roles">
|
||||
{{ role.name }}
|
||||
</NListItem>
|
||||
</NList>
|
||||
</AppPanel>
|
||||
</NGi>
|
||||
</NGrid>
|
||||
</AppContainer>
|
||||
</AppLayout>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.n-h) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
:deep(.n-data-table-th),
|
||||
:deep(.n-data-table-td) {
|
||||
font-size: var(--n-font-size);
|
||||
}
|
||||
</style>
|
||||
@@ -7,7 +7,7 @@ import {computed, ref} from "vue";
|
||||
import {format} from "date-fns";
|
||||
import {ru} from "date-fns/locale";
|
||||
import {useNow} from "@vueuse/core";
|
||||
import {TbArticle, TbChartTreemap, TbDoorExit} from "vue-icons-plus/tb";
|
||||
import {TbArticle, TbChartTreemap, TbDoorExit, TbUserCog} from "vue-icons-plus/tb";
|
||||
import {useReportStore} from "../Stores/report.js";
|
||||
import SelectUserModal from "./Report/Components/SelectUserModal.vue";
|
||||
import {Link} from "@inertiajs/vue3";
|
||||
@@ -58,6 +58,12 @@ const reportButtonType = computed(() => authStore.isDoctor ? 'button' : Link)
|
||||
:href="`/statistic`"
|
||||
:icon="TbChartTreemap"
|
||||
/>
|
||||
<StartButton v-if="false" title="Панель администратора"
|
||||
description="Управление приложением"
|
||||
href="/admin"
|
||||
:tag="Link"
|
||||
:icon="TbUserCog"
|
||||
/>
|
||||
<StartButton title="Выйти из системы"
|
||||
description="Завершение работы с текущей учетной записью"
|
||||
href="/dashboard"
|
||||
|
||||
@@ -18,9 +18,11 @@ const authStore = useAuthStore()
|
||||
const reportStore = useReportStore()
|
||||
|
||||
const onSubmit = () => {
|
||||
reportStore.sendReportForm({
|
||||
departmentId: authStore.userDepartment.department_id
|
||||
reportStore.reportFormRef?.validate((errors) => {
|
||||
if (!errors) reportStore.sendReportForm()
|
||||
else window.$message.error('Ошибка отправки отчета')
|
||||
})
|
||||
// reportStore.sendReportForm()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2,16 +2,42 @@
|
||||
import {NCard, NSkeleton, NSpace, NFlex, NFormItem, NForm, NInputNumber, NStatistic} from "naive-ui";
|
||||
import {useReportStore} from "../../../Stores/report.js";
|
||||
import {useAuthStore} from "../../../Stores/auth.js";
|
||||
import {computed, onMounted, ref, watch} from "vue";
|
||||
|
||||
const reportStore = useReportStore()
|
||||
const authStore = useAuthStore()
|
||||
const formRef = ref()
|
||||
|
||||
const rules = computed(() => {
|
||||
const rawRules = {}
|
||||
|
||||
for (const metrika of reportStore.reportInfo.metrikaItems) {
|
||||
const rule = {}
|
||||
rule.required = metrika.is_required
|
||||
rule.trigger = ['input', 'blur']
|
||||
if (metrika.data_type === 'integer') {
|
||||
rule.validator = (rule, value) => {
|
||||
if (!/^\d*$/.test(value)) {
|
||||
return new Error()
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
rawRules[`metrika_item_${metrika.metrika_item_id}`] = rule
|
||||
}
|
||||
|
||||
return rawRules
|
||||
})
|
||||
|
||||
watch(() => formRef.value, (nv) => {
|
||||
reportStore.reportFormRef = nv
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="grid grid-cols-[1fr_auto] gap-x-3">
|
||||
<NCard v-if="reportStore.reportInfo?.report?.isActiveSendButton">
|
||||
<NForm>
|
||||
<NCard>
|
||||
<NForm ref="formRef" :model="reportStore.reportForm" :disabled="!reportStore.reportInfo?.report?.isActiveSendButton" :rules="rules">
|
||||
<template v-if="reportStore.isLoadReportInfo">
|
||||
<NFlex>
|
||||
<NSkeleton class="rounded-md w-[246px]! h-[65px]!" />
|
||||
@@ -22,9 +48,9 @@ const authStore = useAuthStore()
|
||||
<NFlex v-else justify="space-between" align="center">
|
||||
<NSpace>
|
||||
<template v-for="metrikaItem in reportStore.reportInfo?.metrikaItems">
|
||||
<NFormItem :label="metrikaItem.name" :show-feedback="false">
|
||||
<NFormItem :label="metrikaItem.name" :show-feedback="false" :required="metrikaItem.is_required" :path="`metrika_item_${metrikaItem.metrika_item_id}`">
|
||||
<NInputNumber v-model:value="reportStore.reportForm[`metrika_item_${metrikaItem.metrika_item_id}`]"
|
||||
:default-value="metrikaItem.default_value" />
|
||||
:default-value="metrikaItem.default_value" min="0" />
|
||||
</NFormItem>
|
||||
</template>
|
||||
</NSpace>
|
||||
@@ -39,6 +65,19 @@ const authStore = useAuthStore()
|
||||
<NStatistic label="Умерло" :value="reportStore.reportInfo?.department?.deadCount" />
|
||||
</div>
|
||||
</NCard>
|
||||
<NCard class="min-w-[120px] max-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 :value="reportStore.reportInfo.department.percentDead">
|
||||
<template #label>
|
||||
<div class="flex flex-col">
|
||||
<span>Летальность</span>
|
||||
<span>%</span>
|
||||
</div>
|
||||
</template>
|
||||
</NStatistic>
|
||||
</div>
|
||||
</NCard>
|
||||
<NCard class="min-w-[120px] max-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">
|
||||
|
||||
@@ -58,11 +58,11 @@ const currentDate = computed(() => {
|
||||
<div class="grid grid-cols-[auto_1fr_auto] items-center">
|
||||
<NSpace align="center">
|
||||
<NTag v-if="isFillableMode" type="info" :bordered="false">
|
||||
{{ authStore.userDepartment.name_full }}
|
||||
{{ reportStore.reportInfo.department.department_name }}
|
||||
</NTag>
|
||||
<DepartmentSelect v-if="isReadonlyMode" />
|
||||
<NTag v-if="reportStore.reportInfo?.report?.userName" type="warning">
|
||||
Ответственный: {{ reportStore.reportInfo?.report.userName }}
|
||||
<NTag v-if="reportStore.reportInfo.report.userName" type="warning">
|
||||
Ответственный: {{ reportStore.reportInfo.report.userName }}
|
||||
</NTag>
|
||||
</NSpace>
|
||||
|
||||
|
||||
@@ -23,7 +23,8 @@ const fetchPatientCount = async () => {
|
||||
const data = {
|
||||
status: props.status,
|
||||
startAt: reportStore.timestampCurrentRange[0],
|
||||
endAt: reportStore.timestampCurrentRange[1]
|
||||
endAt: reportStore.timestampCurrentRange[1],
|
||||
departmentId: reportStore.reportInfo.department.department_id
|
||||
}
|
||||
await axios.post('/api/mis/patients/count', data).then((res) => {
|
||||
countPatient.value = res.data
|
||||
|
||||
@@ -229,6 +229,7 @@ const fetchPatients = async () => {
|
||||
status: props.status,
|
||||
startAt: reportStore.timestampCurrentRange[0],
|
||||
endAt: reportStore.timestampCurrentRange[1],
|
||||
departmentId: reportStore.reportInfo.department.department_id
|
||||
}
|
||||
await axios.post('/api/mis/patients', data).then((res) => {
|
||||
patientsData.value[props.status] = reportStore.addRowNumbers(res.data)
|
||||
|
||||
@@ -1,56 +1,139 @@
|
||||
<script setup>
|
||||
import { NSelect, NModal, NForm, NFormItem, NButton } from 'naive-ui'
|
||||
import { NSelect, NModal, NForm, NFormItem, NButton, NAlert } from 'naive-ui'
|
||||
import {useReportStore} from "../../../Stores/report.js";
|
||||
import {computed, onMounted, ref} from "vue";
|
||||
import {router} from "@inertiajs/vue3";
|
||||
import {computed, onMounted, ref, watch} from "vue";
|
||||
import {router, Link} from "@inertiajs/vue3";
|
||||
import {useDebounceFn} from "@vueuse/core";
|
||||
const show = defineModel('show')
|
||||
const reportStore = useReportStore()
|
||||
const formRef = ref()
|
||||
|
||||
const users = computed(() => reportStore.departmentUsers.map(itm => ({
|
||||
const rawUsers = ref([])
|
||||
const rawDepartments = ref([])
|
||||
const reportExists = ref(false)
|
||||
const existingReportId = ref(null)
|
||||
|
||||
const users = computed(() => rawUsers.value.map(itm => ({
|
||||
label: `${itm.FAM_V} ${itm.IM_V} ${itm.OT_V}`,
|
||||
value: itm.LPUDoctorID
|
||||
})))
|
||||
|
||||
const departments = computed(() => rawDepartments.value.map(itm => ({
|
||||
label: itm.name_short,
|
||||
value: itm.department_id
|
||||
})))
|
||||
|
||||
const fetchUsers = () => {
|
||||
axios.get('/api/mis/department-users')
|
||||
.then((res) => {
|
||||
reportStore.departmentUsers = res.data
|
||||
rawUsers.value = res.data
|
||||
})
|
||||
}
|
||||
|
||||
const fetchDepartments = () => {
|
||||
axios.get('/api/app/departments')
|
||||
.then((res) => {
|
||||
rawDepartments.value = res.data
|
||||
})
|
||||
}
|
||||
|
||||
const checkReportExists = async (userId, departmentId) => {
|
||||
if (!userId || !departmentId) {
|
||||
reportExists.value = false;
|
||||
existingReportId.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await axios.get(`/api/report/check`, {
|
||||
params: {
|
||||
department_id: departmentId,
|
||||
}
|
||||
}).then(res => {
|
||||
reportExists.value = res.data.exists;
|
||||
existingReportId.value = res.data.report_id || null;
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Ошибка при проверке отчета:', error);
|
||||
reportExists.value = false;
|
||||
existingReportId.value = null;
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
// Дебаунс функция
|
||||
const debouncedCheck = useDebounceFn((userId, departmentId) => {
|
||||
checkReportExists(userId, departmentId);
|
||||
}, 300);
|
||||
|
||||
// Автоматическая проверка при изменении любого из полей
|
||||
watch(
|
||||
() => [reportStore.reportInfo.userId, reportStore.reportInfo.departmentId],
|
||||
([newUserId, newDepartmentId], [oldUserId, oldDepartmentId]) => {
|
||||
// Проверяем, что оба поля заполнены и корректны
|
||||
const userIdValid = newUserId && Number.isInteger(Number(newUserId));
|
||||
const departmentIdValid = newDepartmentId && Number.isInteger(Number(newDepartmentId));
|
||||
|
||||
if (userIdValid && departmentIdValid) {
|
||||
debouncedCheck(newUserId, newDepartmentId);
|
||||
} else {
|
||||
reportExists.value = false;
|
||||
existingReportId.value = null;
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
const rules = {
|
||||
userId: {
|
||||
required: true,
|
||||
validator: (rule, value) => {
|
||||
if (Number.isInteger(value)) return true
|
||||
|
||||
return false
|
||||
return Number.isInteger(value) ?? false
|
||||
},
|
||||
trigger: ['change', 'blur'],
|
||||
message: 'Выберите ответственного'
|
||||
}
|
||||
},
|
||||
departmentId: {
|
||||
required: true,
|
||||
validator: (rule, value) => {
|
||||
return Number.isInteger(value) ?? false
|
||||
},
|
||||
trigger: ['change', 'blur'],
|
||||
message: 'Выберите отделение'
|
||||
},
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
reportStore.reportInfo.userId = null
|
||||
reportStore.reportInfo.departmentId = null
|
||||
fetchUsers()
|
||||
fetchDepartments()
|
||||
})
|
||||
|
||||
const onSubmit = (e) => {
|
||||
e.preventDefault()
|
||||
formRef.value?.validate((errors) => {
|
||||
if (!errors) {
|
||||
router.visit(`/report?userId=${reportStore.reportInfo.userId}`)
|
||||
if (reportExists.value) return
|
||||
router.visit(`/report`, {
|
||||
data: {
|
||||
userId: reportStore.reportInfo.userId,
|
||||
departmentId: reportStore.reportInfo.departmentId
|
||||
}
|
||||
})
|
||||
}
|
||||
else {
|
||||
|
||||
window.$message.warning('Заполните выделенные поля')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const onAfterLeave = () => {
|
||||
reportStore.reportInfo.userId = null
|
||||
reportStore.reportInfo.departmentId = null
|
||||
reportExists.value = false
|
||||
existingReportId.value = null
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -68,12 +151,25 @@ const onAfterLeave = () => {
|
||||
filterable
|
||||
/>
|
||||
</NFormItem>
|
||||
<NFormItem label="Выберите отделение" path="departmentId">
|
||||
<NSelect :options="departments"
|
||||
v-model:value="reportStore.reportInfo.departmentId"
|
||||
filterable
|
||||
/>
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
<NAlert v-if="reportExists" type="warning">
|
||||
Сводная уже создана.
|
||||
<NButton :tag="Link" text :href="`/report?userId=${reportStore.reportInfo.userId}&departmentId=${reportStore.reportInfo.departmentId}`">
|
||||
Перейти
|
||||
</NButton>
|
||||
</NAlert>
|
||||
<template #action>
|
||||
<NButton form-id="select-user-form"
|
||||
type="primary"
|
||||
block
|
||||
@click="onSubmit">
|
||||
@click="onSubmit"
|
||||
:disabled="reportExists">
|
||||
Перейти к заполнению сводной
|
||||
</NButton>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
<script setup>
|
||||
import {
|
||||
NModal,
|
||||
NList,
|
||||
NListItem,
|
||||
NThing,
|
||||
NAvatar,
|
||||
NIcon,
|
||||
NText,
|
||||
NDivider,
|
||||
NFlex,
|
||||
NButton,
|
||||
NScrollbar,
|
||||
NEmpty,
|
||||
NDataTable,
|
||||
NBadge,
|
||||
NForm,
|
||||
NDrawerContent,
|
||||
NInput,
|
||||
NSpin,
|
||||
NDrawer
|
||||
} from 'naive-ui'
|
||||
import {useReportStore} from "../../../Stores/report.js";
|
||||
import {TbAlertCircle, TbCheck, TbEye, TbGripVertical, TbPencil, TbTrashX, TbX} from 'vue-icons-plus/tb'
|
||||
import {computed, h, ref, watch} from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
departmentId: {
|
||||
required: true
|
||||
},
|
||||
startAt: {
|
||||
required: true
|
||||
},
|
||||
endAt: {
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const reportStore = useReportStore()
|
||||
const open = defineModel('open')
|
||||
const loading = ref(true)
|
||||
const baseColumns = reportStore.getColumnsByKey(['num', 'fullname', 'age', 'birth_date', 'mkb.ds'])
|
||||
|
||||
const currentPatient = ref(null)
|
||||
const showMoveDrawer = ref(false)
|
||||
|
||||
const columns = computed(() => {
|
||||
// if (!isFillableMode.value) return baseColumns
|
||||
|
||||
const newColumns = []
|
||||
|
||||
const expandColumn = {
|
||||
title: '',
|
||||
width: '30',
|
||||
render: (rowData) => {
|
||||
return h(
|
||||
NIcon,
|
||||
{
|
||||
onClick: () => {
|
||||
currentPatient.value = rowData
|
||||
showMoveDrawer.value = true
|
||||
}
|
||||
},
|
||||
{
|
||||
default: h(TbEye)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const fillableColumn = {
|
||||
title: '',
|
||||
key: 'fillable',
|
||||
width: '20',
|
||||
render: (row) => h(
|
||||
NBadge,
|
||||
{
|
||||
dot: true,
|
||||
color: (row.comment && row.comment.trim()) ? '#7fe7c4' : '#e88080'
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
newColumns.push(expandColumn)
|
||||
newColumns.push(fillableColumn)
|
||||
newColumns.push(...baseColumns)
|
||||
|
||||
return newColumns
|
||||
})
|
||||
|
||||
const observablePatients = ref([])
|
||||
const fetchUnwantedEvents = () => {
|
||||
loading.value = true
|
||||
const data = {
|
||||
status: 'observation',
|
||||
startAt: props.startAt,
|
||||
endAt: props.endAt,
|
||||
departmentId: props.departmentId
|
||||
}
|
||||
axios.post('/api/mis/patients', data).then((res) => {
|
||||
observablePatients.value = reportStore.addRowNumbers(res.data)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
watch(() => [props.departmentId, props.endAt, props.startAt], () => {
|
||||
if (props.departmentId && props.endAt && props.startAt) {
|
||||
fetchUnwantedEvents()
|
||||
}
|
||||
}, {
|
||||
immediate: true,
|
||||
deep: true
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NModal v-model:show="open"
|
||||
title="Пациенты на контроле"
|
||||
preset="card"
|
||||
:mask-closable="false"
|
||||
:close-on-esc="false"
|
||||
class="max-w-4xl overflow-clip h-[calc(100vh-220px)]"
|
||||
id="modal-observation-patients"
|
||||
>
|
||||
<template v-if="loading">
|
||||
<div class="flex items-center justify-center h-full">
|
||||
<NSpin />
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="observablePatients.length">
|
||||
<NDataTable :columns="columns"
|
||||
ref="tableRef"
|
||||
:data="observablePatients"
|
||||
size="small"
|
||||
:loading="loading"
|
||||
max-height="200"
|
||||
min-height="200"
|
||||
:row-key="(row, index) => row.id"
|
||||
class="text-sm!">
|
||||
</NDataTable>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="h-full flex items-center justify-center">
|
||||
<NEmpty description="Пациентов на контроле не найдено!" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<NDrawer
|
||||
v-model:show="showMoveDrawer"
|
||||
placement="bottom"
|
||||
:min-height="400"
|
||||
:max-height="600"
|
||||
:default-height="400"
|
||||
resizable
|
||||
:trap-focus="false"
|
||||
:block-scroll="false"
|
||||
to="#modal-observation-patients"
|
||||
>
|
||||
<NDrawerContent title="Причина постановки на контроль" closable>
|
||||
<NInput type="textarea" readonly :rows="8" v-model:value="currentPatient.comment" />
|
||||
</NDrawerContent>
|
||||
</NDrawer>
|
||||
</NModal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.n-data-table-th),
|
||||
:deep(.n-data-table-td) {
|
||||
white-space: nowrap !important;
|
||||
font-size: var(--n-font-size);
|
||||
}
|
||||
</style>
|
||||
115
resources/js/Pages/Statistic/Components/ModalUnwantedEvents.vue
Normal file
115
resources/js/Pages/Statistic/Components/ModalUnwantedEvents.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<script setup>
|
||||
import {
|
||||
NModal, NList, NListItem, NThing, NAvatar, NIcon,
|
||||
NText, NDivider, NFlex, NButton, NScrollbar, NEmpty, NSpin
|
||||
} from 'naive-ui'
|
||||
import {useReportStore} from "../../../Stores/report.js";
|
||||
import { TbAlertCircle, TbPencil, TbTrashX } from 'vue-icons-plus/tb'
|
||||
import {ref, watch} from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
departmentId: {
|
||||
required: true
|
||||
},
|
||||
startAt: {
|
||||
required: true
|
||||
},
|
||||
endAt: {
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const open = defineModel('open')
|
||||
const loading = ref(true)
|
||||
|
||||
const unwantedEvents = ref([])
|
||||
const fetchUnwantedEvents = () => {
|
||||
loading.value = true
|
||||
axios.get('/api/statistics/reports/unwanted-events', {
|
||||
params: {
|
||||
departmentId: props.departmentId,
|
||||
startAt: props.startAt,
|
||||
endAt: props.endAt
|
||||
}
|
||||
})
|
||||
.then(res => {
|
||||
unwantedEvents.value = res.data
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
watch(() => [props.departmentId, props.endAt, props.startAt], () => {
|
||||
if (props.departmentId && props.endAt && props.startAt) {
|
||||
fetchUnwantedEvents()
|
||||
}
|
||||
}, {
|
||||
immediate: true,
|
||||
deep: true
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NModal v-model:show="open"
|
||||
title="Нежелательные события"
|
||||
preset="card"
|
||||
:mask-closable="false"
|
||||
:close-on-esc="false"
|
||||
class="max-w-4xl overflow-clip h-[calc(100vh-220px)]"
|
||||
>
|
||||
<template v-if="loading">
|
||||
<div class="flex items-center justify-center h-full">
|
||||
<NSpin />
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="unwantedEvents.length">
|
||||
<NScrollbar class="max-h-[calc(100vh-282px)] pr-3">
|
||||
<NList>
|
||||
<NListItem v-for="event in unwantedEvents">
|
||||
<NThing>
|
||||
<template #avatar>
|
||||
<NAvatar>
|
||||
<NIcon>
|
||||
<TbAlertCircle class="text-red-400" />
|
||||
</NIcon>
|
||||
</NAvatar>
|
||||
</template>
|
||||
<template #header>
|
||||
{{ event.title }}
|
||||
</template>
|
||||
<template #description>
|
||||
<NText depth="3">
|
||||
{{ event.created_at }}
|
||||
</NText>
|
||||
</template>
|
||||
<NText>
|
||||
{{ event.comment }}
|
||||
</NText>
|
||||
</NThing>
|
||||
</NListItem>
|
||||
</NList>
|
||||
</NScrollbar>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="h-full flex items-center justify-center">
|
||||
<NEmpty description="Нежелательные события не найдены!" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- <template #action>-->
|
||||
<!-- <NFlex id="modal-action" align="center" justify="space-between">-->
|
||||
<!-- <NButton type="primary" secondary @click="onCreateEvent()">-->
|
||||
<!-- <template #icon>-->
|
||||
<!-- <TbCirclePlus />-->
|
||||
<!-- </template>-->
|
||||
<!-- Создать событие-->
|
||||
<!-- </NButton>-->
|
||||
<!-- </NFlex>-->
|
||||
<!-- </template>-->
|
||||
</NModal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,9 +1,12 @@
|
||||
<script setup>
|
||||
import {NDataTable, NFlex, NText, NDatePicker} from 'naive-ui'
|
||||
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: {
|
||||
@@ -37,7 +40,7 @@ const columns = ref([
|
||||
}
|
||||
|
||||
if (row.isTotalRow) {
|
||||
return h(NText, { style: 'font-weight: 600;' }, row.department)
|
||||
return row.department
|
||||
}
|
||||
|
||||
// Получаем текущие query параметры
|
||||
@@ -46,8 +49,10 @@ const columns = ref([
|
||||
const searchParams = currentUrl.searchParams
|
||||
|
||||
// Берем startAt и endAt из текущего URL
|
||||
const startAt = searchParams.get('startAt')
|
||||
const endAt = searchParams.get('endAt')
|
||||
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 = {}
|
||||
|
||||
@@ -56,11 +61,53 @@ const columns = ref([
|
||||
if (endAt)
|
||||
linkData.endAt = endAt
|
||||
|
||||
return h(Link, {
|
||||
href: `/report`,
|
||||
data: linkData,
|
||||
class: 'underline decoration-dashed'
|
||||
}, row.department)
|
||||
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: 'Пациенты на контроле'
|
||||
}),
|
||||
])
|
||||
])
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -69,13 +116,6 @@ const columns = ref([
|
||||
width: 60,
|
||||
titleAlign: 'center',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
if (row.isTotalRow) {
|
||||
return h(NText, { style: 'font-weight: 600;' }, row.beds)
|
||||
} else {
|
||||
return h(NText, { }, row.beds)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Поступило',
|
||||
@@ -88,13 +128,6 @@ const columns = ref([
|
||||
width: 60,
|
||||
titleAlign: 'center',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
if (row.isTotalRow) {
|
||||
return h(NText, { style: 'font-weight: 600;' }, row.recipients.all)
|
||||
} else {
|
||||
return h(NText, { }, row.recipients.all)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'План',
|
||||
@@ -102,13 +135,6 @@ const columns = ref([
|
||||
width: 60,
|
||||
titleAlign: 'center',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
if (row.isTotalRow) {
|
||||
return h(NText, { style: 'font-weight: 600;' }, row.recipients.plan)
|
||||
} else {
|
||||
return h(NText, { }, row.recipients.plan)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Экстр',
|
||||
@@ -116,13 +142,6 @@ const columns = ref([
|
||||
width: 60,
|
||||
titleAlign: 'center',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
if (row.isTotalRow) {
|
||||
return h(NText, { style: 'font-weight: 600;' }, row.recipients.emergency)
|
||||
} else {
|
||||
return h(NText, { }, row.recipients.emergency)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Перевод',
|
||||
@@ -130,13 +149,6 @@ const columns = ref([
|
||||
width: 84,
|
||||
titleAlign: 'center',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
if (row.isTotalRow) {
|
||||
return h(NText, { style: 'font-weight: 600;' }, row.recipients.transferred)
|
||||
} else {
|
||||
return h(NText, { }, row.recipients.transferred)
|
||||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
},
|
||||
@@ -146,13 +158,6 @@ const columns = ref([
|
||||
width: 84,
|
||||
titleAlign: 'center',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
if (row.isTotalRow) {
|
||||
return h(NText, { style: 'font-weight: 600;' }, row.outcome)
|
||||
} else {
|
||||
return h(NText, { }, row.outcome)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Состоит',
|
||||
@@ -160,13 +165,13 @@ const columns = ref([
|
||||
width: 84,
|
||||
titleAlign: 'center',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
if (row.isTotalRow) {
|
||||
return h(NText, { style: 'font-weight: 600;' }, row.consist)
|
||||
} else {
|
||||
return h(NText, { }, row.consist)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Ср. койко-день',
|
||||
key: 'averageBedDays',
|
||||
width: 44,
|
||||
titleAlign: 'center',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '% загруженности',
|
||||
@@ -174,13 +179,6 @@ const columns = ref([
|
||||
width: 84,
|
||||
titleAlign: 'center',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
if (row.isTotalRow) {
|
||||
return h(NText, { style: 'font-weight: 600;' }, row.percentLoadedBeds)
|
||||
} else {
|
||||
return h(NText, { }, row.percentLoadedBeds)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Операции',
|
||||
@@ -193,13 +191,6 @@ const columns = ref([
|
||||
width: 60,
|
||||
titleAlign: 'center',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
if (row.isTotalRow) {
|
||||
return h(NText, { style: 'font-weight: 600;' }, row.surgical.emergency)
|
||||
} else {
|
||||
return h(NText, { }, row.surgical.emergency)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'П',
|
||||
@@ -207,13 +198,6 @@ const columns = ref([
|
||||
width: 60,
|
||||
titleAlign: 'center',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
if (row.isTotalRow) {
|
||||
return h(NText, { style: 'font-weight: 600;' }, row.surgical.plan)
|
||||
} else {
|
||||
return h(NText, { }, row.surgical.plan)
|
||||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
},
|
||||
@@ -223,24 +207,41 @@ const columns = ref([
|
||||
width: 84,
|
||||
titleAlign: 'center',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
if (row.isTotalRow) {
|
||||
return h(NText, { style: 'font-weight: 600;' }, row.deceased)
|
||||
} else {
|
||||
return h(NText, { }, row.deceased)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
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); --n-text-color: var(--n-th-icon-color-active);`
|
||||
style: `--n-merged-td-color: var(--n-merged-th-color);`
|
||||
}
|
||||
}
|
||||
|
||||
const rowClassName = (row) => {
|
||||
if (row.isTotalRow)
|
||||
return `total-row`
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -256,9 +257,12 @@ const rowProps = (row) => {
|
||||
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>
|
||||
|
||||
@@ -267,4 +271,9 @@ const rowProps = (row) => {
|
||||
: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>
|
||||
|
||||
@@ -5,6 +5,7 @@ import {router} from "@inertiajs/vue3";
|
||||
|
||||
export const useReportStore = defineStore('reportStore', () => {
|
||||
const timestampNow = useTimestamp()
|
||||
const reportFormRef = ref(null)
|
||||
|
||||
const _timestampCurrent = ref(null)
|
||||
const timestampCurrent = computed({
|
||||
@@ -93,7 +94,7 @@ export const useReportStore = defineStore('reportStore', () => {
|
||||
return result
|
||||
}
|
||||
|
||||
const sendReportForm = (assignForm) => {
|
||||
const sendReportForm = (assignForm = null) => {
|
||||
const form = {
|
||||
metrics: reportForm.value,
|
||||
observationPatients: patientsData.value['observation'],
|
||||
@@ -103,6 +104,7 @@ export const useReportStore = defineStore('reportStore', () => {
|
||||
timestampCurrentRange.value[1]
|
||||
],
|
||||
userId: reportInfo.value.report.userId,
|
||||
departmentId: reportInfo.value.department.department_id,
|
||||
reportId: reportInfo.value.report.report_id,
|
||||
...assignForm
|
||||
}
|
||||
@@ -206,6 +208,7 @@ export const useReportStore = defineStore('reportStore', () => {
|
||||
}
|
||||
|
||||
return {
|
||||
reportFormRef,
|
||||
timestampNow,
|
||||
timestampCurrent,
|
||||
timestampCurrentRange,
|
||||
|
||||
Reference in New Issue
Block a user