301 lines
8.3 KiB
Vue
301 lines
8.3 KiB
Vue
<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>
|