tinymce リッチテキストエディターの使用経験

この記事は次の機能に分かれています。

  1. tinymce 初期化のためのいくつかの設定
  2. ツールバーとテキストの間に特定のタイトルと概要を挿入します。
  3. ツールバーのボタンをカスタマイズして特定の機能を実現する
  4. 写真アップロード時のネットワークアップロード機能を削除
  5. 貼り付けた画像はデフォルトでアップロードされます
  6. 写真に透かし機能を追加する
  7. ワンクリック組版を実現

    1. tinymce 初期化のいくつかの構成

          1. まず、次の 2 つのプラグインを導入する必要があります

        cnpm install @tinymce/tinymce-vue --save バージョン: 2.0.0

        cnpm install tinymce --save バージョン: 5.0.3
        注: 以前使用したとき、tinymce 6 以降のバージョンには一部の組み込みプラグインが存在しないことがわかり、一部の機能が使用できなくなったので、バージョンはこちら

        2. プラグインをダウンロードした後、node_modules で tinymce フォルダーを見つけ、その中にある Skins フォルダーをコピーして、vue プロジェクトのパブリック フォルダーに配置する必要があります。

   

        3. プラグインを中国語に翻訳するには、言語パックもダウンロードする必要があります。ダウンロード アドレスは次のとおりです。

   公式サイトアドレス: https: //www.tiny.cloud/get-tiny/ language-packages/     

    次のように、ダウンロードする zh-ch を見つけます。

        その後、ダウンロードした言語パックを vue のパブリック フォルダーに配置し、コードに導入します。ダウンロードした言語パックの名前は zh-Hans で、他の言語パックとは異なる場合がありますが、最終的な効果は同じです。同じように、ファイル名に従って直接インポートするだけです

        以下は、導入されたコード、いくつかのプラグイン、およびいくつかの基本構成です。

<template>
  <div class="editor_wraper">
    <editor :init="tinymceInit" v-model="content"> </editor>
  </div>
</template>

<script>
import tinymce from "tinymce";
import Editor from "@tinymce/tinymce-vue";
import "tinymce/themes/silver";
import {
  toolbar,
  fontsizeFormats,
  fontFormats,
  lineheightFormats,
} from "@/utils/tinymceConfig";
export default {
  data() {
    return {
      content: "",
      tinymceInit: {
        language_url: "tinymce/langs/zh-Hans.js", //引入语言包文件
        language: "zh-Hans", //语言类型
        skin_url: "tinymce/skins/ui/oxide", //皮肤:浅色
        quickbars_image_toolbar: "", // 选中媒体文件时的弹框
        // skin_url: '/tinymce/skins/ui/oxide-dark',//皮肤:暗色
        toolbar: toolbar, //工具栏配置,设为false则隐藏
        menubar: false, //菜单栏配置,设为false则隐藏,不配置则默认显示全部菜单,也可自定义配置--查看 http://tinymce.ax-z.cn/configure/editor-appearance.php --搜索“自定义菜单”
        height: 500, // 富文本高度
        model: 'dom', // 是否可拉伸富文本框
        fontsize_formats: fontsizeFormats, //字体大小
        font_formats: fontFormats, //字体样式
        lineheight_formats: lineheightFormats, //行高配置,也可配置成"12px 14px 16px 20px"这种形式
        placeholder: "在这里输入文字",
        branding: false, //tiny技术支持信息是否显示
        resize: "both", //编辑器宽高是否可变,false-否,true-高可变,'both'-宽高均可,注意引号
        statusbar: false,  //最下方的元素路径和字数统计那一栏是否显示
        paste_data_images: true, //图片是否可粘贴
        elementpath: false, //元素路径是否显示
      },
    };
  },
  components: { Editor },
};
</script>

        上記で紹介した tinymceConfig ファイルの構成をいくつか示します。

/**
 * @description: 工具栏配置
 * @return {*}
 */
export const toolbar = `
  undo redo restoredraft |
  removeformat|
  formatselect |
  subscript superscript |
  bold italic underline strikethrough link anchor |
  numlist bullist blockquote |
  alignleft aligncenter alignright alignjustify|
  quicklink searchreplace image|
  forecolor backcolor |
  fontselect fontsizeselect|
  outdent indent lineheight|`

