Добавлено сохранение правок поверх МИС

Добавлено версионирование сохраненных правок
Добавлено сохранение отчета мед. сестры
This commit is contained in:
brusnitsyn
2026-05-04 22:23:21 +09:00
parent 7a58812072
commit 82673f385b
9 changed files with 241 additions and 23 deletions

View File

@@ -4,14 +4,16 @@ namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\MedicalHistory; use App\Models\MedicalHistory;
use App\Models\MedicalHistoryCorrection;
use App\Models\MedicalHistoryNurse; use App\Models\MedicalHistoryNurse;
use App\Models\UnifiedMedicalHistory;
use Illuminate\Http\Request; use Illuminate\Http\Request;
class NurseController extends Controller class NurseController extends Controller
{ {
public function getPatient($id, Request $request) public function getPatient($id, Request $request)
{ {
return MedicalHistory::where('id', $id)->first(); return UnifiedMedicalHistory::where('id', $id)->first();
} }
public function searchPatients(Request $request) public function searchPatients(Request $request)
@@ -54,4 +56,31 @@ class NurseController extends Controller
'data' => $result, 'data' => $result,
], 201); ], 201);
} }
public function storeCorrection($id, Request $request)
{
$data = $request->validate([
'medical_card_number' => 'nullable',
'full_name' => 'nullable',
'birth_date' => 'nullable',
'recipient_date' => 'nullable',
'extract_date' => 'nullable',
'death_date' => 'nullable',
'male' => 'nullable',
'urgency_id' => 'nullable',
'hospital_result_id' => 'nullable',
'visit_result_id' => 'nullable',
'mis_user_id' => 'nullable',
'comment' => 'nullable',
]);
$data['medical_history_id'] = $id;
$data['user_id'] = auth()->user()->id;
$result = MedicalHistoryCorrection::create($data);
return response()->json([
'data' => $result,
], 201);
}
} }

View File

