Files
SaaS-lib/lib/component/form/upload/upload.vue
xuziqiang d0155dbe3c push
2024-05-15 17:29:42 +08:00

551 lines
17 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>
<div class="container clearfix">
<!-- 图片显示框 -->
<template v-if="currentImg.length">
<div class="imgList" v-for="(item, index) in fileList" :key="item">
<div class="imgContainer" @mouseenter="mouseEnter(index)" @mouseleave="mouseLeave(index)">
<img :src="currentImg[index]" class="imgCover" />
<span v-if="maskShow[index]" class="mask">
<EyeOutlined v-if="false" @click="handlePreview(item, index)" />
<DeleteOutlined v-if="!disabled" @click="deleteImg(index)" />
</span>
</div>
</div>
</template>
<!-- 图片显示框 /-->
<!-- 上传图片框/内置了input -->
<a-spin :spinning="spinning">
<a-upload
v-if="count === 1 ? 'true' : fileList.length < count"
list-type="picture-card"
:before-upload="beforeUpload"
@change="handleChange"
:multiple="count != 1"
:disabled="disabled"
:customRequest="selfUpload"
:showUploadList="false">
<!-- :disabled="count == 1 && fileUuid ? true : false" -->
<div>
<template v-if="isLt5M && isJpgOrPngOrJpeg">
<UploadOutlined :style="{ fontSize: '14px' }" />
<div class="ant-upload-text">上传图片</div>
</template>
<template v-else>
<LoadingOutlined />
<div class="ant-upload-text">校验失败</div>
</template>
</div>
</a-upload>
</a-spin>
<!-- 上传图片框/内置input /-->
</div>
<!-- 预览图片弹窗 -->
<ns-modal :visible="previewVisible" :footer="null" @cancel="handleCancel">
<img v-if="previewVisible" alt="example" style="width: 100%" :src="previewImage" />
</ns-modal>
<!-- 预览图片弹窗 /-->
<!-- 错误消息提示 -->
<div class="err-msg" v-if="!isJpgOrPngOrJpeg">
<p>{{ fileName }} 文件上传失败</p>
<p :style="{ color: 'red' }">请选择{{ fileType.join(',') }}的图片</p>
</div>
<div class="err-msg" v-if="isJpgOrPngOrJpeg ? !isLt5M : ''">
<p>{{ fileName }} 文件上传失败</p>
<p :style="{ color: 'red' }">请选择{{ maxSize / 1024 / 1024 }}M内的图片</p>
</div>
<!-- 错误消息提示 /-->
</div>
</template>
<script lang="ts">
import {
UploadOutlined,
LoadingOutlined,
EyeOutlined,
DeleteOutlined,
} from '@ant-design/icons-vue';
import { defineComponent, ref, computed, reactive, watch } from 'vue';
import { http } from '/nerv-lib/util/http';
import { NsMessage } from '../../message';
interface FileItem {
uid: string;
type: string;
size: number;
name?: string;
status?: string;
response?: string;
percent?: number;
url?: string;
preview?: string;
originFileObj?: any;
}
interface FileInfo {
file: FileItem;
fileList: FileItem[];
}
// 转base64
function getBase64(file: File) {
return new Promise((resolve, reject) => {
// console.log(file);
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = (error) => reject(error);
});
}
export default defineComponent({
name: 'NsUpload',
components: {
UploadOutlined,
LoadingOutlined,
DeleteOutlined,
EyeOutlined,
},
props: {
// 上传的地址
url: {
type: String,
require: true,
},
// 上传的图片大小
maxSize: {
type: Number,
default: 5242880,
},
// 上传的图片类型
fileType: {
type: Array,
default: () => {
return ['jpg', 'png', 'jpeg'];
},
},
// 展示图片数量
count: {
type: Number,
default: 1,
},
// 是否禁用
disabled: {
type: Boolean,
default: false,
},
// 上传文件类型0-证书1-图片2-身份证件
uploadType: {
type: Number,
default: 1,
},
baseImageUrl: {
type: [String, Object],
},
compatibilityUuid: {
type: [String, Object],
},
// 图片归类所需
params: {
type: [Object],
},
// 是否两个都emit
whetherTwo: {
type: Boolean,
default: false,
},
},
emits: ['change'],
setup(props, { emit }) {
console.log(props);
const previewVisible = ref<boolean>(false);
const isLt5M = ref<boolean>(true);
const isJpgOrPngOrJpeg = ref<boolean>(true);
const previewImage = ref<string | undefined>('');
const fileList = ref<FileItem[]>([]);
const fileUuid = ref<string>('');
const uuidList = ref([]);
const uuidString = ref('');
const currentImg = ref<string[]>([]);
const isIntImg = ref(false);
const spinning = ref(false);
const filterValue = (value) => {
if (typeof value == 'object') {
let arr = [];
value.forEach((item) => {
if (item.includes('ParkPic/')) {
arr.push(item.slice(item.lastIndexOf('/') + 1));
} else {
arr.push(item);
}
});
return arr;
} else {
let str = '';
if (value) {
if (value.includes('ParkPic/')) {
str = value.slice(value.lastIndexOf('/') + 1);
} else {
str = value;
}
}
return str;
}
};
if (props.baseImageUrl) {
isIntImg.value = true;
if (typeof props.baseImageUrl == 'object') {
uuidList.value = props.compatibilityUuid;
// currentImg.value.concat(props.baseImageUrl);
props.baseImageUrl.map((item, index) => {
console.log(item);
fileList.value.push(item);
currentImg.value.push(item);
// previewImage.value = props.baseImageUrl[index];
});
// currentImg.value.unshift(...props.baseImageUrl);
emit(
'change',
props.whetherTwo
? [filterValue(fileList.value), filterValue(uuidList.value)]
: filterValue(fileList.value),
);
} else {
console.log(props);
fileList.value.push({});
uuidString.value = props.compatibilityUuid;
fileUuid.value = props.baseImageUrl;
currentImg.value.unshift(props.baseImageUrl);
previewImage.value = props.baseImageUrl;
emit(
'change',
props.whetherTwo
? [filterValue(fileUuid.value), filterValue(uuidString.value)]
: filterValue(fileUuid.value),
);
}
}
const fileName = ref<string | undefined>('');
const maskShow = ref<boolean[]>([]);
const acceptType = computed(() =>
props.fileType.map((item: String) => {
return 'image/' + item;
}),
);
watch(
() => props.baseImageUrl,
(e) => {
if (e) {
isIntImg.value = true;
if (typeof e == 'object') {
e.map((item, index) => {
fileList.value.push(item);
currentImg.value.push(item);
});
} else {
fileList.value.push({});
currentImg.value.unshift(e);
previewImage.value = e;
}
}
},
);
const beforeUpload = (file: FileItem) => {
// 上传出错后下次上传图片前重置为true让图片可以上传
isLt5M.value = true;
isJpgOrPngOrJpeg.value = true;
// 限制图片格式,服务器不支持gif图片
// if (file.type === 'image/gif') {
// NsMessage.warn('不支持gif图片');
// return false;
// }
isJpgOrPngOrJpeg.value = acceptType.value.includes(file.type);
// 如果大于指定的大小,显示错误信息
if (file.size > props.maxSize) {
isLt5M.value = false;
}
fileName.value = file.name;
return isLt5M.value && isJpgOrPngOrJpeg.value;
};
const handleChange = ({ fileList: newFileList }: FileInfo) => {
// 单图上传
if (props.count === 1) {
// 删除图片时newFileList.length = 0
// 图片大小不符合规范时,!isLt5M.value为true
if (!isLt5M.value || !isJpgOrPngOrJpeg.value || newFileList.length - 1 < 0) {
// 让图片不显示,也不上传
fileList.value = [];
} else {
// 添加\更换图片
// newFileList[newFileList.length - 1]的目的是为了只显示最新一张图片
fileList.value = [newFileList[newFileList.length - 1]];
}
} else {
// 多图上传
// if (typeof props.baseImageUrl == 'object') {
// props.baseImageUrl.map((item, index) => {
// console.log(item);
// fileList.value.push(item);
// // currentImg.value.unshift(item);
// // previewImage.value = props.baseImageUrl[index];
// });
// }
if (!isLt5M.value || !isJpgOrPngOrJpeg.value) {
// fileList.value = newFileList.slice(0, newFileList.length - 1);
// fileList.value = fileList.slice(0, fileList.length - 1);
// console.log(123444, fileList.value, newFileList);
} else {
fileList.value.push(newFileList);
// uuidList.value.push(newFileList);
// emit(
// 'change',
// props.whetherTwo
// ? [filterValue(fileList.value), filterValue(uuidList.value)]
// : filterValue(fileList.value)
// );
// fileList.value = newFileList;
}
}
};
// const fill = (data) => {
// let arr = [];
// if (props.count == 1) return;
// data.map((item) => {
// if (item.indexOf('ParkPic') !== -1) {
// arr.push(item.split('ParkPic/')[1]);
// }
// });
// return arr;
// };
const selfUpload = async ({ file }) => {
if (props.count !== 1) {
// emit(
// 'change',
// props.whetherTwo
// ? [filterValue(fileList.value), filterValue(uuidList.value)]
// : filterValue(fileList.value),
// );
currentImg.value.push(await getBase64(file));
} else {
currentImg.value = [await getBase64(file)];
}
const params = {
uploadType: props.uploadType,
};
const formData = new FormData();
formData.append('file', file);
formData.append('uploadType', props.uploadType);
Object.keys(props.params).map((item) => {
formData.append(item, props.params[item]);
});
//formData.append('uploadType', 1);
const config = {
headers: {
'Content-Type': 'multipart/form-data',
},
// params: params,
};
// console.log(formData);
// if (props.count == 1) {
// fileUuid.value = 'ceshi';
// }
spinning.value = true;
http
.post(props.url, formData, config)
.then((res) => {
if (props.count > 1) {
// let imgBox = ref([]);
// console.log(fileList.value);
fileList.value.push(res.data.httpUrl);
if (!uuidList.value) {
uuidList.value = [];
}
uuidList.value.push(res.data.fileUuid);
// fileList.value.push('/api/parking_merchant/objs/ParkPic/' + res.data.fileUuid);
fileList.value.forEach((item, index) => {
if (typeof item == 'object') {
fileList.value.splice(index, 1);
}
});
uuidList.value.forEach((item, index) => {
if (typeof item == 'object') {
uuidList.value.splice(index, 1);
}
});
// props.whetherTwo;
emit(
'change',
props.whetherTwo
? [filterValue(fileList.value), filterValue(uuidList.value)]
: filterValue(fileList.value),
);
} else {
fileUuid.value = res.data.httpUrl || res.data.picUuid || res.data.fileUuid;
uuidString.value = res.data.fileUuid;
emit(
'change',
props.whetherTwo
? [filterValue(fileUuid.value), filterValue(uuidString.value)]
: filterValue(fileUuid.value),
);
}
spinning.value = false;
})
.catch(() => {
fileList.value.forEach((item, index) => {
if (typeof item == 'object') {
fileList.value.splice(fileList.value.length - 1, 1);
}
});
NsMessage.error('上传失败,请重试');
spinning.value = false;
});
};
const handlePreview = async (item, index: number) => {
if (props.count > 1) {
previewImage.value = fileList.value[index];
} else {
previewImage.value = fileUuid.value;
}
// if (!isIntImg.value) {
// previewImage.value = (await getBase64(fileList.value[index])) as string;
// // previewImage.value = (await getBase64(fileList.value[index].originFileObj)) as string;
// }
previewVisible.value = true;
};
const deleteImg = (index: number) => {
currentImg.value.splice(index, 1);
fileList.value.splice(index, 1);
uuidList.value && uuidList.value.splice(index, 1);
uuidString.value = '';
fileUuid.value = '';
isIntImg.value = false;
if (props.count == 1) {
emit(
'change',
props.whetherTwo
? [filterValue(fileUuid.value), filterValue(uuidString.value)]
: filterValue(fileUuid.value),
);
} else {
emit(
'change',
props.whetherTwo
? [filterValue(fileList.value), filterValue(uuidList.value)]
: filterValue(fileList.value),
);
}
};
const handleCancel = () => {
previewVisible.value = false;
};
const mouseEnter = (index: number) => {
maskShow.value[index] = true;
};
const mouseLeave = (index: number) => {
maskShow.value[index] = false;
};
return {
previewVisible,
previewImage,
fileList,
isLt5M,
isJpgOrPngOrJpeg,
fileName,
currentImg,
maskShow,
fileUuid,
selfUpload,
handleCancel,
handlePreview,
handleChange,
beforeUpload,
mouseEnter,
mouseLeave,
deleteImg,
spinning,
};
},
});
</script>
<style lang="less" scoped>
:deep(.ant-upload-picture-card-wrapper) {
width: unset !important;
}
:deep(.ant-upload-picture-card-wrapper .ant-upload.ant-upload-select-picture-card) {
margin: 0;
width: 64px !important;
height: 64px !important;
border: 1px solid #d9d9d9;
}
:deep(.ant-upload-picture-card-wrapper .ant-upload.ant-upload-select-picture-card:hover) {
border-color: #d9d9d9;
}
:deep(.ant-upload-select-picture-card i) {
font-size: 32px;
color: #999;
}
:deep(.ant-upload-select-picture-card .ant-upload-text) {
color: #666;
font-size: 12px;
}
:deep(.ant-upload-picture-card-wrapper .ant-upload-list-picture-card-container) {
width: 88px;
height: 64px !important;
}
:deep(.ant-upload-picture-card-wrapper .ant-upload-list-picture-card .ant-upload-list-item) {
margin: 0;
padding: 0;
width: 64px !important;
height: 64px !important;
}
.title,
.err-msg {
text-align: left;
}
.err-msg p {
margin: 0;
}
.container {
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
}
.imgList {
display: flex;
}
.imgContainer {
margin-right: 16px;
border: 1px solid #d9d9d9;
position: relative;
margin-bottom: 8px;
}
.imgCover {
width: 64px;
height: 64px;
object-fit: contain;
}
.imgContainer .mask {
position: absolute;
top: 0;
left: 0;
width: 64px !important;
height: 64px !important;
background: rgba(0, 0, 0, 0.5);
text-align: center;
line-height: 64px !important;
}
.mask .anticon-eye {
color: white;
margin-right: 18px;
}
.mask .anticon-eye:hover {
color: #00acff;
margin-right: 18px;
}
.mask .anticon-delete {
color: white;
}
.mask .anticon-delete:hover {
color: #00acff;
}
</style>