question-check-page.blade.php 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. @php
  2. $choiceQuestions = $questions['choice'] ?? [];
  3. $fillQuestions = $questions['fill'] ?? [];
  4. $answerQuestions = $questions['answer'] ?? [];
  5. $stepPattern = '(步骤\s*[0-9一二三四五六七八九十百零两]+\s*[::]?|第\s*[0-9一二三四五六七八九十百零两]+\s*步\s*[::]?)';
  6. $allQuestions = collect()
  7. ->concat(collect($choiceQuestions)->map(fn ($q) => ['q' => $q, 'detail_type' => 'choice']))
  8. ->concat(collect($fillQuestions)->map(fn ($q) => ['q' => $q, 'detail_type' => 'fill']))
  9. ->concat(collect($answerQuestions)->map(fn ($q) => ['q' => $q, 'detail_type' => 'answer']))
  10. ->sortBy(fn ($item) => (int) (($item['q']->question_number ?? 0)))
  11. ->values();
  12. $normalizeAnswerText = function (?string $answer, bool $compact = false): string {
  13. $answer = trim((string) $answer);
  14. if ($answer === '') {
  15. return '—';
  16. }
  17. $answer = preg_replace('/\s+/u', ' ', $answer) ?? $answer;
  18. $answer = str_replace([';', ';'], [';<wbr>', ';<wbr>'], $answer);
  19. if ($compact) {
  20. $answer = preg_replace('/<br\s*\/?>/iu', ' ', $answer) ?? $answer;
  21. }
  22. return trim($answer);
  23. };
  24. $quickAnswers = $allQuestions->map(function ($item) use ($normalizeAnswerText) {
  25. $q = $item['q'];
  26. $answerText = $normalizeAnswerText($q->answer ?? '', true);
  27. $isLong = mb_strlen(strip_tags($answerText)) > 20
  28. || str_contains($answerText, "\n")
  29. || str_contains($answerText, "\r")
  30. || str_contains($answerText, '{')
  31. || str_contains($answerText, '\\frac')
  32. || str_contains($answerText, '见解析');
  33. $processedAnswer = \App\Services\MathFormulaProcessor::processFormulas($answerText);
  34. return [
  35. 'no' => (int) ($q->question_number ?? 0),
  36. 'answer' => $processedAnswer,
  37. 'long' => $isLong,
  38. ];
  39. })->values();
  40. $normalizeDetailHtml = function (?string $text): string {
  41. $text = trim((string) $text);
  42. if ($text === '') {
  43. return '';
  44. }
  45. $text = preg_replace('/<\s*image\b/iu', '<img', $text) ?? $text;
  46. $text = preg_replace('/<\s*\/\s*image\s*>/iu', '', $text) ?? $text;
  47. $text = preg_replace('/(^|[\s>])img\s+src\s*=\s*([\'"][^\'"]+[\'"])\s*\/?>/iu', '$1<img src=$2 />', $text) ?? $text;
  48. $text = preg_replace('/font-size\s*:[^;"]+;?/iu', '', $text) ?? $text;
  49. $text = preg_replace('/line-height\s*:[^;"]+;?/iu', '', $text) ?? $text;
  50. $text = preg_replace('/style\s*=\s*([\'"])\s*\1/iu', '', $text) ?? $text;
  51. return $text;
  52. };
  53. $formatDetailForReadability = function (?string $text) use ($stepPattern): string {
  54. $text = trim((string) $text);
  55. if ($text === '') {
  56. return '';
  57. }
  58. $text = preg_replace('/\s*(' . $stepPattern . ')/u', '<br>$1', $text) ?? $text;
  59. $text = str_replace([';', ';'], [';<wbr>', ';<wbr>'], $text);
  60. $text = preg_replace('/,\s*(则|故|所以)/u', ',<br>$1', $text) ?? $text;
  61. $text = preg_replace('/(?:<br>\s*){2,}/u', '<br>', $text) ?? $text;
  62. return preg_replace('/^(?:\s*<br\s*\/?>\s*)+/iu', '', $text) ?? $text;
  63. };
  64. $renderSolutionLikeGrading = function (string $solutionHtml, bool $withStepBoxes = false) use ($stepPattern): string {
  65. $solution = trim($solutionHtml);
  66. if ($solution === '' || $solution === '(无详解)') {
  67. return $solutionHtml;
  68. }
  69. $solution = preg_replace('/(\s*\d+\s*分\s*)/u', '', $solution) ?? $solution;
  70. $solution = preg_replace('/【(解题思路|详细解答|最终答案)】/u', "\n\n===SECTION_START===\n【$1】\n===SECTION_END===\n\n", $solution) ?? $solution;
  71. $solution = preg_replace('/(解题过程\s*[^:\n]*:)/u', "\n\n===SECTION_START===\n【解题过程】\n===SECTION_END===\n\n", $solution) ?? $solution;
  72. $sections = explode('===SECTION_START===', $solution);
  73. $processedSections = [];
  74. foreach ($sections as $section) {
  75. if (trim((string) $section) === '') {
  76. continue;
  77. }
  78. $section = str_replace('===SECTION_END===', '', $section);
  79. if (preg_match('/【(解题思路|详细解答|最终答案|解题过程)】/u', $section, $matches)) {
  80. $sectionTitle = $matches[0];
  81. $sectionContent = preg_replace('/【(解题思路|详细解答|最终答案|解题过程)】/u', '', $section) ?? $section;
  82. if ($withStepBoxes) {
  83. if (preg_match('/' . $stepPattern . '/u', $sectionContent)) {
  84. $parts = preg_split('/(?=' . $stepPattern . ')/u', $sectionContent, -1, PREG_SPLIT_NO_EMPTY) ?: [];
  85. $processed = '';
  86. foreach ($parts as $index => $part) {
  87. $stepText = trim((string) $part);
  88. if ($stepText === '') {
  89. continue;
  90. }
  91. $prefix = $index > 0 ? '<br>' : '';
  92. $isStep = preg_match('/^' . $stepPattern . '/u', $stepText);
  93. if ($isStep) {
  94. $processed .= $prefix
  95. . '<span class="solution-step"><span class="step-box"><span class="detail-grade-box"></span></span><span class="step-label">'
  96. . $stepText
  97. . '</span></span>';
  98. } else {
  99. $processed .= $prefix . '<span class="step-label">' . $stepText . '</span>';
  100. }
  101. }
  102. $sectionContent = $processed;
  103. } else {
  104. $sectionContent = '<span class="solution-step"><span class="step-box"><span class="detail-grade-box"></span></span><span class="step-label">&nbsp;</span></span> ' . trim((string) $sectionContent);
  105. }
  106. }
  107. $processedSections[] = '<div class="solution-section"><strong>' . $sectionTitle . '</strong><br>' . $sectionContent . '</div>';
  108. } else {
  109. $processedSections[] = $section;
  110. }
  111. }
  112. $solution = implode('', $processedSections);
  113. $solution = preg_replace('/\n{3,}/u', "\n\n", $solution) ?? $solution;
  114. return nl2br($solution);
  115. };
  116. @endphp
  117. <div class="answer-detail-page">
  118. <div class="answer-quick">
  119. <div class="answer-quick-label">答案速查</div>
  120. <div class="answer-quick-flow answer-quick-flow-ordered">
  121. @foreach($quickAnswers as $item)
  122. <span class="answer-quick-item {{ $item['long'] ? 'answer-quick-item-long' : 'answer-quick-item-compact' }}">
  123. <strong>{{ $item['no'] }}.&nbsp;</strong>{!! $item['answer'] !!}
  124. </span>
  125. @endforeach
  126. </div>
  127. </div>
  128. <div class="answer-detail-two-cols">
  129. @foreach($allQuestions as $item)
  130. @php
  131. $q = $item['q'];
  132. $no = (int) ($q->question_number ?? 0);
  133. $detailType = strtolower((string) ($item['detail_type'] ?? ''));
  134. $isAnswerType = $detailType === 'answer';
  135. $rawAnswer = trim((string) ($q->answer ?? ''));
  136. $rawSolution = $normalizeDetailHtml($q->solution ?? '');
  137. $isSeeSolutionAnswer = (bool) preg_match('/^见\s*解析[。\.]?$|^详见解析/u', $rawAnswer);
  138. $hasImageLikeSolution = (bool) preg_match('/<\s*img\b|<\s*image\b|(^|[\s>])img\s+src\s*=/iu', $rawSolution);
  139. $showAnswer = !$isSeeSolutionAnswer;
  140. $renderAnswer = \App\Services\MathFormulaProcessor::processFormulas($normalizeAnswerText($rawAnswer, false));
  141. if ($rawSolution === '') {
  142. $renderSolution = '(无详解)';
  143. } elseif ($hasImageLikeSolution) {
  144. $renderSolution = $formatDetailForReadability($rawSolution);
  145. } else {
  146. $renderSolution = \App\Services\MathFormulaProcessor::processFormulas($formatDetailForReadability($rawSolution));
  147. }
  148. $renderSolution = $renderSolutionLikeGrading($renderSolution, $isAnswerType);
  149. $useSplitAnalysisLine = $isAnswerType && $showAnswer;
  150. @endphp
  151. <div class="answer-detail-item">
  152. @if($useSplitAnalysisLine)
  153. <div class="entry-line entry-answer-line">
  154. <span class="qno">{{ $no }}.</span>
  155. <span class="answer-only">【答案】{!! $renderAnswer !!}</span>
  156. </div>
  157. <div class="entry-line entry-analysis-line">
  158. <span class="analysis-tag">【解析】</span>
  159. <span class="solution-only">{!! $renderSolution !!}</span>
  160. </div>
  161. @else
  162. <div class="entry-line entry-inline-line">
  163. <span class="qno">{{ $no }}.</span>
  164. @if($showAnswer)
  165. <span class="answer-only">【答案】{!! $renderAnswer !!}</span>
  166. @endif
  167. <span class="analysis-tag">【解析】</span>
  168. <span class="solution-only">{!! $renderSolution !!}</span>
  169. </div>
  170. @endif
  171. </div>
  172. @endforeach
  173. </div>
  174. </div>