diff --git a/package.json b/package.json index b5bd3a0d..af7203cc 100644 --- a/package.json +++ b/package.json @@ -41,10 +41,11 @@ "clipboard": "2.0.8", "core-js": "3.25.3", "crypto-js": "^4.2.0", + "docx": "^9.5.1", "echarts": "5.4.0", "element-ui": "2.15.14", "exceljs": "^4.4.0", - "file-saver": "2.0.5", + "file-saver": "^2.0.5", "fuse.js": "6.4.3", "highlight.js": "9.18.5", "html2canvas": "^1.4.1", diff --git a/src/views/material/cost/component/applyHome.vue b/src/views/material/cost/component/applyHome.vue index 1ad62268..65f306b3 100644 --- a/src/views/material/cost/component/applyHome.vue +++ b/src/views/material/cost/component/applyHome.vue @@ -235,7 +235,8 @@
@@ -324,6 +325,20 @@ import Treeselect from '@riophae/vue-treeselect' import '@riophae/vue-treeselect/dist/vue-treeselect.css' import printJS from 'print-js' import ExcelJS from 'exceljs' +import { + Document, + Packer, + Paragraph, + TextRun, + Table, + TableRow, + TableCell, + AlignmentType, + WidthType, + BorderStyle, + PageOrientation +} from 'docx' + export default { name: 'ApplyHome', dicts: ['cost_status'], @@ -806,6 +821,335 @@ export default { // this.download('material/agreementInfo/export', { // ...this.queryParams // }, `协议_${new Date().getTime()}.xlsx`) + }, + async exportWord() { + const data = this.agreementContent + + // ====== 通用单元格样式 ====== + const cellOpts = { + margins: { top: 300, bottom: 300, left: 40, right: 40 }, // 缩小上下间距 + verticalAlign: 'center', + borders: { + top: { style: BorderStyle.SINGLE, size: 1 }, + bottom: { style: BorderStyle.SINGLE, size: 1 }, + left: { style: BorderStyle.SINGLE, size: 1 }, + right: { style: BorderStyle.SINGLE, size: 1 } + } + } + + const noBorder = { + top: { style: BorderStyle.NONE }, + bottom: { style: BorderStyle.NONE }, + left: { style: BorderStyle.NONE }, + right: { style: BorderStyle.NONE } + } + + // ====== 创建文档 ====== + const doc = new Document({ + sections: [ + { + properties: { + page: { + size: { width: 11900, height: 16840, orientation: PageOrientation.PORTRAIT }, + margin: { top: 720, right: 720, bottom: 720, left: 720 } + } + }, + children: [ + // ===== 标题 ===== + new Paragraph({ + alignment: AlignmentType.CENTER, + spacing: { before: 100, after: 200 }, + children: [new TextRun({ text: '机具设备有偿使用费结算协议书', bold: true, size: 32 })] + }), + + // ===== 协议号和编号 ===== + + new Table({ + width: { size: 100, type: WidthType.PERCENTAGE }, + rows: [ + new TableRow({ + children: [ + new TableCell({ + width: { size: 70, type: WidthType.PERCENTAGE }, + borders: noBorder, + children: [new Paragraph({ text: '' })] + }), + new TableCell({ + width: { size: 30, type: WidthType.PERCENTAGE }, + borders: noBorder, + children: [ + new Paragraph({ + alignment: AlignmentType.LEFT, + text: `协议号:${data.agreementCode || ''}`, + spacing: { before: 0, after: 0 }, + margins: { top: 100, bottom: 100 } + }) + ] + }) + ] + }), + new TableRow({ + children: [ + new TableCell({ + width: { size: 70, type: WidthType.PERCENTAGE }, + borders: noBorder, + children: [new Paragraph({ text: '' })] + }), + new TableCell({ + width: { size: 30, type: WidthType.PERCENTAGE }, + borders: noBorder, + children: [ + new Paragraph({ + alignment: AlignmentType.LEFT, + text: `编号:`, + spacing: { before: 0, after: 0 }, + margins: { top: 100, bottom: 100 } + }) + ] + }) + ] + }) + ] + }), + + // === 添加一个空白 Paragraph === + new Paragraph({ + text: '', + spacing: { before: 100, after: 100 } // 给点上下间距,让后面内容不受影响 + }), + + // ===== 主体表格 ===== + new Table({ + width: { size: 100, type: WidthType.PERCENTAGE }, + rows: [ + // 工程名称 + new TableRow({ + children: [ + new TableCell({ + ...cellOpts, + width: { size: 25, type: WidthType.PERCENTAGE }, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: '工程名称:' + }) + ] + }), + new TableCell({ + ...cellOpts, + width: { size: 75, type: WidthType.PERCENTAGE }, + columnSpan: 3, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: data.projectName + }) + ] + }) + ] + }), + + // 承租单位 + 日期 + new TableRow({ + children: [ + new TableCell({ + ...cellOpts, + width: { size: 25, type: WidthType.PERCENTAGE }, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: '承租单位:' + }) + ] + }), + new TableCell({ + ...cellOpts, + width: { size: 40, type: WidthType.PERCENTAGE }, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: data.unitName + }) + ] + }), + new TableCell({ + ...cellOpts, + width: { size: 10, type: WidthType.PERCENTAGE }, + children: [ + new Paragraph({ alignment: AlignmentType.CENTER, text: '日期:' }) + ] + }), + new TableCell({ + ...cellOpts, + width: { size: 25, type: WidthType.PERCENTAGE }, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: data.applyTime + }) + ] + }) + ] + }), + + // 表头 + new TableRow({ + children: [ + new TableCell({ + ...cellOpts, + columnSpan: 4, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: '结算项目及金额(元)', + bold: true + }) + ] + }) + ] + }), + + // 金额明细 + ...[ + ['一、施工机具有偿使用费:', `¥ ${Number(data.leaseCost).toFixed(2)}`], + ['二、施工机具维修费:', `¥ ${Number(data.repairCost).toFixed(2)}`], + ['三、施工机具丢失费:', `¥ ${Number(data.loseCost).toFixed(2)}`], + ['四、施工机具损坏赔偿费:', `¥ ${Number(data.scrapCost).toFixed(2)}`], + ['五、施工机具租赁减免费:', `¥ ${Number(data.reductionCost).toFixed(2)}`] + ].map( + ([label, value]) => + new TableRow({ + children: [ + new TableCell({ + ...cellOpts, + columnSpan: 2, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: label + }) + ] + }), + new TableCell({ + ...cellOpts, + columnSpan: 2, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: value + }) + ] + }) + ] + }) + ), + + // 合计 + new TableRow({ + children: [ + new TableCell({ + ...cellOpts, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: '费用合计金额(大写):' + }) + ] + }), + new TableCell({ + ...cellOpts, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: data.costAllUpper + }) + ] + }), + new TableCell({ + ...cellOpts, + columnSpan: 2, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: `¥ ${Number(data.costAll).toFixed(2)}` + }) + ] + }) + ] + }), + + // 说明 + new TableRow({ + children: [ + new TableCell({ + ...cellOpts, + children: [ + new Paragraph({ alignment: AlignmentType.CENTER, text: '说明:' }) + ] + }), + new TableCell({ + ...cellOpts, + columnSpan: 3, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: '本协议一式两份,甲方一份,乙方一份,经双方签字后生效。' + }) + ] + }) + ] + }), + + // 备注 + new TableRow({ + children: [ + new TableCell({ + ...cellOpts, + children: [ + new Paragraph({ alignment: AlignmentType.CENTER, text: '备注:' }) + ] + }), + new TableCell({ + ...cellOpts, + columnSpan: 3, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: '此费用仅为在机具设备分公司发生费用,未计从项目部领用机具费用。' + }) + ] + }) + ] + }) + ] + }), + + // 空白间距 + new Paragraph({ text: '', spacing: { before: 200 } }), + + // ===== 底部签名行(无边框) ===== + new Table({ + width: { size: 100, type: WidthType.PERCENTAGE }, + rows: [ + new TableRow({ + children: ['部门负责人:', '承租负责人:', '核算员:'].map( + text => + new TableCell({ + borders: noBorder, + children: [new Paragraph({ alignment: AlignmentType.LEFT, text })], + margins: { top: 200, bottom: 200 } // 缩小上下空间 + }) + ) + }) + ] + }) + ] + } + ] + }) + + const blob = await Packer.toBlob(doc) + saveAs(blob, `机具设备有偿使用费结算协议书-${data.agreementCode}.docx`) } } } diff --git a/src/views/material/cost/component/examHome.vue b/src/views/material/cost/component/examHome.vue index 25703dcd..a2e9f02f 100644 --- a/src/views/material/cost/component/examHome.vue +++ b/src/views/material/cost/component/examHome.vue @@ -268,8 +268,9 @@ @@ -286,6 +287,19 @@ import '@riophae/vue-treeselect/dist/vue-treeselect.css' import { formatTime } from '@/utils/bonus' import { saveAs } from 'file-saver' import ExcelJS from 'exceljs' +import { + Document, + Packer, + Paragraph, + TextRun, + Table, + TableRow, + TableCell, + AlignmentType, + WidthType, + BorderStyle, + PageOrientation +} from 'docx' export default { name: 'ExamHome', @@ -657,6 +671,335 @@ export default { // ======= 导出文件 ======= const buf = await workbook.xlsx.writeBuffer() saveAs(new Blob([buf]), `机具设备有偿使用费结算协议书-${data.agreementCode}_${formatTime(new Date())}.xlsx`) + }, + async exportWord() { + const data = this.agreementContent + + // ====== 通用单元格样式 ====== + const cellOpts = { + margins: { top: 300, bottom: 300, left: 40, right: 40 }, // 缩小上下间距 + verticalAlign: 'center', + borders: { + top: { style: BorderStyle.SINGLE, size: 1 }, + bottom: { style: BorderStyle.SINGLE, size: 1 }, + left: { style: BorderStyle.SINGLE, size: 1 }, + right: { style: BorderStyle.SINGLE, size: 1 } + } + } + + const noBorder = { + top: { style: BorderStyle.NONE }, + bottom: { style: BorderStyle.NONE }, + left: { style: BorderStyle.NONE }, + right: { style: BorderStyle.NONE } + } + + // ====== 创建文档 ====== + const doc = new Document({ + sections: [ + { + properties: { + page: { + size: { width: 11900, height: 16840, orientation: PageOrientation.PORTRAIT }, + margin: { top: 720, right: 720, bottom: 720, left: 720 } + } + }, + children: [ + // ===== 标题 ===== + new Paragraph({ + alignment: AlignmentType.CENTER, + spacing: { before: 100, after: 200 }, + children: [new TextRun({ text: '机具设备有偿使用费结算协议书', bold: true, size: 32 })] + }), + + // ===== 协议号和编号 ===== + + new Table({ + width: { size: 100, type: WidthType.PERCENTAGE }, + rows: [ + new TableRow({ + children: [ + new TableCell({ + width: { size: 70, type: WidthType.PERCENTAGE }, + borders: noBorder, + children: [new Paragraph({ text: '' })] + }), + new TableCell({ + width: { size: 30, type: WidthType.PERCENTAGE }, + borders: noBorder, + children: [ + new Paragraph({ + alignment: AlignmentType.LEFT, + text: `协议号:${data.agreementCode || ''}`, + spacing: { before: 0, after: 0 } + }) + ], + margins: { top: 100, bottom: 100 } // 缩小上下空间 + }) + ] + }), + new TableRow({ + children: [ + new TableCell({ + width: { size: 70, type: WidthType.PERCENTAGE }, + borders: noBorder, + children: [new Paragraph({ text: '' })] + }), + new TableCell({ + width: { size: 30, type: WidthType.PERCENTAGE }, + borders: noBorder, + children: [ + new Paragraph({ + alignment: AlignmentType.LEFT, + text: `编号:`, + spacing: { before: 0, after: 0 } + }) + ], + margins: { top: 100, bottom: 100 } // 缩小上下空间 + }) + ] + }) + ] + }), + + // === 添加一个空白 Paragraph === + new Paragraph({ + text: '', + spacing: { before: 100, after: 100 } // 给点上下间距,让后面内容不受影响 + }), + + // ===== 主体表格 ===== + new Table({ + width: { size: 100, type: WidthType.PERCENTAGE }, + rows: [ + // 工程名称 + new TableRow({ + children: [ + new TableCell({ + ...cellOpts, + width: { size: 25, type: WidthType.PERCENTAGE }, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: '工程名称:' + }) + ] + }), + new TableCell({ + ...cellOpts, + width: { size: 75, type: WidthType.PERCENTAGE }, + columnSpan: 3, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: data.projectName + }) + ] + }) + ] + }), + + // 承租单位 + 日期 + new TableRow({ + children: [ + new TableCell({ + ...cellOpts, + width: { size: 25, type: WidthType.PERCENTAGE }, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: '承租单位:' + }) + ] + }), + new TableCell({ + ...cellOpts, + width: { size: 40, type: WidthType.PERCENTAGE }, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: data.unitName + }) + ] + }), + new TableCell({ + ...cellOpts, + width: { size: 10, type: WidthType.PERCENTAGE }, + children: [ + new Paragraph({ alignment: AlignmentType.CENTER, text: '日期:' }) + ] + }), + new TableCell({ + ...cellOpts, + width: { size: 25, type: WidthType.PERCENTAGE }, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: data.applyTime + }) + ] + }) + ] + }), + + // 表头 + new TableRow({ + children: [ + new TableCell({ + ...cellOpts, + columnSpan: 4, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: '结算项目及金额(元)', + bold: true + }) + ] + }) + ] + }), + + // 金额明细 + ...[ + ['一、施工机具有偿使用费:', `¥ ${Number(data.leaseCost).toFixed(2)}`], + ['二、施工机具维修费:', `¥ ${Number(data.repairCost).toFixed(2)}`], + ['三、施工机具丢失费:', `¥ ${Number(data.loseCost).toFixed(2)}`], + ['四、施工机具损坏赔偿费:', `¥ ${Number(data.scrapCost).toFixed(2)}`], + ['五、施工机具租赁减免费:', `¥ ${Number(data.reductionCost).toFixed(2)}`] + ].map( + ([label, value]) => + new TableRow({ + children: [ + new TableCell({ + ...cellOpts, + columnSpan: 2, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: label + }) + ] + }), + new TableCell({ + ...cellOpts, + columnSpan: 2, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: value + }) + ] + }) + ] + }) + ), + + // 合计 + new TableRow({ + children: [ + new TableCell({ + ...cellOpts, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: '费用合计金额(大写):' + }) + ] + }), + new TableCell({ + ...cellOpts, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: data.costAllUpper + }) + ] + }), + new TableCell({ + ...cellOpts, + columnSpan: 2, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: `¥ ${Number(data.costAll).toFixed(2)}` + }) + ] + }) + ] + }), + + // 说明 + new TableRow({ + children: [ + new TableCell({ + ...cellOpts, + children: [ + new Paragraph({ alignment: AlignmentType.CENTER, text: '说明:' }) + ] + }), + new TableCell({ + ...cellOpts, + columnSpan: 3, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: '本协议一式两份,甲方一份,乙方一份,经双方签字后生效。' + }) + ] + }) + ] + }), + + // 备注 + new TableRow({ + children: [ + new TableCell({ + ...cellOpts, + children: [ + new Paragraph({ alignment: AlignmentType.CENTER, text: '备注:' }) + ] + }), + new TableCell({ + ...cellOpts, + columnSpan: 3, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: '此费用仅为在机具设备分公司发生费用,未计从项目部领用机具费用。' + }) + ] + }) + ] + }) + ] + }), + + // 空白间距 + new Paragraph({ text: '', spacing: { before: 200 } }), + + // ===== 底部签名行(无边框) ===== + new Table({ + width: { size: 100, type: WidthType.PERCENTAGE }, + rows: [ + new TableRow({ + children: ['部门负责人:', '承租负责人:', '核算员:'].map( + text => + new TableCell({ + borders: noBorder, + children: [new Paragraph({ alignment: AlignmentType.LEFT, text })], + margins: { top: 200, bottom: 200 } // 缩小上下空间 + }) + ) + }) + ] + }) + ] + } + ] + }) + + const blob = await Packer.toBlob(doc) + saveAs(blob, `机具设备有偿使用费结算协议书-${data.agreementCode}.docx`) } } }