diff --git a/app/Http/Controllers/Business/SystemizationTodos.php b/app/Http/Controllers/Business/SystemizationTodos.php index 256d7621..80d8b7fc 100644 --- a/app/Http/Controllers/Business/SystemizationTodos.php +++ b/app/Http/Controllers/Business/SystemizationTodos.php @@ -102,6 +102,13 @@ class SystemizationTodos extends Controller ->orderBy('sort_order') ->orderBy('id') ->get(); + if ($filters['sequence'] > 0) { + $items = $items + ->filter(static fn (SystemizationTodo $todo): bool => + (int) $todo->category_sequence === $filters['sequence'] + ) + ->values(); + } $base = SystemizationTodo::query(); $categories = SystemizationTodo::query() @@ -176,7 +183,7 @@ class SystemizationTodos extends Controller $todo = SystemizationTodo::query()->create($data); $this->storeAttachments($request, $todo); - return $this->backToPage('待办事项已新增。'); + return $this->backToPage('待办事项已新增并定位。', 'success', $todo); } public function update(Request $request, SystemizationTodo $todo): RedirectResponse @@ -204,7 +211,7 @@ class SystemizationTodos extends Controller $todo->update($data); $this->storeAttachments($request, $todo); - return $this->backToPage('待办事项已更新。'); + return $this->backToPage('待办事项已更新并定位。', 'success', $todo); } public function destroy(SystemizationTodo $todo): RedirectResponse @@ -273,6 +280,7 @@ class SystemizationTodos extends Controller 'assignees' => $this->peopleFilter('assignee'), 'start_date' => $this->normalizeDateFilter(request('start_date')), 'due_date' => $this->normalizeDateFilter(request('due_date')), + 'sequence' => max(0, (int) request('sequence', 0)), 'keyword' => trim((string) request('keyword', '')), ]; } @@ -371,13 +379,22 @@ class SystemizationTodos extends Controller } } - private function backToPage(string $message, string $type = 'success'): RedirectResponse + private function backToPage( + string $message, + string $type = 'success', + ?SystemizationTodo $focus = null, + ): RedirectResponse { + $parameters = [ + 'section' => 'other', + 'page' => 'systemization-todos', + ]; + if ($focus) { + $parameters['highlight'] = $focus->id; + } + return redirect() - ->route('business.pages.show', [ - 'section' => 'other', - 'page' => 'systemization-todos', - ]) + ->route('business.pages.show', $parameters) ->with($type === 'success' ? 'success' : 'error', $message); } } diff --git a/resources/views/business/pages/systemization-todos.blade.php b/resources/views/business/pages/systemization-todos.blade.php index 656d300f..04a9438d 100644 --- a/resources/views/business/pages/systemization-todos.blade.php +++ b/resources/views/business/pages/systemization-todos.blade.php @@ -44,6 +44,7 @@ .sys-todo-table td { padding:.72rem .6rem; border-bottom:1px solid #eee7da; background:#fff; color:#2f2a23; line-height:1.6; vertical-align:top; } .sys-todo-table tbody tr { cursor:pointer; } .sys-todo-table tbody tr:hover td { background:#fdfbf6; } + .sys-todo-table tbody tr.is-highlighted td { background:#fff4cf !important; } .sys-todo-table tbody tr:focus-visible { outline:2px solid var(--sys-gold); outline-offset:-2px; } .sys-todo-table__index-cell { width:3.6rem; min-width:3.6rem; text-align:center; } .sys-todo-table__index { color:#6d5630; font-weight:600; font-variant-numeric:tabular-nums; white-space:nowrap; } @@ -198,6 +199,7 @@ @endforeach + @php $selectedOwners = $todoOwners->filter( static fn (string $owner): bool => in_array($owner, $todoFilters['owners'], true) @@ -309,6 +311,7 @@ } $lastItem = $items->last(); $lastSectionLabel = $items->isNotEmpty() ? ($sectionLabels[$items->count() - 1] ?? '') : ''; + $highlightId = max(0, (int) request('highlight', 0)); @endphp
@@ -366,7 +369,7 @@ $categorySpan = $groupRowspans['category'][$loop->index] ?? 1; $sectionSpan = $groupRowspans['section'][$loop->index] ?? 1; @endphp - + @if ($categorySpan !== 0) 1) rowspan="{{ $categorySpan }}" @endif>{{ $item->category ?: '—' }} @endif @@ -899,6 +902,13 @@ new ResizeObserver(scheduleScrollRailSync).observe(tableWrap); } scheduleScrollRailSync(); + var highlightedRow = document.querySelector('.sys-todo-table tbody tr.is-highlighted'); + if (highlightedRow) { + window.setTimeout(function () { + highlightedRow.scrollIntoView({ behavior: 'smooth', block: 'center' }); + highlightedRow.focus({ preventScroll: true }); + }, 120); + } screenshotInput.addEventListener('change', function () { if (screenshotInput.files.length) { uploadNote.textContent = '已选择 ' + screenshotInput.files.length + ' 张:' + selectedFilesLabel() + '。保存后上传。'; diff --git a/tests/Feature/Business/SystemizationTodosTest.php b/tests/Feature/Business/SystemizationTodosTest.php index 5f3d471a..4336d67c 100644 --- a/tests/Feature/Business/SystemizationTodosTest.php +++ b/tests/Feature/Business/SystemizationTodosTest.php @@ -28,6 +28,7 @@ class SystemizationTodosTest extends FeatureTestCase ->assertSeeText('从 Excel 同步待办') ->assertSeeText('总负责人') ->assertSeeText('责任人') + ->assertSeeText('类型内序号') ->assertSeeText('已逾期') ->assertSeeText('开始时间') ->assertSeeText('完成时间') @@ -49,7 +50,7 @@ class SystemizationTodosTest extends FeatureTestCase ->assertDontSeeText('下一页') ->assertSee('id="sysTodoImageViewer"', false); - $this->post(route('business.systemization-todos.store'), [ + $storeResponse = $this->post(route('business.systemization-todos.store'), [ 'category' => '收入', 'section' => '平台收入核对', 'title' => '核对淘系收入与平台账单差异', @@ -64,12 +65,13 @@ class SystemizationTodosTest extends FeatureTestCase $this->pngUpload('first.png'), $this->pngUpload('existing.png'), ], - ])->assertRedirect(route('business.pages.show', [ - 'section' => 'other', - 'page' => 'systemization-todos', - ])); + ]); + $storeResponse->assertRedirect(); $todo = SystemizationTodo::query()->firstOrFail(); + $location = (string) $storeResponse->headers->get('Location'); + parse_str((string) parse_url($location, PHP_URL_QUERY), $redirectQuery); + $this->assertSame((string) $todo->id, $redirectQuery['highlight'] ?? null); $this->assertSame('收入', $todo->category); $this->assertSame('晨曦、人参', $todo->owner); $this->assertSame(SystemizationTodo::STATUS_IN_PROGRESS, $todo->status); @@ -220,6 +222,24 @@ class SystemizationTodosTest extends FeatureTestCase ->all(), ); + $sequenceResponse = $this->get(route('business.pages.show', [ + 'section' => 'other', + 'page' => 'systemization-todos', + 'category' => '收入', + 'sequence' => 2, + ]))->assertOk(); + $sequenceContent = $sequenceResponse->viewData('content'); + $this->assertSame(['2、收入事项 B'], $sequenceContent['items']->pluck('title')->all()); + + $highlightedTodo = $pageItems->firstWhere('title', '2、收入事项 B'); + $this->get(route('business.pages.show', [ + 'section' => 'other', + 'page' => 'systemization-todos', + 'highlight' => $highlightedTodo->id, + ])) + ->assertOk() + ->assertSee('id="sys-todo-row-' . $highlightedTodo->id . '" class="is-highlighted"', false); + $response = $this->get(route('business.pages.show', [ 'section' => 'other', 'page' => 'systemization-todos',