富文本编辑器wangeditor支持粘贴图片实现图片上传

1,841 阅读8分钟

效果:

image.png

下面是一个 Vue 3 (setup script) + WangEditor 5 的示例,演示了如何配置编辑器以支持粘贴图片并“上传”(此处为模拟上传,实际中你需要对接后端接口)。

主要思路:

  1. 安装依赖:

    • @wangeditor/editor: WangEditor 核心库。
    • @wangeditor/editor-for-vue: WangEditor 的 Vue 3 组件封装。
  2. 配置 editorConfig:

    • 通过 MENU_CONF['uploadImage'] 来配置图片上传。

    • 关键是使用 customUpload 方法。当用户粘贴、拖拽或选择图片时,此方法会被调用。

    • customUpload(file, insertFn):

      • file: File 对象,即用户粘贴或选择的图片文件。

      • insertFn(url, alt, href): 一个回调函数,你需要调用它来将图片插入到编辑器中。

        • url: 图片的最终可访问 URL。
        • alt: 图片的 alt 文本(可选)。
        • href: 图片链接(可选)。
  3. 模拟上传:

    • 在 customUpload 中,我们会接收到 file 对象。
    • 为了演示,我们将使用 URL.createObjectURL(file) 生成一个临时的本地 URL,并模拟一个异步上传过程(例如使用 setTimeout)。
    • 在实际项目中,你需要将 file 通过 FormData 发送到你的后端服务器,服务器保存图片后返回一个永久的图片 URL,然后你再调用 insertFn。

步骤:

1. 安装依赖:

npm install @wangeditor/editor @wangeditor/editor-for-vue
# 或者
yarn add @wangeditor/editor @wangeditor/editor-for-vue

2. 创建组件 WangEditorDemo.vue:

<template>
  <div style="border: 1px solid #ccc; margin-top: 20px;">
    <!-- 工具栏 -->
    <Toolbar
      style="border-bottom: 1px solid #ccc"
      :editor="editorRef"
      :defaultConfig="toolbarConfig"
      mode="default"
    />
    <!-- 编辑器 -->
    <Editor
      style="height: 500px; overflow-y: hidden;"
      v-model="valueHtml"
      :defaultConfig="editorConfig"
      mode="default"
      @onCreated="handleCreated"
      @onChange="handleChange"
    />
  </div>
  <div style="margin-top: 10px;">
    <p>编辑器内容:</p>
    <div v-html="valueHtml"></div>
  </div>
</template>

<script setup>
import { ref, shallowRef, onBeforeUnmount, onMounted } from 'vue';
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
import '@wangeditor/editor/dist/css/style.css'; // 引入 Wangeditor 样式

// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef();

// 内容 HTML
const valueHtml = ref('<p>Hello WangEditor!</p><p>尝试从剪贴板粘贴一张图片,或者拖拽图片进来。</p>');

// 工具栏配置
const toolbarConfig = {
  // 在此处配置工具栏,不需要的可以不配置
  // excludeKeys: ['group-image'], // 例如,如果完全依赖粘贴上传,可以隐藏默认的图片上传按钮
};

