111 lines
3.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="file-uploader">
<el-upload
class="file-uploader__upload"
:http-request="customUploadRequest"
:on-success="handleUploadSuccess"
:on-remove="handleRemove"
:file-list="fileList"
list-type="picture-card"
:limit="limit"
:show-file-list="true"
:auto-upload="true"
:disabled="readonly"
:accept="accept"
@preview="handlePreview"
>
<el-icon v-if="fileList.length < limit"><Plus /></el-icon>
</el-upload>
<el-image-viewer v-if="previewShow" :url-list="previewList" :initial-index="previewIndex" @close="previewShow = false" />
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { Plus } from '@element-plus/icons-vue';
import { CommonUpload } from '@/apis/index';
// 1. props & emit
const props = defineProps({
modelValue: { type: [Array, String], default: () => [] },
ossUrl: { type: String, default: 'http://gov-cloud.oss-cn-chengdu.aliyuncs.com/' },
limit: { type: Number, default: 5 },
accept: { type: String, default: 'image/*' },
readonly: { type: Boolean, default: false },
});
const emit = defineEmits(['update:modelValue']);
// 2. 中间层 computed统一成数组写时根据 limit 决定发出数组还是字符串
const selectedFiles = computed({
get() {
// 回显:如果父传字符串且 limit===1就把它当成长度 1 数组
if (props.limit === 1 && typeof props.modelValue === 'string' && props.modelValue) {
return [props.modelValue];
}
// 其他情况,确保数组
return Array.isArray(props.modelValue) ? props.modelValue : [];
},
set(val) {
// 内部操作后:如果单文件场景,传字符串;多文件场景,传数组
if (props.limit === 1) {
emit('update:modelValue', val.length ? val[0] : '');
} else {
emit('update:modelValue', val);
}
},
});
// 3. fileList & previewList 都基于 selectedFiles
const fileList = computed(() =>
selectedFiles.value.map((path, idx) => ({
name: `file_${idx}`,
url: props.ossUrl + path,
uid: `${idx}`,
}))
);
const previewShow = ref(false);
const previewIndex = ref(0);
const previewList = computed(() => fileList.value.map((f) => f.url));
// 4. 上传 & 移除
const customUploadRequest = async ({ file, onSuccess, onError }) => {
const formData = new FormData();
formData.append('file', file);
try {
const res = await CommonUpload(formData);
onSuccess(res, file);
} catch (err) {
onError(err);
}
};
function handleUploadSuccess(res) {
const relative = res.data?.url;
if (!relative) return;
// 推入中间层
selectedFiles.value = [...selectedFiles.value, relative];
}
function handleRemove(file) {
const rel = file.url.replace(props.ossUrl, '');
selectedFiles.value = selectedFiles.value.filter((p) => p !== rel);
}
// 5. 预览
function handlePreview(file) {
const idx = fileList.value.findIndex((item) => item.uid === file.uid);
if (idx >= 0) {
previewIndex.value = idx;
previewShow.value = true;
}
}
</script>
<style scoped>
.file-uploader {
display: flex;
flex-wrap: wrap;
}
.file-uploader__upload {
margin-right: 16px;
}
</style>