前端将Markdown文本转换为富文本显示/编辑,并保存为word文件

参考:https://www.wangeditor.com/
https://blog.csdn.net/weixin_43797577/article/details/138854324

插件:
markdown-it
@traptitech/markdown-it-katex
markdown-it-link-attributes
highlight.js
@wangeditor/editor
@wangeditor/editor-for-vue
html-docx-js-typescript

markdown展示组件:

<!-- 展示 后端传来的Markdown格式文字 的组件 -->
<script setup lang="ts">
import MarkdownIt from 'markdown-it'
import mdKatex from '@traptitech/markdown-it-katex'
import mila from 'markdown-it-link-attributes'
import hljs from 'highlight.js'
import 'highlight.js/styles/default.css';
import { ref, computed } from 'vue';

const props = defineProps<{
  markdown: string // 父组件传入要展示/编辑的markdown格式文本
  fontSize?: string
}>()
// 设定文字大小
const fontSize = computed<string>(() => {
  if (props.fontSize) {
    return props.fontSize
  } else {
    return '16px'
  }
})
// 对外暴露innerText,以供复制
const showAreaRef = ref()
const innerText = computed<string>(() => {
  return showAreaRef.value.innerText
})
defineExpose({
  innerText
})

const mdi = new MarkdownIt({
  linkify: true,
  highlight: (code: any, lang: any) => {
    if (lang && hljs.getLanguage(lang)) {
      return hljs.highlight(code, { language: lang }).value;
    } else {
      return hljs.highlightAuto(code).value;
    }
  }
})

mdi.use(mila, { attrs: { target: '_blank', rel: 'noopener' } })
mdi.use(mdKatex, { blockClass: 'katexmath-block rounded-md p-[10px]', errorColor: ' #cc0000' })


const text = computed<string>(() => {
  const value = props.markdown ?? ''
  // mdi实例将markdown文本渲染成HTML格式文本
  return mdi.render(value)
})


</script>

<template>
  <!-- 展示状态 -->
  <div class="show-area" v-html="text" ref="showAreaRef"></div>
</template>

<style scoped lang="scss">
.show-area {
  width: 100%;
  word-wrap: break-word;
  font-size: v-bind(fontSize);
}
</style>

markdown文本放入富文本编辑器、可导出为word组件

<!-- 编辑 后端传来的Markdown格式文字 的组件 -->
<script setup lang="ts">
import MarkdownIt from 'markdown-it'
import mdKatex from '@traptitech/markdown-it-katex'
import mila from 'markdown-it-link-attributes'
import hljs from 'highlight.js'
import 'highlight.js/styles/default.css';
import { ref, computed, onBeforeUnmount, shallowRef, watch, nextTick } from 'vue';
// WangEditor 相关
import '@wangeditor/editor/dist/css/style.css'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
//将html转为word
import { asBlob } from 'html-docx-js-typescript'
import { useWriteStore } from '@/stores'
import { storeToRefs } from 'pinia'

// 是否要导出文档,监听它,只要值改变就导出
const { isExportDoc } = storeToRefs(useWriteStore())
const props = defineProps<{
  markdown: string // 父组件传入要展示/编辑的markdown格式文本
  title?: string
}>()

// markdown-it 相关
const mdi = new MarkdownIt({
  linkify: true,
  highlight: (code: any, lang: any) => {
    if (lang && hljs.getLanguage(lang)) {
      return hljs.highlight(code, { language: lang }).value;
    } else {
      return hljs.highlightAuto(code).value;
    }
  }
})

mdi.use(mila, { attrs: { target: '_blank', rel: 'noopener' } })
mdi.use(mdKatex, { blockClass: 'katexmath-block rounded-md p-[10px]', errorColor: ' #cc0000' })

const text = computed<string>(() => {
  const value = props.markdown ?? ''
  // mdi实例将markdown文本渲染成HTML格式文本
  return mdi.render(value)
})