/**
 * @description: 字体大小设置
 * @return {*}
 */
export const fontsizeFormats = `12px 14px 16px 18px 20px 22px 24px 28px 32px 36px 48px 56px 72px`
/**
 * @description: 字体设置
 * @return {*}
 */
export const fontFormats = `微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;宋体=simsun,serif;仿宋体=FangSong,serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;`
/**
 * @description: 行高设置
 * @return {*}
 */
export const lineheightFormats = "0.5 0.8 1 1.2 1.5 1.75 2 2.5 3 4 5"

上記のコンテンツを構成すると、ページに次の効果が表示される場合があります

2. ツールバーとテキストの間に特定のタイトルと要約を挿入します。

        タイトルと要約を挿入する方法は、次のようにインライン モードを使用してそれを実現することです。上記のコードに基づいて変更しました。

       注: インライン モードを有効にした後、toolbar_persist を true に設定した場合でも、固定表示ではなくツールバーを表示するには tinymce をクリックする必要があります。前に npm アンインストールで削除したときに tinymce パッケージを削除しなかった可能性があります。 .node_modulesには多くのtinymceパッケージが存在します.このとき、node_modules全体を削除してから、再度インストールする必要があります.個人テストの落とし穴

<template>
  <div class="editor_wraper">
    <div id="mytoolbar"></div>
    <el-input
      class="titleStyle"
      v-model="defaultTitle"
      type="textarea"
      resize="none"
      :autosize="{ minRows: 1, maxRows: 3 }"
      placeholder="请在这里输入标题"
    ></el-input>
    <el-input
      class="abstractStyle"
      v-model="defaultAbstract"
      type="textarea"
      resize="none"
      :autosize="{ minRows: 1 }"
      placeholder="摘要(可选)"
    ></el-input>
    <editor :init="tinymceInit" v-model="content"> </editor>
  </div>
</template>

<script>
import tinymce from "tinymce";
import Editor from "@tinymce/tinymce-vue";
import "tinymce/themes/silver";
import {
  toolbar,
  fontsizeFormats,
  fontFormats,
  lineheightFormats,
} from "@/utils/tinymceConfig";
import "tinymce/icons/default"; // 方式工具栏的icon乱码
export default {
  data() {
    return {
      defaultTitle: "",
      defaultAbstract: "",
      content: "",
      tinymceInit: {
        language_url: "tinymce/langs/zh-Hans.js", //引入语言包文件
        language: "zh-Hans", //语言类型
        skin_url: "tinymce/skins/ui/oxide", //皮肤:浅色
        quickbars_image_toolbar: "", // 选中媒体文件时的弹框
        // skin_url: '/tinymce/skins/ui/oxide-dark',//皮肤:暗色
        toolbar: toolbar, //工具栏配置,设为false则隐藏
        menubar: false, //菜单栏配置,设为false则隐藏,不配置则默认显示全部菜单,也可自定义配置--查看 http://tinymce.ax-z.cn/configure/editor-appearance.php --搜索“自定义菜单”
        height: 500, // 富文本高度
        model: 'dom', // 是否可拉伸富文本框
        fontsize_formats: fontsizeFormats, //字体大小
        font_formats: fontFormats, //字体样式
        lineheight_formats: lineheightFormats, //行高配置,也可配置成"12px 14px 16px 20px"这种形式
        placeholder: "在这里输入文字",
        branding: false, //tiny技术支持信息是否显示
        resize: "both", //编辑器宽高是否可变,false-否,true-高可变,'both'-宽高均可,注意引号
        statusbar: false,  //最下方的元素路径和字数统计那一栏是否显示
        paste_data_images: true, //图片是否可粘贴
        elementpath: false, //元素路径是否显示

        inline: true, //使用内联模式
        toolbar_persist: true, // 在内联模式中,工具栏是否自动显示和隐藏
        fixed_toolbar_container: "#mytoolbar", // 将工具栏显示在一个固定的html元素上
      },
    };
  },
  components: { Editor },
};
</script>

