Files
supervision-test-frontend-repo/src/components/semri/fileComponent/SFileUploadModal.vue
T

264 lines
7.2 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>
<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>