123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- <template>
- <div>
- <MarkdownNodeRenderer v-for="(node, index) in renderedContent" :key="index" :node="node"/>
- </div>
- </template>
- <script setup lang="ts">
- import { computed, defineComponent, h, VNode } from 'vue'
- import MarkdownIt from 'markdown-it';
- import {parseDocument} from 'htmlparser2'
- import { MarkdownPlugin } from '/@/components/markdown/type/markdown'
- interface Props {
- content: string
- plugins?: Array<MarkdownPlugin<any>>
- }
- const props = withDefaults(defineProps<Props>(), {
- plugins: undefined
- })
- const md = new MarkdownIt({
- html: true,
- linkify: true,
- typographer: true
- })
- for (const plugin of props.plugins ?? []) {
- md.use(plugin.mdItPlugin, plugin.settings)
- }
- const renderedContent = computed(() => {
- // Markdown模式添加安全过滤和样式类,并处理成dom ast
- return parseDocument(
- md.render(props.content),
- ).children
- })
- const MarkdownNodeRenderer = defineComponent({
- name: 'MarkdownNodeRenderer',
- props: {
- node: {
- type: Object,
- required: true,
- },
- },
- setup(subprops) {
- return () => {
- const {node} = subprops;
- if (node.type === 'text') {
- return node.data
- }
- for (let i = 0; i < (props.plugins ?? []).length; i++) {
- const plugin = (props.plugins ?? [])[i]
- if (plugin.tagName === node.tagName) {
- return plugin.renderer(node as {attribs: Record<string,string>})
- }
- }
- return h(
- node.tagName,
- {...node.attribs},
- node.children.map((child, index) =>
- h(MarkdownNodeRenderer, {node: child, key: index})
- )
- )
- }
- },
- })
- </script>
- <style scoped>
- /* 基本的markdown样式 */
- :deep(h1), :deep(h2), :deep(h3), :deep(h4), :deep(h5), :deep(h6) {
- margin-top: 24px;
- margin-bottom: 16px;
- font-weight: 600;
- line-height: 1.25;
- }
- :deep(h1) {
- font-size: 2em;
- }
- :deep(h2) {
- font-size: 1.5em;
- }
- :deep(h3) {
- font-size: 1.25em;
- }
- :deep(p) {
- margin-bottom: 16px;
- line-height: 1.6;
- }
- :deep(pre) {
- background-color: #f6f8fa;
- border-radius: 6px;
- padding: 16px;
- overflow: auto;
- margin: 16px 0;
- }
- :deep(code) {
- background-color: #f6f8fa;
- padding: 2px 4px;
- border-radius: 3px;
- font-size: 85%;
- }
- :deep(blockquote) {
- border-left: 4px solid #d1d5da;
- padding-left: 16px;
- margin: 16px 0;
- color: #6a737d;
- }
- :deep(ul), :deep(ol) {
- margin: 16px 0;
- padding-left: 32px;
- }
- :deep(li) {
- margin: 4px 0;
- }
- </style>
|