diff --git a/app/Http/Controllers/Business/SystemizationTodos.php b/app/Http/Controllers/Business/SystemizationTodos.php index 45a54d5b..607c60d7 100644 --- a/app/Http/Controllers/Business/SystemizationTodos.php +++ b/app/Http/Controllers/Business/SystemizationTodos.php @@ -19,6 +19,13 @@ use Throwable; class SystemizationTodos extends Controller { private const OVERDUE_FILTER = '__overdue__'; + private const REMINDER_OPTIONS = [ + 'overdue' => '已逾期', + 'today' => '今天到期', + 'upcoming' => '剩余天数', + 'pending' => '时间待定', + 'none' => '无提醒', + ]; public function page(): array { @@ -36,6 +43,24 @@ class SystemizationTodos extends Controller } elseif ($filters['status'] === SystemizationTodo::STATUS_COMPLETED) { $query->where('status', $filters['status']); } + if ($filters['reminder'] !== '') { + if ($filters['reminder'] === 'overdue') { + $query->where('status', '<>', SystemizationTodo::STATUS_COMPLETED) + ->whereNotNull('due_date') + ->whereDate('due_date', '<', today()); + } elseif ($filters['reminder'] === 'today') { + $query->where('status', '<>', SystemizationTodo::STATUS_COMPLETED) + ->whereDate('due_date', today()); + } elseif ($filters['reminder'] === 'upcoming') { + $query->where('status', '<>', SystemizationTodo::STATUS_COMPLETED) + ->whereDate('due_date', '>', today()); + } elseif ($filters['reminder'] === 'pending') { + $query->where('status', '<>', SystemizationTodo::STATUS_COMPLETED) + ->whereNull('due_date'); + } elseif ($filters['reminder'] === 'none') { + $query->where('status', SystemizationTodo::STATUS_COMPLETED); + } + } if ($filters['category'] !== '') { $query->where('category', $filters['category']); } @@ -150,6 +175,12 @@ class SystemizationTodos extends Controller 'todoAssignees' => $this->peopleOptions('assignee'), 'todoStatuses' => SystemizationTodo::STATUSES, 'todoStatusOptions' => $statusOptions, + 'todoReminderOptions' => collect(self::REMINDER_OPTIONS) + ->map(static fn (string $label, string $value): array => [ + 'value' => $value, + 'label' => $label, + ]) + ->values(), 'todoReturnFilters' => base64_encode(json_encode( $filters, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES, @@ -602,6 +633,7 @@ class SystemizationTodos extends Controller { return [ 'status' => $this->normalizeStatusValue(request('status')), + 'reminder' => $this->normalizeReminderValue(request('reminder')), 'category' => trim((string) request('category', '')), 'owners' => $this->peopleFilter('owner'), 'assignees' => $this->peopleFilter('assignee'), @@ -624,6 +656,13 @@ class SystemizationTodos extends Controller : ''; } + private function normalizeReminderValue(mixed $value): string + { + $reminder = trim((string) $value); + + return array_key_exists($reminder, self::REMINDER_OPTIONS) ? $reminder : ''; + } + private function peopleFilter(string $key): array { return collect((array) request($key, [])) @@ -818,6 +857,7 @@ class SystemizationTodos extends Controller return array_filter([ 'status' => $this->normalizeStatusValue($filters['status'] ?? null), + 'reminder' => $this->normalizeReminderValue($filters['reminder'] ?? null), 'category' => $category, 'owner' => $owners, 'assignee' => $assignees, diff --git a/resources/views/business/pages/systemization-todos.blade.php b/resources/views/business/pages/systemization-todos.blade.php index 7b12efff..d2d4eb7d 100644 --- a/resources/views/business/pages/systemization-todos.blade.php +++ b/resources/views/business/pages/systemization-todos.blade.php @@ -264,6 +264,12 @@ @endforeach + @php $selectedOwners = $todoOwners->filter( static fn (string $owner): bool => in_array($owner, $todoFilters['owners'], true) @@ -595,7 +601,7 @@
- +
@csrf @@ -783,6 +789,7 @@ var dialog = document.getElementById('sysTodoDialog'); var form = dialog.querySelector('[data-sys-todo-form]'); var pageRoot = dialog.closest('.sys-todo-page'); + var defaultStartDate = dialog.getAttribute('data-sys-todo-default-start-date') || ''; if (window.CaiWuDatePicker) window.CaiWuDatePicker.setup(pageRoot || document); var methodInput = dialog.querySelector('[data-sys-todo-method]'); var title = document.getElementById('sysTodoDialogTitle'); @@ -1382,7 +1389,7 @@ setCategoryValue(''); refreshSectionOptions(''); setOwnerValue(''); - applyDateControl(startDateControl, '', ''); + applyDateControl(startDateControl, defaultStartDate, ''); applyDateControl(dueDateControl, '', ''); } if (!currentTodoId && !(payload && payload.status)) { diff --git a/tests/Feature/Business/SystemizationTodosTest.php b/tests/Feature/Business/SystemizationTodosTest.php index ce7afcf4..f692255b 100644 --- a/tests/Feature/Business/SystemizationTodosTest.php +++ b/tests/Feature/Business/SystemizationTodosTest.php @@ -34,8 +34,11 @@ class SystemizationTodosTest extends FeatureTestCase ->assertSeeText('开始时间') ->assertSeeText('完成时间') ->assertSeeText('待定') + ->assertSee('name="reminder"', false) + ->assertSeeText('全部提醒') ->assertSeeText('完成情况 / 备注') ->assertSeeText('提醒') + ->assertSee('data-sys-todo-default-start-date', false) ->assertSee('data-sys-todo-inline-add', false) ->assertSee('data-sys-todo-col-resizer', false) ->assertSee('data-sys-todo-reset-columns', false) @@ -478,6 +481,7 @@ class SystemizationTodosTest extends FeatureTestCase $todo = $this->createTodo('收入', '平台收入核对', '筛选保持测试', '2026-10-30', 1); $returnFilters = base64_encode(json_encode([ 'status' => SystemizationTodo::STATUS_INCOMPLETE, + 'reminder' => 'upcoming', 'category' => '收入', 'owners' => ['晨曦', '人参'], 'assignees' => ['栎阳'], @@ -501,6 +505,7 @@ class SystemizationTodosTest extends FeatureTestCase $location = (string) $response->headers->get('Location'); parse_str((string) parse_url($location, PHP_URL_QUERY), $query); $this->assertSame(SystemizationTodo::STATUS_INCOMPLETE, $query['status'] ?? null); + $this->assertSame('upcoming', $query['reminder'] ?? null); $this->assertSame('收入', $query['category'] ?? null); $this->assertSame(['晨曦', '人参'], $query['owner'] ?? null); $this->assertSame(['栎阳'], $query['assignee'] ?? null); @@ -526,11 +531,16 @@ class SystemizationTodosTest extends FeatureTestCase ]); $legacy = $this->createTodo('收入', '提醒测试', '历史进行中事项', '2026-11-01', 5); $legacy->update(['status' => '进行中']); + $pendingDate = $this->createTodo('收入', '提醒测试', '时间待定事项', '2026-10-30', 6); + $pendingDate->update(['due_date' => null, 'due_note' => '待定']); - $content = $this->get(route('business.pages.show', [ + $pageResponse = $this->get(route('business.pages.show', [ 'section' => 'other', 'page' => 'systemization-todos', - ]))->assertOk()->viewData('content'); + ]))->assertOk() + ->assertSee('data-sys-todo-default-start-date="2026-10-26"', false) + ->assertSee("applyDateControl(startDateControl, defaultStartDate, '');", false); + $content = $pageResponse->viewData('content'); $items = $content['items']->keyBy('title'); $this->assertSame('逾期 6 天', $items['逾期事项']->reminder_label); @@ -538,6 +548,7 @@ class SystemizationTodosTest extends FeatureTestCase $this->assertSame('今天到期', $items['今天到期事项']->reminder_label); $this->assertSame('剩余 4 天', $items['未来事项']->reminder_label); $this->assertSame('—', $items['已完成事项']->reminder_label); + $this->assertSame('时间待定', $items['时间待定事项']->reminder_label); $this->assertSame('', $items['已完成事项']->displayProgress()); $this->assertSame(SystemizationTodo::STATUS_INCOMPLETE, $items['历史进行中事项']->status_label); @@ -555,6 +566,23 @@ class SystemizationTodosTest extends FeatureTestCase 'status' => SystemizationTodo::STATUS_COMPLETED, ]))->assertOk()->viewData('content')['items']->pluck('title'); $this->assertSame(['已完成事项'], $completedTitles->all()); + + foreach ([ + 'overdue' => ['逾期事项'], + 'today' => ['今天到期事项'], + 'upcoming' => ['未来事项', '历史进行中事项'], + 'pending' => ['时间待定事项'], + 'none' => ['已完成事项'], + ] as $reminder => $expectedTitles) { + $reminderContent = $this->get(route('business.pages.show', [ + 'section' => 'other', + 'page' => 'systemization-todos', + 'reminder' => $reminder, + ]))->assertOk()->viewData('content'); + + $this->assertSame($reminder, $reminderContent['todoFilters']['reminder']); + $this->assertSame($expectedTitles, $reminderContent['items']->pluck('title')->all()); + } } finally { Carbon::setTestNow(); }