Tinymce+Vue3 组件封装

1,725 阅读6分钟

下载资源

npm i tinymce tinymce/tinymce-vue

资源切换 tinymce-script-src

通过tinymce-vue远源码查看,可知加载tinymce是以script的形式加载的,默认加载的地址是cdn.tiny.cloud ,这应该是个外网地址,资源访比较慢。为了提高速度,将资源切换成本地的资源访问。

  • 下载npm i tinymce或者通过 www.tiny.cloud/get-tiny/se… 下载tinymce
  • 将tinymce复制到public中(public中的内容是不会编译的,是静态文件)
  • 在tinymce-vue中传入tinymce-script-src属性,值为/tinymce/tinymce.min.js。原理就是在public中的文件,可以直接通过/tinymce/tinymce.min.js访问到

image.png

但是下载tinymce是没有语言包的,你需要自己去下载然后复制到tinymce中,下载地址:www.tiny.cloud/get-tiny/la…

Attributes

参数说明类型可选值默认值
v-model.value文本内容,可以是标签内容String-''
id编辑器id,用于缓存内容时使用,相关内容看save插件,如果有多个编辑器确保id唯一string-''
upload上传回调,详见uploadFunction-null
init设置,详见initObject-详见init

upload

上传函数,接受四个参数

参数说明
file上传的文件
callback回调函数,接受一个字符串,表示上传文件的线上地址
value现在输入框中线上文件的地址
meta一个对象,{fieldname, filetype}, filetype 上传的类型,image:图片、file:链接、media:视频

v-model:value 这里比较简单,就不具体叙述了。但有一点是,可以不设置v-model,但是在使用保存按钮的时候,会将编辑器内容返回。所以内部并不是直接使用传过来的value,而是在内部定义一个变量,这个变量保持与value一致。

init

width

编辑器的宽

  • 类型:string/number
  • 默认:'100%'

height

编辑器的高

  • 类型:string/number
  • 默认:400

language

编辑器语言

  • 类型:string
  • 默认:'zh_CN'

plugins

编辑器插件,具体详见

  • 类型:string
  • 默认:'preview importcss searchreplace autolink save directionality code visualblocks visualchars fullscreen image link media template codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount help charmap quickbars emoticons'

tabbar

  • 类型:string
  • 默认:'redo bold italic strikethrough fontfamily fontsize blocks | alignleft aligncenter alignright alignjustify numlist bullist | removeformat pagebreak ullscreen preview save image media link codesample'

“|”分割线的作用,当设置过多内容,一行显示不下,会出现折叠的情况,但是所有的tabar被“|”分为几个部分,并不是每个tabar单独分开。可以想象为每个“|”将tabbar分割为多个inline-black,计算一行能否显示是以当前这个inline-black能是显示,不能整个inline-black都隐藏

const toolbar = "redo bold italic strikethrough fontfamily fontsize blocks | alignleft aligncenter alignright alignjustify numlist bullist | removeformat pagebreak ullscreen preview save image media link codesample ";

如上,比如最后一个inline-black显示不下,不是media link codesample等不显示,而是从removeformat之后的都不显示,即使加上removeformat能显示得下

menubar

菜单栏,设置false则不展示菜单栏详情见

  • 类型:boolean/string
  • 默认:'file edit view insert format tools table help'

image_advtab

上传图片的时候增加高级选项,可以调增样式

  • 类型:boolean
  • 默认:false

image.png

content_css

引入的额外样式,值为样式地址

  • 类型:string
  • 默认:''

content_style

  • 类型:string
  • 默认:''

样式内容,内容将作为style插入到样式head中

importcss_append

  • 类型:boolean
  • 默认:false

这个属性要结合content_css。在格式-格式中添加选项,选项为content_css定义的样式,只有 .类名{样式}  或  .类名.类名{样式}  或 标签.类名{样式} 才能插入到选项中

image.png

toolbar_mode

当tabbar一行显示不下的时候,点击显示更多按钮,其余的tabbar展示的模式。可选值为floating或者sliding,默认为floating

  • 类型:string
  • 取值:loating / sliding / scrolling / wrap
  • 默认:wrap

contextmenu

右键时显示的菜单

  • 类型:string
  • 默认:link image imagetools table spellchecker

中文文档

branding

是否显示tinymce的logo

  • 类型:boolean
  • 默认:false

toolbar_sticky

当编辑器超过一屏幕时,是否开启fixed布局

  • 类型:boolean
  • 默认:false

toolbar_sticky_offset

开启fixed布局后,top的值

  • 类型:number
  • 默认:0

media_url_resolver

此选项允许您指定一个函数,该函数将用于将 TinyMCE 的默认媒体嵌入逻辑替换为您自己的自定义逻辑。参数如下

  • data:{url:string} url是用户输入的地址
  • resolve:({html:string}) => void html表示插入页面的内容。如果html是空字符串,那么会自动以video的形式嵌入链接
  • reject:({msg:string}) => void msg报错信息
  media_url_resolver: function (data, resolve) {
    console.log("media_url_resolver", data, resolve);
    if (data.url.indexOf("bilibili") !== -1) {
      const embedHtml = `<iframe src="${data.url}"  width="600" height="600" ></iframe>`;
      resolve({ html: embedHtml });
    } else {
      resolve({ html: "" });
    }
  },

