question-detail.blade.php 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. <div class="min-h-screen bg-gray-50">
  2. <div class="container mx-auto p-6">
  3. {{-- 面包屑导航 --}}
  4. <nav class="flex text-sm" aria-label="Breadcrumb">
  5. <ol class="flex items-center space-x-4">
  6. @foreach ($this->getBreadcrumbs() as $key => $breadcrumb)
  7. <li class="flex items-center">
  8. @if ($key !== array_key_last($this->getBreadcrumbs()))
  9. <a href="{{ $breadcrumb['url'] }}" class="text-gray-500 hover:text-gray-700">
  10. {{ $breadcrumb['name'] }}
  11. </a>
  12. <svg class="flex-shrink-0 h-5 w-5 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
  13. <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10l-1.293 1.414a1 1 0 001.414 0z" clip-rule="evenodd" />
  14. </svg>
  15. @else
  16. <span class="text-gray-900">{{ $breadcrumb['name'] }}</span>
  17. @endif
  18. </li>
  19. @endforeach
  20. </ol>
  21. </nav>
  22. @if (empty($this->questionData))
  23. <div class="bg-white rounded-lg shadow p-12 text-center">
  24. <svg class="mx-auto h-12 w-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
  25. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002 2v10a2 2 0 01-2 2H9a2 2 0 01-2-2V7a2 2 0 012-2z" />
  26. </svg>
  27. <h3 class="mt-4 text-lg font-medium text-gray-900">题目不存在</h3>
  28. <p class="mt-2 text-sm text-gray-500">请检查题目ID是否正确</p>
  29. <a href="{{ url()->previous() }}" class="mt-4 inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-indigo-600 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500">
  30. 返回上一页
  31. </a>
  32. </div>
  33. @else
  34. {{-- 主要内容区域 --}}
  35. <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
  36. {{-- 左侧:题目内容 --}}
  37. <div class="lg:col-span-2 space-y-6">
  38. {{-- 题目卡片 --}}
  39. <div class="bg-white rounded-lg shadow-sm p-6">
  40. <div class="flex items-start justify-between mb-4">
  41. {{-- 题目标识 --}}
  42. <div class="flex items-center space-x-3">
  43. @if ($this->sourceType === 'mistake')
  44. @php
  45. $answeredCorrectly = $this->questionData['mistake_info']['correct'] ?? false;
  46. @endphp
  47. <span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium {{ $answeredCorrectly ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700' }}">
  48. {{ $answeredCorrectly ? '答题记录' : '错题' }}
  49. </span>
  50. @else
  51. <span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-blue-100 text-blue-700">
  52. 题库
  53. </span>
  54. @endif
  55. @if ($this->questionData['question_number'] ?? null)
  56. <span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-gray-100 text-gray-700">
  57. 第{{ $this->questionData['question_number'] }}题
  58. </span>
  59. @endif
  60. <span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium {{ $this->getDifficultyColor() }}">
  61. {{ $this->getDifficultyLabel() }}
  62. </span>
  63. </div>
  64. {{-- 操作按钮 --}}
  65. <div class="flex items-center space-x-2">
  66. <button onclick="window.history.back()" class="text-gray-500 hover:text-gray-700">
  67. <svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
  68. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
  69. </svg>
  70. </button>
  71. </div>
  72. </div>
  73. {{-- 题干 --}}
  74. <div class="prose prose-sm max-w-none mb-6">
  75. @php
  76. $displayStem = $this->questionData['display_stem'] ?? $this->questionData['stem'] ?? '';
  77. @endphp
  78. @if (!empty($displayStem))
  79. <div class="p-4 bg-gray-50 rounded-lg">
  80. <h4 class="text-sm font-medium text-gray-700 mb-2">题目</h4>
  81. <div class="text-gray-900">{{ $displayStem }}</div>
  82. </div>
  83. @endif
  84. {{-- 标签 --}}
  85. <div class="flex flex-wrap gap-2 mb-4">
  86. @php
  87. $kpCode = $this->questionData['kp_code'] ?? '';
  88. $kpName = $this->getKnowledgePointName();
  89. @endphp
  90. @if ($kpCode)
  91. <span class="inline-flex items-center px-3 py-1 rounded-lg text-sm font-medium bg-indigo-50 text-indigo-700 hover:bg-indigo-100 transition-colors">
  92. <a href="{{ url('/admin/knowledge-point-detail') }}?kp_code={{ urlencode($kpCode) }}" class="hover:text-indigo-900">
  93. {{ $kpName }}
  94. </a>
  95. </span>
  96. @endif
  97. @if (!empty($this->questionData['tags']))
  98. @foreach(json_decode($this->questionData['tags'], true) as $tag)
  99. <span class="inline-flex items-center px-2 py-1 rounded text-xs bg-gray-100 text-gray-600">
  100. {{ $tag }}
  101. </span>
  102. @endforeach
  103. @endif
  104. @if (!empty($this->questionData['skills']) && is_array($this->questionData['skills']))
  105. @foreach($this->questionData['skills'] as $skill)
  106. <span class="inline-flex items-center px-2 py-1 rounded text-xs bg-teal-50 text-teal-600">
  107. {{ $skill }}
  108. </span>
  109. @endforeach
  110. @endif
  111. </div>
  112. {{-- 选择题选项(独立呈现,不与题干混排) --}}
  113. @php
  114. $options = $this->questionData['display_options'] ?? ($this->questionData['options'] ?? []);
  115. @endphp
  116. @if (!empty($options) && is_array($options))
  117. <div class="mt-4">
  118. <h4 class="text-sm font-medium text-gray-700 mb-2">选项</h4>
  119. <div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
  120. @php
  121. $letters = range('A', 'Z');
  122. @endphp
  123. @foreach($options as $idx => $option)
  124. @php
  125. $label = $letters[$idx] ?? chr(65 + ($idx % 26));
  126. @endphp
  127. <div class="flex items-start space-x-2">
  128. <span class="font-semibold text-gray-700">{{ $label }}.</span>
  129. <div class="text-gray-900">{!! $option !!}</div>
  130. </div>
  131. @endforeach
  132. </div>
  133. </div>
  134. @endif
  135. {{-- 答案对比(如果是错题) --}}
  136. @if ($this->sourceType === 'mistake' && isset($this->questionData['mistake_info']))
  137. <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
  138. @php
  139. $answeredCorrectly = $this->questionData['mistake_info']['correct'] ?? false;
  140. $studentBg = $answeredCorrectly ? 'bg-green-50' : 'bg-red-50';
  141. $studentText = $answeredCorrectly ? 'text-green-700' : 'text-red-700';
  142. $statusLabel = $answeredCorrectly ? '本次作答正确' : '本次作答错误';
  143. @endphp
  144. <div class="p-4 rounded-lg {{ $studentBg }}">
  145. <h4 class="text-sm font-medium {{ $studentText }} mb-2">学生作答</h4>
  146. <p class="text-gray-900">{{ $this->questionData['mistake_info']['student_answer'] ?? '未作答' }}</p>
  147. @if (isset($this->questionData['mistake_info']['score']))
  148. <div class="mt-2 text-sm text-gray-500">
  149. 得分: {{ $this->questionData['mistake_info']['score'] }}/{{ $this->questionData['mistake_info']['full_score'] ?? '-' }}
  150. </div>
  151. @endif
  152. <div class="mt-2 inline-flex items-center px-2 py-1 rounded-full text-xs font-medium {{ $answeredCorrectly ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700' }}">
  153. {{ $statusLabel }}
  154. </div>
  155. </div>
  156. <div class="p-4 bg-green-50 rounded-lg">
  157. <h4 class="text-sm font-medium text-green-700 mb-2">正确答案</h4>
  158. <p class="text-gray-900">{{ $this->questionData['answer'] ?? '暂无' }}</p>
  159. </div>
  160. </div>
  161. @else
  162. {{-- 正确答案 --}}
  163. @if (!empty($this->questionData['answer']))
  164. <div class="p-4 bg-green-50 rounded-lg mb-4">
  165. <h4 class="text-sm font-medium text-green-700 mb-2">正确答案</h4>
  166. <p class="text-gray-900 text-lg font-mono">{{ $this->questionData['answer'] }}</p>
  167. </div>
  168. @endif
  169. @endif
  170. {{-- 解题思路 --}}
  171. @if (!empty($this->questionData['solution']))
  172. <div class="p-4 bg-blue-50 rounded-lg">
  173. <h4 class="text-sm font-medium text-blue-700 mb-2">解题思路</h4>
  174. <div class="prose prose-sm max-w-none text-gray-900">
  175. {!! $this->questionData['solution'] !!}
  176. </div>
  177. </div>
  178. @endif
  179. </div>
  180. </div>
  181. {{-- AI分析(如果是错题) --}}
  182. @if ($this->sourceType === 'mistake' && !empty($this->questionData['mistake_info']['ai_analysis']))
  183. <div class="bg-white rounded-lg shadow-sm p-6">
  184. <h3 class="text-lg font-semibold text-gray-900 mb-4 flex items-center">
  185. <svg class="w-5 h-5 mr-2 text-amber-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
  186. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-9M3 21l6.364-6.364M3 12l6.364 6.364M12 3L12 21m0-10.5v9M3 12l6.364-6.364" />
  187. </svg>
  188. AI 智能分析
  189. </h3>
  190. <div class="space-y-4">
  191. @if (!empty($this->questionData['mistake_info']['ai_analysis']['reason']))
  192. <div class="bg-amber-50 p-4 rounded-lg">
  193. <h4 class="text-sm font-medium text-amber-700 mb-2">错误原因</h4>
  194. <p class="text-gray-900">{{ $this->questionData['mistake_info']['ai_analysis']['reason'] }}</p>
  195. </div>
  196. @endif
  197. @if (!empty($this->questionData['mistake_info']['ai_analysis']['solution']))
  198. <div class="bg-blue-50 p-4 rounded-lg">
  199. <h4 class="text-sm font-medium text-blue-700 mb-2">改进方法</h4>
  200. <p class="text-gray-900">{{ $this->questionData['mistake_info']['ai_analysis']['solution'] }}</p>
  201. </div>
  202. @endif
  203. @if (!empty($this->questionData['mistake_info']['ai_analysis']['suggestions']))
  204. <div class="bg-green-50 p-4 rounded-lg">
  205. <h4 class="text-sm font-medium text-green-700 mb-2">学习建议</h4>
  206. <p class="text-gray-900">{{ $this->questionData['mistake_info']['ai_analysis']['suggestions'] }}</p>
  207. </div>
  208. @endif
  209. @if (!empty($this->questionData['mistake_info']['ai_analysis']['next_steps']))
  210. <div class="bg-purple-50 p-4 rounded-lg">
  211. <h4 class="text-sm font-medium text-purple-700 mb-2">后续步骤</h4>
  212. <ul class="list-disc list-inside text-gray-900 space-y-1">
  213. @php
  214. $nextSteps = $this->questionData['mistake_info']['ai_analysis']['next_steps'];
  215. if (!is_array($nextSteps)) {
  216. $nextSteps = [];
  217. }
  218. @endphp
  219. @foreach($nextSteps as $item)
  220. <li>{{ $item }}</li>
  221. @endforeach
  222. </ul>
  223. </div>
  224. @endif
  225. @php
  226. $modelUsed = $this->questionData['mistake_info']['ai_analysis']['model_used'] ?? '';
  227. @endphp
  228. @if ($modelUsed)
  229. <p class="text-xs text-gray-500 mt-2">分析模型: {{ $modelUsed }}</p>
  230. @endif
  231. </div>
  232. </div>
  233. @endif
  234. {{-- 相关题目 --}}
  235. @if (!empty($this->relatedQuestions))
  236. <div class="bg-white rounded-lg shadow-sm p-6">
  237. <h3 class="text-lg font-semibold text-gray-900 mb-4">相似题目</h3>
  238. <div class="space-y-4">
  239. @foreach($this->relatedQuestions as $relatedQuestion)
  240. <div class="border border-gray-200 rounded-lg p-4 hover:bg-gray-50 transition-colors cursor-pointer">
  241. <div class="flex items-center justify-between mb-2">
  242. <span class="text-sm font-medium text-gray-900">
  243. {{ substr($relatedQuestion['stem'] ?? '', 0, 100) }}...
  244. </span>
  245. <a href="/admin/question-detail?question_id={{ $relatedQuestion['id'] }}"
  246. class="text-indigo-600 hover:text-indigo-700 text-sm">
  247. 查看详情
  248. </a>
  249. </div>
  250. @if (!empty($relatedQuestion['difficulty']))
  251. <span class="inline-flex items-center px-2 py-1 rounded text-xs font-medium bg-gray-100 text-gray-700">
  252. {{ $relatedQuestion['difficulty'] < 0.4 ? '简单' : ($relatedQuestion['difficulty'] < 0.7 ? '中等' : '困难') }}
  253. </span>
  254. @endif
  255. </div>
  256. @endforeach
  257. </div>
  258. </div>
  259. @endif
  260. </div>
  261. {{-- 右侧:信息面板 --}}
  262. <div class="space-y-6">
  263. {{-- 题目信息 --}}
  264. @if ($this->questionData)
  265. <div class="bg-white rounded-lg shadow-sm p-6">
  266. <h3 class="text-lg font-semibold text-gray-900 mb-4">题目信息</h3>
  267. <dl class="space-y-3 text-sm">
  268. <div>
  269. <dt class="text-gray-500">题目ID</dt>
  270. <dd class="text-gray-900 font-mono">{{ $this->questionData['id'] ?? $this->questionData['question_code'] }}</dd>
  271. </div>
  272. @if (!empty($this->questionData['kp_code']))
  273. <div>
  274. <dt class="text-gray-500">知识点</dt>
  275. <dd class="text-gray-900">{{ $this->getKnowledgePointName() }}</dd>
  276. </div>
  277. @endif
  278. <div>
  279. <dt class="text-gray-500">难度等级</dt>
  280. <dd class="text-gray-900">{{ $this->getDifficultyLabel() }}</dd>
  281. </div>
  282. @php
  283. $globalAccuracy = $this->questionData['global_accuracy']
  284. ?? $this->questionData['correct_rate']
  285. ?? null;
  286. @endphp
  287. @if ($globalAccuracy !== null)
  288. <div>
  289. <dt class="text-gray-500">全体正确率</dt>
  290. <dd class="text-gray-900">
  291. <span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-blue-50 text-blue-700">
  292. {{ round((float) $globalAccuracy * 100, 1) }}%
  293. </span>
  294. </dd>
  295. </div>
  296. @endif
  297. @if (!empty($this->historySummary))
  298. @php
  299. $accuracy = $this->historySummary['total'] > 0 ? ($this->historySummary['correct'] / $this->historySummary['total']) : null;
  300. if ($accuracy === null) {
  301. $masteryLabel = '未知';
  302. $masteryStyle = 'bg-gray-100 text-gray-700';
  303. } elseif ($accuracy >= 0.8) {
  304. $masteryLabel = '已掌握';
  305. $masteryStyle = 'bg-green-100 text-green-700';
  306. } elseif ($accuracy >= 0.5) {
  307. $masteryLabel = '待巩固';
  308. $masteryStyle = 'bg-amber-100 text-amber-700';
  309. } else {
  310. $masteryLabel = '待攻克';
  311. $masteryStyle = 'bg-red-100 text-red-700';
  312. }
  313. @endphp
  314. <div>
  315. <dt class="text-gray-500">掌握度</dt>
  316. <dd class="text-gray-900">
  317. <span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium {{ $masteryStyle }}">
  318. {{ $masteryLabel }}({{ $this->historySummary['correct'] }}/{{ $this->historySummary['total'] }})
  319. </span>
  320. </dd>
  321. </div>
  322. @endif
  323. @if (!empty($this->questionData['created_at']))
  324. <div>
  325. <dt class="text-gray-500">创建时间</dt>
  326. <dd class="text-gray-900">{{ \Carbon\Carbon::parse($this->questionData['created_at'])->format('Y-m-d H:i:s') }}</dd>
  327. </div>
  328. @endif
  329. </dl>
  330. </div>
  331. @endif
  332. {{-- 作答历史 --}}
  333. @if (!empty($this->historySummary))
  334. <div class="bg-white rounded-lg shadow-sm p-6">
  335. <h3 class="text-lg font-semibold text-gray-900 mb-4">作答历史</h3>
  336. <dl class="space-y-3 text-sm">
  337. <div class="flex items-center justify-between">
  338. <dt class="text-gray-500">总尝试</dt>
  339. <dd class="text-gray-900 font-medium">{{ $this->historySummary['total'] }}</dd>
  340. </div>
  341. <div class="flex items-center justify-between">
  342. <dt class="text-gray-500">答对次数</dt>
  343. <dd class="text-gray-900 font-medium">{{ $this->historySummary['correct'] }}</dd>
  344. </div>
  345. <div class="flex items-center justify-between">
  346. <dt class="text-gray-500">最近结果</dt>
  347. <dd class="text-gray-900">
  348. @if($this->historySummary['last_correct'])
  349. <span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-700">正确</span>
  350. @else
  351. <span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-red-100 text-red-700">错误</span>
  352. @endif
  353. </dd>
  354. </div>
  355. @if(!empty($this->historySummary['last_time']))
  356. <div class="flex items-center justify-between">
  357. <dt class="text-gray-500">最近时间</dt>
  358. <dd class="text-gray-900">
  359. {{ \Carbon\Carbon::parse($this->historySummary['last_time'])->format('Y-m-d H:i') }}
  360. </dd>
  361. </div>
  362. @endif
  363. </dl>
  364. </div>
  365. @endif
  366. {{-- 答题信息(错题/正确均展示) --}}
  367. @if ($this->sourceType === 'mistake' && !empty($this->questionData['mistake_info']))
  368. <div class="bg-white rounded-lg shadow-sm p-6">
  369. <h3 class="text-lg font-semibold text-gray-900 mb-4">
  370. {{ $this->questionData['mistake_info']['correct'] ? '答题信息' : '错题信息' }}
  371. </h3>
  372. <dl class="space-y-3 text-sm">
  373. <div>
  374. <dt class="text-gray-500">记录ID</dt>
  375. <dd class="text-gray-900 font-mono">{{ $this->mistakeId }}</dd>
  376. </div>
  377. <div>
  378. <dt class="text-gray-500">学生</dt>
  379. <dd class="text-gray-900 font-mono">
  380. @if($this->studentName)
  381. {{ $this->studentName }}({{ $this->studentId }})
  382. @else
  383. {{ $this->studentId }}
  384. @endif
  385. </dd>
  386. </div>
  387. <div>
  388. <dt class="text-gray-500">是否正确</dt>
  389. <dd class="text-gray-900">
  390. <span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium {{ $this->questionData['mistake_info']['correct'] ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700' }}">
  391. {{ $this->questionData['mistake_info']['correct'] ? '正确' : '错误' }}
  392. </span>
  393. </dd>
  394. </div>
  395. <div>
  396. <dt class="text-gray-500">错误类型</dt>
  397. <dd class="text-gray-900">{{ $this->questionData['mistake_info']['error_type'] ?? '未分类' }}</dd>
  398. </div>
  399. <div>
  400. <dt class="text-gray-500">错误类别</dt>
  401. <dd class="text-gray-900">{{ $this->questionData['mistake_info']['mistake_category'] ?? '未分类' }}</dd>
  402. </div>
  403. @if (!empty($this->questionData['mistake_info']['created_at']))
  404. <div>
  405. <dt class="text-gray-500">记录时间</dt>
  406. <dd class="text-gray-900">{{ \Carbon\Carbon::parse($this->questionData['mistake_info']['created_at'])->format('Y-m-d H:i:s') }}</dd>
  407. </div>
  408. @endif
  409. </dl>
  410. </div>
  411. @endif
  412. </div>
  413. </div>
  414. @endif
  415. </div>
  416. </div>
  417. </div>