<style lang="scss">
.editor_wraper {
  width: 60%;
  height: 80vh;
  margin: 0 auto;
}
.mce-content-body{
  height: 100%;
  outline: none;
}
.titleStyle .el-textarea__inner {
  width: 100%;
  height: 56px;
  font-size: 24px;
  font-weight: 500;
  margin-top: 10px;
  padding-left: 0;
  border: none;
}
.abstractStyle .el-textarea__inner {
  font-size: 22px;
  font-weight: 500;
  color: #bbb;
  margin-top: 5px;
  padding-left: 0;
  border: none;
}
</style>

 マッチング後、以下の効果が表示されます

 

3. ツールバーのボタンをカスタマイズして特定の機能を実現する

        カスタム ボタンを作成するには、次の 3 つの手順があります。

        1. tinymceConfig ファイルにメソッドを作成します

/**
 * @description: 自定义按钮
 * @param {*} editor
 * @return {*}
 */
export const customBtn = (editor) => {
  // 参数一:自定义名称,需要放置在工具栏中
  editor.ui.registry.addButton("customBtn", {
    icon: "brightness", // 显示的图标
    tooltip: '自定义按钮', // 提示文字
    onAction: function () {
      console.log("点击了自定义按钮")
    }
  })
}

        2. tinymce 設定で参照します。

        3. addButton で宣言したカスタム名をツールバーに配置します。

      

        最後に作成したボタンが表示されます。

        注: アイコンを変更したい場合は、公式 Web サイトに記載されているいくつかのアイコン アドレスを参照できます
        : https://www.tiny.cloud/docs/tinymce/6/editor-icon-identifiers/         

 4.写真アップロード時のネットワークアップロード機能を削除する

        いわゆる削除とは、画像アップロードを自分でカスタマイズし直すという意味です tinymce では画像アップロードを使用していません カスタムボタンは上記の方法で作成できます ここでは画像アップロード方法のみ書きます

/**
 * @description: 自定义图片上传
 * @param {*} editor
 * @return {*}
 */
export const imageUpload = (editor) => {
  editor.ui.registry.addButton("imageUpload", {
    icon: "image",
    tooltip: '上传图片',
    onAction: function () {
      var input = document.createElement("input");
      input.setAttribute("type", "file");
      input.setAttribute("accept", "image/*");
      input.onchange = function () {
        var file = this.files[0];
        if (file.size / 1024 / 1024 > 20) {
          failure("上传失败,图片大小请控制在 20M 以内");
        } else {
          let formData = new FormData();
          formData.append("picture", file);
          formData.append("action", "add");
          // 上传后的逻辑
          uploadImage({ formData }).then((res) => {
            var reader = new FileReader();
            reader.onload = function () {
              var id = "blobid" + new Date().getTime();
              var blobCache = tinymce.activeEditor.editorUpload.blobCache;
              var base64 = reader.result.split(",")[1];
              var blobInfo = blobCache.create(id, file, base64);
              blobCache.add(blobInfo);
              // 将图片插入到文本中
              editor.insertContent(`<img src="${blobInfo.blobUri()}"/>`);
            };
            reader.readAsDataURL(file);
          }).catch(() => {
            failure("上传出错,服务器开小差了");
          });
        }
      };
      // 触发上传
      input.click();
    },
  });
}

5. 貼り付けた画像はデフォルトでアップロードできます

        貼り付けとコピー機能を使用するには、ページに直接貼り付けプラグインを導入する必要があります

         import "tinymce/plugins/paste"; インポートして、設定用の設定にプラグインを追加するだけです

 次に、構成内のpaste_postprocessを通じてコピーされたコンテンツを監視し、貼り付けたロジックをpaste_postprocessに書き込みます。

