feat(excel): 添加日期时间解析和换行格式处理功能

- 新增日期时间解析函数,支持多种日期格式转换为Excel序列号
- 实现Excel序列号与Chrono时间的相互转换功能
- 添加对各种换行符格式的统一处理(\r\n、\\r\\n、\\n等)
- 在插入和更新操作中集成日期格式识别和设置
- 实现自动换行和行高自适应功能
- 优化单元格值获取逻辑以支持日期格式化显示
This commit is contained in:
macro
2026-04-26 01:01:12 +08:00
parent 1abac4e28d
commit 72c2431f0f
+58 -10
View File
@@ -411,7 +411,7 @@ fn count_excel(book: &Spreadsheet, sheet_name: &Option<String>) -> Result<()> {
Ok(()) Ok(())
} }
// --- 修改版:插入行 (支持日期写入) --- // --- 最终版:插入行 (支持所有换行格式 + 自动换行 + 样式保留) ---
fn insert_excel( fn insert_excel(
book: &mut Spreadsheet, book: &mut Spreadsheet,
value: &str, value: &str,
@@ -438,7 +438,7 @@ fn insert_excel(
sheet.insert_new_row(&insert_idx_1based, &1); sheet.insert_new_row(&insert_idx_1based, &1);
} }
// 写入数据 (逻辑增强) // 写入数据
let vals: Vec<&str> = value.split(split).collect(); let vals: Vec<&str> = value.split(split).collect();
for (i, val) in vals.iter().enumerate() { for (i, val) in vals.iter().enumerate() {
let col = (i + 1) as u32; let col = (i + 1) as u32;
@@ -446,15 +446,40 @@ fn insert_excel(
let val_trimmed = val.trim(); let val_trimmed = val.trim();
if !val_trimmed.is_empty() { if !val_trimmed.is_empty() {
// 1. 尝试解析日期 // 1. 统一换行格式:处理所有换行符为 Excel 标准的 \n
// 同时保留对 Shell 转义的兼容(即使 Shell 吃掉了也能工作)
let processed_val = val_trimmed
.replace("\r\n", "\n") // 真正的 Windows 换行 (ASCII 13+10) -> Unix 换行
.replace("\\r\\n", "\n") // 字面量 \r\n (4个字符) -> Unix 换行
.replace("\\n", "\n") // 字面量 \n (2个字符) -> Unix 换行
.replace("\\t", "\t"); // 字面量 \t (2个字符) -> 制表符
// 2. 尝试解析日期(用原始 trim 后的值,日期不应该包含换行)
if let Some((dt, excel_fmt)) = try_parse_datetime(val_trimmed) { if let Some((dt, excel_fmt)) = try_parse_datetime(val_trimmed) {
let serial = datetime_to_excel_serial(&dt); let serial = datetime_to_excel_serial(&dt);
cell.set_value_number(serial); cell.set_value_number(serial);
// 关键:设置单元格格式,这样 Excel 才会把它当日期看
cell.get_style_mut().get_number_format_mut().set_format_code(excel_fmt); cell.get_style_mut().get_number_format_mut().set_format_code(excel_fmt);
} else { } else {
// 2. 写入文本 // 3. 写入处理后的纯文本
cell.set_value(val_trimmed); cell.set_value(processed_val.clone());
}
// 4. 【关键】统一判断:只要内容包含换行符,就强制开启自动换行 + 自动调整行高
// 不管是日期还是文本(虽然日期带换行极少见,但逻辑完整)
if processed_val.contains('\n') {
// 保留单元格原有的所有对齐样式,只开启自动换行
let mut align = cell.get_style().get_alignment().cloned().unwrap_or_default();
align.set_wrap_text(true);
cell.get_style_mut().set_alignment(align);
// 5. 自动调整行高(简单估算)
let line_count = processed_val.matches('\n').count() + 1;
let estimated_height = (line_count as f64) * 15.0; // 每行约 15 磅
let row_dim = sheet.get_row_dimension_mut(&insert_idx_1based);
// 只有当当前行高小于估算值时才调整,避免覆盖用户已设置的行高
if *row_dim.get_height() < estimated_height {
row_dim.set_height(estimated_height);
}
} }
} }
} }
@@ -463,7 +488,7 @@ fn insert_excel(
Ok(()) Ok(())
} }
// --- 修改版:更新行 (支持日期写入) --- // --- 最终版:更新行 (支持所有换行格式 + 自动换行 + 样式保留) ---
fn update_excel( fn update_excel(
book: &mut Spreadsheet, book: &mut Spreadsheet,
value: &str, value: &str,
@@ -486,14 +511,37 @@ fn update_excel(
let val_trimmed = val.trim(); let val_trimmed = val.trim();
if !val_trimmed.is_empty() { if !val_trimmed.is_empty() {
// 1. 尝试解析日期 // 1. 统一换行格式
let processed_val = val_trimmed
.replace("\r\n", "\n") // 真正的 Windows 换行 (ASCII 13+10) -> Unix 换行
.replace("\\r\\n", "\n") // 字面量 \r\n (4个字符) -> Unix 换行
.replace("\\n", "\n") // 字面量 \n (2个字符) -> Unix 换行
.replace("\\t", "\t"); // 字面量 \t (2个字符) -> 制表符
// 2. 尝试解析日期
if let Some((dt, excel_fmt)) = try_parse_datetime(val_trimmed) { if let Some((dt, excel_fmt)) = try_parse_datetime(val_trimmed) {
let serial = datetime_to_excel_serial(&dt); let serial = datetime_to_excel_serial(&dt);
cell_obj.set_value_number(serial); cell_obj.set_value_number(serial);
cell_obj.get_style_mut().get_number_format_mut().set_format_code(excel_fmt); cell_obj.get_style_mut().get_number_format_mut().set_format_code(excel_fmt);
} else { } else {
// 2. 写入文本 // 3. 写入文本
cell_obj.set_value(val_trimmed); cell_obj.set_value(processed_val.clone());
}
// 4. 统一开启自动换行 + 自动调整行高
if processed_val.contains('\n') {
let mut align = cell_obj.get_style().get_alignment().cloned().unwrap_or_default();
align.set_wrap_text(true);
cell_obj.get_style_mut().set_alignment(align);
// 5. 自动调整行高(简单估算)
let line_count = processed_val.matches('\n').count() + 1;
let estimated_height = (line_count as f64) * 15.0; // 每行约 15 磅
let row_dim = sheet.get_row_dimension_mut(&target_row);
// 只有当当前行高小于估算值时才调整,避免覆盖用户已设置的行高
if *row_dim.get_height() < estimated_height {
row_dim.set_height(estimated_height);
}
} }
} }
} }