1.可以复制粘粘上传图片,如果图片解密了则自行封装或者采用其余库引入进行解密

2.使用时可以在main中全局挂载: app.component('RichText', RichText):前提是需要先在main中引入组件然后在挂载哈

3.页面使用:<RichText @getContent="getContent" ref="editor" v-model="skuTabData.detail" />

4.富文本子传父方法:const getContent = (v: string) => {

skuTabData.detail = v/**富文本自定义赋值 */

}

<!--

    富文本编辑器

    vue2.0请下载以下版本

    "tinymce" : "5.1.0"

    "@tinymce/tinymce-vue" : "3.0.1"

    拷贝tinymce三个目录plugins,skins,themes到publiu/tinymce

---------------------------------------------------------------------

vue3.0请下载以下版本

    "tinymce" : "5.10.3"

    "@tinymce/tinymce-vue" : "5.0.0"

    拷贝tinymce四个目录icons,plugins,skins,themes到publiu/tinymce

---------------------------------------------------------------------

汉化

    publiu/tinymce/zh_CN.js 汉化,下载地址  https://www.tiny.cloud/get-tiny/language-packages/

-->

<template>

  <editor v-model="myValue" :init="init" :disabled="disabled" :id="tinymceId" ref="editorRef"></editor>

</template>

<script lang="ts" setup>

import { ref, reactive, watch, onMounted } from "vue";

// //JS部分

// //1、在js中引入所需的主题和组件

import tinymce from "tinymce/tinymce"; //富文本

// import "tinymce/skins/content/default/content.css";

import Editor from "@tinymce/tinymce-vue"; //富文本标签

// import "tinymce/themes/silver";

import "tinymce/themes/silver/theme";

import "tinymce/icons/default"; //引入编辑器图标icon,不引入则不显示对应图标

// 2、引入相关插件

import "tinymce/icons/default/icons"; //自定义icon  操作图标

import "tinymce/plugins/image"; // 插入上传图片插件

import "tinymce/plugins/media"; // 插入视频插件

import "tinymce/plugins/table"; // 插入表格插件

import "tinymce/plugins/emoticons";

import "tinymce/plugins/wordcount"; // 字数统计插件

import "tinymce/plugins/code"; // 源码

import "tinymce/plugins/fullscreen"; //全屏

import "tinymce/plugins/preview";

import "tinymce/icons/default";

import "tinymce/themes/silver";

import "tinymce/plugins/advlist"; //高级列表

import "tinymce/plugins/anchor"; //锚点

import "tinymce/plugins/autolink"; //自动链接

import "tinymce/plugins/autosave"; //编辑器高度自适应,注:plugins里引入此插件时,Init里设置的height将失效

import "tinymce/plugins/codesample"; //代码示例

import "tinymce/plugins/directionality"; //文字方向

import "tinymce/plugins/insertdatetime"; //插入日期时间

import "tinymce/plugins/link"; //链接插件

import "tinymce/plugins/lists"; ////列表插件

import "tinymce/plugins/nonbreaking"; //插入不间断空格

import "tinymce/plugins/noneditable";

import "tinymce/plugins/pagebreak"; //插入分页符

import "tinymce/plugins/paste"; //预览

import "tinymce/plugins/print"; //打印

import "tinymce/plugins/save"; //保存

import 'tinymce/plugins/quickbars'

import 'tinymce/plugins/help'  //帮助

import 'tinymce/plugins/hr'  //水平分割线

import "tinymce/plugins/searchreplace"; //查找替换

// import 'tinymce/plugins/spellchecker' //拼写检查,未加入汉化,不建议使用

import "tinymce/plugins/tabfocus"; //切入切出,按tab键切出编辑器,切入页面其他输入框中

import "tinymce/plugins/template"; //内容模板

import "tinymce/plugins/textpattern"; //快速排版

import "tinymce/plugins/visualblocks"; //显示元素范围

import "tinymce/plugins/visualchars"; //显示不可见字符

import "tinymce/plugins/textcolor"; //文字颜色

import "tinymce/plugins/toc"; //目录生成器

import "../../../public/tinymce/plugins/emoticons/js/emojis.js"

import { ElMessage } from 'element-plus'

import { Upload, Uploadnews } from "@/utils/http";

const emits = defineEmits(["getContent"]);

// 父组件传值

