Files
sgxt_web/src/components/MyComponents/Upload/index.vue
2025-12-10 16:17:17 +08:00

301 lines
8.3 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="form-item-box" :class="props.showBtn ? 'showBtn-upload' : ''" :style="{ width: width }">
<el-upload
v-bind="$attrs"
:headers="headers"
:multiple="false"
class="avatar-uploader"
:limit="props.limit"
:action="actionUrl"
:list-type="props.showBtn ? '' : 'picture-card'"
:file-list="fileList"
show-file-list
:before-remove="beforeRemove"
:on-exceed="handleExceed"
:on-success="handlerSuccess"
:before-upload="beforeImgUpload">
<template #default>
<el-button v-if="props.showBtn" size="small" type="primary">上传文件</el-button>
<el-icon v-else><Plus /></el-icon>
</template>
<template #file="{ file }" v-if="!props.showBtn">
<div v-if="props.isImg">
<img class="el-upload-list__item-thumbnail" :src="file.url || ''" alt="" />
<span class="el-upload-list__item-actions">
<span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)">
<el-icon> <zoom-in /></el-icon>
</span>
<span v-if="!disabled" class="el-upload-list__item-delete" @click="handleRemove(file, fileList)">
<el-icon><Delete /></el-icon>
</span>
</span>
</div>
<div v-else>
<div class="file-wrap">
<span><svg-icon :icon="getSuffix(file.name)" /></span>
<span class="file-name">{{ file.name }}</span>
</div>
<span class="el-upload-list__item-actions">
<span v-if="!disabled" class="el-upload-list__item-delete" @click="handleDownload(file)">
<el-icon><Download /></el-icon>
</span>
<span v-if="!disabled" class="el-upload-list__item-delete" @click="handleRemove(file, fileList)">
<el-icon><Delete /></el-icon>
</span>
</span>
</div>
</template>
</el-upload>
<el-dialog v-model="dialogVisible">
<img style="width: 100%" :src="dialogImageUrl" alt="" />
</el-dialog>
</div>
</template>
<script setup>
import { COMPONENT_WIDTH } from "@/constant";
import { ref, defineProps, defineEmits, computed, watch, onMounted, onUnmounted } from "vue";
import { ElMessage } from "element-plus";
import { useStore } from "vuex";
const props = defineProps({
//获取组件传值
modelValue: {
type: Array,
default: []
},
limit: {
type: Number,
default: 1
},
isImg: {
type: Boolean,
default: true
},
width: {
default: COMPONENT_WIDTH,
type: String
},
showBtn: {
type: Boolean,
default: false
},
isAll: {
type: Boolean,
default: true //所有类型都可以用这个接口接口返回的是id
}
});
const emits = defineEmits(["update:modelValue"]);
const store = useStore();
const dialogImageUrl = ref("");
const dialogVisible = ref(false);
const disabled = ref(false);
const headers = ref({
Authorization: store.getters.token
});
const fileList = ref([]);
watch(() => props.modelValue,(val) => {
let arr = val ? (Array.isArray(val) ? val :[val]): [];
if(arr.length == 0 ) return fileList.value = [];
fileList.value = arr.map((el) => {
if (Object.prototype.toString.call(el) === "[object Object]") {
// 确保file.url始终是字符串URL
const fileUrl = props.isAll ? `/mosty-api/mosty-base/minio/image/download/` + el.id : el.url;
return {
url: String(fileUrl || ''),
name: el.name || '',
id: el
};
} else {
return {
url: String(`/mosty-api/mosty-base/minio/image/download/` + el || ''),
id: el
};
}
});
},{ immediate: true,deep:true });
const actionUrl = computed(() => {
if (props.isAll) {
return "/mosty-api/mosty-base/minio/image/upload/id";
} else {
return props.isImg ? "/mosty-api/mosty-base/minio/image/upload/id": "/mosty-api/mosty-base/minio/file/uploadObj";
}
});
//获取后缀
const getSuffix = (fileName) => {
let suffix = "";
try {
suffix = fileName.substr(fileName.lastIndexOf(".") + 1, 4); //截取最后一个点号后4个字符
} catch (err) {
suffix = "";
return "OTHER";
}
// fileName无后缀返回 false
if (!suffix) return "";
// 图片格式
var imglist = ["png", "jpg", "jpeg", "bmp", "gif"];
if (imglist.includes(suffix)) return "IMG";
//txt
if (suffix === "txt") return "TXT";
//excel XLS
const excelist = ["xls", "xlsx"];
if (excelist.includes(suffix)) return "XLS";
// 匹配 word
var wordlist = ["doc", "docx"];
if (wordlist.includes(suffix)) return "DOC";
//pdf
if (suffix === "pdf") return "PDF";
//视频 音频
var videolist = ["mp4","m2v","mkv","rmvb","wmv","avi","flv","mov","m4v"];
if (videolist.includes(suffix)) return "VIDEO";
var musiclist = ["mp3", "wav", "wmv"];
if (musiclist.includes(suffix)) return "MUSIC";
var pptlist = ["ppt", "pptx"];
if (pptlist.includes(suffix)) return "PPT";
//压缩包
var yslist = ["7z", "rar", "zip", "apz", "ar", "hpk", "hyp", "hbc2"];
if (yslist.includes(suffix)) return "YS";
//否则返回other
return "OTHER";
};
const handlerSuccess = (res, file) => {
// 设置file.url确保其存在且为字符串类型
file.url = `/mosty-api/mosty-base/minio/image/download/` + res.data;
file.id = res.data;
fileList.value.push(file);
let arr = []
if(props.isImg){
arr = fileList.value.map((el) => el.id)
}else{
arr = fileList.value.map((el) => ({ id:el.id, name:el.name}))
}
emits("update:modelValue", arr);
};
const handleExceed = (files, fileList) => {
ElMessage.warning(`限制,只能上传${props.limit}个文件或图片`);
};
const beforeImgUpload = (file) => {
if (props.isImg) {
let isIMG = false;
if (getSuffix(file.name) === "IMG") isIMG = true;
const isLt5M = file.size / 1024 / 1024 < 5;
if (!isIMG) ElMessage.error("上传图片只能是jpg/png/jpeg/bmp/gif格式!");
if (!isLt5M) ElMessage.error("上传图片大小不能超过 5MB!");
return isIMG && isLt5M;
} else {
return true;
}
};
//查询图片
const handlePictureCardPreview = (file) => {
dialogImageUrl.value = file.url || '';
dialogVisible.value = true;
};
function downloadFile(url, filename) {
fetch(url)
.then((response) => response.blob())
.then((blob) => {
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
})
.catch((error) => console.error("下载失败:", error));
}
const handleDownload = (file) => {
if (file?.response?.data) {
window.open(file.response.data);
} else if (file?.url) {
downloadFile(file.url, file.name);
}
};
// const handleDownload = (file) => {
// window.open(file.response.data);
// };
// 删除文件 触发父组件更新
const beforeRemove = (file) => {
let index = fileList.value.findIndex(function (item) {
return item.id === file.id;
});
props.modelValue.splice(index, 1);
emits("update:modelValue", props.modelValue);
}
const handleRemove = (file) => {
let index = fileList.value.findIndex(function (item) {
return item.id === file.id;
});
fileList.value.splice(index, 1);
props.modelValue.splice(index, 1);
emits("update:modelValue", props.modelValue);
};
</script>
<style lang="scss" scoped>
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409eff;
}
.el-icon.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
text-align: center;
}
.avatar {
width: 178px;
height: 178px;
display: block;
}
.file-wrap {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
::v-deep .svg-icon {
font-size: 48px;
margin: 28px 0 2px 0;
}
.file-name {
width: 88%;
font-size: 14px;
line-height: 22px;
text-align: center;
overflow: hidden;
/*将对象作为弹性伸缩盒子模型显示*/
display: -webkit-box;
/*设置子元素排列方式*/
-webkit-box-orient: vertical;
/*设置显示的行数,多出的部分会显示为...*/
-webkit-line-clamp: 2;
}
}
</style>