Markdown.vue 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. <template>
  2. <div>
  3. <MarkdownNodeRenderer v-for="(node, index) in renderedContent" :key="index" :node="node"/>
  4. </div>
  5. </template>
  6. <script setup lang="ts">
  7. import { computed, defineComponent, h, VNode } from 'vue'
  8. import MarkdownIt from 'markdown-it';
  9. import {parseDocument} from 'htmlparser2'
  10. import { MarkdownPlugin } from '/@/components/markdown/type/markdown'
  11. interface Props {
  12. content: string
  13. plugins?: Array<MarkdownPlugin<any>>
  14. }
  15. const props = withDefaults(defineProps<Props>(), {
  16. plugins: undefined
  17. })
  18. const md = new MarkdownIt({
  19. html: true,
  20. linkify: true,
  21. typographer: true
  22. })
  23. for (const plugin of props.plugins ?? []) {
  24. md.use(plugin.mdItPlugin, plugin.settings)
  25. }
  26. const renderedContent = computed(() => {
  27. // Markdown模式添加安全过滤和样式类,并处理成dom ast
  28. return parseDocument(
  29. md.render(props.content),
  30. ).children
  31. })
  32. const MarkdownNodeRenderer = defineComponent({
  33. name: 'MarkdownNodeRenderer',
  34. props: {
  35. node: {
  36. type: Object,
  37. required: true,
  38. },
  39. },
  40. setup(subprops) {
  41. return () => {
  42. const {node} = subprops;
  43. if (node.type === 'text') {
  44. return node.data
  45. }
  46. for (let i = 0; i < (props.plugins ?? []).length; i++) {
  47. const plugin = (props.plugins ?? [])[i]
  48. if (plugin.tagName === node.tagName) {
  49. return plugin.renderer(node as {attribs: Record<string,string>})
  50. }
  51. }
  52. return h(
  53. node.tagName,
  54. {...node.attribs},
  55. node.children.map((child, index) =>
  56. h(MarkdownNodeRenderer, {node: child, key: index})
  57. )
  58. )
  59. }
  60. },
  61. })
  62. </script>
  63. <style scoped>
  64. /* 基本的markdown样式 */
  65. :deep(h1), :deep(h2), :deep(h3), :deep(h4), :deep(h5), :deep(h6) {
  66. margin-top: 24px;
  67. margin-bottom: 16px;
  68. font-weight: 600;
  69. line-height: 1.25;
  70. }
  71. :deep(h1) {
  72. font-size: 2em;
  73. }
  74. :deep(h2) {
  75. font-size: 1.5em;
  76. }
  77. :deep(h3) {
  78. font-size: 1.25em;
  79. }
  80. :deep(p) {
  81. margin-bottom: 16px;
  82. line-height: 1.6;
  83. }
  84. :deep(pre) {
  85. background-color: #f6f8fa;
  86. border-radius: 6px;
  87. padding: 16px;
  88. overflow: auto;
  89. margin: 16px 0;
  90. }
  91. :deep(code) {
  92. background-color: #f6f8fa;
  93. padding: 2px 4px;
  94. border-radius: 3px;
  95. font-size: 85%;
  96. }
  97. :deep(blockquote) {
  98. border-left: 4px solid #d1d5da;
  99. padding-left: 16px;
  100. margin: 16px 0;
  101. color: #6a737d;
  102. }
  103. :deep(ul), :deep(ol) {
  104. margin: 16px 0;
  105. padding-left: 32px;
  106. }
  107. :deep(li) {
  108. margin: 4px 0;
  109. }
  110. </style>