2025-06-27 09:28:06 +08:00
|
|
|
<template>
|
2025-09-05 16:41:50 +08:00
|
|
|
<Upload
|
|
|
|
|
:customRequest="customRequest"
|
|
|
|
|
listType="picture-card"
|
2025-06-27 09:28:06 +08:00
|
|
|
action="/"
|
2025-09-05 16:41:50 +08:00
|
|
|
:maxCount="limit"
|
2025-06-27 09:28:06 +08:00
|
|
|
:fileList="fileList"
|
2025-09-05 16:41:50 +08:00
|
|
|
:showUploadList="{ showPreviewIcon: true, showRemoveIcon: true }"
|
2025-06-27 09:28:06 +08:00
|
|
|
@change="onChange"
|
2025-09-05 16:41:50 +08:00
|
|
|
@preview="handlePreview"
|
2025-06-27 09:28:06 +08:00
|
|
|
/>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { ref } from 'vue';
|
2025-09-05 16:41:50 +08:00
|
|
|
import { Upload, message } from 'ant-design-vue';
|
2025-06-27 09:28:06 +08:00
|
|
|
import { fetchImageUploadFile } from '@/api/all';
|
|
|
|
|
import axios from 'axios';
|
|
|
|
|
|
|
|
|
|
const fileList = ref([]);
|
|
|
|
|
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
modelValue: {
|
|
|
|
|
type: [Array, String],
|
|
|
|
|
default: '',
|
|
|
|
|
},
|
|
|
|
|
limit: {
|
|
|
|
|
type: Number,
|
|
|
|
|
default: 0, // 0 表示不限制
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const emit = defineEmits(['update:modelValue']);
|
|
|
|
|
|
|
|
|
|
const handleSuccess = (fileItem) => {
|
|
|
|
|
let response = fileItem.response;
|
|
|
|
|
response = JSON.parse(response);
|
|
|
|
|
if (response && response.data.file_url) {
|
|
|
|
|
if (props.limit === 1) {
|
|
|
|
|
emit('update:modelValue', response.data.file_url);
|
|
|
|
|
} else {
|
|
|
|
|
emit('update:modelValue', [...props.modelValue, response.data.file_url]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
watch(
|
|
|
|
|
() => props.modelValue,
|
|
|
|
|
async (value) => {
|
|
|
|
|
console.log(value, 'value');
|
|
|
|
|
if (value) {
|
|
|
|
|
fileList.value =
|
|
|
|
|
props.limit == 1
|
|
|
|
|
? [
|
|
|
|
|
{
|
|
|
|
|
name: '',
|
|
|
|
|
url: props.modelValue as string,
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
: (props.modelValue as string[]).map((item) => {
|
|
|
|
|
return {
|
|
|
|
|
name: '',
|
|
|
|
|
url: item,
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
fileList.value = [];
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{ once: true },
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let previousFileListLength = 0;
|
2025-09-05 16:41:50 +08:00
|
|
|
const handlePreview = (file) => {
|
|
|
|
|
console.log('Preview file:', file);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const onChange = (info) => {
|
|
|
|
|
const { fileList } = info;
|
|
|
|
|
|
|
|
|
|
// 如果删除了文件
|
2025-06-27 09:28:06 +08:00
|
|
|
if (fileList.length < previousFileListLength) {
|
|
|
|
|
if (props.limit === 1) {
|
|
|
|
|
if (fileList.length === 0) {
|
|
|
|
|
emit('update:modelValue', '');
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (fileList.length === 0) {
|
|
|
|
|
emit('update:modelValue', []);
|
|
|
|
|
} else {
|
|
|
|
|
let image_data = fileList.map((item) => item.url);
|
|
|
|
|
emit('update:modelValue', image_data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-05 16:41:50 +08:00
|
|
|
|
|
|
|
|
// 处理上传成功的文件
|
|
|
|
|
if (info.file.status === 'done' && info.file.response) {
|
|
|
|
|
handleSuccess(info.file);
|
|
|
|
|
} else if (info.file.status === 'error') {
|
|
|
|
|
handleError(info.file.error);
|
|
|
|
|
}
|
2025-06-27 09:28:06 +08:00
|
|
|
|
|
|
|
|
previousFileListLength = fileList.length;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const beforeUpload = (file, files) => {
|
|
|
|
|
if (props.limit > 0 && files.length >= props.limit) {
|
2025-09-05 16:41:50 +08:00
|
|
|
message.warning(`最多只能上传 ${props.limit} 张图片`);
|
2025-06-27 09:28:06 +08:00
|
|
|
return false; // 阻止上传
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleError = (error) => {
|
2025-09-05 16:41:50 +08:00
|
|
|
message.error('上传失败');
|
2025-06-27 09:28:06 +08:00
|
|
|
console.error(error);
|
|
|
|
|
};
|
|
|
|
|
|
2025-06-27 16:26:03 +08:00
|
|
|
const customRequest = async (option) => {
|
2025-06-27 09:28:06 +08:00
|
|
|
const { onProgress, onError, onSuccess, fileItem, name } = option;
|
2025-06-27 16:26:03 +08:00
|
|
|
try {
|
|
|
|
|
// 1. 获取预签名上传URL
|
|
|
|
|
const response = await fetchImageUploadFile({ suffix: getFileExtension(fileItem.file.name) });
|
|
|
|
|
const preSignedUrl = response?.data?.upload_url;
|
|
|
|
|
|
|
|
|
|
if (!preSignedUrl) {
|
|
|
|
|
throw new Error('未能获取有效的预签名上传地址');
|
2025-06-27 09:28:06 +08:00
|
|
|
}
|
2025-06-27 16:26:03 +08:00
|
|
|
console.log('preSignedUrl', preSignedUrl);
|
|
|
|
|
// 2. 使用预签名URL上传文件
|
|
|
|
|
const blob = new Blob([fileItem.file], { type: fileItem.file.type });
|
|
|
|
|
await axios.put(preSignedUrl, blob, {
|
|
|
|
|
headers: { 'Content-Type': fileItem.file.type },
|
|
|
|
|
});
|
2025-06-27 09:28:06 +08:00
|
|
|
|
2025-06-27 16:26:03 +08:00
|
|
|
onSuccess(JSON.stringify(response));
|
|
|
|
|
} catch (error) {
|
|
|
|
|
onError(error);
|
|
|
|
|
}
|
2025-06-27 09:28:06 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function getFileExtension(filename: string): string {
|
|
|
|
|
const match = filename.match(/\.([^.]+)$/);
|
|
|
|
|
return match ? match[1].toLowerCase() : '';
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
/* 添加一些样式 */
|
|
|
|
|
</style>
|