// 编辑器配置
const editorConfig = {
  placeholder: '请输入内容...',
  MENU_CONF: {
    // 图片上传配置
    uploadImage: {
      // 自定义图片上传逻辑
      // file 即选中的文件,async customUpload 返回的是一个数组 [{ url, alt, href }]
      async customUpload(file, insertFn) {
        console.log('Pasted/Uploaded file:', file);

        // --- 模拟上传过程 ---
        // 1. 创建 FormData 对象
        // const formData = new FormData();
        // formData.append('file', file); // 'file' 是你后端接口接收的字段名
        // formData.append('otherParam', 'value'); // 其他参数

        // 2. 发起 AJAX 请求 (例如使用 fetch 或 axios)
        // try {
        //   const response = await fetch('/api/upload-image', { // 替换为你的后端上传接口
        //     method: 'POST',
        //     body: formData,
        //     // headers: { 'Authorization': 'Bearer your_token_if_needed' }
        //   });
        //   if (!response.ok) {
        //     throw new Error(`Upload failed: ${response.statusText}`);
        //   }
        //   const result = await response.json(); // 假设后端返回 { errno: 0, data: { url: '...', alt: '...', href: '...' } }
        //
        //   if (result.errno === 0 && result.data && result.data.url) {
        //     // 上传成功,调用 insertFn 插入图片
        //     insertFn(result.data.url, result.data.alt || file.name, result.data.href || result.data.url);
        //     console.log('Image uploaded and inserted:', result.data.url);
        //   } else {
        //     alert(`上传失败: ${result.message || '未知错误'}`);
        //     console.error('Upload error from server:', result);
        //   }
        // } catch (error) {
        //   alert(`上传出错: ${error.message}`);
        //   console.error('Upload fetch error:', error);
        // }
        // --- 模拟上传过程结束 ---

        // --- DEMO: 使用本地 Blob URL 模拟上传成功 ---
        // 提示:URL.createObjectURL 创建的 URL 只在当前文档生命周期内有效
        // 实际项目中,你需要上传到服务器并获取一个永久的 URL
        console.log('Simulating upload for demo purposes...');
        await new Promise(resolve => setTimeout(resolve, 1500)); // 模拟网络延迟

        const url = URL.createObjectURL(file);
        const alt = file.name; // 使用文件名作为 alt
        const href = url; // 可以让图片本身可点击,并链接到原图

        // 调用 insertFn 插入图片
        // insertFn(url, alt, href)
        insertFn(url, alt, href);
        console.log(`Simulated upload complete. Inserted image with local URL: ${url}`);
        // 注意: 如果使用 createObjectURL,理论上在图片不再需要时(例如编辑器销毁或图片被删除时)
        // 应该调用 URL.revokeObjectURL(url) 来释放资源。
        // 但在 WangEditor 中,编辑器内部可能会处理,或者对于简单演示,浏览器关闭时也会释放。
        // --- DEMO 结束 ---
      },

      // 其他配置项 (根据 WangEditor 文档按需配置)
      // server: '/api/upload', // 如果你不想用 customUpload,可以直接配置服务器地址,但这样粘贴的自定义处理会复杂
      fieldName: 'file', // 上传时传递的文件表单名,默认 'wangeditor-uploaded-image'
      maxFileSize: 5 * 1024 * 1024, // 5M
      allowedFileTypes: ['image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/webp'], // 允许的文件类型
      // meta: { token: 'xxx', a: 100 }, // 上传时附带的额外参数
      // headers: { Authorization: 'Bearer your_token' }, // 自定义请求头

      // 粘贴图片时的行为,默认为 'upload',即触发上传逻辑
      // 如果只想 base64 编码,可以设为 'base64',但不推荐大图片用 base64
      pasteIgnoreImg: false, // 是否忽略粘贴的图片,默认为 false
      // onInserted(files) { console.log('图片已插入编辑器', files); },
      // onProgress(progress) { console.log('上传进度', progress); }, // 0-100
      // onSuccess(file, res) { console.log(`${file.name} 上传成功`, res); },
      // onFailed(file, res) { console.log(`${file.name} 上传失败`, res); },
      // onError(file, err, res) { console.error(`${file.name} 上传出错`, err, res); },
    },
    // 其他 MENU_CONF 配置...
    // 例如:配置粘贴文本的过滤
    // pasteTextHandle: (html) => {
    //   console.log('Pasted HTML:', html);
    //   // 在这里可以对粘贴的 HTML 内容进行处理
    //   // return html.replace(/<p>/g, '<span>').replace(/<\/p>/g, '</span>');
    //   return html;
    // }
  },
};

