nuxt3中使用wangeditor5 踩坑笔记

462 阅读2分钟

1、服务端渲染时 总是会 报Element is not defined 在网上找一堆资料 说是要这样

<ClientOnly>
  <rich-editor @changeHtml="changeHtml" />
</ClientOnly>

rich-editor 是我封装的一个wangeditor5 的组件 但是仍然保错,后面去nuxt3看文档 在RichEditor.vue 文件名 改为RichEditor.client.vue 就没有再报错了

2、 以下是我的rich-editor 组件的代码


<template>
<div class="editor" v-loading.fullscreen.lock="fullLoading">
 <Toolbar
   style="border-bottom: 1px solid #ccc"
   :editor="editorRef"
   :defaultConfig="toolbarConfig"
   mode="default"
 />
 <Editor
   style="height: 300px; overflow-y: hidden"
   v-model="valueHtml"
   :defaultConfig="editorConfig"
   mode="default"
   @onCreated="handleCreated"
   @onChange="returnHtml"
 />
</div>
</template>

<script setup>
// import { DomEditor } from '@wangeditor/editor'
import { i18nChangeLanguage } from '@wangeditor/editor'
import '@wangeditor/editor/dist/css/style.css' // 引入 css

const { Editor, Toolbar } = await import('@wangeditor/editor-for-vue')
const lang = useCookie('lang').value
if (lang !== 'cn') {
i18nChangeLanguage('en')
}
console.log('lang', lang)
const emit = defineEmits(['changeHtml'])
// 上传显示loading
const fullLoading = ref(false)
// 获取token
const token = useCookie('token').value
// 工具栏
const toolBarComponent = ref(null)
// 编辑组件
const editorComponent = ref(null)
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef(null)
// 内容 HTML
const valueHtml = ref()
// 工具栏默认配置
const toolbarConfig = {
excludeKeys: [
 'headerSelect',
 'italic',
 'insertVideo',
 'deleteVideo',
 'uploadVideo',
 'video',
 'linkImage',
 'uploadImageLink',
 'insertLink',
 'viewImageLink',
 'group-more-style' // 排除菜单组,写菜单组 key 的值即可
]
}
const {
public: { api }
} = useRuntimeConfig()
// 输入框配置
const editorConfig = {
placeholder: lang === 'cn' ? '请输入内容...' : 'Please input',
readOnly: false,
showLinkImg: false,
excludeMenus: ['emoticon', 'video'],
MENU_CONF: {
 showLinkImg: false,
 // 上传图片
 uploadImage: {
   showLinkImg: false,
   fieldName: 'multipartFile',
   // 上传地址
   server: `${api}`,
   // 超时时间,默认为 10 秒
   timeout: 30 * 1000, // 5s
   // 将 meta 拼接到 url 参数中,默认 false
   metaWithUrl: false,
   // 自定义上传参数。
   meta: {
   },
   // 跨域是否传递 cookie ,默认为 false
   withCredentials: true,
   // 自定义增加 http  header
   headers: {
   },
   // 限制图片大小
   maxFileSize: 10 * 1024 * 1024, // 10M
   // 如果文件大小小于5kb,请插入base64格式
   base64LimitSize: 5 * 1024,
   // 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
   allowedFileTypes: ['image/*'],
   // 最多可上传几个文件,默认为 100
   maxNumberOfFiles: 1,
   // 上传之前触发
   onBeforeUpload(file) {
     // console.log("onBeforeUpload", file);
     // 显示loading
     fullLoading.value = true
     return file
   },
   // 上传进度的回调函数 progress 是 0-100 的数字
   onProgress(progress) {
     // console.log("onProgress", progress);
   },
   // 单个文件上传成功之后
   onSuccess(file, res) {
     // console.log("onSuccess", file, res);
   },
   // 单个文件上传失败
   onFailed(file, res) {
     // console.log("onFailed", file, res);
   },
   // 上传错误,或者触发 timeout 超时
   onError(file, err, res) {
     // console.error("onError", file, err, res);
   },
   // 自定义插入图片 res 服务器返回的数据 insertFn 方法
   customInsert(res, insertFn) {
     // 隐藏loading
     fullLoading.value = false
     insertFn(res.data.url)
   }
 },
 uploadVideo: false
 // 上传视频
 // uploadVideo: {
 //   fieldName: 'file',
 //   // 上传地址
 //   server: `${baseUrl}file/upload`,
 //   // 超时时间,默认为 30 秒
 //   timeout: 3000 * 1000, // 15 秒
 //   // 将 meta 拼接到 url 参数中,默认 false
 //   metaWithUrl: false,
 //   // 自定义上传参数。
 //   meta: {
 //     type: 3
 //   },
 //   // 跨域是否传递 cookie ,默认为 false
 //   withCredentials: true,
 //   // 自定义增加 http  header
 //   headers: {
 //     Authorization: `Bearer ${token}`
 //   },
 //   // 选择文件时的类型限制,默认为 ['video/*'] 。如不想限制,则设置为 []
 //   allowedFileTypes: ['video/*'],
 //   // 最多可上传几个文件,默认为 5
 //   maxNumberOfFiles: 1,
 //   // 单个文件的最大体积限制,默认为 10M
 //   maxFileSize: 1200 * 1024 * 1024, // 1200M
 //   // 上传之前触发
 //   onBeforeUpload(file) {
 //     // console.log("onBeforeUpload", file);
 //     // 显示loading
 //     fullLoading.value = true
 //     return file
 //   },
 //   // 上传进度的回调函数
 //   onProgress(progress) {
 //     // console.log("progress", progress);
 //   },
 //   // 单个文件上传成功之后
 //   onSuccess(file, res) {
 //     // console.log(`${file.name} 上传成功`, res);
 //   },
 //   // 单个文件上传失败
 //   onFailed(file, res) {
 //     // console.log(`${file.name} 上传失败`, res);
 //   },
 //   // 上传错误,或者触发 timeout 超时
 //   onError(file, err, res) {
 //     // console.log(`${file.name} 上传出错`, err, res);
 //   },
 //   // 自定义插入视频 res 服务器返回的数据 insertFn 方法
 //   customInsert(res, insertFn) {
 //     // 隐藏loading
 //     fullLoading.value = false
 //     insertFn(res.url)
 //   }
 // }
}
}

// console.log('editorRef', editorRef.value, 'editorConfig', editorConfig)
// 动态组件赋值
if (process.client) {
const { Editor, Toolbar } = await import('@wangeditor/editor-for-vue')
editorComponent.value = Editor
toolBarComponent.value = Toolbar
// console.log('toolBarComponent', toolBarComponent.value)
}
// 记录 editor 实例,重要!
const handleCreated = (editor) => {
editorRef.value = Object.seal(editor)
// console.log(editorRef.value, 'editorComponent.value=', editorComponent.value)
// const toolbar = DomEditor.getToolbar(editorRef.value)

// const curToolbarConfig = toolbar.getConfig()
// console.log(124, curToolbarConfig.toolbarKeys) // 当前菜单排序和分组
}

// 返回内容
const returnHtml = async () => {
emit('changeHtml', valueHtml.value)
}

// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
</script>

<style scoped lang="scss">
.editor {
border: 1px solid #ccc;
}
:deep(.w-e-text-placeholder) {
top: 10px;
}
</style>