Tinymce富文本编辑器在vue项目中的使用;引入第三方插件和上传视频、图片等

news2025/1/17 4:13:05

先放张效果图

第一步:安装依赖 npm install tinymce@5.0.12

第二步:在项目中的public文件夹中新建tinymce文件夹(因为我的项目是脚手架创建的,所以公共文件夹是public);在node_modules中找到skins文件夹复制到tinymce文件夹中

第三步:因为tinymce是英文版本的,所以要下载汉化包中文js,放到上面的tinymce文件夹中

下载网址:https://www.tiny.cloud/get-tiny/language-packages/

翻到最底部,找到中文语言点击Download按钮,放到和上一步的skins文件夹同级目录的tinymce文件夹中

第四步:封装tinymce组件,这里就直接上代码了;

Tip: 1. <script>里面import的这些都是富文本编辑器的工具栏,需要什么就引入什么;

2. 运行项目可能会有引入的工具报错,针对报错的工具再npm install即可;

第三方插件的引入(数学公式、导入word):

1. 最后两行是另外下载的插件(数学公式、导入word),上面的二和三步的tinymce文件夹里面新建plugins文件夹,下载的两个插件就丢这里面;

数学公式: kityformula-editor 下载地址: http://tinymce.ax-z.cn/more-plugins/kityformula-editor.php

导入word:importword 下载地址:https://github.com/Five-great/tinymce-plugins

2.代码中的toolbar数组里面就是工具的名称;| 符号就是编辑器工具栏的分隔符,界面的工具摆放顺序就是根据代码中的工具摆放顺序

上传视频工具:

1. 编辑器初始化中的“file_picker_callback”方法里面写的是上传视频的内容

2. validateVideo 方法是在文件后面的方法中定义的,目的是验证上传的视频是否符合,验证通过定义了一个全局的遮罩loading,再调用定义的uploadFile方法,调用后台接口把视频源传到后台进行业务的交互,上传成功后必须要写这行代码才能将视频显示在富文本编辑器中:callback(域名 + 代理地址 + 后台返回的路径)

<template>
  <div class="tinymce-editor">
    <editor v-model="myValue" :init="init" :disabled="disabled" @onClick="onClick">
    </editor>
  </div>
