2024-08-30 18:01:34 +08:00
|
|
|
<template>
|
|
|
|
<div class="simpleUploadDiv">
|
2025-01-03 11:36:46 +08:00
|
|
|
<a-progress v-if="uploading" type="circle" :percent="percent" />
|
2024-08-30 18:01:34 +08:00
|
|
|
<a-image
|
2025-01-03 11:36:46 +08:00
|
|
|
height="80%"
|
|
|
|
v-else
|
|
|
|
:src="minioBaseUrl + modelValue"
|
|
|
|
alt="avatar"
|
|
|
|
/>
|
2024-11-05 14:30:18 +08:00
|
|
|
|
2024-08-30 18:01:34 +08:00
|
|
|
<a-button class="btn-success" @click="selectFile">{{ btnLabel }}</a-button>
|
2025-01-03 11:36:46 +08:00
|
|
|
<input :id="id" type="file" style="display: none" ref="fileInput" />
|
2024-08-30 18:01:34 +08:00
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2025-01-03 11:36:46 +08:00
|
|
|
import { message } from "ant-design-vue";
|
|
|
|
import { onMounted, onUnmounted, ref } from "vue";
|
|
|
|
import {
|
|
|
|
generateSimpleObjectName,
|
|
|
|
getResignedObjectUrl,
|
|
|
|
} from "@/utils/minioUtil";
|
|
|
|
import axios, { CancelTokenSource } from "axios";
|
|
|
|
import { convertFileSizeToStr } from "@/utils/index.ts";
|
2024-08-30 18:01:34 +08:00
|
|
|
|
2025-01-03 11:36:46 +08:00
|
|
|
const minioBaseUrl = __APP_ENV.VITE_APP_MINIO_URL;
|
2024-08-30 18:01:34 +08:00
|
|
|
|
2025-01-03 11:36:46 +08:00
|
|
|
const modelValue = defineModel<string>("value");
|
|
|
|
const props = withDefaults(
|
|
|
|
defineProps<{
|
|
|
|
parentDir?: string;
|
|
|
|
allowedExtensions?: string[];
|
|
|
|
maxSize?: number;
|
|
|
|
width?: string | number;
|
|
|
|
height?: string | number;
|
|
|
|
btnLabel?: string;
|
|
|
|
id: string;
|
|
|
|
}>(),
|
|
|
|
{
|
|
|
|
parentDir: "",
|
|
|
|
allowedExtensions: () => ["jpg", "jpeg", "png", "gif"],
|
|
|
|
maxSize: 1024 * 1024 * 4,
|
|
|
|
width: "150px",
|
|
|
|
height: "150px",
|
|
|
|
btnLabel: "选择图片",
|
|
|
|
id: "myFileInput",
|
|
|
|
}
|
|
|
|
);
|
2024-08-30 18:01:34 +08:00
|
|
|
|
2025-01-03 11:36:46 +08:00
|
|
|
const uploading = ref(false);
|
|
|
|
const percent = ref(0);
|
|
|
|
let cancelToken: CancelTokenSource | null = null;
|
|
|
|
const uploadUrl = ref();
|
2024-09-11 09:22:00 +08:00
|
|
|
const fileInput = ref(null);
|
2024-08-30 18:01:34 +08:00
|
|
|
|
|
|
|
const selectFile = () => {
|
2025-01-03 11:36:46 +08:00
|
|
|
document.getElementById(props.id)?.click();
|
|
|
|
};
|
2024-08-30 18:01:34 +08:00
|
|
|
|
|
|
|
async function inputFileListener(this: HTMLInputElement) {
|
|
|
|
const selectedFile: File = this.files?.[0] as File;
|
2025-01-03 11:36:46 +08:00
|
|
|
const fileExtension = selectedFile.name
|
|
|
|
?.split(".")
|
|
|
|
.pop()
|
|
|
|
.toLowerCase() as string;
|
|
|
|
|
2024-08-30 18:01:34 +08:00
|
|
|
if (!props.allowedExtensions.includes(fileExtension)) {
|
2025-01-03 11:36:46 +08:00
|
|
|
return message.error(
|
|
|
|
`错误:不支持的文件格式,目前支持:【${props.allowedExtensions}】`
|
|
|
|
);
|
2024-08-30 18:01:34 +08:00
|
|
|
}
|
|
|
|
const isMax = selectedFile.size > props.maxSize;
|
|
|
|
if (isMax) {
|
2025-01-03 11:36:46 +08:00
|
|
|
return message.error(
|
|
|
|
`文件大小超出限制,最大支持:【${convertFileSizeToStr(props.maxSize)}】`
|
|
|
|
);
|
2024-08-30 18:01:34 +08:00
|
|
|
}
|
|
|
|
cancelToken?.cancel();
|
|
|
|
percent.value = 0;
|
|
|
|
uploading.value = true;
|
|
|
|
|
2025-01-03 11:36:46 +08:00
|
|
|
const objectName = generateSimpleObjectName(
|
|
|
|
selectedFile.name,
|
|
|
|
props.parentDir
|
|
|
|
);
|
|
|
|
|
|
|
|
uploadUrl.value = await getResignedObjectUrl(
|
|
|
|
__APP_ENV.VITE_APP_MINIO_BUCKET,
|
|
|
|
objectName
|
|
|
|
);
|
|
|
|
cancelToken = axios.CancelToken.source();
|
2024-09-11 09:22:00 +08:00
|
|
|
await axios.put(uploadUrl.value, selectedFile, {
|
2024-08-30 18:01:34 +08:00
|
|
|
cancelToken: cancelToken.token,
|
|
|
|
onUploadProgress: (progressEvent) => {
|
2025-01-03 11:36:46 +08:00
|
|
|
percent.value =
|
|
|
|
((progressEvent.loaded / (progressEvent.total as number)) * 100) | 0;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
modelValue.value = "/" + __APP_ENV.VITE_APP_MINIO_BUCKET + objectName;
|
2024-08-30 18:01:34 +08:00
|
|
|
uploading.value = false;
|
|
|
|
}
|
|
|
|
|
2025-01-03 11:36:46 +08:00
|
|
|
const fileDelete = () => {
|
|
|
|
uploadUrl.value = "";
|
|
|
|
fileInput.value.value = "";
|
|
|
|
modelValue.value = "";
|
|
|
|
};
|
2024-08-30 18:01:34 +08:00
|
|
|
onMounted(() => {
|
2025-01-03 11:36:46 +08:00
|
|
|
document
|
|
|
|
.getElementById(props.id)
|
|
|
|
?.addEventListener("change", inputFileListener);
|
|
|
|
});
|
2024-08-30 18:01:34 +08:00
|
|
|
|
|
|
|
onUnmounted(() => {
|
2025-01-03 11:36:46 +08:00
|
|
|
document
|
|
|
|
.getElementById(props.id)
|
|
|
|
?.removeEventListener("change", inputFileListener);
|
|
|
|
});
|
|
|
|
defineExpose({ fileDelete });
|
2024-08-30 18:01:34 +08:00
|
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
.simpleUploadDiv {
|
|
|
|
width: v-bind(width);
|
|
|
|
height: v-bind(height);
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
}
|
|
|
|
</style>
|