// 编辑器创建时的回调
const handleCreated = (editor) => {
  editorRef.value = editor; // 记录 editor 实例,重要!
  console.log('Editor created:', editor.getAllMenuKeys());
};

// 编辑器内容变化时的回调
const handleChange = (editor) => {
  // console.log('Content changed:', editor.getHtml());
};

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

onMounted(() => {
  // 可以在这里做一些初始化,但王编辑器的主要配置在 editorConfig 中
});

</script>

<style scoped>
/* 可以添加一些组件局部样式 */
</style>

3. 在你的 App.vue 或其他父组件中使用:

<template>
  <div>
    <h1>WangEditor 5 in Vue 3 Demo</h1>
    <WangEditorDemo />
  </div>
</template>

<script setup>
import WangEditorDemo from './components/WangEditorDemo.vue';
</script>

<style>
/* 全局样式,如果需要 */
body {
  margin: 20px;
}
</style>

解释:

  1. @wangeditor/editor-for-vue: 提供了 Editor 和 Toolbar 组件。

  2. shallowRef for editorRef: WangEditor 官方推荐使用 shallowRef 来存储编辑器实例,因为编辑器实例本身比较复杂,不需要深度响应。

  3. editorConfig.MENU_CONF.uploadImage: 这是配置图片上传的核心。

    • customUpload(file, insertFn) :

      • 当用户粘贴图片(或其他方式如拖拽、点击上传按钮选择文件)时,WangEditor 会捕获到图片数据并将其包装成一个 File 对象,然后调用这个 customUpload 方法。
      • file: 就是这个图片文件。
      • insertFn: 你必须调用此函数,并传入图片的最终 URL (以及可选的 alt 和 href),WangEditor 才会将 转存失败,建议直接上传图片文件 标签插入到编辑区域。
    • 模拟上传 vs 实际上传:

      • 模拟 (Demo中) : 我们使用 URL.createObjectURL(file) 生成一个临时的、仅在当前浏览器会话中有效的对象 URL。这对于本地演示非常方便,但这个 URL 不能被其他人访问,也不能持久保存。
      • 实际: 你需要将 file 对象通过 fetch 或 axios 发送到你的后端 API。后端接收文件、保存到服务器或云存储,并返回一个公开可访问的永久 URL。然后你用这个返回的 URL 去调用 insertFn。示例代码中注释掉了实际上传的逻辑框架。
    • pasteIgnoreImg: false: 确保粘贴的图片不会被忽略,而是触发上传流程。这是默认行为,但明确写出来有助于理解。

  4. 生命周期管理:

    • handleCreated: 编辑器创建后,将实例赋值给 editorRef。
    • onBeforeUnmount: 组件销毁前,必须调用 editor.destroy() 来销毁编辑器实例,释放资源,防止内存泄漏。