const props = defineProps({

  value: {

    type: String,

    default: () => {

      return "";

    },

  },

  // 禁用富文本编辑器,由父组件传值

  disabled: {

    type: Boolean,

    default: false,

  },

  // 需要加载的插件

  plugins: {

    type: [String, Array],

    default:

      "link lists image code emoticons table wordcount preview fullscreen media searchreplace insertdatetime preview print",

  }, //必填

  // 工具栏

  toolbar: {

    type: [String, Array],

    default:

      "fullscreen undo redo emoticons cut copy paste pastetext | table media image insertdatetime bold italic underline preview | forecolor backcolor print | alignleft aligncenter alignright alignjustify | bdmap indent2em lineheight formatpainter axupimgs letterspacing|  searchreplace  | bullist numlist |  fontselect fontsizeselect",

  }, //必填

  Tinymcesource: {

    type: Number,

    required: true

  }

});

//用于接收外部传递进来的富文本

const editorRef = ref(null);

const myValue = ref(props.value);

// Source:0:教程,1:新闻

const Source = ref(props.Tinymcesource)

const tinymceId = ref(

  //富文本编辑器id

  "vue-tinymce-ID-" + +new Date() + ((Math.random() * 1000).toFixed(0) + "")

);

//定义一个对象 init初始化