<script>
import tinymce from "tinymce";
import Editor from "@tinymce/tinymce-vue";
import "tinymce/themes/silver";
import {
  toolbar,
  fontsizeFormats,
  fontFormats,
  lineheightFormats,
  customBtn,
  imageUpload,
} from "@/utils/tinymceConfig";
import "tinymce/plugins/paste"; // 复制粘贴的插件
import "tinymce/icons/default"; // 方式工具栏的icon乱码
export default {
  data() {
    return {
      defaultTitle: "",
      defaultAbstract: "",
      content: "",
      tinymceInit: {
        language_url: "tinymce/langs/zh-Hans.js", //引入语言包文件
        language: "zh-Hans", //语言类型
        skin_url: "tinymce/skins/ui/oxide", //皮肤:浅色
        quickbars_image_toolbar: "", // 选中媒体文件时的弹框
        plugins: "paste", //插件配置
        // skin_url: '/tinymce/skins/ui/oxide-dark',//皮肤:暗色
        toolbar: toolbar, //工具栏配置,设为false则隐藏
        menubar: false, //菜单栏配置,设为false则隐藏,不配置则默认显示全部菜单,也可自定义配置--查看 http://tinymce.ax-z.cn/configure/editor-appearance.php --搜索“自定义菜单”
        height: 500, // 富文本高度
        model: "dom", // 是否可拉伸富文本框
        fontsize_formats: fontsizeFormats, //字体大小
        font_formats: fontFormats, //字体样式
        lineheight_formats: lineheightFormats, //行高配置,也可配置成"12px 14px 16px 20px"这种形式
        placeholder: "在这里输入文字",
        branding: false, //tiny技术支持信息是否显示
        resize: "both", //编辑器宽高是否可变,false-否,true-高可变,'both'-宽高均可,注意引号
        statusbar: false, //最下方的元素路径和字数统计那一栏是否显示
        paste_data_images: true, //图片是否可粘贴
        elementpath: false, //元素路径是否显示

        inline: true, // 使用内联模式
        toolbar_persist: true, // 在内联模式中,工具栏是否自动显示和隐藏
        fixed_toolbar_container: "#mytoolbar", // 将工具栏显示在一个固定的html元素上
        // 粘贴内容时的回调
        paste_postprocess: (plugin, args) => {
          this.$nextTick(() => {
            // 获取当前编辑器标签
            const doc = tinymce.activeEditor.getBody();
            // 循序所有标签
            doc.childNodes.forEach(async (item) => {
              const tag = item.firstChild; // 获取当前标签下的第一个标签
              // 判断当前标签或当前标签下面的第一个标签是否是一个img
              if (item.localName === "img" || tag.localName === "img") {
                const src =
                  item.localName === "img"
                    ? item.getAttribute("src")
                    : tag.getAttribute("src");
                // 如果是则拿到当前标签的图片地址,传递给后端,进行保存
                const { data } = await uploadImgByUrl({ url: src });
                // 拼接后端保存后的图片地址
                const url = process.env.VUE_APP_BASE_API + data;
                // 然后将新地址重新赋值给img标签
                item.localName === "img"
                  ? item.setAttribute("src", url)
                  : tag.setAttribute("src", url);
              }
            });
          });
        },
        // 自定义工具栏配置
        setup: function (editor) {
          // 自定义图片上传功能
          customBtn(editor);
          // 图片上传
          imageUpload(editor);
        },
      },
    };
  },
  components: { Editor },
};
</script>

6. 画像に透かし機能を追加する

        また、ウォーターマークを追加する機能を実現するためのカスタム ボタンも作成しました。ここでのウォーターマークの追加とは、記事内のすべての画像にウォーターマークを追加することです。個別のニーズがある場合は、自分で変更できます。上記を表示するにはカスタム ボタンを作成します道

        画像の数が多すぎる場合、ウォーターマークを追加するのに少し時間がかかる場合があります。ローディング状態を追加することをお勧めします

/**
 * @description: 添加图片水印
 * @param {*} editor
 * @return {*}
 */
export const imageWatermark = (editor) => {
  editor.ui.registry.addButton("imageWatermark", {
    icon: "edit-image",
    tooltip: '添加图片水印',
    onAction: function () {
      const doc = tinymce.activeEditor.getBody()
      doc.childNodes.forEach(item => {
        const tag = item.firstChild
        // 单独修改img标签
        if (item.localName === "img" || tag.localName === "img") {
          editImages(tag)
        }
      })
    }
  })
}

/**
 * @description: 给图片添加水印
 * @param {*} child
 * @return {*}
 */
