1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 纯前端通过插件 导入/导出word markdown到tinymce编辑器中

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

时间:2022-05-14 15:47:01

相关推荐

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

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

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

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

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

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

用到插件

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

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

import { Button, Space, Upload } from 'ant-design';// 将markdown转为htmlimport { marked } from 'marked';// 将word转为htmlimport Mammoth from 'mammoth';// 方法集合import { saveAs } from 'file-saver';// 将html转为docximport { asBlob } from 'html-docx-js-typescript';// 将html转为markdownimport htmlToMd from 'html-to-md';interface ImportBtnProps {// 导出html内容contentHtml: string;// 导入给编辑器赋值onContentChange: (html: string) => void;}const ImportBtn: React.FC<ImportBtnProps> = (props) => {const { contentHtml, onContentChange } = props;// 上传markdownconst 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 转换成htmlMammoth.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转换为markdownconst markdown = htmlToMd(contentHtml);let blob = new Blob([markdown]);// 创建urllet 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} /><EditorinitialValue={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="/2000/svg" xmlns:xlink="/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

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。