AI_Painting_V2.0/src/components/dialogBox/imageUploader/index.vue

250 lines
5.1 KiB
Vue

<template>
<div class="image-uploader">
<div class="image-list">
<div
v-for="(item, index) in localPreviewList"
:key="item.uid"
class="image-item"
@click.stop
>
<img :src="item.url" class="uploaded-image" alt="上传的图片" />
<div class="image-index">{{ index + 1 }}</div>
<div class="delete-icon" @click="handleDelete(index)">
<i-ep-close />
</div>
</div>
<div v-if="localPreviewList.length < limit" class="upload-trigger" @click="triggerUpload">
<i-ep-plus />
<span>参考内容</span>
</div>
</div>
<el-upload
ref="uploadRef"
v-show="false"
:action="uploadurl"
multiple
:limit="limit"
:before-upload="beforeUpload"
:on-success="handleSuccess"
:on-error="handleError"
:on-exceed="handleExceed"
/>
</div>
</template>
<script setup>
import { genFileId } from 'element-plus'
const props = defineProps({
limit: {
type: Number,
default: 1
},
modelValue: {
type: Array,
default: () => []
}
})
const emit = defineEmits(['update:modelValue'])
const uploadurl = import.meta.env.VITE_API_WORKFLOW_UPLOAD
const uploadRef = ref(null)
const imageList = ref([...props.modelValue])
const localPreviewList = ref([...props.modelValue])
watch(() => props.modelValue, (newVal) => {
imageList.value = [...newVal]
if (newVal.length === 0) {
localPreviewList.value = []
}
}, { deep: true })
const triggerUpload = () => {
uploadRef.value.$el.querySelector('input').click()
}
const beforeUpload = (rawFile) => {
const allowedTypes = ['image/jpeg', 'image/png']
if (!allowedTypes.includes(rawFile.type)) {
ElMessage.error('不支持的文件类型,请上传 JPG、PNG 格式的图片')
return false
}
if (rawFile.size / 1024 / 1024 > 10) {
ElMessage.error('图片大小不能超过 10MB')
return false
}
}
const handleSuccess = (response, uploadFile) => {
ElMessage.success('上传成功')
const localUrl = URL.createObjectURL(uploadFile.raw)
const newImage = {
uid: uploadFile.uid,
url: response.url
}
imageList.value.push(newImage)
emit('update:modelValue', [...imageList.value])
const newPreview = {
uid: uploadFile.uid,
url: localUrl
}
localPreviewList.value.push(newPreview)
}
const handleError = () => {
ElMessage.error('上传失败,请重新选择图片')
}
const handleExceed = () => {
ElMessage.warning(`最多只能上传 ${props.limit} 张图片`)
}
const handleDelete = (index) => {
const previewItem = localPreviewList.value[index]
if (previewItem && previewItem.url.startsWith('blob:')) {
URL.revokeObjectURL(previewItem.url)
}
localPreviewList.value.splice(index, 1)
imageList.value.splice(index, 1)
emit('update:modelValue', [...imageList.value])
}
const handleSelect = async (url) => {
const initialFile = await fetch(url)
const blob = await initialFile.blob()
const file = new File([blob], `selected_image_${Date.now()}.jpg`, { type: blob.type })
file.uid = genFileId()
if (props.limit === 1 && imageList.value.length === 1) {
imageList.value = []
}
uploadRef.value.handleStart(file)
uploadRef.value.submit()
}
defineExpose({
handleSelect,
triggerUpload
})
</script>
<style lang="less" scoped>
.image-uploader {
display: flex;
align-items: center;
}
.image-list {
display: flex;
gap: 8px;
align-items: center;
}
.image-item {
position: relative;
width: 56px;
height: 56px;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
transform: scale(1.1);
z-index: 10;
.delete-icon {
opacity: 1;
}
}
.uploaded-image {
width: 100%;
height: 100%;
object-fit: cover;
transition: all 0.3s ease;
border-radius: 8px;
}
.image-index {
position: absolute;
bottom: 4px;
right: 4px;
width: 20px;
height: 20px;
background: rgba(0, 15, 51, 0.8);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 12px;
font-weight: bold;
z-index: 5;
transition: all 0.3s ease;
}
.delete-icon {
position: absolute;
top: -6px;
right: -6px;
width: 20px;
height: 20px;
background: #ef4444;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 12px;
cursor: pointer;
opacity: 0;
transition: all 0.3s ease;
z-index: 20;
&:hover {
background: #dc2626;
transform: scale(1.1);
}
}
}
.upload-trigger {
width: 56px;
height: 56px;
// border: 2px dashed #d1d5db;
background-color: #F8F9FA;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
cursor: pointer;
color: #9ca3af;
font-size: 15px;
transition: all 0.3s ease;
span {
color: #999;
font-family: "Microsoft YaHei";
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: normal;
}
&:hover {
background-color: #F0F1F2;
border-color: #626aef;
// color: #626aef;
transform: scale(1.05);
}
}
</style>