vue3 + ts + tinymce 简单使用~

 

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

<script setup lang="ts">
//JS部分
//在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,不引入则不显示对应图标


//在TinyMce.vue中接着引入相关插件
import "tinymce/icons/default/icons";  //自定义icon  操作图标
import "tinymce/plugins/image"; // 插入上传图片插件
import "tinymce/plugins/media"; // 插入视频插件
import "tinymce/plugins/table"; // 插入表格插件
import "tinymce/plugins/lists"; // 列表插件
import "tinymce/plugins/link";
import "tinymce/plugins/wordcount"; // 字数统计插件
import "tinymce/plugins/code"; // 源码
import "tinymce/plugins/fullscreen"; //全屏
import "tinymce/plugins/searchreplace";
import "tinymce/plugins/preview";
import '@/utils/letterspacing'  // 间距插件
import { uploadImg } from "@/api/understand";
const emits = defineEmits(["getContent"]);
//这里我选择将数据定义在props里面,方便在不同的页面也可以配置出不同的编辑器,当然也可以直接在组件中直接定义
const props = defineProps({
  value: {
    type: String,
    default: () => {
      return "";
    },
  },
  baseUrl: {
    type: String,
    default: "",
  },
  disabled: {
    type: Boolean,
    default: false,
  },
  plugins: {
    type: [String, Array],
    default:
      "link lists image code table wordcount preview fullscreen media searchreplace letterspacing",
    // 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 bdmap indent2em autoresize formatpainter axupimgs',
  }, //必填
  toolbar: {
    type: [String, Array],
    default:
      "undo redo  | bold italic underline | fontselect fontsizeselect | forecolor backcolor | alignleft aligncenter alignright alignjustify | bdmap indent2em lineheight formatpainter axupimgs  image letterspacing| media| searchreplace  | bullist numlist | ",
    // default:
    //   ' 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 emoticons hr pagebreak insertdatetime print preview | fullscreen | bdmap indent2em lineheight formatpainter axupimgs',
  }, //必填
});
//用于接收外部传递进来的富文本
let win: any = window;
const editorRef = ref(null);
const myValue = ref(props.value);
const tinymceId = ref(
  "vue-tinymce-" + +new Date() + ((Math.random() * 1000).toFixed(0) + "")
);
//定义一个对象 init初始化
const init = reactive({
  selector: "#" + tinymceId.value, //富文本编辑器的id,
  // icons_url: '/项目名/icons/custom/icons.js', //自定义图标
  // icons: 'custom',
  language_url: "/项目名/langs/zh-Hans.js",
  language: "zh-Hans",
  skin_url: "/项目名/skins/ui/oxide",
  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",
  paste_merge_formats: true,
  nonbreaking_force_tab: false,
  paste_auto_cleanup_on_paste: false,
  file_picker_types: "media",
  setup: function (editor: any) {
    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文件需自己创建并引入
  //图片上传
  // images_upload_url: "/localFile/uploadFile",
  images_upload_handler: (blobInfo: any, progress: any, success: any) =>
    new Promise(async (resolve, reject) => {
      if (blobInfo.blob().size / 1024 / 1024 > 2) {
        reject({ message: "上传失败,图片大小请控制在 2M 以内", remove: true });
        return;
      } else {
        const ph = win.configs.IMG_URL;
        let params = new FormData();
        params.append("file", blobInfo.blob());
        const res: any = await uploadImg(params);
        if (res.code == 200) {
          progress(ph + res.data); //上传成功,在成功函数里填入图片路径
        } else {
          reject("HTTP Error: 上传失败" + res.data.code);
          return;
        }
      }
    }),

  // 文件上传  需要的话添加
  // 上传视频
  file_picker_callback: (callback: any, value: any, meta: any) => {
    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) {
        // console.log(v.target.files[0]); //视频文件信息   上传即可    规定视频格式大小需重写逻辑  获取视频时长
      }
    }
  }
});
//监听外部传递进来的的数据变化
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 {
  width: 100%;
  touch-action: none !important;
}
</style>

开箱即用 , 封装一个组件直接使用即可      

注释的配置可以放开看一下都是什么   我用的都是我需要的

上传视频的话参考官网  我上面也有写

遇到一个问题,有小可爱知道的话帮我一下.   需要限制字数  超过之后便无法输入需求       我有找到插件  但是他无法识别回车  个别特殊符号    感谢~