const editImages = (child) => {
  const src = child.getAttribute("src")
  var image = new Image();
  image.crossOrigin = 'anonymous';
  image.src = src;
  image.onload = async () => {
    // 创建canvas,并将创建的img绘制成canvas
    const canvas = document.createElement('canvas')
    canvas.width = child.width
    canvas.height = child.height
    const ctx = canvas.getContext('2d')
    ctx.drawImage(image, 0, 0)
    ctx.textAlign = "left"
    ctx.textBaseline = "top"
    ctx.font = "18px Microsoft Yahei"
    ctx.fillStyle = "rgba(255, 255, 255, 0.5)"
    ctx.rotate((Math.PI / 180) * 15)
    for (let i = 0; i < child.height / 120; i++) {
      for (let j = 0; j < child.width / 50; j++) {
        ctx.fillText("这是水印", i * 200, j * 100, child.width)
      }
    }
    const base64Url = canvas.toDataURL()
    setTimeout(() => {
      // 将添加好水印的图片重新插入到富文本中
      child.setAttribute("src", base64Url)
    }, 5000)
  }
}

7. ワンキー写植を実現

ワンクリック写植方式でもカスタムボタンを作成しますが、実装方法は基本的に同じで、domノードを取得した後、対応するノードラベルを判定し、対応するラベルに異なるスタイルを追加します。

/**
 * @description: 一键排版
 * @param {*} editor
 * @return {*}
 */
const objLabel = {
  "h1": "title",
  "h2": "title",
  "h3": "title",
  "p": "paragraph"
}
const objStyle = {
  title: {
    "font-family": "微软雅黑",
    "font-size": "22px"
  },
  paragraph: {
    "font-size": "16px",
    "color": "red"
  }
}
export const oneClickLayout = (editor) => {
  // 参数一:自定义名称,需要放置在工具栏中
  editor.ui.registry.addButton("oneClickLayout", {
    icon: "orientation", // 显示的图标
    tooltip: '一键布局', // 提示文字
    onAction: function () {
      const doc = tinymce.activeEditor.getBody()
      doc.childNodes.forEach(item => {
        // 获取需要修改的标签
        const text = objLabel[item.localName]
        if (text) {
          // 找到对应样式循环进行配置
          for (let v in objStyle[text]) {
            item.style[v] = objStyle[text][v]
          }
        }
      })
    }
  })
}

この時点で、すべての関数が完了しました。関数全体のコードを以下に添付します。

<template>
  <div class="editor_wraper">
    <div id="mytoolbar"></div>
    <el-input
      class="titleStyle"
      v-model="defaultTitle"
      type="textarea"
      resize="none"
      :autosize="{ minRows: 1, maxRows: 3 }"
      placeholder="请在这里输入标题"
    ></el-input>
    <el-input
      class="abstractStyle"
      v-model="defaultAbstract"
      type="textarea"
      resize="none"
      :autosize="{ minRows: 1 }"
      placeholder="摘要(可选)"
    ></el-input>
    <editor :init="tinymceInit" v-model="content"> </editor>
  </div>
</template>

