Parcourir la source

feat(qc-review): expose tem metadata and improve import action visibility

Show tem audit metadata in review cards, compress status display into tags, and color the direct import action by qualification state for faster operator decisions.

Made-with: Cursor
yemeishu il y a 2 semaines
Parent
commit
d3ac2c6c18

+ 42 - 1
app/Filament/Pages/QuestionTemQualityReview.php

@@ -199,7 +199,7 @@ class QuestionTemQualityReview extends Page
     /**
      * 独立审核卡片模型:清洗题干、给出三项快检,并附上该题完整渲染所需分组数据。
      *
-     * @return list<array{id:int, stem_preview:string, checks:array{stem:bool,answer:bool,solution:bool}, grouped_questions:array{choice:array<int,object>,fill:array<int,object>,answer:array<int,object>}}>
+     * @return list<array{id:int, stem_preview:string, checks:array{stem:bool,answer:bool,solution:bool}, meta:array{created_at:?string,updated_at:?string,difficulty:string,audit_reason:string}, grouped_questions:array{choice:array<int,object>,fill:array<int,object>,answer:array<int,object>}}>
      */
     #[Computed]
     public function temQuestionCards(): array
@@ -226,6 +226,12 @@ class QuestionTemQualityReview extends Page
                         'answer' => $answer !== '',
                         'solution' => $solution !== '',
                     ],
+                    'meta' => [
+                        'created_at' => $this->formatMetaDatetime($arr['created_at'] ?? null),
+                        'updated_at' => $this->formatMetaDatetime($arr['updated_at'] ?? null),
+                        'difficulty' => $this->formatMetaDifficulty($arr['difficulty'] ?? null),
+                        'audit_reason' => $this->formatMetaAuditReason($arr['audit_reason'] ?? null),
+                    ],
                     'grouped_questions' => $this->buildPreviewGroupedQuestionsFromTemRows([$arr]),
                 ];
             }
@@ -981,6 +987,41 @@ class QuestionTemQualityReview extends Page
         return trim($text);
     }
 