// 以下是编辑状态相关代码
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef()
// 编辑器页面高度
const editorHeight = computed(() => {
  return (window.innerHeight - 40) + 'px'
})
// 编辑器编辑部分最小高度
const editorInitHeight = computed(() => {
  return (window.innerHeight - 70) + 'px'
})
const editArea = ref<HTMLDivElement>()
// 内容 HTML
const valueHtml = ref<string>(text.value)
watch(text, (newValue) => {
  // 如果newValue为空字符串,说明传输已经结束,writeStore临时存储的文本已被重置,因此编辑器不再接收
  if (newValue) {
    valueHtml.value = newValue
  }
  else {
    // 传输结束,开启新的一行
    valueHtml.value += '<p>\n</p>'
    ElMessage.success({
      offset: 55,
      message: 'AI撰写完成'
    })
  }
  nextTick(() => {
    editorRef.value.focus(true) // 在内容末尾focus,nextTick确保在内容加载完成后,才让光标focus到末尾
    editArea.value!.scrollTop = editArea.value!.scrollHeight
  })
})

// mode
const mode = ref<string>('default')

const toolbarConfig = {}
const editorConfig = {
  placeholder: '请输入内容...',
  scroll: false
}

// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
  const editor = editorRef.value
  if (editor == null) return
  editor.destroy()
})

const handleCreated = (editor: any) => {
  editorRef.value = editor // 记录 editor 实例,重要!
}
// 下载为word文档函数
async function exportDoc() {
  const editor = editorRef.value
  // 将富文本内容拼接为一个完整的html
  const html = editor.getHtml()
  const value = `<!DOCTYPE html>
  <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <title>文档</title>
    </head>
    <body>
        ${html}
    </body>
  </html>`
  //  landscape就是横着的,portrait是竖着的,默认是竖屏portrait。
  const data = await asBlob(value, { orientation: 'portrait' }) as Blob
  const a = document.createElement('a')
  a.href = window.URL.createObjectURL(data)
  a.setAttribute('download', `${props.title ? props.title : '知识平台智能生成文档'}.docx`)
  a.click()
  // 下载后将标签移除
  a.remove()
}
// 监听,如果值变动,就调用下载函数,导出为word
watch(isExportDoc, () => {
  exportDoc()
})
</script>

<template>
  <!-- 编辑状态 -->
  <Toolbar :defaultConfig="toolbarConfig" :mode="mode" :editor="editorRef"
    style="width: 100%;height: 40px; border-bottom: 1px solid #ccc;position: fixed;z-index: 99;" />
  <div class="edit-area" style="border: 1px solid #ccc" ref="editArea">
    <Editor style="height: auto;margin: 15px 200px 15px 200px;" v-model="valueHtml" :defaultConfig="editorConfig"
      :mode="mode" @onCreated="handleCreated" />
  </div>
</template>

<style scoped lang="scss">
.edit-area {
  margin-top: 40px;
  width: 100%;
  height: v-bind(editorHeight);
  overflow-y: auto;

  :deep(.w-e-text-container) {
    min-height: v-bind(editorInitHeight);
  }
}
</style>

相关推荐

  1. 如何csv文件转换markdown表格

    2024-06-18 04:52:01       38 阅读
  2. ChatGPT对话:如何把Html文件转换Markdown文件

    2024-06-18 04:52:01       19 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-18 04:52:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-18 04:52:01       101 阅读
  3. 在Django里面运行非项目文件

    2024-06-18 04:52:01       82 阅读
  4. Python语言-面向对象

    2024-06-18 04:52:01       91 阅读

热门阅读

  1. C# —— for循环语句

    2024-06-18 04:52:01       34 阅读
  2. 鸿蒙开发:【启动本地PageAbility】

    2024-06-18 04:52:01       31 阅读
  3. 地学类期刊最新CiteScore™ 汇总

    2024-06-18 04:52:01       27 阅读
  4. 怎么通过AI构架一个个人简介并且写出来

    2024-06-18 04:52:01       37 阅读
  5. C++240617

    C++240617

    2024-06-18 04:52:01      28 阅读
  6. 数据库修复实例2(副本出入口修复)

    2024-06-18 04:52:01       26 阅读
  7. Leetcode 415. 字符串相加-大数相加

    2024-06-18 04:52:01       30 阅读
  8. Docker使用心得

    2024-06-18 04:52:01       37 阅读
  9. 富格林:细心发现虚假确保安全

    2024-06-18 04:52:01       34 阅读
  10. 解析文字示例

    2024-06-18 04:52:01       85 阅读