feat : update info

This commit is contained in:
2025-12-03 09:53:10 +08:00
parent e09f1f38e6
commit b088d9c8ee
6 changed files with 178 additions and 68 deletions

View File

@@ -7,31 +7,67 @@ pub fn write_report<P: AsRef<Path>>(path: P, reports: Vec<ClassReport>) -> Resul
let mut workbook = Workbook::new();
let worksheet = workbook.add_worksheet();
// Define Colors
let header_bg_color = rust_xlsxwriter::Color::RGB(0x4472C4); // Blue for main headers
let subheader_bg_color = rust_xlsxwriter::Color::RGB(0x4874CB); // Slightly different blue for subheaders
let text_color = rust_xlsxwriter::Color::White; // White text on blue background
let border_color = rust_xlsxwriter::Color::Black;
// Formats
let header_format = Format::new().set_bold().set_align(rust_xlsxwriter::FormatAlign::Center);
let center_format = Format::new().set_align(rust_xlsxwriter::FormatAlign::Center);
// 1. Main Header Format (Class Name, Row 1 Headers)
// Songti, 16pt, Bold, Blue Fill, White Text, Center, Thin Borders
let main_header_format = Format::new()
.set_font_name("宋体")
.set_font_size(16)
.set_bold()
.set_background_color(header_bg_color)
.set_font_color(text_color)
.set_align(rust_xlsxwriter::FormatAlign::Center)
.set_align(rust_xlsxwriter::FormatAlign::VerticalCenter)
.set_border(rust_xlsxwriter::FormatBorder::Thin)
.set_border_color(border_color);
// 2. Sub Header Format (Row 2 Headers: 次数, 单价, 费用合计)
// Songti, 11pt, Bold, Blue Fill, White Text, Center, Thin Borders
let sub_header_format = Format::new()
.set_font_name("宋体")
.set_font_size(11)
.set_bold()
.set_background_color(subheader_bg_color)
.set_font_color(text_color)
.set_align(rust_xlsxwriter::FormatAlign::Center)
.set_align(rust_xlsxwriter::FormatAlign::VerticalCenter)
.set_border(rust_xlsxwriter::FormatBorder::Thin)
.set_border_color(border_color);
// 3. Data Cell Format
// Songti, 10pt, Center, Thin Borders
let data_format = Format::new()
.set_font_name("宋体")
.set_font_size(10)
.set_align(rust_xlsxwriter::FormatAlign::Center)
.set_align(rust_xlsxwriter::FormatAlign::VerticalCenter)
.set_border(rust_xlsxwriter::FormatBorder::Thin)
.set_border_color(border_color);
let mut current_row = 0;
for report in reports {
// Write Class Name
worksheet.write_string(current_row, 0, &report.class_name)?;
// Write Class Name (Merged across columns A-E?)
// Actually, looking at res.xlsx, "思维B" is in A147.
// Let's merge A to E for the class name to look nice, or just put in A.
// The user sample shows it in A.
worksheet.write_string_with_format(current_row, 0, &report.class_name, &main_header_format)?;
// Apply empty format to B-E to show borders/bg if we want, but let's stick to simple first.
current_row += 1;
// Write Headers
let headers = ["序号", "入学年份", "年级", "班级", "姓名"];
for (i, header) in headers.iter().enumerate() {
worksheet.write_string_with_format(current_row, i as u16, *header, &header_format)?;
worksheet.write_string_with_format(current_row, i as u16, *header, &main_header_format)?;
}
// Determine all unique months across all students in this class to create columns
// Actually, the requirement implies we list months for each student?
// Looking at res.xlsx:
// Row 148: ..., 9月陈南岚老师, None, None, 10月陈南岚老师, ...
// Row 149: ..., 次数, 单价, 费用合计, 次数, 单价, 费用合计
// It seems the columns are dynamic based on months.
// We need to find all months present in this class and create columns for them.
// Collect all months
let mut all_months = std::collections::BTreeSet::new();
for student in &report.students {
@@ -46,9 +82,8 @@ pub fn write_report<P: AsRef<Path>>(path: P, reports: Vec<ClassReport>) -> Resul
for month in &all_months {
// Write Month Header (merged 3 cells)
// We don't know the teacher name, so just use "X月"
let month_str = format!("{}", month);
worksheet.merge_range(current_row, col_idx, current_row, col_idx + 2, &month_str, &header_format)?;
worksheet.merge_range(current_row, col_idx, current_row, col_idx + 2, &month_str, &main_header_format)?;
month_col_map.insert(*month, col_idx);
col_idx += 3;
@@ -57,16 +92,17 @@ pub fn write_report<P: AsRef<Path>>(path: P, reports: Vec<ClassReport>) -> Resul
current_row += 1;
// Write Sub-headers (次数, 单价, 费用合计)
for _ in &all_months {
// We need to know the column index for this month
// But we are iterating linearly.
}
// Actually, let's iterate columns again
// Columns A-E are empty in this row in the sample?
// Sample Row 149: [None, None, None, None, None, '次数', ...]
// So we leave A-E empty or apply a default format?
// Let's apply data format to keep borders consistent if needed, or just leave blank.
// Sample shows blank.
let mut sub_col_idx = 5;
for _ in &all_months {
worksheet.write_string_with_format(current_row, sub_col_idx, "次数", &center_format)?;
worksheet.write_string_with_format(current_row, sub_col_idx + 1, "单价", &center_format)?;
worksheet.write_string_with_format(current_row, sub_col_idx + 2, "费用合计", &center_format)?;
worksheet.write_string_with_format(current_row, sub_col_idx, "次数", &sub_header_format)?;
worksheet.write_string_with_format(current_row, sub_col_idx + 1, "单价", &sub_header_format)?;
worksheet.write_string_with_format(current_row, sub_col_idx + 2, "费用合计", &sub_header_format)?;
sub_col_idx += 3;
}
@@ -74,36 +110,67 @@ pub fn write_report<P: AsRef<Path>>(path: P, reports: Vec<ClassReport>) -> Resul
// Write Student Data
for (i, student) in report.students.iter().enumerate() {
worksheet.write_number_with_format(current_row, 0, (i + 1) as f64, &center_format)?;
worksheet.write_number_with_format(current_row, 0, (i + 1) as f64, &data_format)?;
if let Some(year) = &student.student.year {
worksheet.write_string_with_format(current_row, 1, year, &center_format)?;
worksheet.write_string_with_format(current_row, 1, year, &data_format)?;
} else {
worksheet.write_blank(current_row, 1, &data_format)?;
}
if let Some(grade) = &student.student.grade {
worksheet.write_string_with_format(current_row, 2, grade, &center_format)?;
worksheet.write_string_with_format(current_row, 2, grade, &data_format)?;
} else {
worksheet.write_blank(current_row, 2, &data_format)?;
}
if let Some(class_info) = &student.student.class {
worksheet.write_string_with_format(current_row, 3, class_info, &center_format)?;
worksheet.write_string_with_format(current_row, 3, class_info, &data_format)?;
} else {
worksheet.write_blank(current_row, 3, &data_format)?;
}
worksheet.write_string_with_format(current_row, 4, &student.student.name, &center_format)?;
worksheet.write_string_with_format(current_row, 4, &student.student.name, &data_format)?;
for record in &student.monthly_records {
if let Some(&start_col) = month_col_map.get(&record.month) {
worksheet.write_number_with_format(current_row, start_col, record.count as f64, &center_format)?;
worksheet.write_number_with_format(current_row, start_col + 1, record.price, &center_format)?;
worksheet.write_number_with_format(current_row, start_col, record.count as f64, &data_format)?;
worksheet.write_number_with_format(current_row, start_col + 1, record.price, &data_format)?;
// Formula: =PRODUCT(次数:单价)
// Column indices are 0-based.
// rust_xlsxwriter uses (row, col)
let count_cell = rust_xlsxwriter::utility::row_col_to_cell(current_row, start_col);
let price_cell = rust_xlsxwriter::utility::row_col_to_cell(current_row, start_col + 1);
let formula = format!("=PRODUCT({}:{})", count_cell, price_cell);
worksheet.write_formula_with_format(current_row, start_col + 2, formula.as_str(), &center_format)?;
worksheet.write_formula_with_format(current_row, start_col + 2, formula.as_str(), &data_format)?;
}
}
current_row += 1;
}
// Add Total Row
// Sample Row 162: [None, ..., =SUM(...)]
// We need to sum the columns.
// Let's add a "Total" row.
// Columns F, H, I, K, etc. (Count and Total Cost)
// We need to iterate through all month columns again
for month in &all_months {
if let Some(&start_col) = month_col_map.get(month) {
// Sum Count (start_col)
let start_cell = rust_xlsxwriter::utility::row_col_to_cell(current_row - report.students.len() as u32, start_col);
let end_cell = rust_xlsxwriter::utility::row_col_to_cell(current_row - 1, start_col);
let formula = format!("=SUM({}:{})", start_cell, end_cell);
worksheet.write_formula_with_format(current_row, start_col, formula.as_str(), &data_format)?;
// Sum Total Cost (start_col + 2)
let start_cell = rust_xlsxwriter::utility::row_col_to_cell(current_row - report.students.len() as u32, start_col + 2);
let end_cell = rust_xlsxwriter::utility::row_col_to_cell(current_row - 1, start_col + 2);
let formula = format!("=SUM({}:{})", start_cell, end_cell);
worksheet.write_formula_with_format(current_row, start_col + 2, formula.as_str(), &data_format)?;
}
}
current_row += 1;
current_row += 2; // Spacing between classes
}

View File

@@ -41,7 +41,7 @@ async fn process_attendance(input_path: String) -> Result<String, String> {
excel_writer::write_report(&output_path, reports)
.map_err(|e| format!("Failed to write Excel: {}", e))?;
Ok(format!("Successfully saved to {:?}", output_path))
Ok(format!("保存成功,文件保存到 {:?}", output_path))
}
#[cfg(test)]