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

相关推荐
杨云龙UP5 小时前
MySQL 数据库常用备份工具对比:mysqldump、mydumper 与 XtraBackup
linux·运维·服务器·数据库·mysql·dba·备份恢复
PC2005-cloud6 小时前
从零上手 openGauss:Docker 部署 + 全场景连接教程
运维·docker·容器
独隅6 小时前
Cursor+GitOps 自动化运维实战:智能编写 Ansible 与 Terraform
运维·自动化·ansible
happymagic6 小时前
java spring boot做的jar包程序,如何实现自动运行启动
java·运维·服务器·spring boot·jar
星空你好7 小时前
AI辅助运维:开源一个内网日志监控工具
运维
风曦Kisaki7 小时前
Kubernetes(K8s)笔记Day06: 持久化存储(emptyDir,hostPath,NFS),PV 和 PVC,StorageClass存储类
linux·运维·笔记·云原生·容器·kubernetes
Dr.kangder7 小时前
嵌入式处理器仿真技术——Hypervisor 原理与实践
服务器·嵌入式硬件·架构·嵌入式
星蓝_starblue9 小时前
零服务器、零数据库!开源growth-board,利用GitHub自动管理刷题/学习/求职全流程
服务器·数据库·程序人生·系统架构·node.js·github·改行学it
回眸&啤酒鸭10 小时前
【回眸】搞钱灵感——自动化敏捷开发项目管理系统落地设计
运维·自动化·敏捷流程
Crazy________11 小时前
k8s部署若依微服务架构流程,v3.6.6
运维·云原生·容器·kubernetes·状态模式