====================== 核心 ======================

   MENU_CONF: {
    // 图片上传配置
    uploadImage: {
      // 自定义图片上传逻辑
      // file 即选中的文件,async customUpload 返回的是一个数组 [{ url, alt, href }]
      async customUpload(file, insertFn) {
        console.log('Pasted/Uploaded file:', file);

        // --- 模拟上传过程 ---
        // 1. 创建 FormData 对象
        // const formData = new FormData();
        // formData.append('file', file); // 'file' 是你后端接口接收的字段名
        // formData.append('otherParam', 'value'); // 其他参数

        // 2. 发起 AJAX 请求 (例如使用 fetch 或 axios)
        // try {
        //   const response = await fetch('/api/upload-image', { // 替换为你的后端上传接口
        //     method: 'POST',
        //     body: formData,
        //     // headers: { 'Authorization': 'Bearer your_token_if_needed' }
        //   });
        //   if (!response.ok) {
        //     throw new Error(`Upload failed: ${response.statusText}`);
        //   }
        //   const result = await response.json(); // 假设后端返回 { errno: 0, data: { url: '...', alt: '...', href: '...' } }
        //
        //   if (result.errno === 0 && result.data && result.data.url) {
        //     // 上传成功,调用 insertFn 插入图片
        //     insertFn(result.data.url, result.data.alt || file.name, result.data.href || result.data.url);
        //     console.log('Image uploaded and inserted:', result.data.url);
        //   } else {
        //     alert(`上传失败: ${result.message || '未知错误'}`);
        //     console.error('Upload error from server:', result);
        //   }
        // } catch (error) {
        //   alert(`上传出错: ${error.message}`);
        //   console.error('Upload fetch error:', error);
        // }
        // --- 模拟上传过程结束 ---

        // --- DEMO: 使用本地 Blob URL 模拟上传成功 ---
        // 提示:URL.createObjectURL 创建的 URL 只在当前文档生命周期内有效
        // 实际项目中,你需要上传到服务器并获取一个永久的 URL
        console.log('Simulating upload for demo purposes...');
        await new Promise(resolve => setTimeout(resolve, 1500)); // 模拟网络延迟

        const url = URL.createObjectURL(file);
        const alt = file.name; // 使用文件名作为 alt
        const href = url; // 可以让图片本身可点击,并链接到原图

        // 调用 insertFn 插入图片
        // insertFn(url, alt, href)
        insertFn(url, alt, href);
        console.log(`Simulated upload complete. Inserted image with local URL: ${url}`);
        // 注意: 如果使用 createObjectURL,理论上在图片不再需要时(例如编辑器销毁或图片被删除时)
        // 应该调用 URL.revokeObjectURL(url) 来释放资源。
        // 但在 WangEditor 中,编辑器内部可能会处理,或者对于简单演示,浏览器关闭时也会释放。
        // --- DEMO 结束 ---
      },

      // 其他配置项 (根据 WangEditor 文档按需配置)
      // server: '/api/upload', // 如果你不想用 customUpload,可以直接配置服务器地址,但这样粘贴的自定义处理会复杂
      fieldName: 'file', // 上传时传递的文件表单名,默认 'wangeditor-uploaded-image'
      maxFileSize: 5 * 1024 * 1024, // 5M
      allowedFileTypes: ['image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/webp'], // 允许的文件类型
      // meta: { token: 'xxx', a: 100 }, // 上传时附带的额外参数
      // headers: { Authorization: 'Bearer your_token' }, // 自定义请求头

      // 粘贴图片时的行为,默认为 'upload',即触发上传逻辑
      // 如果只想 base64 编码,可以设为 'base64',但不推荐大图片用 base64
      pasteIgnoreImg: false, // 是否忽略粘贴的图片,默认为 false
      // onInserted(files) { console.log('图片已插入编辑器', files); },
      // onProgress(progress) { console.log('上传进度', progress); }, // 0-100
      // onSuccess(file, res) { console.log(`${file.name} 上传成功`, res); },
      // onFailed(file, res) { console.log(`${file.name} 上传失败`, res); },
      // onError(file, err, res) { console.error(`${file.name} 上传出错`, err, res); },
    },
    // 其他 MENU_CONF 配置...
    // 例如:配置粘贴文本的过滤
    // pasteTextHandle: (html) => {
    //   console.log('Pasted HTML:', html);
    //   // 在这里可以对粘贴的 HTML 内容进行处理
    //   // return html.replace(/<p>/g, '<span>').replace(/<\/p>/g, '</span>');
    //   return html;
    // }
  },

=========有用!!!!!!!!!!!!===================!!!!!!!!!!!!!!!!!!!!!!!!!!!!

<template>
  <div style="border: 1px solid #ccc; margin-top: 20px;">
    <!-- 工具栏 -->
    <Toolbar
      style="border-bottom: 1px solid #ccc"
      :editor="editorRef"
      :defaultConfig="toolbarConfig"
      mode="default"
    />
    <!-- 编辑器 -->
    <Editor
      style="height: 500px; overflow-y: hidden;"
      v-model="valueHtml"
      :defaultConfig="editorConfig"
      mode="default"
      @onCreated="handleCreated"
      @onChange="handleChange"
    />
  </div>
  <!-- <div style="margin-top: 10px;">
    <p>编辑器内容:</p>
    <div v-html="valueHtml"></div>
  </div> -->
