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
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>
|
|
|