+    private function formatMetaDatetime(mixed $value): ?string
+    {
+        if ($value === null) {
+            return null;
+        }
+        $raw = trim((string) $value);
+        if ($raw === '' || $raw === '0000-00-00 00:00:00') {
+            return null;
+        }
+
+        try {
+            return \Carbon\Carbon::parse($raw)->format('Y-m-d H:i:s');
+        } catch (\Throwable) {
+            return $raw;
+        }
+    }
+
+    private function formatMetaDifficulty(mixed $value): string
+    {
+        if ($value === null || $value === '') {
+            return '-';
+        }
+        if (! is_numeric($value)) {
+            return (string) $value;
+        }
+
+        return number_format((float) $value, 2, '.', '');
+    }
+
+    private function formatMetaAuditReason(mixed $value): string
+    {
+        $text = trim((string) ($value ?? ''));
+        return $text !== '' ? $text : '-';
+    }
+
     private function buildPageCacheKey(string $suffix): string
     {
         $uid = Auth::id() ?: 'guest';

+ 22 - 2
resources/views/filament/pages/question-tem-quality-review.blade.php

@@ -229,7 +229,7 @@
                             .qtr-card { border: 1px solid rgb(229 231 235); border-radius: 0.5rem; padding: 0.5rem; }
                             .dark .qtr-card { border-color: rgb(75 85 99); }
                             .qtr-badge { font-size: 11px; border-radius: 999px; padding: 1px 8px; border: 1px solid transparent; }
-                            .qtr-badge-ok { color: #166534; background: #dcfce7; border-color: #86efac; }
+                            .qtr-badge-ok { color: #166534; background: #bbf7d0; border-color: #4ade80; }
                             .qtr-badge-no { color: #991b1b; background: #fee2e2; border-color: #fca5a5; }
                             .dark .qtr-badge-ok { color: #86efac; background: rgba(22,101,52,0.25); border-color: rgba(134,239,172,0.35); }
                             .dark .qtr-badge-no { color: #fca5a5; background: rgba(127,29,29,0.25); border-color: rgba(252,165,165,0.35); }
@@ -241,6 +241,13 @@
                                 @php
                                     $tid = (int) $card['id'];
                                     $checks = $card['checks'];
+                                    $meta = $card['meta'] ?? [];
+                                    $auditReason = trim((string) ($meta['audit_reason'] ?? ''));
+                                    $isQualified = $auditReason === '合格';
+                                    $updatedDate = '';
+                                    if (! empty($meta['updated_at']) && $meta['updated_at'] !== '-') {
+                                        $updatedDate = substr((string) $meta['updated_at'], 0, 10);
+                                    }
                                     $grouped = $card['grouped_questions'] ?? ['choice' => [], 'fill' => [], 'answer' => []];
                                     $q = $grouped['choice'][0] ?? $grouped['fill'][0] ?? $grouped['answer'][0] ?? null;
                                 @endphp
@@ -249,6 +256,7 @@
                                         <button type="button" wire:click="focusTemQuestion({{ $tid }})" class="text-left hover:underline">
                                             <span class="font-mono text-xs">tem #{{ $tid }}</span>
                                         </button>
+                                        <span class="qtr-badge qtr-badge-ok">难度 {{ $meta['difficulty'] ?? '-' }}</span>
                                         @if ((int) ($this->selectedTemId ?? 0) === $tid)
                                             <span class="qtr-badge qtr-badge-ok">当前聚焦</span>
                                         @endif
@@ -260,6 +268,11 @@
                                         <span class="qtr-badge {{ $checks['stem'] ? 'qtr-badge-ok' : 'qtr-badge-no' }}">题干</span>
                                         <span class="qtr-badge {{ $checks['answer'] ? 'qtr-badge-ok' : 'qtr-badge-no' }}">答案</span>
                                         <span class="qtr-badge {{ $checks['solution'] ? 'qtr-badge-ok' : 'qtr-badge-no' }}">解题思路</span>
+                                        @if ($updatedDate !== '')
+                                            <span class="qtr-badge qtr-badge-ok">
+                                                {{ $updatedDate }}
+                                            </span>
+                                        @endif
                                     </div>
                                     <div class="qtr-full-preview-wrap">
                                         @if ($q)
@@ -280,6 +293,10 @@
                                     </div>
                                     <div class="mt-3 border-t border-gray-100 pt-2 dark:border-white/10" x-data="{ operated: false }">
                                         @php $st = $this->importStatusMap[$tid] ?? null; @endphp
+                                        @php
+                                            $directBtnBase = $isQualified ? '#16a34a' : '#dc2626';
+                                            $directBtnHover = $isQualified ? '#15803d' : '#b91c1c';
+                                        @endphp
                                         @if ($st)
                                             <div class="mb-1 text-[11px]">
                                                 @if (($st['state'] ?? '') === 'queued')
@@ -312,7 +329,10 @@
                                             @endphp
                                             <button
                                                 type="button"
-                                                class="fi-btn fi-color-danger fi-size-sm inline-flex items-center gap-1.5 rounded-lg px-3 py-2 text-sm font-semibold"
+                                                class="fi-btn fi-size-sm inline-flex items-center gap-1.5 rounded-lg px-3 py-2 text-sm font-semibold text-white shadow-sm transition"
+                                                style="background:{{ $directBtnBase }};"
+                                                onmouseover="this.style.background='{{ $directBtnHover }}'"
+                                                onmouseout="this.style.background='{{ $directBtnBase }}'"
                                                 :disabled="operated || {{ $opLocked ? 'true' : 'false' }}"
                                                 :class="{ 'opacity-80 cursor-not-allowed': operated || {{ $opLocked ? 'true' : 'false' }} }"
                                                 x-on:click.prevent="if (operated || {{ $opLocked ? 'true' : 'false' }}) return; operated = true; $wire.queueImportTem({{ $tid }})"