| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798 |
- {{--
- 与 components/exam/paper-body 选择题选项渲染完全一致(布局 + 公式预处理)。
- 参数:
- - $options: array 选项(数字下标或 A/B/C/D 键)
- - $gradingMode: bool 是否判卷上下文(影响 OptionLayoutDecider)
- - $mathProcessed: bool 题目是否已整体预处理公式
- - $logQuestionNumber: string|int 仅用于日志标识
- - $showLeadSpacer: bool 是否在选项前插入与 question-grid 对齐的隐形占位(嵌入案例行等非 grid 场景可设为 false)
- --}}
- @php
- /** @var array $options */
- /** @var bool $gradingMode */
- /** @var bool $mathProcessed */
- /** @var string|int|null $logQuestionNumber */
- /** @var bool $showLeadSpacer */
- $showLeadSpacer = $showLeadSpacer ?? true;
- $layoutDeciderService = app(\App\Support\OptionLayoutDecider::class);
- $layoutMeta = $layoutDeciderService->decide(
- $options,
- $gradingMode ? 'grading' : 'exam'
- );
- $optionsClass = $layoutMeta['class'];
- $layoutDesc = $layoutMeta['layout'];
- $hasImageOptionInQuestion = false;
- foreach ($options as $optRaw) {
- if (preg_match('/<(img|image|svg)\\b|data:image\\//i', (string) $optRaw) === 1) {
- $hasImageOptionInQuestion = true;
- break;
- }
- }
- if ($hasImageOptionInQuestion) {
- $optionsClass = 'options-grid-4';
- $layoutDesc = '4列布局(图片选项固定)';
- }
- \Illuminate\Support\Facades\Log::debug('选择题布局决策', [
- 'question_number' => $logQuestionNumber ?? null,
- 'context' => $gradingMode ? 'grading' : 'exam',
- 'opt_count' => $layoutMeta['opt_count'],
- 'max_length' => $layoutMeta['max_length'],
- 'has_complex_formula' => $layoutMeta['has_complex_formula'],
- 'has_image_option' => $hasImageOptionInQuestion,
- 'selected_class' => $optionsClass,
- 'layout' => $layoutDesc,
- ]);
- @endphp
- @if($showLeadSpacer)
- <div class="question-lead spacer"></div>
- @endif
- <div class="{{ $optionsClass }}">
- @foreach($options as $optIndex => $opt)
- @php
- // 兼容两种格式:数字索引 (0,1,2,3) 或字母键 (A,B,C,D)
- if (is_numeric($optIndex)) {
- $label = chr(65 + (int) $optIndex);
- } else {
- $label = strtoupper($optIndex);
- }
- // 【修复】根据是否已预处理决定处理方式
- $normalizedOpt = (string) $opt;
- // 选项内优先使用行内分式,避免 \dfrac 导致单个选项视觉突兀
- $normalizedOpt = str_replace('\\dfrac', '\\frac', $normalizedOpt);
- $normalizedOpt = str_replace('\\displaystyle', '', $normalizedOpt);
- $normalizedOpt = $layoutDeciderService->normalizeCompactMathForDisplay($normalizedOpt);
- // 清理来源HTML里可能携带的超大字号,避免单题选项异常放大
- $normalizedOpt = preg_replace('/font-size\s*:[^;"]+;?/iu', '', $normalizedOpt);
- $normalizedOpt = preg_replace('/line-height\s*:[^;"]+;?/iu', '', $normalizedOpt);
- $normalizedOpt = preg_replace('/style\s*=\s*([\'"])\s*\1/iu', '', $normalizedOpt);
- if ($mathProcessed) {
- // 已预处理:数据已包含处理好的 <img> 和公式,直接使用
- $renderedOpt = $normalizedOpt;
- } else {
- // 未预处理:先转义保护,processFormulas() 内部会解码并处理
- $encodedOpt = htmlspecialchars($normalizedOpt, ENT_QUOTES | ENT_HTML5, 'UTF-8');
- $renderedOpt = \App\Services\MathFormulaProcessor::processFormulas($encodedOpt);
- }
- // 仅针对“选项图片”覆盖公式处理器默认的题干尺寸,避免四列布局被 220px 宽图撑出边界
- $renderedOpt = preg_replace('/max-width\s*:\s*220px\s*;?/iu', 'max-width:100%;', (string) $renderedOpt);
- $renderedOpt = preg_replace('/max-height\s*:\s*60mm\s*;?/iu', 'max-height:28mm;', (string) $renderedOpt);
- // 标记选项内图片,供 PDF 全局宽图放大逻辑识别并跳过
- $renderedOpt = preg_replace('/<img\b(?![^>]*\bdata-option-image=)/iu', '<img data-option-image="1"', (string) $renderedOpt);
- // 兼容未来选项直接使用 <svg> 的场景,同样打标走选项专用规则
- $renderedOpt = preg_replace('/<svg\b(?![^>]*\bdata-option-image=)/iu', '<svg data-option-image="1"', (string) $renderedOpt);
- // 细粒度控制:短选项(如 1/2、-1/3、x、-x)尽量单行展示,长选项允许换行
- $rawOptText = html_entity_decode(strip_tags((string) $opt), ENT_QUOTES | ENT_HTML5, 'UTF-8');
- $rawOptText = preg_replace('/\s+/u', '', $rawOptText ?? '');
- $rawOptLen = mb_strlen((string) $rawOptText, 'UTF-8');
- $isShortOption = $rawOptLen <= 8;
- @endphp
- @php $hasImageOption = preg_match('/<(img|image|svg)\\b|data:image\\//i', (string) $renderedOpt) === 1; @endphp
- <div class="option option-compact {{ $hasImageOption ? 'option-with-image' : '' }}">
- <strong>{{ $label }}.</strong>
- <span class="option-value {{ $isShortOption ? 'option-short' : 'option-long' }}">{!! $renderedOpt !!}</span>
- </div>
- @endforeach
- </div>
|