这里识别是bilibili地址,然后以iframe的形式嵌入。width和height也会自动显示在输入框中,并支持修改

image.png

file_picker_callback

自定义上传,对应的插件是link media image。当点击这三个按钮的时候,出现的弹窗里面,会出现上传的按钮,如果不定义这个属性,就不会出现上传按钮。

image.png

参数如下:

  • callback 回调函数
  • value 当前的值
  • meta
    • fieldname 资源对应的属性
    • filetype 那种类型上传 link:file、image:image、media:media 以上是meta公共的属性,对于不同的资源还有不同的属性

link的meta:

  • text 显示的文本
  • title 链接的title

image的meta

  • alt 图片属性的alt

media的meta

  • width和height 视频的width和height

save_onsavecallback

保存按钮的回调函数,参数editor实例,对应的plugin是save

image.png

save_enablewhendirty

默认保存按钮只有在内容变化的时候才能点击,点击时候也不能在点击了。设置这个属性为false,可以随时点击保存按钮

  • 类型:Boolean
  • 默认:true

autosave_interval

自动保存的间隔时间,

  • 类型:String
  • 默认:'30s'

autosave_prefix

自动保存,会存放到localstorage中,此参数设置localstorage对应key的前缀

  • 类型:String
  • 默认:"tinymce-autosave-{path}{query}-{id}-"

autosave_restore_when_empty

当内容为空的时候,是否自动从localstorage中获取

  • 类型:Boolean
  • 默认:false

autosave_retention

localstorage的有效时间。

  • 类型:String
  • 默认:'20m'

image_list

上传图片的时候提供可选择的图片。title是下拉的label,value是选中的图片的地址

  • 类型:array / url / function
  • 默认:[]
image_list: [ 
    { title: 'Cat', value: 'cat.png' }, 
    { title: 'Dog', value: 'dog.jpg' } 
]

image.png

image_class_list

上传图片的时候提供可选择的class

  • 类型:array
  • 默认:[]

image.png

image_caption

上传图片的会增加一个是否显示标题的功能,选择之后,默认之后默认为caption,用户可以点击修改 image.png

image.png

skin

皮肤

  • 类型:string
  • 默认:''

skin_url

引入的外部皮肤地址

  • 类型:string
  • 默认:''

init的file_picker_callback会覆盖upload函数

这里只是介绍了默认属性,还有很多配置参数,大家可以自行查看tinymce中文文档

Events

事件名称说明回调参数
init组件初始化完成e:事件 editor:编辑器实例
save保存编辑器内容

完整代码

<template>
  <Editor
    tinymce-script-src="/tinymce/tinymce.min.js"
    :model-value="valueThis"
    :init="initThis"
    @update:model-value="change"
  />
</template>

<script lang="ts">
import { toRefs, watch, ref } from "vue";
import Editor from "@tinymce/tinymce-vue";
import getInit, { filePickerCallback } from "./init";
import plugins from "./plugins";

export default {
  name: "JhEditor",
  components: {
    Editor,
  },
  props: {
    value: {
      type: String,
      default: "",
    },
    id: {
      type: String,
      default: "",
    },
    upload: {
      type: Function,
      default: null,
    },
    init: {
      type: Object,
      default: () => {
        return {};
      },
    },
  },
  emits: ["update:value"],
  setup(props, { attrs, emit }) {
    console.log("attr", attrs);
    const { upload, init, value, id } = toRefs(props);
    if (upload.value) {
      init.value.file_picker_callback = filePickerCallback(upload.value);
    }

    if (id.value) {
      init.value.plugin = "autosave";
      init.value.autosave_prefix = id.value;
      init.value.plugins = plugins + " autosave";
    }

    const valueThis = ref("");
    watch(
      value,
      (val) => {
        valueThis.value = val;
      },
      {
        immediate: true,
      }
    );

    const save = (a, b) => {
      emit("save", valueThis.value);
    };
    init.value.save_onsavecallback = save;
    const initThis = getInit(init.value);
    const change = (val) => {
      valueThis.value = val;
      emit("update:value", val);
    };
    return {
      change,
      initThis,
      valueThis,
    };
  },
};
</script>

使用代码

<template>
  <div class="show_block">
    content:{{ content }}
    <a-button type="primary" @click="aaa">修改内容</a-button>
  </div>
  <JhEditor
    id="id"
    v-model:value="content"
    :upload="upload"
    @save="save"
    @init="init"
  />
</template>

<script lang="ts" setup>
import { ref } from "vue";
const content = ref("111");

const upload = (file, callback, value, meta) => {
  console.log(meta);
  console.log("file", file);
  setTimeout(() => {
    callback("https://www.baidu.com/img/bd_logo1.png");
  }, 1000);
};

const save = (val) => {
  console.log("保存", val);
};

const init = (e, editor) => {
  console.log("e", e);
  console.log("editor", editor);
};

const aaa = () => {
  content.value = "修改之后的内容";
};
</script>

<style type="text/css">
.show_block {
  margin-bottom: 10px;
}
</style>