120 lines
3.6 KiB
Vue
120 lines
3.6 KiB
Vue
<script setup>
|
||
import {
|
||
NModal, NDataTable, NEmpty, NSpin
|
||
} from 'naive-ui'
|
||
import { computed, ref, watch, h } from 'vue'
|
||
import { MODAL_TYPES } from './config'
|
||
import { getColumns } from './columns' // фабрика колонок
|
||
|
||
const props = defineProps({
|
||
type: {
|
||
type: String,
|
||
required: true,
|
||
validator: (val) => Object.keys(MODAL_TYPES).includes(val),
|
||
},
|
||
startAt: { type: String, default: null },
|
||
endAt: { type: String, default: null },
|
||
// Дополнительные параметры фильтрации (например, отделение)
|
||
departmentId: { type: Number, default: null },
|
||
})
|
||
|
||
const open = defineModel('open')
|
||
|
||
const config = computed(() => MODAL_TYPES[props.type])
|
||
const loading = ref(true)
|
||
const patients = ref([])
|
||
const filteredDepartments = ref([])
|
||
|
||
// Получаем уникальные отделения для фильтра
|
||
const departmentOptions = computed(() => {
|
||
const depts = new Set()
|
||
patients.value.forEach(p => {
|
||
if (p.department_name) depts.add(p.department_name)
|
||
})
|
||
return Array.from(depts).map(d => ({ label: d, value: d }))
|
||
.sort((a, b) => a.label.localeCompare(b.label))
|
||
})
|
||
|
||
// Фильтр по отделениям
|
||
const filteredData = computed(() => {
|
||
if (!filteredDepartments.value.length) return patients.value
|
||
return patients.value.filter(p =>
|
||
filteredDepartments.value.includes(p.department_name)
|
||
)
|
||
})
|
||
|
||
// Динамические колонки – получаем из фабрики
|
||
const columns = computed(() => {
|
||
const baseColumns = getColumns(props.type)
|
||
// Добавляем фильтр по отделению в колонку 'department_name' если она существует
|
||
return baseColumns.map(col => {
|
||
if (col.key === 'department_name') {
|
||
return {
|
||
...col,
|
||
filterMultiple: true,
|
||
filterOptions: departmentOptions.value,
|
||
filter: (value, row) => row.department_name?.includes(value) ?? false,
|
||
}
|
||
}
|
||
return col
|
||
})
|
||
})
|
||
|
||
const fetchData = () => {
|
||
loading.value = true
|
||
const params = {
|
||
startAt: props.startAt,
|
||
endAt: props.endAt,
|
||
departmentId: props.departmentId,
|
||
type: props.type
|
||
}
|
||
axios.get('/api/statistics/reports/patients', { params })
|
||
.then(res => {
|
||
patients.value = res.data ?? []
|
||
filteredDepartments.value = []
|
||
})
|
||
.finally(() => { loading.value = false })
|
||
}
|
||
|
||
watch(open, (val) => {
|
||
if (val) fetchData()
|
||
})
|
||
</script>
|
||
|
||
<template>
|
||
<NModal
|
||
v-model:show="open"
|
||
:title="config?.title"
|
||
preset="card"
|
||
:mask-closable="false"
|
||
:close-on-esc="false"
|
||
class="max-w-5xl h-[calc(100vh-220px)]"
|
||
>
|
||
<template v-if="loading">
|
||
<div class="flex items-center justify-center h-full"><NSpin /></div>
|
||
</template>
|
||
<template v-else-if="patients.length">
|
||
<NDataTable
|
||
:columns="columns"
|
||
:data="filteredData"
|
||
table-layout="fixed"
|
||
size="small"
|
||
max-height="calc(100vh - 350px)"
|
||
:row-key="row => row.id"
|
||
/>
|
||
</template>
|
||
<template v-else>
|
||
<div class="h-full flex items-center justify-center">
|
||
<NEmpty description="Нет данных" />
|
||
</div>
|
||
</template>
|
||
</NModal>
|
||
</template>
|
||
|
||
<style scoped>
|
||
:deep(.n-data-table-th),
|
||
:deep(.n-data-table-td) {
|
||
font-size: var(--n-font-size);
|
||
}
|
||
</style>
|