markdown-renderer.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import MarkdownIt from 'markdown-it';
  2. import hljs from 'highlight.js';
  3. import mk from 'markdown-it-katex';
  4. // 配置 markdown-it
  5. const md = new MarkdownIt({
  6. html: true,
  7. linkify: true,
  8. typographer: true,
  9. highlight: function (str, lang) {
  10. if (lang && hljs.getLanguage(lang)) {
  11. try {
  12. return '<pre class="hljs"><code>' +
  13. hljs.highlight(str, { language: lang, ignoreIllegals: true }).value +
  14. '</code></pre>';
  15. } catch (__) {}
  16. }
  17. return '<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>';
  18. }
  19. });
  20. // 添加 LaTeX 数学公式支持
  21. md.use(mk);
  22. // 导出渲染函数
  23. export function renderMarkdown(markdown, container) {
  24. if (!container) {
  25. console.error('Markdown container not found');
  26. return;
  27. }
  28. if (!markdown) {
  29. container.innerHTML = '<p class="text-gray-500">No content</p>';
  30. return;
  31. }
  32. const html = md.render(markdown);
  33. container.innerHTML = html;
  34. // 添加样式类
  35. addMarkdownStyles(container);
  36. }
  37. function addMarkdownStyles(container) {
  38. // 为 markdown 内容添加样式类
  39. const elements = container.querySelectorAll('h1, h2, h3, h4, h5, h6, p, code, pre, blockquote, ul, ol, li, table, thead, tbody, tr, th, td, a, img');
  40. elements.forEach(el => {
  41. // 标题样式
  42. if (el.tagName.match(/^H[1-6]$/)) {
  43. el.classList.add('font-bold', 'mt-6', 'mb-4');
  44. if (el.tagName === 'H1') el.classList.add('text-3xl');
  45. if (el.tagName === 'H2') el.classList.add('text-2xl');
  46. if (el.tagName === 'H3') el.classList.add('text-xl');
  47. if (el.tagName === 'H4') el.classList.add('text-lg');
  48. }
  49. // 段落样式
  50. if (el.tagName === 'P') {
  51. el.classList.add('mb-4', 'leading-relaxed');
  52. }
  53. // 链接样式
  54. if (el.tagName === 'A') {
  55. el.classList.add('text-blue-600', 'hover:text-blue-800', 'underline');
  56. }
  57. // 代码样式
  58. if (el.tagName === 'CODE') {
  59. el.classList.add('bg-gray-100', 'px-2', 'py-1', 'rounded', 'text-sm', 'font-mono');
  60. }
  61. // 预格式化代码样式
  62. if (el.tagName === 'PRE') {
  63. el.classList.add('bg-gray-900', 'p-4', 'rounded-lg', 'overflow-x-auto', 'mb-4');
  64. }
  65. // 块引用样式
  66. if (el.tagName === 'BLOCKQUOTE') {
  67. el.classList.add('border-l-4', 'border-blue-500', 'pl-4', 'italic', 'text-gray-600', 'my-4');
  68. }
  69. // 表格样式
  70. if (el.tagName === 'TABLE') {
  71. el.classList.add('w-full', 'border-collapse', 'mb-4');
  72. }
  73. if (el.tagName === 'TH' || el.tagName === 'TD') {
  74. el.classList.add('border', 'border-gray-300', 'px-4', 'py-2', 'text-left');
  75. }
  76. if (el.tagName === 'TH') {
  77. el.classList.add('bg-gray-100', 'font-semibold');
  78. }
  79. // 列表样式
  80. if (el.tagName === 'UL' || el.tagName === 'OL') {
  81. el.classList.add('mb-4', 'pl-6');
  82. }
  83. if (el.tagName === 'LI') {
  84. el.classList.add('mb-2');
  85. }
  86. // 图片样式
  87. if (el.tagName === 'IMG') {
  88. el.classList.add('max-w-full', 'h-auto', 'rounded-lg', 'shadow-md', 'my-4');
  89. }
  90. });
  91. }
  92. // 导出到全局
  93. if (typeof window !== 'undefined') {
  94. window.renderMarkdown = renderMarkdown;
  95. }