yintai-company-home-am/components/UploadInput.vue

260 lines
6.8 KiB
Vue

<template>
<div v-loading="isUploading" element-loading-text="上传中..." class="upload-loading-wrapper">
<el-upload ref="uploadRef" v-model:file-list="fileList" action="*" :on-preview="handlePictureCardPreview"
v-bind="options" :http-request="handleHttpRequest" :on-change="handleChange"
:class="isMaxCount ? 'max-count' : ''" style="width: 100%">
<el-icon v-show="['image', 'images'].includes(type)">
<Plus />
</el-icon>
<div v-show="['file', 'files', 'video', 'videos'].includes(type)">
<el-icon class="el-icon--upload">
<upload-filled />
</el-icon>
<div class="el-upload__text">拖拽到这里或者 <em>点击上传</em></div>
</div>
</el-upload>
<el-button v-show="showUploadButton" type="primary" @click="handleUpload">上传</el-button>
</div>
</template>
<script lang="ts" setup>
import {
computed,
ref,
createVNode,
render,
} from "vue";
import { Plus } from "@element-plus/icons-vue";
import type { UploadFile, UploadFiles, UploadUserFile } from "element-plus";
import { ElImageViewer } from "element-plus";
import utils from "lib/utils";
interface Props {
value: {
data?: FormData;
uid?: string;
url: string;
name?: string;
id?: string;
}[];
uploadFile: (file: FormData) => Promise<any>;
type?: "image" | "images" | "file" | "files" | "video" | "videos";
transport?: string;
accept?: string;
}
const props = withDefaults(defineProps<Props>(), {
type: "image",
transport: "",
accept: "*",
value: [] as any,
uploadFile: () => Promise.resolve({ url: "" }),
});
const showUploadButton = computed(() => {
return options.value.autoUpload === false;
});
const emit = defineEmits(["updateValue"])
const fileList = ref<UploadUserFile[]>([...props.value] as any);
const uploadRef = ref<any>(null);
const options = computed(() => {
switch (props.type) {
case "image":
return {
limit: 1,
listType: "picture-card" as const, // 明确指定为字面量类型
accept: "image/*",
};
case "images":
return {
limit: 9,
listType: "picture-card" as const, // 明确指定为字面量类型
accept: "image/*",
multiple: true,
autoUpload: false,
};
case "video":
return {
limit: 1,
listType: "text" as const, // 明确指定为字面量类型
accept: "video/*",
};
case "videos":
return {
limit: 9,
listType: "text" as const, // 明确指定为字面量类型
accept: "video/*",
multiple: true,
autoUpload: false,
};
case "file":
return {
limit: 1,
accept: props.accept,
drag: true,
listType: "text" as const, // 为文件类型添加合适的 listType
};
case "files":
return {
limit: 10,
accept: props.accept,
drag: true,
listType: "text" as const, // 为文件类型添加合适的 listType
multiple: true,
autoUpload: false,
};
default:
return {
limit: 1,
listType: "picture-card" as const,
accept: props.accept,
};
}
});
const isMaxCount = computed(() => {
return fileList.value.length >= options.value.limit;
});
const isUploading = ref(false);
interface UploadRequestOptions {
file: File & { uid?: number; raw?: File, url?: string };
filename: string;
onSuccess: (res: any) => void;
onError: (err: any) => void;
}
const handleHttpRequest = async (options: UploadRequestOptions) => {
isUploading.value = true;
const formData = new FormData();
const rawFile = options.file?.raw ?? options.file;
formData.append(options.filename || "file", rawFile, rawFile.name);
try {
const res = await props.uploadFile(formData);
const url = res?.data?.url ?? res?.url;
if (url) {
options.file.url = url;
}
options.onSuccess(res);
isUploading.value = false;
} catch (err) {
console.log('err', err)
options.onError(err as any);
isUploading.value = false;
}
};
const handleRemove = async (uploadFile: UploadFile) => {
// props.onFileChange(uploadFile);
// emit("onUpdateValue", fileList.value.filter((f: any) => f.uid !== uploadFile.uid));
};
const handleChange = (file: any, list: any) => {
const updateList = fileList.value
.filter((f: any) => f.status === 'success')
.map((f: any) => {
return {
url: f.raw?.url,
uid: f.raw?.uid,
name: f.raw?.name,
id: f.raw?.id,
}
})
emit("updateValue", updateList);
}
const handlePictureCardPreview = (file: any) => {
const imageUrl = file.url;
if (["file", "files"].includes(props.type)) {
// 下载文件
utils.downloadfile(imageUrl, imageUrl, "image/png");
return;
}
// 如果是视频,打开标签页
if (["video", "videos"].includes(props.type)) {
window.open(imageUrl, "_blank");
return;
}
if (!imageUrl) return;
// 创建一个用于挂载 el-image-viewer 的容器
const container = document.createElement("div");
const parentNode =
document.querySelector(props.transport || "body") || document.body;
parentNode.appendChild(container);
// 创建并挂载 el-image-viewer 节点
const vnode = createVNode(ElImageViewer as any, {
urlList: [imageUrl],
onClose: () => {
render(null, container);
parentNode.removeChild(container);
},
});
render(vnode, container);
};
const handleUpload = () => {
uploadRef.value.submit();
};
</script>
<style lang="less" scoped>
.upload-loading-wrapper {
position: relative;
min-height: 100px;
}
:deep(.el-upload--picture-card) {
--el-upload-picture-card-size: 100px;
}
:deep(.el-upload-list--picture-card) {
--el-upload-list-picture-card-size: 100px;
}
:deep(.el-icon--close-tip) {
display: none !important;
}
// 设置上传中进度条的大小
:deep(.el-upload-list__item.is-uploading .el-progress) {
.el-progress-circle {
transform: scale(0.7);
}
}
.max-count {
:deep(.el-upload--picture-card) {
display: none;
}
:deep(.el-upload) {
display: none;
}
}
:deep(.el-upload.el-upload--text) {
border: 1px solid #dcdfe6;
border-radius: 4px;
width: 100%;
min-height: 100px;
&>div {
display: flex;
flex-direction: column;
align-items: center;
.el-icon.el-icon--upload {
font-size: 20px;
}
}
}
</style>