Feature: 为多层纸带计算添加Excel导出功能

This commit is contained in:
2025-07-12 14:28:56 +08:00
parent 1fc9e226a7
commit 13a1c7f3cf
6 changed files with 454 additions and 12 deletions

View File

@ -135,7 +135,7 @@
<tr>
<th>{{ $t('layer') }}</th>
<th>{{ $t('paperGrammage') }}</th>
<th>{{ $t('cumulative Thickness') }}</th>
<th>{{ $t('cumulativeThickness') }}</th>
<th>{{ $t('angle') }}</th>
<th>{{ $t('paperWidth') }}</th>
</tr>
@ -158,10 +158,14 @@
</template>
<script setup lang="ts">
import * as EXCEL from 'exceljs'
import { computed, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { createParam, type Param } from '@/types/param'
import { degreesToRadians, radiansToDegrees } from '@/utils/angle'
const { t } = useI18n()
interface LayerRecord {
layer: number
grammage: Param
@ -201,9 +205,259 @@
recordList.value = []
}
const saveParams = () => {
// TODO 计算结果导出为Excel
console.log('SAVE')
const saveParams = async () => {
if (recordList.value.length === 0) {
console.warn('No records to save.')
return
}
try {
const filename = `${t('multiLayerPaperTapeWidthAngleCalculate')}-${new Date().toISOString().slice(0, 10)}.xlsx`.replace(/\s+/g, '')
// 创建工作簿和工作表
const workbook = new EXCEL.Workbook()
const worksheet = workbook.addWorksheet(t('results'))
// 创建样式
// headerStyle: 表头样式根据Excel内建样式Calculation设置
const headerStyle: EXCEL.Style = {
fill: {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FFF2F2F2' },
},
font: {
name: 'Arial',
size: 11,
bold: true,
color: { argb: 'FFFA7D00' },
},
alignment: {
horizontal: 'center',
vertical: 'middle',
},
numFmt: 'General',
protection: {},
border: {
top: { style: 'thin', color: { argb: 'FF7F7F7F' } },
bottom: { style: 'thin', color: { argb: 'FF7F7F7F' } },
left: { style: 'thin', color: { argb: 'FF7F7F7F' } },
right: { style: 'thin', color: { argb: 'FF7F7F7F' } },
},
}
// parameterStyle: 参数样式,使用默认样式
const parameterStyle: EXCEL.Style = {
font: {
name: 'Arial',
size: 12,
color: { argb: 'FF000000' },
},
alignment: {
horizontal: 'center',
vertical: 'middle',
},
numFmt: 'General',
protection: {},
border: {},
fill: {
type: 'pattern',
pattern: 'none',
},
}
// valueStyle: 数值样式使用默认样式保留2位小数
const valueStyle: EXCEL.Style = {
font: {
name: 'Arial',
size: 12,
color: { argb: 'FF000000' },
},
alignment: {
horizontal: 'center',
vertical: 'middle',
},
numFmt: '0.00',
protection: {},
border: {},
fill: {
type: 'pattern',
pattern: 'none',
},
}
// conditionStyle: 条件样式,使用默认样式
const conditionStyle: EXCEL.Style = {
font: {
name: 'Arial',
size: 16,
bold: true,
color: { argb: 'FF444444' },
},
alignment: {
horizontal: 'center',
vertical: 'middle',
},
numFmt: 'General',
protection: {},
border: {
top: { style: 'thin', color: { argb: 'FF7F7F7F' } },
bottom: { style: 'thin', color: { argb: 'FF7F7F7F' } },
left: { style: 'thin', color: { argb: 'FF7F7F7F' } },
right: { style: 'thin', color: { argb: 'FF7F7F7F' } },
},
fill: {
type: 'pattern',
pattern: 'none',
},
}
// 添加Parameters表头
worksheet.getCell('A1').value = t('parameters')
worksheet.getCell('A1').style = headerStyle
worksheet.mergeCells('A1:D1')
worksheet.getCell('A2').value = `${t('paperCoreDiameter')} (${paperCoreDiameter.value.unit})`
worksheet.getCell('A2').style = parameterStyle
worksheet.mergeCells('A2:B2')
worksheet.getCell('C2').value = `${t('bottomPaperWidth')} (${bottomPaperWidth.value.unit})`
worksheet.getCell('C2').style = parameterStyle
worksheet.mergeCells('C2:D2')
// 添加Parameters数据
worksheet.getCell('A3').value = paperCoreDiameter.value.value
worksheet.getCell('A3').style = valueStyle
worksheet.mergeCells('A3:B3')
worksheet.getCell('C3').value = bottomPaperWidth.value.value
worksheet.getCell('C3').style = valueStyle
worksheet.mergeCells('C3:D3')
// 添加Results表头
worksheet.getCell('A7').value = t('results')
worksheet.getCell('A7').style = headerStyle
worksheet.mergeCells('A7:E7')
// 添加Results表头列名
worksheet.getCell('A8').value = t('layer')
worksheet.getCell('A8').style = parameterStyle
worksheet.getCell('B8').value = t('paperGrammage')
worksheet.getCell('B8').style = parameterStyle
worksheet.getCell('C8').value = t('cumulativeThickness')
worksheet.getCell('C8').style = parameterStyle
worksheet.getCell('D8').value = t('angle')
worksheet.getCell('D8').style = parameterStyle
worksheet.getCell('E8').value = t('paperWidth')
worksheet.getCell('E8').style = parameterStyle
// 添加Results数据
for (const [index, record] of recordList.value.entries()) {
worksheet.getCell(`A${index + 9}`).value = record.layer
worksheet.getCell(`A${index + 9}`).style = parameterStyle
worksheet.getCell(`B${index + 9}`).value = record.grammage.value
worksheet.getCell(`B${index + 9}`).style = valueStyle
worksheet.getCell(`C${index + 9}`).value = record.cumulativeThickness.value
worksheet.getCell(`C${index + 9}`).style = valueStyle
worksheet.getCell(`D${index + 9}`).value = record.angle.value
worksheet.getCell(`D${index + 9}`).style = valueStyle
worksheet.getCell(`E${index + 9}`).value = record.paperWidth.value
worksheet.getCell(`E${index + 9}`).style = valueStyle
}
// 添加Reference表头
worksheet.getCell('F1').value = t('reference')
worksheet.getCell('F1').style = headerStyle
worksheet.mergeCells('F1:H1')
worksheet.getCell('F2').value = t('angle')
worksheet.getCell('F2').style = parameterStyle
worksheet.mergeCells('F2:F3')
worksheet.getCell('G2').value = t('minimum')
worksheet.getCell('G2').style = parameterStyle
worksheet.getCell('H2').value = t('maximum')
worksheet.getCell('H2').style = parameterStyle
// 添加Reference数据
worksheet.getCell('G3').value = 10
worksheet.getCell('G3').style = parameterStyle
worksheet.getCell('H3').value = 45
worksheet.getCell('H3').style = parameterStyle
worksheet.getCell('F4').value = t('calculatedValue')
worksheet.getCell('F4').style = parameterStyle
// 所有记录中最小角度
worksheet.getCell('G4').value = {
formula: `MIN(D9: D${recordList.value.length + 8})`,
}
worksheet.getCell('G4').style = valueStyle
// 所有记录中最大角度
worksheet.getCell('H4').value = {
formula: `MAX(D9: D${recordList.value.length + 8})`,
}
worksheet.getCell('H4').style = valueStyle
// 计算结果: 最小角度>10 且 最大角度<45
worksheet.getCell('F5').value = {
formula: `IF(AND(G4>10, H4<45), "${t('fit')}", "${t('notFit')}")`,
}
worksheet.getCell('F5').style = conditionStyle
worksheet.addConditionalFormatting({
ref: 'F5',
rules: [
{
type: 'expression',
formulae: ['OR(G4<=10, H4>=45)'],
style: {
fill: { type: 'pattern', pattern: 'solid', bgColor: { argb: 'FFFF7F7F' } }, // 红色背景
},
priority: 1,
},
{
type: 'expression',
formulae: ['AND(G4>10, H4<45)'],
style: {
fill: { type: 'pattern', pattern: 'solid', bgColor: { argb: 'FFA6E65C' } }, // 绿色背景
},
priority: 1,
},
],
})
worksheet.mergeCells('F5:H6')
// 自动调整列宽
for (const [index, column] of worksheet.columns.entries()) {
let maxLength = 0
const columnLetter = String.fromCodePoint(65 + index)
if (column && typeof column.eachCell === 'function') {
column.eachCell({ includeEmpty: true }, cell => {
const cellValue = cell.value ? String(cell.value) : ''
if (cellValue.length > maxLength) {
maxLength = cellValue.length
}
})
// 设置列宽
column.width = maxLength + 4 // 增加一些额外空间
}
}
// 导出为文件
const buffer = await workbook.xlsx.writeBuffer()
const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = filename
document.body.append(link)
link.click()
link.remove()
window.URL.revokeObjectURL(url)
console.log('Saved as Excel file:', filename)
} catch (error) {
console.error('Error saving parameters:', error)
return
}
}
const addLayer = () => {