From 59ac16336dd3824316ad90aad73c630db6459757 Mon Sep 17 00:00:00 2001 From: fengyuan_yang <1976974459@qq.com> Date: Thu, 3 Sep 2026 16:21:11 +0800 Subject: [PATCH] =?UTF-8?q?mrb=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cclqms-vue/src/utils/filePreview.js | 56 ++++++++++++++++++- .../src/views/modules/mrb/mrbRecord.vue | 8 ++- .../src/views/modules/oss/ossComponents.vue | 8 ++- 3 files changed, 67 insertions(+), 5 deletions(-) diff --git a/cclqms-vue/src/utils/filePreview.js b/cclqms-vue/src/utils/filePreview.js index 928709c..f715161 100644 --- a/cclqms-vue/src/utils/filePreview.js +++ b/cclqms-vue/src/utils/filePreview.js @@ -10,7 +10,8 @@ const FILE_TYPES = { video: ['mp4', 'avi', 'mov', 'wmv', 'flv', 'webm', 'mkv', 'ogg'], audio: ['mp3', 'wav', 'ogg', 'flac', 'aac', 'm4a', 'wma'], pdf: ['pdf'], - txt: ['txt', 'log', 'json', 'xml', 'csv', 'html', 'htm', 'css', 'js'], + txt: ['txt', 'log', 'json', 'xml', 'html', 'htm', 'css', 'js'], + csv: ['csv'], excel: ['xls', 'xlsx'], word: ['doc', 'docx'], ppt: ['ppt', 'pptx'] @@ -512,6 +513,51 @@ function safeSheetToHtml(worksheet, options = {}) { } } +/** + * 解码CSV内容,兼容UTF-8 BOM和常见中文编码 + */ +async function decodeCsvBlob (blobData) { + const buffer = blobData instanceof Blob ? await blobToArrayBuffer(blobData) : blobData + const bytes = new Uint8Array(buffer) + let offset = 0 + if (bytes.length >= 3 && bytes[0] === 0xEF && bytes[1] === 0xBB && bytes[2] === 0xBF) { + offset = 3 + } + const slice = bytes.subarray(offset) + const utf8Text = new TextDecoder('utf-8').decode(slice) + if (utf8Text.indexOf('\uFFFD') === -1) { + return utf8Text + } + try { + return new TextDecoder('gb18030').decode(slice) + } catch (error) { + return utf8Text + } +} + +/** + * 预览CSV文件(按表格展示,失败时回退为纯文本) + */ +export async function previewCsv (blobData, fileName) { + try { + const csvText = await decodeCsvBlob(blobData) + const workbook = XLSX.read(csvText, { type: 'string' }) + const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' }) + if (previewExcel(excelBuffer, fileName)) { + return true + } + const textBlob = new Blob([csvText], { type: 'text/plain;charset=utf-8' }) + return previewText(textBlob, fileName) + } catch (error) { + console.error('CSV预览失败:', error) + try { + return await previewText(blobData, fileName) + } catch (textError) { + return false + } + } +} + /** * 预览Excel文件 * @param {ArrayBuffer} arrayBuffer - 文件数据(ArrayBuffer格式) @@ -688,6 +734,13 @@ export async function previewFile(data, fileName) { } else { throw new Error('文本预览失败') } + + case 'csv': + if (await previewCsv(blobData, fileName)) { + return { success: true, type: 'csv' } + } else { + throw new Error('CSV预览失败') + } case 'excel': // Excel文件需要ArrayBuffer格式 @@ -733,6 +786,7 @@ export default { previewAudio, previewPDF, previewText, + previewCsv, previewExcel, previewWord, previewPPT, diff --git a/cclqms-vue/src/views/modules/mrb/mrbRecord.vue b/cclqms-vue/src/views/modules/mrb/mrbRecord.vue index f61fe73..d3971eb 100644 --- a/cclqms-vue/src/views/modules/mrb/mrbRecord.vue +++ b/cclqms-vue/src/views/modules/mrb/mrbRecord.vue @@ -3744,7 +3744,7 @@ export default { if (extension === 'pdf') { return 'application/pdf;charset=UTF-8' } - if (extension === 'txt') { + if (extension === 'txt' || extension === 'csv') { return 'text/plain;charset=utf-8' } return 'application/octet-stream' @@ -3763,12 +3763,16 @@ export default { this.$message.warning(this.$t('mrb.message.filePreviewUnsupported')) return } - const supportPreviewTypeList = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv', 'webm', 'txt', 'pdf', 'xls', 'xlsx', 'docx'] + const supportPreviewTypeList = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv', 'webm', 'txt', 'csv', 'pdf', 'xls', 'xlsx', 'docx'] if (!supportPreviewTypeList.includes(fileType)) { this.$message.warning(this.$t('mrb.message.filePreviewUnsupported')) return } previewOssFileById({ id: row.id }).then(({ data }) => { + if (fileType === 'csv') { + const fileName = String((row && (row.fileName || row.newFileName)) || 'preview.csv') + return this.$filePreview.previewFile(data, fileName.toLowerCase().endsWith('.csv') ? fileName : `${fileName}.csv`) + } const blob = new Blob([data], { type: this.resolvePreviewMimeType(fileType) }) const fileURL = URL.createObjectURL(blob) if (fileType === 'xls' || fileType === 'xlsx' || fileType === 'docx') { diff --git a/cclqms-vue/src/views/modules/oss/ossComponents.vue b/cclqms-vue/src/views/modules/oss/ossComponents.vue index e68f507..9462489 100644 --- a/cclqms-vue/src/views/modules/oss/ossComponents.vue +++ b/cclqms-vue/src/views/modules/oss/ossComponents.vue @@ -403,7 +403,7 @@ export default { if (extension === 'pdf') { return 'application/pdf;charset=UTF-8' } - if (extension === 'txt') { + if (extension === 'txt' || extension === 'csv') { return 'text/plain;charset=utf-8' } return 'application/octet-stream' @@ -422,12 +422,16 @@ export default { this.$message.warning('该文件格式暂不支持预览') return } - const supportPreviewTypeList = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv', 'webm', 'txt', 'pdf', 'xls', 'xlsx', 'docx'] + const supportPreviewTypeList = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv', 'webm', 'txt', 'csv', 'pdf', 'xls', 'xlsx', 'docx'] if (!supportPreviewTypeList.includes(fileType)) { this.$message.warning('该文件格式暂不支持预览') return } this.requestFileBlob(row.id).then(({ data }) => { + if (fileType === 'csv') { + const fileName = String((row && (row.fileName || row.newFileName)) || 'preview.csv') + return this.$filePreview.previewFile(data, fileName.toLowerCase().endsWith('.csv') ? fileName : `${fileName}.csv`) + } const blob = new Blob([data], { type: this.resolvePreviewMimeType(fileType) }) const fileURL = URL.createObjectURL(blob) if (fileType === 'xls' || fileType === 'xlsx' || fileType === 'docx') {