const init = reactive({

  selector: "#" + tinymceId.value, //富文本编辑器的id,

  resize: false, //右下角哪个拖动改变大小是否可用

  emoticons_database_url:'/tinymce/emoticons/js/emojis.js',

  // icons_url: '/项目名/icons/custom/icons.js', //自定义图标

  language_url: "/tinymce/langs/zh-Hans.js", //语言包路径

  language: "zh-Hans", //语言

  skin_url: "/tinymce/skins/ui/oxide", //皮肤,自带浅色和深色,在piblic的tinymce的skins的ui里面

  height: 500, //编辑器高度

  branding: false, //是否禁用“Powered by TinyMCE”

  menubar: true, //顶部菜单栏显示

  image_dimensions: true, //去除宽高属性

  plugins: props.plugins, //这里的数据是在props里面就定义好了的

  toolbar: props.toolbar, //这里的数据是在props里面就定义好了的

  toolbar_mode: "sliding", //工具栏模式

  //字体选择

  font_formats: "Arial=arial,helvetica,sans-serif; 宋体=SimSun; 微软雅黑=Microsoft Yahei; Impact=impact,chicago;", //字体

  fontsize_formats: "11px 12px 14px 16px 18px 24px 30px 36px 48px 64px 72px", //文字大小

  // paste_convert_word_fake_lists: false, // 插入word文档需要该属性

  paste_webkit_styles: "all", //此选项允许您指定在WebKit中粘贴时要保留的样式,true保留,false默认样式

  paste_merge_formats: true, //此选项启用粘贴插件的合并格式功能

  nonbreaking_force_tab: true, // 此选项允许您在用户按下键盘tab键时强制TinyMCE插入三个实体

  paste_auto_cleanup_on_paste: false, //粘贴自动清理粘贴

  file_picker_types: "media", // // 此选项允许您通过空格或逗号分隔的类型名称列表指定所需的文件选取器类型。目前有三种有效类型:文件、图像和媒体

  insertdatetime_element: true,

  insertdatetime_formats: ["%Y-%m-%d", "%H:%M:%S"],

  paste_data_images: true,//图片是否可粘贴

  init_instance_callback: function (editor: { on: (arg0: string, arg1: (evt: { clipboardData: object; preventDefault: () => void }) => void) => void }) {

    editor.on('paste', (evt: { clipboardData: any; preventDefault: () => void; }) => {

      // 监听粘贴事件,实现图片粘贴上传

      const items = (evt.clipboardData).items

      if (items[0].type.indexOf('image') !== -1) {

        const formData = new FormData();

        formData.append("file", items[0].getAsFile());

        // 自定义上传图片的方法

        // Source:0:教程,1:新闻

        switch (Source.value) {

          case 0:

            Upload(formData).then(res => {

              tinymce.execCommand(

                "mceReplaceContent",

                true,

                `<img src="${res.data.fileAddr}" >`

              );

            })

            break;

          case 1:

          formData.append("type", "3");

            Uploadnews(formData).then(res => {

              tinymce.execCommand(

                "mceReplaceContent",

                true,

                `<img src="${res.data.fileAddr}" >`

              );

            })

            break;

          default:

            break;

        }

        // 阻止默认事件,防止粘贴的图片进入富文本编辑器中

        evt.preventDefault();

      } else if (items[0].type.indexOf('video') !== -1) {

        const blob = items[0].getAsFile();

        const videoURL = URL.createObjectURL(blob);

        tinymce.execCommand(

          'mceInsertContent',

          true,

          `<video src="${videoURL}" controls></video>`

        );

        evt.preventDefault();

      }

    })

  },

  //开始前执行

  setup: function (editor: { ui: { registry: { addIcon: (arg0: string, arg1: string) => void; }; }; }) {

    editor.ui.registry.addIcon(

      "image",

      `<svg t="1664002320321" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4420" width="24" height="24"><path d="M125.9 185h772.2v653.9H125.9z" fill="#1F53CC" p-id="4421"></path><path d="M164.7 217.2h694.6v516.7H164.7z" fill="#FECD44" p-id="4422"></path><path d="M458.9 734l-8.6-43.8-101.5-102.8-135 146.6z" fill="#FC355D" p-id="4423"></path><path d="M306.9 348.7m-66.7 0a66.7 66.7 0 1 0 133.4 0 66.7 66.7 0 1 0-133.4 0Z" fill="#FFFFFF" p-id="4424"></path><path d="M384.6 734h474.7V608.8L687.8 400.1z" fill="#FC355D" p-id="4425"></path><path d="M422.5 662l-37.9 72 52.1-57.5z" fill="#BF2847" p-id="4426"></path><path d="M302.5 778.9h418.9v16.7H302.5z" fill="#00F0D4" p-id="4427"></path></svg>`

    );

  },

  // content_css: "/web-official-website-manage/skins/content/default/content.css", //以css文件方式自定义可编辑区域的css样式,css文件需自己创建并引入

  content_css: "/tinymce/skins/content/default/content.min.css", //以css文件方式自定义可编辑区域的css样式,css文件需自己创建并引入

  /**

   * 上传图片

   * @param blobInfo 图片信息

   * @param success 成功回调

   * @param failure 失败回调

   */

  images_upload_handler: (

    blobInfo: { blob: () => string | Blob },

    success: (arg0: string) => void,

    failure: (arg0: string) => void

  ) => {

    const formImg = new FormData(); //创建表单对象

    formImg.append("file", blobInfo.blob());//通过formImg对象封装图片二进制数据

    // Source:0:教程,1:新闻

    switch (Source.value) {

      case 0:

        Upload(formImg).then((res) => {

          const { status, message } = res

          if (200 == status) {

            success(res.data.fileAddr);

          } else {

            ElMessage({

              message: message,

              type: 'error',

            })

            failure('上传失败');

          }

        }).catch((err) => {

          failure(err);

        });

        break;

      case 1:

      formImg.append("type", "3");

        Uploadnews(formImg).then(res => {

          const { status, message } = res

          if (200 == status) {

            success(res.data.fileAddr);

          } else {

            ElMessage({

              message: message,

              type: 'error',

            })

            failure('上传失败');

          }

        }).catch((err) => {

          failure(err);

        });

        break;

      default:

        break;

    }

  },

  // 文件上传  需要的话添加

  // 上传视频

  file_picker_callback: (callback: (arg0: string) => void, value: Array<any>, meta: { filetype: string; }) => {

    if (meta.filetype == "media") {

      // callback('movie.mp4', { source2: 'alt.ogg', poster: 'image.jpg' });

      let input = document.createElement("input");

      input.setAttribute("type", "file");

      input.setAttribute("accept", ".mp3, .mp4");

      input.click();

      input.onchange = function (v: any) {

        let formVideo = new FormData();

        formVideo.append("file", v.target.files[0]);

        // Source:0:教程,1:新闻

        switch (Source.value) {

          case 0:

            Upload(formVideo).then((res) => {

              const { status, message } = res

              if (200 == status) {

                let rr = res.data.fileAddr;

                // callback 回调的作用是将所选择的视频的url显示在输入框中

                callback(rr);

              } else {

                ElMessage({

                  message: message,

                  type: 'error',

                })

              }

            });

            break;

            case 1:

            formVideo.append("type", "3");

            Uploadnews(formVideo).then((res) => {

              const { status, message } = res

              if (200 == status) {

                let rr = res.data.fileAddr;

                // callback 回调的作用是将所选择的视频的url显示在输入框中

                callback(rr);

              } else {

                ElMessage({

                  message: message,

                  type: 'error',

                })

              }

            });

              break;

          default:

            break;

        }

      };

    }

  },

});

//监听外部传递进来的的数据变化,如果有变化就赋值然后调用父组件的方法同样赋值

watch(() => props.value, () => {

  myValue.value = props.value;

  emits("getContent", myValue.value);

}

);

//监听富文本中的数据变化

watch(() => myValue.value, () => {

  emits("getContent", myValue.value);

}

);

//在onMounted中初始化编辑器

onMounted(() => { tinymce.init({}) });

</script>

<style>

.tox-tinymce {

  height: 50vh !important;

  width: 100%;

  touch-action: none !important;

}

</style>

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