</template>

<script setup>
import { ref, shallowRef, onBeforeUnmount, onMounted, watch } from 'vue';
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
import '@wangeditor/editor/dist/css/style.css'; // 引入 Wangeditor 样式

// 定义 props 和 emits
const props = defineProps({
  modelValue: {
    type: String,
    default: ''
  }
});

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

// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef();

// 内容 HTML
const valueHtml = ref(props.modelValue || '<p></p>');

// 监听 modelValue 变化
watch(() => props.modelValue, (newVal) => {
  if (newVal !== valueHtml.value) {
    valueHtml.value = newVal;
  }
});

// 监听 valueHtml 变化,触发 emit
watch(valueHtml, (newVal) => {
  emit('update:modelValue', newVal);
});

// 工具栏配置
const toolbarConfig = {
  // 在此处配置工具栏,不需要的可以不配置
  // excludeKeys: ['group-image'], // 例如,如果完全依赖粘贴上传,可以隐藏默认的图片上传按钮
};

// 编辑器配置
const editorConfig = {
  placeholder: 'Please input content...',
  MENU_CONF: {
    // 图片上传配置
    uploadImage: {
      // 自定义图片上传逻辑
      async customUpload(file, insertFn) {
        console.log('图片上传触发', file);

        try {
          console.log('开始处理图片上传...');
          
          const url = URL.createObjectURL(file);
          const alt = file.name || 'pasted image';
          const href = url;

          // 调用 insertFn 插入图片
          insertFn(url, alt, href);
          console.log(`图片上传完成,URL: ${url}`);
          
        } catch (error) {
          console.error('图片上传失败:', error);
          alert('Image upload failed, please try again');
        }
      },

      fieldName: 'file',
      maxFileSize: 10 * 1024 * 1024, // 10M
      allowedFileTypes: ['image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/webp'],
      
      // 关键配置:确保粘贴图片能正常工作
      pasteIgnoreImg: false,
      
      // 添加事件回调来调试
      onInserted(files) { 
        console.log('图片已插入编辑器', files); 
      },
      onSuccess(file, res) { 
        console.log(`${file.name} 上传成功`, res); 
      },
      onFailed(file, res) { 
        console.log(`${file.name} 上传失败`, res); 
      },
      onError(file, err, res) { 
        console.error(`${file.name} 上传出错`, err, res); 
      },
    }
  },
  
  // 确保编辑器能接收粘贴事件
  readOnly: false,
  autoFocus: false
};

// 编辑器创建时的回调
const handleCreated = (editor) => {
  editorRef.value = editor; // 记录 editor 实例,重要!
  console.log('Editor created:', editor.getAllMenuKeys());
  
  // 添加粘贴图片监听器
  const editorDom = editor.getEditableContainer();
  if (editorDom) {
    editorDom.addEventListener('paste', handlePasteImage);
    console.log('已添加粘贴监听器');
  }
};

// 处理粘贴图片
const handlePasteImage = (event) => {
  console.log('粘贴事件触发', event);
  
  const clipboardData = event.clipboardData;
  if (!clipboardData) return;
  
  const items = clipboardData.items;
  if (!items || items.length === 0) return;
  
  // 查找图片类型
  for (let i = 0; i < items.length; i++) {
    const item = items[i];
    console.log('粘贴项目类型:', item.type);
    
    if (item.type.includes('image')) {
      console.log('检测到图片粘贴');
      event.preventDefault(); // 阻止默认粘贴行为
      
      const file = item.getAsFile();
      if (file) {
        console.log('获取到图片文件:', file);
        
        // 创建临时URL
        const url = URL.createObjectURL(file);
        const alt = 'pasted image';
        
        // 插入图片到编辑器
        const editor = editorRef.value;
        if (editor) {
          console.log('插入图片到编辑器:', url);
          editor.dangerouslyInsertHtml(`<img src="${url}" alt="${alt}" style="max-width: 100%; height: auto;" />`);
        }
      }
      break;
    }
  }
};

