Files
sgxt_web/src/components/MyComponents/Upload/index.vue

301 lines
8.3 KiB
Vue
Raw Normal View History

2025-04-12 14:54:02 +08:00
<template>
2025-07-19 20:33:14 +08:00
<div class="form-item-box" :class="props.showBtn ? 'showBtn-upload' : ''" :style="{ width: width }">
2025-10-26 12:25:50 +08:00
<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">
2025-04-12 14:54:02 +08:00
<template #default>
2025-07-19 17:08:53 +08:00
<el-button v-if="props.showBtn" size="small" type="primary">上传文件</el-button>
2025-10-26 12:25:50 +08:00
<el-icon v-else><Plus /></el-icon>
2025-04-12 14:54:02 +08:00
</template>
2025-07-05 12:03:00 +08:00
<template #file="{ file }" v-if="!props.showBtn">
2025-04-12 14:54:02 +08:00
<div v-if="props.isImg">
2025-12-03 16:58:06 +08:00
<img class="el-upload-list__item-thumbnail" :src="file.url || ''" alt="" />
2025-04-12 14:54:02 +08:00
<span class="el-upload-list__item-actions">
2025-10-26 12:25:50 +08:00
<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>
2025-04-12 14:54:02 +08:00
</span>
</div>
<div v-else>
<div class="file-wrap">
2025-07-19 20:33:14 +08:00
<span><svg-icon :icon="getSuffix(file.name)" /></span>
2025-04-12 14:54:02 +08:00
<span class="file-name">{{ file.name }}</span>
</div>
<span class="el-upload-list__item-actions">
2025-07-19 20:33:14 +08:00
<span v-if="!disabled" class="el-upload-list__item-delete" @click="handleDownload(file)">
2025-10-26 12:25:50 +08:00
<el-icon><Download /></el-icon>
2025-04-12 14:54:02 +08:00
</span>
2025-07-19 20:33:14 +08:00
<span v-if="!disabled" class="el-upload-list__item-delete" @click="handleRemove(file, fileList)">
2025-10-26 12:25:50 +08:00
<el-icon><Delete /></el-icon>
2025-04-12 14:54:02 +08:00
</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";
2025-10-26 12:25:50 +08:00
import { ref, defineProps, defineEmits, computed, watch, onMounted, onUnmounted } from "vue";
2025-07-05 12:03:00 +08:00
import { ElMessage } from "element-plus";
2025-04-12 14:54:02 +08:00
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
2025-07-05 12:03:00 +08:00
},
2025-07-10 19:51:22 +08:00
showBtn: {
type: Boolean,
default: false
2025-07-19 17:08:53 +08:00
},
2025-07-19 20:33:14 +08:00
isAll: {
2025-07-19 18:37:07 +08:00
type: Boolean,
2025-10-26 12:25:50 +08:00
default: true //所有类型都可以用这个接口接口返回的是id
2025-07-19 18:37:07 +08:00
}
2025-04-12 14:54:02 +08:00
});
2025-10-26 12:25:50 +08:00
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]") {
2025-12-03 16:58:06 +08:00
// 确保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
};
2025-10-26 12:25:50 +08:00
} else {
2025-12-03 16:58:06 +08:00
return {
url: String(`/mosty-api/mosty-base/minio/image/download/` + el || ''),
id: el
};
2025-10-26 12:25:50 +08:00
}
});
},{ immediate: true,deep:true });
2025-04-12 14:54:02 +08:00
2025-07-19 20:33:14 +08:00
const actionUrl = computed(() => {
if (props.isAll) {
return "/mosty-api/mosty-base/minio/image/upload/id";
} else {
2025-12-03 16:58:06 +08:00
return props.isImg ? "/mosty-api/mosty-base/minio/image/upload/id": "/mosty-api/mosty-base/minio/file/uploadObj";
2025-07-19 18:37:07 +08:00
}
});
2025-04-12 14:54:02 +08:00
//获取后缀
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";
//视频 音频
2025-10-26 12:25:50 +08:00
var videolist = ["mp4","m2v","mkv","rmvb","wmv","avi","flv","mov","m4v"];
2025-04-12 14:54:02 +08:00
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";
};
2025-07-10 19:51:22 +08:00
2025-04-12 14:54:02 +08:00
const handlerSuccess = (res, file) => {
2025-12-03 16:58:06 +08:00
// 设置file.url确保其存在且为字符串类型
file.url = `/mosty-api/mosty-base/minio/image/download/` + res.data;
2025-10-26 12:25:50 +08:00
file.id = res.data;
2025-07-19 17:08:53 +08:00
fileList.value.push(file);
2025-10-26 12:25:50 +08:00
let arr = []
if(props.isImg){
arr = fileList.value.map((el) => el.id)
}else{
arr = fileList.value.map((el) => ({ id:el.id, name:el.name}))
}
2025-07-19 17:08:53 +08:00
emits("update:modelValue", arr);
2025-04-12 14:54:02 +08:00
};
2025-07-05 19:37:28 +08:00
2025-04-12 14:54:02 +08:00
const handleExceed = (files, fileList) => {
ElMessage.warning(`限制,只能上传${props.limit}个文件或图片`);
};
const beforeImgUpload = (file) => {
if (props.isImg) {
let isIMG = false;
2025-10-26 12:25:50 +08:00
if (getSuffix(file.name) === "IMG") isIMG = true;
2025-04-12 14:54:02 +08:00
const isLt5M = file.size / 1024 / 1024 < 5;
2025-10-26 12:25:50 +08:00
if (!isIMG) ElMessage.error("上传图片只能是jpg/png/jpeg/bmp/gif格式!");
if (!isLt5M) ElMessage.error("上传图片大小不能超过 5MB!");
2025-04-12 14:54:02 +08:00
return isIMG && isLt5M;
} else {
return true;
}
};
//查询图片
const handlePictureCardPreview = (file) => {
2025-12-03 16:58:06 +08:00
dialogImageUrl.value = file.url || '';
2025-04-12 14:54:02 +08:00
dialogVisible.value = true;
};
2025-12-10 16:17:17 +08:00
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));
}
2025-04-12 14:54:02 +08:00
const handleDownload = (file) => {
2025-12-10 16:17:17 +08:00
if (file?.response?.data) {
window.open(file.response.data);
} else if (file?.url) {
downloadFile(file.url, file.name);
}
2025-04-12 14:54:02 +08:00
};
2025-12-10 16:17:17 +08:00
// const handleDownload = (file) => {
// window.open(file.response.data);
// };
2025-10-26 12:25:50 +08:00
// 删除文件 触发父组件更新
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);
}
2025-04-12 14:54:02 +08:00
const handleRemove = (file) => {
let index = fileList.value.findIndex(function (item) {
2025-10-26 12:25:50 +08:00
return item.id === file.id;
2025-04-12 14:54:02 +08:00
});
fileList.value.splice(index, 1);
props.modelValue.splice(index, 1);
2025-07-05 19:37:28 +08:00
emits("update:modelValue", props.modelValue);
2025-04-12 14:54:02 +08:00
};
2025-10-26 12:25:50 +08:00
2025-04-12 14:54:02 +08:00
</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;
}
}
2025-08-16 16:54:03 +08:00
</style>