测试代码
<template>
<div class='box'>
demo
<el-button type="primary" @click="exportImageToExcel">test</el-button>
</div>
</template>
<script lang='ts' setup>
import { ref } from 'vue';
import ExcelJS from 'exceljs';
import type { Anchor } from 'exceljs';
import { baseImg } from "./ts/basePic";
// Constants
const MM_EMU_RATIO = 10000; // 点到 emu 单位的转换
const PX_DOT_RATIO = 0.75; // px 到点的转换
const CHAR_DOT_RATIO = 5.25; // 字符到点的转换
// Convert px to dots
function pxToDots(px: number): number {
return px * PX_DOT_RATIO;
}
// Convert dots to character width
function dotsToCharWidth(dots: number): number {
return dots / CHAR_DOT_RATIO;
}
// Setup worksheet columns and rows
function setupWorksheetDimensions(worksheet: ExcelJS.Worksheet, picWidthDots: number, picHeightDots: number) {
const charWidth = dotsToCharWidth(picWidthDots) + 20; // Width in character units
worksheet.getColumn(2).width = charWidth;
worksheet.getColumn(3).width = charWidth;
worksheet.getRow(2).height = picHeightDots + 100; // Row height
worksheet.mergeCells('B2:D2');
}
// Calculate image offset
function calculateImageOffset(worksheet: ExcelJS.Worksheet, picWidthDots: number, picHeightDots: number) {
const mergedWidth = (worksheet.getColumn(2).width as number) * 2 + (worksheet.getColumn(3).width as number);
const rowHeight = worksheet.getRow(2).height as number;
const marginTop = ((rowHeight * 1.3) - picHeightDots) / 2;
const marginLeft = ((mergedWidth * 1.48 * CHAR_DOT_RATIO - picWidthDots) / 2);
return {
topOffset: Math.floor(marginTop * MM_EMU_RATIO),
leftOffset: Math.floor(marginLeft * MM_EMU_RATIO),
};
}
// Add image to worksheet
function addImageToWorksheet(workbook: ExcelJS.Workbook, worksheet: ExcelJS.Worksheet, picWidthDots: number, picHeightDots: number) {
const imageId = workbook.addImage({
extension: 'png',
base64: baseImg,
});
const { topOffset, leftOffset } = calculateImageOffset(worksheet, picWidthDots, picHeightDots);
// 核心居中方式就是这个设置了
const imgSets = {
tl: { nativeRow: 1, nativeCol: 1, nativeRowOff: topOffset, nativeColOff: leftOffset } as Anchor,
ext: { width: picWidthDots, height: picHeightDots },
editAs: 'oneCell',
};
worksheet.addImage(imageId, imgSets);
}
// Download Excel file
async function downloadExcelFile(workbook: ExcelJS.Workbook, fileName: string) {
const buffer = await workbook.xlsx.writeBuffer();
const blob = new Blob([buffer], { type: 'application/octet-stream' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}
// Main export function
async function exportImageToExcel() {
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet('Images');
const picWidth = pxToDots(100); // Width in dots
const picHeight = pxToDots(100); // Height in dots
setupWorksheetDimensions(worksheet, picWidth, picHeight);
addImageToWorksheet(workbook, worksheet, picWidth, picHeight);
await downloadExcelFile(workbook, 'output.xlsx');
}
</script>
<style lang='scss' scoped>
.box {
padding: 16px;
}
</style>
- 效果
刚好居中
垮了3个单元格,宽度都太一样的单元格
核心实现
const imgSets = {
tl: { nativeRow: 1, nativeCol: 1, nativeRowOff: topOffset, nativeColOff: leftOffset } as Anchor,
ext: { width: picWidthDots, height: picHeightDots },
editAs: 'oneCell',
};
worksheet.addImage(imageId, imgSets);
Anchor 参数参考
- 部分参数解释
tl 是top left 起始点设置
nativeRow 是行位置,只能是正整数
nativeCol 是列位置,只能是正整数
nativeRowOff 是行偏移量,不受单元格限制,可以是正数也可以是负数,最好传入整数,单位是EMU,1点等于10000emu,excel的单位是点,1px约等于0.75个点,这是网页的转换单位
nativeColOff 是列偏移量,同nativeRowOff
ext 用来设置图片宽度高度
width 图片宽度,单位点
height 图片宽度,单位点
editAs 是用来设置生成后的图片被单元格的移动的影响方式,和这里我们的定位无关,可以不用设置
单位转换解释
-
MM_EMU_RATIO = 10000
- 解释:这是点(EMU 单位)到 Excel 所用的 EMU 单位(英语单位 - 英寸的 1/914400)的转换比率。EMU(English Metric Unit)是 Excel 中用于图形和图片定位的内部单位,1 点约等于 10000 EMU。
- 用途:用于将点单位转换为 EMU,以精确控制图片在 Excel 中的定位偏移(如
nativeRowOff
和nativeColOff
)。
-
PX_DOT_RATIO = 0.75
- 解释:这是 px(像素)到点的转换比率。一般情况下,1 px 约等于 0.75 点(点是字体大小和距离的传统度量单位,1 英寸为 72 点)。
- 用途:用于将像素值转换为点单位,以便统一尺寸单位,例如将图片的像素宽度或高度转换为 Excel 的点单位。
-
CHAR_DOT_RATIO = 5.25
- 解释:这是字符宽度到点的转换比率。在 Excel 中,列宽通常以字符宽度来表示,
CHAR_DOT_RATIO
约定了字符宽度和点之间的关系,1 字符宽度约等于 5.25 点。 - 用途:将点数转换为字符单位,以便根据 Excel 的列宽设置需求调整宽度,使之与字符数对齐。
- 解释:这是字符宽度到点的转换比率。在 Excel 中,列宽通常以字符宽度来表示,
这些转换比率允许将尺寸从 px 和字符单位转换到 Excel 内部使用的点或 EMU 单位,实现对图片和表格单元格尺寸的更精确控制。
前端工程师、程序员