Добавил строку с итогами
Рефакторинг контроллера статистики
This commit is contained in:
@@ -10,21 +10,21 @@ use App\Models\MetrikaItem;
|
|||||||
use App\Models\MetrikaResult;
|
use App\Models\MetrikaResult;
|
||||||
use App\Models\Report;
|
use App\Models\Report;
|
||||||
use App\Services\DateRangeService;
|
use App\Services\DateRangeService;
|
||||||
|
use App\Services\StatisticsService;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
use Inertia\Inertia;
|
use Inertia\Inertia;
|
||||||
|
|
||||||
class StatisticController extends Controller
|
class StatisticController extends Controller
|
||||||
{
|
{
|
||||||
protected DateRangeService $dateService;
|
public function __construct(
|
||||||
|
protected DateRangeService $dateService,
|
||||||
public function __construct(DateRangeService $dateRangeService)
|
protected StatisticsService $statisticsService
|
||||||
{
|
) { }
|
||||||
$this->dateService = $dateRangeService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function index(Request $request)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
@@ -32,104 +32,14 @@ class StatisticController extends Controller
|
|||||||
|
|
||||||
$queryStartDate = $request->query('startAt');
|
$queryStartDate = $request->query('startAt');
|
||||||
$queryEndDate = $request->query('endAt');
|
$queryEndDate = $request->query('endAt');
|
||||||
[$startDate, $endDate] = $this->dateService->getDateRangeForUser($user, $queryStartDate, $queryEndDate);
|
[$startDate, $endDate] = $this->dateService->getStatisticsDateRange($user, $queryStartDate, $queryEndDate);
|
||||||
$isRangeOneDay = $this->dateService->isRangeOneDay($startDate, $endDate);
|
$isRangeOneDay = $this->dateService->isRangeOneDay($startDate, $endDate);
|
||||||
|
|
||||||
// Если диапазон содержит сутки
|
// Генерируем ключ кэша на основе параметров запроса
|
||||||
if ($isRangeOneDay) {
|
// $cacheKey = $this->generateCacheKey($user, $startDate, $endDate, $isRangeOneDay);
|
||||||
// Устанавливаем дату отчета, как последний день из выборки
|
|
||||||
$dateReport = $endDate;
|
|
||||||
} else {
|
|
||||||
// Устанавливаем дату отчета, как выборку
|
|
||||||
$dateReport = [$startDate, $endDate];
|
|
||||||
}
|
|
||||||
|
|
||||||
$groupedData = [];
|
// Получаем данные из кэша или вычисляем
|
||||||
|
$finalData = $this->statisticsService->getStatisticsData($user, $startDate, $endDate, $isRangeOneDay);
|
||||||
$departments = Department::select('department_id', 'rf_department_type', 'name_short')
|
|
||||||
->with(['reports'])
|
|
||||||
->orderBy('name_short')->get();
|
|
||||||
|
|
||||||
foreach ($departments as $department) {
|
|
||||||
$departmentType = $department->departmentType->name_full;
|
|
||||||
|
|
||||||
if (!isset($groupedData[$departmentType])) {
|
|
||||||
$groupedData[$departmentType] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($isRangeOneDay) {
|
|
||||||
// Статистика выводится с нарастающим числом
|
|
||||||
$query = $department->reports();
|
|
||||||
$reports = $query->whereDate('created_at', $dateReport)
|
|
||||||
->get();
|
|
||||||
$lastReport = $query->whereDate('created_at', $dateReport)->first();
|
|
||||||
} else {
|
|
||||||
$query = $department->reports();
|
|
||||||
$reports = $query->clone()->whereBetween('created_at', $dateReport)
|
|
||||||
->get();
|
|
||||||
$lastReport = $query->clone()->whereDate('created_at', $dateReport[1])->first();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Метрики зависищие от отчетов
|
|
||||||
$allCount = 0; $outcomeCount = 0; $currentCount = 0; $planCount = 0;
|
|
||||||
$emergencyCount = 0; $planSurgical = 0; $emergencySurgical = 0; $transferredCount = 0;
|
|
||||||
$deceasedCount = 0;
|
|
||||||
$currentCount = $lastReport ? $this->getMetrikaResultFromReport($lastReport, 8, false) : 0; // Состоит
|
|
||||||
foreach ($reports as $report) {
|
|
||||||
$planCount += $this->getMetrikaResultFromReport($report, 4, $isRangeOneDay); // Поступление - Планово
|
|
||||||
$emergencyCount += $this->getMetrikaResultFromReport($report, 12, $isRangeOneDay); // Поступление - Экстренно
|
|
||||||
$planSurgical += $this->getMetrikaResultFromReport($report, 11, $isRangeOneDay); // Операции - Планово
|
|
||||||
$emergencySurgical += $this->getMetrikaResultFromReport($report, 10, $isRangeOneDay); // Операции - Экстренно
|
|
||||||
$transferredCount += $this->getMetrikaResultFromReport($report, 13, $isRangeOneDay); // Поступление - Перевод
|
|
||||||
$outcomeCount += $this->getMetrikaResultFromReport($report, 7, $isRangeOneDay); // Выбыло
|
|
||||||
$deceasedCount += $this->getMetrikaResultFromReport($report, 9, $isRangeOneDay); // Умерло
|
|
||||||
}
|
|
||||||
|
|
||||||
$allCount = $planCount + $emergencyCount; // Поступило
|
|
||||||
|
|
||||||
// Независимые метрики (установки по умолчанию и т.п.)
|
|
||||||
$bedsCount = $department->metrikaDefault()
|
|
||||||
->where('rf_metrika_item_id', 1)->value('value');
|
|
||||||
|
|
||||||
$percentLoadedBeds = $bedsCount > 0 ? round($currentCount * 100 / $bedsCount) : 0;
|
|
||||||
|
|
||||||
$groupedData[$departmentType][] = [
|
|
||||||
'department' => $department->name_short,
|
|
||||||
'beds' => $bedsCount,
|
|
||||||
'recipients' => [
|
|
||||||
'all' => $allCount,
|
|
||||||
'plan' => $planCount,
|
|
||||||
'emergency' => $emergencyCount,
|
|
||||||
'transferred' => $transferredCount,
|
|
||||||
],
|
|
||||||
'outcome' => $outcomeCount,
|
|
||||||
'consist' => $currentCount,
|
|
||||||
'percentLoadedBeds' => $percentLoadedBeds,
|
|
||||||
'surgical' => [
|
|
||||||
'plan' => $planSurgical,
|
|
||||||
'emergency' => $emergencySurgical
|
|
||||||
],
|
|
||||||
'deceased' => $deceasedCount,
|
|
||||||
'type' => $departmentType
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Преобразуем группированные данные в плоский массив с заголовками групп
|
|
||||||
$finalData = [];
|
|
||||||
foreach ($groupedData as $type => $departmentsInType) {
|
|
||||||
// Добавляем строку-заголовок группы
|
|
||||||
$finalData[] = [
|
|
||||||
'isGroupHeader' => true,
|
|
||||||
'groupName' => $type,
|
|
||||||
'colspan' => 12, // Количество колонок в таблице
|
|
||||||
'type' => $type
|
|
||||||
];
|
|
||||||
|
|
||||||
// Добавляем отделения этой группы
|
|
||||||
foreach ($departmentsInType as $department) {
|
|
||||||
$finalData[] = $department;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$isHeadOrAdmin = $user->isAdmin() || $user->isHeadOfDepartment();
|
$isHeadOrAdmin = $user->isAdmin() || $user->isHeadOfDepartment();
|
||||||
$date = $isHeadOrAdmin ? [
|
$date = $isHeadOrAdmin ? [
|
||||||
@@ -138,160 +48,12 @@ class StatisticController extends Controller
|
|||||||
] : $this->dateService->parseDate($endDate)->getTimestampMs();
|
] : $this->dateService->parseDate($endDate)->getTimestampMs();
|
||||||
|
|
||||||
return Inertia::render('Statistic/Index', [
|
return Inertia::render('Statistic/Index', [
|
||||||
'data' => $finalData,
|
'data' => $finalData['data'],
|
||||||
|
'totalsByType' => $finalData['totalsByType'],
|
||||||
|
'grandTotals' => $finalData['grandTotals'],
|
||||||
'isHeadOrAdmin' => $isHeadOrAdmin,
|
'isHeadOrAdmin' => $isHeadOrAdmin,
|
||||||
'date' => $date,
|
'date' => $date,
|
||||||
'isOneDay' => $isRangeOneDay
|
'isOneDay' => $isRangeOneDay,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getMetrikaResultFromReport(Report $report, int $metrikaItem, bool $sum = true)
|
|
||||||
{
|
|
||||||
if ($sum) {
|
|
||||||
return (int) ($report->metrikaResults()
|
|
||||||
->where('rf_metrika_item_id', $metrikaItem)
|
|
||||||
->sum(DB::raw('CAST(value AS INTEGER)')) ?: 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (int) ($report->metrikaResults()
|
|
||||||
->where('rf_metrika_item_id', $metrikaItem)
|
|
||||||
->value('value') ?: 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function indexOld(Request $request)
|
|
||||||
{
|
|
||||||
$user = Auth::user();
|
|
||||||
|
|
||||||
$validator = Validator::make($request->all(), [
|
|
||||||
'sent_at' => 'required|string'
|
|
||||||
]);
|
|
||||||
|
|
||||||
$groupId = (int)$request->query('groupId');
|
|
||||||
|
|
||||||
if ($validator->fails()) {
|
|
||||||
return response()->json([
|
|
||||||
'success' => false,
|
|
||||||
'errors' => $validator->errors()
|
|
||||||
], 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
$timestamps = explode(',', $request->sent_at);
|
|
||||||
$startAt = intval($timestamps[0] / 1000);
|
|
||||||
$endAt = intval($timestamps[1] / 1000);
|
|
||||||
|
|
||||||
// Проверяем период (максимум 1 год)
|
|
||||||
$daysDiff = ($endAt - $startAt) / (60 * 60 * 24);
|
|
||||||
if ($daysDiff > 365) {
|
|
||||||
return response()->json([
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'Период не может превышать 1 год'
|
|
||||||
], 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
$dateStart = date('Y-m-d', $startAt);
|
|
||||||
$dateEnd = date('Y-m-d', $endAt);
|
|
||||||
|
|
||||||
$group = MetrikaGroup::findOrFail($groupId);
|
|
||||||
|
|
||||||
// Оптимизированный агрегированный запрос
|
|
||||||
$aggregatedData = DB::table('metrika_results as mr')
|
|
||||||
->join('metrika_result_values as mv', 'mr.metrika_result_id', '=', 'mv.rf_metrika_result_id')
|
|
||||||
->join('reports as r', 'mr.rf_report_id', '=', 'r.report_id')
|
|
||||||
->where('mr.rf_metrika_group_id', $groupId)
|
|
||||||
->whereBetween('r.sent_at', [$dateStart, $dateEnd])
|
|
||||||
->when(!$user->isAdmin() && !$user->isHeadOfDepartment(), function ($query) use ($user) {
|
|
||||||
return $query->where('r.rf_user_id', $user->id);
|
|
||||||
})
|
|
||||||
->select([
|
|
||||||
'mv.rf_metrika_item_id',
|
|
||||||
DB::raw('SUM(CAST(mv.value AS DECIMAL(10,2))) as total_sum'),
|
|
||||||
DB::raw('COUNT(DISTINCT r.report_id) as reports_count'),
|
|
||||||
DB::raw('AVG(CAST(mv.value AS DECIMAL(10,2))) as avg_value')
|
|
||||||
])
|
|
||||||
->groupBy('mv.rf_metrika_item_id')
|
|
||||||
->get()
|
|
||||||
->keyBy('rf_metrika_item_id');
|
|
||||||
|
|
||||||
if ($aggregatedData->isEmpty()) {
|
|
||||||
return response()->json([
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'Данные за указанный период не найдены'
|
|
||||||
], 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Получаем названия метрик одним запросом
|
|
||||||
$itemIds = $aggregatedData->pluck('rf_metrika_item_id')->toArray();
|
|
||||||
$items = MetrikaItem::whereIn('metrika_item_id', $itemIds)
|
|
||||||
->pluck('name', 'metrika_item_id');
|
|
||||||
|
|
||||||
// Формируем ответ
|
|
||||||
$formValues = [];
|
|
||||||
foreach ($aggregatedData as $itemId => $data) {
|
|
||||||
$formValues["metrika_item_{$itemId}"] = [
|
|
||||||
'sum' => (float) $data->total_sum,
|
|
||||||
'average' => (float) $data->avg_value,
|
|
||||||
'reports_count' => $data->reports_count,
|
|
||||||
'item_name' => $items[$itemId] ?? 'Неизвестный показатель'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Получаем структуру формы
|
|
||||||
$formData = MetrikaForm::getFormData($groupId);
|
|
||||||
|
|
||||||
return Inertia::render('Statistic/Index', [
|
|
||||||
'is_view_only' => true,
|
|
||||||
'period' => [
|
|
||||||
'start' => $dateStart,
|
|
||||||
'end' => $dateEnd,
|
|
||||||
'days' => $daysDiff + 1
|
|
||||||
],
|
|
||||||
'group' => [
|
|
||||||
'id' => $group->metrika_group_id,
|
|
||||||
'name' => $group->name,
|
|
||||||
'description' => $group->description,
|
|
||||||
],
|
|
||||||
'metrics' => [
|
|
||||||
'total_items' => count($formValues),
|
|
||||||
'total_reports' => $aggregatedData->first()->reports_count ?? 0,
|
|
||||||
'values' => $formValues,
|
|
||||||
'aggregation' => 'sum_and_average'
|
|
||||||
],
|
|
||||||
'form' => [
|
|
||||||
'fields' => $formData,
|
|
||||||
'sections' => $this->groupFieldsBySection($formData)
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function groupFieldsBySection($fields)
|
|
||||||
{
|
|
||||||
$sections = [];
|
|
||||||
|
|
||||||
foreach ($fields as $field) {
|
|
||||||
$section = $field['section'] ?? 'general';
|
|
||||||
|
|
||||||
if (!isset($sections[$section])) {
|
|
||||||
$sections[$section] = [
|
|
||||||
'name' => $this->getSectionName($section),
|
|
||||||
'fields' => []
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$sections[$section]['fields'][] = $field;
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_values($sections);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getSectionName($section)
|
|
||||||
{
|
|
||||||
$names = [
|
|
||||||
'general' => 'Основные показатели',
|
|
||||||
'admissions' => 'Поступления',
|
|
||||||
'discharges' => 'Выписки',
|
|
||||||
'additional' => 'Дополнительная информация'
|
|
||||||
];
|
|
||||||
|
|
||||||
return $names[$section] ?? ucfirst($section);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
683
app/Services/StatisticsService.php
Normal file
683
app/Services/StatisticsService.php
Normal file
@@ -0,0 +1,683 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
use App\Models\Department;
|
||||||
|
use App\Models\Report;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
|
||||||
|
class StatisticsService
|
||||||
|
{
|
||||||
|
protected array $metricMapping = [
|
||||||
|
4 => 'plan', // Плановые поступления
|
||||||
|
12 => 'emergency', // Экстренные поступления
|
||||||
|
11 => 'plan_surgical', // Плановые операции
|
||||||
|
10 => 'emergency_surgical', // Экстренные операции
|
||||||
|
13 => 'transferred', // Переведенные
|
||||||
|
7 => 'outcome', // Выбыло
|
||||||
|
9 => 'deceased', // Умерло
|
||||||
|
8 => 'current', // Состоит
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Получить статистические данные с оптимизацией
|
||||||
|
*/
|
||||||
|
public function getStatisticsData(User $user, string $startDate, string $endDate, bool $isRangeOneDay): array
|
||||||
|
{
|
||||||
|
// Определяем порог для использования оптимизированного метода
|
||||||
|
$daysDiff = Carbon::parse($startDate)->diffInDays(Carbon::parse($endDate));
|
||||||
|
|
||||||
|
// Для диапазонов больше 30 дней используем агрегированные данные
|
||||||
|
if ($daysDiff > 30) {
|
||||||
|
return $this->getAggregatedStatistics($user, $startDate, $endDate, $isRangeOneDay);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Для диапазонов 7-30 дней используем оптимизированный метод
|
||||||
|
if ($daysDiff > 7) {
|
||||||
|
return $this->getOptimizedStatistics($user, $startDate, $endDate, $isRangeOneDay);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Для малых диапазонов используем детальный метод
|
||||||
|
return $this->getDetailedStatistics($user, $startDate, $endDate, $isRangeOneDay);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Агрегированный метод для очень больших диапазонов (больше 30 дней)
|
||||||
|
*/
|
||||||
|
private function getAggregatedStatistics(User $user, string $startDate, string $endDate, bool $isRangeOneDay): array
|
||||||
|
{
|
||||||
|
// Устанавливаем дату отчета
|
||||||
|
if ($isRangeOneDay) {
|
||||||
|
$dateReport = $endDate;
|
||||||
|
} else {
|
||||||
|
$dateReport = [$startDate, $endDate];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загружаем все отделения
|
||||||
|
$departments = Department::select('department_id', 'rf_department_type', 'name_short')
|
||||||
|
->with(['departmentType'])
|
||||||
|
->orderBy('name_short')
|
||||||
|
->get()
|
||||||
|
->keyBy('department_id');
|
||||||
|
|
||||||
|
// Загружаем метрики по умолчанию
|
||||||
|
$defaultMetrics = $this->getDefaultMetricsBatch($departments->pluck('department_id')->toArray());
|
||||||
|
|
||||||
|
// Получаем агрегированные данные по отчетам
|
||||||
|
$aggregatedData = $this->getAggregatedReportData(
|
||||||
|
$departments->pluck('department_id')->toArray(),
|
||||||
|
$dateReport,
|
||||||
|
$isRangeOneDay
|
||||||
|
);
|
||||||
|
|
||||||
|
// Получаем последние отчеты для текущих пациентов
|
||||||
|
$lastReportsData = $this->getLastReportsData(
|
||||||
|
$departments->pluck('department_id')->toArray(),
|
||||||
|
$isRangeOneDay ? $dateReport : $dateReport[1]
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->processAggregatedData(
|
||||||
|
$departments,
|
||||||
|
$defaultMetrics,
|
||||||
|
$aggregatedData,
|
||||||
|
$lastReportsData,
|
||||||
|
$isRangeOneDay
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Получить агрегированные данные по отчетам
|
||||||
|
*/
|
||||||
|
private function getAggregatedReportData(array $departmentIds, $dateReport, bool $isRangeOneDay): Collection
|
||||||
|
{
|
||||||
|
$query = DB::table('reports as r')
|
||||||
|
->join('metrika_results as mr', 'r.report_id', '=', 'mr.rf_report_id')
|
||||||
|
->select(
|
||||||
|
'r.rf_department_id',
|
||||||
|
'mr.rf_metrika_item_id',
|
||||||
|
DB::raw('SUM(CAST(mr.value AS INTEGER)) as total')
|
||||||
|
)
|
||||||
|
->whereIn('r.rf_department_id', $departmentIds);
|
||||||
|
|
||||||
|
if ($isRangeOneDay) {
|
||||||
|
$query->whereDate('r.created_at', $dateReport);
|
||||||
|
} else {
|
||||||
|
$query->whereBetween('r.created_at', $dateReport);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query->whereIn('mr.rf_metrika_item_id', array_keys($this->metricMapping))
|
||||||
|
->groupBy('r.rf_department_id', 'mr.rf_metrika_item_id')
|
||||||
|
->get()
|
||||||
|
->groupBy('rf_department_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Получить данные последних отчетов
|
||||||
|
*/
|
||||||
|
private function getLastReportsData(array $departmentIds, string $date): Collection
|
||||||
|
{
|
||||||
|
// Находим ID последних отчетов для каждого отделения
|
||||||
|
$subQuery = DB::table('reports')
|
||||||
|
->select('rf_department_id', DB::raw('MAX(report_id) as last_report_id'))
|
||||||
|
->whereIn('rf_department_id', $departmentIds)
|
||||||
|
->whereDate('created_at', '<=', $date)
|
||||||
|
->groupBy('rf_department_id');
|
||||||
|
|
||||||
|
return DB::table('metrika_results as mr')
|
||||||
|
->joinSub($subQuery, 'last_reports', function ($join) {
|
||||||
|
$join->on('mr.rf_report_id', '=', 'last_reports.last_report_id');
|
||||||
|
})
|
||||||
|
->where('mr.rf_metrika_item_id', 8) // Только текущие пациенты
|
||||||
|
->select('last_reports.rf_department_id', 'mr.value')
|
||||||
|
->get()
|
||||||
|
->keyBy('rf_department_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Обработать агрегированные данные
|
||||||
|
*/
|
||||||
|
private function processAggregatedData(
|
||||||
|
Collection $departments,
|
||||||
|
Collection $defaultMetrics,
|
||||||
|
Collection $aggregatedData,
|
||||||
|
Collection $lastReportsData,
|
||||||
|
bool $isRangeOneDay
|
||||||
|
): array {
|
||||||
|
$groupedData = [];
|
||||||
|
$totalsByType = [];
|
||||||
|
|
||||||
|
foreach ($departments as $department) {
|
||||||
|
$departmentId = $department->department_id;
|
||||||
|
$departmentType = $department->departmentType->name_full;
|
||||||
|
|
||||||
|
if (!isset($groupedData[$departmentType])) {
|
||||||
|
$groupedData[$departmentType] = [];
|
||||||
|
$totalsByType[$departmentType] = $this->initTypeTotals();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получаем агрегированные метрики
|
||||||
|
$metrics = $aggregatedData->get($departmentId, collect());
|
||||||
|
$counters = array_fill_keys(array_values($this->metricMapping), 0);
|
||||||
|
|
||||||
|
foreach ($metrics as $metric) {
|
||||||
|
$key = $this->metricMapping[$metric->rf_metrika_item_id] ?? null;
|
||||||
|
if ($key) {
|
||||||
|
// Для агрегированных данных всегда берем сумму
|
||||||
|
$counters[$key] = (int)$metric->total;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Текущие пациенты из последнего отчета
|
||||||
|
$currentCount = (int)($lastReportsData->get($departmentId)?->value ?? 0);
|
||||||
|
$counters['current'] = $currentCount;
|
||||||
|
|
||||||
|
// Количество коек
|
||||||
|
$bedsCount = (int)($defaultMetrics->get($departmentId)?->value ?? 0);
|
||||||
|
|
||||||
|
// Рассчитываем значения
|
||||||
|
$allCount = $counters['plan'] + $counters['emergency'];
|
||||||
|
$percentLoadedBeds = $bedsCount > 0 ? round($currentCount * 100 / $bedsCount) : 0;
|
||||||
|
|
||||||
|
// Формируем данные
|
||||||
|
$departmentData = $this->createDepartmentData(
|
||||||
|
$department->name_short,
|
||||||
|
$bedsCount,
|
||||||
|
$allCount,
|
||||||
|
$counters,
|
||||||
|
$percentLoadedBeds,
|
||||||
|
$departmentType
|
||||||
|
);
|
||||||
|
|
||||||
|
$groupedData[$departmentType][] = $departmentData;
|
||||||
|
$this->updateTypeTotals($totalsByType[$departmentType], $departmentData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->buildFinalData($groupedData, $totalsByType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Оптимизированный метод для средних диапазонов (7-30 дней)
|
||||||
|
*/
|
||||||
|
private function getOptimizedStatistics(User $user, string $startDate, string $endDate, bool $isRangeOneDay): array
|
||||||
|
{
|
||||||
|
// Устанавливаем дату отчета
|
||||||
|
if ($isRangeOneDay) {
|
||||||
|
$dateReport = $endDate;
|
||||||
|
} else {
|
||||||
|
$dateReport = [$startDate, $endDate];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загружаем все отделения
|
||||||
|
$departments = Department::select('department_id', 'rf_department_type', 'name_short')
|
||||||
|
->with(['departmentType'])
|
||||||
|
->orderBy('name_short')
|
||||||
|
->get()
|
||||||
|
->keyBy('department_id');
|
||||||
|
|
||||||
|
// Загружаем метрики по умолчанию
|
||||||
|
$defaultMetrics = $this->getDefaultMetricsBatch($departments->pluck('department_id')->toArray());
|
||||||
|
|
||||||
|
// Загружаем отчеты
|
||||||
|
$reports = $this->getReportsBatch($departments->pluck('department_id')->toArray(), $dateReport, $isRangeOneDay);
|
||||||
|
|
||||||
|
// Загружаем метрики отчетов
|
||||||
|
$reportIds = $reports->flatMap(fn($items) => $items->pluck('report_id'))->toArray();
|
||||||
|
$reportMetrics = $this->getReportMetricsBatch($reportIds);
|
||||||
|
|
||||||
|
return $this->processOptimizedData(
|
||||||
|
$departments,
|
||||||
|
$defaultMetrics,
|
||||||
|
$reports,
|
||||||
|
$reportMetrics,
|
||||||
|
$dateReport,
|
||||||
|
$isRangeOneDay
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Получить метрики по умолчанию для всех отделений пачкой
|
||||||
|
*/
|
||||||
|
private function getDefaultMetricsBatch(array $departmentIds): Collection
|
||||||
|
{
|
||||||
|
return DB::table('department_metrika_defaults')
|
||||||
|
->whereIn('rf_department_id', $departmentIds)
|
||||||
|
->where('rf_metrika_item_id', 1) // только койки
|
||||||
|
->select('rf_department_id', 'value')
|
||||||
|
->get()
|
||||||
|
->keyBy('rf_department_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Получить отчеты для всех отделений пачкой
|
||||||
|
*/
|
||||||
|
private function getReportsBatch(array $departmentIds, $dateReport, bool $isRangeOneDay): Collection
|
||||||
|
{
|
||||||
|
$query = Report::whereIn('rf_department_id', $departmentIds);
|
||||||
|
|
||||||
|
if ($isRangeOneDay) {
|
||||||
|
$query->whereDate('created_at', $dateReport);
|
||||||
|
} else {
|
||||||
|
$query->whereBetween('created_at', $dateReport);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query->select('report_id', 'rf_department_id', 'created_at')
|
||||||
|
->orderBy('created_at')
|
||||||
|
->get()
|
||||||
|
->groupBy('rf_department_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Получить метрики отчетов пачкой
|
||||||
|
*/
|
||||||
|
private function getReportMetricsBatch(array $reportIds): Collection
|
||||||
|
{
|
||||||
|
if (empty($reportIds)) {
|
||||||
|
return collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
return DB::table('metrika_results')
|
||||||
|
->whereIn('rf_report_id', $reportIds)
|
||||||
|
->whereIn('rf_metrika_item_id', array_keys($this->metricMapping))
|
||||||
|
->select('rf_report_id', 'rf_metrika_item_id', 'value')
|
||||||
|
->get()
|
||||||
|
->groupBy('rf_report_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Обработать оптимизированные данные
|
||||||
|
*/
|
||||||
|
private function processOptimizedData(
|
||||||
|
Collection $departments,
|
||||||
|
Collection $defaultMetrics,
|
||||||
|
Collection $reports,
|
||||||
|
Collection $reportMetrics,
|
||||||
|
$dateReport,
|
||||||
|
bool $isRangeOneDay
|
||||||
|
): array {
|
||||||
|
$groupedData = [];
|
||||||
|
$totalsByType = [];
|
||||||
|
|
||||||
|
foreach ($departments as $department) {
|
||||||
|
$departmentId = $department->department_id;
|
||||||
|
$departmentType = $department->departmentType->name_full;
|
||||||
|
|
||||||
|
if (!isset($groupedData[$departmentType])) {
|
||||||
|
$groupedData[$departmentType] = [];
|
||||||
|
$totalsByType[$departmentType] = $this->initTypeTotals();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получаем отчеты отделения
|
||||||
|
$departmentReports = $reports->get($departmentId, collect());
|
||||||
|
$lastReport = $departmentReports->last();
|
||||||
|
|
||||||
|
// Инициализируем счетчики
|
||||||
|
$counters = array_fill_keys(array_values($this->metricMapping), 0);
|
||||||
|
|
||||||
|
// Обрабатываем каждый отчет
|
||||||
|
foreach ($departmentReports as $report) {
|
||||||
|
$metrics = $reportMetrics->get($report->report_id, collect())
|
||||||
|
->keyBy('rf_metrika_item_id');
|
||||||
|
|
||||||
|
foreach ($this->metricMapping as $metricId => $key) {
|
||||||
|
if ($metrics->has($metricId)) {
|
||||||
|
$value = (int)$metrics[$metricId]->value;
|
||||||
|
|
||||||
|
// Разная логика для одного дня и диапазона
|
||||||
|
if ($isRangeOneDay) {
|
||||||
|
// Для одного дня: суммируем
|
||||||
|
$counters[$key] += $value;
|
||||||
|
} else {
|
||||||
|
// Для диапазона:
|
||||||
|
if ($metricId === 8) {
|
||||||
|
// Для текущих пациентов берем ПОСЛЕДНЕЕ значение
|
||||||
|
if ($report === $lastReport) {
|
||||||
|
$counters[$key] = $value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Для остальных суммируем
|
||||||
|
$counters[$key] += $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если нет отчетов за день, но есть последний отчет ранее
|
||||||
|
if ($counters['current'] === 0 && $lastReport) {
|
||||||
|
$metrics = $reportMetrics->get($lastReport->report_id, collect());
|
||||||
|
$currentMetric = $metrics->firstWhere('rf_metrika_item_id', 8);
|
||||||
|
if ($currentMetric) {
|
||||||
|
$counters['current'] = (int)$currentMetric->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получаем количество коек
|
||||||
|
$bedsCount = (int)($defaultMetrics->get($departmentId)?->value ?? 0);
|
||||||
|
|
||||||
|
// Рассчитываем значения
|
||||||
|
$allCount = $counters['plan'] + $counters['emergency'];
|
||||||
|
$percentLoadedBeds = $bedsCount > 0 ? round($counters['current'] * 100 / $bedsCount) : 0;
|
||||||
|
|
||||||
|
// Формируем данные отделения
|
||||||
|
$departmentData = $this->createDepartmentData(
|
||||||
|
$department->name_short,
|
||||||
|
$bedsCount,
|
||||||
|
$allCount,
|
||||||
|
$counters,
|
||||||
|
$percentLoadedBeds,
|
||||||
|
$departmentType
|
||||||
|
);
|
||||||
|
|
||||||
|
$groupedData[$departmentType][] = $departmentData;
|
||||||
|
$this->updateTypeTotals($totalsByType[$departmentType], $departmentData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->buildFinalData($groupedData, $totalsByType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Детальный метод для небольших диапазонов (до 7 дней)
|
||||||
|
*/
|
||||||
|
private function getDetailedStatistics(User $user, string $startDate, string $endDate, bool $isRangeOneDay): array
|
||||||
|
{
|
||||||
|
// Устанавливаем дату отчета
|
||||||
|
if ($isRangeOneDay) {
|
||||||
|
$dateReport = $endDate;
|
||||||
|
} else {
|
||||||
|
$dateReport = [$startDate, $endDate];
|
||||||
|
}
|
||||||
|
|
||||||
|
$groupedData = [];
|
||||||
|
$totalsByType = [];
|
||||||
|
|
||||||
|
$departments = Department::select('department_id', 'rf_department_type', 'name_short')
|
||||||
|
->with(['departmentType', 'reports' => function ($query) use ($dateReport, $isRangeOneDay) {
|
||||||
|
if ($isRangeOneDay) {
|
||||||
|
$query->whereDate('created_at', $dateReport);
|
||||||
|
} else {
|
||||||
|
$query->whereBetween('created_at', $dateReport);
|
||||||
|
}
|
||||||
|
$query->with('metrikaResults');
|
||||||
|
}])
|
||||||
|
->orderBy('name_short')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
foreach ($departments as $department) {
|
||||||
|
$departmentType = $department->departmentType->name_full;
|
||||||
|
|
||||||
|
if (!isset($groupedData[$departmentType])) {
|
||||||
|
$groupedData[$departmentType] = [];
|
||||||
|
$totalsByType[$departmentType] = $this->initTypeTotals();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получаем отчеты
|
||||||
|
$reports = $department->reports;
|
||||||
|
$lastReport = $reports->last();
|
||||||
|
|
||||||
|
// Инициализируем счетчики
|
||||||
|
$counters = array_fill_keys(array_values($this->metricMapping), 0);
|
||||||
|
|
||||||
|
// Суммируем метрики
|
||||||
|
foreach ($reports as $report) {
|
||||||
|
foreach ($report->metrikaResults as $metric) {
|
||||||
|
$key = $this->metricMapping[$metric->rf_metrika_item_id] ?? null;
|
||||||
|
if ($key) {
|
||||||
|
$value = (int)$metric->value;
|
||||||
|
|
||||||
|
// ВАЖНО: разная логика для одного дня и диапазона
|
||||||
|
if ($isRangeOneDay) {
|
||||||
|
// Для одного дня: суммируем все значения
|
||||||
|
$counters[$key] += $value;
|
||||||
|
} else {
|
||||||
|
// Для диапазона:
|
||||||
|
if ($metric->rf_metrika_item_id === 8) {
|
||||||
|
// Для текущих пациентов берем ПОСЛЕДНЕЕ значение
|
||||||
|
// из последнего отчета за день
|
||||||
|
if ($report === $lastReport) {
|
||||||
|
$counters[$key] = $value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Для остальных метрик СУММИРУЕМ за весь период
|
||||||
|
$counters[$key] += $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получаем количество коек
|
||||||
|
$bedsCount = (int)$department->metrikaDefault()
|
||||||
|
->where('rf_metrika_item_id', 1)
|
||||||
|
->value('value') ?? 0;
|
||||||
|
|
||||||
|
// Рассчитываем итоговые значения
|
||||||
|
$allCount = $counters['plan'] + $counters['emergency'];
|
||||||
|
$percentLoadedBeds = $bedsCount > 0 ? round($counters['current'] * 100 / $bedsCount) : 0;
|
||||||
|
|
||||||
|
// Формируем данные отделения
|
||||||
|
$departmentData = $this->createDepartmentData(
|
||||||
|
$department->name_short,
|
||||||
|
$bedsCount,
|
||||||
|
$allCount,
|
||||||
|
$counters,
|
||||||
|
$percentLoadedBeds,
|
||||||
|
$departmentType
|
||||||
|
);
|
||||||
|
|
||||||
|
$groupedData[$departmentType][] = $departmentData;
|
||||||
|
$this->updateTypeTotals($totalsByType[$departmentType], $departmentData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->buildFinalData($groupedData, $totalsByType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Создать данные отделения
|
||||||
|
*/
|
||||||
|
private function createDepartmentData(
|
||||||
|
string $name,
|
||||||
|
int $beds,
|
||||||
|
int $allCount,
|
||||||
|
array $counters,
|
||||||
|
int $percentLoadedBeds,
|
||||||
|
string $type
|
||||||
|
): array {
|
||||||
|
return [
|
||||||
|
'department' => $name,
|
||||||
|
'beds' => $beds,
|
||||||
|
'recipients' => [
|
||||||
|
'all' => $allCount,
|
||||||
|
'plan' => $counters['plan'],
|
||||||
|
'emergency' => $counters['emergency'],
|
||||||
|
'transferred' => $counters['transferred'],
|
||||||
|
],
|
||||||
|
'outcome' => $counters['outcome'],
|
||||||
|
'consist' => $counters['current'],
|
||||||
|
'percentLoadedBeds' => $percentLoadedBeds,
|
||||||
|
'surgical' => [
|
||||||
|
'plan' => $counters['plan_surgical'],
|
||||||
|
'emergency' => $counters['emergency_surgical']
|
||||||
|
],
|
||||||
|
'deceased' => $counters['deceased'],
|
||||||
|
'type' => $type,
|
||||||
|
'isDepartment' => true
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Инициализировать итоги по типу
|
||||||
|
*/
|
||||||
|
private function initTypeTotals(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'departments_count' => 0,
|
||||||
|
'beds_sum' => 0,
|
||||||
|
'recipients_all_sum' => 0,
|
||||||
|
'recipients_plan_sum' => 0,
|
||||||
|
'recipients_emergency_sum' => 0,
|
||||||
|
'recipients_transferred_sum' => 0,
|
||||||
|
'outcome_sum' => 0,
|
||||||
|
'consist_sum' => 0,
|
||||||
|
'plan_surgical_sum' => 0,
|
||||||
|
'emergency_surgical_sum' => 0,
|
||||||
|
'deceased_sum' => 0,
|
||||||
|
'percentLoadedBeds_total' => 0,
|
||||||
|
'percentLoadedBeds_count' => 0,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Обновить итоги по типу
|
||||||
|
*/
|
||||||
|
private function updateTypeTotals(array &$totals, array $departmentData): void
|
||||||
|
{
|
||||||
|
$totals['departments_count']++;
|
||||||
|
$totals['beds_sum'] += $departmentData['beds'];
|
||||||
|
$totals['recipients_all_sum'] += $departmentData['recipients']['all'];
|
||||||
|
$totals['recipients_plan_sum'] += $departmentData['recipients']['plan'];
|
||||||
|
$totals['recipients_emergency_sum'] += $departmentData['recipients']['emergency'];
|
||||||
|
$totals['recipients_transferred_sum'] += $departmentData['recipients']['transferred'];
|
||||||
|
$totals['outcome_sum'] += $departmentData['outcome'];
|
||||||
|
$totals['consist_sum'] += $departmentData['consist'];
|
||||||
|
$totals['plan_surgical_sum'] += $departmentData['surgical']['plan'];
|
||||||
|
$totals['emergency_surgical_sum'] += $departmentData['surgical']['emergency'];
|
||||||
|
$totals['deceased_sum'] += $departmentData['deceased'];
|
||||||
|
$totals['percentLoadedBeds_total'] += $departmentData['percentLoadedBeds'];
|
||||||
|
$totals['percentLoadedBeds_count']++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Построить финальные данные с итогами
|
||||||
|
*/
|
||||||
|
private function buildFinalData(array $groupedData, array $totalsByType): array
|
||||||
|
{
|
||||||
|
$finalData = [];
|
||||||
|
$grandTotals = $this->initTypeTotals();
|
||||||
|
|
||||||
|
foreach ($groupedData as $type => $departmentsInType) {
|
||||||
|
// Добавляем заголовок группы
|
||||||
|
$finalData[] = [
|
||||||
|
'isGroupHeader' => true,
|
||||||
|
'groupName' => $type,
|
||||||
|
'colspan' => 12,
|
||||||
|
'type' => $type
|
||||||
|
];
|
||||||
|
|
||||||
|
// Добавляем отделения
|
||||||
|
foreach ($departmentsInType as $department) {
|
||||||
|
$finalData[] = $department;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавляем итоги по группе
|
||||||
|
if (!empty($departmentsInType) && isset($totalsByType[$type])) {
|
||||||
|
$total = $totalsByType[$type];
|
||||||
|
$avgPercent = $total['percentLoadedBeds_count'] > 0
|
||||||
|
? round($total['percentLoadedBeds_total'] / $total['percentLoadedBeds_count'])
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
$finalData[] = $this->createTotalRow($type, $total, $avgPercent, false);
|
||||||
|
|
||||||
|
// Обновляем общие итоги
|
||||||
|
$this->updateGrandTotals($grandTotals, $total);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавляем общие итоги
|
||||||
|
// if ($grandTotals['departments_count'] > 0) {
|
||||||
|
// $avgPercent = $grandTotals['percentLoadedBeds_count'] > 0
|
||||||
|
// ? round($grandTotals['percentLoadedBeds_total'] / $grandTotals['percentLoadedBeds_count'])
|
||||||
|
// : 0;
|
||||||
|
//
|
||||||
|
// $finalData[] = $this->createTotalRow('all', $grandTotals, $avgPercent, true);
|
||||||
|
// }
|
||||||
|
|
||||||
|
return [
|
||||||
|
'data' => $finalData,
|
||||||
|
'totalsByType' => $totalsByType,
|
||||||
|
'grandTotals' => $grandTotals
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Создать строку итогов
|
||||||
|
*/
|
||||||
|
private function createTotalRow(string $type, array $total, int $avgPercent, bool $isGrandTotal): array
|
||||||
|
{
|
||||||
|
$row = [
|
||||||
|
'isTotalRow' => !$isGrandTotal,
|
||||||
|
'isGrandTotal' => $isGrandTotal,
|
||||||
|
'department' => $isGrandTotal
|
||||||
|
? 'ОБЩИЕ ИТОГИ:'
|
||||||
|
: 'ИТОГО:',
|
||||||
|
'beds' => '—',//$total['beds_sum'],
|
||||||
|
'recipients' => [
|
||||||
|
'all' => $total['recipients_all_sum'],
|
||||||
|
'plan' => $total['recipients_plan_sum'],
|
||||||
|
'emergency' => $total['recipients_emergency_sum'],
|
||||||
|
'transferred' => $total['recipients_transferred_sum'],
|
||||||
|
],
|
||||||
|
'outcome' => $total['outcome_sum'],
|
||||||
|
'consist' => $total['consist_sum'],
|
||||||
|
'percentLoadedBeds' => '—',//$avgPercent,
|
||||||
|
'surgical' => [
|
||||||
|
'plan' => $total['plan_surgical_sum'],
|
||||||
|
'emergency' => $total['emergency_surgical_sum']
|
||||||
|
],
|
||||||
|
'deceased' => $total['deceased_sum'],
|
||||||
|
'type' => $type,
|
||||||
|
'departments_count' => $total['departments_count'],
|
||||||
|
'isBold' => true
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($isGrandTotal) {
|
||||||
|
$row['backgroundColor'] = '#f0f8ff';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Обновить общие итоги
|
||||||
|
*/
|
||||||
|
private function updateGrandTotals(array &$grandTotals, array $typeTotal): void
|
||||||
|
{
|
||||||
|
foreach ($grandTotals as $key => &$value) {
|
||||||
|
if (isset($typeTotal[$key])) {
|
||||||
|
$value += $typeTotal[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Очистить кэш статистики при создании отчета
|
||||||
|
*/
|
||||||
|
public function clearStatisticsCache(User $user, ?string $date = null): void
|
||||||
|
{
|
||||||
|
// Очищаем кэш по тегам
|
||||||
|
Cache::tags([
|
||||||
|
'statistics',
|
||||||
|
'reports',
|
||||||
|
'department_' . $user->rf_department_id
|
||||||
|
])->flush();
|
||||||
|
|
||||||
|
\Log::info("Statistics cache cleared for department: " . $user->rf_department_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Очистить кэш статистики для всех пользователей отдела
|
||||||
|
*/
|
||||||
|
public function clearDepartmentStatisticsCache(int $departmentId): void
|
||||||
|
{
|
||||||
|
Cache::tags([
|
||||||
|
'statistics',
|
||||||
|
'reports',
|
||||||
|
'department_' . $departmentId
|
||||||
|
])->flush();
|
||||||
|
|
||||||
|
\Log::info("Statistics cache cleared for entire department: " . $departmentId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,6 +36,10 @@ const columns = ref([
|
|||||||
}, h(NText, { style: 'font-weight: 600;' }, row.groupName))
|
}, h(NText, { style: 'font-weight: 600;' }, row.groupName))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (row.isTotalRow) {
|
||||||
|
return h(NText, { style: 'font-weight: 600;' }, row.department)
|
||||||
|
}
|
||||||
|
|
||||||
// Получаем текущие query параметры
|
// Получаем текущие query параметры
|
||||||
const { url } = usePage()
|
const { url } = usePage()
|
||||||
const currentUrl = new URL(url, window.location.origin)
|
const currentUrl = new URL(url, window.location.origin)
|
||||||
@@ -64,7 +68,14 @@ const columns = ref([
|
|||||||
key: 'beds',
|
key: 'beds',
|
||||||
width: 60,
|
width: 60,
|
||||||
titleAlign: 'center',
|
titleAlign: 'center',
|
||||||
align: 'center'
|
align: 'center',
|
||||||
|
render: (row) => {
|
||||||
|
if (row.isTotalRow) {
|
||||||
|
return h(NText, { style: 'font-weight: 600;' }, row.beds)
|
||||||
|
} else {
|
||||||
|
return h(NText, { }, row.beds)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Поступило',
|
title: 'Поступило',
|
||||||
@@ -76,28 +87,56 @@ const columns = ref([
|
|||||||
key: 'recipients.all',
|
key: 'recipients.all',
|
||||||
width: 60,
|
width: 60,
|
||||||
titleAlign: 'center',
|
titleAlign: 'center',
|
||||||
align: '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: 'План',
|
title: 'План',
|
||||||
key: 'recipients.plan',
|
key: 'recipients.plan',
|
||||||
width: 60,
|
width: 60,
|
||||||
titleAlign: 'center',
|
titleAlign: 'center',
|
||||||
align: '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: 'Экстр',
|
title: 'Экстр',
|
||||||
key: 'recipients.emergency',
|
key: 'recipients.emergency',
|
||||||
width: 60,
|
width: 60,
|
||||||
titleAlign: 'center',
|
titleAlign: 'center',
|
||||||
align: '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: 'Перевод',
|
title: 'Перевод',
|
||||||
key: 'recipients.transferred',
|
key: 'recipients.transferred',
|
||||||
width: 84,
|
width: 84,
|
||||||
titleAlign: 'center',
|
titleAlign: 'center',
|
||||||
align: '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)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -106,21 +145,42 @@ const columns = ref([
|
|||||||
key: 'outcome',
|
key: 'outcome',
|
||||||
width: 84,
|
width: 84,
|
||||||
titleAlign: 'center',
|
titleAlign: 'center',
|
||||||
align: 'center'
|
align: 'center',
|
||||||
|
render: (row) => {
|
||||||
|
if (row.isTotalRow) {
|
||||||
|
return h(NText, { style: 'font-weight: 600;' }, row.outcome)
|
||||||
|
} else {
|
||||||
|
return h(NText, { }, row.outcome)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Состоит',
|
title: 'Состоит',
|
||||||
key: 'consist',
|
key: 'consist',
|
||||||
width: 84,
|
width: 84,
|
||||||
titleAlign: 'center',
|
titleAlign: 'center',
|
||||||
align: 'center'
|
align: 'center',
|
||||||
|
render: (row) => {
|
||||||
|
if (row.isTotalRow) {
|
||||||
|
return h(NText, { style: 'font-weight: 600;' }, row.consist)
|
||||||
|
} else {
|
||||||
|
return h(NText, { }, row.consist)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '% загруженности',
|
title: '% загруженности',
|
||||||
key: 'percentLoadedBeds',
|
key: 'percentLoadedBeds',
|
||||||
width: 84,
|
width: 84,
|
||||||
titleAlign: 'center',
|
titleAlign: 'center',
|
||||||
align: 'center'
|
align: 'center',
|
||||||
|
render: (row) => {
|
||||||
|
if (row.isTotalRow) {
|
||||||
|
return h(NText, { style: 'font-weight: 600;' }, row.percentLoadedBeds)
|
||||||
|
} else {
|
||||||
|
return h(NText, { }, row.percentLoadedBeds)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Операции',
|
title: 'Операции',
|
||||||
@@ -132,14 +192,28 @@ const columns = ref([
|
|||||||
key: 'surgical.emergency',
|
key: 'surgical.emergency',
|
||||||
width: 60,
|
width: 60,
|
||||||
titleAlign: 'center',
|
titleAlign: 'center',
|
||||||
align: '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: 'П',
|
title: 'П',
|
||||||
key: 'surgical.plan',
|
key: 'surgical.plan',
|
||||||
width: 60,
|
width: 60,
|
||||||
titleAlign: 'center',
|
titleAlign: 'center',
|
||||||
align: '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)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -148,7 +222,14 @@ const columns = ref([
|
|||||||
key: 'deceased',
|
key: 'deceased',
|
||||||
width: 84,
|
width: 84,
|
||||||
titleAlign: 'center',
|
titleAlign: 'center',
|
||||||
align: 'center'
|
align: 'center',
|
||||||
|
render: (row) => {
|
||||||
|
if (row.isTotalRow) {
|
||||||
|
return h(NText, { style: 'font-weight: 600;' }, row.deceased)
|
||||||
|
} else {
|
||||||
|
return h(NText, { }, row.deceased)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -156,6 +237,9 @@ const rowProps = (row) => {
|
|||||||
if (row.isGroupHeader) return {
|
if (row.isGroupHeader) return {
|
||||||
style: `--n-merged-td-color: var(--n-merged-th-color)`
|
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);`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user