vue3 wangeditor5 编辑器,使用方法

发布于:2025-08-17 ⋅ 阅读:(17) ⋅ 点赞:(0)

针对于vue3,如果你也是遇到了这些问题,直接使用下面的方法可以完美解决
问题1:Cannot read properties of null (reading 'emitsOptions') 

问题2:使用 wangeditor 时报错 Uncaught (in promise) TypeError: Cannot read properties of null (reading 'emitsOptions') 和 Uncaught (in promise) Error: Cannot find a descendant at path [2] in node: {"children":[{"type":"paragraph","children":

问题3:自定义按钮报错,这种是重复注册的问题
Duplicated key 'customInsertImage' in menu items





进入正题直接可以使用下面的代码和方法,
vue3

"@wangeditor/editor": "^5.1.23",

"@wangeditor/editor-for-vue": "^5.1.12",

首先自建个组件,wangEditorCom.vue,可直接使用下面代码

<template>
    <div>
      <!-- 工具栏 -->
      <Toolbar
        :editor="editorRef"
        :defaultConfig="toolbarConfig"
        :mode="'default'"
        style="border: 1px solid #ccc"
      />
      <!-- 编辑器 -->
      <Editor
        v-model="valueHtml"
        :defaultConfig="editorConfig"
        :mode="'default'"
        style="height: 300px; border: 1px solid #ccc; overflow-y: auto;"
        @onCreated="handleCreated"
      />
      <!--这个是弹窗,可以改成自己的  -->
   <!-- <ResourceSelector
      v-model="resourceStore.showResourceDialog"
      title="从资源库选择图片"
      :multiple="false"
      :accept="['image']"
      ref="resourceSelectorRef"
      @confirm="handleResourceConfirm"
    />-->
    </div>
  </template>
  
  <script setup>
  import '@wangeditor/editor/dist/css/style.css'
  import { ref, onBeforeUnmount } from 'vue'
  import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
    //弹窗
 //import ResourceSelector from '../components/ResourceSelector.vue
    //这里使用的是pinia,可以改成自己的
  import { useResourceStore } from '../stores/resource'
  const resourceStore = useResourceStore()
    // end 这里使用的是pinia
  const resourceSelectorRef = ref(null)

  // 2. 编辑器数据
  const valueHtml = ref('<p>这里是</p>')
  const editorRef = ref(null)
  
  const toolbarConfig = {
    insertKeys: {
        index: 0, // 插到最前
        keys: ['|', 'customInsertImage']
    }
  }
  const editorConfig = {
    placeholder: '请输入内容...',
    MENU_CONF: {
        insertImage: {
        onInsertedImage(imageNode) {
            console.log('已插入网络图片', imageNode)
        },
        checkImage(src) {
            return /^https?:\/\//.test(src) // 只允许 http/https
        }
        },
        uploadImage: {
        //图片服务地址,可以改成自己的
        server: '/api/upload',
        fieldName: 'file',
        maxFileSize: 5 * 1024 * 1024,
        allowedFileTypes: ['image/*']
        }
    }
    }

  
  const handleCreated = (editor) => {
        //一定要这样Object.seal加这个,不然会报错
    editorRef.value = Object.seal(editor)
  }
//这是弹窗的回调事件,resource,
  const handleResourceConfirm = (resource) => {
    if (resource) {
        //插入图片,全局的一个editor,是在main里面注册的
        if (window.editor) {
            window.editor.dangerouslyInsertHtml(`<img src="${resource[0].url}" alt="" />`)
        }
    }
  }
  

  onBeforeUnmount(() => {
//一定要销毁
    if (editorRef.value) {
      editorRef.value.destroy()
    }
  })
//这里是父页面给子页面赋值用的
  defineExpose({
    valueHtml,
  })
  </script>
  

然后建立一个editorMenus.js,自定义toobar按钮,不需要自定义按钮的话,直接跳到最后使用

// editorMenus.js
import { Boot } from '@wangeditor/editor'
import { useResourceStore } from '../stores/resource'
// 自定义菜单配置
export const InsertImgMenuConf = {
//key值要和,wangEditCom.vue里面的 toolbarConfig配置的对应
  key: 'customInsertImage',
  factory() {
    return {
      title: '插入图片',
      iconSvg:
        '<svg viewBox="0 0 1024 1024" width="16" height="16"><path d="M896 160H128c-35.2 0-64 28.8-64 64v576c0 35.2 28.8 64 64 64h768c35.2 0 64-28.8 64-64V224c0-35.2-28.8-64-64-64zM128 800V224h768v288l-160-160-256 256-128-128-224 224z"/></svg>',
      tag: 'button',
      getValue() { return '' },
      isActive() { return false },
      isDisabled(editor) { return editor.isDisabled() },
      exec(editor) {
        if (this.isDisabled(editor)) return
        //全局保存editor实例
        window.editor = editor
        //使用pinia打开资源库弹窗
        const resourceStore = useResourceStore()
        resourceStore.showResourceDialog = true
      },
    }
  },
}

// 全局只注册一次
if (!window.__hasRegisteredCustomInsertImage) {
  Boot.registerMenu(InsertImgMenuConf)
  window.__hasRegisteredCustomInsertImage = true
}

再然后pinia,

//这里是弹窗关闭和显示
import { defineStore } from 'pinia'

export const useResourceStore = defineStore('resource', {
  state: () => ({
    showResourceDialog: false
  })
})

再然后main.js
直接引用   自己放置的目录 import ../utils/editorMenus.js

最后使用如下,因为我的是在form表单里面,下面例子可以直接使用
 

<template>
    <el-button type="success" @click="handleAdd">新增动态</el-button>
<el-table :data="newsList" style="width: 100%">
<el-table-column  type="index" width="80" />
<el-table-column  prop="title" label="标题" width="80" />
<el-table-column label="操作" width="200">
          <template #default="scope">
            <el-button size="small" type="primary" @click="handleEdit(scope.row)">编辑</el-button>
          </template>
        </el-table-column>
      </el-table>

<!--destroy-on-close   这个一定要加,这是弹窗销毁事件,不加会报错的-->
<el-dialog
          v-model="dialogVisible"
      :title="dialogTitle"
      width="60%"
      @close="resetForm"
      destroy-on-close
    >
<el-form :model="form"  ref="formRef" label-width="100px">
        <el-form-item label="标题" prop="title">
          <el-input v-model="form.title" placeholder="请输入标题" />
        </el-form-item>
<el-form-item label="内容" prop="description">
    <wangEditcom ref="wangEditcomRef" />
 </el-form-item>
<template #footer>
        <span class="dialog-footer">
          <el-button @click="dialogVisible = false">取消</el-button>
          <el-button type="primary" @click="handleSubmit">确定</el-button>
        </span>
      </template>
</el-dialog>

</<template>
<script setup>
import { ref, reactive, onMounted, onBeforeUnmount, shallowRef, nextTick } from 'vue'
import wangEditcom from '../views/wangEditcom.vue'
const wangEditcomRef = ref(null)
const formRef= ref(null)
const newsList=ref({
[id:1,title:"晚上嗯"]
})
const form=ref(null)
// 对话框相关
const dialogVisible = ref(false)
const dialogTitle = ref('新增')


const handleAdd = () => {
  dialogTitle.value = '新增'

  dialogVisible.value = true
}
const handleEdit = (row) => {
  dialogTitle.value = '编辑'
  Object.assign(form, row)
  dialogVisible.value = true 
  nextTick(() => {
  if (wangEditcomRef.value) {
      wangEditcomRef.value.valueHtml = row.description || '<p>请输入内容...</p>'
    }
  })
}
const handleSubmit=()=>{
newsList.push(...form.value)
}
</script>


网站公告

今日签到

点亮在社区的每一天
去签到