// 编辑器内容变化时的回调
const handleChange = (editor) => {
  // console.log('Content changed:', editor.getHtml());
};

// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
  const editor = editorRef.value;
  if (editor == null) return;
  
  // 移除粘贴监听器
  const editorDom = editor.getEditableContainer();
  if (editorDom) {
    editorDom.removeEventListener('paste', handlePasteImage);
  }
  
  editor.destroy();
});

// 移除全局的paste监听器,让编辑器自己处理粘贴事件
onMounted(() => {
  console.log('WangEditor 组件已挂载,粘贴功能已就绪');
});

</script>

<style scoped>
/* 可以添加一些组件局部样式 */
</style>


====上述是本地上传,没有走接口,下面这个示例是联调接口的示例,并用接口展示图片 =====

<template>
  <div style="border: 1px solid #ccc; margin-top: 20px">
    <!-- 工具栏 -->
    <Toolbar
      style="border-bottom: 1px solid #ccc"
      :editor="editorRef"
      :defaultConfig="toolbarConfig"
      mode="default"
    />
    <!-- 编辑器 -->
    <Editor
      style="height: 200px; overflow-y: hidden"
      v-model="valueHtml"
      :defaultConfig="editorConfig"
      mode="default"
      @onCreated="handleCreated"
      @onChange="handleChange"
    />
  </div>
  <!-- <div style="margin-top: 10px;">
    <p>编辑器内容:</p>
    <div v-html="valueHtml"></div>
  </div> -->
</template>

<script setup>
import { ref, shallowRef, onBeforeUnmount, onMounted, watch } from "vue";
import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
import "@wangeditor/editor/dist/css/style.css"; // 引入 Wangeditor 样式
import { uploadFileApiNew } from "@/api/featuredPapers/index";
import { ElMessage, ElMessageBox } from "element-plus";

// 定义 props 和 emits
const props = defineProps({
  modelValue: {
    type: String,
    default: "",
  },
});

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

// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef();

// 内容 HTML
const valueHtml = ref(props.modelValue || "<p></p>");

// 监听 modelValue 变化
watch(
  () => props.modelValue,
  (newVal) => {
    if (newVal !== valueHtml.value) {
      valueHtml.value = newVal;
    }
  }
);

// 监听 valueHtml 变化,触发 emit
watch(valueHtml, (newVal) => {
  emit("update:modelValue", newVal);
});

// 工具栏配置
const toolbarConfig = {
  // 只保留文本和图片相关的工具栏按钮
  toolbarKeys: [
    'bold',
    'italic',
    'underline',
    'fontSize',
    'fontFamily',
    'color',
    'bgColor',
    '|',
    'uploadImage',
    '|',
    'justifyLeft',
    'justifyCenter',
    'justifyRight',
  ]
};

