VUE后台管理中使用富文本编辑器导入word 文档进行编辑

VUE后台管理中使用富文本编辑器导入word 文档进行编辑

前言

最近因业务需求在项目中嵌入了tinymce这个编辑器,用于满足平台给用户编辑各类新闻内容什么的业务需求,前后也花了不少时间体验和对比了市面上各类开源编辑器,直接将新闻部门的工作从半天工作量提升只要一个小时就可以搞到,一两分钟就可以搞定一个任务


优点

文档好,功能强,bug少,无外部依赖。

word文档粘贴进来要带格式
兼容移动端
word文档粘贴进来要正常显示并且还要兼容移动端
电脑网页里粘贴进来内容要正常显示并且排版还不能乱
电脑网页拷过来的内容还要兼容到移动端

安装

npm install tinymce @tinymce/tinymce-vue@3.2.8 -S

下载语言包

中文语言包下载

image-20220211184250189.png

开始操作文件

将依赖包 node_modules 里找到 tinymce文件夹,复制到public 里,【左图是tinymce,右图是public复制后的目录】

一下是注意点:

很多的博客的写法是将 node_modules 里面的skins文件夹复制到public/tinymce目录下,经过尝试是不完善的,需要将整个目录倒入进public里

image-20220211184608731.png

image-20220211184712028.png

不完全导入的错误显示,我还以为是语言包的问题,一个个语言包版本去试,这种展示一半英文一半中文的

image-20220211185124184.png

image-20220211185159630.png

导入tinymcejs

public/index.html 添加 tinymce.js

<div id="app"></div>
<script src="/tinymce/tinymce.min.js"></script>

引入基本文件

// 引入组件
import tinymce from 'tinymce/tinymce'
import Editor from '@tinymce/tinymce-vue'
// 引入富文本编辑器主题的js和css
import 'tinymce/themes/silver/theme.min.js'
import 'tinymce/skins/ui/oxide/skin.min.css'
// 扩展插件
import 'tinymce/plugins/image'
import 'tinymce/plugins/link'
import 'tinymce/plugins/code'
import 'tinymce/plugins/table'
import 'tinymce/plugins/lists'
import 'tinymce/plugins/wordcount'

注册组件

components: { Editor },

使用组件

<div class="activeConfig-container">
      <Editor id="tinymce" v-model="tinymceHtml" :init="editorInit" />
    </div>

tinymce 初始化配置

data() {
    return {
      // tinymce的绑定值
      tinymceHtml: '',
      // tinymce的初始化配置
      editorInit: {
        selector: '#tinymce',
        language_url: '/tinymce/langs/zh_CN.js',
        language: 'zh_CN',
        skin_url: '/tinymce/skins/ui/oxide',
        height: 400,
        plugins: 'link lists image code table wordcount importword',
        toolbar: 'bold italic underline strikethrough | fontsizeselect | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist | outdent indent blockquote | undo redo | link unlink image code | removeformat | importword',
        //点击富文本图片上传的时候将图片转成base64再通过success插入
        images_upload_handler: (blobInfo, success) => {
          const img = 'data:image/jpeg;base64,' + blobInfo.base64()
          success(img)
        },
        importword_filter: function(result, insert, message) {
          // console.log(result)
          // console.log(insert)
          // console.log(message)
          // 自定义操作部分
          insert(result) // 回插函数
        },
        // statusbar: false // 是否隐藏底部的状态栏
        // menubar: false, // 是否隐藏最上方的菜单
        branding: false // 是否禁用“Powered by TinyMCE”
      }
    }
  },

整体代码

<template>
  <div class="activeConfig">
    <div class="activeConfig-container">
      <Editor id="tinymce" v-model="tinymceHtml" :init="editorInit" />
    </div>
  </div>
