diff --git a/README.md b/README.md index 33ee8d5..4df27bf 100644 --- a/README.md +++ b/README.md @@ -11,11 +11,10 @@ - 🗑️ 删除患者记录 ### 随访提醒 -- ⏰ 智能计算随访日期(出院后1周、1月、3月、6月) -- 🔴 过期提醒 - 显示已过期未随访的患者 -- 🟡 今日到期 - 当天需要随访的患者 -- 🔵 即将到期 - 7天内需要随访的患者 -- ✅ 标记随访完成,自动计算下次随访时间 +- ⏰ 智能计算随访日期(出院后1、3、6、12个月) +- 📅 本月提醒 - 只显示本月需要随访的患者列表 +- 📊 进度自动更新 - 过期月份自动算作已完成 +- 📞 一键拨打电话进行回访 ### 用户系统 - 👤 用户注册/登录 diff --git a/app/Http/Controllers/PatientController.php b/app/Http/Controllers/PatientController.php index 7e416ae..05abf7a 100644 --- a/app/Http/Controllers/PatientController.php +++ b/app/Http/Controllers/PatientController.php @@ -46,54 +46,33 @@ class PatientController extends Controller } /** - * 显示需要随访提醒的患者 + * 显示需要随访提醒的患者(只显示当月需要随访的) */ public function reminders(Request $request) { $patients = $this->userPatients()->get(); - // 筛选需要随访的患者 + // 只筛选当月需要随访的患者 $reminders = $patients->filter(function ($patient) { - return !$patient->isCompleted(); + return $patient->needsFollowUpThisMonth(); })->map(function ($patient) { - $nextDate = $patient->getNextFollowUpDate(); return [ 'patient' => $patient, - 'next_follow_up_date' => $nextDate, - 'next_follow_up_number' => $patient->getNextFollowUpNumber(), - 'status' => $patient->getFollowUpStatus(), - 'needs_attention' => $patient->needsFollowUp(), - 'days_until' => $nextDate ? Carbon::today()->diffInDays($nextDate, false) : null, + 'follow_up_date' => $patient->getCurrentMonthFollowUpDate(), + 'follow_up_number' => $patient->getCurrentMonthFollowUpNumber(), ]; - })->sortBy('days_until'); - - // 筛选类型 - $filter = $request->input('filter', 'all'); - - if ($filter === 'overdue') { - $reminders = $reminders->filter(fn($r) => $r['days_until'] !== null && $r['days_until'] < 0); - } elseif ($filter === 'today') { - $reminders = $reminders->filter(fn($r) => $r['days_until'] === 0); - } elseif ($filter === 'upcoming') { - $reminders = $reminders->filter(fn($r) => $r['days_until'] !== null && $r['days_until'] > 0 && $r['days_until'] <= 7); - } + })->sortBy(function ($r) { + return $r['follow_up_date']; + }); // 统计数据 $stats = [ - 'total' => $this->userPatients()->count(), - 'overdue' => $patients->filter(fn($p) => !$p->isCompleted() && $p->getNextFollowUpDate()?->lt(Carbon::today()))->count(), - 'today' => $patients->filter(fn($p) => !$p->isCompleted() && $p->getNextFollowUpDate()?->isToday())->count(), - 'upcoming' => $patients->filter(function ($p) { - if ($p->isCompleted()) return false; - $next = $p->getNextFollowUpDate(); - if (!$next) return false; - $diff = Carbon::today()->diffInDays($next, false); - return $diff > 0 && $diff <= 7; - })->count(), + 'count' => $reminders->count(), 'completed' => $patients->filter(fn($p) => $p->isCompleted())->count(), + 'total' => $patients->count(), ]; - return view('patients.reminders', compact('reminders', 'stats', 'filter')); + return view('patients.reminders', compact('reminders', 'stats')); } /** @@ -359,46 +338,34 @@ class PatientController extends Controller } /** - * 导出提醒列表 (使用 xlswriter 生成 Excel) + * 导出本月随访列表 (使用 xlswriter 生成 Excel) */ public function export(Request $request) { - $filter = $request->input('filter', 'all'); - $patients = $this->userPatients()->get(); - // 筛选需要随访的患者 + // 只筛选本月需要随访的患者 $reminders = $patients->filter(function ($patient) { - return !$patient->isCompleted(); + return $patient->needsFollowUpThisMonth(); })->map(function ($patient) { - $nextDate = $patient->getNextFollowUpDate(); return [ 'patient' => $patient, - 'next_follow_up_date' => $nextDate, - 'next_follow_up_number' => $patient->getNextFollowUpNumber(), - 'status' => $patient->getFollowUpStatus(), - 'days_until' => $nextDate ? Carbon::today()->diffInDays($nextDate, false) : null, + 'follow_up_date' => $patient->getCurrentMonthFollowUpDate(), + 'follow_up_number' => $patient->getCurrentMonthFollowUpNumber(), ]; - })->sortBy('days_until'); - - // 根据筛选条件过滤 - if ($filter === 'overdue') { - $reminders = $reminders->filter(fn($r) => $r['days_until'] !== null && $r['days_until'] < 0); - } elseif ($filter === 'today') { - $reminders = $reminders->filter(fn($r) => $r['days_until'] === 0); - } elseif ($filter === 'upcoming') { - $reminders = $reminders->filter(fn($r) => $r['days_until'] !== null && $r['days_until'] > 0 && $r['days_until'] <= 7); - } + })->sortBy(function ($r) { + return $r['follow_up_date']; + }); // 使用 xlswriter 生成 Excel 文件 $config = ['path' => sys_get_temp_dir()]; - $filename = '随访提醒_' . date('Y-m-d_His') . '.xlsx'; + $filename = '本月随访_' . date('Y-m') . '.xlsx'; $excel = new \Vtiful\Kernel\Excel($config); // 创建文件并设置表头 $fileHandle = $excel->fileName($filename) - ->header(['姓名', '性别', '年龄', '出院诊断', '转诊时间', '下次随访日期', '第几次随访', '状态', '户籍地址', '联系方式', '备注']); + ->header(['姓名', '性别', '年龄', '出院诊断', '转诊时间', '随访日期', '第几次随访', '户籍地址', '联系方式', '备注']); // 设置表头样式 $headerStyle = $fileHandle->getHandle(); @@ -415,9 +382,8 @@ class PatientController extends Controller $p->age, $p->diagnosis, $p->discharge_date->format('Y-m-d'), - $reminder['next_follow_up_date']?->format('Y-m-d') ?? '', - '第' . $reminder['next_follow_up_number'] . '次', - $reminder['status'], + $reminder['follow_up_date']?->format('Y-m-d') ?? '', + '第' . $reminder['follow_up_number'] . '次', $p->address ?? '', $p->phone ?? '', $p->remark ?? '', diff --git a/app/Models/Patient.php b/app/Models/Patient.php index aa7e6fb..f7e92e1 100644 --- a/app/Models/Patient.php +++ b/app/Models/Patient.php @@ -53,66 +53,74 @@ class Patient extends Model } /** - * 获取下次随访日期 + * 自动计算已完成的随访次数(过期的月份自动算已完成) */ - public function getNextFollowUpDate(): ?Carbon + public function getAutoFollowUpCount(): int { $schedule = $this->getFollowUpSchedule(); - $nextIndex = $this->follow_up_count; + $currentMonth = Carbon::now()->startOfMonth(); + $completed = 0; - if ($nextIndex >= count($schedule)) { - return null; // 已完成所有随访 + foreach ($schedule as $months) { + $followUpDate = $this->discharge_date->copy()->addMonths($months); + // 如果随访日期的月份早于当前月份,则算作已完成 + if ($followUpDate->startOfMonth()->lt($currentMonth)) { + $completed++; + } else { + break; + } } - $months = $schedule[$nextIndex]; - return $this->discharge_date->copy()->addMonths($months); + return $completed; } /** - * 获取下次随访是第几次 + * 获取当月需要随访的日期(如果有的话) */ - public function getNextFollowUpNumber(): int + public function getCurrentMonthFollowUpDate(): ?Carbon { - return $this->follow_up_count + 1; + $schedule = $this->getFollowUpSchedule(); + $currentMonth = Carbon::now(); + $monthStart = $currentMonth->copy()->startOfMonth(); + $monthEnd = $currentMonth->copy()->endOfMonth(); + + foreach ($schedule as $index => $months) { + $followUpDate = $this->discharge_date->copy()->addMonths($months); + // 如果随访日期在当月内 + if ($followUpDate->gte($monthStart) && $followUpDate->lte($monthEnd)) { + return $followUpDate; + } + } + + return null; } /** - * 检查是否需要随访(到期或已过期) + * 获取当月随访是第几次 */ - public function needsFollowUp(): bool + public function getCurrentMonthFollowUpNumber(): ?int { - $nextDate = $this->getNextFollowUpDate(); + $schedule = $this->getFollowUpSchedule(); + $currentMonth = Carbon::now(); + $monthStart = $currentMonth->copy()->startOfMonth(); + $monthEnd = $currentMonth->copy()->endOfMonth(); - if (!$nextDate) { - return false; + foreach ($schedule as $index => $months) { + $followUpDate = $this->discharge_date->copy()->addMonths($months); + if ($followUpDate->gte($monthStart) && $followUpDate->lte($monthEnd)) { + return $index + 1; + } } - return $nextDate->lte(Carbon::today()); + return null; } /** - * 获取随访状态 + * 检查当月是否需要随访 */ - public function getFollowUpStatus(): string + public function needsFollowUpThisMonth(): bool { - $nextDate = $this->getNextFollowUpDate(); - - if (!$nextDate) { - return '已完成'; - } - - $today = Carbon::today(); - $diff = $today->diffInDays($nextDate, false); - - if ($diff < 0) { - return '已过期 ' . abs($diff) . ' 天'; - } elseif ($diff == 0) { - return '今日到期'; - } elseif ($diff <= 7) { - return '即将到期(' . $diff . '天后)'; - } else { - return '未到期'; - } + return $this->getCurrentMonthFollowUpDate() !== null; } /** @@ -120,7 +128,7 @@ class Patient extends Model */ public function isCompleted(): bool { - return $this->follow_up_count >= count($this->getFollowUpSchedule()); + return $this->getAutoFollowUpCount() >= count($this->getFollowUpSchedule()); } /** diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index 683b167..756fd17 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -1113,7 +1113,31 @@ .reminder-actions { display: flex; gap: 10px; - justify-content: flex-end; + margin-top: 16px; + padding-top: 16px; + border-top: 1px solid var(--color-border-light); + } + + .call-btn { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + width: 100%; + padding: 12px 16px; + background: linear-gradient(135deg, var(--color-success), #34d399); + color: white; + border: none; + border-radius: 10px; + font-size: 14px; + font-weight: 600; + text-decoration: none; + transition: all 0.2s; + } + + .call-btn:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(16, 185, 129, 0.4); } .action-form { @@ -1778,12 +1802,12 @@ 随访提醒 @php - $overdueCount = \App\Models\Patient::where('user_id', Auth::id())->get()->filter(function($p) { - return !$p->isCompleted() && $p->getNextFollowUpDate()?->lt(\Carbon\Carbon::today()); + $monthCount = \App\Models\Patient::where('user_id', Auth::id())->get()->filter(function($p) { + return $p->needsFollowUpThisMonth(); })->count(); @endphp - @if($overdueCount > 0) - + @if($monthCount > 0) + @endif diff --git a/resources/views/patients/index.blade.php b/resources/views/patients/index.blade.php index 71b8235..54127cd 100644 --- a/resources/views/patients/index.blade.php +++ b/resources/views/patients/index.blade.php @@ -600,7 +600,6 @@