452 lines
12 KiB
Vue
452 lines
12 KiB
Vue
<!-- @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>
|