markdown控件增加支持渲染mermaid图表
This commit is contained in:
parent
f96a710611
commit
5855b84989
@ -9,11 +9,15 @@ const props = defineProps<{ df: any; record: Record<string, any>; canEdit: boole
|
||||
const labelLayout = computed(() => props.df.label_layout || 'vertical')
|
||||
|
||||
const editorHost = ref<HTMLDivElement | null>(null)
|
||||
const previewHost = ref<HTMLDivElement | null>(null)
|
||||
let monaco: any = null
|
||||
let editor: any = null
|
||||
const expanded = ref(false)
|
||||
const mode = ref<'preview' | 'edit'>('preview')
|
||||
|
||||
// 用于动态替换 mermaid 代码块的唯一 ID 计数器
|
||||
let mermaidCounter = 0
|
||||
|
||||
const mdPreview = computed(() => {
|
||||
try {
|
||||
return marked.parse(String(props.record[props.df.fieldname] || '')) as string
|
||||
@ -22,6 +26,49 @@ const mdPreview = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
// 渲染 Mermaid 代码块(异步,marked.parse 之后调用)
|
||||
async function renderMermaidBlocks() {
|
||||
if (!previewHost.value) return
|
||||
const { default: mermaid } = await import('mermaid')
|
||||
mermaid.initialize({ startOnLoad: false, theme: 'default' })
|
||||
|
||||
// 处理带 ```mermaid 围栏的代码块
|
||||
const codeBlocks = previewHost.value.querySelectorAll('pre code.language-mermaid')
|
||||
for (const block of Array.from(codeBlocks)) {
|
||||
const pre = block.parentElement
|
||||
if (!pre || pre.dataset.mermaidRendered) continue
|
||||
const code = block.textContent || ''
|
||||
const id = `md-mermaid-${++mermaidCounter}`
|
||||
try {
|
||||
const { svg } = await mermaid.render(id, code)
|
||||
pre.outerHTML = `<div class="mermaid-diagram" data-mermaid-rendered="true">${svg}</div>`
|
||||
} catch (err) {
|
||||
console.error('[MarkdownEditor] Mermaid render error:', err)
|
||||
pre.dataset.mermaidRendered = 'true'
|
||||
}
|
||||
}
|
||||
|
||||
// 处理裸 Mermaid 语法(不以 ``` 开头,以 flowchart/graph/... 开头)
|
||||
// 遍历所有 <p>,检测是否整段都是裸 Mermaid
|
||||
const paragraphs = previewHost.value.querySelectorAll('p')
|
||||
for (const p of Array.from(paragraphs)) {
|
||||
if (p.dataset.mermaidRendered || p.dataset.mermaidChecked) continue
|
||||
p.dataset.mermaidChecked = 'true'
|
||||
const text = p.textContent || ''
|
||||
const trimmed = text.trim()
|
||||
if (/^(flowchart|graph|sequenceDiagram|classDiagram|stateDiagram|erDiagram|gantt|pie|requirementDiagram|gitGraph|journey)\s/i.test(trimmed)) {
|
||||
const id = `md-mermaid-${++mermaidCounter}`
|
||||
try {
|
||||
const { svg } = await mermaid.render(id, trimmed)
|
||||
p.outerHTML = `<div class="mermaid-diagram" data-mermaid-rendered="true">${svg}</div>`
|
||||
} catch (err) {
|
||||
console.error('[MarkdownEditor] Mermaid render error:', err)
|
||||
p.dataset.mermaidRendered = 'true'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function mountEditor() {
|
||||
if (!editorHost.value) return
|
||||
if (!monaco) monaco = await loader.init()
|
||||
@ -91,6 +138,13 @@ watch(mode, async (m) => {
|
||||
}
|
||||
})
|
||||
|
||||
// 预览模式切换或内容变化时,渲染 Mermaid 图表
|
||||
watch([mode, mdPreview], async () => {
|
||||
if (mode.value !== 'preview') return
|
||||
await nextTick()
|
||||
await renderMermaidBlocks()
|
||||
})
|
||||
|
||||
onBeforeUnmount(disposeEditor)
|
||||
</script>
|
||||
|
||||
@ -118,7 +172,7 @@ onBeforeUnmount(disposeEditor)
|
||||
>{{ props.ctx?.t ? props.ctx.t('Edit') : 'Edit' }}</n-button>
|
||||
</div>
|
||||
|
||||
<div v-if="mode==='preview'" class="md-editor__preview" v-html="mdPreview"></div>
|
||||
<div v-if="mode==='preview'" ref="previewHost" class="md-editor__preview" v-html="mdPreview"></div>
|
||||
<div v-else ref="editorHost" class="md-editor__monaco" :class="{ collapsed: !expanded }"></div>
|
||||
|
||||
<div v-if="mode==='edit'" class="md-editor__actions">
|
||||
@ -158,6 +212,23 @@ onBeforeUnmount(disposeEditor)
|
||||
/* 无溢出隐藏滚动条 */
|
||||
.no-scroll-y :deep(.monaco-scrollable-element > .scrollbar.vertical) { display: none !important; }
|
||||
.no-scroll-x :deep(.monaco-scrollable-element > .scrollbar.horizontal) { display: none !important; }
|
||||
|
||||
/* Mermaid 图表样式 */
|
||||
.md-editor__preview :deep(.mermaid-diagram) {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: #f8fafc;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin: 12px 0;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.md-editor__preview :deep(.mermaid-diagram svg) {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user