264 lines
7.2 KiB
Vue
264 lines
7.2 KiB
Vue
<!-- 文件上传弹窗组件 -->
|
||
<template>
|
||
<a-modal
|
||
:visible="visible"
|
||
title="选择文件密级并上传"
|
||
:width="600"
|
||
:confirm-loading="uploading"
|
||
@ok="handleUpload"
|
||
@cancel="handleCancel"
|
||
:destroyOnClose="true"
|
||
>
|
||
<a-form layout="vertical" style="padding: 0 20px">
|
||
<a-form-item label="文件密级" required>
|
||
<a-radio-group v-model:value="selectedSecretLevel">
|
||
<a-radio
|
||
v-for="option in secretLevelOptions"
|
||
:key="option.value"
|
||
:value="option.value"
|
||
>
|
||
<a-tag :color="getSecretLevelColor(option.value)">
|
||
{{ option.label }}
|
||
</a-tag>
|
||
</a-radio>
|
||
</a-radio-group>
|
||
<!-- <div class="secret-tip">-->
|
||
<!-- <Icon icon="ant-design:info-circle-outlined" />-->
|
||
<!-- 文件密级将决定谁能查看此文件,请谨慎选择-->
|
||
<!-- </div>-->
|
||
</a-form-item>
|
||
|
||
<a-form-item label="选择文件" required>
|
||
<a-upload
|
||
:before-upload="beforeUpload"
|
||
:file-list="tempFileList"
|
||
@remove="handleRemoveFile"
|
||
:multiple="multiple"
|
||
:max-count="multiple ? undefined : 1"
|
||
>
|
||
<a-button>
|
||
<Icon icon="ant-design:file-add-outlined" />
|
||
{{ multiple ? '选择文件(可多选)' : '选择文件' }}
|
||
</a-button>
|
||
</a-upload>
|
||
</a-form-item>
|
||
|
||
<a-alert
|
||
v-if="selectedFiles.length > 0"
|
||
:message="`已选择 ${selectedFiles.length} 个文件`"
|
||
type="info"
|
||
show-icon
|
||
>
|
||
<template #description>
|
||
<div class="selected-files-summary">
|
||
<div v-for="(f, idx) in selectedFiles" :key="f.uid" class="summary-item">
|
||
{{ idx + 1}}. {{ f.name }}({{ formatFileSize(f.size) }})
|
||
</div>
|
||
</div>
|
||
</template>
|
||
</a-alert>
|
||
</a-form>
|
||
</a-modal>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import { ref, watch } from 'vue';
|
||
import { Icon } from '/src/components/Icon';
|
||
import { useMessage } from '/src/hooks/web/useMessage';
|
||
import { uploadMyFile } from '/src/api/common/api';
|
||
|
||
const { createMessage } = useMessage();
|
||
|
||
const props = defineProps({
|
||
visible: {
|
||
type: Boolean,
|
||
required: true
|
||
},
|
||
secretLevelOptions: {
|
||
type: Array,
|
||
default: () => []
|
||
},
|
||
businessId: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
multiple: {
|
||
type: Boolean,
|
||
default: true
|
||
}
|
||
});
|
||
|
||
const emit = defineEmits(['update:visible', 'upload-success', 'upload-error']);
|
||
|
||
const selectedSecretLevel = ref<number>(1);
|
||
const selectedFiles = ref<any[]>([]);
|
||
const tempFileList = ref<any[]>([]);
|
||
const uploading = ref<boolean>(false);
|
||
|
||
function getSecretLevelColor(level: number) {
|
||
const colors = {
|
||
1: 'success',
|
||
2: 'processing',
|
||
3: 'warning',
|
||
4: 'error'
|
||
};
|
||
return colors[level] || 'default';
|
||
}
|
||
|
||
function formatFileSize(bytes: number): string {
|
||
if (bytes === 0) return '0 B';
|
||
const k = 1024;
|
||
const sizes = ['B', 'KB', 'MB', 'GB'];
|
||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
|
||
}
|
||
|
||
function beforeUpload(file) {
|
||
const isValidSize = file.size / 1024 / 1024 < 500;
|
||
if (!isValidSize) {
|
||
createMessage.error(`文件"${file.name}"大小超过500MB,已跳过`);
|
||
return false;
|
||
}
|
||
|
||
if (!props.multiple) {
|
||
selectedFiles.value = [file];
|
||
tempFileList.value = [{
|
||
uid: file.uid,
|
||
name: file.name,
|
||
status: 'done',
|
||
size: file.size
|
||
}];
|
||
} else {
|
||
selectedFiles.value = [...selectedFiles.value, file];
|
||
tempFileList.value = [...tempFileList.value, {
|
||
uid: file.uid,
|
||
name: file.name,
|
||
status: 'done',
|
||
size: file.size
|
||
}];
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
function handleRemoveFile(file) {
|
||
selectedFiles.value = selectedFiles.value.filter(f => f.uid !== file.uid);
|
||
tempFileList.value = tempFileList.value.filter(f => f.uid !== file.uid);
|
||
}
|
||
|
||
async function handleUpload() {
|
||
if (selectedFiles.value.length === 0) {
|
||
createMessage.warning('请先选择文件');
|
||
return;
|
||
}
|
||
|
||
if (!selectedSecretLevel.value) {
|
||
createMessage.warning('请选择文件密级');
|
||
return;
|
||
}
|
||
|
||
uploading.value = true;
|
||
|
||
try {
|
||
const uploadPromises = selectedFiles.value.map(file => {
|
||
const formData = new FormData();
|
||
formData.append('file', file);
|
||
formData.append('secretLevel', selectedSecretLevel.value.toString());
|
||
if (props.businessId) {
|
||
formData.append('business_id', props.businessId);
|
||
}
|
||
return uploadMyFile('/sys/file/upload', formData).then(res => ({
|
||
rawFile: file,
|
||
response: res
|
||
}));
|
||
});
|
||
|
||
const results = await Promise.allSettled(uploadPromises);
|
||
|
||
let successCount = 0;
|
||
let failCount = 0;
|
||
const successResults: any[] = [];
|
||
|
||
results.forEach((r) => {
|
||
if (r.status === 'fulfilled') {
|
||
const result = r.value?.response?.data;
|
||
if (result && result.success) {
|
||
successCount++;
|
||
successResults.push({
|
||
fileId: result.result.fileId,
|
||
fileName: r.value.rawFile.name,
|
||
filePath: result.result.savePath,
|
||
fileSize: r.value.rawFile.size,
|
||
secretLevel: selectedSecretLevel.value,
|
||
fileUploadType: result.result.fileUploadType,
|
||
bound: !!props.businessId
|
||
});
|
||
} else {
|
||
failCount++;
|
||
console.error('[SUploadFile] 上传响应未通过校验:', result);
|
||
}
|
||
} else {
|
||
failCount++;
|
||
console.error('[SUploadFile] 上传请求失败:', r.reason);
|
||
}
|
||
});
|
||
|
||
if (successCount > 0) {
|
||
createMessage.success(`成功上传 ${successCount} 个文件${failCount > 0 ? `,${failCount} 个失败` : ''}`);
|
||
emit('upload-success', { success: true, results: successResults });
|
||
handleCancel();
|
||
} else {
|
||
const firstResult = results.find(r => r.status === 'fulfilled')?.value?.response?.data;
|
||
const firstReason = results.find(r => r.status === 'rejected')?.reason;
|
||
const serverMsg = firstResult?.message ? `:${firstResult.message}` : '';
|
||
const debugMsg = firstResult ? `:${JSON.stringify(firstResult).slice(0, 200)}` : (firstReason ? `:${firstReason?.message || firstReason}` : '');
|
||
createMessage.error('所有文件上传失败' + serverMsg + debugMsg);
|
||
emit('upload-error', new Error('所有文件上传失败'));
|
||
}
|
||
} catch (error) {
|
||
console.error('上传失败:', error);
|
||
createMessage.error('文件上传失败: ' + (error?.message || error));
|
||
emit('upload-error', error);
|
||
} finally {
|
||
uploading.value = false;
|
||
}
|
||
}
|
||
|
||
function handleCancel() {
|
||
emit('update:visible', false);
|
||
setTimeout(() => {
|
||
selectedSecretLevel.value = 1;
|
||
selectedFiles.value = [];
|
||
tempFileList.value = [];
|
||
}, 300);
|
||
}
|
||
|
||
watch(() => props.visible, (newVal) => {
|
||
if (newVal && props.secretLevelOptions.length > 0) {
|
||
selectedSecretLevel.value = props.secretLevelOptions[0].value;
|
||
}
|
||
});
|
||
</script>
|
||
|
||
<style lang="less" scoped>
|
||
.secret-tip {
|
||
margin-top: 8px;
|
||
color: #faad14;
|
||
font-size: 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
}
|
||
|
||
.selected-files-summary {
|
||
max-height: 150px;
|
||
overflow-y: auto;
|
||
|
||
.summary-item {
|
||
padding: 2px 0;
|
||
font-size: 12px;
|
||
color: #595959;
|
||
}
|
||
}
|
||
</style>
|