From fd35ac6e78faa589a8e6723e6b01093b62f0f98f Mon Sep 17 00:00:00 2001 From: yueyang <2495544247@qq.com> Date: Sun, 25 Oct 2026 20:36:30 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=E7=B3=BB=E7=BB=9F=E5=8C=96?= =?UTF-8?q?=E5=BE=85=E5=8A=9E=E7=AD=9B=E9=80=89=E4=B8=8E=E5=88=97=E9=A1=BA?= =?UTF-8?q?=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Business/SystemizationTodos.php | 72 +++++++------------ .../pages/systemization-todos.blade.php | 63 ++++++++++------ .../Business/SystemizationTodosTest.php | 36 +++++++--- 3 files changed, 89 insertions(+), 82 deletions(-) diff --git a/app/Http/Controllers/Business/SystemizationTodos.php b/app/Http/Controllers/Business/SystemizationTodos.php index 35a51209..023a43de 100644 --- a/app/Http/Controllers/Business/SystemizationTodos.php +++ b/app/Http/Controllers/Business/SystemizationTodos.php @@ -8,6 +8,7 @@ use App\Models\Business\SystemizationTodoAttachment; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Routing\Controller; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; use Illuminate\Validation\Rule; @@ -19,17 +20,6 @@ class SystemizationTodos extends Controller public function page(): array { $filters = $this->filters(); - if ( - $filters['date_from'] !== '' - && $filters['date_to'] !== '' - && $filters['date_from'] > $filters['date_to'] - ) { - [$filters['date_from'], $filters['date_to']] = [ - $filters['date_to'], - $filters['date_from'], - ]; - } - $query = SystemizationTodo::query() ->with('attachments') ->withCount('attachments'); @@ -41,16 +31,16 @@ class SystemizationTodos extends Controller $query->where('category', $filters['category']); } if ($filters['owner'] !== '') { - $query->where(function ($builder) use ($filters): void { - $builder->where('owner', $filters['owner']) - ->orWhere('assignee', $filters['owner']); - }); + $query->where('owner', 'like', '%' . addcslashes($filters['owner'], '%_\\') . '%'); } - if ($filters['date_from'] !== '') { - $query->whereDate($filters['date_scope'], '>=', $filters['date_from']); + if ($filters['assignee'] !== '') { + $query->where('assignee', 'like', '%' . addcslashes($filters['assignee'], '%_\\') . '%'); } - if ($filters['date_to'] !== '') { - $query->whereDate($filters['date_scope'], '<=', $filters['date_to']); + if ($filters['start_date'] !== '') { + $query->whereDate('start_date', $filters['start_date']); + } + if ($filters['due_date'] !== '') { + $query->whereDate('due_date', $filters['due_date']); } if ($filters['keyword'] !== '') { $like = '%' . addcslashes($filters['keyword'], '%_\\') . '%'; @@ -85,21 +75,6 @@ class SystemizationTodos extends Controller ->distinct() ->orderBy('category') ->pluck('category'); - $owners = SystemizationTodo::query() - ->whereNotNull('owner') - ->where('owner', '<>', '') - ->pluck('owner') - ->merge( - SystemizationTodo::query() - ->whereNotNull('assignee') - ->where('assignee', '<>', '') - ->pluck('assignee') - ) - ->flatMap(static fn (string $value): array => array_map('trim', preg_split('/[、,,]/u', $value) ?: [])) - ->filter() - ->unique() - ->sort() - ->values(); return [ 'view' => 'business.pages.systemization-todos', @@ -107,9 +82,9 @@ class SystemizationTodos extends Controller 'items' => $items, 'todoFilters' => $filters, 'todoCategories' => $categories, - 'todoOwners' => $owners, + 'todoOwners' => $this->peopleOptions('owner'), + 'todoAssignees' => $this->peopleOptions('assignee'), 'todoStatuses' => SystemizationTodo::STATUSES, - 'todoDateScopes' => $this->dateScopes(), 'todoSummary' => [ 'total' => (clone $base)->count(), 'pending' => (clone $base)->where('status', SystemizationTodo::STATUS_PENDING)->count(), @@ -229,29 +204,30 @@ class SystemizationTodos extends Controller private function filters(): array { - $dateScopes = $this->dateScopes(); - $dateScope = (string) request('date_scope', 'due_date'); - return [ 'status' => in_array(request('status'), SystemizationTodo::STATUSES, true) ? (string) request('status') : '', 'category' => trim((string) request('category', '')), 'owner' => trim((string) request('owner', '')), + 'assignee' => trim((string) request('assignee', '')), + 'start_date' => $this->normalizeDateFilter(request('start_date')), + 'due_date' => $this->normalizeDateFilter(request('due_date')), 'keyword' => trim((string) request('keyword', '')), - 'date_scope' => array_key_exists($dateScope, $dateScopes) ? $dateScope : 'due_date', - 'date_from' => $this->normalizeDateFilter(request('date_from')), - 'date_to' => $this->normalizeDateFilter(request('date_to')), ]; } - private function dateScopes(): array + private function peopleOptions(string $column): Collection { - return [ - 'due_date' => '完成时间', - 'start_date' => '开始时间', - 'created_at' => '新增时间', - ]; + return SystemizationTodo::query() + ->whereNotNull($column) + ->where($column, '<>', '') + ->pluck($column) + ->flatMap(static fn (string $value): array => array_map('trim', preg_split('/[、,,]/u', $value) ?: [])) + ->filter() + ->unique() + ->sort() + ->values(); } private function normalizeDateFilter(mixed $value): string diff --git a/resources/views/business/pages/systemization-todos.blade.php b/resources/views/business/pages/systemization-todos.blade.php index 74aac1f4..687c4fe7 100644 --- a/resources/views/business/pages/systemization-todos.blade.php +++ b/resources/views/business/pages/systemization-todos.blade.php @@ -24,24 +24,23 @@ .sys-todo-filter__actions { display:flex; flex:0 0 auto; gap:.45rem; } .sys-todo-table-wrap { overflow:auto; border:1px solid var(--sys-border); border-radius:.65rem; background:#fff; } .sys-todo-table-wrap { max-height:clamp(24rem,58vh,46rem); overscroll-behavior:contain; scrollbar-gutter:stable; } - .sys-todo-table { width:100%; min-width:88rem; border-collapse:separate; border-spacing:0; font-size:.75rem; } + .sys-todo-table { width:100%; min-width:80rem; border-collapse:separate; border-spacing:0; font-size:.75rem; } .sys-todo-table th { position:sticky; top:0; z-index:4; padding:.55rem .48rem; border-bottom:1px solid var(--sys-border); background:#f3ece0; color:#3d3a35; text-align:left; white-space:nowrap; } .sys-todo-table td { padding:.45rem .48rem; border-bottom:1px solid #eee7da; background:#fff; color:#2f2a23; vertical-align:top; } .sys-todo-table tbody tr { cursor:pointer; } .sys-todo-table tbody tr:hover td { background:#fdfbf6; } .sys-todo-table tbody tr:focus-visible { outline:2px solid var(--sys-gold); outline-offset:-2px; } - .sys-todo-table th:first-child, .sys-todo-table td:first-child { position:sticky; left:0; z-index:3; width:4rem; min-width:4rem; text-align:center; box-shadow:1px 0 0 #e5ddcf; } - .sys-todo-table th:first-child { z-index:6; background:#f3ece0; } + .sys-todo-table__index-cell { width:3.6rem; min-width:3.6rem; text-align:center; } .sys-todo-table th:last-child, .sys-todo-table td:last-child { position:sticky; right:0; z-index:3; width:5.8rem; min-width:5.8rem; background:#fff; box-shadow:-1px 0 0 #e5ddcf; } .sys-todo-table th:last-child { z-index:6; background:#f3ece0; } - .sys-todo-table tbody tr:hover td:first-child, .sys-todo-table tbody tr:hover td:last-child { background:#fdfbf6; } + .sys-todo-table tbody tr:hover td:last-child { background:#fdfbf6; } .sys-todo-table__index { color:#6d5630; font-weight:600; font-variant-numeric:tabular-nums; white-space:nowrap; } .sys-todo-table__group { vertical-align:middle; background:#fcfaf5; font-weight:600; } - .sys-todo-table__section { min-width:14rem; max-width:20rem; } + .sys-todo-table__section { min-width:11rem; max-width:17rem; } .sys-todo-table__title, .sys-todo-table__progress, .sys-todo-table__notes { line-height:1.45; white-space:pre-line; overflow-wrap:anywhere; } - .sys-todo-table__title { min-width:20rem; max-width:27rem; font-weight:600; } - .sys-todo-table__progress { min-width:15rem; max-width:23rem; } - .sys-todo-table__notes { min-width:11rem; max-width:18rem; } + .sys-todo-table__title { min-width:17rem; max-width:24rem; font-weight:600; } + .sys-todo-table__progress { min-width:12rem; max-width:20rem; } + .sys-todo-table__notes { min-width:9rem; max-width:16rem; } .sys-todo-table__clamp { display:-webkit-box; overflow:hidden; -webkit-box-orient:vertical; -webkit-line-clamp:3; } .sys-todo-table__section .sys-todo-table__clamp { -webkit-line-clamp:2; } .sys-todo-badge { display:inline-flex; align-items:center; padding:.1rem .45rem; border:1px solid #dfd6c5; border-radius:999px; background:#faf6ee; color:#51483c; white-space:nowrap; } @@ -167,19 +166,20 @@ @endforeach - - - - + +
@@ -188,13 +188,29 @@ @php + $cleanSectionLabel = static function (?string $value): string { + return trim((string) preg_replace( + '/^\s*(?:\d+|[一二三四五六七八九十百]+)\s*[、..::)]\s*/u', + '', + (string) $value, + )); + }; + $sectionLabels = $items + ->map(static fn ($groupItem): string => $cleanSectionLabel($groupItem->section)) + ->all(); + $groupValues = [ + 'category' => $items->map(static fn ($groupItem): string => trim((string) ($groupItem->category ?? '')))->all(), + 'section' => $items->map(static fn ($groupItem, int $index): string => trim((string) ($groupItem->category ?? '')) + . "\0" + . ($sectionLabels[$index] ?? ''))->all(), + ]; $groupRowspans = ['category' => [], 'section' => []]; foreach (['category', 'section'] as $groupField) { $start = 0; $previous = null; $count = 0; foreach ($items as $index => $groupItem) { - $current = trim((string) ($groupItem->{$groupField} ?? '')); + $current = (string) ($groupValues[$groupField][$index] ?? ''); if ($index === 0) { $start = 0; $previous = $current; @@ -221,13 +237,14 @@ } } $lastItem = $items->last(); + $lastSectionLabel = $items->isNotEmpty() ? ($sectionLabels[$items->count() - 1] ?? '') : ''; @endphp
- + @@ -246,7 +263,7 @@ $todoPayload = [ 'id' => $item->id, 'category' => $item->category ?? '', - 'section' => $item->section ?? '', + 'section' => $sectionLabels[$loop->index] ?? '', 'title' => $item->title, 'owner' => $item->owner ?? '', 'assignee' => $item->assignee ?? '', @@ -270,15 +287,15 @@ $sectionSpan = $groupRowspans['section'][$loop->index] ?? 1; @endphp - @if ($categorySpan !== 0) @endif @if ($sectionSpan !== 0) @endif + @@ -329,10 +346,10 @@
- 点击表内任意事项可直接编辑;表格在框内滚动,表头、序号和操作列保持可见。 + 点击表内任意事项可直接编辑;表格在框内滚动,表头和操作列保持可见。
@@ -407,7 +424,7 @@ @foreach ($todoCategories as $category) - @foreach ($todoOwners as $owner) + @foreach ($todoOwners->merge($todoAssignees)->unique()->sort() as $person)
序号类型内容待办事项总负责人责任人类型内容序号待办事项总负责人责任人 开始时间完成时间状态完成情况完成截图备注操作
{{ $items->firstItem() + $loop->index }} 1) rowspan="{{ $categorySpan }}" @endif>{{ $item->category ?: '—' }} 1) rowspan="{{ $sectionSpan }}" @endif> -
{{ $item->section ?: '—' }}
+
{{ $sectionLabels[$loop->index] ?: '—' }}
{{ $items->firstItem() + $loop->index }}
{{ $item->title }}
{{ $item->owner ?: '—' }} {{ $item->assignee ?: '—' }}