纯前端通过插件 导入/导出word, markdown到tinymce编辑器中

已实现功能(有些功能可能实现不太好, 毕竟现在需求被砍了这里的代码只用到了一点点):

1.导入word只能导入docx格式,文中图片可上传到后端数据库里,样式大体可以,细节有问题

2. 导入markdown, 基本没问题, 图片没办法导入本地的

3.导出markdown, 基本可以形成闭环, 图片会导出为链接形式,我们这里的需求也是将图片放在某个服务器的数据库

4.导出word, 导出word也可以是docx文件, 但是因为原理不同, 不能导出之后重新导入,未实现闭环,没研究,这个放在最后细讲, 导出之后的word只能另存为一份再导入可成功

用到插件

// 将markdown转为html
import { marked } from 'marked';
// 将word转为html
import Mammoth from 'mammoth';
// 方法集合
import { saveAs } from 'file-saver';
// 将html转为docx
import { asBlob } from 'html-docx-js-typescript';
// 将html转为markdown
import htmlToMd from 'html-to-md';

完整代码(只用于测试功能是否可行, 代码并未精简):

import { Button, Space, Upload } from 'ant-design';

// 将markdown转为html
import { marked } from 'marked';
// 将word转为html
import Mammoth from 'mammoth';
// 方法集合
import { saveAs } from 'file-saver';
// 将html转为docx
import { asBlob } from 'html-docx-js-typescript';
// 将html转为markdown
import htmlToMd from 'html-to-md';

interface ImportBtnProps {
  // 导出html内容
  contentHtml: string;
  // 导入给编辑器赋值
  onContentChange: (html: string) => void;
}
const ImportBtn: React.FC<ImportBtnProps> = (props) => {
  const { contentHtml, onContentChange } = props;

  // 上传markdown
  const uploadProps = {
    name: 'file',
    //action: `上传文件的接口地址`,
    headers: {}, // 请求头
    // 是否展示文件列表
    showUploadList: false,
    maxCount: 1,
    beforeUpload: (file: any) => {
      const reader = new FileReader();
      reader.readAsText(file);
      reader.onload = (result) => {
        let targetNum = result?.target?.result;
        // targetNum是文件内容
        const html = marked(targetNum as string, { highlight: (code) => require('/public/js/highlight.min.js').highlightAuto(code).value });
        onContentChange(html);
      };
      return false;
    },
  };
  // 上传word文档
  const wordUploadProps = {
    name: 'file',
    //action: `上传文件的接口地址`,
    headers: {}, // 请求头
    // 是否展示文件列表
    showUploadList: false,
    maxCount: 1,
    beforeUpload: (file: any) => {
      const reader = new FileReader();
      reader.onloadend = function (event) {
        let arrayBuffer = reader.result;
        //将word 转换成html
        Mammoth.convertToHtml({ arrayBuffer: arrayBuffer as ArrayBuffer }).then(function (resultObject) {
          // 重要!不适用setTimeout,无法使用
          setTimeout(() => {
            //获取原来编辑器的内容
            let content = resultObject.value;
            onContentChange(content);
          }, 100);
        });
      };
      reader.readAsArrayBuffer(file);
      return false;
    },
  };

  // 导出markdown文档
  function handleExportMD() {
    // html转换为markdown
    const markdown = htmlToMd(contentHtml);
    let blob = new Blob([markdown]);
    // 创建url
    let url = window.URL.createObjectURL(blob);
    // 创建a标签
    let a = document.createElement('a');
    // 导出地址
    a.href = url;
    // 导出文件名
    a.download = 'markdown.md';
    a.click();
  }

  // 导出word文档
  function handleExportWord() {
    asBlob(`<!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
    </head>
    <body>
    ${contentHtml}
    </body>
    </html>`).then((data) => {
      saveAs(data as Blob, 'test.docx');
    });
  }

  return (
    <div style={{ padding: 8 }}>
      <Space>
        <Upload {...uploadProps}>
          <Button>导入markdown</Button>
        </Upload>
        <Upload {...wordUploadProps}>
          <Button>导入word</Button>
        </Upload>
        <Button onClick={handleExportMD}>导出markdown</Button>
        <Button onClick={handleExportWord}>导出word</Button>
      </Space>
    </div>
  );
};
export default ImportBtn;