</template>
​
<script>
// 引入组件
import tinymce from 'tinymce/tinymce'
import Editor from '@tinymce/tinymce-vue'
// 引入富文本编辑器主题的js和css
import 'tinymce/themes/silver/theme.min.js'
import 'tinymce/skins/ui/oxide/skin.min.css'
// 扩展插件
import 'tinymce/plugins/image'
import 'tinymce/plugins/link'
import 'tinymce/plugins/code'
import 'tinymce/plugins/table'
import 'tinymce/plugins/lists'
import 'tinymce/plugins/wordcount'
​
// import { uploadImgage } from '@/api/activeConfig'
​
export default {
  name: 'ActiveConfig',
  components: { Editor },
  data() {
    return {
      // tinymce的绑定值
      tinymceHtml: '',
      // tinymce的初始化配置
      editorInit: {
        selector: '#tinymce',
        language_url: '/tinymce/langs/zh_CN.js',
        language: 'zh_CN',
        skin_url: '/tinymce/skins/ui/oxide',
        height: 400,
        plugins: 'link lists image code table wordcount importword',
        toolbar: 'bold italic underline strikethrough | fontsizeselect | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist | outdent indent blockquote | undo redo | link unlink image code | removeformat | importword',
        // 此处为图片上传处理函数
        // images_upload_handler: (blobInfo, success, failure) => {
        //   this.handleImgUpload(blobInfo, success, failure)
        // },
        images_upload_handler: (blobInfo, success) => {
          const img = 'data:image/jpeg;base64,' + blobInfo.base64()
          success(img)
        },
        importword_filter: function(result, insert, message) {
          // console.log(result)
          // console.log(insert)
          // console.log(message)
          // 自定义操作部分
          insert(result) // 回插函数
        },
        // statusbar: false // 是否隐藏底部的状态栏
        // menubar: false, // 是否隐藏最上方的菜单
        branding: false // 是否禁用“Powered by TinyMCE”
      }
    }
  },
  mounted() {
    tinymce.init({})
  },
  methods: {
    // 图片上传
    handleImgUpload(blobInfo, success, failure) {
      // this.baseUrl = process.env.VUE_APP_BASE_URL
      // const imgBase64 = 'data:image/jpeg;base64,' + blobInfo.base64()
      // const data = { img: [imgBase64] }
      // uploadImgage(data).then(res => {
      //   // 传入success回调里的数据就是富文本编辑器里插入图片的src的值
      //   success(`${this.baseUrl}/${res.data[0]}`)
      // }).catch(() => { failure('error') })
    }
  }
}
</script>
​
<style lang="scss" scoped>
.activeConfig {
  &-container {
    margin: 30px;
  }
}
</style>
​

特殊需求

需要找个能实现word文档上传到富文本编辑器,减轻编辑机构的负担,解决机构需要每次打开文档复制再编辑的繁琐工作,实现直接导入编辑

tinymce插件

一个下载插件,一个是注意点,图片在富文本是base64的方式

image-20220211191016020.png

导入插件

下载解压后

image-20220211191235021.png

将整个文件复制到public的插件目录

image-20220211191330453.png

再往tinymce配置添加即可

editorInit.plugins 和 editorInit.toolbar 添加importword即可

editorInit: {
        selector: '#tinymce',
        language_url: '/tinymce/langs/zh_CN.js',
        language: 'zh_CN',
        skin_url: '/tinymce/skins/ui/oxide',
        height: 400,
        plugins: 'link lists image code table wordcount importword',
        toolbar: 'bold italic underline strikethrough | fontsizeselect | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist | outdent indent blockquote | undo redo | link unlink image code | removeformat | importword'
        }

图片解决方案

如果碰到图片的问题,处理图片有两种方式:
1、一种在提交内容的时候统一处理图片,稿件文件里图片只要提交一次。
2、一种是监听图片上传,每次更新图片都上传图片。

图片上传函数有

images_upload_handler

第二种

const files = []
const base64ImgSum = []
for (let i = 0; i < imgs.length; i++) {
    //去除不用的属性
    imgs[i].removeAttribute('data-mce-src')
    imgs[i].removeAttribute('alt')
    //拿到所有的图片列表
    imgs[i].src.includes('base') && base64ImgSum.push(imgs[i])
}

//将base64图片转换file文件
dataURLToFile(dataURL, filename) {
    const arr = dataURL.split(',')
    const mime = arr[0].match(/:(.*?);/)[1] // mime类型 image/png
    const bstr = atob(arr[1]) // base64 解码

    let n = bstr.length
    const u8arr = new Uint8Array(n)

    while (n--) {
        u8arr[n] = bstr.charCodeAt(n)
    }

    return new File([u8arr], filename, { type: mime })
    // return new Blob([a8arr], {type: mime});
},
    
//将处理file文件当参处理即可
fd.append('file', this.dataURLToFile(base64ImgSum[i].src, `${(new Date()).getTime()}.jpg`))

完整代码

