180 lines
6.6 KiB
TypeScript
180 lines
6.6 KiB
TypeScript
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失败,请稍后重试');
|
||
}
|
||
}
|