// 编辑器配置
const editorConfig = {
  placeholder: "Please input content...",
  MENU_CONF: {
    // 图片上传配置
    uploadImage: {
      // 自定义图片上传逻辑
      async customUpload(file, insertFn) {
        console.log("图片上传触发", file);

        try {
          // console.log('开始处理图片上传...');

          // const url = URL.createObjectURL(file);
          // const alt = file.name || 'pasted image';
          // const href = url;

          const uploadResponse = await uploadFileApiNew(file);
          if (uploadResponse.code === 2000 && uploadResponse.data) {
            const fileId = uploadResponse.data.uploadFileData.id || "";
            // https://chatgpt.com/share/68592c2a-44e0-8000-a162-4a847e6a745a
            // const imgUrl = URL.createObjectURL(file); // 只能上传的那个人的电脑才能看 blob
            const imageData = {
              url: imgUrl,
              name: file.name,
              fileId: fileId,
            };

            ElMessage.success("图片上传成功");
          } else {
            ElMessage.error("图片上传失败");
          }

          // 调用 insertFn 插入图片
          insertFn(url, alt, href);
          console.log(`图片上传完成,URL: ${url}`);
        } catch (error) {
          console.error("图片上传失败:", error);
          alert("Image upload failed, please try again");
        }
      },

      fieldName: "file",
      maxFileSize: 10 * 1024 * 1024, // 10M
      allowedFileTypes: [
        "image/jpeg",
        "image/png",
        "image/gif",
        "image/bmp",
        "image/webp",
      ],

      // 关键配置:确保粘贴图片能正常工作
      pasteIgnoreImg: false,

      // 添加事件回调来调试
      onInserted(files) {
        console.log("图片已插入编辑器", files);
      },
      onSuccess(file, res) {
        console.log(`${file.name} 上传成功`, res);
      },
      onFailed(file, res) {
        console.log(`${file.name} 上传失败`, res);
      },
      onError(file, err, res) {
        console.error(`${file.name} 上传出错`, err, res);
      },
    },
  },

  // 确保编辑器能接收粘贴事件
  readOnly: false,
  autoFocus: false,
};

// 编辑器创建时的回调
const handleCreated = (editor) => {
  editorRef.value = editor; // 记录 editor 实例,重要!
  console.log("Editor created:", editor.getAllMenuKeys());

  // 添加粘贴图片监听器
  const editorDom = editor.getEditableContainer();
  if (editorDom) {
    editorDom.addEventListener("paste", handlePasteImage);
    console.log("已添加粘贴监听器");
  }
};

// 处理粘贴图片
const handlePasteImage = async (event) => {
  console.log("粘贴事件触发", event);

  const clipboardData = event.clipboardData;
  if (!clipboardData) return;

  const items = clipboardData.items;
  if (!items || items.length === 0) return;

  // 查找图片类型
  for (let i = 0; i < items.length; i++) {
    const item = items[i];
    console.log("粘贴项目类型:", item.type);

    if (item.type.includes("image")) {
      console.log("检测到图片粘贴");
      event.preventDefault(); // 阻止默认粘贴行为

      const file = item.getAsFile();
      if (file) {
        console.log("获取到图片文件:", file);

        // 创建临时URL
        // const url = URL.createObjectURL(file);
        const alt = "pasted image";

        const uploadResponse = await uploadFileApiNew(file);
        if (uploadResponse.code === 2000 && uploadResponse.data) {
          const fileId = uploadResponse.data.uploadFileData.id || "";
          // const imgUrl = URL.createObjectURL(file);
          const realUrl = `/api/poster/show/image/${fileId}`; // ✅ 使用后端真实 URL

          ElMessage.success("粘贴---图片上传成功");

          // 插入图片到编辑器
          const editor = editorRef.value;
          if (editor) {
            editor.dangerouslyInsertHtml(
              `<img src="${realUrl}" alt="${alt}" style="max-width: 100%; height: auto;" />`
            );
          }
        } else {
          ElMessage.error("粘贴---图片上传失败");
        }
      }
      break;
    }
  }
};

// 编辑器内容变化时的回调
const handleChange = (editor) => {
  // console.log('Content changed:', editor.getHtml());
};

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

  // 移除粘贴监听器
  const editorDom = editor.getEditableContainer();
  if (editorDom) {
    editorDom.removeEventListener("paste", handlePasteImage);
  }

  editor.destroy();
});

// 移除全局的paste监听器,让编辑器自己处理粘贴事件
onMounted(() => {
  console.log("WangEditor 组件已挂载,粘贴功能已就绪");
});
</script>