<template>
 <!-- 稿件文本编辑器 -->
 <div class="activeConfig">
   <div style="display:flex">
     <div class="activeConfig-container">
       <Editor id="tinymce" v-model="tinymceHtml" :init="editorInit" />
     </div>
     <div style="width:98px;margin: 0 10px;text-align:center">
       <div><el-button :disabled="!tinymceHtml" @click="openPreview">预览页面</el-button></div>
       <div><el-button :disabled="!tinymceHtml" style="margin:10px 0" @click="deleteBlankLine">删除空行</el-button></div>
       <!-- <div style="margin:10px 0"><el-button @click="getImgList" :disabled="!tinymceHtml">上传图片</el-button></div> -->
     </div>
   </div>
   <previewView ref="previewId" :content="tinymceHtml" />
 </div>
</template>

<script>
// 引入组件
import tinymce from 'tinymce/tinymce'
import Editor from '@tinymce/tinymce-vue'
// 引入富文本编辑器主题的js和css
import 'tinymce/themes/silver/theme.min.js'
import 'tinymce/skins/ui/oxide/skin.min.css'
// 扩展插件
import 'tinymce/plugins/image'
import 'tinymce/plugins/link'
import 'tinymce/plugins/code'
import 'tinymce/plugins/table'
import 'tinymce/plugins/lists'
import 'tinymce/plugins/wordcount'

import {
 uploadBatch
} from '@/api/myTask'

import previewView from './preview'

export default {
 name: 'ActiveConfig',
 components: { Editor, previewView },
 props: {
   taskId: {
     type: Number,
     required: true,
     default: 1
   },
   authorize: {
     type: Object,
     required: true
   }
 },
 data() {
   return {
     // tinymce的绑定值
     tinymceHtml: '',
     // tinymce的初始化配置
     editorInit: {
       selector: '#tinymce',
       language_url: '/tinymce/langs/zh_CN.js',
       language: 'zh_CN',
       skin_url: '/tinymce/skins/ui/oxide',
       height: 400,
       plugins: 'image importword wordcount fullscreen',
       toolbar: 'image | importword | fullscreen',
       menubar: false,
       content_style: '.tox-tinymce-aux {z-index: 2600 !important;}',
       // 此处为图片上传处理函数
       // images_upload_handler: (blobInfo, success, failure) => {
       //   this.handleImgUpload(blobInfo, success, failure)
       // },
       images_upload_handler: (blobInfo, success) => {
         const img = 'data:image/jpeg;base64,' + blobInfo.base64()
         //   // console.log(1111)
         //   setTimeout(() => {
         //     // success('https://t7.baidu.com/it/u=1956604245,3662848045&fm=193&f=GIF')
         //   }, 3000)

         success(img)
       },
       importword_filter: (result, insert, message) => {
         // console.log(result)

         // console.log(insert)
         // console.log(message)
         // 自定义操作部分
         setTimeout(() => {
           const trTileList = document.getElementById('tinymce_ifr').contentWindow.document.body.getElementsByTagName('table').item(0).getElementsByTagName('tr')
           console.log(trTileList, trTileList.length)
           if (trTileList.length < 4) {
             console.log('表格数据格式错误')
           } else {
             console.log(document.getElementById('tinymce_ifr').contentWindow.document.body.getElementsByTagName('table').item(0).getElementsByTagName('tr'))
             const titleList = {}
             for (const index in trTileList) {
               console.log(index, typeof index)
               if (index === '0') titleList.bigTitleA = trTileList[index].innerText
               if (index === '1') titleList.bigTitleB = trTileList[index].innerText
               if (index === '2') titleList.bigTitleC = trTileList[index].innerText
               if (index === '3') titleList.shortTitle = trTileList[index].innerText
             }
             console.log(trTileList[0].innerText)
             console.log(titleList)
             this.$emit('updateTitle', titleList)

             document.getElementById('tinymce_ifr').contentWindow.document.body.getElementsByTagName('table').item(0).remove()
           }
         }, 1000)
         insert(result) // 回插函数
       },
       // statusbar: false // 是否隐藏底部的状态栏
       // menubar: false, // 是否隐藏最上方的菜单
       branding: false // 是否禁用“Powered by TinyMCE”
     }
   }
 },
 mounted() {
   tinymce.init({})
 },
 methods: {
   // 图片上传
   handleImgUpload(blobInfo, success, failure) {
     // this.baseUrl = process.env.VUE_APP_BASE_URL
     // const imgBase64 = 'data:image/jpeg;base64,' + blobInfo.base64()
     // const data = { img: [imgBase64] }
     // uploadImgage(data).then(res => {
     //   // 传入success回调里的数据就是富文本编辑器里插入图片的src的值
     //   success(`${this.baseUrl}/${res.data[0]}`)
     // }).catch(() => { failure('error') })
   },
   dataURLToFile(dataURL, filename) {
     const arr = dataURL.split(',')
     const mime = arr[0].match(/:(.*?);/)[1] // mime类型 image/png
     const bstr = atob(arr[1]) // base64 解码

     let n = bstr.length
     const u8arr = new Uint8Array(n)

     while (n--) {
       u8arr[n] = bstr.charCodeAt(n)
     }

     return new File([u8arr], filename, { type: mime })
     // return new Blob([a8arr], {type: mime});
   },
   getImgList() {
     const imgs = document.getElementById('tinymce_ifr').contentWindow.document.getElementsByTagName('img')
     // console.log(imgs)
     const files = []
     const base64ImgSum = []
     for (let i = 0; i < imgs.length; i++) {
       imgs[i].removeAttribute('data-mce-src')
       imgs[i].removeAttribute('alt')
       // console.log(imgs[i].src.includes('base'))
       imgs[i].src.includes('base') && base64ImgSum.push(imgs[i])
     }

     if (base64ImgSum.length) {
       for (let i = 0; i < base64ImgSum.length; i++) {
         // const blob = this.dataURLToFile(base64ImgSum[i].src, 'Whatever.jpg')
         // console.log(blob)
         const fd = new FormData()
         fd.append('file', this.dataURLToFile(base64ImgSum[i].src, `${(new Date()).getTime()}.jpg`))
         fd.append('id', this.taskId)
         uploadBatch(fd, this.authorize).then(res => {
           // console.log(res)
           files.push(res.msg[0].url)
           base64ImgSum[i].src = res.msg[0].url
           if (files.length === base64ImgSum.length) {
             // console.log('图片上传成功')
             this.$emit('updateEidt')
           }
         })
       }
     } else {
       this.$emit('updateEidt')
     }
   },
   // 删除空行
   deleteBlankLine() {
     const brLength = document.getElementById('tinymce_ifr').contentWindow.document.getElementsByTagName('br')
     console.log(brLength.length)
     while (brLength.length) {
       console.log(brLength[brLength.length - 1].parentNode.children.length)
       if (brLength[brLength.length - 1].parentNode.children.length === 1) {
         brLength[brLength.length - 1].parentNode.remove()
       } else if (brLength[brLength.length - 1].parentNode.children.length > 1) {
         brLength[brLength.length - 1].remove()
       }
     }

     this.tinymceHtml = document.getElementById('tinymce_ifr').contentWindow.document.body.innerHTML
   },
   openPreview() {
     this.$refs.previewId.dialogVisible = true
   }
 }
}
</script>

