Преглед на файлове

fix: 知识点讲解功能添加完成,知识点讲解使用 php 解析

yemeishu преди 2 седмици
родител
ревизия
a539ac70f3
променени са 2 файла, в които са добавени 51 реда и са изтрити 118 реда
  1. 50 3
      app/Services/ExamPdfExportService.php
  2. 1 115
      resources/views/pdf/exam-knowledge-explanation.blade.php

+ 50 - 3
app/Services/ExamPdfExportService.php

@@ -2439,7 +2439,6 @@ class ExamPdfExportService
             // 批量获取知识点讲解
             $kps = \App\Models\KnowledgePoint::whereIn('kp_code', $kpCodes)->get()->keyBy('kp_code');
 
-            info('dd', $kps->toArray());
             // 有多少算多少
             foreach ($kpCodes as $kpCode) {
                 $kp = $kps->get($kpCode);
@@ -2447,9 +2446,18 @@ class ExamPdfExportService
                     $result[] = [
                         'kp_code' => $kpCode,
                         'kp_name' => $kp->name ?? $kpCode,
-                        'explanation' => $kp->explanation,
+                        'explanation' => $this->normalizeKpExplanation($kp->explanation),
                     ];
+                    continue;
                 }
+
+                $result[] = [
+                    'kp_code' => $kpCode,
+                    'kp_name' => $kp->name ?? $kpCode,
+                    'explanation' => $this->normalizeKpExplanation(
+                        $this->getDefaultExplanation($kpCode, $kp->name ?? $kpCode)
+                    ),
+                ];
             }
         } catch (\Throwable $e) {
             Log::warning('批量获取知识点讲解失败', [
@@ -2459,7 +2467,13 @@ class ExamPdfExportService
 
             // 失败时返回默认内容
             foreach ($kpCodes as $kpCode) {
-                $result[$kpCode] = $this->getDefaultExplanation($kpCode, $kpCode);
+                $result[] = [
+                    'kp_code' => $kpCode,
+                    'kp_name' => $kpCode,
+                    'explanation' => $this->normalizeKpExplanation(
+                        $this->getDefaultExplanation($kpCode, $kpCode)
+                    ),
+                ];
             }
         }
 
@@ -2513,6 +2527,39 @@ $$
 MARKDOWN;
     }
 
+    private function normalizeKpExplanation(string $content): string
+    {
+        $content = trim($content);
+        if ($content === '') {
+            return '';
+        }
+
+        if ($this->looksLikeHtml($content)) {
+            return $content;
+        }
+
+        if (! class_exists(\Michelf\MarkdownExtra::class)) {
+            $safe = htmlspecialchars($content, ENT_QUOTES, 'UTF-8');
+            return '<div class="kp-markdown-container kp-markdown-content">'.nl2br($safe).'</div>';
+        }
+
+        $parser = new \Michelf\MarkdownExtra;
+        $markdown = html_entity_decode($content, ENT_QUOTES, 'UTF-8');
+        $rendered = $parser->transform($markdown);
+
+        return '<div class="kp-markdown-container kp-markdown-content">'.$rendered.'</div>';
+    }
+
+    private function looksLikeHtml(string $content): bool
+    {
+        if (stripos($content, 'kp-markdown-container') !== false ||
+            stripos($content, 'kp-markdown-content') !== false) {
+            return true;
+        }
+
+        return (bool) preg_match('/<\s*(p|div|h[1-6]|ul|ol|li|table|span|blockquote|pre|code|br)\b/i', $content);
+    }
+
     private function shouldUseDefaultExplanations(): bool
     {
         if (!Schema::hasTable('knowledge_points')) {

+ 1 - 115
resources/views/pdf/exam-knowledge-explanation.blade.php

@@ -25,10 +25,7 @@
                         </div>
                         <div class="kp-section-body">
                             @if(!empty($kp['explanation']))
-                                {{-- 隐藏容器存储原始 Markdown --}}
-                                <div class="kp-markdown-source" style="display:none;">{!! $kp['explanation'] !!}</div>
-                                {{-- 渲染容器 --}}
-                                <div class="kp-markdown-container kp-markdown-content"></div>
+                                {!! $kp['explanation'] !!}
                             @endif
                         </div>
                     </div>
@@ -36,116 +33,5 @@
             </div>
         @endif
     </div>
-
-    {{-- 引入脚本 --}}
-    <script src="/js/markdown-it.min.js"></script>
-    <script src="/js/katex.min.js"></script>
-
-    <script>
-    (function() {
-        'use strict';
-
-        function waitForLibs(callback) {
-            let attempts = 0;
-            const maxAttempts = 50;
-            const interval = setInterval(function() {
-                attempts++;
-                if (typeof window.markdownit === 'function') {
-                    clearInterval(interval);
-                    callback();
-                } else if (attempts >= maxAttempts) {
-                    clearInterval(interval);
-                    console.error('[Render] Libraries failed to load after', maxAttempts, 'attempts');
-                    console.log('[Render] markdownit:', typeof window.markdownit);
-                    callback();
-                }
-            }, 100);
-        }
-
-        function renderMarkdown(md, targetEl) {
-            if (!md) return;
-
-            if (typeof window.markdownit !== 'function') {
-                targetEl.textContent = md;
-                return;
-            }
-
-            const mdParser = window.markdownit({
-                html: false,
-                breaks: false,
-                linkify: true,
-                typographer: false
-            });
-
-            let html = mdParser.render(md);
-
-            if (typeof window.katex !== 'undefined') {
-                const katexOptions = {
-                    throwOnError: false,
-                    displayMode: false
-                };
-
-                function decodeEntities(input) {
-                    return input
-                        .replace(/&gt;/g, '>')
-                        .replace(/&lt;/g, '<')
-                        .replace(/&amp;/g, '&')
-                        .replace(/&quot;/g, '"')
-                        .replace(/&#39;/g, "'");
-                }
-
-                // 先渲染块级公式 $$...$$
-                html = html.replace(/\$\$([\s\S]*?)\$\$/g, function(_, tex) {
-                    try {
-                        const cleaned = decodeEntities(tex.trim());
-                        return window.katex.renderToString(cleaned, { ...katexOptions, displayMode: true });
-                    } catch (e) {
-                        return '<span style="color:red">[KaTeX error]</span>';
-                    }
-                });
-
-                // 再渲染行内公式 $...$
-                html = html.replace(/\$([^\$\n]+?)\$/g, function(_, tex) {
-                    try {
-                        const cleaned = decodeEntities(tex.trim());
-                        return window.katex.renderToString(cleaned, { ...katexOptions, displayMode: false });
-                    } catch (e) {
-                        return '<span style="color:red">[KaTeX error]</span>';
-                    }
-                });
-            }
-
-            targetEl.innerHTML = html;
-        }
-
-        function renderAll() {
-            const containers = document.querySelectorAll('.kp-markdown-container');
-            containers.forEach((container) => {
-                const sourceEl = container.previousElementSibling;
-                let markdown = '';
-                if (sourceEl && sourceEl.classList.contains('kp-markdown-source')) {
-                    markdown = sourceEl.textContent.trim();
-                }
-                if (!markdown) return;
-                renderMarkdown(markdown, container);
-            });
-        }
-
-        if (document.readyState === 'loading') {
-            document.addEventListener('DOMContentLoaded', function() {
-                waitForLibs(renderAll);
-            });
-        } else {
-            waitForLibs(renderAll);
-        }
-
-        document.addEventListener('livewire:initialized', function() {
-            waitForLibs(renderAll);
-        });
-        document.addEventListener('livewire:navigated', () => setTimeout(function() {
-            waitForLibs(renderAll);
-        }, 100));
-    })();
-    </script>
 </body>
 </html>