Files
jyls_front/src/components/Upload/SingleImageUpload.vue

210 lines
4.5 KiB
Vue
Raw Normal View History

2025-12-05 10:17:26 +08:00
<!-- 单图上传组件 -->
<template>
<el-upload
class="single-upload"
list-type="picture-card"
:show-file-list="false"
:accept="props.accept"
:before-upload="handleBeforeUpload"
:http-request="handleUpload"
:on-success="onSuccess"
:on-error="onError"
>
<template #default>
<template v-if="modelValue">
<el-image
class="single-upload__image"
:src="modelValue"
:preview-src-list="[modelValue]"
@click.stop="handlePreview"
/>
<el-icon class="single-upload__delete-btn" @click.stop="handleDelete">
<CircleCloseFilled />
</el-icon>
</template>
<template v-else>
<el-icon>
<Plus />
</el-icon>
</template>
</template>
</el-upload>
</template>
<script setup lang="ts">
2025-12-11 17:13:37 +08:00
import { UploadRawFile, UploadRequestOptions } from 'element-plus'
import FileAPI, { FileInfo } from '@/api/file-api'
2025-12-05 10:17:26 +08:00
const props = defineProps({
/**
* 请求携带的额外参数
*/
data: {
type: Object,
default: () => {
2025-12-11 17:13:37 +08:00
return {}
}
2025-12-05 10:17:26 +08:00
},
/**
* 上传文件的参数名
*/
name: {
type: String,
2025-12-11 17:13:37 +08:00
default: 'file'
2025-12-05 10:17:26 +08:00
},
/**
* 最大文件大小单位M
*/
maxFileSize: {
type: Number,
2025-12-11 17:13:37 +08:00
default: 10
2025-12-05 10:17:26 +08:00
},
/**
* 上传图片格式默认支持所有图片(image/*)指定格式示例'.png,.jpg,.jpeg,.gif,.bmp'
*/
accept: {
type: String,
2025-12-11 17:13:37 +08:00
default: 'image/*'
2025-12-05 10:17:26 +08:00
},
/**
* 自定义样式用于设置组件的宽度和高度等其他样式
*/
style: {
type: Object,
default: () => {
return {
2025-12-11 17:13:37 +08:00
width: '150px',
height: '150px'
}
}
}
})
2025-12-05 10:17:26 +08:00
2025-12-11 17:13:37 +08:00
const modelValue = defineModel('modelValue', {
2025-12-05 10:17:26 +08:00
type: String,
2025-12-11 17:13:37 +08:00
default: () => ''
})
2025-12-05 10:17:26 +08:00
/**
* 限制用户上传文件的格式和大小
*/
function handleBeforeUpload(file: UploadRawFile) {
// 校验文件类型:虽然 accept 属性限制了用户在文件选择器中可选的文件类型,但仍需在上传时再次校验文件实际类型,确保符合 accept 的规则
2025-12-11 17:13:37 +08:00
const acceptTypes = props.accept.split(',').map((type) => type.trim())
2025-12-05 10:17:26 +08:00
// 检查文件格式是否符合 accept
const isValidType = acceptTypes.some((type) => {
2025-12-11 17:13:37 +08:00
if (type === 'image/*') {
2025-12-05 10:17:26 +08:00
// 如果是 image/*,检查 MIME 类型是否以 "image/" 开头
2025-12-11 17:13:37 +08:00
return file.type.startsWith('image/')
} else if (type.startsWith('.')) {
2025-12-05 10:17:26 +08:00
// 如果是扩展名 (.png, .jpg),检查文件名是否以指定扩展名结尾
2025-12-11 17:13:37 +08:00
return file.name.toLowerCase().endsWith(type)
2025-12-05 10:17:26 +08:00
} else {
// 如果是具体的 MIME 类型 (image/png, image/jpeg),检查是否完全匹配
2025-12-11 17:13:37 +08:00
return file.type === type
2025-12-05 10:17:26 +08:00
}
2025-12-11 17:13:37 +08:00
})
2025-12-05 10:17:26 +08:00
if (!isValidType) {
2025-12-11 17:13:37 +08:00
ElMessage.warning(`上传文件的格式不正确,仅支持:${props.accept}`)
return false
2025-12-05 10:17:26 +08:00
}
// 限制文件大小
if (file.size > props.maxFileSize * 1024 * 1024) {
2025-12-11 17:13:37 +08:00
ElMessage.warning('上传图片不能大于' + props.maxFileSize + 'M')
return false
2025-12-05 10:17:26 +08:00
}
2025-12-11 17:13:37 +08:00
return true
2025-12-05 10:17:26 +08:00
}
/*
* 上传图片
*/
function handleUpload(options: UploadRequestOptions) {
return new Promise((resolve, reject) => {
2025-12-11 17:13:37 +08:00
const file = options.file
2025-12-05 10:17:26 +08:00
2025-12-11 17:13:37 +08:00
const formData = new FormData()
formData.append(props.name, file)
2025-12-05 10:17:26 +08:00
// 处理附加参数
Object.keys(props.data).forEach((key) => {
2025-12-11 17:13:37 +08:00
formData.append(key, props.data[key])
})
2025-12-05 10:17:26 +08:00
FileAPI.upload(formData)
.then((data) => {
2025-12-11 17:13:37 +08:00
resolve(data)
2025-12-05 10:17:26 +08:00
})
.catch((error) => {
2025-12-11 17:13:37 +08:00
reject(error)
})
})
2025-12-05 10:17:26 +08:00
}
/**
* 预览图片
*/
function handlePreview() {
2025-12-11 17:13:37 +08:00
console.log('预览图片,停止冒泡')
2025-12-05 10:17:26 +08:00
}
/**
* 删除图片
*/
function handleDelete() {
2025-12-11 17:13:37 +08:00
modelValue.value = ''
2025-12-05 10:17:26 +08:00
}
/**
* 上传成功回调
*
* @param fileInfo 上传成功后的文件信息
*/
const onSuccess = (fileInfo: FileInfo) => {
2025-12-11 17:13:37 +08:00
ElMessage.success('上传成功')
modelValue.value = fileInfo.url
}
2025-12-05 10:17:26 +08:00
/**
* 上传失败回调
*/
const onError = (error: any) => {
2025-12-11 17:13:37 +08:00
console.log('onError')
ElMessage.error('上传失败: ' + error.message)
}
2025-12-05 10:17:26 +08:00
</script>
<style scoped lang="scss">
:deep(.el-upload--picture-card) {
position: relative;
width: v-bind("props.style.width ?? '150px'");
height: v-bind("props.style.height ?? '150px'");
}
.single-upload {
&__image {
border-radius: 6px;
}
&__delete-btn {
position: absolute;
top: 1px;
right: 1px;
font-size: 16px;
color: #ff7901;
cursor: pointer;
background: #fff;
border-radius: 100%;
:hover {
color: #ff4500;
}
}
}
</style>