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.

551 lines
17 KiB

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