diff --git a/src/views/common/UploadMoreFile.vue b/src/views/common/UploadMoreFile.vue index 5c26738..380691c 100644 --- a/src/views/common/UploadMoreFile.vue +++ b/src/views/common/UploadMoreFile.vue @@ -4,36 +4,13 @@ :before-upload="beforeUpload" :on-remove="handleRemove" :on-change="handleFileChange" :on-exceed="handleExceed" :file-list="files" :accept="accept" :limit="limitUploadNum" :auto-upload="autoUpload" :http-request="customUpload"> +
- -
- -
-
-
点击更换图片
-
或拖拽新图片到此处
-
-
-
- - -
-
- -
{{ previewFileName }}
-
-
-
-
点击更换文件
-
或拖拽新文件到此处
-
-
-
- -
+
+ 点击或将文件拖拽到这里上传
+
最多可上传 {{ limitUploadNum }} 个文件,
单份文件大小上限 {{ maxFileTips }},
支持文件类型:{{ uploadType }}
@@ -50,6 +27,7 @@ import { uploadSmallFile, uploadLargeFile, } from '@/api/common/uploadFile.js' + export default { name: 'UploadFile', props: { @@ -67,7 +45,7 @@ export default { }, limitUploadNum: { type: Number, - default: 1, + default: 5, }, fileUploadRule: { type: Object, @@ -85,39 +63,22 @@ export default { data() { return { files: [], - previewImageUrl: '', - previewImageName: '', - previewFileName: '', - previewFileType: '', - isUploading: false, // 添加上传状态标识 - defaultFileSize: 1024 * 1024 * 5, // 默认文件大小5MB 超过5MB算大文件上传 - skipNextChange: false, + isUploading: false, + uploadingFiles: new Set(), + defaultFileSize: 1024 * 1024 * 5, + uploadControllers: new Map(), } }, computed: { - // 是否显示图片预览 - showImagePreview() { - return this.previewImageUrl && this.files.length === 1 - }, - // 是否显示文件预览 - showFilePreview() { - return ( - this.previewFileName && - this.previewFileType && - this.files.length === 1 - ) - }, accept() { return this.uploadType .split('、') .map((type) => `.${type}`) .join(',') }, - // 获取支持的文件类型数组 allowedTypes() { return this.uploadType.split('、') }, - // 获取文件大小限制(转换为MB数字) maxSizeMB() { const sizeStr = this.maxFileTips.toLowerCase() if (sizeStr.includes('mb')) { @@ -129,7 +90,6 @@ export default { } return 20 }, - // 获取MIME类型映射 mimeTypes() { const typeMap = { png: 'image/png', @@ -148,15 +108,11 @@ export default { fileList: { handler(newVal) { if (this.files.length === 0 && newVal.length > 0) { - // 使用 $nextTick 确保 DOM 更新完成 this.$nextTick(() => { - // 直接设置上传组件的内部文件列表 if (this.$refs.upload) { this.$refs.upload.uploadFiles = this.formatFileList(newVal) } this.files = this.formatFileList(newVal) - // 单独处理预览 - this.handlePreviewFromExternal(newVal) }) } }, @@ -166,20 +122,9 @@ export default { }, methods: { beforeUpload(file) { - // 如果正在上传中,阻止新的上传 - if (this.isUploading) { - this.$message.warning('当前有文件正在上传,请稍后再试') - return false - } - - // 验证文件类型 const fileExtension = file.name.split('.').pop().toLowerCase() const isAllowedType = this.allowedTypes.includes(fileExtension) - - // 验证MIME类型(额外验证) const isAllowedMimeType = this.mimeTypes.includes(file.type) - - // 验证文件大小 const isLtMaxSize = file.size / 1024 / 1024 < this.maxSizeMB if (!isAllowedType || !isAllowedMimeType) { @@ -192,150 +137,209 @@ export default { return false } + if (this.files.length >= this.limitUploadNum) { + this.$message.error(`最多只能上传 ${this.limitUploadNum} 个文件!`) + return false + } + return true }, - // 文件状态改变 + handleFileChange(file, fileList) { - // 如果文件状态是移除或失败或者成功,不处理 if (file.status === 'removed' || file.status === 'fail' || file.status === 'success') { - console.log('现在的文件:', this.files); return } - // 更新文件列表 + this.files = this.formatFileList(fileList) - // 生成预览 - if (file.raw && fileList.length === 1) { - if (this.isImageFile(file.raw)) { - // 图片预览 - this.generateImagePreview(file.raw) - } else if (this.isDocumentFile(file.raw)) { - // 文档预览 - this.generateDocumentPreview(file.raw) - } - } else { - // 如果不是单个文件,清除预览 - this.clearPreview() + + if (this.autoUpload && file.status === 'ready' && !this.isUploading) { + this.$nextTick(() => { + this.uploadFile(file) + }) } }, - // 添加上传成功的处理 - handleSuccess(response, file) { - this.$bus.$emit('endUpload') - // 更新文件状态为成功 - this.updateFileStatus(file.uid, 'success', '', response.data, 100) - // 触发文件变化事件 - this.$emit('file-change', this.getCurrentFiles(), this.type) - // 重置上传状态 - this.isUploading = false - }, - - // 添加上传失败的处理 - handleError(error, file) { - console.error('上传失败:', error) - this.$bus.$emit('endUpload') - // 上传失败,移除文件 - this.removeFailedFile(file.uid) - // 重置上传状态 - this.isUploading = false - }, - // 自定义上传逻辑 - async customUpload(options) { - console.log(options); - - const { file } = options; - // 设置上传状态 - this.isUploading = true; - const formData = new FormData(); - formData.append('file', file); - formData.append('params', JSON.stringify(this.fileUploadRule)); - - // 从 el-upload 的文件列表中查找对应的完整文件对象 - const uploadFileObj = this.findFileByRawFile(file); - if (!uploadFileObj) { - console.error('未找到对应的文件对象'); - this.handleError(new Error('文件对象不存在'), file); - return; + async uploadFile(file) { + if (this.uploadingFiles.has(file.uid)) { + return } - const fileUid = uploadFileObj.uid; + const controller = new AbortController() + this.uploadControllers.set(file.uid, controller) + this.uploadingFiles.add(file.uid) + this.isUploading = true + + const formData = new FormData() + formData.append('file', file.raw) + formData.append('params', JSON.stringify(this.fileUploadRule)) - // 更新文件状态为上传中,并设置初始进度为 0 this.updateFileStatus( - fileUid, + file.uid, 'uploading', this.fileUploadRule.fields_json ? '识别中' : '上传中', null, 0, - ); + ) try { this.$bus.$emit( 'startUpload', this.fileUploadRule.fields_json ? '识别中' : '上传中', - ); + ) - let res = null; + let res = null if (this.defaultFileSize < file.size) { if (this.fileUploadRule.fields_json) { - res = await uploadLargeFileByOcr(formData); + res = await uploadLargeFileByOcr(formData, { + signal: controller.signal, + onUploadProgress: (progressEvent) => { + if (progressEvent.lengthComputable) { + const percentComplete = Math.round( + (progressEvent.loaded * 100) / progressEvent.total + ) + this.updateFileStatus(file.uid, 'uploading', '', null, percentComplete) + } + } + }) } else { - res = await uploadLargeFile(formData); + res = await uploadLargeFile(formData, { + signal: controller.signal, + onUploadProgress: (progressEvent) => { + if (progressEvent.lengthComputable) { + const percentComplete = Math.round( + (progressEvent.loaded * 100) / progressEvent.total + ) + this.updateFileStatus(file.uid, 'uploading', '', null, percentComplete) + } + } + }) } } else { if (this.fileUploadRule.fields_json) { - res = await uploadSmallFileByOcr(formData); + res = await uploadSmallFileByOcr(formData, { + signal: controller.signal + }) } else { - res = await uploadSmallFile(formData); + res = await uploadSmallFile(formData, { + signal: controller.signal + }) } } + if (res.code === 200) { - this.handleSuccess(res, uploadFileObj); + this.handleSuccess(res, file) } else { - this.handleError(new Error(res.message || '上传失败'), uploadFileObj); + this.handleError(new Error(res.message || '上传失败'), file) } } catch (err) { - this.handleError(err, uploadFileObj); + if (err.name === 'AbortError') { + console.log('上传已取消:', file.name) + } else { + this.handleError(err, file) + } } finally { - // 无论成功失败,都重置上传状态 - this.isUploading = false; + this.uploadingFiles.delete(file.uid) + this.uploadControllers.delete(file.uid) + this.isUploading = this.uploadingFiles.size > 0 + } + }, + + async customUpload(options) { + const { file } = options + const uploadFileObj = this.findFileByRawFile(file) + if (!uploadFileObj) { + console.error('未找到对应的文件对象') + return + } + this.uploadFile(uploadFileObj) + }, + + handleSuccess(response, file) { + this.$bus.$emit('endUpload') + this.updateFileStatus(file.uid, 'success', '', response.data, 100) + this.$emit('file-change', this.getCurrentFiles(), this.type) + }, + + handleError(error, file) { + console.error('上传失败:', error) + this.$bus.$emit('endUpload') + this.updateFileStatus(file.uid, 'fail', error.message) + this.$emit('file-change', this.getCurrentFiles(), this.type) + }, + + handleExceed(files, fileList) { + this.$message.warning(`最多只能上传 ${this.limitUploadNum} 个文件,已自动截取前 ${this.limitUploadNum} 个文件`); + + // 将 FileList 转换为数组 + const filesArray = Array.from(files); + const remainingSlots = this.limitUploadNum - this.files.length; + + if (remainingSlots > 0) { + const filesToAdd = filesArray.slice(0, remainingSlots); + filesToAdd.forEach(file => { + const newFileObj = this.createFileObject(file); + this.files.push(newFileObj); + + if (this.autoUpload) { + this.$nextTick(() => { + this.uploadFile(newFileObj); + }); + } + }); + } + }, + + handleRemove(file, fileList) { + if (file == null) { + return true + } + + if (file.status === 'uploading') { + this.cancelUpload(file) + } + + const delFileObj = this.findFileByRawFile(file.raw) + if (delFileObj) { + delFileObj.response = delFileObj.res + this.$emit('del-file', delFileObj) + } + + this.files = this.formatFileList(fileList) + this.$emit('file-change', this.getCurrentFiles(), this.type) + return true + }, + + cancelUpload(file) { + const controller = this.uploadControllers.get(file.uid) + if (controller) { + controller.abort() + } + this.updateFileStatus(file.uid, 'fail', '上传已取消') + this.uploadingFiles.delete(file.uid) + this.uploadControllers.delete(file.uid) + }, + + createFileObject(file) { + return { + name: file.name, + size: file.size, + type: file.type, + raw: file, + uid: Date.now() + Math.random(), + status: 'ready', + percentage: 0, } }, - // 通过原始文件对象查找对应的上传文件对象 findFileByRawFile(rawFile) { return this.files.find(item => item.raw === rawFile || (item.name === rawFile.name && item.size === rawFile.size) - ); - }, - // 移除上传失败的文件 - removeFailedFile(fileUid) { - console.log('移除上传失败的文件:', fileUid) - const fileIndex = this.files.findIndex( - (item) => item.uid === fileUid, ) - if (fileIndex !== -1) { - // 从文件列表中移除 - this.files.splice(fileIndex, 1) - console.log('移除失败文件后的文件列表:', this.files) - // 清除预览 - this.clearPreview() - // 触发文件变化事件 - this.$emit('file-change', this.getCurrentFiles(), this.type) - } }, - // 更新文件状态 - updateFileStatus( - fileUid, - status, - statusText, - responseData = null, - percentage = null, - ) { - const fileIndex = this.files.findIndex( - (item) => item.uid === fileUid, - ) + updateFileStatus(fileUid, status, statusText, responseData = null, percentage = null) { + const fileIndex = this.files.findIndex(item => item.uid === fileUid) if (fileIndex !== -1) { const updatedFile = { ...this.files[fileIndex], @@ -346,119 +350,26 @@ export default { updatedFile.statusText = statusText } - // 确保 percentage 是有效的数字(0-100) - if ( - percentage !== null && - percentage >= 0 && - percentage <= 100 - ) { + if (percentage !== null && percentage >= 0 && percentage <= 100) { updatedFile.percentage = percentage } else if (status === 'uploading') { - // 上传中状态默认设置进度为 0 updatedFile.percentage = 0 } else if (status === 'success') { - // 成功状态设置进度为 100 updatedFile.percentage = 100 } if (responseData) { - // 保存服务器返回的文件信息 - updatedFile.response = responseData; - updatedFile.response.businessType = - this.fileUploadRule?.fileUploadType; - updatedFile.res = updatedFile.response; + updatedFile.response = responseData + updatedFile.response.businessType = this.fileUploadRule?.fileUploadType + updatedFile.res = updatedFile.response } - // 使用 Vue.set 确保响应式更新 this.$set(this.files, fileIndex, updatedFile) - console.log('更新后的文件列表:', this.files) - } else { - console.warn('未找到要更新的文件:', fileUid) } }, - // 处理文件超出限制 - handleExceed(files, fileList) { - console.log('文件超出限制处理', files, fileList) - // 当文件数量超出限制时,用新文件替换旧文件 - if (files.length > 0) { - // 触发文件删除事件 - this.$emit('del-file', { ...fileList[0], response: fileList[0].res }) - - // 清空原有文件列表 - this.files = [] - - // 手动触发新文件的上传流程 - const newFile = files[0] - // 创建新的文件对象,确保包含 percentage - const newFileObj = { - name: newFile.name, - size: newFile.size, - type: newFile.type, - raw: newFile, - uid: Date.now(), // 生成新的uid - status: 'ready', - percentage: 0, // 添加默认进度 - } - - // 更新文件列表 - this.files = [newFileObj] - - // 生成预览 - if (this.isImageFile(newFile)) { - this.generateImagePreview(newFile) - } else if (this.isDocumentFile(newFile)) { - this.generateDocumentPreview(newFile) - } - - console.log('handleExceed 后的文件列表:', this.files) - // 自动上传新文件 - if (this.autoUpload && !this.isUploading) { - this.$nextTick(() => { - this.$refs.upload.submit(); - }); - } - } - }, - - handleRemove(file, fileList) { - if (file == null) { - this.clearPreview(); - return true; - } - // 如果正在上传中,阻止移除 - if (this.isUploading && file.status === 'uploading') { - this.$message.warning('文件正在上传中,请稍后再删除'); - return false; // 阻止删除操作 - } - // 根据剩余文件重新生成预览 - if (fileList.length === 0) { - this.clearPreview(); - } else if (fileList.length === 1 && fileList[0] && fileList[0].raw) { - if (this.isImageFile(fileList[0].raw)) { - this.generateImagePreview(fileList[0].raw); - } else if (this.isDocumentFile(fileList[0].raw)) { - this.generateDocumentPreview(fileList[0].raw); - } - } else { - this.clearPreview(); - } - const delFileObj = this.findFileByRawFile(file.raw); - delFileObj.response = delFileObj.res; - // 更新文件列表 - this.files = this.formatFileList(fileList); - // 触发文件删除事件给父组件 - this.$emit('del-file', delFileObj); - // 传递当前文件信息给父组件 - this.$emit('file-change', this.getCurrentFiles(), this.type); - // 返回 true 允许删除继续,false 阻止删除 - return true; - }, - - // 格式化文件列表,确保数据结构正确 formatFileList(fileList) { return fileList.map((file) => { - // 创建一个标准的文件对象 const formattedFile = { uid: file.uid, name: file.name, @@ -468,40 +379,25 @@ export default { raw: file.raw, } - // 确保 percentage 属性存在且有效 if (file.percentage !== undefined && file.percentage !== null) { - formattedFile.percentage = Math.max( - 0, - Math.min(100, file.percentage), - ) + formattedFile.percentage = Math.max(0, Math.min(100, file.percentage)) } else if (file.status === 'uploading') { formattedFile.percentage = 0 } else if (file.status === 'success') { formattedFile.percentage = 100 } - // 保留其他可能存在的属性 if (file.response) { - formattedFile.response = file.response; - formattedFile.res = file.response; - } - - if (file.filePath) { - formattedFile.filePath = file.filePath; - } - - - if (file.statusText) { - formattedFile.statusText = file.statusText + formattedFile.response = file.response + formattedFile.res = file.response } return formattedFile }) }, - // 获取当前文件列表(确保是普通数组) getCurrentFiles() { - const currentFiles = this.files.map((file) => { + return this.files.map((file) => { const fileObj = { uid: file.uid, name: file.name, @@ -510,125 +406,22 @@ export default { status: file.status, } - // 确保 percentage 存在 - if (file.percentage !== undefined) { - fileObj.percentage = file.percentage - } - - // 保留原始文件对象(如果存在) if (file.raw) { fileObj.raw = file.raw } - // 如果文件已上传成功,添加服务器返回的信息 if (file.response) { fileObj.response = file.response - fileObj.response.businessType = - this.fileUploadRule?.fileUploadType + fileObj.response.businessType = this.fileUploadRule?.fileUploadType } return fileObj }) - - return currentFiles - }, - - - // 判断是否为图片文件 - isImageFile(file) { - return ( - (file && file.type && file.type.startsWith('image/')) || - (file && file.fileType === '1') - ) - }, - // 判断是否为文档文件 - isDocumentFile(file) { - if (!file || !file.name) return false - const fileExtension = file.name.split('.').pop().toLowerCase() - return ( - ['pdf', 'doc', 'docx', 'xls', 'xlsx'].includes(fileExtension) || - (file && file.fileType === '2') - ) - }, - // 生成图片预览 - generateImagePreview(file) { - const reader = new FileReader() - reader.onload = (e) => { - this.previewImageUrl = e.target.result - this.previewImageName = file.name - // 清除文档预览 - this.previewFileName = '' - this.previewFileType = '' - } - reader.readAsDataURL(file) - }, - // 修改回显生成图片预览 - generateImagePreviewFromPath(file) { - this.previewImageUrl = file.lsFilePath - this.previewImageName = file.name - // 清除文档预览 - this.previewFileName = '' - this.previewFileType = '' - }, - // 生成文档预览 - generateDocumentPreview(file) { - const fileExtension = file.name.split('.').pop().toLowerCase() - this.previewFileName = file.name - this.previewFileType = fileExtension - // 清除图片预览 - this.previewImageUrl = '' - this.previewImageName = '' - }, - // 清除预览 - clearPreview() { - this.previewImageUrl = '' - this.previewImageName = '' - this.previewFileName = '' - this.previewFileType = '' - }, - // 获取文件图标类型 - getFileIconClass() { - const iconMap = { - pdf: 'el-icon-document', - doc: 'el-icon-document', - docx: 'el-icon-document', - xls: 'el-icon-document', - xlsx: 'el-icon-document', - } - return iconMap[this.previewFileType] || 'el-icon-document' - }, - // 清空所有文件 - clearFiles() { - this.files = [] - this.clearPreview() - this.$emit('file-change', []) - }, - // 专门处理外部传入的文件预览 - handlePreviewFromExternal(fileList) { - if (fileList.length > 0) { - const firstFile = fileList[0] - if (firstFile && firstFile.lsFilePath) { - if (this.isImageFile(firstFile)) { - this.generateImagePreviewFromPath(firstFile) - } else if (this.isDocumentFile(firstFile)) { - this.generateDocumentPreview(firstFile) - } - } else if (firstFile && firstFile.raw) { - if (this.isImageFile(firstFile.raw)) { - this.generateImagePreview(firstFile.raw) - } else if (this.isDocumentFile(firstFile.raw)) { - this.generateDocumentPreview(firstFile.raw) - } - } else { - this.clearPreview() - } - } else { - this.clearPreview() - } }, }, } + + \ No newline at end of file diff --git a/src/views/enterpriseLibrary/technical/components/child/FileInfo.vue b/src/views/enterpriseLibrary/technical/components/child/FileInfo.vue index 0244a52..8b34a7d 100644 --- a/src/views/enterpriseLibrary/technical/components/child/FileInfo.vue +++ b/src/views/enterpriseLibrary/technical/components/child/FileInfo.vue @@ -20,13 +20,13 @@ const defaultParams = { fileType: 'technical_solution', uploadType: 'pdf、doc、docx', - maxFileTips: '1MB', + maxFileTips: '500MB', fileUploadRule: { fileUploadType: 'technical_solution', fields_json: '', suffix: 'technical_solution_database' }, - limitUploadNum: 10 + limitUploadNum: 5 }; import UploadMoreFile from '@/views/common/UploadMoreFile.vue' export default { @@ -84,7 +84,6 @@ export default { // 文件删除时触发 handleDelFile(file) { const delPath = file?.response?.fileRes?.filePath || file?.filePath || null; - alert(delPath); if(delPath){ this.form.delFileList.push(delPath); }