cycleccc / wangEditor-next

wangEditor-next —— 基于 slate.js 的富文本编辑器。wangEditor-next —— rich text editor, based on slate.js.
https://cycleccc.github.io/docs/
MIT License
115 stars 17 forks source link

【BUG】vue2多个编辑器复制粘贴报错 #69

Closed JaneYork closed 2 months ago

JaneYork commented 3 months ago

bug 描述

【BUG】vue2多个编辑器复制粘贴报错

你预期的样子是?

控制台报错

系统和浏览器及版本号

wangEditor-next 版本

"@wangeditor-next/editor": "^5.3.13",
"@wangeditor-next/editor-for-vue2": "^1.0.2",

demo 能否复现该 bug ?

在线 demo

JaneYork commented 3 months ago
<template>
  <div>
<!--    <div>-->
<!--      <button @click="printEditorHtml">print html</button>-->
<!--      <button @click="getEditorText">print text</button>-->
<!--    </div>-->
    <div style="border: 1px solid #ccc; margin-top: 10px">
      <!-- 工具栏 -->
      <Toolbar style="border-bottom: 1px solid #ccc" :editor="editor" :defaultConfig="toolbarConfig" />
      <!-- 编辑器 -->
      <Editor
        style="height: 400px; overflow-y: hidden"
        :defaultConfig="editorConfig"
        v-model="html"
        @onChange="onChange"
        @onCreated="onCreated"
      />
    </div>
<!--    <div style="margin-top: 10px">-->
<!--      <textarea v-model="html" readonly style="width: 100%; height: 200px; outline: none"></textarea>-->
<!--    </div>-->
  </div>
</template>

<script>
import { Editor, Toolbar } from '@wangeditor-next/editor-for-vue2'
import storage from 'store'
import { ACCESS_TOKEN } from '@/store/mutation-types'

export default {
  name: 'WangNextEditor',
  props: {
    value: {
      type: String,
      default: '',
    },
  },
  components: { Editor, Toolbar },
  data() {
    return {
      uploadImgUrl: process.env.VUE_APP_BASE_API + '/common/upload',
      headers: {
        Authorization: 'Bearer ' + storage.get(ACCESS_TOKEN),
        Accept: 'application/json, text/plain, */*'
      },
      editor: null,
      html: '',
      toolbarConfig: {
        // toolbarKeys: [ /* 显示哪些菜单,如何排序、分组 */ ],
        // excludeKeys: [ /* 隐藏哪些菜单 */ ],
      },
      editorConfig: {
        placeholder: '请输入内容...',
        // autoFocus: false,

        // 所有的菜单配置,都要在 MENU_CONF 属性下
        MENU_CONF: {},
      },
    }
  },
  created() {
    this.editorConfig.MENU_CONF['uploadImage'] = {
      server: this.uploadImgUrl,
      // server: '/api/upload-img-10s', // test timeout
      // server: '/api/upload-img-failed', // test failed
      // server: '/api/xxx', // test 404

      timeout: 5 * 1000, // 5s

      fieldName: 'file',
      meta: { token: 'xxx', a: 100 },
      metaWithUrl: true, // join params to url
      headers: this.headers,

      maxFileSize: 10 * 1024 * 1024, // 10M

      base64LimitSize: 1 * 1024, // insert base64 format, if file's size less than 5kb
      // maxNumberOfSize: 1,

      onBeforeUpload(file) {
        console.log('onBeforeUpload', file)

        return file // will upload this file
        // return false // prevent upload
      },
      onProgress(progress) {
        console.log('onProgress', progress)
      },
      onSuccess(file, res) {
        console.log('onSuccess', file, res)
      },
      onFailed(file, res) {
        alert(res.message)
        console.log('onFailed', file, res)
      },
      onError(file, err, res) {
        alert(err.message)
        console.error('onError', file, err, res)
      },

      customInsert(res, insertFn) {
        console.log('customInsert', res)
        const imgInfo = res.data
        const { url, alt, href } = imgInfo
        if (!url) throw new Error(`Image url is empty`)

        console.log('Your image url ', url)
        insertFn(url, 'pusdn', '')
      },

      // customUpload(file, insertFn) {
      //   console.log('customUpload', file)

      //   return new Promise((resolve) => {
      //     // Simulate async insert image
      //     setTimeout(() => {
      //       const src = `https://www.baidu.com/img/flexible/logo/pc/result@2.png?r=${Math.random()}`
      //       insertFn(src, 'baidu logo', src)
      //       resolve('ok')
      //     }, 500)
      //   })
      // },

      // customBrowseAndUpload(insertFn) {
      //   alert('Custom browse and upload')

      //   // Simulate async insert image
      //   setTimeout(() => {
      //     const src = 'https://www.baidu.com/img/flexible/logo/pc/result@2.png'
      //     insertFn(src, 'baidu logo', src) // insert a image
      //   }, 500)
      // },
    }
  },
  methods: {
    onCreated(editor) {
      this.editor = Object.seal(editor) // 【注意】一定要用 Object.seal() 否则会报错
      console.log(this.editor.id)
    },
    onChange(editor) {
      console.log('onChange', editor.getHtml()) // onChange 时获取编辑器最新内容
      this.$emit('input', editor.getHtml())
    },
    getEditorText() {
      const editor = this.editor
      if (editor == null) return

      console.log(editor.getText()) // 执行 editor API
    },
    printEditorHtml() {
      const editor = this.editor
      if (editor == null) return

      console.log(editor.getHtml()) // 执行 editor API
    },
  },
  watch: {
    value: {
      handler (val) {
        // if (val !== this.currentValue) {
        //   this.currentValue = val === null ? '' : val
        //   if (this.editor) {
        //     this.editor.txt.html(this.currentValue)
        //   }
        // }
        // if (this.editor) {
        //   // 假设你有一个方法来设置编辑器的HTML
        //   this.editor.setContent(this.html = newVal)
        // }
        this.html = val
      },
      immediate: true
    }
  },

  mounted() {
    // 模拟 ajax 请求,异步渲染编辑器
    // setTimeout(() => {
    //   this.html = '<p>Ajax 异步设置内容 HTML</p>'
    // }, 1500)
  },
  beforeDestroy() {
    const editor = this.editor
    if (editor == null) return
    editor.destroy() // 组件销毁时,及时销毁 editor ,重要!!!
  },
}
</script>

