ocr-record-view-new.blade.php 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754
  1. <x-filament-panels::page>
  2. <div class="space-y-6" @if($isGenerating) wire:poll.5s="checkGenerationStatus" @endif>
  3. @php
  4. $record = $this->record();
  5. @endphp
  6. {{-- 面包屑导航 --}}
  7. <div class="breadcrumbs text-sm">
  8. <ul>
  9. <li>
  10. <a href="{{ route('filament.admin.pages.ocr-records') }}" class="link link-primary link-hover no-underline">
  11. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  12. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path>
  13. </svg>
  14. OCR识别记录
  15. </a>
  16. </li>
  17. <li>
  18. <span class="text-base-content/60">/</span>
  19. </li>
  20. <li>记录详情 #{{ $record->id }}</li>
  21. </ul>
  22. </div>
  23. @if($record)
  24. {{-- 头部信息卡片 --}}
  25. <div class="card bg-base-100 border border-base-300 shadow-xl">
  26. <div class="card-body">
  27. <div class="flex flex-col lg:flex-row justify-between gap-4">
  28. <div class="flex items-center gap-3">
  29. <div class="avatar placeholder">
  30. <div class="bg-primary text-primary-content rounded-full w-12 h-12 flex items-center justify-center">
  31. <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  32. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
  33. </svg>
  34. </div>
  35. </div>
  36. <div>
  37. <h1 class="text-2xl font-bold">OCR记录详情</h1>
  38. <p class="text-sm text-base-content/60">记录ID: {{ $record->id }}</p>
  39. </div>
  40. </div>
  41. <div class="flex items-center gap-2">
  42. @php
  43. $statusConfig = $this->getStatusBadgeConfig($record->status);
  44. @endphp
  45. <div class="badge {{ $statusConfig['class'] }} gap-2">
  46. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  47. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
  48. </svg>
  49. {{ $statusConfig['text'] }}
  50. </div>
  51. @if($record->status === 'pending' || $record->status === 'failed')
  52. <button
  53. wire:click="startRecognition"
  54. class="btn btn-primary btn-sm"
  55. >
  56. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  57. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path>
  58. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
  59. </svg>
  60. 开始识别
  61. </button>
  62. @endif
  63. <a href="{{ route('filament.admin.pages.ocr-records') }}" class="btn btn-ghost btn-sm">
  64. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  65. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path>
  66. </svg>
  67. 返回列表
  68. </a>
  69. </div>
  70. </div>
  71. <div class="stats stats-vertical lg:stats-horizontal shadow bg-base-200 border border-base-300">
  72. <div class="stat">
  73. <div class="stat-figure text-primary">
  74. <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  75. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path>
  76. </svg>
  77. </div>
  78. <div class="stat-title">学生</div>
  79. <div class="stat-value text-lg">{{ $record->student->name ?? '未知' }}</div>
  80. <div class="stat-desc">{{ $record->student->grade ?? '-' }} - {{ $record->student->class_name ?? '-' }}</div>
  81. </div>
  82. <div class="stat">
  83. <div class="stat-figure text-secondary">
  84. <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  85. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
  86. </svg>
  87. </div>
  88. <div class="stat-title">图片</div>
  89. <div class="stat-value text-lg">{{ $record->image_filename }}</div>
  90. <div class="stat-desc">{{ number_format($record->image_size / 1024, 2) }} KB</div>
  91. </div>
  92. <div class="stat">
  93. <div class="stat-figure text-info">
  94. <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  95. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path>
  96. </svg>
  97. </div>
  98. <div class="stat-title">进度</div>
  99. <div class="stat-value text-lg">{{ $record->processed_questions ?? 0 }}/{{ $record->total_questions ?? 0 }}</div>
  100. <div class="stat-desc">
  101. @if($record->total_questions > 0)
  102. @php
  103. $percent = round(($record->processed_questions / $record->total_questions) * 100, 1);
  104. @endphp
  105. <progress class="progress progress-primary w-16" value="{{ $percent }}" max="100"></progress>
  106. {{ $percent }}%
  107. @else
  108. 未开始
  109. @endif
  110. </div>
  111. </div>
  112. <div class="stat">
  113. <div class="stat-figure text-success">
  114. <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  115. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
  116. </svg>
  117. </div>
  118. <div class="stat-title">置信度</div>
  119. <div class="stat-value text-lg">
  120. @if($record->confidence_avg)
  121. {{ number_format($record->confidence_avg * 100, 1) }}%
  122. @else
  123. -
  124. @endif
  125. </div>
  126. <div class="stat-desc">
  127. @if($record->confidence_avg)
  128. @if($record->confidence_avg >= 0.7)
  129. <span class="text-success">优秀</span>
  130. @elseif($record->confidence_avg >= 0.5)
  131. <span class="text-warning">良好</span>
  132. @else
  133. <span class="text-error">需改进</span>
  134. @endif
  135. @else
  136. 暂无数据
  137. @endif
  138. </div>
  139. </div>
  140. </div>
  141. @if($record->error_message)
  142. <div class="alert alert-error mt-4">
  143. <svg class="w-6 h-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
  144. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
  145. </svg>
  146. <div>
  147. <h3 class="font-bold">错误信息</h3>
  148. <div class="text-xs">{{ $record->error_message }}</div>
  149. </div>
  150. </div>
  151. @endif
  152. </div>
  153. </div>
  154. {{-- 图片预览卡片 --}}
  155. @if($record->image_path)
  156. <div class="card bg-base-100 border border-base-300 shadow-xl">
  157. <div class="card-body">
  158. <h2 class="card-title flex items-center gap-2">
  159. <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  160. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
  161. </svg>
  162. 原图预览
  163. </h2>
  164. <div class="mockup-window border border-base-300">
  165. <div class="mockup-browser-toolbar">
  166. <div class="mockup-browser-dot"></div>
  167. <div class="mockup-browser-dot"></div>
  168. <div class="mockup-browser-dot"></div>
  169. </div>
  170. <div class="mockup-browser-content bg-base-200">
  171. <img
  172. src="{{ asset('storage/' . $record->image_path) }}"
  173. alt="卷子图片"
  174. class="w-full h-auto rounded-b-lg"
  175. >
  176. </div>
  177. </div>
  178. <div class="mt-4 grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
  179. <div class="stat bg-base-200 rounded-lg border border-base-300">
  180. <div class="stat-title text-xs">文件大小</div>
  181. <div class="stat-value text-sm">{{ number_format($record->image_size / 1024, 2) }} KB</div>
  182. </div>
  183. @if($record->image_width)
  184. <div class="stat bg-base-200 rounded-lg border border-base-300">
  185. <div class="stat-title text-xs">宽度</div>
  186. <div class="stat-value text-sm">{{ $record->image_width }} px</div>
  187. </div>
  188. @endif
  189. @if($record->image_height)
  190. <div class="stat bg-base-200 rounded-lg border border-base-300">
  191. <div class="stat-title text-xs">高度</div>
  192. <div class="stat-value text-sm">{{ $record->image_height }} px</div>
  193. </div>
  194. @endif
  195. <div class="stat bg-base-200 rounded-lg border border-base-300">
  196. <div class="stat-title text-xs">创建时间</div>
  197. <div class="stat-value text-sm">{{ $record->created_at->format('m-d H:i') }}</div>
  198. </div>
  199. </div>
  200. </div>
  201. </div>
  202. @endif
  203. {{-- 题目识别结果列表 --}}
  204. <div class="card bg-base-100 border border-base-300 shadow-xl">
  205. <div class="card-body">
  206. <div class="flex justify-between items-center mb-6">
  207. <h2 class="card-title text-xl flex items-center gap-2">
  208. <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  209. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path>
  210. </svg>
  211. 题目识别结果 ({{ count($record->questions ?? []) }} 道题)
  212. </h2>
  213. @if(!$this->hasAnalysisResults)
  214. <div class="badge badge-primary badge-lg gap-2">
  215. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  216. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
  217. </svg>
  218. 待分析
  219. </div>
  220. @else
  221. <div class="badge badge-success badge-lg gap-2">
  222. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  223. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
  224. </svg>
  225. 已分析
  226. </div>
  227. @endif
  228. </div>
  229. {{-- 题库生成提示 --}}
  230. @if(!$this->canSubmitAnalysis())
  231. <div class="alert alert-warning shadow-sm mb-6">
  232. <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current flex-shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /></svg>
  233. <div class="flex-1">
  234. <h3 class="font-bold">需生成题库题目</h3>
  235. <div class="text-xs">当前有题目尚未关联题库,必须先生成题库题目才能进行 AI 分析。</div>
  236. @if($isGenerating)
  237. <div class="mt-2 flex items-center gap-2 text-xs">
  238. <span class="loading loading-spinner loading-xs"></span>
  239. 正在生成中,请稍候...
  240. </div>
  241. @endif
  242. </div>
  243. <div class="flex-none">
  244. <button
  245. wire:click="generateQuestionBankQuestions"
  246. class="btn btn-sm btn-ghost border-current"
  247. @if($isGenerating) disabled @endif
  248. wire:loading.attr="disabled"
  249. wire:target="generateQuestionBankQuestions"
  250. >
  251. <span wire:loading.remove wire:target="generateQuestionBankQuestions">
  252. @if($isGenerating)
  253. 生成中...
  254. @else
  255. 生成题库题目
  256. @endif
  257. </span>
  258. <span wire:loading wire:target="generateQuestionBankQuestions">
  259. <span class="loading loading-spinner loading-xs"></span>
  260. 提交中...
  261. </span>
  262. </button>
  263. </div>
  264. </div>
  265. @endif
  266. @if($record->questions && count($record->questions) > 0)
  267. <div class="overflow-x-auto">
  268. <table class="table table-zebra table-compact">
  269. <thead>
  270. <tr>
  271. <th class="w-12">#</th>
  272. <th>题目内容</th>
  273. <th>学生答案</th>
  274. <th>题库关联</th>
  275. <th class="w-32">手动校准</th>
  276. <th class="w-32">判卷</th>
  277. <th class="w-24">AI分析</th>
  278. <th class="w-32">状态</th>
  279. </tr>
  280. </thead>
  281. <tbody>
  282. @foreach($record->questions as $index => $question)
  283. <tr class="hover">
  284. <td>
  285. <div class="badge badge-primary badge-sm">
  286. {{ $question->question_number }}
  287. </div>
  288. </td>
  289. <td>
  290. <div class="max-w-xs">
  291. @if($question->question_text)
  292. <p class="text-sm leading-tight">@math($question->question_text)</p>
  293. @else
  294. <span class="text-gray-400 italic text-sm">未识别到题目内容</span>
  295. @endif
  296. </div>
  297. </td>
  298. <td>
  299. <div class="flex items-center gap-2">
  300. <div class="text-sm font-medium">
  301. @if($question->student_answer)
  302. <span class="text-primary">@math($question->student_answer)</span>
  303. @else
  304. <span class="text-gray-400 italic">未识别</span>
  305. @endif
  306. </div>
  307. @if($question->answer_confidence)
  308. <div class="w-2 h-2 rounded-full" style="background-color: {{ $question->answer_confidence >= 0.7 ? '#10b981' : ($question->answer_confidence >= 0.5 ? '#f59e0b' : '#ef4444') }}"></div>
  309. @endif
  310. </div>
  311. </td>
  312. <td>
  313. @if($question->question_bank_id)
  314. <div class="badge badge-success badge-sm gap-1" title="题库ID: {{ $question->question_bank_id }}">
  315. <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg>
  316. 已关联
  317. </div>
  318. <div class="text-[10px] text-base-content/40 font-mono mt-1">ID:{{ $question->question_bank_id }}</div>
  319. @else
  320. @php
  321. $genStatus = $questionGenerationStatus[$question->id] ?? ($question->generation_status ?? 'pending');
  322. @endphp
  323. @if($genStatus === 'generating')
  324. <div class="badge badge-info badge-sm gap-1">
  325. <span class="loading loading-spinner loading-xs w-3 h-3"></span>
  326. 生成中
  327. </div>
  328. @elseif($genStatus === 'failed')
  329. <div class="badge badge-error badge-sm gap-1 tooltip" data-tip="{{ $question->generation_error }}">
  330. <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
  331. 失败
  332. </div>
  333. @else
  334. <div class="badge badge-warning badge-sm gap-1">
  335. 未关联
  336. </div>
  337. @endif
  338. @endif
  339. </td>
  340. <td>
  341. <input
  342. type="text"
  343. wire:model.defer="manualAnswers.{{ $question->id }}"
  344. placeholder="手动输入答案"
  345. class="input input-bordered input-xs w-full"
  346. maxlength="10"
  347. value="{{ $question->manual_answer }}"
  348. >
  349. @if($question->manual_answer)
  350. <div class="mt-1">
  351. <div class="badge badge-success badge-xs">
  352. <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  353. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
  354. </svg>
  355. 已校验
  356. </div>
  357. </div>
  358. @endif
  359. </td>
  360. <td>
  361. {{-- 判卷区域 --}}
  362. <div class="space-y-2">
  363. {{-- 对错判断 --}}
  364. <div class="flex gap-1">
  365. <label class="label cursor-pointer gap-1 p-1">
  366. <input
  367. type="radio"
  368. wire:model="questionGrades.{{ $question->id }}.is_correct"
  369. value="1"
  370. class="radio radio-success radio-xs"
  371. >
  372. <span class="text-xs">✓</span>
  373. </label>
  374. <label class="label cursor-pointer gap-1 p-1">
  375. <input
  376. type="radio"
  377. wire:model="questionGrades.{{ $question->id }}.is_correct"
  378. value="0"
  379. class="radio radio-error radio-xs"
  380. >
  381. <span class="text-xs">✗</span>
  382. </label>
  383. </div>
  384. {{-- 分数输入 --}}
  385. <input
  386. type="number"
  387. wire:model="questionGrades.{{ $question->id }}.score"
  388. placeholder="分数"
  389. class="input input-bordered input-xs w-full"
  390. min="0"
  391. max="100"
  392. step="0.5"
  393. >
  394. </div>
  395. </td>
  396. <td>
  397. @if($question->ai_score !== null || $question->ai_feedback !== null)
  398. <div class="space-y-1">
  399. @if($question->ai_score !== null)
  400. <div class="text-center">
  401. <div class="stat-value text-lg text-success">{{ $question->ai_score }}</div>
  402. <div class="stat-title text-xs">AI评分</div>
  403. </div>
  404. @endif
  405. @if($question->ai_confidence)
  406. <div class="w-full">
  407. <progress class="progress progress-success w-full h-1" value="{{ $question->ai_confidence * 100 }}" max="100"></progress>
  408. <div class="text-xs text-center mt-1">{{ number_format($question->ai_confidence * 100, 1) }}%</div>
  409. </div>
  410. @endif
  411. </div>
  412. @else
  413. <div class="text-center">
  414. <div class="badge badge-ghost badge-sm">
  415. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  416. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
  417. </svg>
  418. 待分析
  419. </div>
  420. </div>
  421. @endif
  422. </td>
  423. <td>
  424. <div class="flex flex-wrap gap-1">
  425. @if($question->answer_verified)
  426. <div class="badge badge-success badge-xs">
  427. <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  428. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
  429. </svg>
  430. 已校验
  431. </div>
  432. @endif
  433. @if($question->score_value !== null)
  434. <div class="badge badge-info badge-xs">
  435. <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  436. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11.049 2.927c.3-.921 1.603-.921 2.902 0l1.519 1.519a.922.922 0 011.603.921l1.518-1.519a.922.922 0 012.902 0l2.12 2.12a.922.922 0 010-1.303l-2.12-2.12z"></path>
  437. </svg>
  438. {{ $question->score_value }}分
  439. </div>
  440. @endif
  441. @if($question->kp_code)
  442. <div class="badge badge-warning badge-xs">
  443. <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  444. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 00-3.86.517L6.05 15.21a2 2 0 00-1.022.547c-.505 0-.903.197-1.255.537L3.82 16.673a6 6 0 003.86 2.518l.318.158a6 6 0 003.86-.517l2.387-.477a2 2 0 001.022-.547zM10 15.272a8 8 0 00-5.457-2.91l3.569-3.569a8 8 0 015.458 2.91l-3.57 3.568a8 8 0 00-5.457 2.91z"></path>
  445. </svg>
  446. {{ $question->kp_code }}
  447. </div>
  448. @endif
  449. @if($question->mark_detected)
  450. <div class="text-lg">
  451. {!! $question->mark_badge ?? $question->mark_detected !!}
  452. </div>
  453. @endif
  454. </div>
  455. </td>
  456. </tr>
  457. @endforeach
  458. </tbody>
  459. </table>
  460. </div>
  461. {{-- 提交分析按钮 --}}
  462. @if(!$this->hasAnalysisResults)
  463. <div class="card bg-primary/10 border border-primary mt-6">
  464. <div class="card-body">
  465. <div class="flex justify-between items-center">
  466. <div>
  467. <h3 class="font-bold text-lg flex items-center gap-2">
  468. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  469. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path>
  470. </svg>
  471. 提交 AI 分析
  472. </h3>
  473. <p class="text-sm text-base-content/70 mt-1">
  474. 将使用手动校准的答案(如有),否则使用 OCR 识别结果进行智能分析
  475. </p>
  476. @if(!$this->canSubmitAnalysis())
  477. <div class="text-xs text-warning mt-2">
  478. <svg class="w-3 h-3 inline-block mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  479. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.5 0L5.268 16.5c-.77.833.192 2.5 1.732 2.5z"></path>
  480. </svg>
  481. 需要先完成题库题目生成
  482. </div>
  483. @endif
  484. </div>
  485. <button
  486. wire:click="submitForAnalysis"
  487. class="btn btn-primary btn-lg"
  488. wire:loading.attr="disabled"
  489. @if(!$this->canSubmitAnalysis()) disabled @endif
  490. >
  491. <span wire:loading.remove>
  492. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  493. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path>
  494. </svg>
  495. </span>
  496. <span wire:loading class="loading loading-spinner loading-sm"></span>
  497. <span wire:loading.remove>提交分析</span>
  498. <span wire:loading>分析中...</span>
  499. </button>
  500. </div>
  501. </div>
  502. </div>
  503. @else
  504. <div class="card bg-success/10 border border-success mt-6">
  505. <div class="card-body">
  506. <div class="flex justify-between items-center">
  507. <div>
  508. <h3 class="font-bold text-lg text-success flex items-center gap-2">
  509. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  510. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
  511. </svg>
  512. AI 分析已完成
  513. </h3>
  514. <p class="text-sm text-base-content/70 mt-1">
  515. 所有题目已完成 AI 智能分析,查看上方表格中的详细分析结果
  516. </p>
  517. @if($record->ai_analyzed_at)
  518. <div class="flex gap-4 mt-2 text-sm text-base-content/60">
  519. <span>分析完成时间:{{ $record->ai_analyzed_at }}</span>
  520. @if($record->ai_analysis_count)
  521. <span>分析题目数:{{ $record->ai_analysis_count }}</span>
  522. @endif
  523. </div>
  524. @endif
  525. </div>
  526. <div class="badge badge-success badge-lg gap-2">
  527. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  528. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
  529. </svg>
  530. 已分析
  531. </div>
  532. </div>
  533. </div>
  534. </div>
  535. @endif
  536. @else
  537. <div class="text-center py-12 text-base-content/60">
  538. <svg class="w-12 h-12 mx-auto mb-4 opacity-50" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  539. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 13h6m-3-3v6m5 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
  540. </svg>
  541. <p class="text-lg font-medium">暂无识别结果</p>
  542. @if($record->status === 'pending')
  543. <p class="text-sm mt-2">点击上方"开始识别"按钮开始处理</p>
  544. @endif
  545. </div>
  546. @endif
  547. </div>
  548. </div>
  549. {{-- DaisyUI 时间轴 --}}
  550. <div class="card bg-base-100 border border-base-300 shadow-xl">
  551. <div class="card-body">
  552. <h2 class="card-title text-xl flex items-center gap-2 mb-6">
  553. <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  554. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
  555. </svg>
  556. 处理时间线
  557. </h2>
  558. <ul class="timeline timeline-snap-icon timeline-vertical">
  559. <li>
  560. <div class="timeline-middle">
  561. <div class="timeline-box timeline-box-success">
  562. <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  563. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
  564. </svg>
  565. </div>
  566. </div>
  567. <div class="timeline-end">
  568. <time class="text-xs opacity-70">{{ $record->created_at->format('m-d H:i:s') }}</time>
  569. <div class="timeline-title font-bold text-base">上传成功</div>
  570. <div class="timeline-body text-sm opacity-80">卷子图片已上传,等待OCR识别</div>
  571. </div>
  572. <hr class="border-success" />
  573. </li>
  574. @if($record->status === 'processing')
  575. <li>
  576. <div class="timeline-middle">
  577. <div class="timeline-box timeline-box-info animate-pulse">
  578. <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  579. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
  580. </svg>
  581. </div>
  582. </div>
  583. <div class="timeline-end">
  584. <time class="text-xs opacity-70">{{ now()->format('m-d H:i:s') }}</time>
  585. <div class="timeline-title font-bold text-base text-info">处理中</div>
  586. <div class="timeline-body text-sm opacity-80">OCR识别正在进行中...</div>
  587. <div class="loading loading-spinner loading-sm mt-2"></div>
  588. </div>
  589. <hr class="border-info" />
  590. </li>
  591. @elseif($record->status === 'completed')
  592. <li>
  593. <div class="timeline-middle">
  594. <div class="timeline-box timeline-box-success">
  595. <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  596. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
  597. </svg>
  598. </div>
  599. </div>
  600. <div class="timeline-end">
  601. <time class="text-xs opacity-70">{{ $record->processed_at?->format('m-d H:i:s') }}</time>
  602. <div class="timeline-title font-bold text-base text-success">OCR识别完成</div>
  603. <div class="timeline-body text-sm opacity-80">
  604. OCR识别已完成,识别出 {{ $record->total_questions ?? 0 }} 道题目
  605. <div class="badge badge-success badge-sm mt-2">
  606. 准确率: {{ number_format(($record->confidence_avg ?? 0) * 100, 1) }}%
  607. </div>
  608. </div>
  609. </div>
  610. <hr class="border-success" />
  611. </li>
  612. @elseif($record->status === 'failed')
  613. <li>
  614. <div class="timeline-middle">
  615. <div class="timeline-box timeline-box-error">
  616. <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  617. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
  618. </svg>
  619. </div>
  620. </div>
  621. <div class="timeline-end">
  622. <time class="text-xs opacity-70">{{ now()->format('m-d H:i:s') }}</time>
  623. <div class="timeline-title font-bold text-base text-error">OCR识别失败</div>
  624. @if($record->error_message)
  625. <div class="timeline-body text-sm text-error bg-error/10 p-2 rounded mt-2">
  626. {{ $record->error_message }}
  627. </div>
  628. @endif
  629. </div>
  630. <hr class="border-error" />
  631. </li>
  632. @endif
  633. {{-- AI Analysis Timeline --}}
  634. @if($record->status === 'completed' && $record->ai_analyzed_at)
  635. <li>
  636. <div class="timeline-middle">
  637. <div class="timeline-box timeline-box-info">
  638. <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  639. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
  640. </svg>
  641. </div>
  642. </div>
  643. <div class="timeline-end">
  644. <time class="text-xs opacity-70">{{ $record->ai_analyzed_at }}</time>
  645. <div class="timeline-title font-bold text-base text-info">AI 分析完成</div>
  646. <div class="timeline-body text-sm opacity-80">
  647. 已完成 {{ $record->ai_analysis_count ?? count($record->questions) }} 道题目的智能分析
  648. <div class="flex gap-2 mt-2">
  649. <div class="badge badge-success badge-sm">
  650. <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  651. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
  652. </svg>
  653. 智能分析
  654. </div>
  655. <div class="badge badge-info badge-sm">学习分析</div>
  656. <div class="badge badge-warning badge-sm">掌握度评估</div>
  657. </div>
  658. </div>
  659. </div>
  660. <hr class="border-info" />
  661. </li>
  662. @endif
  663. {{-- 学生仪表板跳转 --}}
  664. @if($record->status === 'completed' && $record->ai_analyzed_at)
  665. <li>
  666. <div class="timeline-middle">
  667. <div class="timeline-box timeline-box-warning">
  668. <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  669. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
  670. </svg>
  671. </div>
  672. </div>
  673. <div class="timeline-end">
  674. <div class="timeline-title font-bold text-base text-warning">查看详细分析</div>
  675. <div class="timeline-body text-sm opacity-80">
  676. 在学生仪表板中查看更详细的学习分析报告
  677. <div class="mt-3">
  678. @if($record->student)
  679. <a href="{{ route('filament.admin.pages.student-dashboard') }}?student_id={{ $record->student->student_id }}"
  680. class="btn btn-primary btn-sm gap-2 hover:btn-primary-focus transition-all">
  681. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  682. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6"></path>
  683. </svg>
  684. 学生仪表板
  685. </a>
  686. @else
  687. <div class="btn btn-disabled btn-sm gap-2">
  688. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  689. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
  690. </svg>
  691. 学生信息缺失
  692. </div>
  693. @endif
  694. </div>
  695. </div>
  696. </div>
  697. </li>
  698. @endif
  699. </ul>
  700. </div>
  701. </div>
  702. @else
  703. <div class="card bg-base-100 border border-base-300 shadow-xl">
  704. <div class="card-body">
  705. <div class="alert alert-error">
  706. <svg class="w-6 h-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
  707. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
  708. </svg>
  709. <div>
  710. <h3 class="font-bold">记录不存在</h3>
  711. <div class="text-xs">找不到ID为 {{ $recordId }} 的OCR记录</div>
  712. </div>
  713. </div>
  714. <div class="mt-4">
  715. <a href="{{ route('filament.admin.pages.ocr-records') }}" class="btn btn-primary">
  716. <svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  717. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path>
  718. </svg>
  719. 返回列表
  720. </a>
  721. </div>
  722. </div>
  723. </div>
  724. @endif
  725. </div>
  726. <x-math-render />
  727. </x-filament-panels::page>