+ 点击或将文件拖拽到这里上传
+
最多可上传 {{ 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);
}