math-render.blade.php 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. @props(['content' => '', 'class' => '', 'inline' => false])
  2. <div class="math-render {{ $class }}" data-math-content="{!! $content !!}">
  3. {!! $content !!}
  4. </div>
  5. @push('scripts')
  6. <script>
  7. (function() {
  8. 'use strict';
  9. function renderMathElement(element) {
  10. if (typeof window.katex === 'undefined') {
  11. // 等待 KaTeX 加载
  12. if (window.mathRenderAttempts < 50) {
  13. window.mathRenderAttempts++;
  14. setTimeout(() => renderMathElement(element), 100);
  15. }
  16. return;
  17. }
  18. // 避免重复渲染
  19. if (element.dataset.rendered === 'true') {
  20. return;
  21. }
  22. const content = element.dataset.mathContent || element.textContent;
  23. if (!content) return;
  24. try {
  25. let html = content;
  26. // 渲染 $$...$$ 块级公式
  27. html = html.replace(/\$\$([\s\S]*?)\$\$/g, (match, formula) => {
  28. try {
  29. return window.katex.renderToString(formula.trim(), {
  30. throwOnError: false,
  31. displayMode: true
  32. });
  33. } catch (e) {
  34. console.warn('KaTeX render error:', e);
  35. return match;
  36. }
  37. });
  38. // 渲染 $...$ 行内公式
  39. html = html.replace(/\$(.*?)\$/g, (match, formula) => {
  40. try {
  41. return window.katex.renderToString(formula, {
  42. throwOnError: false,
  43. displayMode: false
  44. });
  45. } catch (e) {
  46. console.warn('KaTeX render error:', e);
  47. return match;
  48. }
  49. });
  50. // 渲染 \(...\) 行内公式
  51. html = html.replace(/\\\((.*?)\\\)/g, (match, formula) => {
  52. try {
  53. return window.katex.renderToString(formula, {
  54. throwOnError: false,
  55. displayMode: false
  56. });
  57. } catch (e) {
  58. console.warn('KaTeX render error:', e);
  59. return match;
  60. }
  61. });
  62. element.innerHTML = html;
  63. element.dataset.rendered = 'true';
  64. } catch (e) {
  65. console.error('Math render error:', e);
  66. }
  67. }
  68. function renderAllMath() {
  69. document.querySelectorAll('.math-render:not([data-rendered="true"])').forEach(renderMathElement);
  70. }
  71. // 初始化
  72. document.addEventListener('DOMContentLoaded', () => {
  73. if (typeof window.katex === 'undefined') {
  74. const script = document.createElement('script');
  75. script.src = '/js/katex.min.js';
  76. script.onload = () => {
  77. window.mathRenderAttempts = 0;
  78. renderAllMath();
  79. };
  80. document.head.appendChild(script);
  81. } else {
  82. renderAllMath();
  83. }
  84. });
  85. // Livewire 兼容性
  86. document.addEventListener('livewire:initialized', () => {
  87. renderAllMath();
  88. });
  89. document.addEventListener('livewire:updated', () => {
  90. setTimeout(renderAllMath, 100);
  91. });
  92. // Alpine.js 兼容性
  93. document.addEventListener('alpine:init', () => {
  94. renderAllMath();
  95. });
  96. // 自定义事件监听
  97. document.addEventListener('math:render', () => {
  98. renderAllMath();
  99. });
  100. })();
  101. </script>
  102. @endpush
  103. @push('styles')
  104. <link rel="stylesheet" href="/css/katex/katex.min.css">
  105. @endpush