answer-detail-page.blade.php 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. @php
  2. $choiceQuestions = $questions['choice'] ?? [];
  3. $fillQuestions = $questions['fill'] ?? [];
  4. $answerQuestions = $questions['answer'] ?? [];
  5. $allQuestions = collect()
  6. ->concat($choiceQuestions)
  7. ->concat($fillQuestions)
  8. ->concat($answerQuestions)
  9. ->sortBy(fn ($q) => (int) ($q->question_number ?? 0))
  10. ->values();
  11. $normalizeQuickAnswer = function (?string $answer): string {
  12. $answer = trim((string) $answer);
  13. if ($answer === '') {
  14. return '—';
  15. }
  16. $answer = preg_replace('/\s+/u', ' ', $answer) ?? $answer;
  17. $answer = str_replace([';', ';'], ' / ', $answer);
  18. return trim($answer);
  19. };
  20. $quickAnswers = $allQuestions->map(function ($q) use ($normalizeQuickAnswer) {
  21. $answerText = $normalizeQuickAnswer($q->answer ?? '');
  22. $isLong = mb_strlen(strip_tags($answerText)) > 20
  23. || str_contains($answerText, "\n")
  24. || str_contains($answerText, "\r")
  25. || str_contains($answerText, '{')
  26. || str_contains($answerText, '\\frac')
  27. || str_contains($answerText, '见解析');
  28. $processedAnswer = \App\Services\MathFormulaProcessor::processFormulas($answerText);
  29. // 速查统一为流式:移除显式换行
  30. $processedAnswer = preg_replace('/<br\s*\/?>/iu', ' ', $processedAnswer) ?? $processedAnswer;
  31. return [
  32. 'no' => (int) ($q->question_number ?? 0),
  33. 'answer' => $processedAnswer,
  34. 'long' => $isLong,
  35. 'extended' => false,
  36. ];
  37. })->values();
  38. $normalizeDetailHtml = function (?string $text): string {
  39. $text = trim((string) $text);
  40. if ($text === '') {
  41. return '';
  42. }
  43. $text = preg_replace('/font-size\s*:[^;"]+;?/iu', '', $text) ?? $text;
  44. $text = preg_replace('/line-height\s*:[^;"]+;?/iu', '', $text) ?? $text;
  45. $text = preg_replace('/style\s*=\s*([\'"])\s*\1/iu', '', $text) ?? $text;
  46. return $text;
  47. };
  48. $formatDetailForReadability = function (?string $text): string {
  49. $text = trim((string) $text);
  50. if ($text === '') {
  51. return '';
  52. }
  53. // 仅做轻量分行:遇到“步骤X”时换行,避免整段拥挤
  54. $text = preg_replace('/\s*(步骤\s*[0-9一二三四五六七八九十]+\s*[::])/u', '<br>$1', $text) ?? $text;
  55. $text = preg_replace('/\s*(第\s*[0-9一二三四五六七八九十]+\s*步\s*[::])/u', '<br>$1', $text) ?? $text;
  56. // 软换行点:分号后允许优先换行,缓解长公式串拥挤
  57. $text = str_replace([';', ';'], [';<wbr>', ';<wbr>'], $text);
  58. // 轻量语义断点:在“则/故/所以”前给换行机会,避免整段公式堆在一行
  59. $text = preg_replace('/,\s*(则|故|所以)/u', ',<br>$1', $text) ?? $text;
  60. // 清理重复换行
  61. $text = preg_replace('/(?:<br>\s*){2,}/u', '<br>', $text) ?? $text;
  62. return ltrim($text, '<br>');
  63. };
  64. $renderAnswerSolutionWithStepBoxes = function (string $solutionHtml): string {
  65. $solution = trim($solutionHtml);
  66. if ($solution === '' || $solution === '(无详解)') {
  67. return $solutionHtml;
  68. }
  69. // 去掉分步得分等分值标记,与判卷页一致
  70. $solution = preg_replace('/(\s*\d+\s*分\s*)/u', '', $solution) ?? $solution;
  71. // 与判卷页一致:优先识别“步骤X / 第X步”
  72. if (preg_match('/(步骤\s*\d+|第\s*\d+\s*步)/u', $solution)) {
  73. $parts = preg_split('/(?=步骤\s*\d+|第\s*\d+\s*步)/u', $solution, -1, PREG_SPLIT_NO_EMPTY) ?: [];
  74. $processed = '';
  75. foreach ($parts as $index => $part) {
  76. $stepText = trim((string) $part);
  77. if ($stepText === '') {
  78. continue;
  79. }
  80. $prefix = $index > 0 ? '<br>' : '';
  81. $isStep = preg_match('/^(步骤\s*\d+|第\s*\d+\s*步)/u', $stepText);
  82. if ($isStep) {
  83. $processed .= $prefix
  84. . '<span class="solution-step"><span class="step-box"><span class="detail-grade-box"></span></span><span class="step-label">'
  85. . $stepText
  86. . '</span></span>';
  87. } else {
  88. // 与判卷页保持一致:步骤前引导语不加方框
  89. $processed .= $prefix . '<span class="step-label">' . $stepText . '</span>';
  90. }
  91. }
  92. return $processed !== '' ? $processed : $solution;
  93. }
  94. // 无步骤关键词:与判卷页一致,在解析开头加一个判分框
  95. return '<span class="solution-step"><span class="step-box"><span class="detail-grade-box"></span></span><span class="step-label">'
  96. . $solution
  97. . '</span></span>';
  98. };
  99. @endphp
  100. <div class="answer-detail-page">
  101. <div class="answer-quick">
  102. <div class="answer-quick-label">答案速查</div>
  103. <div class="answer-quick-flow answer-quick-flow-ordered">
  104. @foreach($quickAnswers as $item)
  105. <span class="answer-quick-item {{ $item['long'] ? 'answer-quick-item-long' : 'answer-quick-item-compact' }}">
  106. <strong>{{ $item['no'] }}.&nbsp;</strong>{!! $item['answer'] !!}
  107. </span>
  108. @endforeach
  109. </div>
  110. </div>
  111. <div class="answer-detail-two-cols">
  112. @foreach($allQuestions as $q)
  113. @php
  114. $no = (int) ($q->question_number ?? 0);
  115. $questionType = strtolower((string) ($q->question_type ?? ''));
  116. $isAnswerType = $questionType === 'answer';
  117. $rawAnswer = trim((string) ($q->answer ?? ''));
  118. $rawSolution = $normalizeDetailHtml($q->solution ?? '');
  119. $isSeeSolutionAnswer = (bool) preg_match('/^见\s*解析[。\.]?$|^详见解析/u', $rawAnswer);
  120. $renderAnswer = $rawAnswer !== ''
  121. ? \App\Services\MathFormulaProcessor::processFormulas($rawAnswer)
  122. : '—';
  123. $renderSolution = $rawSolution !== ''
  124. ? \App\Services\MathFormulaProcessor::processFormulas($formatDetailForReadability($rawSolution))
  125. : '(无详解)';
  126. if ($isAnswerType) {
  127. $renderSolution = $renderAnswerSolutionWithStepBoxes($renderSolution);
  128. }
  129. @endphp
  130. <div class="answer-detail-item">
  131. <div class="entry-line">
  132. <span class="entry-head">
  133. <span class="qno">{{ $no }}.</span>
  134. @if(!$isSeeSolutionAnswer)
  135. <span class="answer-only">{!! $renderAnswer !!}</span>
  136. @endif
  137. <span class="analysis-tag">【解析】</span>
  138. </span>
  139. <span class="solution-only">{!! $renderSolution !!}</span>
  140. </div>
  141. </div>
  142. @endforeach
  143. </div>
  144. </div>