<script>
import tinymce from "tinymce";
import Editor from "@tinymce/tinymce-vue";
import "tinymce/themes/silver";
import {
  toolbar,
  fontsizeFormats,
  fontFormats,
  lineheightFormats,
  customBtn,
  imageUpload,
  oneClickLayout
} from "@/utils/tinymceConfig";
import "tinymce/plugins/paste";
import "tinymce/icons/default"; // 方式工具栏的icon乱码
export default {
  data() {
    return {
      defaultTitle: "",
      defaultAbstract: "",
      content: "",
      tinymceInit: {
        language_url: "tinymce/langs/zh-Hans.js", //引入语言包文件
        language: "zh-Hans", //语言类型
        skin_url: "tinymce/skins/ui/oxide", //皮肤:浅色
        quickbars_image_toolbar: "", // 选中媒体文件时的弹框
        plugins: "paste", //插件配置
        // skin_url: '/tinymce/skins/ui/oxide-dark',//皮肤:暗色
        toolbar: toolbar, //工具栏配置,设为false则隐藏
        menubar: false, //菜单栏配置,设为false则隐藏,不配置则默认显示全部菜单,也可自定义配置--查看 http://tinymce.ax-z.cn/configure/editor-appearance.php --搜索“自定义菜单”
        height: 500, // 富文本高度
        model: "dom", // 是否可拉伸富文本框
        fontsize_formats: fontsizeFormats, //字体大小
        font_formats: fontFormats, //字体样式
        lineheight_formats: lineheightFormats, //行高配置,也可配置成"12px 14px 16px 20px"这种形式
        placeholder: "在这里输入文字",
        branding: false, //tiny技术支持信息是否显示
        resize: "both", //编辑器宽高是否可变,false-否,true-高可变,'both'-宽高均可,注意引号
        statusbar: false, //最下方的元素路径和字数统计那一栏是否显示
        paste_data_images: true, //图片是否可粘贴
        elementpath: false, //元素路径是否显示

        inline: true, // 使用内联模式
        toolbar_persist: true, // 在内联模式中,工具栏是否自动显示和隐藏
        fixed_toolbar_container: "#mytoolbar", // 将工具栏显示在一个固定的html元素上
        // 粘贴内容时的回调
        paste_postprocess: (plugin, args) => {
          this.$nextTick(() => {
            // 获取当前编辑器标签
            const doc = tinymce.activeEditor.getBody();
            // 循序所有标签
            doc.childNodes.forEach(async (item) => {
              const tag = item.firstChild; // 获取当前标签下的第一个标签
              // 判断当前标签或当前标签下面的第一个标签是否是一个img
              if (item.localName === "img" || tag.localName === "img") {
                const src =
                  item.localName === "img"
                    ? item.getAttribute("src")
                    : tag.getAttribute("src");
                // 如果是则拿到当前标签的图片地址,传递给后端,进行保存
                const { data } = await uploadImgByUrl({ url: src });
                // 拼接后端保存后的图片地址
                const url = process.env.VUE_APP_BASE_API + data;
                // 然后将新地址重新赋值给img标签
                item.localName === "img"
                  ? item.setAttribute("src", url)
                  : tag.setAttribute("src", url);
              }
            });
          });
        },
        // 自定义工具栏配置
        setup: function (editor) {
          // 自定义图片上传功能
          customBtn(editor);
          // 图片上传
          imageUpload(editor);
          // 一键布局
          oneClickLayout(editor)
        },
      },
    };
  },
  components: { Editor },
};
</script>

<style lang="scss">
.editor_wraper {
  width: 60%;
  height: 80vh;
  margin: 0 auto;
}
.mce-content-body {
  height: 100%;
  outline: none;
}
.titleStyle .el-textarea__inner {
  width: 100%;
  height: 56px;
  font-size: 24px;
  font-weight: 500;
  margin-top: 10px;
  padding-left: 0;
  border: none;
}
.abstractStyle .el-textarea__inner {
  font-size: 22px;
  font-weight: 500;
  color: #bbb;
  margin-top: 5px;
  padding-left: 0;
  border: none;
}
</style>

tinymceConfig ファイルのコードは次のとおりです。


/**
 * @description: 工具栏配置
 * @return {*}
 */
export const toolbar = `
  customBtn imageUpload oneClickLayout|
  undo redo restoredraft |
  removeformat|
  formatselect |
  subscript superscript |
  bold italic underline strikethrough link anchor |
  numlist bullist blockquote |
  alignleft aligncenter alignright alignjustify|
  quicklink searchreplace image|
  forecolor backcolor |
  fontselect fontsizeselect|
  outdent indent lineheight|`

/**
 * @description: 字体大小设置
 * @return {*}
 */
export const fontsizeFormats = `12px 14px 16px 18px 20px 22px 24px 28px 32px 36px 48px 56px 72px`
/**
 * @description: 字体设置
 * @return {*}
 */
export const fontFormats = `微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;宋体=simsun,serif;仿宋体=FangSong,serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;`
/**
 * @description: 行高设置
 * @return {*}
 */
export const lineheightFormats = "0.5 0.8 1 1.2 1.5 1.75 2 2.5 3 4 5"

/**
 * @description: 自定义按钮
 * @param {*} editor
 * @return {*}
 */
export const customBtn = (editor) => {
  // 参数一:自定义名称,需要放置在工具栏中
  editor.ui.registry.addButton("customBtn", {
    icon: "brightness", // 显示的图标
    tooltip: '自定义按钮', // 提示文字
    onAction: function () {
      console.log("点击了自定义按钮")
    }
  })
}

