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