paper-body.blade.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. @php
  2. $choiceQuestions = $questions['choice'] ?? [];
  3. $fillQuestions = $questions['fill'] ?? [];
  4. $answerQuestions = $questions['answer'] ?? [];
  5. $gradingMode = $grading ?? false;
  6. // 计算填空空格数量
  7. $countBlanks = function($text) {
  8. $count = 0;
  9. $count += preg_match_all('/_{2,}/u', $text, $m);
  10. $count += preg_match_all('/(\s*)/u', $text, $m);
  11. $count += preg_match_all('/\(\s*\)/', $text, $m);
  12. return max(1, $count);
  13. };
  14. // 计算步骤数量
  15. $countSteps = function($text) {
  16. $matches = [];
  17. $cnt = preg_match_all('/第\s*\d+\s*步/u', $text ?? '', $matches);
  18. return max(1, $cnt);
  19. };
  20. $renderBoxes = function($num) {
  21. // 判卷方框放大 1.2 倍,保持单行布局
  22. return str_repeat('<span style="display:inline-block;width:17px;height:17px;line-height:17px;border:1px solid #333;margin-right:4px;vertical-align:middle;"></span>', $num);
  23. };
  24. @endphp
  25. <!-- 一、选择题 -->
  26. <div class="section-title">一、选择题
  27. @if(count($choiceQuestions) > 0)
  28. @php
  29. $choiceTotal = array_sum(array_map(fn($q) => $q->score ?? 5, $choiceQuestions));
  30. @endphp
  31. (本大题共 {{ count($choiceQuestions) }} 小题,共 {{ $choiceTotal }} 分)
  32. @else
  33. (本大题共 0 小题,共 0 分)
  34. @endif
  35. </div>
  36. @if(count($choiceQuestions) > 0)
  37. @foreach($choiceQuestions as $index => $q)
  38. @php
  39. $questionNumber = $index + 1;
  40. $cleanContent = preg_replace('/^\d+[\.、]\s*/', '', $q->content);
  41. $cleanContent = trim($cleanContent);
  42. $options = $q->options ?? [];
  43. if (empty($options)) {
  44. $pattern = '/([A-D])[\.、:.:]\s*(.+?)(?=\s*[A-D][\.、:.:]|$)/su';
  45. if (preg_match_all($pattern, $cleanContent, $matches, PREG_SET_ORDER)) {
  46. foreach ($matches as $match) {
  47. $optionText = trim($match[2]);
  48. if (!empty($optionText)) {
  49. $options[] = $optionText;
  50. }
  51. }
  52. }
  53. }
  54. $stemLine = $cleanContent;
  55. if (!empty($options)) {
  56. if (preg_match('/^(.+?)(?=[A-D][\.、:.:])/su', $cleanContent, $stemMatch)) {
  57. $stemLine = trim($stemMatch[1]);
  58. }
  59. }
  60. // 将题干中的空括号/下划线替换为短波浪线;如无占位符,则在末尾追加短波浪线
  61. $blankSpan = '<span style="display:inline-block; min-width:80px; border-bottom:1.2px dashed #444; vertical-align:bottom;">&nbsp;</span>';
  62. $renderedStem = preg_replace(['/((\s*))/u', '/\(\s*\)/', '/_{2,}/'], $blankSpan, $stemLine);
  63. if ($renderedStem === $stemLine) {
  64. $renderedStem .= ' ' . $blankSpan;
  65. }
  66. $renderedStem = \App\Services\MathFormulaProcessor::processFormulas($renderedStem);
  67. @endphp
  68. <div class="question">
  69. <div class="question-grid">
  70. <div class="question-lead">
  71. @if($gradingMode)
  72. <span class="grading-boxes">{!! $renderBoxes(1) !!}</span>
  73. @endif
  74. <span class="question-number">{{ $gradingMode ? '题目 ' : '' }}{{ $questionNumber }}.</span>
  75. </div>
  76. <div class="question-main">
  77. <span class="question-stem">{!! $renderedStem !!}</span>
  78. </div>
  79. @if(!empty($options))
  80. @php
  81. $optCount = count($options);
  82. $optionsClass = $optCount <= 4 ? 'options-grid-2' : 'options';
  83. @endphp
  84. <div class="question-lead spacer"></div>
  85. <div class="{{ $optionsClass }}">
  86. @foreach($options as $optIndex => $opt)
  87. @php $label = chr(65 + $optIndex); @endphp
  88. <div class="option option-compact">
  89. <strong>{{ $label }}.</strong>&nbsp;{!! \App\Services\MathFormulaProcessor::processFormulas($opt) !!}
  90. </div>
  91. @endforeach
  92. </div>
  93. @endif
  94. @if($gradingMode)
  95. @php
  96. $solutionText = trim($q->solution ?? '');
  97. // 去掉前置的“解题思路”标签,避免出现“解题思路:【解题思路】”重复
  98. $solutionText = preg_replace('/^【?\s*解题思路\s*】?\s*[::]?\s*/u', '', $solutionText);
  99. $solutionHtml = $solutionText === ''
  100. ? '<span style="color:#999;font-style:italic;">(暂无解题思路)</span>'
  101. : \App\Services\MathFormulaProcessor::processFormulas($solutionText);
  102. @endphp
  103. <div class="question-lead spacer"></div>
  104. <div class="answer-meta">
  105. <div class="answer-line"><strong>正确答案:</strong><span class="solution-content">{!! \App\Services\MathFormulaProcessor::processFormulas($q->answer ?? '') !!}</span></div>
  106. <div class="answer-line"><strong>解题思路:</strong><span class="solution-content">{!! $solutionHtml !!}</span></div>
  107. </div>
  108. @endif
  109. </div>
  110. </div>
  111. @endforeach
  112. @else
  113. <div class="question">
  114. <div class="question-content" style="font-style: italic; color: #999; padding: 20px; border: 1px dashed #ccc; background: #f9f9f9;">
  115. 该题型正在生成中或暂无题目,请稍后刷新页面查看
  116. </div>
  117. </div>
  118. @endif
  119. <!-- 二、填空题 -->
  120. <div class="section-title">二、填空题
  121. @if(count($fillQuestions) > 0)
  122. @php
  123. $fillTotal = array_sum(array_map(fn($q) => $q->score ?? 5, $fillQuestions));
  124. @endphp
  125. (本大题共 {{ count($fillQuestions) }} 小题,共 {{ $fillTotal }} 分)
  126. @else
  127. (本大题共 0 小题,共 0 分)
  128. @endif
  129. </div>
  130. @if(count($fillQuestions) > 0)
  131. @foreach($fillQuestions as $index => $q)
  132. @php
  133. $questionNumber = count($choiceQuestions) + $index + 1;
  134. $blankSpan = '<span style="display:inline-block; min-width:80px; border-bottom:1.2px dashed #444; vertical-align:bottom;">&nbsp;</span>';
  135. $renderedContent = preg_replace(['/((\s*))/u', '/\(\s*\)/', '/_{2,}/'], $blankSpan, $q->content);
  136. if ($renderedContent === $q->content) {
  137. $renderedContent .= ' ' . $blankSpan;
  138. }
  139. $renderedContent = \App\Services\MathFormulaProcessor::processFormulas($renderedContent);
  140. @endphp
  141. <div class="question">
  142. <div class="question-grid">
  143. <div class="question-lead">
  144. @if($gradingMode)
  145. <span class="grading-boxes">{!! $renderBoxes($countBlanks($q->content ?? '')) !!}</span>
  146. @endif
  147. <span class="question-number">{{ $gradingMode ? '题目 ' : '' }}{{ $questionNumber }}.</span>
  148. </div>
  149. <div class="question-main">
  150. <span class="question-stem">{!! $renderedContent !!}</span>
  151. </div>
  152. @if($gradingMode)
  153. @php
  154. $solutionText = trim($q->solution ?? '');
  155. // 去掉前置的“解题思路”标签,避免出现“解题思路:【解题思路】”重复
  156. $solutionText = preg_replace('/^【?\s*解题思路\s*】?\s*[::]?\s*/u', '', $solutionText);
  157. $solutionHtml = $solutionText === ''
  158. ? '<span style="color:#999;font-style:italic;">(暂无解题思路)</span>'
  159. : \App\Services\MathFormulaProcessor::processFormulas($solutionText);
  160. @endphp
  161. <div class="question-lead spacer"></div>
  162. <div class="answer-meta">
  163. <div class="answer-line"><strong>正确答案:</strong><span class="solution-content">{!! \App\Services\MathFormulaProcessor::processFormulas($q->answer ?? '') !!}</span></div>
  164. <div class="answer-line"><strong>解题思路:</strong><span class="solution-content">{!! $solutionHtml !!}</span></div>
  165. </div>
  166. @endif
  167. </div>
  168. </div>
  169. @endforeach
  170. @else
  171. <div class="question">
  172. <div class="question-content" style="font-style: italic; color: #999; padding: 20px; border: 1px dashed #ccc; background: #f9f9f9;">
  173. 该题型正在生成中或暂无题目,请稍后刷新页面查看
  174. </div>
  175. </div>
  176. @endif
  177. <!-- 三、解答题 -->
  178. <div class="section-title">三、解答题
  179. @if(count($answerQuestions) > 0)
  180. (本大题共 {{ count($answerQuestions) }} 小题,共 {{ array_sum(array_column($answerQuestions, 'score')) }} 分。解答应写出文字说明、证明过程或演算步骤)
  181. @else
  182. (本大题共 0 小题,共 0 分)
  183. @endif
  184. </div>
  185. @if(count($answerQuestions) > 0)
  186. @foreach($answerQuestions as $index => $q)
  187. @php
  188. $questionNumber = count($choiceQuestions) + count($fillQuestions) + $index + 1;
  189. @endphp
  190. <div class="question">
  191. <div class="question-grid">
  192. <div class="question-lead">
  193. <span class="question-number">{{ $gradingMode ? '题目 ' : '' }}{{ $questionNumber }}.</span>
  194. </div>
  195. <div class="question-main">
  196. @unless($gradingMode)
  197. <span class="question-score-inline">(本小题满分 {{ $q->score ?? 10 }} 分)</span>
  198. @endunless
  199. <span class="question-stem">{!! \App\Services\MathFormulaProcessor::processFormulas($q->content) !!}</span>
  200. </div>
  201. @unless($gradingMode)
  202. <div class="question-lead spacer"></div>
  203. <div class="answer-area boxy">
  204. <span class="answer-label">作答</span>
  205. </div>
  206. @endunless
  207. @if($gradingMode)
  208. @php
  209. $solutionRaw = $q->solution ?? '';
  210. $solutionProcessed = \App\Services\MathFormulaProcessor::processFormulas($solutionRaw);
  211. // 去掉分步得分等分值标记
  212. $solutionProcessed = preg_replace('/(\s*\d+\s*分\s*)/u', '', $solutionProcessed);
  213. // 优化解析分段格式
  214. $solutionProcessed = preg_replace('/【(解题思路|详细解答|最终答案)】/u', "\n\n【$1】\n\n", $solutionProcessed);
  215. $solutionProcessed = preg_replace('/^【(解题思路|详细解答|最终答案)】\n\n/u', '<div class="solution-section"><strong>【$1】</strong><br>', $solutionProcessed);
  216. $solutionProcessed = preg_replace('/\n\n【(解题思路|详细解答|最终答案)】\n\n/u', '</div><div class="solution-section"><strong>【$1】</strong><br>', $solutionProcessed);
  217. $solutionProcessed = preg_replace('/\n\n/u', '<br>', $solutionProcessed);
  218. // 为每个"第 N 步"前添加方框;若没有,则在【详细解答】段落开头添加一个方框
  219. if (preg_match('/第\s*\d+\s*步/u', $solutionProcessed)) {
  220. $solutionProcessed = preg_replace_callback('/第\s*\d+\s*步/u', function($m) use ($renderBoxes) {
  221. return '<br><span class="solution-step"><span class="step-box">' . $renderBoxes(1) . '</span><span class="step-label">' . $m[0] . '</span></span>';
  222. }, $solutionProcessed);
  223. } else {
  224. // 在【详细解答】标题后追加方框;若无标题则在正文最前补一个
  225. $injected = false;
  226. $count = 0;
  227. $solutionProcessed = preg_replace(
  228. '/(<strong>【详细解答】<\/strong><br>)/u',
  229. '$1' . '<span class="solution-step"><span class="step-box">' . $renderBoxes(1) . '</span><span class="step-label">&nbsp;</span></span> ',
  230. $solutionProcessed,
  231. 1,
  232. $count
  233. );
  234. if (!empty($count)) {
  235. $injected = true;
  236. }
  237. if (!$injected) {
  238. $solutionProcessed = '<span class="solution-step"><span class="step-box">' . $renderBoxes(1) . '</span><span class="step-label">&nbsp;</span></span> ' . ltrim($solutionProcessed);
  239. }
  240. }
  241. @endphp
  242. <div class="question-lead spacer"></div>
  243. <div class="answer-meta">
  244. <div class="answer-line"><strong>正确答案:</strong><span class="solution-content">{!! \App\Services\MathFormulaProcessor::processFormulas($q->answer ?? '') !!}</span></div>
  245. <div class="answer-line solution-parsed">{!! $solutionProcessed !!}</div>
  246. </div>
  247. @endif
  248. </div>
  249. </div>
  250. @endforeach
  251. @else
  252. <div class="question">
  253. <div class="question-content" style="font-style: italic; color: #999; padding: 20px; border: 1px dashed #ccc; background: #f9f9f9;">
  254. 该题型正在生成中或暂无题目,请稍后刷新页面查看
  255. </div>
  256. </div>
  257. @endif