diff --git a/package.json b/package.json index 28bd629..b04e3df 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "core-js": "3.6.5", "docx": "^9.4.1", "docx-parser": "^0.2.1", + "docx-preview": "^0.1.14", "docx2html": "^1.3.2", "docxtemplater": "^3.61.1", "echarts": "^5.6.0", diff --git a/src/views/businessConfig/EditPayForm.vue b/src/views/businessConfig/EditPayForm.vue index ee21003..f082ebe 100644 --- a/src/views/businessConfig/EditPayForm.vue +++ b/src/views/businessConfig/EditPayForm.vue @@ -26,15 +26,7 @@
- -
- 请上传文档进行预览 -
+
@@ -159,15 +151,7 @@ > @@ -189,7 +173,7 @@ export default { previewContent: '', // 预览内容 showEditDrawer: false, showPreviewModal: false, // 添加预览模态窗口控制变量 - currentTemplateIndex: 1, + currentTemplateIndex: 0, editForm: { field: '', // Renamed from name to field name: '', // Renamed from label to name (Chinese name) @@ -201,6 +185,10 @@ export default { docxUrl: null, templateFile: null, templates: [ + { + name:'', + content:'' + }, { name: '资金划拨审批单', content: ` @@ -866,15 +854,21 @@ export default { this.loadTemplate(newVal); } }, - formatType: { - immediate: true, - handler(val) { - if (val === 1) { - this.loadTemplate(this.currentTemplateIndex); - } else { - this.codeContent = '\n\n \n' - this.previewContent = '' + formatType(newVal) { + if (newVal === 1) { + // HTML 格式 + if (this.$refs.codeEditor) { + this.$refs.codeEditor.setValue(this.codeContent) } + this.previewContent = this.codeContent + this.handleRefresh() + } else if (newVal === 2) { + // DOCX 格式 + if (this.$refs.codeEditor) { + this.$refs.codeEditor.setValue(this.codeContent) + } + this.previewContent = this.codeContent + this.handleRefresh() } } }, @@ -916,68 +910,21 @@ export default { this.previewContent = processedContent; }, handleUpload() { - // 创建文件输入元素 - const input = document.createElement('input'); - input.type = 'file'; - input.accept = '.docx'; - - // 监听文件选择事件 + const input = document.createElement('input') + input.type = 'file' + input.accept = '.docx' input.onchange = async (e) => { - const file = e.target.files[0]; + e.preventDefault() // 阻止默认行为 + const file = e.target.files[0] if (file) { - // 检查文件类型 if (!file.name.endsWith('.docx')) { - this.$message.error('请选择docx格式的文件'); - return; - } - - try { - // 创建临时URL - const blobUrl = URL.createObjectURL(file); - // 使用 Microsoft Office Online Viewer - this.docxUrl = `https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(blobUrl)}`; - - // 清空代码预览区 - this.codeContent = ''; - - // 将文件转换为ArrayBuffer - const arrayBuffer = await this.readFileAsArrayBuffer(file); - - // 使用mammoth转换docx为HTML,用于提取变量 - const result = await mammoth.convertToHtml({ - arrayBuffer, - transformDocument: (document) => { - return document; - } - }); - - // 提取占位变量 - const variables = this.extractVariables(result.value); - - // 显示提取的HTML内容 - this.codeContent = result.value; - - // 更新字段元数据 - this.updateFieldMetadata(variables); - - } catch (error) { - console.error('处理文档失败:', error); - this.$message.error('处理文档失败,请重试'); + this.$message.error('请选择docx格式的文件') + return } + await this.convertDocxToHtml(file) } - }; - - // 触发文件选择对话框 - input.click(); - }, - - readFileAsArrayBuffer(file) { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = (event) => resolve(event.target.result); - reader.onerror = (error) => reject(error); - reader.readAsArrayBuffer(file); - }); + } + input.click() }, handleEditField(field) { this.editForm = { ...field }; @@ -1189,7 +1136,132 @@ export default { extractAndUpdateFields() { const variables = this.extractVariables(this.codeContent) // variables are field names this.updateFieldMetadata(variables) // Pass field names - } + }, + async convertDocxToHtml(file) { + try { + // 设置表格名称为文件名(去掉扩展名) + const fileName = file.name.replace(/\.[^/.]+$/, "") + this.formName = fileName + + const reader = new FileReader() + const arrayBuffer = await new Promise((resolve, reject) => { + reader.onload = (e) => resolve(e.target.result) + reader.onerror = (e) => reject(e) + reader.readAsArrayBuffer(file) + }) + + const result = await mammoth.convertToHtml({ + arrayBuffer, + convertImage: mammoth.images.imgElement(function(image) { + return image.read("base64").then(function(imageBuffer) { + return { + src: "data:" + image.contentType + ";base64," + imageBuffer + }; + }); + }), + styleMap: [ + "p[style-name='Normal'] => p:style='margin: 0; padding: 0;'", + "p[style-name='Heading 1'] => h1:style='font-size: 24px; font-weight: bold; margin: 0 0 20px 0;'", + "p[style-name='Heading 2'] => h2:style='font-size: 20px; font-weight: bold; margin: 0 0 16px 0;'", + "p[style-name='Heading 3'] => h3:style='font-size: 18px; font-weight: bold; margin: 0 0 14px 0;'", + "p[style-name='Heading 4'] => h4:style='font-size: 16px; font-weight: bold; margin: 0 0 12px 0;'", + "p[style-name='Heading 5'] => h5:style='font-size: 14px; font-weight: bold; margin: 0 0 10px 0;'", + "p[style-name='Heading 6'] => h6:style='font-size: 12px; font-weight: bold; margin: 0 0 8px 0;'", + "p[style-name='List Paragraph'] => p:style='margin: 0; padding: 0; list-style-type: disc;'", + "p[style-name='Quote'] => blockquote:style='margin: 0 0 16px 0; padding: 10px 20px; border-left: 4px solid #ccc;'", + "p[style-name='Intense Quote'] => blockquote:style='margin: 0 0 16px 0; padding: 10px 20px; border-left: 4px solid #666; background: #f5f5f5;'", + "r[style-name='Strong'] => strong:style='font-weight: bold;'", + "r[style-name='Emphasis'] => em:style='font-style: italic;'", + "r[style-name='Code'] => code:style='font-family: monospace; background: #f5f5f5; padding: 2px 4px;'", + "table => table:style='width: 100%; border-collapse: collapse; margin: 0 0 16px 0;'", + "tr => tr:style='border-bottom: 1px solid #ddd;'", + "td => td:style='padding: 8px; border: 1px solid #ddd; text-align: center; vertical-align: middle;'", + "th => th:style='padding: 8px; border: 1px solid #ddd; font-weight: bold; text-align: center; vertical-align: middle;'", + "table[style-name='Table Grid'] => table:style='width: 100%; border-collapse: collapse; margin: 0 0 16px 0; border: 1px solid #000;'", + "table[style-name='Table Grid Light'] => table:style='width: 100%; border-collapse: collapse; margin: 0 0 16px 0; border: 1px solid #ddd;'", + "table[style-name='Table Grid Dark'] => table:style='width: 100%; border-collapse: collapse; margin: 0 0 16px 0; border: 1px solid #000;'", + "table[style-name='Table Grid Medium'] => table:style='width: 100%; border-collapse: collapse; margin: 0 0 16px 0; border: 1px solid #666;'" + ], + transformDocument: function(document) { + return document; + } + }) + + // 处理转换后的 HTML,确保所有样式都内嵌 + let html = result.value + + // 处理表格样式,保留原始边框样式 + html = html.replace(/ { + if (match.includes('style-name="Table Grid"')) { + return '
{ + let style = 'padding: 8px; border: 1px solid #ddd; text-align: center; vertical-align: middle;' + if (match.includes('style-name="Table Grid"')) { + style = 'padding: 8px; border: 1px solid #000; text-align: center; vertical-align: middle;' + } else if (match.includes('style-name="Table Grid Light"')) { + style = 'padding: 8px; border: 1px solid #ddd; text-align: center; vertical-align: middle;' + } else if (match.includes('style-name="Table Grid Dark"')) { + style = 'padding: 8px; border: 1px solid #000; text-align: center; vertical-align: middle;' + } else if (match.includes('style-name="Table Grid Medium"')) { + style = 'padding: 8px; border: 1px solid #666; text-align: center; vertical-align: middle;' + } + return `
{ + let style = 'padding: 8px; border: 1px solid #ddd; font-weight: bold; text-align: center; vertical-align: middle;' + if (match.includes('style-name="Table Grid"')) { + style = 'padding: 8px; border: 1px solid #000; font-weight: bold; text-align: center; vertical-align: middle;' + } else if (match.includes('style-name="Table Grid Light"')) { + style = 'padding: 8px; border: 1px solid #ddd; font-weight: bold; text-align: center; vertical-align: middle;' + } else if (match.includes('style-name="Table Grid Dark"')) { + style = 'padding: 8px; border: 1px solid #000; font-weight: bold; text-align: center; vertical-align: middle;' + } else if (match.includes('style-name="Table Grid Medium"')) { + style = 'padding: 8px; border: 1px solid #666; font-weight: bold; text-align: center; vertical-align: middle;' + } + return `/g, '

') + + // 处理图片样式 + html = html.replace(/ { + // 更新代码编辑器内容 + if (this.$refs.codeEditor) { + this.$refs.codeEditor.setValue(html) + } + // 更新预览内容 + this.previewContent = html + this.handleRefresh() + }) + } catch (error) { + console.error('转换失败:', error) + this.$message.error('文件转换失败') + } + }, }, // 在组件销毁时释放URL beforeDestroy() {