</template>
<script>
import tinymce from "tinymce/tinymce";
import "tinymce/themes/silver/theme";
import Editor from "@tinymce/tinymce-vue";
// import "tinymce/icons/default/icons";
// import "tinymce/models/dom/model";
import "tinymce/plugins/print";
import "tinymce/plugins/preview";
import "tinymce/plugins/searchreplace";
import "tinymce/plugins/autolink";
import "tinymce/plugins/directionality";
import "tinymce/plugins/visualblocks";
import "tinymce/plugins/visualchars";
import "tinymce/plugins/fullscreen";
import "tinymce/plugins/image";
import "tinymce/plugins/link";
import "tinymce/plugins/media";
import "tinymce/plugins/template";
import "tinymce/plugins/code";
import "tinymce/plugins/codesample";
import "tinymce/plugins/table";
import "tinymce/plugins/charmap";
import "tinymce/plugins/hr";
import "tinymce/plugins/pagebreak";
import "tinymce/plugins/nonbreaking";
import "tinymce/plugins/anchor";
import "tinymce/plugins/insertdatetime";
import "tinymce/plugins/advlist";
import "tinymce/plugins/lists";
import "tinymce/plugins/wordcount";
import "tinymce/plugins/imagetools";
import "tinymce/plugins/textpattern";
import "tinymce/plugins/help";
import "tinymce/plugins/emoticons";
import "tinymce/plugins/autosave";
// import "tinymce/plugins/bdmap";
// import "tinymce/plugins/indent2em";
import "tinymce/plugins/autoresize";
// import "tinymce/plugins/formatpainter";
// import "tinymce/plugins/axupimgs";
import "../../../../../public/tinymce/plugins/kityformula-editor/plugin.min.js";
import "../../../../../public/tinymce/plugins/importword/plugin.min.js";
import { uploadvideo } from "@admin/api/Settings/Secureaccess/Onlinelearning";
export default {
  components: {
    Editor,
  },
  props: {
    //传入一个value,使组件支持v-model绑定
    value: {
      type: String,
      default: "",
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    plugins: {
      type: [String, Array],
      // default: "lists image media table textcolor wordcount contextmenu",
      default:
        "print preview searchreplace autolink directionality visualblocks visualchars fullscreen image link media template code codesample table charmap hr pagebreak nonbreaking anchor insertdatetime advlist lists wordcount imagetools textpattern help emoticons autosave  autoresize kityformula-editor importword",
    },
    fontsize_formats: "12px 14px 16px 18px 24px 36px 48px 56px 72px",
    toolbar: {
      type: [String, Array],
      // default: "undo redo |  formatselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | lists image media table | removeformat",
      default:
        "code undo redo restoredraft | cut copy paste pastetext | forecolor backcolor bold italic underline strikethrough link anchor | alignleft aligncenter alignright alignjustify outdent indent | \
    styleselect formatselect fontselect fontsizeselect | bullist numlist | blockquote subscript superscript removeformat | \
    table image media charmap hr pagebreak insertdatetime print preview | fullscreen | bdmap indent2em lineheight formatpainter axupimgs | kityformula-editor importword",
    },
    fontFormats: {
      type: String,
      default:
        "微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;宋体=simsun, serif;苹果苹方=PingFang SC, Microsoft YaHei, sans- serif;Arial=arial, helvetica, sans- serif;Arial Black=arial black, avant garde;Book Antiqua=book antiqua, palatino;Comic Sans MS=comic sans ms, sans- serif;Courier New = courier new, courier;Georgia = georgia, palatino;Helvetica = helvetica;Symbol = symbol;Tahoma = tahoma, arial, helvetica, sans - serif;Terminal = terminal, monaco;Times New Roman = times new roman, times;Verdana = verdana, geneva",
    },
  },
  data() {
    return {
      //初始化配置
      init: {
        language_url: "/tinymce/zh_CN.js",
        language: "zh_CN",
        height: 650,
        skin_url: "/tinymce/skins/ui/oxide",
        plugins: this.plugins,
        toolbar: this.toolbar,
        // branding: false,
        // menubar: true,
        // quickbars_selection_toolbar:
        //   "removeformat | bold italic underline strikethrough | fontsizeselect forecolor backcolor",
        font_formats: this.fontFormats,
        fontsize_formats: this.fontsize_formats,
        //此处为图片上传处理函数,这个直接用了base64的图片形式上传图片,
        //如需ajax上传可参考https://www.tiny.cloud/docs/configure/file-image-upload/#images_upload_handler
        images_upload_handler: (blobInfo, success, failure) => {
          const img = "data:image/jpeg;base64," + blobInfo.base64();
          success(img);
        },
        file_picker_types: "media",
        media_live_embeds: true,
        file_picker_callback: (callback, value, meta) => {
          if (meta.filetype === "media") {
            const input = document.createElement("input");
            input.setAttribute("type", "file");
            const that = this; // 为 Vue 构造函数中的 this,指向 Vue 实例对象
            input.onchange = async function () {
              const file = this.files[0]; // 为 HTMLInputElement 构造函数中的 this,指向 input 实例对象
              const isValid = await that.validateVideo(file);

              if (isValid) {
                const loading = that.$loading({
                  lock: true,
                  text: "Loading",
                  spinner: "el-icon-loading",
                  background: "rgba(0, 0, 0, 0.7)",
                });
                that.uploadFile(file, "video");
                setTimeout(() => {
                  loading.close();
                  if (that.videomsg.url != "") {
                    callback(that.location + "lab/" + that.videomsg.url);
                  }
                  // console.log(that.location + "lab/" + that.videomsg.url);
                }, 2000);
                // const { url } = await that.uploadFile(file, "video");
                // callback(url);
              } else {
                callback();
              }
            };

            input.click();
          }
        },
        // video_template_callback: (data) => {
        //   console.log(data);
        //   let source = that.location + "lab/" + data.source1;
        //   console.log(source);
        //   return `<video width="745" height="420" controls="controls" src=${data.source} />`;
        // },
      },
      myValue: "",
      videomsg: {
        url: "",
        name: "",
      },
      location: "http://" + window.location.host + "/",
    };
  },
  mounted() {
    tinymce.init({});
  },
  methods: {
    //添加相关的事件,可用的事件参照文档=> https://github.com/tinymce/tinymce-vue => All available events
    //需要什么事件可以自己增加
    onClick(e) {
      this.$emit("onClick", e, tinymce);
    },
    //可以添加一些自己的自定义事件,如清空内容
    clear() {
      this.myValue = "";
    },
    //校验视频格式和内存大小
    validateVideo(file) {
      const isMP4 = file.type === "video/mp4";
      const isLt3M = file.size / 1024 / 1024 < 5;

      if (!isMP4) {
        this.$message.error("上传视频必须为 MP4 格式!");

        return false;
      }

      if (!isLt3M) {
        this.$message.error("上传视频大小限制 5M 以内!");

        return false;
      }

      const duration = this.getVideoDuration(file);
      if (duration > 60) {
        this.$message.error("上传视频时长不能超过 60 秒!");

        return false;
      }

      return true;
    },
    //获取视频源
    getVideoDuration(file) {
      return new Promise((resolve) => {
        const videoElement = document.createElement("video");
        videoElement.src = URL.createObjectURL(file);

        videoElement.addEventListener("loadedmetadata", () => {
          resolve(videoElement.duration);
        });
      });
    },
    //上传视频
    uploadFile(file, folder = "images") {
      const formData = new FormData();
      formData.append("file", file);
      uploadvideo(formData).then((res) => {
        if (res.code == 0) {
          this.videomsg.url = res.data;
          this.videomsg.name = file.name;
          // return {
          //   url: res.data,
          //   name: file.name,
          // };
        } else {
          this.$message({
            message: res.message,
            type: "error",
            duration: "5000",
          });
        }
      });
    },
  },
  watch: {
    value(newValue) {
      this.myValue = newValue;
      // console.log(this.myValue);
    },
    myValue(newValue) {
      this.$emit("input", newValue);
    },
  },
};
</script>
<style>
.tox-tinymce-aux {
  z-index: 3000 !important;
}
.el-loading-mask {
  z-index: 4000 !important;
}
</style>

第五步:在页面中调用上面封装好的组件

Tip: 1.<el-dialog>中使用TinymceEditor要注意延迟显示,延迟在弹窗显示之后再显示富文本编辑器,因为弹窗元素未加载出来时,编辑器找不到父级元素就无法正常显示

2.赋值和清除富文本编辑器的内容只要针对绑定的值进行清空和赋值操作就行

<template>
<div>
<TinymceEditor
        v-model="diaForm.content"
        :disabled="disabled"
        @onClick="onClick"
        ref="editor"
        v-if="openEditor"
      >
      </TinymceEditor>
</div>
</template>
<script>
import TinymceEditor from "@admin/components/tinymce/index";
export default {
  data() {
    return {
      
    }
  },
  components: {
    TinymceEditor
  }
}

</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/422987.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

插件化换肤原理—— 布局加载过程、View创建流程、Resources 浅析

作者&#xff1a;孙先森Blog 本文主要分析了 Android 布局加载流程 分析 一般的换肤功能大概是这样的&#xff1a;在 App 的皮肤商城内下载“皮肤包”&#xff0c;下载完成后点击更换界面上的 View 相关资源&#xff08;颜色、样式、图片、背景等&#xff09;发生改变&#xf…

栈的实现及相关OJ题

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了 博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点!人生格言&#xff1a;当你的才华撑不起你的野心的时候,你就应该静下心来学习! 欢迎志同道合的朋友一起加油喔&#x1f9be;&am…

28岁,他是如何成为上市公司测试总监的

现在的大环境下&#xff0c;各行各业都开始内卷起来&#xff0c;测试也不例外&#xff0c;企业要求也越来越高&#xff0c;“会代码”逐渐成为测试工程师的一个标签。你要想拿到一个不错的薪资&#xff0c;必不可少的一个技能—自动化测试&#xff0c;自动化测试难吗&#xff1…

Oracle集合查询详解加练习题

#集合查询 概念&#xff1a;将不同的数据集合&#xff08;SQL查询语句&#xff09;按照集合的规则&#xff0c;拼接一个临时的&#xff0c;新的数据集合&#xff08;表&#xff09; 1.集合&#xff1a;并集、交集、差集 并集 union all 语法&#xff1a;select column1,COLUM…

「MongoDB」时序数据库和MongoDB第二部分-模式设计最佳实践

在上一篇博客文章时间序列数据与MongoDB&#xff1a;第一部分-简介中&#xff0c;我们介绍了时间序列数据的概念&#xff0c;然后介绍了一些可以用于帮助收集时间序列应用程序需求的发现问题。对这些问题的回答有助于指导支持大容量生产应用程序部署所需的模式和MongoDB数据库配…

基于深度学习PaddleOcr身份证识别

之前使用opencv机械学习处理图片&#xff0c;使用Testseract-OCR进行身份证和姓名识别&#xff0c;发现受背景图片的影响比较大&#xff0c;转PaddleOcr&#xff0c;识别成功率能达到使用要求。 PaddleOcr官网地址&#xff1a;飞桨PaddlePaddle-源于产业实践的开源深度学习平台…

Python爬虫-某跨境电商(AM)搜索热词

前言 本文是该专栏的第42篇,后面会持续分享python爬虫干货知识,记得关注。 关于某跨境电商(AM),本专栏前面有单独详细介绍过,获取配送地的cookie信息以及商品库存数据,感兴趣的同学可往前翻阅。 1. python爬虫|爬取某跨境电商AM的商品库存数据(Selenium实战) 2. Seleni…

偶数科技发布实时湖仓数据平台 Skylab 5.0

2023 年 4 月 11 日&#xff0c; 偶数发布了最新的实时湖仓数据平台 Skylab 5.0&#xff0c;平台各个组件进行了全面升级。新版 Skylab 的发布标志着偶数科技具有从数据存储、计算、管理到分析、应用和 AI 能力的完整的数据管理生态闭环&#xff0c;帮助用户实现批流一体、实时…

贴完车衣开车就走?

贴完车衣之后&#xff0c;你以为直接开走就好了吗&#xff1f; 大错特错&#xff01;&#xff01;&#xff01; 正确流程&#xff0c;记得收藏起来&#xff01; 1&#xff1a;膜开箱&#xff1a;这个当场开箱&#xff0c;防止偷梁换柱 2&#xff1a;装贴过程&#xff1a;确认施…

损失函数(Loss Function)一文详解-分类问题常见损失函数Python代码实现+计算原理解析

目录 前言 一、损失函数概述 二、损失函数分类 1.分类问题的损失函数 1.交叉熵损失函数&#xff08;Cross Entropy Loss&#xff09; 2.Hinge损失函数 3.余弦相似度损失函数&#xff08;Cosine Similarity Loss&#xff09; 4.指数损失函数&#xff08;Exponential Los…

Next.js Polygon, Solidity,The Graph,IPFS,Hardhat web3博客系统

参考 源文档The Complete Guide to Full Stack Web3 Development - DEV Community 源码&#xff0c;源文章里的github项目无法直接运行&#xff0c;经过修改后可mac中可用GitHub - daocodedao/web3-blog: https://linzhji.blog.csdn.net/article/details/130125634 框架 博客…

python pandas数据处理excel、csv列转行、行转列(具体示例)

一、数据处理需求 对Excel或CSV格式的数据,我们经常都是使用pandas库读取后转为DataFrame进行处理。有的时候我们需要对其中的数据进行行列转换,但是不是简单的行列转换,因为数据中有重复的数据属性。比如我们的数据在Excel中的格式如下: 那么,我们如何将上面的数据格式转…

2023爱分析·商业智能应用解决方案市场厂商评估报告:数聚股份

目录 1. 研究范围定义 2. 商业智能应用解决方案市场分析 3. 厂商评估&#xff1a;数聚股份 4. 入选证书 1. 研究范围定义 商业智能&#xff08;BI&#xff09;是在实现数据集成和统一管理的基础上&#xff0c;利用数据存储和处理、分析与展示等技术&#xff0c;满足企…

Java多线程案例-Java多线程(3)

各位观众老爷们, 创作不易, 多多支持&#x1f636;‍&#x1f32b;️&#x1f64f;&#x1f618; 字数11223, 时间:2023年4月16日11:19:58 状态: 精神恍恍惚惚, 想打游戏&#x1f975; 目录(点击传送) 单例模式 饿汉模式 懒汉模式 单线程版 多线程版 阻塞队列 什么是阻塞队…

助力工业物联网,工业大数据之ODS层及DWD层建表语法【七】

文章目录ODS层及DWD层构建01&#xff1a;课程回顾02&#xff1a;课程目标03&#xff1a;数仓分层回顾04&#xff1a;Hive建表语法05&#xff1a;Avro建表语法ODS层及DWD层构建 01&#xff1a;课程回顾 一站制造项目的数仓设计为几层以及每一层的功能是什么&#xff1f; ODS&am…

Pytorch-gpu的安装

1.先安装cuda和cudnn 推荐安装cuda11.3和cuda10.2&#xff0c;因为这两个版本用的多。 安装教程可以看我的另一篇文章&#xff1a; cuda和cudnn安装教程 2.安装对应版本的Pytorch Pytorch的版本需要和cuda的版本相对应。具体对应关系可以去官网查看。这里先附上一张对应关系…

openpnp - 顶部相机 - 辅助光(环形灯)的电路原理图

文章目录openpnp - 顶部相机 - 辅助光(环形灯)的电路原理图概述ENDopenpnp - 顶部相机 - 辅助光(环形灯)的电路原理图 概述 同学帮我做的简易灯板设计不太合理, 发热量极大. 想看看商用的环形灯电路啥样的, 如果有可能, 自己做块灯板, 塞进商用环形灯外壳中. 拆解了一个环形灯…

数据库备份

数据库备份&#xff0c;恢复实操 策略一&#xff1a;&#xff08;文件系统备份工具 cp&#xff09;&#xff08;适合小型数据库&#xff0c;是最可靠的&#xff09; 1、停止MySQL服务器。 2、直接复制整个数据库目录。注意&#xff1a;使用这种方法最好还原到相同版本服务器中&…

【图像分类】【深度学习】ViT算法Pytorch代码讲解

【图像分类】【深度学习】ViT算法Pytorch代码讲解 文章目录【图像分类】【深度学习】ViT算法Pytorch代码讲解前言ViT(Vision Transformer)讲解patch embeddingpositional embeddingTransformer EncoderEncoder BlockMulti-head attentionMLP Head完整代码总结前言 ViT是由谷歌…

Spring Boot+Vue实现Socket通知推送

目录 Spring Boot端 第一步&#xff0c;引入依赖 第二步&#xff0c;创建WebSocket配置类 第三步&#xff0c;创建WebSocket服务 第四步&#xff0c;创建Controller进行发送测试 Vue端 第一步&#xff0c;创建连接工具类 第二步&#xff0c;建立连接 ​编辑 第三步&a…