+图片直接粘贴上传(png、jpg)

This commit is contained in:
task
2026-10-11 12:39:45 +08:00
parent bcc3d1475a
commit 3c39b85aee
3 changed files with 103 additions and 7 deletions
+17 -1
View File
@@ -64,4 +64,20 @@ export const uploadFile = (data) => {
method: "post",
data,
});
};
};
/**
* 上传文件(FormData):粘贴图片等场景使用,noSave=1 不保存进媒体库
* @param data FormData
* @returns {*}
*/
export const uploadAttachment = (data) => {
return service({
url: '/fileUploadAndDownload/upload?noSave=1',
method: 'post',
data,
headers: {
'Content-Type': 'multipart/form-data'
}
})
}
@@ -27,16 +27,23 @@
:preview-src-list="srcList"
fit="cover"
/>
<template v-else>
<div
v-else
class="text-gray-600 group-hover:bg-gray-200 group-hover:opacity-60 w-full h-full flex justify-center items-center"
@click="chooseItem"
>
<el-icon>
<plus />
</el-icon>
上传
</div>
<div id="paste-zone" tabindex="0"
class="absolute z-10 w-16 h-16 -mt-18 ml-22 text-xs flex items-center border border-dashed justify-center p-0.5 rounded border-gray-300 outline-none transition-colors focus:border-blue-400 focus:text-blue-400 text-gray-500"
@click="(e) => e.currentTarget.focus()"
@paste="onPaste"
>
先点击这里,然后粘贴(Ctrl / ⌘ + V)
</div>
</template>
</div>
<!-- 删除按钮在外层容器中 -->
<div
@@ -66,7 +73,7 @@
}
})
const emits = defineEmits(['chooseItem', 'deleteItem'])
const emits = defineEmits(['chooseItem', 'deleteItem', 'pasteFile'])
const chooseItem = () => {
emits('chooseItem')
@@ -83,4 +90,28 @@
const srcList = computed(() => {
return imgUrl.value ? [imgUrl.value] : []
})
// 粘贴图片:必须在事件回调内同步提取文件,异步后 clipboardData 会失效
const onPaste = (e) => {
const clipboardData = e.clipboardData
const files = []
if (clipboardData?.items) {
for (let i = 0; i < clipboardData.items.length; i++) {
const item = clipboardData.items[i]
if (item.kind === 'file' && item.type.startsWith('image/')) {
const file = item.getAsFile()
if (file) files.push(file)
}
}
}
// items 为空时兜底(部分场景只有 files)
if (!files.length && clipboardData?.files) {
for (const file of clipboardData.files) {
if (file.type.startsWith('image/')) files.push(file)
}
}
if (!files.length) return // 非图片内容交给浏览器默认行为
e.preventDefault()
files.forEach((file) => emits('pasteFile', file))
}
</script>
+52 -3
View File
@@ -1,6 +1,6 @@
<template>
<div>
<selectComponent :rounded="rounded" v-if="!props.multiple" :model="model" @chooseItem="openChooseImg" @deleteItem="openChooseImg" />
<selectComponent :rounded="rounded" v-if="!props.multiple" :model="model" @chooseItem="openChooseImg" @deleteItem="openChooseImg" @pasteFile="onPasteFile" />
<div v-else class="w-full gap-4 flex flex-wrap">
<draggable
v-model="model"
@@ -24,7 +24,7 @@
</template>
</draggable>
<selectComponent :rounded="rounded" v-if="currentImageCount < props.maxUpdateCount || props.maxUpdateCount === 0"
@chooseItem="openChooseImg" @deleteItem="openChooseImg"
@chooseItem="openChooseImg" @deleteItem="openChooseImg" @pasteFile="onPasteFile"
/>
</div>
@@ -150,7 +150,7 @@
<script setup>
import { getUrl, isVideoExt } from '@/utils/image'
import { computed, ref } from 'vue'
import { getFileList, editFileName, deleteFile } from '@/api/fileUploadAndDownload'
import {getFileList, editFileName, deleteFile, uploadAttachment} from '@/api/fileUploadAndDownload'
import UploadImage from '@/components/upload/image.vue'
import UploadCommon from '@/components/upload/common.vue'
import WarningBar from '@/components/warningBar/warningBar.vue'
@@ -485,6 +485,55 @@ const onDragEnd = () => {
}
}
// 粘贴上传:noSave 不保存进媒体库,成功后直接回填 model(单图覆盖 / 多图追加)
const onPasteFile = async(file) => {
if (props.fileType && props.fileType !== 'image') {
ElMessage({
type: 'error',
message: '当前类型不支持使用'
})
return
}
if (props.multiple && props.maxUpdateCount > 0 && (model.value?.length || 0) >= props.maxUpdateCount) {
ElMessage({
type: 'error',
message: `最多只能选择 ${props.maxUpdateCount} 张图片`
})
return
}
// 截图类文件常无可用文件名,补一个(后端按文件名识别类型)
const ext = (file.type.split('/')[1] || 'png').replace('jpeg', 'jpg')
const fd = new FormData()
fd.append('file', file, file.name || `paste-${Date.now()}.${ext}`)
const res = await uploadAttachment(fd)
if (res.code !== 0) return // 失败提示由响应拦截器统一处理
const url = res.data?.file.url
if (!url) {
ElMessage({
type: 'error',
message: '上传失败'
})
return
}
if (props.multiple) {
// 并发粘贴多张时可能同时通过上限校验,回填前再检查一次
if (props.maxUpdateCount > 0 && (model.value?.length || 0) >= props.maxUpdateCount) {
ElMessage({
type: 'error',
message: `最多只能选择 ${props.maxUpdateCount} 张图片`
})
return
}
model.value = [...(model.value || []), url]
} else {
model.value = url
}
ElMessage({
type: 'success',
message: '粘贴上传成功'
})
}
</script>
<style scoped>
.selected {