<think>好的,我现在需要帮助用户在Vue3和TypeScript项目中配置TinyMCE富文本编辑器。首先,我得回顾一下用户提供的引用内容,看看有哪些已有的信息和步骤可以借鉴。引用[1]提到了组件使用的方法,包括如何在模板中引入Tinymce组件,以及脚本部分的导入和声明。引用[3]和[4]提到了初始化配置,比如语言包和皮肤路径的设置,还有如何复制静态资源到public目录。引用[2]则是关于TypeScript相关的依赖安装,这可能涉及到ESLint配置的问题。 首先,我需要确定安装步骤。用户使用的是Vue3和TypeScript,所以需要安装TinyMCE的官方Vue组件,也就是@tinymce/tinymce-vue。同时,还需要安装核心包tinymce。此外,可能需要处理静态资源的路径问题,比如skins和语言文件,这些通常需要放在public目录下,如引用[4]所述。 接下来,关于组件封装。用户可能需要创建一个Tinymce组件,参考引用[1]中的示例,使用v-model来绑定内容。这里需要注意在Vue3中,组件的props和事件处理可能与Vue2有所不同,比如使用defineProps和emit。 然后是初始化配置,根据引用[3],需要设置language_url、language和skin_url。但根据引用[4],如果设置skin为false,可以不加载默认皮肤,手动引入样式。这可能涉及到如何正确配置路径,避免404错误。需要确保静态资源路径正确,比如将skins复制到public/tinymce目录下,并在初始化时指定skin_url为正确的路径。 另外,TypeScript的支持需要安装对应的类型定义,可能需要@types/tinymce,但根据TinyMCE的文档,可能已经内置了类型,所以需要确认。同时,引用[2]提到的@typescript-eslint/parser和eslint-plugin可能也需要安装,以避免ESLint报错。 最后,测试组件是否正常工作,包括内容绑定、工具栏配置和图片上传等功能。如果有问题,可能需要检查路径配置、依赖版本是否兼容Vue3和TypeScript,以及是否正确处理了静态资源。 可能遇到的问题包括:静态资源加载失败,语言包不生效,皮肤样式缺失,或者TypeScript类型错误。需要逐一排查,比如检查public目录结构,确认初始化参数是否正确,查看浏览器控制台是否有404错误,以及确保所有依赖包版本兼容。 此外,用户可能需要自定义插件和工具栏按钮,这需要在init配置中添加plugins和toolbar选项。如果需要使用高级功能,比如图片上传,可能需要集成后端API,并配置images_upload_handler。 总结步骤应该是:安装依赖、复制静态资源、创建Vue组件、配置初始化参数、处理TypeScript类型、集成到父组件中,并测试功能。同时,注意路径配置和版本兼容性问题,参考官方文档和用户提供的引用内容进行调整。</think>### 在 Vue3 + TypeScript 中集成 TinyMCE 的步骤 #### 1. 安装依赖 ```bash npm install tinymce @tinymce/tinymce-vue ``` 需要同时安装核心包和官方 Vue3 组件[^1] #### 2. 复制静态资源 ```bash cp -r node_modules/tinymce/skins public/tinymce/ cp node_modules/tinymce/themes/silver/theme.js public/tinymce/themes/ ``` 确保皮肤、主题等静态资源可访问[^4] #### 3. 创建组件 ```typescript // components/TinymceEditor.vue <script setup lang="ts"> import { ref, watch } from &#39;vue&#39; import tinymce from &#39;tinymce/tinymce&#39; import Editor from &#39;@tinymce/tinymce-vue&#39; const props = defineProps({ modelValue: String }) const emit = defineEmits([&#39;update:modelValue&#39;]) const editorRef = ref() const init = { skin_url: &#39;/tinymce/skins/ui/oxide&#39;, content_css: &#39;/tinymce/skins/content/default/content.css&#39;, language_url: &#39;/tinymce/langs/zh_CN.js&#39;, language: &#39;zh_CN&#39;, height: 500, plugins: &#39;link lists image code&#39;, toolbar: &#39;undo redo | styleselect | bold italic | alignleft aligncenter alignright | image&#39;, images_upload_handler: (blobInfo) => { return new Promise((resolve) => { // 实现图片上传逻辑 resolve(&#39;https://demo.img.url&#39;) }) } } </script> <template> <editor v-model="modelValue" :init="init" api-key="your-api-key" ref="editorRef" /> </template> ``` #### 4. 父组件调用 ```typescript // App.vue <script setup lang="ts"> import { ref } from &#39;vue&#39; import TinymceEditor from &#39;./components/TinymceEditor.vue&#39; const content = ref(&#39;<p>初始内容</p>&#39;) </script> <template> <TinymceEditor v-model="content" /> </template> ``` #### 5. 类型声明扩展 ```typescript // shims-tinymce.d.ts declare module &#39;tinymce/tinymce&#39; { const tinymce: any export default tinymce } ``` ### 关键配置说明 1. **路径配置**:确保皮肤文件路径正确,开发环境建议使用绝对路径`/tinymce/skins/...`[^3] 2. **中文支持**:需单独下载语言包并配置`language_url`和`language`参数[^3] 3. **按需加载**:通过`plugins`和`toolbar`配置需要的功能模块[^1] 4. **图片上传**:需实现`images_upload_handler`处理上传逻辑[^1] ### 常见问题解决 - **404资源错误**:检查public目录结构是否匹配配置路径[^4] - **类型报错**:安装`@types/tinymce`或添加类型声明[^2] - **样式丢失**:确保同时配置`skin_url`和`content_css`路径[^3]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值