diff --git a/src/api/prearchiveLibrary/prearchiveLibrary.js b/src/api/prearchiveLibrary/prearchiveLibrary.js index 880c577..9de4409 100644 --- a/src/api/prearchiveLibrary/prearchiveLibrary.js +++ b/src/api/prearchiveLibrary/prearchiveLibrary.js @@ -117,4 +117,12 @@ export function FetchReDocumentBase64ByFileId(params) { }) } -export default { add, prearchEdit, del, FetchInitPreDocument, FetchInitDocumentsViewTable, FetchDoeditDocument, FetchBatchToFile, FetchMergeToFile, FetchMove, FetchArchivesDetails, FetchFileListByDocumentId, FetchArchivesMetadata, FetchReDocumentBase64ByFileId } +export function FetchMinioReDocumentBase64ByFileId(params) { + return request({ + url: 'api/minioUpload/getBase64', + method: 'get', + params + }) +} + +export default { add, prearchEdit, del, FetchInitPreDocument, FetchInitDocumentsViewTable, FetchDoeditDocument, FetchBatchToFile, FetchMergeToFile, FetchMove, FetchArchivesDetails, FetchFileListByDocumentId, FetchArchivesMetadata, FetchReDocumentBase64ByFileId, FetchMinioReDocumentBase64ByFileId } diff --git a/src/views/components/category/PreviewForm.vue b/src/views/components/category/PreviewForm.vue index 8da32a5..3eec61b 100644 --- a/src/views/components/category/PreviewForm.vue +++ b/src/views/components/category/PreviewForm.vue @@ -290,7 +290,10 @@ export default { fileList: [], archivesSummaryResponse: {}, pickerDateMap: {}, - minioPreResult: {}, + // 初始化(如Vue的data/React的state/普通对象的属性) + minioPreResult: [], // 存储合并后的minio结果 + jsonArrayStore: [], // 存储每次的jsonArrayToSend项,与minioPreResult一一对应 + fileNameStore: [], // 存储每次的fileName,与minioPreResult一一对应 iconShowRule: { '3-1': 'item_no', '3-2': 'record_no', @@ -373,53 +376,100 @@ export default { }, preUplpadClose() { - this.$refs.preUploadRefs.handleClearData() + this.$refs.preUploadRefs.handleCloseDialog() }, - handleSuccessResource(filePath, fileName, jsonArrayToSend) { - this.minioPreResult = filePath - console.log('handleSuccessResource', filePath) - console.log('handleSuccessResource', fileName) - console.log('handleSuccessResource', jsonArrayToSend) - console.log('handleSuccessResource', jsonArrayToSend[0].fileJsonString) - // this.fileOriginal = fileName.join(',') - this.fileOriginal = fileName + // handleSuccessResource(data, fileName, jsonArrayToSend) { + // this.minioPreResult = data + // // console.log('handleSuccessResource', filePath) + // // console.log('handleSuccessResource', fileName) + // // console.log('handleSuccessResource', jsonArrayToSend) + // // console.log('handleSuccessResource', jsonArrayToSend[0].fileJsonString) + // this.fileOriginal = fileName.join(',') + // // this.fileOriginal = fileName - const fileJson = JSON.parse(jsonArrayToSend[0].fileJsonString) - fileJson[0].file_path = '/' + filePath.filePath - fileJson[0].is_quote = null - fileJson[0].last_modified = jsonArrayToSend[0].last_modified - fileJson[0].ca_id = this.minioPreResult.caId - fileJson[0].encryption_time = this.minioPreResult.timestamp - fileJson[0].signature = this.minioPreResult.signature - this.fileJsonString = JSON.stringify(fileJson) - }, - // handleSuccessResource(filePath, fileName, jsonArrayToSend) { - // console.log('handleSuccessResource', filePath) - // console.log('handleSuccessResource', fileName) - // console.log('handleSuccessResource', jsonArrayToSend) + // const fileJson = JSON.parse(jsonArrayToSend[0].fileJsonString) + // fileJson[0].file_path = '/' + data.filePath + // fileJson[0].is_quote = null + // fileJson[0].last_modified = jsonArrayToSend[0].last_modified + // fileJson[0].ca_id = this.minioPreResult.caId + // fileJson[0].encryption_time = this.minioPreResult.timestamp + // fileJson[0].signature = this.minioPreResult.signature + // this.fileJsonString = JSON.stringify(fileJson) + // }, + handleSuccessResource(data, fileName, jsonArrayToSend) { + // 1. 初始化全局数组,确保为数组类型 + if (!Array.isArray(this.minioPreResult)) this.minioPreResult = [] + if (!Array.isArray(this.jsonArrayStore)) this.jsonArrayStore = [] + if (!Array.isArray(this.fileNameStore)) this.fileNameStore = [] - // this.fileOriginal = fileName.join(',') + // 2. 统一处理入参格式:转为数组,兼容单个值的情况 + const newMinioData = Array.isArray(data) ? data : [data] // 新minio项数组 + const newFileNames = Array.isArray(fileName) ? fileName : [fileName] // 新文件名数组 + const newJsonData = Array.isArray(jsonArrayToSend) ? jsonArrayToSend : [jsonArrayToSend] // 新json项数组 - // // 遍历jsonArrayToSend,将每个item处理成对象并收集到数组中 - // const fileJsonList = jsonArrayToSend.flatMap(item => { - // // 解析当前item的fileJsonString - // const parsed = JSON.parse(item.fileJsonString) + // 3. 按文件名去重,合并三组数据(核心逻辑) + newMinioData.forEach((minioItem, index) => { + // 获取当前索引对应的文件名(容错:避免文件名数组长度不足) + const currentFileName = newFileNames[index]?.trim() || `未知文件_${index}` + // 获取当前索引对应的json项(容错) + const currentJsonItem = newJsonData[index] || {} - // // 如果解析结果是数组,直接取其元素;如果是单个对象,包装成数组 - // const fileItems = Array.isArray(parsed) ? parsed : [parsed] + // 3.1 判断文件名是否重复:检查全局fileNameStore中是否已存在该文件名 + const isDuplicate = this.fileNameStore.some( + storedName => storedName.trim() === currentFileName + ) - // // 处理每个对象的属性 - // return fileItems.map(fileItem => ({ - // ...fileItem, // 保留原对象的其他属性 - // file_path: '/' + filePath, - // is_quote: null, - // last_modified: item.last_modified - // })) - // }) - // console.log('fileJsonList', fileJsonList) - // // 最终fileJsonList是包含多个{}的数组 - // this.fileJsonString = JSON.stringify(fileJsonList) - // }, + if (isDuplicate) { + console.log(`文件名【${currentFileName}】已存在,跳过合并该条数据`) + return + } + + // 3.2 不重复则同步追加三组数据(保证索引一一对应) + this.minioPreResult.push(minioItem) + this.jsonArrayStore.push(currentJsonItem) + this.fileNameStore.push(currentFileName) + console.log(`文件名【${currentFileName}】合并成功`) + }) + + // 4. 重新拼接全量文件名(去重后的所有文件名用逗号分隔) + this.fileOriginal = this.fileNameStore.join(',') + + // 5. 基于合并后的全量数据生成fileJsonList + let fileJsonList = [] + this.minioPreResult.forEach((minioItem, index) => { + const jsonItem = this.jsonArrayStore[index] + // 容错:无对应json项或无fileJsonString时跳过 + if (!jsonItem || !jsonItem.fileJsonString) { + console.warn(`第${index}个文件【${this.fileNameStore[index]}】无有效JSON数据,跳过`) + return + } + + try { + // 解析JSON字符串并统一转为数组 + const parsed = JSON.parse(jsonItem.fileJsonString) + const fileItems = Array.isArray(parsed) ? parsed : [parsed] + console.log('minioItem', minioItem) + // 映射minio属性到每个文件项 + const mappedItems = fileItems.map(fileItem => ({ + ...fileItem, + file_path: '/' + (minioItem.filePath || ''), + is_quote: null, + last_modified: minioItem.lastModified, + ca_id: minioItem.caId || '', + encryption_time: minioItem.timestamp || '', + signature: minioItem.signature || '' + })) + + fileJsonList = fileJsonList.concat(mappedItems) + } catch (error) { + console.error(`第${index}个文件【${this.fileNameStore[index]}】JSON解析失败:`, error) + } + }) + + // 6. 生成最终的fileJsonString(此时是去重后的全量数据) + console.log('按文件名去重后的fileJsonList:', fileJsonList) + this.fileJsonString = JSON.stringify(fileJsonList) + }, handleErrorResource(res) { console.log('handleErrorResource', res) }, diff --git a/src/views/components/category/preUpload4.vue b/src/views/components/category/preUpload4.vue index 40599fe..6bec456 100644 --- a/src/views/components/category/preUpload4.vue +++ b/src/views/components/category/preUpload4.vue @@ -1,5 +1,13 @@ @@ -59,7 +94,8 @@ export default { data() { return { - fileList: [], // 多文件上传列表,存储每个文件的状态 + uploadMinioVisible: false, // 弹框显示状态 + fileList: [], // 多文件上传列表 CHUNK_SIZE: 5 * 1024 * 1024, // 分片大小 (5MB) totalMergeStartTime: null, // 整体批量合并开始时间 totalMergeEndTime: null, // 整体批量合并结束时间 @@ -74,6 +110,11 @@ export default { }, methods: { + formatFileSize(bytes) { + if (bytes === 0) return '0.00MB' + const mb = bytes / (1024 * 1024) // 1MB = 1024KB = 1024*1024B + return mb.toFixed(2) + 'MB' + }, /** * 工具方法:格式化时间戳为易读的本地时间(带毫秒) * @param {number} timestamp - 时间戳 @@ -136,7 +177,7 @@ export default { }, /** - * 工具方法:显示提示信息(可替换为项目的Message组件) + * 工具方法:显示提示信息 * @param {string} msg - 提示内容 * @param {string} type - 类型(success/error) */ @@ -158,10 +199,8 @@ export default { try { this.isCheckingCa = true const res = await FetchCheckCaValidity() - this.isCaValid = !!res // 转为布尔值,确保结果是true/false - if (this.isCaValid) { - // this.showMessage('CA证书有效,可进行分片上传', 'success') - } else { + this.isCaValid = !!res // 转为布尔值 + if (!this.isCaValid) { this.showMessage('CA证书不在有效期内,无法进行分片上传', 'error') } } catch (err) { @@ -181,26 +220,27 @@ export default { }, /** - * 多文件选择处理函数 + * 多文件选择处理函数:选择后打开弹框,仅初始化文件列表(不上传) * @param {Event} e - 选择文件的事件对象 */ async handleFileSelect(e) { - // 1. 校验CA状态,无效则拦截上传 + // 1. 校验CA状态 if (this.isCheckingCa) { - this.showMessage('正在校验CA证书,请稍候...', 'error') + this.showMessage('正在校验CA证书,请稍候...', 'warning') + e.target.value = '' return } if (!this.isCaValid) { this.showMessage('CA证书不在有效期内,无法上传文件', 'error') - e.target.value = '' // 清空文件选择框 + e.target.value = '' return } - // 2. 原有的文件上传逻辑 + // 2. 获取选择的文件并初始化列表 const selectedFiles = Array.from(e.target.files) if (selectedFiles.length === 0) return - // 初始化每个文件的上传状态(移除验签相关变量) + // 初始化文件上传状态(仅初始化,不上传) const newFileList = selectedFiles.map(file => ({ file, uploading: false, @@ -218,14 +258,11 @@ export default { })) this.fileList = [...this.fileList, ...newFileList] - // 串行上传文件分片 - for (const fileItem of newFileList) { - await this.uploadFileChunks(fileItem) - } + // 3. 选择文件后立即打开弹框(核心修改点) + this.uploadMinioVisible = true - // 所有文件分片上传完成后,标记状态并触发批量合并 - this.allChunksUploaded = true - this.handleUploadConfirm() + // 清空input的文件选择(避免重复选择同一文件不触发change) + e.target.value = '' }, /** @@ -386,11 +423,34 @@ export default { }, /** - * 所有文件分片上传完成后自动执行批量合并 + * 批量上传并合并:弹框内点击按钮触发(核心修改点:新增串行上传逻辑) */ async handleUploadConfirm() { - if (this.fileList.length === 0 || !this.allChunksUploaded) { - this.showMessage('没有可处理的文件或分片上传未完成!', 'error') + if (this.fileList.length === 0) { + this.showMessage('请先选择要上传的文件!', 'warning') + return + } + + // 过滤掉已上传/合并失败的文件,仅处理未上传的文件 + const pendingFiles = this.fileList.filter(item => !item.successMsg && !item.errorMsg) + if (pendingFiles.length === 0) { + this.showMessage('暂无待上传的文件!', 'warning') + return + } + + // 1. 串行上传所有文件的分片 + this.allChunksUploaded = true + for (const fileItem of pendingFiles) { + await this.uploadFileChunks(fileItem) + // 如果单个文件上传失败,标记整体上传状态为失败 + if (fileItem.errorMsg) { + this.allChunksUploaded = false + } + } + + // 2. 分片上传完成后执行合并 + if (!this.allChunksUploaded) { + this.showMessage('部分文件分片上传失败,无法执行合并', 'error') return } @@ -428,7 +488,7 @@ export default { jsonString.file_name = file.name jsonString.file_size = file.size jsonString.file_type = file.name.split('.').pop() || '' - json.last_modified = file.lastModified + jsonString.last_modified = file.lastModified jsonString.file_path = '' jsonString.sequence = null jsonString.archive_id = this.arcId @@ -437,6 +497,8 @@ export default { jsonString.file_thumbnail = '' jsonArray.push(jsonString) + console.log('file.lastModified', file.lastModified) + if (this.isBatchMount === 'true') { json.categoryId = this.selectedCategory.id } else { @@ -460,8 +522,8 @@ export default { }) const jsonArray = await Promise.all(processFiles) - // 注意:若后端支持批量合并,需将jsonArray整体传入,此处原代码传了第一个元素,需根据后端接口调整 - const response = await axios.post('/api/minioUpload/merge', jsonArray[0], { + // 调用合并接口 + const response = await axios.post('/api/minioUpload/merge', jsonArray, { headers: { 'Authorization': getToken(), 'Content-Type': 'application/json' @@ -472,26 +534,27 @@ export default { const totalMergeDuration = this.getTimeDiff(this.totalMergeStartTime, this.totalMergeEndTime) if (response.data.code === 200) { - this.showMessage('所有文件合并成功', 'success') - validFiles.forEach((fileItem, index) => { + this.showMessage('所有文件上传并合并成功', 'success') + validFiles.forEach((fileItem) => { fileItem.mergeEndTime = new Date().getTime() const mergeDuration = this.getTimeDiff(fileItem.mergeStartTime, fileItem.mergeEndTime) console.log(`【文件${fileItem.file.name}】合并结束时间:${this.formatTime(fileItem.mergeEndTime)},合并耗时:${mergeDuration}`) - // const mergeResult = response.data.data || {} - // fileItem.successMsg = '上传成功! 路径: ' + (mergeResult.filePath || '未知路径') fileItem.successMsg = '上传成功!' fileItem.merging = false }) - console.log(`【整体合并】所有文件合并完成,合并结束时间:${this.formatTime(this.totalMergeEndTime)},整体合并耗时:${totalMergeDuration}`) + console.log(`【整体合并】所有文件合并完成,整体合并耗时:${totalMergeDuration}`) this.$emit('onUploadSuccess', response.data.data, validFiles.map(f => f.file.name), jsonArray) + + this.uploadMinioVisible = false + this.fileList = [] // 清空列表,避免下次打开弹框显示旧数据 } else { throw new Error(response.data.msg || '合并失败') } } catch (err) { this.totalMergeEndTime = new Date().getTime() const totalMergeDuration = this.getTimeDiff(this.totalMergeStartTime, this.totalMergeEndTime) - console.log(`【整体合并】合并失败,失败时间:${this.formatTime(this.totalMergeEndTime)},合并耗时:${totalMergeDuration},异常信息:`, err) - this.showMessage(`合并失败: ${err.message}`, 'error') + console.log(`【整体合并】合并失败,耗时:${totalMergeDuration},异常信息:`, err) + this.showMessage(`文件合并失败: ${err.message}`, 'error') validFiles.forEach(fileItem => { fileItem.merging = false fileItem.errorMsg = fileItem.errorMsg || `合并失败: ${err.message}` @@ -501,8 +564,14 @@ export default { this.resetUploadState() } }, - handleClearData() { + + /** + * 关闭弹框时清空文件列表 + */ + handleCloseDialog() { + this.uploadMinioVisible = false this.fileList = [] + this.resetUploadState() } } } @@ -511,58 +580,41 @@ export default { diff --git a/src/views/components/categoryTree.vue b/src/views/components/categoryTree.vue index c7fb559..6ca15f2 100644 --- a/src/views/components/categoryTree.vue +++ b/src/views/components/categoryTree.vue @@ -6,7 +6,7 @@

档案门类

- + @@ -106,7 +106,6 @@ export default { localStorage.removeItem('currentArchivesKey') } this.topLevelNode = this.findTopLevelNode(this.categroyTree, currentKey.fondsId) - // 如果找到了顶级节点,则从该节点开始递归查找指定元素 if (this.topLevelNode) { if (currentKey) { // 展开顶级节点的子节点 @@ -137,6 +136,7 @@ export default { }) }, handleNodeClick(val) { + localStorage.setItem('currentArchivesKey', JSON.stringify(val)) this.selectedCategory = val this.$emit('nodeClick', val) }, diff --git a/src/views/prearchiveLibrary/treeList.vue b/src/views/prearchiveLibrary/treeList.vue index d006f73..06004db 100644 --- a/src/views/prearchiveLibrary/treeList.vue +++ b/src/views/prearchiveLibrary/treeList.vue @@ -5,7 +5,7 @@
- +
{{ data.label }} @@ -115,13 +115,46 @@ export default { } return newItem }) + console.log('this.crud.data', this.crud.data) this.$nextTick(() => { - if (this.crud.data.length > 0 && this.crud.data[0].children && this.crud.data[0].children.length > 0) { - const targetNode = this.crud.data[0].children[0] - const node = this.$refs.tree.getNode(targetNode.keyId) - if (node) { - this.$refs.tree.setCurrentKey(targetNode.keyId) - this.handleNodeClick(node.data) + const cachedKey = localStorage.getItem('currentDocumentKey') + let targetNode = null + + if (cachedKey) { + try { + const { fondsId, id } = JSON.parse(cachedKey) + // 1. 根据fondsId找到父节点(一级节点) + const parentNode = this.crud.data.find(parent => parent.fonds_id === fondsId) + if (parentNode) { + const treeParentNode = this.$refs.tree.getNode(parentNode.keyId) + if (treeParentNode) { + treeParentNode.expanded = true + // 3. 在父节点的子集中,通过id找到目标子节点 + targetNode = parentNode.children.find(child => child.id === id) + } + } + } catch (e) { + console.error('解析缓存节点失败', e) + localStorage.removeItem('currentDocumentKey') + } + } + + if (targetNode) { + this.$refs.tree.setCurrentKey(targetNode.keyId) + this.handleNodeClick(targetNode) + } else { + // 默认选中第一个父节点的第一个子节点 + if (this.crud.data.length > 0 && this.crud.data[0].children && this.crud.data[0].children.length > 0) { + const defaultChild = this.crud.data[0].children[0] + const treeDefaultNode = this.$refs.tree.getNode(defaultChild.keyId) + if (treeDefaultNode) { + // 展开默认父节点 + const defaultParentNode = this.$refs.tree.getNode(this.crud.data[0].keyId) + defaultParentNode.expanded = true + // 选中默认子节点 + this.$refs.tree.setCurrentKey(defaultChild.keyId) + this.handleNodeClick(defaultChild) + } } } }) @@ -129,6 +162,7 @@ export default { // 选中门类后 handleNodeClick(val) { console.log('val', val) + localStorage.setItem('currentDocumentKey', JSON.stringify(val)) if (val) { this.$emit('nodeClick', val) } diff --git a/src/views/preview/index.vue b/src/views/preview/index.vue index 12deccb..1c0577b 100644 --- a/src/views/preview/index.vue +++ b/src/views/preview/index.vue @@ -54,7 +54,7 @@