@@ -5,7 +5,7 @@ namespace App\Http\Controllers\Web;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\Department; use App\Models\Department;
use App\Services\DateRangeService; use App\Services\DateRangeService;
use App\Services\MedicalHistoryService; use App\Services\UnifiedMedicalHistoryService;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Inertia\Inertia; use Inertia\Inertia;
@@ -14,7 +14,7 @@ class NurseReportController extends Controller
{ {
public function __construct( public function __construct(
protected DateRangeService $dateRangeService, protected DateRangeService $dateRangeService,
protected MedicalHistoryService $medicalHistoryService protected UnifiedMedicalHistoryService $unifiedMedicalHistoryService
) )
{} {}
@@ -30,11 +30,11 @@ class NurseReportController extends Controller
$department = Department::where('department_id', $departmentId)->firstOrFail(); $department = Department::where('department_id', $departmentId)->firstOrFail();
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user); $dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
$inDepartmentHistories = $this->medicalHistoryService->getDepartmentHistories($dateRange, $department->rf_mis_department_id); $inDepartmentHistories = $this->unifiedMedicalHistoryService->getDepartmentHistories($dateRange, $department->rf_mis_department_id);
$recipientHistories = $this->medicalHistoryService->getRecipientHistories($dateRange, $department->rf_mis_department_id); $recipientHistories = $this->unifiedMedicalHistoryService->getRecipientHistories($dateRange, $department->rf_mis_department_id);
$dischargedHistories = $this->medicalHistoryService->getDischargedHistories($dateRange, $department->rf_mis_department_id); $dischargedHistories = $this->unifiedMedicalHistoryService->getDischargedHistories($dateRange, $department->rf_mis_department_id);
$deceasedHistories = $this->medicalHistoryService->getDeceasedHistories($dateRange, $department->rf_mis_department_id); $deceasedHistories = $this->unifiedMedicalHistoryService->getDeceasedHistories($dateRange, $department->rf_mis_department_id);
$transferredHistories = $this->medicalHistoryService->getTransferredHistories($dateRange, $department->rf_mis_department_id); $transferredHistories = $this->unifiedMedicalHistoryService->getTransferredHistories($dateRange, $department->rf_mis_department_id);
return Inertia::render('Nurse/Report/Index', [ return Inertia::render('Nurse/Report/Index', [
'inDepartmentHistories' => $inDepartmentHistories, 'inDepartmentHistories' => $inDepartmentHistories,

View File

@@ -18,6 +18,9 @@ class MedicalHistoryCorrection extends Model
'urgency_id', 'urgency_id',
'hospital_result_id', 'hospital_result_id',
'visit_result_id', 'visit_result_id',
'user_id',
'mis_user_id',
'comment',
]; ];
protected $casts = [ protected $casts = [

View File

@@ -6,7 +6,7 @@ use Illuminate\Database\Eloquent\Model;
class UnifiedMedicalHistory extends MaterializedViewModel class UnifiedMedicalHistory extends MaterializedViewModel
{ {
protected $table = 'v_unified_medical_history'; protected $table = 'v_unified_medical_history_nurse';
protected $primaryKey = 'id'; protected $primaryKey = 'id';
protected $casts = [ protected $casts = [
@@ -16,4 +16,31 @@ class UnifiedMedicalHistory extends MaterializedViewModel
'death_date' => 'datetime', 'death_date' => 'datetime',
'male' => 'boolean', 'male' => 'boolean',
]; ];
public function migrations(): \Illuminate\Database\Eloquent\Relations\HasMany|MedicalHistory
{
return $this->hasMany(MigrationPatient::class, 'medical_history_id', 'id');
}
public function operations(): \Illuminate\Database\Eloquent\Relations\HasMany|MedicalHistory
{
return $this->hasMany(SurgicalOperation::class, 'medical_history_id', 'id');
}
public function latestMigration()
{
return $this->hasOne(MigrationPatient::class, 'medical_history_id', 'id')
->latest('ingoing_date');
}
public function operationsInDepartment($query, $departmentId)
{
return $this->operations()->where('department_id', $departmentId);
}
// Скоупы
public function scopeUrgency($query, $typeId) // 1 = Экстренно, 2 = Планово
{
return $query->where('urgency_id', $typeId);
}
} }

View File

@@ -0,0 +1,118 @@
<?php
namespace App\Services;
use App\Models\MedicalHistory;
use App\Models\MigrationPatient;
use App\Models\UnifiedMedicalHistory;
use Illuminate\Support\Carbon;
class UnifiedMedicalHistoryService
{
public function getHistories(DateRange $dateRange, int $departmentId)
{
$query = UnifiedMedicalHistory::query();
$query->where('recipient_date', '>=', $dateRange->startSql())
->where('recipient_date', '<', $dateRange->endSql())
// 1. Оставляем только тех пациентов, у которых БЫЛО движение в этом отделении
->whereHas('latestMigration', fn($q) => $q->where('department_id', $departmentId))
// 2. Загружаем ТОЛЬКО последнее движение в этом отделении (не все миграции)
->with(['latestMigration' => fn($q) => $q->where('department_id', $departmentId)]);
$result = $query->paginate();
return $result;
}
public function getUrgencyHistory(DateRange $dateRange, int $departmentId, int $urgencyId)
{
$query = UnifiedMedicalHistory::query();
$query->where('recipient_date', '>=', $dateRange->startSql())
->where('recipient_date', '<', $dateRange->endSql())
->urgency($urgencyId)
->whereHas('migrations', function ($m) use ($departmentId) {
$m->where('department_id', $departmentId);
})
->with([
'migrations' => fn ($m) => $m->where('department_id', $departmentId),
'migrations.operations'
]);
$result = $query->paginate();
return $result;
}
public function getDepartmentHistories(DateRange $dateRange, int $departmentId)
{
return UnifiedMedicalHistory::query()
->whereHas('migrations', function ($q) use ($departmentId, $dateRange) {
$q->department($departmentId)->current($dateRange);
})
->with(['latestMigration' => function ($q) use ($departmentId, $dateRange) {
$q->department($departmentId)->current($dateRange); // подгружаем только отфильтрованные движения
}])
->get()
// Сортировка по дате поступления в отделение (поле дочерней таблицы)
->sortByDesc(fn ($mh) => $mh->latestMigration->ingoing_date ?? $mh->recipient_date)
->values();
}
/**
* Получить карты поступившие сегодня
* @param DateRange $dateRange
* @param int $departmentId
*/
public function getRecipientHistories(DateRange $dateRange, int $departmentId)
{
$now = Carbon::now();
return UnifiedMedicalHistory::query()
->whereHas('migrations', function ($q) use ($departmentId, $dateRange) {
$q->department($departmentId)->admitted($dateRange->startSql(), $dateRange->endSql());
})
->with(['latestMigration' => function ($q) use ($departmentId) {
$q->department($departmentId);
}])
->get();
}
public function getDischargedHistories(DateRange $dateRange, int $departmentId)
{
return UnifiedMedicalHistory::query()
->whereHas('migrations', function ($q) use ($departmentId, $dateRange) {
$q->department($departmentId)->discharged($dateRange->startSql(), $dateRange->endSql());
})
->with(['latestMigration' => function ($q) use ($departmentId) {
$q->department($departmentId);
}])
->get();
}
public function getDeceasedHistories(DateRange $dateRange, int $departmentId)
{
return UnifiedMedicalHistory::query()
->whereHas('migrations', function ($q) use ($departmentId, $dateRange) {
$q->department($departmentId)->deceased($dateRange->startSql(), $dateRange->endSql());
})
->with(['latestMigration' => function ($q) use ($departmentId) {
$q->department($departmentId);
}])
->get();
}
public function getTransferredHistories(DateRange $dateRange, int $departmentId)
{
return UnifiedMedicalHistory::query()
->whereHas('migrations', function ($q) use ($departmentId, $dateRange) {
$q->department($departmentId)->transferred($dateRange->startSql(), $dateRange->endSql());
})
->with(['latestMigration' => function ($q) use ($departmentId) {
$q->department($departmentId);
}])
->get();
}
}

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('medical_history_corrections', function (Blueprint $table) {
$table->integer('mis_user_id')->nullable()->change();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('medical_history_corrections', function (Blueprint $table) {
$table->integer('mis_user_id')->change();
});
}
};

View File

@@ -64,13 +64,18 @@ const visitResultOptions = [
] ]
const submit = () => { const submit = () => {
axios.post(`/api/nurse/patients/${form.value.patient_id}/correction`, {
...form.value
}).then(res => {
console.log(res)
})
} }
const fetchPatient = async (historyId) => { const fetchPatient = async (historyId) => {
loading.value = true loading.value = true
await axios.get(`/api/nurse/patients/${historyId}`) await axios.get(`/api/nurse/patients/${historyId}`)
.then(res => { .then(res => {
form.value.patient_id = historyId
form.value.full_name = res.data.full_name form.value.full_name = res.data.full_name
form.value.urgency_id = res.data.urgency_id form.value.urgency_id = res.data.urgency_id
form.value.visit_result_id = res.data.visit_result_id form.value.visit_result_id = res.data.visit_result_id

View File

@@ -5,8 +5,8 @@ import AppContainer from "../../../Components/AppContainer.vue";
import AppPanel from "../../../Components/AppPanel.vue"; import AppPanel from "../../../Components/AppPanel.vue";
import DatePickerQuery from "../../../Components/DatePickerQuery.vue"; import DatePickerQuery from "../../../Components/DatePickerQuery.vue";
import UrgencyBadge from "../../../Components/UrgencyBadge.vue"; import UrgencyBadge from "../../../Components/UrgencyBadge.vue";
import {computed, h, ref, shallowRef} from "vue" import {h, ref, shallowRef} from "vue"
import {TbArrowMoveRight, TbArrowMoveUp, TbEdit, TbCirclePlus, TbPencil} from 'vue-icons-plus/tb' import {TbCirclePlus, TbPencil} from 'vue-icons-plus/tb'
import {useAuthStore} from "../../../Stores/auth.js"; import {useAuthStore} from "../../../Stores/auth.js";
import AddMedicalHistoryModal from "../Components/AddMedicalHistoryModal.vue"; import AddMedicalHistoryModal from "../Components/AddMedicalHistoryModal.vue";
import EditMedicalHistoryModal from "../Components/EditMedicalHistoryModal.vue"; import EditMedicalHistoryModal from "../Components/EditMedicalHistoryModal.vue";
@@ -84,6 +84,10 @@ const onClickEditButton = (historyId) => {
editHistoryId.value = historyId editHistoryId.value = historyId
} }
const submit = () => {
// TODO: сохранение отчета и пациентов
}
const formattedLabel = (word, count) => { const formattedLabel = (word, count) => {
return `${word} (${count})` return `${word} (${count})`
} }
@@ -97,7 +101,7 @@ const formattedLabel = (word, count) => {
<NTag type="info" :bordered="false"> <NTag type="info" :bordered="false">
{{ userDepartment.name_full }} {{ userDepartment.name_full }}
</NTag> </NTag>
<DatePickerQuery :date="dates" /> <DatePickerQuery :date="dates" class="text-lg!" />
</NFlex> </NFlex>
</AppPanel> </AppPanel>
<AppPanel header="Пациенты в отделении" header-include-body> <AppPanel header="Пациенты в отделении" header-include-body>
@@ -116,44 +120,47 @@ const formattedLabel = (word, count) => {
<NDataTable :columns="columns" <NDataTable :columns="columns"
:data="inDepartmentHistories" :data="inDepartmentHistories"
table-layout="fixed" table-layout="fixed"
max-height="calc(100vh - 375px)" max-height="calc(100vh - 435px)"
min-height="calc(100vh - 375px)" min-height="calc(100vh - 435px)"
/> />
</NTabPane> </NTabPane>
<NTabPane name="income" :tab="formattedLabel('Поступившие', recipientHistories.length)"> <NTabPane name="income" :tab="formattedLabel('Поступившие', recipientHistories.length)">
<NDataTable :columns="columns" <NDataTable :columns="columns"
:data="recipientHistories" :data="recipientHistories"
table-layout="fixed" table-layout="fixed"
max-height="calc(100vh - 375px)" max-height="calc(100vh - 435px)"
min-height="calc(100vh - 375px)" min-height="calc(100vh - 435px)"
/> />
</NTabPane> </NTabPane>
<NTabPane name="outcome" :tab="formattedLabel('Выписанные', dischargedHistories.length)"> <NTabPane name="outcome" :tab="formattedLabel('Выписанные', dischargedHistories.length)">
<NDataTable :columns="columns" <NDataTable :columns="columns"
:data="dischargedHistories" :data="dischargedHistories"
table-layout="fixed" table-layout="fixed"
max-height="calc(100vh - 375px)" max-height="calc(100vh - 435px)"
min-height="calc(100vh - 375px)" min-height="calc(100vh - 435px)"
/> />
</NTabPane> </NTabPane>
<NTabPane name="dead" :tab="formattedLabel('Умершие', deceasedHistories.length)"> <NTabPane name="dead" :tab="formattedLabel('Умершие', deceasedHistories.length)">
<NDataTable :columns="columns" <NDataTable :columns="columns"
:data="deceasedHistories" :data="deceasedHistories"
table-layout="fixed" table-layout="fixed"
max-height="calc(100vh - 375px)" max-height="calc(100vh - 435px)"
min-height="calc(100vh - 375px)" min-height="calc(100vh - 435px)"
/> />
</NTabPane> </NTabPane>
<NTabPane name="transfer" :tab="formattedLabel('Переведенные', transferredHistories.length)"> <NTabPane name="transfer" :tab="formattedLabel('Переведенные', transferredHistories.length)">
<NDataTable :columns="columns" <NDataTable :columns="columns"
:data="transferredHistories" :data="transferredHistories"
table-layout="fixed" table-layout="fixed"
max-height="calc(100vh - 375px)" max-height="calc(100vh - 435px)"
min-height="calc(100vh - 375px)" min-height="calc(100vh - 435px)"
/> />
</NTabPane> </NTabPane>
</NTabs> </NTabs>
</AppPanel> </AppPanel>
<NButton secondary size="large">
Сохранить отчет
</NButton>
</AppContainer> </AppContainer>
<AddMedicalHistoryModal v-model:show="showAddMedicalHistoryModal" /> <AddMedicalHistoryModal v-model:show="showAddMedicalHistoryModal" />
<EditMedicalHistoryModal v-model:show="showEditMedicalHistoryModal" :history-id="editHistoryId" /> <EditMedicalHistoryModal v-model:show="showEditMedicalHistoryModal" :history-id="editHistoryId" />

View File

@@ -96,6 +96,7 @@ Route::middleware(['auth:sanctum'])->group(function () {
Route::prefix('patients')->group(function () { Route::prefix('patients')->group(function () {
Route::prefix('{id}')->group(function () { Route::prefix('{id}')->group(function () {
Route::get('/', [\App\Http\Controllers\Api\NurseController::class, 'getPatient']); Route::get('/', [\App\Http\Controllers\Api\NurseController::class, 'getPatient']);
Route::post('/correction', [\App\Http\Controllers\Api\NurseController::class, 'storeCorrection']);
}); });
Route::post('/search', [\App\Http\Controllers\Api\NurseController::class, 'searchPatients']); Route::post('/search', [\App\Http\Controllers\Api\NurseController::class, 'searchPatients']);
Route::post('/', [\App\Http\Controllers\Api\NurseController::class, 'storePatient']); Route::post('/', [\App\Http\Controllers\Api\NurseController::class, 'storePatient']);