使用位置

 <ImportBtn onContentChange={(html) => setContent(html)} contentHtml={content} />
<Editor
        initialValue={content} //父组件传的值
        onEditorChange={handleChange}
        init={{
          toolbar_sticky: false,
          placeholder: '请输入内容',
          color_cols: 4,
          // 允许自定义颜色,会存储在本地
          custom_colors: true,
          // 文字和背景色列表
          color_map: ['#000000', '', '#4E4E4E', '', '#888888', '', '#FD5151', '', '#20C78A', '', '#FF8F16', '', '#007CF8', ''],
          fontsize_formats: '20px 16px 14px 12px',
          // 区块列表
          block_formats: 'Header 1=h1;Header 2=h2;Header 3=h3;正文=p;',
          // 工具栏模式
          toolbar_mode: 'sliding',
          //([插入]快捷工具栏)
          quickbars_insert_toolbar: 'quickimage quicktable becodesample',
          // [选择]快捷工具栏
          quickbars_selection_toolbar: 'h1 h2 h3 blockquote bold removeformat | numlist bullist',
          // 去水印
          branding: false,
          // 禁用编辑器底部的状态栏
          elementpath: false,
          // 隐藏编辑器底部的状态栏
          statusbar: false,
          //隐藏菜单栏
          menubar: false,
          // 网格视觉辅助线 border 为0时
          visual: false,
          language: 'zh_CN',
          inline: false,
          content_css: 'default',
          height: 'calc(100vh - 352px)',
          min_height: 130,
          images_upload_url: '/api/file/uploadFile',
          images_upload_credentials: true,
          convert_urls: true,
          // 拖入上传
          paste_data_images: true,
          powerpaste_allow_local_images: true,
          file_picker_types: 'media',
          // 移除菜单项
          removed_menuitems: 'fontselect',
          plugins:
            'powerpaste textcolor print paste importcss searchreplace autolink autosave save directionality code visualblocks visualchars fullscreen link media template codesample table charmap hr pagebreak nonbreaking toc insertdatetime advlist lists wordcount image imagetools textpattern noneditable help charmap quickbars emoticons becodesample blockquote',
          powerpaste_word_import: 'propmt',
          powerpaste_html_import: 'propmt',
          images_upload_handler: images_upload_handler,
          toolbar:
            'fullscreen code | bold italic underline strikethrough forecolor backcolor link removeformat | formatselect fontsizeselect | numlist bullist | alignleft aligncenter alignright alignjustify outdent indent lineheight | blockquote subscript superscript | table image charmap emoticons insertdatetime fu importMd | pastetext searchreplace undo redo',
          automatic_uploads: true,
          // imagetools_toolbar: 'rotateleft rotateright | flipv fliph | editimage imageoptions',
          // 自定义工具栏按钮
          setup: (editor) => {
            editor.ui.registry.addButton('importMd', {
              icon: 'upload',
              // 通过text自定义图标
              //text: `<span><svg width="87px" height="22px" viewBox="0 0 87 22" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
              // </svg></span>`,
              text: '导入markdown',
              tooltip: '导入markdown',
              onAction: () => importMarkdown(editor),
            })
          }
        }}
      />

继续说上面第三点的未解决问题

word转hmtl的原理是将.docx的后缀名改为zip,然后解压缩,打开/word/docment.xml, 通过转义成html对应的标签

通过上面说的html-docx-js-typescript插件下载的文档, 解压缩之后与我本地的文档解压缩后格式不一致,但是当我把下载下来的文档转为本地最新的word文档(不使用兼容模式)后, 解压后的与本地保持一致.

本地docx文档

插件下载的docx文档

参考文章:

