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

452 lines
12 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.

<!-- @format -->
<template>
<div>
<div class="container clearfix">
<!-- 图片显示框 -->
<template v-if="fileList.length">
<div class="imgList" v-for="(item, index) in fileList" :key="item">
<div class="imgContainer" @mouseenter="mouseEnter(index)" @mouseleave="mouseLeave(index)">
<img :src="baseImageUrl + item" class="imgCover" />
<span v-if="maskShow[index]" class="mask">
<EyeOutlined v-if="false" @click="handlePreview(item, index)" />
<DeleteOutlined @click="deleteImg(index)" />
</span>
</div>
</div>
</template>
<!-- 图片显示框 /-->
<!-- 上传图片框/内置了input -->
<a-upload
v-if="fileList.length < count"
list-type="picture-card"
:before-upload="beforeUpload"
@change="handleChange"
:multiple="count != 1"
:customRequest="selfUpload"
:showUploadList="false">
<!-- :disabled="count == 1 && fileUuid ? true : false" -->
<div v-if="fileList.length < count">
<template v-if="isLt5M && isJpgOrPngOrJpeg && !loading">
<UploadOutlined :style="{ fontSize: '14px' }" />
<div class="ant-upload-text">上传图片</div>
</template>
<template v-else-if="loading">
<LoadingOutlined />
<div class="ant-upload-text">上传中...</div>
</template>
<template v-else>
<LoadingOutlined />
<div class="ant-upload-text">校验失败</div>
</template>
</div>
</a-upload>
<!-- 上传图片框/内置input /-->
</div>
<!-- 预览图片弹窗 -->
<ns-modal :visible="previewVisible" :footer="null" @cancel="handleCancel">
<img alt="example" style="width: 100%" :src="baseImageUrl + 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 '/nerv-lib/component/message';
import { get } from 'lodash-es';
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: 'NsUploadV2.1',
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,
},
// 图片归类所需
params: {
type: [Object],
},
// 请求头配置
config: {
type: Object,
},
// 上传文件类型0-证书1-图片2-身份证件
uploadType: {
type: Number,
default: 1,
},
baseImageUrl: {
type: String,
default: '',
},
value: {
type: String || Array,
},
valueField: {
type: String,
default: 'data.picUuid',
},
},
emits: ['change'],
setup(props, { emit, attrs }) {
console.log(props.config);
const previewVisible = ref<boolean>(false);
const isLt5M = ref<boolean>(true);
const isJpgOrPngOrJpeg = ref<boolean>(true);
const previewImage = ref<string | undefined>('');
const fileList = ref<string[]>([]);
const currentImg = ref<string[]>([]);
const isIntImg = ref(false);
const baseImageUrl = props.baseImageUrl || '';
const fileName = ref<string | undefined>('');
const maskShow = ref<boolean[]>([]);
const fileUuid = ref<string>('');
let loading = ref<boolean>(false);
const acceptType = computed(() =>
props.fileType.map((item: String) => {
return 'image/' + item;
}),
);
watch(
() => props.value,
(e) => {
if (e && !fileList.value.length) {
isIntImg.value = true;
if (typeof e == 'array') {
e.map((item: string) => {
item = baseImageUrl + item;
fileList.value.push(item);
currentImg.value.push(item);
});
} else {
fileList.value.push(e);
currentImg.value.unshift(e);
previewImage.value = e;
}
let fileValue: Array<any> | string = '';
if (props.count === 1) {
fileValue = fileList.value[0];
} else {
fileValue = fileList.value;
}
emit('change', fileValue);
}
},
{
deep: true,
immediate: true,
},
);
const beforeUpload = (file: FileItem) => {
// 上传出错后下次上传图片前重置为true让图片可以上传
isLt5M.value = true;
isJpgOrPngOrJpeg.value = true;
// 限制图片格式,服务器不支持gif图片
if (file.type === 'image/gif') {
NsMessage.warn('不支持gif图片');
return false;
}
// console.log(file);
if (file.name?.split('.')[file.name?.split('.').length - 1] === 'jfif') {
NsMessage.warn('不支持jfif图片');
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 = (info: FileInfo) => {
// 单图上传
if (info.file.status === 'uploading') {
console.log(loading.value);
loading.value = true;
}
};
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", fill(fileList.value));
// }
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]);
});
let config;
if (!props.config) {
config = {
headers: {
'Content-Type': 'multipart/form-data',
},
// params: params,
};
} else {
config = props.config;
}
http
.post(props.url, formData, config)
.then((res) => {
if (props.count > 1) {
fileList.value.push(get(res, props.valueField));
emit('change', fill(fileList.value), res);
} else {
fileUuid.value = get(res, props.valueField);
fileList.value = [fileUuid.value];
loading.value = false;
emit('change', fileUuid.value, res);
}
})
.catch(() => {
fileList.value.forEach((item, index) => {
if (typeof item == 'object') {
fileList.value.splice(fileList.value.length - 1, 1);
}
});
NsMessage.error('上传失败,请重试');
});
};
const handlePreview = async (item, index: number) => {
// emit('change', fill(fileList.value));
console.log(item, index);
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);
fileUuid.value = '';
isIntImg.value = false;
if (props.count == 1) {
emit('change', fileUuid.value);
} else {
emit('change', fill(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,
loading,
};
},
});
</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>