完善文件上传

This commit is contained in:
ChenYi 2025-07-28 17:41:56 +08:00
parent b16a495cf1
commit 9a9420f423
4 changed files with 289 additions and 31 deletions

View File

@ -54,5 +54,6 @@
"exportFailed": "Data export failed",
"getDataFailed": "Failed to get data",
"PhoneNumberFormatError": "PhoneNumber Format Error",
"IoTPlatform": "IoTPlatform"
"IoTPlatform": "IoTPlatform",
"note": "note"
}

View File

@ -55,5 +55,6 @@
"getDataFailed": "获取数据失败",
"IsEnabled": "是否启用",
"PhoneNumberFormatError": "手机号码格式错误",
"IoTPlatform": "物联网平台"
"IoTPlatform": "物联网平台",
"note": "备注"
}

View File

@ -1,12 +1,26 @@
<script lang="ts" setup>
import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { message } from 'ant-design-vue';
import { createIconifyIcon } from '@vben/icons';
import { useVbenForm } from '#/adapter/form';
import { $t } from '#/locales';
import { addFormSchema } from './schema';
import { postFilesUpload } from '#/api-client/index';
const emit = defineEmits(['reload']);
//
const UploadIcon = createIconifyIcon('mdi:upload');
const FolderIcon = createIconifyIcon('mdi:folder');
//
const fileList = ref<any[]>([]);
const uploading = ref(false);
const fileInput = ref<HTMLInputElement>();
const [Form, formApi] = useVbenForm({
//
commonConfig: {
@ -22,18 +36,277 @@ const [Form, formApi] = useVbenForm({
schema: addFormSchema,
wrapperClass: 'grid-cols-1',
});
//
const handleFileChange = (info: any) => {
console.log('File change:', info);
console.log('File list before:', fileList.value);
fileList.value = info.fileList;
console.log('File list after:', fileList.value);
};
//
const beforeUpload = (file: File) => {
//
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isLt10M) {
message.error('文件大小不能超过10MB!');
return false;
}
return true; //
};
//
const handleRemove = (file: any) => {
const index = fileList.value.indexOf(file);
const newFileList = fileList.value.slice();
newFileList.splice(index, 1);
fileList.value = newFileList;
};
//
const triggerFileInput = () => {
fileInput.value?.click();
};
//
const handleFileInputChange = (event: Event) => {
const target = event.target as HTMLInputElement;
if (target.files) {
const newFiles = Array.from(target.files).map(file => ({
name: file.name,
size: file.size,
type: file.type,
originFileObj: file
}));
// 10MB
const validFiles = newFiles.filter(file => {
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isLt10M) {
message.error(`文件 ${file.name} 大小超过10MB限制`);
return false;
}
return true;
});
fileList.value = [...fileList.value, ...validFiles];
}
};
//
const handleDrop = (e: DragEvent) => {
console.log('Drop event:', e);
e.preventDefault();
if (e.dataTransfer?.files) {
const newFiles = Array.from(e.dataTransfer.files).map(file => ({
name: file.name,
size: file.size,
type: file.type,
originFileObj: file
}));
// 10MB
const validFiles = newFiles.filter(file => {
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isLt10M) {
message.error(`文件 ${file.name} 大小超过10MB限制`);
return false;
}
return true;
});
fileList.value = [...fileList.value, ...validFiles];
}
};
//
const removeFile = (index: number) => {
fileList.value.splice(index, 1);
};
//
const formatFileSize = (bytes: number) => {
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 parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
//
const handleUpload = async () => {
if (fileList.value.length === 0) {
message.warning('请选择要上传的文件');
return;
}
uploading.value = true;
try {
//
const formValues = await formApi.getValues();
console.log('Form values:', formValues);
console.log('File list:', fileList.value);
// FormData
const uploadFormData = new FormData();
// FormData
fileList.value.forEach((fileInfo, index) => {
if (fileInfo.originFileObj) {
console.log(`Adding file ${index}:`, fileInfo.name, fileInfo.originFileObj);
uploadFormData.append('files', fileInfo.originFileObj);
}
});
//
if (formValues) {
Object.keys(formValues).forEach(key => {
if (formValues[key] !== undefined && formValues[key] !== null) {
console.log(`Adding form field ${key}:`, formValues[key]);
uploadFormData.append(key, formValues[key]);
}
});
}
// FormData
for (let [key, value] of uploadFormData.entries()) {
console.log(`FormData entry: ${key} =`, value);
}
//
const result = await postFilesUpload({
body: uploadFormData as any,
});
console.log('Upload result:', result);
//
if (result.status === 204 || result.status === 200) {
message.success(`文件上传成功!共上传 ${fileList.value.length} 个文件`);
emit('reload');
modalApi.close();
} else {
message.error('文件上传失败,请重试');
}
} catch (error) {
console.error('Upload error:', error);
message.error('文件上传失败');
} finally {
uploading.value = false;
}
};
const [Modal, modalApi] = useVbenModal({
onCancel() {
fileList.value = [];
modalApi.close();
},
async onConfirm() {
emit('reload');
modalApi.close();
await handleUpload();
},
});
</script>
<template>
<Modal :title="$t('common.upload')">
<Modal :title="$t('common.upload')" :confirm-loading="uploading">
<Form />
<div class="mt-4">
<div class="upload-area" @click="triggerFileInput" @drop="handleDrop" @dragover.prevent>
<input
ref="fileInput"
type="file"
multiple
accept="*/*"
style="display: none"
@change="handleFileInputChange"
/>
<div class="ant-upload-drag-container">
<p class="ant-upload-drag-icon">
<FolderIcon />
</p>
<p class="ant-upload-text">点击或拖拽文件到此区域上传</p>
<p class="ant-upload-hint">
支持单个或批量上传严禁上传公司数据或其他敏感文件
</p>
</div>
</div>
<!-- 显示已选择的文件列表 -->
<div v-if="fileList.length > 0" class="mt-4">
<h4>已选择的文件</h4>
<div v-for="(file, index) in fileList" :key="index" class="file-item">
<div class="file-info">
<span class="file-name">{{ file.name }}</span>
<span class="file-size">{{ formatFileSize(file.size) }}</span>
</div>
<button type="button" class="ant-btn ant-btn-link ant-btn-sm" @click="removeFile(index)">删除</button>
</div>
</div>
</div>
</Modal>
</template>
<style scoped>
.ant-upload-drag-container {
padding: 20px;
text-align: center;
border: 2px dashed #d9d9d9;
border-radius: 6px;
background: #fafafa;
transition: border-color 0.3s;
}
.ant-upload-drag-container:hover {
border-color: #1890ff;
}
.ant-upload-drag-icon {
font-size: 48px;
color: #999;
margin-bottom: 16px;
}
.ant-upload-text {
font-size: 16px;
color: #666;
margin-bottom: 8px;
}
.ant-upload-hint {
font-size: 14px;
color: #999;
}
.upload-area {
cursor: pointer;
}
.file-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px;
border: 1px solid #d9d9d9;
border-radius: 4px;
margin-bottom: 8px;
background: #fafafa;
}
.file-info {
display: flex;
flex-direction: column;
flex: 1;
}
.file-name {
font-weight: 500;
color: #333;
margin-bottom: 2px;
}
.file-size {
font-size: 12px;
color: #666;
}
</style>

View File

@ -10,6 +10,9 @@ import { postFilesDelete } from '#/api-client/index';
import { $t } from '#/locales';
const userStore = useUserStore();
export const querySchema = computed(() => [
{
component: 'RangePicker',
@ -66,33 +69,13 @@ export const tableSchema: any = computed((): VxeGridProps['columns'] => [
]);
export const addFormSchema = computed(() => [
// 文件上传现在在AddModal中处理
{
component: 'Upload',
fieldName: 'files',
label: $t('abp.file.file'),
componentProps: () => {
return {
listType: 'picture-card',
autoUpload: true,
multiple: true,
name: 'files',
action: `${import.meta.env.VITE_APP_API_ADDRESS}/Files/Upload?access_token=${
userStore.userInfo?.token
}`,
onPreview: () => {},
onRemove: async (file: any) => {
await postFilesDelete({
body: {
id: file.response[0].id,
},
});
},
};
},
renderComponentContent: () => {
return {
default: () => 'Upload',
};
component: 'Input',
fieldName: 'note',
label: $t('common.note'),
componentProps: {
placeholder: '请输入备注信息',
},
},
]);