<style src="@wangeditor-next/editor/dist/css/style.css"></style>

使用组件

 <WangNextEditor v-model="form.details" />
 <WangNextEditor v-model="form.details2" />
 <WangNextEditor v-model="form.details3" />

报错截图

QQ_1722521378784

JaneYork commented 3 months ago

@cycleccc

cycleccc commented 3 months ago

多编辑器都有的问题,现在手头上正在处理这个,要把多个编辑器创的监听隔离开,需要花点时间处理,可以的话先尝试在项目层面隔离多个编辑器之间的影响。

JaneYork commented 3 months ago

多编辑器都有的问题,现在手头上正在处理这个,要把多个编辑器创的监听隔离开,需要花点时间处理,可以的话先尝试在项目层面隔离多个编辑器之间的影响。

具体如何操作呢,有相关文档吗

cycleccc commented 3 months ago

尽量隔离开或是多编辑器切换时销毁重建,这属于 hack 手段,你可以都试下🌚

JaneYork commented 3 months ago

尽量隔离开或是多编辑器切换时销毁重建,这属于 hack 手段,你可以都试下🌚

一个页面多个编辑器,上面一个,下面一个,上面最大化后,样式错误。 演示站点也有这个问题 QQ_1722605638266 QQ_1722605881574

@cycleccc

cycleccc commented 3 months ago

我更新了 5.3.14 版本 你可以试试现在复制粘贴还有没有问题。

cycleccc commented 3 months ago

样式错误这个可以另提一个 issue,这应该和过往的改动无关,估计是 wangeditor 历史遗留问题,我们在另一个 issue 可以讨论下。

JaneYork commented 2 months ago

我更新了 5.3.14 版本 你可以试试现在复制粘贴还有没有问题。

"@wangeditor-next/editor": "^5.3.14",
"@wangeditor-next/editor-for-vue2": "^1.0.2",这个需要更新吗

😋node至少18 error nanoid@5.0.7: The engine "node" is incompatible with this module. Expected version "^18 || >=20". Got "16.20.1" error Found incompatible module.

cycleccc commented 2 months ago

vue2包 不用改,这几个 框架包 只是包了层皮,做个中间层传输数据给 editor 这个包

cycleccc commented 2 months ago

nanoid 是前段时间升的,有人在 wangEditor 原项目反馈说 nanoid 版本太低,报错了,我就升了新版,node 16 可能确实不适配 意思是 你的node 版本已经 18 了,还是报错 16 吗,如果是的话那就得看看了,可能还得升其它依赖了nanoid 的包的版本。🤯

JaneYork commented 2 months ago

nanoid 是前段时间升的,有人在 wangEditor 原项目反馈说 nanoid 版本太低,报错了,我就升了新版,node 16 可能确实不适配 意思是 你的node 版本已经 18 了,还是报错 16 吗,如果是的话那就得看看了,可能还得升其它依赖了nanoid 的包的版本。🤯

不不,我的是node16。


另外, https://cycleccc.github.io/docs/guide/toolbar-config#excludekeys vue2如何排除某些菜单?有示例吗

cycleccc commented 2 months ago

排除菜单就按文档示例的来就行,创建菜单时加入对应配置即可