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 文档)粘贴了一篇文章,里面包含图片、文本 等复杂场景,要如何处理呢?

相关推荐
fengjay012 小时前
AI Coding——VsCode
ide·vscode·编辑器
rain_in_spring2 小时前
十、项目:营销中心
linux·运维·服务器
小杍随笔2 小时前
【Rust `lib.rs` 使用方法:模块组织、API导出与最佳实践】
服务器·开发语言·rust
csdn_life182 小时前
# Debian 10 升级到 Debian 13 指南
运维·debian·php
U盘失踪了2 小时前
Debian 使用 Xfce 桌面
linux·运维
k7Cx7e2 小时前
Debian安装 curl 时提示插入 DVD 光盘
运维·windows·debian
云飞云共享云桌面2 小时前
广东某智能装备工厂8人共享一台服务器
大数据·运维·服务器·人工智能·3d·自动化·电脑
鹓于2 小时前
OmniParser视觉鼠标自动化实战
运维·自动化·计算机外设
北冥湖畔的燕雀2 小时前
Linux Shell开发实战:从零打造命令行工具
linux·运维·服务器