| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- @php
- $choiceQuestions = $questions['choice'] ?? [];
- $fillQuestions = $questions['fill'] ?? [];
- $answerQuestions = $questions['answer'] ?? [];
- $stepPattern = '(步骤\s*[0-9一二三四五六七八九十百零两]+\s*[::]?|第\s*[0-9一二三四五六七八九十百零两]+\s*步\s*[::]?)';
- $allQuestions = collect()
- ->concat(collect($choiceQuestions)->map(fn ($q) => ['q' => $q, 'detail_type' => 'choice']))
- ->concat(collect($fillQuestions)->map(fn ($q) => ['q' => $q, 'detail_type' => 'fill']))
- ->concat(collect($answerQuestions)->map(fn ($q) => ['q' => $q, 'detail_type' => 'answer']))
- ->sortBy(fn ($item) => (int) (($item['q']->question_number ?? 0)))
- ->values();
- $normalizeQuickAnswer = function (?string $answer): string {
- $answer = trim((string) $answer);
- if ($answer === '') {
- return '—';
- }
- $answer = preg_replace('/\s+/u', ' ', $answer) ?? $answer;
- $answer = str_replace([';', ';'], ' / ', $answer);
- return trim($answer);
- };
- $quickAnswers = $allQuestions->map(function ($item) use ($normalizeQuickAnswer) {
- $q = $item['q'];
- $answerText = $normalizeQuickAnswer($q->answer ?? '');
- $isLong = mb_strlen(strip_tags($answerText)) > 20
- || str_contains($answerText, "\n")
- || str_contains($answerText, "\r")
- || str_contains($answerText, '{')
- || str_contains($answerText, '\\frac')
- || str_contains($answerText, '见解析');
- $processedAnswer = \App\Services\MathFormulaProcessor::processFormulas($answerText);
- // 速查统一为流式:移除显式换行
- $processedAnswer = preg_replace('/<br\s*\/?>/iu', ' ', $processedAnswer) ?? $processedAnswer;
- return [
- 'no' => (int) ($q->question_number ?? 0),
- 'answer' => $processedAnswer,
- 'long' => $isLong,
- ];
- })->values();
- $normalizeDetailHtml = function (?string $text): string {
- $text = trim((string) $text);
- if ($text === '') {
- return '';
- }
- // 兼容历史/AI返回的非标准图片标签,统一转为标准 <img>
- $text = preg_replace('/<\s*image\b/iu', '<img', $text) ?? $text;
- $text = preg_replace('/<\s*\/\s*image\s*>/iu', '', $text) ?? $text;
- // 兼容被裁掉 "<" 的图片标签文本:img src="..."/>
- $text = preg_replace('/(^|[\s>])img\s+src\s*=\s*([\'"][^\'"]+[\'"])\s*\/?>/iu', '$1<img src=$2 />', $text) ?? $text;
- $text = preg_replace('/font-size\s*:[^;"]+;?/iu', '', $text) ?? $text;
- $text = preg_replace('/line-height\s*:[^;"]+;?/iu', '', $text) ?? $text;
- $text = preg_replace('/style\s*=\s*([\'"])\s*\1/iu', '', $text) ?? $text;
- return $text;
- };
- $formatDetailForReadability = function (?string $text) use ($stepPattern): string {
- $text = trim((string) $text);
- if ($text === '') {
- return '';
- }
- // 仅做轻量分行:遇到“步骤X”时换行,避免整段拥挤
- $text = preg_replace('/\s*(' . $stepPattern . ')/u', '<br>$1', $text) ?? $text;
- // 软换行点:分号后允许优先换行,缓解长公式串拥挤
- $text = str_replace([';', ';'], [';<wbr>', ';<wbr>'], $text);
- // 轻量语义断点:在“则/故/所以”前给换行机会,避免整段公式堆在一行
- $text = preg_replace('/,\s*(则|故|所以)/u', ',<br>$1', $text) ?? $text;
- // 清理重复换行
- $text = preg_replace('/(?:<br>\s*){2,}/u', '<br>', $text) ?? $text;
- // 仅移除前导 <br> 标签,避免把 <img> 这类标签的起始 "<" 误删
- return preg_replace('/^(?:\s*<br\s*\/?>\s*)+/iu', '', $text) ?? $text;
- };
- $renderSolutionLikeGrading = function (string $solutionHtml, bool $withStepBoxes = false) use ($stepPattern): string {
- $solution = trim($solutionHtml);
- if ($solution === '' || $solution === '(无详解)') {
- return $solutionHtml;
- }
- // 去掉分步得分等分值标记(与判卷页一致)
- $solution = preg_replace('/(\s*\d+\s*分\s*)/u', '', $solution) ?? $solution;
- // 与判卷页一致:先做“解题思路/解题过程”等分段
- $solution = preg_replace('/【(解题思路|详细解答|最终答案)】/u', "\n\n===SECTION_START===\n【$1】\n===SECTION_END===\n\n", $solution) ?? $solution;
- $solution = preg_replace('/(解题过程\s*[^:\n]*:)/u', "\n\n===SECTION_START===\n【解题过程】\n===SECTION_END===\n\n", $solution) ?? $solution;
- $sections = explode('===SECTION_START===', $solution);
- $processedSections = [];
- foreach ($sections as $section) {
- if (trim((string) $section) === '') {
- continue;
- }
- $section = str_replace('===SECTION_END===', '', $section);
- if (preg_match('/【(解题思路|详细解答|最终答案|解题过程)】/u', $section, $matches)) {
- $sectionTitle = $matches[0];
- $sectionContent = preg_replace('/【(解题思路|详细解答|最终答案|解题过程)】/u', '', $section) ?? $section;
- if ($withStepBoxes) {
- if (preg_match('/' . $stepPattern . '/u', $sectionContent)) {
- $parts = preg_split('/(?=' . $stepPattern . ')/u', $sectionContent, -1, PREG_SPLIT_NO_EMPTY) ?: [];
- $processed = '';
- foreach ($parts as $index => $part) {
- $stepText = trim((string) $part);
- if ($stepText === '') {
- continue;
- }
- $prefix = $index > 0 ? '<br>' : '';
- $isStep = preg_match('/^' . $stepPattern . '/u', $stepText);
- if ($isStep) {
- $processed .= $prefix
- . '<span class="solution-step"><span class="step-box"><span class="detail-grade-box"></span></span><span class="step-label">'
- . $stepText
- . '</span></span>';
- } else {
- $processed .= $prefix . '<span class="step-label">' . $stepText . '</span>';
- }
- }
- $sectionContent = $processed;
- } else {
- $sectionContent = '<span class="solution-step"><span class="step-box"><span class="detail-grade-box"></span></span><span class="step-label"> </span></span> ' . trim((string) $sectionContent);
- }
- }
- $processedSections[] = '<div class="solution-section"><strong>' . $sectionTitle . '</strong><br>' . $sectionContent . '</div>';
- } else {
- $processedSections[] = $section;
- }
- }
- $solution = implode('', $processedSections);
- if ($withStepBoxes && (empty($processedSections) || (count($processedSections) === 1 && !str_contains($processedSections[0] ?? '', 'step-box')))) {
- if (preg_match('/' . $stepPattern . '/u', $solution)) {
- $parts = preg_split('/(?=' . $stepPattern . ')/u', $solution, -1, PREG_SPLIT_NO_EMPTY) ?: [];
- $processed = '';
- foreach ($parts as $index => $part) {
- $stepText = trim((string) $part);
- if ($stepText === '') {
- continue;
- }
- $prefix = $index > 0 ? '<br>' : '';
- $isStep = preg_match('/^' . $stepPattern . '/u', $stepText);
- if ($isStep) {
- $processed .= $prefix . '<span class="solution-step"><span class="step-box"><span class="detail-grade-box"></span></span><span class="step-label">' . $stepText . '</span></span>';
- } else {
- $processed .= $prefix . '<span class="step-label">' . $stepText . '</span>';
- }
- }
- $solution = $processed;
- } else {
- $solution = '<span class="solution-step"><span class="step-box"><span class="detail-grade-box"></span></span><span class="step-label">' . trim((string) $solution) . '</span></span>';
- }
- }
- $solution = preg_replace('/\n{3,}/u', "\n\n", $solution) ?? $solution;
- return nl2br($solution);
- };
- @endphp
- <div class="answer-detail-page">
- <div class="answer-quick">
- <div class="answer-quick-label">答案速查</div>
- <div class="answer-quick-flow answer-quick-flow-ordered">
- @foreach($quickAnswers as $item)
- <span class="answer-quick-item {{ $item['long'] ? 'answer-quick-item-long' : 'answer-quick-item-compact' }}">
- <strong>{{ $item['no'] }}. </strong>{!! $item['answer'] !!}
- </span>
- @endforeach
- </div>
- </div>
- <div class="answer-detail-two-cols">
- @foreach($allQuestions as $item)
- @php
- $q = $item['q'];
- $no = (int) ($q->question_number ?? 0);
- $detailType = strtolower((string) ($item['detail_type'] ?? ''));
- $isAnswerType = $detailType === 'answer';
- $rawAnswer = trim((string) ($q->answer ?? ''));
- $rawSolution = $normalizeDetailHtml($q->solution ?? '');
- $isSeeSolutionAnswer = (bool) preg_match('/^见\s*解析[。\.]?$|^详见解析/u', $rawAnswer);
- $hasImageLikeSolution = (bool) preg_match('/<\s*img\b|<\s*image\b|(^|[\s>])img\s+src\s*=/iu', $rawSolution);
- $showAnswer = !$isSeeSolutionAnswer;
- $renderAnswer = $rawAnswer !== ''
- ? \App\Services\MathFormulaProcessor::processFormulas($rawAnswer)
- : '—';
- if ($rawSolution === '') {
- $renderSolution = '(无详解)';
- } elseif ($hasImageLikeSolution) {
- // 与判卷页保持一致:图片型解析优先保留原始HTML结构,避免标签被公式处理链打散为纯文本
- $renderSolution = $formatDetailForReadability($rawSolution);
- } else {
- $renderSolution = \App\Services\MathFormulaProcessor::processFormulas($formatDetailForReadability($rawSolution));
- }
- if ($isAnswerType) {
- $renderSolution = $renderSolutionLikeGrading($renderSolution, true);
- } else {
- $renderSolution = $renderSolutionLikeGrading($renderSolution, false);
- }
- // 版式规则:
- // 1) 选择/填空同行
- // 2) 简答默认解析另起一行
- // 3) 若答案为“见解析”被隐藏,则题号与【解析】同行
- $useSplitAnalysisLine = $isAnswerType && $showAnswer;
- @endphp
- <div class="answer-detail-item">
- @if($useSplitAnalysisLine)
- <div class="entry-line entry-answer-line">
- <span class="qno">{{ $no }}.</span>
- <span class="answer-only">{!! $renderAnswer !!}</span>
- </div>
- <div class="entry-line entry-analysis-line">
- <span class="analysis-tag">【解析】</span>
- <span class="solution-only">{!! $renderSolution !!}</span>
- </div>
- @else
- <div class="entry-line entry-inline-line">
- <span class="qno">{{ $no }}.</span>
- @if($showAnswer)
- <span class="answer-only">{!! $renderAnswer !!}</span>
- @endif
- <span class="analysis-tag">【解析】</span>
- <span class="solution-only">{!! $renderSolution !!}</span>
- </div>
- @endif
- </div>
- @endforeach
- </div>
- </div>
|