/**
 * @description: 自定义图片上传
 * @param {*} editor
 * @return {*}
 */
export const imageUpload = (editor) => {
  editor.ui.registry.addButton("imageUpload", {
    icon: "image",
    tooltip: '上传图片',
    onAction: function () {
      var input = document.createElement("input");
      input.setAttribute("type", "file");
      input.setAttribute("accept", "image/*");
      input.onchange = function () {
        var file = this.files[0];
        if (file.size / 1024 / 1024 > 20) {
          failure("上传失败,图片大小请控制在 20M 以内");
        } else {
          let formData = new FormData();
          formData.append("picture", file);
          formData.append("action", "add");
          // 上传后的逻辑
          uploadImage({ formData }).then((res) => {
            var reader = new FileReader();
            reader.onload = function () {
              var id = "blobid" + new Date().getTime();
              var blobCache = tinymce.activeEditor.editorUpload.blobCache;
              var base64 = reader.result.split(",")[1];
              var blobInfo = blobCache.create(id, file, base64);
              blobCache.add(blobInfo);
              // 将图片插入到文本中
              editor.insertContent(`<img src="${blobInfo.blobUri()}"/>`);
            };
            reader.readAsDataURL(file);
          }).catch(() => {
            failure("上传出错,服务器开小差了");
          });
        }
      };
      // 触发上传
      input.click();
    },
  });
}
/**
 * @description: 一键排版
 * @param {*} editor
 * @return {*}
 */
const objLabel = {
  "h1": "title",
  "h2": "title",
  "h3": "title",
  "p": "paragraph"
}
const objStyle = {
  title: {
    "font-family": "微软雅黑",
    "font-size": "22px"
  },
  paragraph: {
    "font-size": "16px",
    "color": "red"
  }
}
export const oneClickLayout = (editor) => {
  // 参数一:自定义名称,需要放置在工具栏中
  editor.ui.registry.addButton("oneClickLayout", {
    icon: "orientation", // 显示的图标
    tooltip: '一键布局', // 提示文字
    onAction: function () {
      const doc = tinymce.activeEditor.getBody()
      doc.childNodes.forEach(item => {
        // 获取需要修改的标签
        const text = objLabel[item.localName]
        if (text) {
          // 找到对应样式循环进行配置
          for (let v in objStyle[text]) {
            item.style[v] = objStyle[text][v]
          }
        }
      })
    }
  })
}

/**
 * @description: 添加图片水印
 * @param {*} editor
 * @return {*}
 */
export const imageWatermark = (editor) => {
  editor.ui.registry.addButton("imageWatermark", {
    icon: "edit-image",
    tooltip: '添加图片水印',
    onAction: function () {
      const doc = tinymce.activeEditor.getBody()
      doc.childNodes.forEach(item => {
        const tag = item.firstChild
        // 单独修改img标签
        if (item.localName === "img" || tag.localName === "img") {
          editImages(tag)
        }
      })
    }
  })
}
/**
 * @description: 给图片添加水印
 * @param {*} child
 * @return {*}
 */
const editImages = (child) => {
  const src = child.getAttribute("src")
  var image = new Image();
  image.crossOrigin = 'anonymous';
  image.src = src;
  image.onload = async () => {
    // 创建canvas,并将创建的img绘制成canvas
    const canvas = document.createElement('canvas')
    canvas.width = child.width
    canvas.height = child.height
    const ctx = canvas.getContext('2d')
    ctx.drawImage(image, 0, 0)
    ctx.textAlign = "left"
    ctx.textBaseline = "top"
    ctx.font = "18px Microsoft Yahei"
    ctx.fillStyle = "rgba(255, 255, 255, 0.5)"
    ctx.rotate((Math.PI / 180) * 15)
    for (let i = 0; i < child.height / 120; i++) {
      for (let j = 0; j < child.width / 50; j++) {
        ctx.fillText("这是水印", i * 200, j * 100, child.width)
      }
    }
    const base64Url = canvas.toDataURL()
    setTimeout(() => {
      child.setAttribute("src", base64Url)
    }, 5000)
  }
}

これは tinymce を使用中に経験したことですが、これを実現するまでに長い時間がかかりました。

おすすめ

転載: blog.csdn.net/qq_39215166/article/details/130688837