wms-naivevue/src/components/Upload/Images.vue
2024-12-21 15:51:43 +08:00

412 lines
11 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 class="upload-box">
<div v-for="(item, index) in fileList" class="items">
<img v-if="item.filePath" :src="item.filePath" class="upload-image" />
<div class="upload-handle" @click.stop>
<div class="handle-icon" @click="handleEdit(index)" v-if="!self_disabled">
<n-icon size="20">
<EditOutlined />
</n-icon>
</div>
<div class="handle-icon" @click="onPreview(index)">
<n-icon size="20">
<ZoomInOutlined />
</n-icon>
</div>
<div class="handle-icon" @click="handleRemove(index)" v-if="!self_disabled">
<n-icon size="20">
<DeleteOutlined />
</n-icon>
</div>
</div>
</div>
<n-upload
action="#"
:id="uuid"
class="upload-demo my-upload-images"
:multiple="multiple"
ref="uploadRef"
:style="{ width: width, height: height }"
:disabled="loading ? true : self_disabled || fileList.length >= props.limit"
@before-upload="beforeUpload"
@change="handleUploadChange"
:max="limit"
v-model:file-list="fileList"
:show-file-list="false"
v-if="!self_disabled"
:accept="props.fileType"
>
<n-icon size="40" color="#666666" v-if="!loading">
<PlusOutlined />
</n-icon>
<n-progress type="circle" v-else :percentage="progress" />
</n-upload>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
import { generateUUID } from '@/utils/auth';
import type { UploadFileInfo } from 'naive-ui';
import { PlusOutlined, EditOutlined, DeleteOutlined, ZoomInOutlined } from '@vicons/antd';
import { useNotification } from 'naive-ui';
import OSS from 'ali-oss';
const uploadRef = ref();
import { upload, getOssConfig } from '@/api/common';
// 接受父组件参数
const props = defineProps({
zIndex: {
default: -1,
},
multiple: {
type: Boolean,
default: false,
},
btnTip: {
type: String,
default: '',
},
limit: {
type: Number,
default: undefined,
},
width: {
type: String,
default: '120px',
},
height: {
type: String,
default: '120px',
},
borderRadius: {
type: String,
default: '8px',
},
fileLists: {
type: Array,
required: true,
default: [],
},
disabled: {
default: false,
},
fileSize: {
default: 200,
},
fileType: {
default: '',
},
name: {
default: '',
},
});
// 生成组件唯一id
const uuid = ref('id-' + generateUUID());
const notification = useNotification();
// 判断是否禁用上传和删除
const self_disabled = computed(() => {
return props.disabled;
});
const fileList = computed({
get: () => {
console.log(props.fileLists);
return props.fileLists;
},
set: (val) => {
emit('update:modelValue', fileList.value);
},
});
let editIndex = '';
const emit = defineEmits(['upload']);
const progress = ref(0);
const loading = ref(false);
//接口上传
const handleHttpUpload = async (options) => {
loading.value = true;
try {
const formData = new FormData();
formData.append('file', options.file.file);
formData.append('name', props.name);
const res = await upload(formData);
if (props.multiple) {
let list = JSON.parse(JSON.stringify(fileList.value));
if (editIndex !== '') {
list.splice(editIndex, 1, {
url: res.fileUrl,
name: res.originalName,
filePath: res.fileUrl,
fileName: res.originalName,
});
editIndex = '';
} else {
list.push({
url: res.fileUrl,
name: res.originalName,
filePath: res.fileUrl,
fileName: res.originalName,
});
}
emit('upload', list, props.zIndex);
} else {
emit('upload', res, props.zIndex);
}
} catch (error) {
notification.error({
message: '温馨提示',
duration: 2000,
description: '上传文件失败!',
});
if (props.multiple) {
emit('upload', fileList.value, props.zIndex);
} else {
uploadRef.value?.clear();
emit('upload', '', props.zIndex);
}
options.onError(error as any);
} finally {
progress.value = 0;
loading.value = false;
}
};
//oss上传
const handleHttpUploads = async (options: UploadRequestOptions) => {
console.log(options);
const configData = await getOssConfig();
let date = new Date();
let year = date.getFullYear();
let month: string | number = date.getMonth() + 1;
month = month < 10 ? '0' + month : month;
let day: string | number = date.getDate();
day = day < 10 ? '0' + day : day;
let h: string | number = date.getHours();
h = h < 10 ? '0' + h : h;
let minute: string | number = date.getMinutes();
minute = minute < 10 ? '0' + minute : minute;
let second: string | number = date.getSeconds();
second = second < 10 ? '0' + second : second;
let time = h + '' + minute + '' + second;
let fileName = time + '-' + options.file.name;
fileName = fileName.replace(/[\+\_\,\!\|\~\`\(\)\#\$\%\^\&\*\{\}\:\;\"\<\>\?]/g, '');
let filePath = props.name + '/' + year + '/' + month + '/' + day + '/' + fileName;
loading.value = true;
try {
const headers = {
'Access-Control-Allow-Origin': '*',
};
const client = new OSS({
// yourRegion填写Bucket所在地域。以华东1杭州为例yourRegion填写为oss-cn-hangzhou。
endpoint: configData.oss_endpoint,
// 从STS服务获取的临时访问密钥AccessKey ID和AccessKey Secret
secure: true,
region: configData.oss_region,
accessKeyId: configData.oss_accessKeyId,
accessKeySecret: configData.oss_accessKeySecret,
// 填写Bucket名称。
bucket: configData.oss_bucketName,
timeout: 600000,
});
const res = await client.multipartUpload(filePath, options.file.file, {
headers: headers,
// 获取分片上传进度、断点和返回值。
progress: (p, cpt, res) => {
progress.value = parseFloat(parseFloat(p * 100).toFixed(2));
},
// 设置并发上传的分片数量。
parallel: 4,
// 设置分片大小。默认值为1 MB最小值为100 KB。
partSize: 1024 * 1024,
// headers,
mime: options.file.type,
});
// 文件地址拼接
const fileUrl = configData.oss_domain + res.name;
if (props.multiple) {
let list = JSON.parse(JSON.stringify(fileList.value));
if (editIndex !== '') {
list.splice(editIndex, 1, {
url: fileUrl,
name: options.file.name,
filePath: fileUrl,
fileName: options.file.name,
});
editIndex = '';
} else {
list.push({
url: fileUrl,
name: options.file.name,
filePath: fileUrl,
fileName: options.file.name,
});
}
emit('upload', list, props.zIndex);
} else {
emit('upload', fileUrl, options.file.name, props.zIndex);
}
} catch (error) {
notification.error({
message: '温馨提示',
description: '上传文件失败!',
});
if (props.multiple) {
emit('upload', props.fileList, props.zIndex);
} else {
uploadRef.value!.clearFiles();
emit('upload', '', props.zIndex);
}
} finally {
progress.value = 0;
loading.value = false;
}
};
const handleUploadChange = (data: { fileList: UploadFileInfo[] }) => {
handleHttpUpload(data);
};
const onPreview = (index: any) => {
window.open(fileList.value[index].filePath);
};
const handleEdit = (index: any) => {
editIndex = index;
const dom = document.querySelector(`#${uuid.value} .n-upload-file-input`);
dom && dom.dispatchEvent(new MouseEvent('click'));
};
const handleRemove = (index: any) => {
editIndex = index;
if (props.multiple) {
emit(
'upload',
fileList.value.filter((f, i) => !(editIndex == i)),
props.zIndex,
);
} else {
uploadRef.value?.clear();
emit('upload', '', props.zIndex);
}
};
const onExceed = () => {
if (props.limit) {
notification.warning({
message: '温馨提示',
duration: 2000,
description: `最多支持上传${props.limit}张`,
});
}
};
/**
* @description 文件上传之前判断
* @param rawFile 选择的文件
* */
const beforeUpload = (rawFile) => {
console.log(rawFile);
const imgSize = rawFile.file.file.size / 1024 / 1024 < props.fileSize;
if (!imgSize) {
notification.warning({
message: '温馨提示',
duration: 2000,
description: `上传文件大小不能超过 ${props.fileSize}M`,
});
return false;
}
if (props.fileType) {
let fileType = props.fileType.replace(/\s/g, '');
fileType.split('.').join('|');
let fileIndex = rawFile.file.file.name.lastIndexOf('.');
let substrName = rawFile.file.file.name.substr(fileIndex);
if (fileType.indexOf(substrName) == -1) {
notification.warning({
message: '温馨提示',
duration: 2000,
description: '上传文件不符合所需的格式!',
});
return false;
}
}
return true;
};
</script>
<style scoped lang="less">
.upload-box {
position: relative;
display: flex;
flex-wrap: wrap;
> div {
margin-right: 10px;
margin-bottom: 10px;
position: relative;
&.items {
width: v-bind(width);
height: v-bind(height);
border: 1px dashed #d9d9d9;
border-radius: 4px;
padding: 5px;
}
&:hover {
.upload-handle {
opacity: 1;
}
}
}
:deep(.n-upload) {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: v-bind(width);
height: v-bind(height);
overflow: hidden;
border-radius: 4px;
border: 1px dashed #d9d9d9;
cursor: pointer;
.n-upload-trigger {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
}
.upload-image {
width: 100%;
height: 100%;
object-fit: contain;
}
.upload-handle {
position: absolute;
top: 0;
right: 0;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
cursor: pointer;
background: rgb(0 0 0 / 60%);
opacity: 0;
.handle-icon {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0 6%;
color: aliceblue;
span {
font-size: 85%;
line-height: 85%;
}
}
}
}
</style>