| 
					
				 | 
			
			
				@@ -0,0 +1,164 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+<template> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  <el-card class="my-card h-full flex-grow"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    <template #header> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      <h3 class="m-0 px-7 shrink-0 flex items-center justify-between"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        <span>思维导图预览</span> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        <!-- 展示在右上角 --> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        <el-button type="primary" v-show="isEnd" @click="downloadImage" size="small"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          <template #icon> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            <Icon icon="ph:copy-bold" /> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          </template> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          下载图片 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        </el-button> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      </h3> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    </template> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    <div ref="contentRef" class="hide-scroll-bar h-full box-border"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      <!--展示 markdown 的容器,最终生成的是 html 字符串,直接用 v-html 嵌入--> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      <div v-if="isGenerating" ref="mdContainerRef" class="wh-full overflow-y-auto"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        <div class="flex flex-col items-center justify-center" v-html="html"></div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      </div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      <div ref="mindMapRef" class="wh-full"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        <svg ref="svgRef" class="w-full" :style="{ height: `${contentAreaHeight}px` }" /> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        <div ref="toolBarRef" class="absolute bottom-[10px] right-5"></div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      </div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    </div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  </el-card> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+</template> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+<script setup lang="ts"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import { Markmap } from 'markmap-view' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import { Transformer } from 'markmap-lib' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import { Toolbar } from 'markmap-toolbar' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import markdownit from 'markdown-it' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import download from '@/utils/download' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const md = markdownit() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const message = useMessage() // 消息弹窗 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const props = defineProps<{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  generatedContent: string // 生成结果 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  isEnd: boolean // 是否结束 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  isGenerating: boolean // 是否正在生成 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  isStart: boolean // 开始状态,开始时需要清除 html 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}>() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const contentRef = ref<HTMLDivElement>() // 右侧出来header以下的区域 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const mdContainerRef = ref<HTMLDivElement>() // markdown 的容器,用来滚动到底下的 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const mindMapRef = ref<HTMLDivElement>() // 思维导图的容器 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const svgRef = ref<SVGElement>() // 思维导图的渲染 svg 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const toolBarRef = ref<HTMLDivElement>() // 思维导图右下角的工具栏,缩放等 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const html = ref('') // 生成过程中的文本 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const contentAreaHeight = ref(0) // 生成区域的高度,出去 header 部分 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+let markMap: Markmap | null = null 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const transformer = new Transformer() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+onMounted(() => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  contentAreaHeight.value = contentRef.value?.clientHeight || 0 // 获取区域高度 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /** 初始化思维导图 **/ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    markMap = Markmap.create(svgRef.value!) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const { el } = Toolbar.create(markMap) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    toolBarRef.value?.append(el) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    nextTick(update) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } catch (e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    message.error('思维导图初始化失败') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+watch(props, ({ generatedContent, isGenerating, isEnd, isStart }) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // 开始生成的时候清空一下 markdown 的内容 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (isStart) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    html.value = '' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // 生成内容的时候使用 markdown 来渲染 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (isGenerating) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    html.value = md.render(generatedContent) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // 生成结束时更新思维导图 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (isEnd) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    update() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** 更新思维导图的展示 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const update = () => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const { root } = transformer.transform(processContent(props.generatedContent)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    markMap?.setData(root) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    markMap?.fit() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } catch (e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    console.error(e) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** 处理内容 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const processContent = (text: string) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const arr: string[] = [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const lines = text.split('\n') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  for (let line of lines) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (line.indexOf('```') !== -1) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      continue 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    line = line.replace(/([*_~`>])|(\d+\.)\s/g, '') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    arr.push(line) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return arr.join('\n') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** 下载图片 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// download SVG to png file 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const downloadImage = () => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const svgElement = mindMapRef.value 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // 将 SVG 渲染到图片对象 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const serializer = new XMLSerializer() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const source = `<?xml version="1.0" standalone="no"?>\r\n${serializer.serializeToString(svgRef.value!)}` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const base64Url = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(source)}` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  download.image({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    url: base64Url, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    canvasWidth: svgElement?.offsetWidth, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    canvasHeight: svgElement?.offsetHeight, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    drawWithImageSize: false 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+defineExpose({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  scrollBottom() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    mdContainerRef.value?.scrollTo(0, mdContainerRef.value?.scrollHeight) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+</script> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+<style lang="scss" scoped> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+.hide-scroll-bar { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  -ms-overflow-style: none; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  scrollbar-width: none; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  &::-webkit-scrollbar { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    width: 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    height: 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+.my-card { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  display: flex; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  flex-direction: column; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  :deep(.el-card__body) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    box-sizing: border-box; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    flex-grow: 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    overflow-y: auto; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    padding: 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @extend .hide-scroll-bar; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// markmap的tool样式覆盖 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+:deep(.markmap) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  width: 100%; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+:deep(.mm-toolbar-brand) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  display: none; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+:deep(.mm-toolbar) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  display: flex; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  flex-direction: row; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+</style> 
			 |