wms-antdvue/src/components/Upload/Image.vue
2024-12-21 15:00:11 +08:00

371 lines
11 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="upload-box">
<a-upload-dragger
:style="{ width: width }"
action="#"
:id="uuid"
:class="['upload', self_disabled ? 'disabled' : '', drag ? 'no-border' : '']"
:multiple="false"
:disabled="loading ? true : self_disabled"
:show-upload-list="false"
:custom-request="handleHttpUpload"
:before-upload="beforeUpload"
@reject="uploadError"
:accept="fileType.join(',')"
>
<template v-if="imageUrl">
<img v-if="imageUrl.indexOf('.pdf') < 0" :src="imageUrl" class="upload-image" />
<div v-else class="upload-image">{{ imageUrl }}</div>
<div class="upload-handle" @click.stop>
<div class="handle-icon" @click="editImg" v-if="!self_disabled">
<EditOutlined style="font-size: 20px" />
</div>
<div class="handle-icon" @click="viewImg">
<ZoomInOutlined style="font-size: 20px" />
</div>
<div class="handle-icon" @click="deleteImg" v-if="!self_disabled">
<DeleteOutlined style="font-size: 20px" />
</div>
</div>
</template>
<template v-else>
<div class="upload-empty">
<slot name="empty">
<plus-outlined v-if="!loading" style="color: #999999" />
<span v-else>上传中{{ progress }}%</span>
</slot>
</div>
</template>
</a-upload-dragger>
<div class="ant-upload__tip">
<slot name="tip"></slot>
</div>
<a-image
:width="200"
:style="{ display: 'none' }"
:preview="{
visible: imgViewVisible,
onVisibleChange: (vis) => (imgViewVisible = vis),
}"
:src="imageUrl"
/>
</div>
<Cropper
v-model:visible="cropperVisible"
v-if="cropperVisible"
:cropperSize="cropperSize"
:oImg="oImg"
@success="cropperSuccess"
:fileName="fileName"
/>
</template>
<script setup lang="ts" name="UploadImg">
import { ref, computed, inject } from 'vue';
import { generateUUID } from '@/utils/auth';
import { upload,getOssConfig } from '@/api/common';
import {
PlusOutlined,
EditOutlined,
DeleteOutlined,
ZoomInOutlined,
} from '@ant-design/icons-vue';
import { notification } from 'ant-design-vue';
import Cropper from '@/components/Upload/croppers.vue';
import OSS from "ali-oss";
interface UploadFileProps {
imageUrl?: string; // 图片地址 ==> 必传
uploadFileUrl?: string; // 上传图片的 api 方法,一般项目上传都是同一个 api 方法,在组件里直接引入即可 ==> 非必传
drag?: boolean; // 是否支持拖拽上传 ==> 非必传(默认为 true
disabled?: boolean; // 是否禁用上传组件 ==> 非必传(默认为 false
fileSize?: number; // 图片大小限制 ==> 非必传(默认为 5M
fileType?: File.ImageMimeType[]; // 图片类型限制 ==> 非必传(默认为 ["image/jpeg", "image/png", "image/gif"]
height?: string; // 组件高度 ==> 非必传(默认为 150px
width?: string; // 组件宽度 ==> 非必传(默认为 150px
borderRadius?: string; // 组件边框圆角 ==> 非必传(默认为 8px
iconSize?: number;
name?: string;
cropper: Boolean;
cropperSize?: Object;
}
// 接受父组件参数
const props = withDefaults(defineProps<UploadFileProps>(), {
imageUrl: '',
uploadFileUrl: '/admin/file/upload',
drag: true,
disabled: false,
fileSize: 5,
fileType: () => ['image/jpeg', 'image/png', 'image/jpg', 'image/gif'],
height: '120px',
width: '120px',
borderRadius: '8px',
name: '',
cropper: false,
cropperSize: null,
});
// 生成组件唯一id
const uuid = ref('id-' + generateUUID());
const imgViewVisible = ref(false);
const viewImg = () => {
if (props.imageUrl.indexOf('.pdf') < 0) {
imgViewVisible.value = true;
} else {
window.open(props.imageUrl);
}
};
// 查看图片
// 判断是否禁用上传和删除
const self_disabled = computed(() => {
return props.disabled;
});
/**
* @description 图片上传
* @param options upload 所有配置项
* */
interface UploadEmits {
(e: 'update:imageUrl', value: string): void;
(e: 'changeFileName', value: string): void;
}
const fileName = ref('');
const oImg = ref('');
const cropperVisible = ref(false);
const emit = defineEmits<UploadEmits>();
const loading = ref(false);
const progress = ref(0);
const cropperSuccess = (fileChile: any) => {
actionFile(fileChile);
};
//接口上传
const actionFile = async (fileChild: any) => {
loading.value = true;
try {
const formData = new FormData();
formData.append('file', fileChild);
formData.append('name', props.name);
const res = await upload(formData);
console.log(res);
emit('update:imageUrl', res.fileUrl);
emit('changeFileName', res.originalName);
} catch (error) {
emit('update:imageUrl', '');
emit('changeFileName', '');
} finally {
progress.value = 0;
loading.value = false;
}
};
//oss上传
const actionFiles=async (fileChild:any)=>{
const configData = await getOssConfig()
let date = new Date();
let year = date.getFullYear();
let month: string | number = date.getMonth() + 1;
month = month < 10 ? ("0" + month) : month;
let day: string | number = date.getDate();
day = day < 10 ? ("0" + day) : day;
let h: string | number = date.getHours();
h = h < 10 ? ("0" + h) : h;
let minute: string | number = date.getMinutes();
minute = minute < 10 ? ("0" + minute) : minute;
let second: string | number = date.getSeconds();
second = second < 10 ? ("0" + second) : second;
let time = h + "" + minute + "" + second;
let filterFileName=time + "-" + fileChild.name
filterFileName=filterFileName.replace(/[\+\_\,\!\|\~\`\(\)\#\$\%\^\&\*\{\}\:\;\"\<\>\?]/g, '')
let filePath = props.name + "/" + year + "/" + month + "/" + day + "/" + filterFileName;
loading.value=true
try {
const headers = {
"Access-Control-Allow-Origin": "*"
};
const client = new OSS({
// yourRegion填写Bucket所在地域。以华东1杭州为例yourRegion填写为oss-cn-hangzhou。
endpoint: configData.oss_endpoint,
// 从STS服务获取的临时访问密钥AccessKey ID和AccessKey Secret
secure: true,
region: configData.oss_region,
accessKeyId: configData.oss_accessKeyId,
accessKeySecret: configData.oss_accessKeySecret,
// 填写Bucket名称。
bucket: configData.oss_bucketName,
timeout: 600000
});
const res = await client.multipartUpload(filePath,fileChild, {
headers: headers,
// 获取分片上传进度、断点和返回值。
progress: (p, cpt, res) => {
progress.value= parseFloat(parseFloat(p * 100).toFixed(2))
handleHttpUploadOptions.value.onProgress({percent: progress.value});
},
// 设置并发上传的分片数量。
parallel: 4,
// 设置分片大小。默认值为1 MB最小值为100 KB。
partSize: 1024 * 1024,
// headers,
mime: fileChild.type
});
emit('update:imageUrl',configData.oss_domain + res.name);
emit('changeFileName',fileChild.name)
} catch (error) {
emit('update:imageUrl','');
emit('changeFileName','')
}finally {
progress.value=0
loading.value=false
}
}
const handleHttpUploadOptions = ref({});
const handleHttpUpload = async (options) => {
handleHttpUploadOptions.value = options;
if (props.cropper) {
fileName.value = options.file.name;
let reader = new FileReader();
reader.readAsDataURL(options.file);
let img = new Image();
reader.onload = function (e) {
img.src = this.result;
img.onload = function () {
let base = e.target.result;
oImg.value = base;
event.target.value = '';
cropperVisible.value = true;
};
};
return;
}
actionFile(options.file);
};
/**
* @description 删除图片
* */
const deleteImg = () => {
emit('update:imageUrl', '');
emit('changeFileName', '');
};
/**
* @description 编辑图片
* */
const editImg = () => {
const dom = document.querySelector(`.ant-upload #${uuid.value}`);
dom && dom.dispatchEvent(new MouseEvent('click'));
};
/**
* @description 文件上传之前判断
* @param rawFile 选择的文件
* */
const beforeUpload = (rawFile) => {
const imgSize = rawFile.size / 1024 / 1024 < props.fileSize;
const imgType = props.fileType.includes(rawFile.type as File.ImageMimeType);
if (!imgType)
notification.warning({
message: '温馨提示',
description: '上传图片不符合所需的格式!',
});
if (!imgSize)
setTimeout(() => {
notification.warning({
message: '温馨提示',
description: `上传图片大小不能超过 ${props.fileSize}M`,
});
}, 0);
return imgType && imgSize;
};
/**
* @description 图片上传错误
* */
const uploadError = () => {
notification.error({
message: '温馨提示',
description: '图片上传失败,请您重新上传!',
});
};
</script>
<style scoped lang="less">
.upload-box {
.no-border {
:deep(.ant-upload) {
border: none !important;
}
}
:deep(.upload) {
.ant-upload {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: v-bind(width);
height: v-bind(height);
overflow: hidden;
border-radius: 4px;
padding: 0;
padding: 5px;
&:hover {
.upload-handle {
opacity: 1;
}
}
.upload-image {
width: 100%;
height: 100%;
object-fit: contain;
}
.upload-empty {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 12px;
line-height: 30px;
color: #d0d0d0;
}
.upload-handle {
position: absolute;
top: 0;
right: 0;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
cursor: pointer;
background: rgb(0 0 0 / 60%);
opacity: 0;
.handle-icon {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0 6%;
color: aliceblue;
span {
font-size: 85%;
line-height: 85%;
}
}
}
}
}
.ant-upload__tip {
line-height: 18px;
font-size: 12px;
margin-top: 7px;
}
}
</style>