魔改mammoth支持导入样式_Jioho_的博客-CSDN博客_mammoth npm

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
在 Vue2 使用 tinymce-markdown 插件tinymce 富文本框的内容转换为 Markdown 格式,可以参考以下步骤: 1. 安装 tinymcetinymce-markdown 插件。 ```bash npm install tinymce@5.8.0 tinymce-markdown@3.0.2 --save ``` 2. 在 vue 组件引入 tinymcetinymce-markdown 的 js 文件。 ```vue <template> <div> <textarea ref="editor"></textarea> </div> </template> <script> import tinymce from 'tinymce/tinymce'; import 'tinymce/themes/silver'; import 'tinymce/plugins/code'; import 'tinymce/plugins/link'; import 'tinymce/plugins/paste'; import 'tinymce/plugins/table'; import 'tinymce/plugins/lists'; import 'tinymce/plugins/imagetools'; import 'tinymce/plugins/media'; import 'tinymce/plugins/advlist'; import 'tinymce/plugins/autosave'; import 'tinymce/plugins/searchreplace'; import 'tinymce/plugins/wordcount'; import 'tinymce/plugins/hr'; import 'tinymce/plugins/anchor'; import 'tinymce/plugins/pagebreak'; import 'tinymce/plugins/nonbreaking'; import 'tinymce/plugins/fullscreen'; import 'tinymce/plugins/help'; import 'tinymce/plugins/preview'; import 'tinymce/plugins/print'; import 'tinymce/plugins/quickbars'; import 'tinymce/plugins/insertdatetime'; import 'tinymce/plugins/fullpage'; import 'tinymce/plugins/importcss'; import 'tinymce/plugins/colorpicker'; import 'tinymce/plugins/textcolor'; import 'tinymce/plugins/colorpicker'; import 'tinymce/plugins/directionality'; import 'tinymce/plugins/emoticons'; import 'tinymce/plugins/emoticons/js/emojis'; import 'tinymce/plugins/charmap'; import 'tinymce/plugins/visualblocks'; import 'tinymce/plugins/visualchars'; import 'tinymce/plugins/template'; import 'tinymce/plugins/toc'; import 'tinymce/plugins/imagetools'; import 'tinymce/plugins/help'; import 'tinymce/plugins/codesample'; import 'tinymce/plugins/autosave'; import 'tinymce/plugins/contextmenu'; import 'tinymce/plugins/autoresize'; import 'tinymce/plugins/wordcount'; import 'tinymce/plugins/lists'; import 'tinymce/plugins/table'; import 'tinymce/plugins/preview'; import 'tinymce/plugins/searchreplace'; import 'tinymce/plugins/advlist'; import 'tinymce/skins/ui/oxide/skin.min.css'; import 'tinymce/skins/content/default/content.min.css'; import 'tinymce/icons/default/icons.min'; import 'tinymce/plugins/markdown'; import 'tinymce-markdown/dist/tinymce-markdown.min'; export default { mounted() { this.initEditor(); }, methods: { initEditor() { tinymce.init({ selector: this.$refs.editor, height: 400, plugins: 'code link paste table lists imagetools media advlist autosave searchreplace wordcount hr anchor pagebreak nonbreaking fullscreen help preview print quickbars insertdatetime fullpage importcss colorpicker textcolor directionality emoticons charmap visualblocks visualchars template toc codesample autosave contextmenu autoresize markdown', toolbar: 'undo redo | bold italic underline strikethrough | link | alignleft aligncenter alignright alignjustify | table | bullist numlist outdent indent | formatselect fontselect fontsizeselect | fullscreen | code | emoticons | preview', setup: (editor) => { editor.on('init', () => { // 将 tinymce 编辑器的内容转换为 markdown 格式 const content = editor.getContent(); const markdown = editor.plugins.markdown.parse(content); console.log(markdown); }); }, }); }, }, }; ``` 在组件引入了 tinymcetinymce-markdown 插件,以及一些常用的 tinymce 插件和样式。在初始化代码,我们启用了 markdown 插件,并添加了一个名为 markdown 的按钮到编辑器的工具栏。在 `setup` 回调函数,我们监听了编辑器的 `init` 事件,并在事件回调函数调用了 `editor.plugins.markdown.parse(content)` 方法,将编辑器的内容转换为 Markdown 格式,并输出到控制台。 3. 使用转换后的 Markdown 格式进行后续操作。 在转换后,你可以使用转换后的 Markdown 格式进行后续操作,例如将它保存到数据库,或者将它在网页渲染为 HTML 格式。可以使用一些流行的 Markdown 渲染库,例如 marked 或 markdown-it 来将 Markdown 转换为 HTML 格式。 ```javascript // 使用 marked 将 Markdown 转换为 HTML const html = marked(markdown); console.log(html); ```
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值