172 lines
5.6 KiB
Vue

<template>
<div class="clearfix">
<a-upload list-type="picture-card" :multiple="multiple" v-model:file-list="fileList" @preview="handlePreview"
@change="handleChange" :customRequest="handleCustomRequest" :beforeUpload="beforeUpload">
<div v-if="fileList.length < fileNumber">
<plus-outlined />
<div class="ant-upload-text">上传</div>
</div>
</a-upload>
<a-modal :open="previewVisible" :footer="null" @cancel="handleCancel">
<img alt="example" style="width: 100%" :src="previewImage" />
</a-modal>
</div>
</template>
<script setup>
import { ref, watch, onMounted } from 'vue';
import { PlusOutlined } from '@ant-design/icons-vue';
import { UploadOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import { config } from '@/config'
import apis from '@/apis'
const previewVisible = ref(false)
const previewImage = ref('')
const props = defineProps({
modelValue: { type: Array, default: () => [] },
headers: { type: Object, default: () => ({}) },
multiple: { type: Boolean, default: false },
maxSize: { type: Number, default: 10 }, // MB
acceptTypes: { type: String, default: '*' },
listType: { type: String, default: 'text' },
disabled: { type: Boolean, default: false },
uploadText: { type: String },
fileNumber: { type: Number, default: 6 }
});
const emit = defineEmits(['update:modelValue', 'uploadSuccess', 'uploadError']);
const uploadUrl = config('http.apiBasic') + '/api/v1/upload'
const fileList = ref([]);
// 初始化文件列表
onMounted(() => {
console.log(' props.modelValue', props.modelValue)
fileList.value = props.modelValue.map(url => ({
uid: `preview-${Date.now()}-${Math.random()}`,
name: url.substring(url.lastIndexOf('/') + 1),
status: 'done',
url: url
}));
});
// 文件上传前校验
const beforeUpload = (file) => {
const isValidType = props.acceptTypes === '*' ||
props.acceptTypes.split(',').some(type => file.name.endsWith(type.replace('*', '')));
const isValidSize = file.size / 1024 / 1024 < props.maxSize;
if (!isValidType) {
message.error(`仅支持 ${props.acceptTypes} 格式文件`);
return false;
}
if (!isValidSize) {
message.error(`文件大小不能超过 ${props.maxSize}MB`);
return false;
}
return true;
};
const handleCancel = () => {
previewVisible.value = false;
};
const getBase64 = (file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
}
const handlePreview = async (file) => {
if (!file.url && !file.preview) {
file.preview = await getBase64(file.originFileObj)
}
previewImage.value = file.url || file.preview;
previewVisible.value = true;
};
// 修改handleChange方法
const handleChange = ({ file, fileList: updatedList }) => {
// 处理上传成功的情况
if (file.status === 'done') {
const response = file.response;
if (response && response.url) {
// 找到当前文件项并更新URL
const targetFile = updatedList.find(f => f.uid === file.uid);
if (targetFile) {
targetFile.url = response.url;
message.success(`${file.name} 上传成功`);
}
// 更新外部绑定值
const urls = updatedList
.filter(item => item.status === 'done')
.map(item => item.url);
emit('update:modelValue', urls);
}
} else if (file.status === 'error') {
message.error(`${file.name} 上传失败`);
}
};
// 修复自定义上传方法
const handleCustomRequest = async (options) => {
const { file, onProgress, onSuccess, onError } = options;
try {
const formData = new FormData();
formData.append('file', file);
const { data } = await apis.common.uploadImg(formData);
const fullUrl = config('http.apiBasic') + data;
// 正确构造文件对象
onSuccess({
uid: file.uid,
name: file.name,
status: 'done',
url: fullUrl,
response: { url: fullUrl } // 确保response包含URL
}, file);
// 触发成功事件
emit('uploadSuccess', data);
} catch (err) {
onError(err);
message.error('上传失败');
emit('uploadError', err);
}
};
// 同步外部v-model变化
watch(() => props.modelValue, (newVal) => {
// fileList.value=[...newVal]
// 仅同步已完成的项目
const doneFiles = fileList.value.filter(f => f.status === 'done');
const doneUrls = doneFiles.map(f => f.url);
// 避免循环更新
if (JSON.stringify(newVal) !== JSON.stringify(doneUrls)) {
fileList.value = [
...newVal.map(url => {
return {
uid: `preview-${Date.now()}-${Math.random()}`,
name: url.substring(url.lastIndexOf('/') + 1),
status: 'done',
url: url
}
}),
...fileList.value.filter(f => f.status !== 'done')
];
}
}, { deep: true });
</script>
<style>
/* you can make up upload button and sample style by using stylesheets */
.ant-upload-select-picture-card i {
font-size: 32px;
color: #999;
}
.ant-upload-select-picture-card .ant-upload-text {
margin-top: 8px;
color: #666;
}
</style>