前言

使用方式不通过api-key,而是直接使用,但是报错,因为Nuxt3默认为ssr模式,即服务端渲染,TinyMce-Vue无法兼容服务端渲染。

报错

[Vue Router warn]: uncaught error during route navigation:
ReferenceError: navigator is not defined
    at D:\project\cpa-read-nuxt3-h5\node_modules\tinymce\tinymce.js:961:23
    at Object.<anonymous> (D:\project\cpa-read-nuxt3-h5\node_modules\tinymce\tinymce.js:31513:3)
    at Module._compile (node:internal/modules/cjs/loader:1241:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1295:10)
    at Module.load (node:internal/modules/cjs/loader:1091:32)
    at Module._load (node:internal/modules/cjs/loader:938:12)
    at cjsLoader (node:internal/modules/esm/translators:284:17)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:234:7)
    at ModuleJob.run (node:internal/modules/esm/module_job:217:25)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

原因

服务端渲染无法访问游览器的navigator

解决方法

参考:stackoverflow的描述,使用nuxt3的ClientOnly加载tinymce组件

ClientOnly:https://nuxt.com.cn/docs/api/components/client-only

源码

关于源码之前的文章有介绍

CustomTinymce.vue

<template>
  <div style="width: 100%">
    <editor v-model="editorValue" :init="initOptions"></editor>
  </div>
</template>

<script setup>
import {onBeforeUnmount, onMounted, ref, toRefs, unref, watch} from "vue";
import 'tinymce/tinymce';
import Editor from '@tinymce/tinymce-vue';
import 'tinymce/models/dom';

// 外觀
import 'tinymce/skins/ui/oxide/skin.css';
import 'tinymce/themes/silver';

// Icon
import 'tinymce/icons/default';
// 語言包
import 'tinymce-i18n/langs6/zh-Hans.js';

// 引入插件
// 源代码
import 'tinymce/plugins/code'
import 'tinymce/plugins/image'

const emit = defineEmits(['update:modelValue']);


const props = defineProps({
  modelValue: {
    type: String,
    default: '',
  },
  plugins: {
    type: [String, Array],
    default: 'code image',
  },
  toolbar: {
    type: [String, Array],
    default: 'undo redo | styles | bold italic image cut copy paste forecolor removeformat code',
  },
});

const initOptions = ref({
  language: 'zh-Hans',
  height: 500,
  skin: false,
  menubar: false,
  content_css: false,
  plugins: props.plugins,
  toolbar: props.toolbar,
  // 禁止url自动转化处理
  convert_urls: false,
  toolbar_mode: 'sliding',
  ...setPasteOption(),
  ...setImageOption()
})


const { modelValue } = toRefs(props);
const editorValue = ref(modelValue.value);

watch(modelValue, (newValue) => {
  editorValue.value = newValue;
});

watch(editorValue, (newValue) => {
  emit('update:modelValue', newValue);
});

onMounted(() => {
  console.log('初始化tinymce')
})

/*
* 图片上传 配置项
* */
function setImageOption() {
  return {
    images_upload_handler: (blobInfo, progress) => new Promise(async (resolve, reject) => {
      // console.log(blobInfo.blobUri())
      const formData = new FormData();
      formData.append('file', blobInfo.blob(), blobInfo.filename());
      // console.log(formData)
      const res = {}
      console.log(res)
      if (res.code === 200 && res.data.url) {
        resolve(res.data.url)
      } else {
        reject({ message: '上传图片失败', remove: true })
      }
    })
  }
}

/*
*  复制粘贴插件 配置项
* https://www.tiny.cloud/docs/tinymce/6/copy-and-paste/
* */
function setPasteOption() {
  return {
    paste_preprocess: (editor, args) => {
      console.log(args.content);
    },
    // paste_remove_styles_if_webkit: false,
    /*
    * 此选项允许您指定在 WebKit 中粘贴时要保留的样式。WebKit 有一个怪癖,
    * 它将获取元素的所有计算 CSS 属性并将它们添加到编辑器中的 span 中。由于大多数用户不希望在整个文档中添加随机跨度,
    * 因此我们需要手动清理它,直到修复错误。此选项默认为'none'但可以设置为'all'或要保留的特定样式列表。
    * */
    paste_webkit_styles: 'color'
  }
}

</script>

<style lang="less" scoped>

</style>

<style>
.tox-tinymce-aux {
  z-index: 3000 !important;
}
</style>

tinymce.vue

使用组件

<script setup lang="ts">
const data = reactive({
  tinymce: ''
})
</script>

<template>
  <ClientOnly fallback-tag="span" fallback="加载评论中...">
    <custom-tinymce v-model="data.tinymce"/>
  </ClientOnly>
</template>

<style scoped lang="scss">
</style>

end

最后结果如图所示

还有一种方式是按照tinymce官网文档使用,申请apikey加载,尝试了存在加载中文js报错问题,未再继续研究。

最后修改:2023 年 11 月 16 日
如果觉得我的文章对你有用,奖励一杯咖啡吧!