preview-tool.blade.php 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>题目预览验证工具 - Math CMS</title>
  7. <!-- KaTeX CSS -->
  8. <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
  9. <!-- Tailwind CSS CDN -->
  10. <script src="https://cdn.tailwindcss.com"></script>
  11. <!-- KaTeX JS -->
  12. <script src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
  13. <style>
  14. [x-cloak] { display: none !important; }
  15. .math-preview {
  16. font-family: "SimSun", "Songti SC", serif;
  17. line-height: 1.8;
  18. }
  19. .math-preview .katex {
  20. font-size: 1.1em;
  21. }
  22. .math-preview .katex-display {
  23. margin: 0.5em 0;
  24. }
  25. /* 题目样式 */
  26. .question-stem {
  27. margin-bottom: 1rem;
  28. }
  29. .question-options {
  30. margin-left: 1rem;
  31. }
  32. .question-option {
  33. margin: 0.5rem 0;
  34. }
  35. .question-answer {
  36. margin-top: 1rem;
  37. padding-top: 0.5rem;
  38. border-top: 1px dashed #ccc;
  39. }
  40. .question-solution {
  41. margin-top: 1rem;
  42. padding: 0.75rem;
  43. background: #f9f9f9;
  44. border-radius: 4px;
  45. }
  46. </style>
  47. @livewireStyles
  48. </head>
  49. <body class="bg-gray-100 min-h-screen">
  50. {{ $slot }}
  51. @livewireScripts
  52. <script>
  53. /**
  54. * 数学公式渲染器
  55. * 与前端 MathText.tsx 逻辑保持一致
  56. */
  57. const MathRenderer = {
  58. /**
  59. * 预处理文本:处理双反斜杠、换行符等
  60. */
  61. preprocessText(text) {
  62. let result = text;
  63. // 1. 处理公式内的双反斜杠 -> 单反斜杠
  64. // 块级公式 $$...$$
  65. result = result.replace(/\$\$([\s\S]*?)\$\$/g, (_, tex) => {
  66. return '$$' + tex.replace(/\\\\/g, '\\') + '$$';
  67. });
  68. // 行内公式 $...$
  69. result = result.replace(/\$([^$\n]+?)\$/g, (_, tex) => {
  70. return '$' + tex.replace(/\\\\/g, '\\') + '$';
  71. });
  72. // 2. 换行符处理:\n -> <br>,但保护 LaTeX 命令如 \neq, \nu
  73. result = result.replace(/\\n(?![a-zA-Z])/g, '<br>');
  74. // 3. 统一 LaTeX 分隔符格式
  75. result = result.replace(/\\\[([\s\S]*?)\\\]/g, (_, tex) => `$$${tex}$$`);
  76. result = result.replace(/\\\(([\s\S]*?)\\\)/g, (_, tex) => `$${tex}$`);
  77. return result;
  78. },
  79. /**
  80. * 渲染数学公式
  81. */
  82. render(text) {
  83. if (!text) return '';
  84. let result = this.preprocessText(text);
  85. // 渲染块级公式 $$...$$
  86. result = result.replace(/\$\$([\s\S]*?)\$\$/g, (_, tex) => {
  87. try {
  88. return katex.renderToString(tex.trim(), {
  89. displayMode: true,
  90. throwOnError: false,
  91. strict: false,
  92. });
  93. } catch (e) {
  94. console.error('KaTeX render error:', e);
  95. return `<span class="text-red-500">$$${tex}$$</span>`;
  96. }
  97. });
  98. // 渲染行内公式 $...$
  99. result = result.replace(/\$([^$\n]+?)\$/g, (_, tex) => {
  100. try {
  101. return katex.renderToString(tex.trim(), {
  102. displayMode: false,
  103. throwOnError: false,
  104. strict: false,
  105. });
  106. } catch (e) {
  107. console.error('KaTeX render error:', e);
  108. return `<span class="text-red-500">$${tex}$</span>`;
  109. }
  110. });
  111. return result;
  112. },
  113. /**
  114. * 渲染指定元素内的所有数学公式
  115. */
  116. renderElement(element) {
  117. const mathElements = element.querySelectorAll('[data-math]');
  118. mathElements.forEach(el => {
  119. const rawText = el.getAttribute('data-math');
  120. if (rawText) {
  121. el.innerHTML = this.render(rawText);
  122. }
  123. });
  124. }
  125. };
  126. // 监听 Livewire 事件
  127. document.addEventListener('livewire:init', () => {
  128. Livewire.on('render-math', () => {
  129. setTimeout(() => {
  130. const previewArea = document.getElementById('web-preview-area');
  131. if (previewArea) {
  132. MathRenderer.renderElement(previewArea);
  133. }
  134. }, 100);
  135. });
  136. });
  137. // 页面加载完成后渲染
  138. document.addEventListener('DOMContentLoaded', () => {
  139. const previewArea = document.getElementById('web-preview-area');
  140. if (previewArea) {
  141. MathRenderer.renderElement(previewArea);
  142. }
  143. });
  144. </script>
  145. </body>
  146. </html>