准考证优化
This commit is contained in:
@@ -78,6 +78,18 @@ const resourceSpecs = {
|
||||
],
|
||||
validations: { published: ['发布', '不发布'] },
|
||||
numberColumns: ['score']
|
||||
},
|
||||
admit_cards: {
|
||||
title: '准考证信息台账', sheet: '准考证信息',
|
||||
columns: [
|
||||
['schoolName', '考生学校', 24, ''], ['className', '班级', 20, ''],
|
||||
['candidateNumber', '报名号', 24, ''], ['candidateName', '姓名', 14, ''], ['idNumber', '证件号码', 22, ''],
|
||||
['examCode', '考试代码', 18, ''], ['examName', '考试名称', 28, ''], ['cardNumber', '准考证号', 22, ''],
|
||||
['centerCode', '考点代码', 16, ''], ['centerName', '考点名称', 26, ''], ['centerAddress', '考点详细地址', 38, ''],
|
||||
['subjectName', '科目', 14, ''], ['subjectDate', '日期', 14, ''], ['subjectTime', '时间', 16, ''],
|
||||
['examRoomCode', '考试考场序号', 16, ''], ['roomName', '考场通用名称', 20, ''], ['roomCode', '物理场地代码', 16, ''],
|
||||
['building', '楼栋', 16, ''], ['floor', '楼层', 12, ''], ['seat', '座位号', 12, '']
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
@@ -173,3 +185,133 @@ export async function buildWorkbook(resource, rows = [], { template = false, sub
|
||||
for (const col of requiredColumns) header.getCell(col).font = { name: '微软雅黑', bold: true, color: { argb: 'FFFFE7A3' } };
|
||||
return workbook.xlsx.writeBuffer();
|
||||
}
|
||||
|
||||
function addOperationalSheet(workbook, name, title, columns, rows, { landscape = true } = {}) {
|
||||
const sheet = workbook.addWorksheet(name, { views: [{ state: 'frozen', ySplit: 2, showGridLines: false }] });
|
||||
sheet.columns = columns.map(([key, , width]) => ({ key, width }));
|
||||
sheet.mergeCells(1, 1, 1, columns.length);
|
||||
const titleCell = sheet.getCell(1, 1);
|
||||
titleCell.value = title;
|
||||
titleCell.font = { name: '微软雅黑', size: 16, bold: true, color: { argb: 'FFFFFFFF' } };
|
||||
titleCell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF173F60' } };
|
||||
titleCell.alignment = { vertical: 'middle', horizontal: 'left' };
|
||||
sheet.getRow(1).height = 34;
|
||||
const header = sheet.getRow(2);
|
||||
header.values = columns.map(([, label]) => label);
|
||||
header.height = 28;
|
||||
header.font = { name: '微软雅黑', bold: true, color: { argb: 'FFFFFFFF' } };
|
||||
header.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF2C7080' } };
|
||||
header.alignment = { vertical: 'middle', horizontal: 'center', wrapText: true };
|
||||
rows.forEach((item, index) => {
|
||||
const row = sheet.addRow(Object.fromEntries(columns.map(([key]) => [key, item[key] ?? ''])));
|
||||
row.height = 28;
|
||||
row.font = { name: '微软雅黑', size: 10, color: { argb: 'FF243B4A' } };
|
||||
row.alignment = { vertical: 'middle', wrapText: true };
|
||||
if (index % 2 === 1) row.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFF3F7F8' } };
|
||||
row.eachCell(cell => { cell.border = { bottom: { style: 'hair', color: { argb: 'FFD8E2E7' } } }; });
|
||||
});
|
||||
sheet.autoFilter = { from: { row: 2, column: 1 }, to: { row: Math.max(2, sheet.rowCount), column: columns.length } };
|
||||
sheet.pageSetup = { orientation: landscape ? 'landscape' : 'portrait', paperSize: 9, fitToPage: true, fitToWidth: 1, fitToHeight: 0, margins: { left: .25, right: .25, top: .4, bottom: .4, header: .2, footer: .2 } };
|
||||
sheet.printTitlesRow = '1:2';
|
||||
return sheet;
|
||||
}
|
||||
|
||||
function addDeskStickerSheet(workbook, rows) {
|
||||
const sheet = workbook.addWorksheet('桌贴', { views: [{ showGridLines: false, zoomScale: 85 }] });
|
||||
const labelColumns = [14.5, 14.5, 14.5, 3, 14.5, 14.5, 14.5];
|
||||
labelColumns.forEach((width, index) => { sheet.getColumn(index + 1).width = width; });
|
||||
sheet.pageSetup = {
|
||||
orientation: 'portrait', paperSize: 9, fitToPage: true, fitToWidth: 1, fitToHeight: 0,
|
||||
margins: { left: .25, right: .25, top: .25, bottom: .25, header: 0, footer: 0 },
|
||||
horizontalCentered: true, verticalCentered: false
|
||||
};
|
||||
sheet.headerFooter = { oddFooter: '&C第 &P / &N 页' };
|
||||
const rowsPerLabel = 8;
|
||||
const labelsPerPage = 6;
|
||||
const rowsPerPage = rowsPerLabel * 3;
|
||||
const borderColor = { argb: 'FF173F60' };
|
||||
const paleBlue = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFEAF1F5' } };
|
||||
const paleGold = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFFFF1C7' } };
|
||||
const mergeLine = (row, startColumn) => {
|
||||
sheet.mergeCells(row, startColumn, row, startColumn + 2);
|
||||
return sheet.getCell(row, startColumn);
|
||||
};
|
||||
const styleLine = (cell, { size = 10, bold = false, color = 'FF243B4A', fill = null } = {}) => {
|
||||
cell.font = { name: '微软雅黑', size, bold, color: { argb: color } };
|
||||
cell.alignment = { vertical: 'middle', horizontal: 'center', wrapText: true, shrinkToFit: true };
|
||||
if (fill) cell.fill = fill;
|
||||
};
|
||||
|
||||
rows.forEach((item, index) => {
|
||||
const page = Math.floor(index / labelsPerPage);
|
||||
const slot = index % labelsPerPage;
|
||||
const startRow = page * rowsPerPage + Math.floor(slot / 2) * rowsPerLabel + 1;
|
||||
const startColumn = slot % 2 === 0 ? 1 : 5;
|
||||
for (let row = startRow; row < startRow + rowsPerLabel; row += 1) sheet.getRow(row).height = 32.5;
|
||||
const cells = Array.from({ length: rowsPerLabel }, (_, offset) => mergeLine(startRow + offset, startColumn));
|
||||
cells[0].value = item.examName;
|
||||
styleLine(cells[0], { size: 11, bold: true, color: 'FFFFFFFF', fill: { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF173F60' } } });
|
||||
cells[1].value = `${item.subjectName}|${item.subjectDate} ${item.subjectTime}`;
|
||||
styleLine(cells[1], { size: 9, bold: true, fill: paleBlue });
|
||||
cells[2].value = '考试考场序号';
|
||||
styleLine(cells[2], { size: 9, bold: true, color: 'FF2C7080' });
|
||||
cells[3].value = { richText: [{ text: String(item.examRoomCode || '') }] };
|
||||
cells[3].numFmt = '@';
|
||||
styleLine(cells[3], { size: 25, bold: true, color: 'FF173F60' });
|
||||
cells[4].value = `座位号 ${item.seat}`;
|
||||
styleLine(cells[4], { size: 20, bold: true, color: 'FF8A4B08', fill: paleGold });
|
||||
cells[5].value = `${item.roomName}(场地 ${item.roomCode})|${item.building} ${item.floor}`;
|
||||
styleLine(cells[5], { size: 9, bold: true });
|
||||
cells[6].value = `${item.candidateName}|${item.schoolName} ${item.className}`;
|
||||
styleLine(cells[6], { size: 10, bold: true, fill: paleBlue });
|
||||
cells[7].value = `${item.centerName}|准考证号 ${item.cardNumber}`;
|
||||
styleLine(cells[7], { size: 8, color: 'FF526576' });
|
||||
|
||||
for (let row = startRow; row < startRow + rowsPerLabel; row += 1) {
|
||||
for (let column = startColumn; column < startColumn + 3; column += 1) {
|
||||
const cell = sheet.getCell(row, column);
|
||||
cell.border = {
|
||||
top: { style: row === startRow ? 'medium' : 'hair', color: borderColor },
|
||||
bottom: { style: row === startRow + rowsPerLabel - 1 ? 'medium' : 'hair', color: borderColor },
|
||||
left: { style: column === startColumn ? 'medium' : 'hair', color: borderColor },
|
||||
right: { style: column === startColumn + 2 ? 'medium' : 'hair', color: borderColor }
|
||||
};
|
||||
}
|
||||
}
|
||||
if ((index + 1) % labelsPerPage === 0 && index + 1 < rows.length) sheet.getRow(startRow + rowsPerLabel - 1).addPageBreak();
|
||||
});
|
||||
if (rows.length) sheet.pageSetup.printArea = `A1:G${Math.ceil(rows.length / labelsPerPage) * rowsPerPage}`;
|
||||
return sheet;
|
||||
}
|
||||
|
||||
export async function buildCenterMaterialsWorkbook(rows, subtitle = '') {
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
workbook.creator = '衡准考试信息管理系统';
|
||||
workbook.created = new Date();
|
||||
const sorted = [...rows].sort((a, b) => String(a.centerCode).localeCompare(String(b.centerCode))
|
||||
|| String(a.subjectDate).localeCompare(String(b.subjectDate)) || String(a.subjectTime).localeCompare(String(b.subjectTime))
|
||||
|| String(a.examRoomCode).localeCompare(String(b.examRoomCode)) || String(a.seat).localeCompare(String(b.seat)));
|
||||
const baseTitle = subtitle ? `|${subtitle}` : '';
|
||||
addDeskStickerSheet(workbook, sorted);
|
||||
|
||||
const doorMap = new Map();
|
||||
for (const item of sorted) {
|
||||
const key = [item.examCode, item.subjectName, item.centerCode, item.examRoomCode, item.roomCode].join('|');
|
||||
const current = doorMap.get(key) || { ...item, candidateCount: 0 };
|
||||
current.candidateCount += 1;
|
||||
doorMap.set(key, current);
|
||||
}
|
||||
addOperationalSheet(workbook, '门贴', `考场门贴${baseTitle}`, [
|
||||
['examName', '考试', 28], ['subjectName', '科目', 14], ['subjectDate', '日期', 14], ['subjectTime', '时间', 16],
|
||||
['centerName', '考点', 24], ['examRoomCode', '考试考场序号', 16], ['roomName', '考场通用名称', 20],
|
||||
['roomCode', '场地代码', 14], ['building', '楼栋', 16], ['floor', '楼层', 10], ['candidateCount', '人数', 10]
|
||||
], [...doorMap.values()]);
|
||||
|
||||
addOperationalSheet(workbook, '考场签名单', `考场考生签名单${baseTitle}`, [
|
||||
['examName', '考试', 26], ['subjectName', '科目', 14], ['subjectDate', '日期', 14], ['subjectTime', '时间', 16],
|
||||
['examRoomCode', '考试考场序号', 16], ['roomName', '考场通用名称', 20], ['building', '楼栋', 16], ['floor', '楼层', 10],
|
||||
['seat', '座位号', 10], ['cardNumber', '准考证号', 22], ['candidateName', '姓名', 14],
|
||||
['schoolName', '考生学校', 24], ['className', '班级', 18], ['signature', '考生签名', 18]
|
||||
], sorted.map(item => ({ ...item, signature: '' })));
|
||||
return workbook.xlsx.writeBuffer();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user