modified: .gitignore

This commit is contained in:
brusnitsyn
2026-04-21 10:08:14 +09:00
parent 0e8b6f61b4
commit 2041ab54ea
74 changed files with 7533 additions and 1544 deletions

View File

@@ -1,12 +1,31 @@
<script setup>
import {NIcon, NText, NDataTable, NButton, NBadge, NTabs, NTabPane, NPopover, NEllipsis, NTooltip} from "naive-ui";
import {
NIcon,
NText,
NDataTable,
NButton,
NBadge,
NTabs,
NTabPane,
NPopover,
NEllipsis,
NTooltip,
NFlex,
NInput,
NPagination,
NSpin,
} from "naive-ui";
import {useReportStore} from "../../../Stores/report.js";
import {computed, h, onMounted, ref, watch} from "vue";
import { VueDraggableNext } from 'vue-draggable-next'
import {computed, h, ref, watch} from "vue";
import {storeToRefs} from "pinia";
import {TbGripVertical, TbEye, TbExternalLink} from "vue-icons-plus/tb";
import {TbEye, TbExternalLink, TbPencil} from "vue-icons-plus/tb";
import MoveModalComment from "./MoveModalComment.vue";
import OperationInfoModal from "./OperationInfoModal.vue";
import ManualPatientOutcomeModal from "./ManualPatientOutcomeModal.vue";
import ManualPatientLinkModal from "./ManualPatientLinkModal.vue";
import ManualPatientEditModal from "./ManualPatientEditModal.vue";
import ManualPatientOperationsModal from "./ManualPatientOperationsModal.vue";
import {useDebounceFn} from "@vueuse/core";
const props = defineProps({
id: {
@@ -19,7 +38,7 @@ const props = defineProps({
},
keys: {
type: Array,
default: ['num', 'fullname', 'age', 'birth_date']
default: ['num', 'fullname', 'age', 'birth_date', 'admitted_at']
},
status: {
type: String,
@@ -41,34 +60,54 @@ const props = defineProps({
type: Array,
default: []
},
enabled: {
type: Boolean,
default: true,
},
})
const isFillableMode = computed(() => props.mode.toLowerCase() === 'fillable')
const isReadonlyMode = computed(() => props.mode.toLowerCase() === 'readonly')
const baseStatus = computed(() => props.status.replace(/^(mis|special)-/, ''))
const isSpecialStatus = computed(() => props.status.startsWith('special-'))
const tableRef = ref()
const emit = defineEmits(['item-dragged', 'item-dropped'])
const reportStore = useReportStore()
const {patientsData} = storeToRefs(reportStore)
// Получаем базовые колонки
const baseColumns = reportStore.getColumnsByKey(props.keys)
const data = ref([])
const isLoading = ref(true)
const {patientsData, statusStates} = storeToRefs(reportStore)
const showMoveModal = ref(false)
const showOperationInfoModal = ref(false)
const showManualOutcomeModal = ref(false)
const showManualLinkModal = ref(false)
const showManualEditModal = ref(false)
const showManualOperationsModal = ref(false)
const currentHistory = ref(null)
const latestDropItem = ref(null)
const activePatient = ref(null)
const hasDisabledEdit = computed(() => {
return reportStore.reportInfo.report.isActiveSendButton === false
return !Boolean(reportStore.reportInfo?.report?.isActiveSendButton)
})
const canEditSpecial = computed(() => isSpecialStatus.value && !hasDisabledEdit.value)
const statusState = computed(() => statusStates.value[props.status] ?? {
page: 1,
perPage: 20,
total: 0,
search: '',
loading: false,
loaded: false,
})
const searchValue = ref('')
const isObservationStatus = computed(() => baseStatus.value === 'observation')
const showPagination = computed(() => !isObservationStatus.value)
// Добавляем drag колонку если режим fillable
const columns = computed(() => {
// if (!isFillableMode.value) return baseColumns
const resolvedBaseColumns = reportStore.getColumnsByKey(props.keys)
.filter(Boolean)
.map((column) => ({ ...column }))
const newColumns = []
@@ -76,28 +115,86 @@ const columns = computed(() => {
title: '',
key: 'goToMis',
width: 40,
render: (row) => h(
NTooltip,
{},
{
trigger: () => h('div',
{
style: {
cursor: 'hand',
textAlign: 'center',
userSelect: 'none'
},
onClick: () => {
window.open(`https://stationar.amurzdrav.ru/prod/statist/edit/card/${row.id}`, '_blank')
render: (row) => {
const actions = []
if (canEditSpecial.value && row?.department_patient_id) {
actions.push(
h(
NTooltip,
{},
{
trigger: () => h(
'div',
{
style: {
cursor: 'pointer',
textAlign: 'center',
userSelect: 'none',
color: '#6b7280',
display: 'inline-flex',
},
onClick: () => {
activePatient.value = row
showManualEditModal.value = true
},
},
[h(NIcon, { depth: 3, size: 16 }, h(TbPencil))]
),
default: () => 'Редактировать',
}
},
[
h(NIcon, { depth: 2 }, h(TbExternalLink))
]
),
default: () => 'Перейти в карту'
)
)
}
)
if (row.medical_history_id) {
actions.push(
h(
NTooltip,
{},
{
trigger: () => h(
'div',
{
style: {
cursor: 'pointer',
textAlign: 'center',
userSelect: 'none',
display: 'inline-flex',
},
onClick: () => {
window.open(`https://stationar.amurzdrav.ru/prod/statist/edit/card/${row.medical_history_id}`, '_blank')
}
},
[h(NIcon, { depth: 2 }, h(TbExternalLink))]
),
default: () => 'Перейти в карту'
}
)
)
}
if (!actions.length) {
return null
}
if (actions.length === 1) {
return actions[0]
}
return h(
'div',
{
style: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '6px',
},
},
actions
)
}
}
const removeColumn = {
@@ -108,17 +205,14 @@ const columns = computed(() => {
{
text: true,
disabled: hasDisabledEdit.value,
onClick: () => {
axios.post('/api/report/observation/remove', {
id: row.id
}).then(async () => {
const indexRemove = patientsData.value['observation'].findIndex(itm => itm.id === row.id)
if (indexRemove !== -1) {
patientsData.value['observation'].splice(indexRemove, 1)
}
})
}
},
onClick: () => {
axios.post('/api/report/observation/remove', {
id: row.id
}).then(async () => {
reportStore.removeObservationPatientLocally(row.id)
})
}
},
[
'Снять с наблюдения'
]
@@ -157,13 +251,13 @@ const columns = computed(() => {
)
}
if (props.status === 'observation') {
if (baseStatus.value === 'observation') {
newColumns.push(expandColumn)
newColumns.push(fillableColumn)
}
newColumns.push(dragColumn)
newColumns.push(...baseColumns)
newColumns.push(...resolvedBaseColumns)
newColumns.push({
title: 'Диагноз',
key: 'ds',
@@ -196,11 +290,30 @@ const columns = computed(() => {
})
if (props.isRemovable) newColumns.push(removeColumn)
if (props.status === 'emergency' || props.status === 'plan') {
if (baseStatus.value === 'emergency' || baseStatus.value === 'plan') {
const operationColumn = {
title: 'Операции',
key: 'operations',
render: (row) => row.operations?.length ?
render: (row) => canEditSpecial.value && row?.department_patient_id
? h(
'div',
{
class: 'underline decoration-dashed cursor-pointer',
style: 'padding: 8px;',
onClick: () => {
activePatient.value = row
showManualOperationsModal.value = true
},
},
row.operations?.length
? h(NEllipsis, {tooltip: false}, row.operations.map(itm => `${itm.code}; `).join(''))
: 'Добавить'
)
: isSpecialStatus.value
? (row.operations?.length
? h('div', {style: 'padding: 8px;'}, h(NEllipsis, {tooltip: false}, row.operations.map(itm => `${itm.code}; `).join('')))
: h('div', {style: 'padding: 8px;'}, '-'))
: row.operations?.length ?
h(
NPopover,
{
@@ -214,7 +327,7 @@ const columns = computed(() => {
class: 'underline decoration-dashed cursor-pointer',
style: 'padding: 8px;',
onClick: () => {
currentHistory.value = row.id
currentHistory.value = row.medical_history_id
showOperationInfoModal.value = true
},
},
@@ -233,7 +346,7 @@ const columns = computed(() => {
newColumns.push(operationColumn)
}
if (props.status === 'outcome') {
if (baseStatus.value === 'outcome') {
const typeColumn = {
title: 'Причина',
key: 'outcome_type',
@@ -325,21 +438,6 @@ const handleDrop = (e) => {
}
}
const fetchPatients = async () => {
isLoading.value = true
const data = {
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)
}).finally(() => {
isLoading.value = false
})
}
function rowProps(row) {
const style = []
const classes = []
@@ -374,29 +472,70 @@ function rowProps(row) {
}
}
onMounted(async () => {
await fetchPatients()
})
const applySearch = useDebounceFn(async () => {
await reportStore.setStatusSearch(props.status, searchValue.value)
}, 350)
const handlePageChange = async (page) => {
await reportStore.setStatusPage(props.status, page)
}
watch(() => props.enabled, async (enabled) => {
if (enabled) {
await reportStore.ensureStatusLoaded(props.status)
}
}, { immediate: true })
watch(() => reportStore.reportInfo?.dates, async () => {
searchValue.value = ''
if (props.enabled) {
await reportStore.loadPatientsByStatus(props.status, { resetPage: true })
}
}, { deep: true })
</script>
<template>
<div>
<NDataTable :columns="columns"
ref="tableRef"
:data="patientsData[status]"
size="small"
@drop="handleDrop"
@dragover="handleDragOver"
:loading="isLoading"
max-height="200"
min-height="200"
:row-props="rowProps"
:row-key="(row, index) => row.id"
class="text-sm!">
</NDataTable>
<NFlex vertical :size="8">
<NInput
v-model:value="searchValue"
clearable
size="small"
placeholder="Поиск по ФИО или диагнозу"
@update:value="applySearch"
/>
<NSpin :show="statusState.loading">
<NDataTable :columns="columns"
ref="tableRef"
:data="patientsData[status]"
size="small"
@drop="handleDrop"
@dragover="handleDragOver"
max-height="200"
min-height="200"
:row-props="rowProps"
:row-key="(row, index) => row.id"
class="text-sm!">
</NDataTable>
</NSpin>
<NPagination
v-if="showPagination"
:page="statusState.page"
:page-size="statusState.perPage"
:item-count="statusState.total"
:page-slot="7"
@update:page="handlePageChange"
/>
</NFlex>
<MoveModalComment v-model:show="showMoveModal" :patient-id="latestDropItem?.id" />
<OperationInfoModal v-model:show="showOperationInfoModal" :history-id="currentHistory" />
<ManualPatientOutcomeModal v-model:show="showManualOutcomeModal" :patient="activePatient" />
<ManualPatientLinkModal v-model:show="showManualLinkModal" :patient="activePatient" />
<ManualPatientEditModal v-model:show="showManualEditModal" :patient="activePatient" :source-status="status" />
<ManualPatientOperationsModal v-model:show="showManualOperationsModal" :patient="activePatient" :source-status="status" />
</div>
</template>