TinyEditor 篇3:拖拽图片到编辑器并同步上传至服务器

上一篇,我们实现了剪贴板粘贴图片并上传至服务器的功能,在本篇,我们实现从外部拖拽图片到编辑器中,也可以将图片上传到服务器。

功能实现:

新增一个 handleDrop 方法,在这个方法中处理拖拽及上传方法的触发。

需要注意的是,我们需要 阻止浏览器默认拖拽行为(否则可能打开图片):

javascript 复制代码
editorRef.value.addEventListener('dragover', (e) => {
    e.preventDefault()
})

下面直接来看完整代码:

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.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
    }
  }
}

const handleDrop = async (e) => {
  const files = e.dataTransfer?.files
  if (!files || files.length === 0) return
  
  const imageFiles = Array.from(files).filter(f => f.type.startsWith('image/'))
  if (imageFiles.length === 0) return

  e.preventDefault()
  e.stopPropagation()

  let index = 0
  try {
    const range = editorInstance.getSelection(true)
    index = range ? range.index : 0
  } catch {
    index = 0
  }

  for (const file of imageFiles) {
    try {
      const url = await uploadImage(file)
      editorInstance.insertEmbed(index, 'image', url)
      index += 1
    } catch (err) {
      console.error('拖拽上传失败', err)
    }
  }
}

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)

  editorRef.value.addEventListener('drop', handleDrop, true)
  
  editorRef.value.addEventListener('dragover', (e) => {
    e.preventDefault()
  })
})
</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>

关于编辑器中直接上传图片的处理场景已经介绍的差不多了,下一篇我们来看看假如我从外部(网页、Word 文档)粘贴了一篇文章,里面包含图片、文本 等复杂场景,要如何处理呢?

相关推荐
SelectDB16 小时前
Litefuse 开源并推出单进程轻量模式,25 秒就能跑起来的 Agent 可观测与评估平台
运维·后端·自动化运维
zzzzzz3102 天前
9K Star 炸裂开源!这个 C 语言写的代码知识图谱,把 Linux 内核索引压缩到了 3 分钟
linux·服务器·sql
XIAOHEZIcode2 天前
Linux系统鼠标偏移常见原因以及修复方案
linux·运维·游戏
用户0328472220703 天前
如何搭建本地yum源(上)
运维
大树886 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠6 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质6 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
VidDown6 天前
VidDown 工具站:免费、本地优先的开发者工具箱
javascript·编辑器·音视频·视频编解码·视频
小宇宙Zz6 天前
Maven依赖冲突
java·服务器·maven
Inhand陈工6 天前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信