You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

420 lines
10 KiB

<script lang="ts">
import { type PropType, defineComponent, ref, watch } from 'vue';
import { ElMessage, type UploadUserFile } from 'element-plus';
import { Plus, ZoomIn, Refresh, Delete } from '@element-plus/icons-vue';
import { imagesAgent } from '@/utils';
import { useFileUpload } from './useFileUpload';
const { fileUpload } = useFileUpload();
export default defineComponent({
name: 'upload-image',
components: { Plus, ZoomIn, Refresh, Delete },
props: {
action: {
type: String,
default: '#'
},
limit: {
type: Number,
default: 0
},
accept: {
type: String,
default: ''
},
showPer: {
type: Boolean,
default: true
},
multiple: {
type: Boolean,
default: false
},
bucketName: {
type: String,
default: 'marker'
},
// fileType: () => [],
showFileList: {
type: Boolean,
default: false
},
autoUpload: {
type: Boolean,
default: true
},
fileSize: {
type: Number,
default: 20
},
isRefreshUpload: {
// 允许重新上传
type: Boolean,
default: false
},
fileList: {
type: Array as PropType<UploadUserFile[]>,
default: () => []
}
},
setup(props: any, { emit }) {
const upload = ref();
const percentage = ref(0);
const uploadImg = ref();
const previewList = ref<any[]>([]);
const loadFiles = ref<any[]>([]);
const isResetImg = ref(false);
const resetFile = ref<any>({});
watch(
() => props.fileList,
(val) => {
if (val) {
loadFiles.value = [];
previewList.value = [];
val.forEach((element: any) => {
loadFiles.value.push({
...element,
url: imagesAgent(element.url)
});
previewList.value.push(imagesAgent(element.url));
});
}
},
{
deep: true
}
);
// 清空文件列表的方法,供父组件使用
const clearFiles = () => {
upload.value?.clearFiles();
};
// 检查图片
const beforeUpload = (file: any) => {
if (props.accept !== '*') {
const fileSuffix = file.name.substring(file.name.lastIndexOf('.') + 1);
const whiteList = props.accept;
if (whiteList.indexOf(fileSuffix) === -1) {
ElMessage({
type: 'error',
message: `上传文件只能是${props.accept}格式`
});
return false;
}
}
const isLt2M = file.size / 1024 / 1024 <= props.fileSize;
if (!isLt2M) {
ElMessage({
type: 'error',
message: `上传文件不能大于${props.fileSize}M`
});
return false;
}
if (props.fileList.length > props.limit) {
ElMessage({
type: 'error',
message: `上传文件数不能超过${props.limit}`
});
return false;
}
return true;
};
const handleFileUpload = (fileObject: any): any => {
const params = {
bucketName: props.bucketName,
file: fileObject.file
};
fileUpload(params, (res: any) => {
percentage.value = Math.floor(res) || 0;
if (res.result) {
// 将图片地址拼接传给父组件
res.result.name = res.result.fileName;
loadFiles.value.push(res.result);
previewList.value.push(imagesAgent(res.result.url));
if (isResetImg.value && loadFiles.value && Array.isArray(loadFiles.value)) {
let indexImg = 0;
const lastImg = loadFiles.value[loadFiles.value.length - 1];
loadFiles.value.forEach((item: any, index: any) => {
if (item.ossId === resetFile.value.ossId) {
indexImg = index;
item.fileName = lastImg.fileName;
}
});
loadFiles.value = _deleteData(indexImg, loadFiles.value);
previewList.value = _deleteData(indexImg, previewList.value);
}
emit('uploadSuccess', loadFiles.value);
}
});
};
const onRemove = (file: any) => {
emit('fileRemove', file);
};
const onPreview = (file: any) => {
emit('onPreview', file);
};
const onSuccess = (file: any) => {
// emit('uploadSuccess', file);
};
const handleChange = (file: any, fileData: any) => {
// emit('uploadChange', file);
};
const _deleteData = (num: any, data: any) => {
const sliceData = data.slice(num + 1, data.length - 1) || [];
const lastData = data[data.length - 1];
// num表示第几项,从0开始算起 num<0,不进行任何操作
return num < 0 ? data : data.slice(0, num).concat(lastData).concat(sliceData);
};
// 重新上传
const handleResetUpload = (file: any) => {
resetFile.value = file;
isResetImg.value = true;
uploadImg.value?.click();
};
// 移除
const handleRemove = (file: any) => {
loadFiles.value = loadFiles.value.filter((el) => el.ossId !== file.ossId) || [];
loadFiles.value.forEach((element) => {
previewList.value.push(imagesAgent(element.url));
});
emit('uploadSuccess', loadFiles.value);
};
// 查看大图
const handlePreview = (file: any) => {
loadFiles.value.forEach((item, index) => {
if (item.ossId === file.ossId) {
const dom = document.querySelectorAll('.media-list-img');
dom?.[index].querySelector('img')?.click();
}
});
};
return {
upload,
uploadImg,
previewList,
loadFiles,
isResetImg,
resetFile,
imagesAgent,
clearFiles,
percentage,
beforeUpload,
handleFileUpload,
onRemove,
onPreview,
onSuccess,
handleChange,
handlePreview,
handleRemove,
handleResetUpload
};
}
});
</script>
<template>
<div class="img-video-content">
<el-upload
ref="upload"
:action="action"
v-bind="$attrs"
:show-file-list="showFileList"
:accept="accept"
:limit="limit"
:file-list="fileList"
:auto-upload="autoUpload"
:before-upload="beforeUpload"
:http-request="handleFileUpload"
:on-remove="onRemove"
:on-preview="onPreview"
:on-success="onSuccess"
:on-change="handleChange"
v-show="fileList.length < limit"
>
<div ref="uploadImg">
<div class="progress" v-if="percentage && showPer">
<el-icon><Plus /></el-icon>
<ElProgress :stroke-width="8" :percentage="percentage" />
</div>
<slot v-else></slot>
</div>
</el-upload>
<div class="sy-image-container" v-if="isRefreshUpload">
<div class="img-video-item" v-for="(file, index) in loadFiles" :key="file.ossId + index">
<el-image
preview-teleported
class="media-list-img"
:alt="file.name"
:src="imagesAgent(file.url)"
fit="fill"
crossOrigin="anonymous"
:preview-src-list="[imagesAgent(file.url)]"
/>
<div class="img-icon-bt">
<el-icon style="margin-right: 8px" @click.stop="handlePreview(file)">
<ZoomIn />
</el-icon>
<el-icon style="margin-right: 8px" @click.stop="handleResetUpload(file)">
<Refresh />
</el-icon>
<el-icon @click.stop="handleRemove(file)"><Delete /></el-icon>
</div>
</div>
</div>
</div>
</template>
<style lang="less" scoped>
.image-wrapper {
width: 100%;
.sy-upload--picture-card {
width: 64px !important;
height: 64px !important;
margin-right: 8px;
line-height: 64px;
color: rgba(255, 255, 255, 0.8);
background: rgba(255, 255, 255, 0.12);
border: none;
i {
line-height: unset;
}
}
.sy-upload-list {
display: none;
margin-top: 8px;
// display: none;
.sy-upload-list__item {
width: 196px !important;
height: 96px !important;
margin: 0 8px 8px 0;
& > div {
width: 100%;
height: 100%;
}
.sy-icon-check {
position: relative;
top: -16px;
left: 0px;
}
}
}
}
.upload-btn {
width: 100%;
// margin-top: 8px;
.download-btn {
width: 293px;
margin: 0px;
border: none;
background: rgba(255, 255, 255, 0.12);
font-weight: 350;
line-height: 20px;
display: flex;
align-items: center;
letter-spacing: 0em;
color: rgba(255, 255, 255, 0.55);
img {
width: 20px;
height: 20px;
margin: 0px 8px 0px 0px;
}
.text {
display: flex;
align-items: center;
justify-content: center;
}
}
.img-card-btn {
height: 72px;
width: 72px;
background: rgba(255, 255, 255, 0.12);
box-sizing: border-box;
border: 1px solid rgba(255, 255, 255, 0.12);
margin: auto;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
.sy-icon {
margin: 16px auto;
text-align: center;
}
.img-card-text {
height: 72px;
width: 72px;
background: rgba(255, 255, 255, 0.12);
box-sizing: border-box;
border: 1px solid rgba(255, 255, 255, 0.12);
margin: auto;
text-align: center;
border-radius: 4px;
}
}
.text-card-btn {
border: none;
background: transparent;
right: 0;
position: absolute;
color: #409eff;
}
}
.img-video-content {
display: flex;
flex-wrap: nowrap;
float: left;
height: 72px;
.upload-btn {
width: 72px;
// margin: 0px 4px 0px 0px;
border-radius: 4px;
}
}
.sy-image-container {
display: flex;
align-items: center;
width: 212px;
height: 72px;
.img-video-item {
width: 72px;
height: 72px;
margin: auto 6px;
border-radius: 4px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
//padding: 4px 8px;
background: rgba(0, 0, 0, 0.4);
.media-list-img {
// margin: auto 12px;
width: 72px;
height: 72px;
}
&:hover {
.img-icon-bt {
display: block;
}
}
}
.img-icon-bt {
display: none;
position: absolute;
width: 72px;
height: 72px;
background: rgba(43, 44, 47, 0.5);
margin: auto;
text-align: center;
.sy-icon {
margin-top: 24px;
cursor: pointer;
}
}
}
.display-add {
.upload-btn {
display: none;
}
}
</style>