<style lang="scss" scoped>
.activeConfig-container{
   width: 100%;
}
.tox-silver-sink,.tox-tinymce-aux{
   z-index: 2600 !important;
}
</style>

  • 8
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 49
    评论
ISO 21498-2是国际标准化组织(ISO)制定的标准,它的全称是"ISO 21498-2:2018 有关信息与文件管理的文件识别和描述术语和数据模型的规范 第2部分:数据模型"。这个标准是为了指导和规范信息和文件管理领域的实践,以确保相关数据能够被准确地识别、描述和管理。 ISO 21498-2标准主要包括两个方面的内容:文件识别和描述术语以及数据模型。其,文件识别和描述术语的目的是为了确保文件能够被唯一地标识和描述。这些术语包括文件类型、属性、关系等,通过统一的命名和定义,可以让用户更好地理解和管理文件。数据模型则是指定义了信息和文件的结构和关系,以及其在系统的表示方式。通过统一的数据模型,不同的系统和应用程序可以对信息和文件进行共享和交换,提高工作效率和数据的一致性。 实际应用,ISO 21498-2可以帮助组织和机构实现信息和文件的准确管理。例如,在电子档案管理,该标准可以确保每个文件都有唯一的标识号,方便检索和访问。在企业信息管理,该标准可以统一数据模型,确保不同系统之间的数据一致性。另外,在数字化存档和信息共享方面,该标准也能提供指导和规范。 总之,ISO 21498-2标准的目的是为了确保文件在信息和文件管理过程能够准确地被识别、描述和管理。它的使用能够帮助机构和组织更好地管理信息和文件,提高工作效率和数据的一致性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 49
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值