index.vue 3.88 KB
<script setup lang="ts">
/**
 * 富文本
 * @module Tinymce
 */
import { ref, onMounted, onUnmounted } from "vue";
import { uploadPicSource } from "@/api/user/login";
import { toolbar, plugins } from "./settings/index";
import load from "./settings/load";
import { ElMessage, ElLoading } from "element-plus";

/**
 * @description 组件属性
 * @prop {string | null} moduleName 上传接口需要传的module值
 * @prop {string} modelValue 默认内容
 * @prop {boolean} readonly 禁用组件
 * @prop {string} id tinymce需要用到的id
 * @prop {number} height 组件高
 * @prop {number} width 组件宽
 */
interface Props {
  moduleName?: string;
  modelValue?: string;
  readonly?: boolean;
  id?: string;
  height?: number;
  width?: number;
}
const props = withDefaults(defineProps<Props>(), {
  moduleName: "test",
  modelValue: "",
  readonly: false,
  id: "vue-tinymce-" + +new Date() + ((Math.random() * 1000).toFixed(0) + ""),
  height: 404,
  width: 600,
});
const emit = defineEmits<{
  (e: "update:modelValue", val: string): void;
}>();
const cdnUrl = ref("http://gongongku.cnlive.com/base/tinymce.min.js");
let pageLoading = ref(null);
// 删除tinymce实例
const destroyTinymce = () => {
  const tinymce = window.tinymce.get(props.id);
  if (tinymce) {
    tinymce.destroy();
  }
};
// 初始化tinymce实例
const initTinymce = () => {
  tinymce.init({
    selector: `#${props.id}`, // 组件id
    height: props.height, // 自定义高
    width: props.width, // 自定义宽
    readonly: props.readonly, // 禁用开关
    language_url: "http://testweb.community.cnlive.com/languages/zh_CN.js", // 工具栏语言包,需要线上地址
    language: "zh_CN", // 中文设置
    plugins: plugins, // 插件
    toolbar: toolbar, // 工具栏配置
    theme_url: "http://gongongku.cnlive.com/base/theme.min.js", // 主题包
    powerpaste_word_import: "clean",
    code_dialog_height: 450,
    code_dialog_width: 1000,
    advlist_bullet_styles: "square",
    advlist_number_styles: "default",
    imagetools_cors_hosts: ["www.tinymce.com", "codepen.io"],
    default_link_target: "_blank",
    link_title: false,
    nonbreaking_force_tab: true,
    end_container_on_empty_block: true, // 空元素回车将其拆分
    images_upload_url: `${import.meta.env.VITE_BASE_URL}upload/uploadPicSource`, // 图片上传接口地址
    // 图片上传
    // 注意!!!!!!!
    // 以下图片上传逻辑经测试可用,目前该功能没开放。
    // 如需启用图片上传功能,移步 settings/index.ts,添加图片上传插件和菜单设置项。
    // 为了提升页面资源加载速度,富文本相关js、css依赖部署在公司自有cdn上,如需开启新插件,须联系运维同事上传新插件相关的依赖文件。
    images_upload_handler: function (blobInfo, succFun, failFun, progress) {
      const files = new window.File([blobInfo.blob()], "图片");
      const fd = new FormData();
      fd.append("multipartFile", files);
      uploadPicSource(
        {
          module: props.moduleName,
        },
        fd
      )
        .then((res) => {
          succFun(res.data.data);
        })
        .catch((error) => {
          failFun(error);
        });
    },
    // 内容改变监听
    init_instance_callback: (editor) => {
      pageLoading.close();
      // 赋默认值
      if (props.modelValue) {
        editor.setContent(props.modelValue);
      }
      // 监听
      editor.on("NodeChange Change KeyUp SetContent", () => {
        emit("update:modelValue", editor.getContent());
      });
    },
  });
};
onUnmounted(() => {
  destroyTinymce();
});
onMounted(() => {
  pageLoading = ElLoading.service({
    lock: true,
    text: "加载中,请稍候...",
  });
  load(cdnUrl.value, (err) => {
    if (err) {
      ElMessage.error(err.message);
      return;
    }
    initTinymce();
  });
});
</script>

<template>
  <textarea :id="props.id"></textarea>
</template>