TinyEditor 篇2:剪贴板粘贴图片并同步上传至服务器

书接上回,我们实现了富文本编辑器的工具栏图片上传按钮选择图片后,可以将图片上传至服务器,这一篇,来看看当我们复制了一张图片到剪贴板,粘贴到编辑器文本编辑区域时,如何同步上传至服务器呢?

功能实现

我们的实现步骤是:

当我们点击 Ctrl + V 的时候,FluentEditor 默认粘贴(插入 base64 图片),此处触发我们自己的粘贴处理方法 handlePaste,此时上传图片,调用FluentEditor 的 .insertEmbed 方法插入图片地址。这里直接上代码:

javascript 复制代码
<template>
  <div class="editor-container">
    <div ref="editorRef" class="editor-inside"></div>
  </div>
</template>
 
<script setup>
import { onMounted, ref } from 'vue'
import '@opentiny/fluent-editor/style.css'
import FluentEditor from '@opentiny/fluent-editor'
import axios from 'axios'
import { uploadFile } from '@/model/editor.js' // 这里使用自己的上传接口即可
 
const editorRef = ref(null)
let editorInstance = null
 
const TOOLBAR_CONFIG = [
  [{ header: [] }],
  ['bold', 'italic', 'underline', 'link'],
  [{ list: 'ordered' }, { list: 'bullet' }],
  ['clean'],
  ['image']
]
 
const selectImage = () => {
  const input = document.createElement('input')
  input.type = 'file'
  input.accept = 'image/*'
  input.click()
 
  input.onchange = async () => {
    const file = input.files[0]
    if (!file) return
 
    try {
      const url = await uploadImage(file)
 
      const range = editorInstance.getSelection()
      editorInstance.insertEmbed(range.index, 'image', url)
 
    } catch (err) {
      console.error('上传失败', err)
    }
  }
}
 
const uploadImage = async (file) => {
  const ext = file.name.split('.').pop()
 
  const res = await uploadFile({ ext })
 
  const resData = res.data
  const uploadUrl = resData.upload_url
  const fileUrl = resData.public_url
 
  await axios.put(uploadUrl, file, {
    headers: {
      'Content-Type': file.type
    }
  })
 
  return fileUrl
}

const handlePaste = async (e) => {
  const items = e.clipboardData?.items
  if (!items) return

  for (let i = 0; i < items.length; i++) {
    const item = items[i]

    if (item.type.indexOf('image') !== -1) {
      e.preventDefault()
      e.stopPropagation()

      const file = item.getAsFile()

      try {
        const url = await uploadImage(file)

        const range = editorInstance.getSelection(true)
        editorInstance.insertEmbed(range.index, 'image', url)
      } catch (err) {
        console.error('粘贴上传失败', err)
      }

      break
    }
  }
}
 
onMounted(() => {
  if (!editorRef.value) return
 
  editorInstance = new FluentEditor(editorRef.value, {
    theme: 'snow',
    modules: {
      toolbar: {
        container: TOOLBAR_CONFIG,
        handlers: {
          image: selectImage
        }
      }
    }
  })

  editorRef.value.addEventListener('paste', handlePaste, true)
})
</script>
 
<style scoped>
.editor-container {
  width: 50%;
  display: flex;
  justify-content: center;
  padding-top: 20px;
}
 
.editor-inside {
  height: 350px;
}
 
.editor-inside :deep(.ql-container) {
  height: 350px;
}
 
.editor-inside :deep(.ql-editor) {
  height: 310px;
}
</style>

注意:我们在监听 paste 的时候,第三个入参传了 true,是为了阻止编辑器默认的图片粘贴行为,如果不写,那么我们在粘贴图片的时候,会粘贴进去两张图片。

下一篇,我们看看将图片拖拽到编辑器中如何渲染和上传。

相关推荐
张元清6 小时前
React 与用户偏好:尊重用户已经在 OS 里设过的那些选项
前端·javascript·面试
RPGMZ6 小时前
RPGMZ 游戏场景全局提示框 带三秒隐藏插件
前端·javascript·游戏·rpgmz
宠..6 小时前
VS Code 修改 C++ 标准同时修改错误检测标准
java·linux·开发语言·javascript·c++·python·qt
|_⊙6 小时前
Linux 深入理解文件(IO)
linux·运维·服务器
JarvanMo6 小时前
2026年最佳Flutter图标包
前端
Arthur14726122865476 小时前
Vue Query 缓存机制实战:别再让 gcTime 和 staleTime 背锅了
前端
largecode6 小时前
给用户打电话,怎么在对方手机显示为“XX旅游”?号码认证办理教程
linux·服务器·容器·智能手机·ssh·旅游·vagrant
Rkgua6 小时前
React中的赋值操作为什么不是=?
前端·javascript
heyCHEEMS6 小时前
记录一个 React 表单的小坑:缓存节流导致页面刷新
前端·javascript
@不误正业6 小时前
多Agent协作框架深度实战-从ReAct到Plan-and-Execute全架构演进
前端·react.js·架构·agent