casualroom/apps/fenghuo/web/lib/profile/utils.ts

180 lines
6.6 KiB
TypeScript
Raw Normal View History

2025-07-28 07:50:50 +08:00
import * as ExcelJS from 'exceljs';
import { EliteFormData } from '@fenghuo/common';
/**
* Excel
* @param data
* @param columns
* @param filename
*/
export async function exportEliteFormToExcel(
data: EliteFormData[],
columns: string[],
filename?: string
): Promise<void> {
try {
// 创建工作簿
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet('骨干名册');
// 设置列定义
worksheet.columns = [
{ header: '序号', key: 'sequence', width: 8 },
{ header: '专业', key: 'profession', width: 15 },
{ header: '台站', key: 'station', width: 20 },
{ header: '技师(值班代号)', key: 'technician', width: 25 },
{ header: '领班员/台站长(值班代号)', key: 'supervisor', width: 28 },
{ header: '值机员/值班员(值班代号)', key: 'operator', width: 25 }
];
// 设置表头样式
const headerRow = worksheet.getRow(1);
headerRow.height = 25;
headerRow.eachCell((cell) => {
// 移除背景色
cell.font = {
size: 10,
name: '黑体'
};
cell.alignment = {
vertical: 'middle',
horizontal: 'center'
};
cell.border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
});
// 按专业分组数据
const groupedData = new Map<string, EliteFormData[]>();
data.forEach(row => {
const profession = row.profession;
if (!groupedData.has(profession)) {
groupedData.set(profession, []);
}
const professionRows = groupedData.get(profession);
if (professionRows) {
professionRows.push(row);
}
});
// 添加数据行并处理合并单元格
let currentRow = 2; // 从第2行开始第1行是表头
let sequenceNumber = 1;
// 辅助函数:根据职务等级生成星星
const getStars = (dutyLevel: number): string => {
return '★'.repeat(dutyLevel);
};
// 辅助函数:格式化人员信息
const formatPersonInfo = (person: { name: string; dutyCode: string; dutyLevel: number }): string => {
const stars = person.dutyLevel > 0 ? getStars(person.dutyLevel) : '';
return `${person.name}(${person.dutyCode})${stars}`;
};
for (const [profession, rows] of groupedData) {
const startRow = currentRow;
// 添加该专业的所有行
for (let i = 0; i < rows.length; i++) {
const row = rows[i];
if (!row) continue; // 跳过undefined的行
const excelRow = worksheet.addRow({
sequence: i === 0 ? sequenceNumber : '', // 只在第一行显示序号
profession: i === 0 ? profession : '', // 只在第一行显示专业名称
station: row.parentOrganization ? `${row.parentOrganization}${row.station}` : row.station,
technician: row.technicians.map(formatPersonInfo).join('、') || '',
supervisor: row.supervisors.map(formatPersonInfo).join('、') || '',
operator: row.operators.map(formatPersonInfo).join('、') || ''
});
// 设置行高(根据内容自动调整)
const maxLines = Math.max(
row.technicians.length || 1,
row.supervisors.length || 1,
row.operators.length || 1
);
excelRow.height = Math.max(40, maxLines * 20);
// 设置单元格样式
excelRow.eachCell((cell, colNumber) => {
cell.alignment = {
vertical: 'middle',
horizontal: colNumber <= 3 ? 'center' : 'left', // 序号、专业、台站居中,其他左对齐
wrapText: true
};
cell.border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
cell.font = {
name: '仿宋GB2312',
size: 10
};
});
currentRow++;
}
// 如果该专业有多行,合并序号和专业单元格
if (rows.length > 1) {
const endRow = currentRow - 1;
// 合并序号列
worksheet.mergeCells(startRow, 1, endRow, 1);
// 合并专业列
worksheet.mergeCells(startRow, 2, endRow, 2);
// 设置合并后单元格的对齐方式和字体
worksheet.getCell(startRow, 1).alignment = {
vertical: 'middle',
horizontal: 'center'
};
worksheet.getCell(startRow, 1).font = {
name: '仿宋GB2312',
size: 11
};
worksheet.getCell(startRow, 2).alignment = {
vertical: 'middle',
horizontal: 'center'
};
worksheet.getCell(startRow, 2).font = {
name: '仿宋GB2312',
size: 11
};
}
sequenceNumber++;
}
// 生成文件并下载
const buffer = await workbook.xlsx.writeBuffer();
const blob = new Blob([buffer], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
});
const defaultFilename = `骨干名册_${new Date().toISOString()}.xlsx`;
// 创建下载链接
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = filename || defaultFilename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
} catch (error) {
console.error('导出Excel失败:', error);
throw new Error('导出Excel失败请稍后重试');
}
}