#xlsx #电子表格 #Excel

xlsx_group_write

Excel xlsx分组输出器

17个稳定版本

1.2.5 2024年5月23日
1.2.4 2023年7月6日
1.2.3 2023年6月21日
1.1.12 2023年5月26日
1.1.8 2023年4月28日

#210 in 编码

Download history 343/week @ 2024-04-25 2/week @ 2024-05-02 152/week @ 2024-05-23 2/week @ 2024-06-06 11/week @ 2024-06-27 135/week @ 2024-07-04 76/week @ 2024-07-25 10/week @ 2024-08-01

每月 86 次下载

MIT/Apache

94KB
1K SLoC

Xlsx-Group-Write


一、解决什么问题

在日常工作中,当需要将数据导出为Excel xlsx文件时,实际上大部分需求是将对应数据按顺序一行行写入到xlsx表格中,具体实现其实很简单,就是使用xlsx相关库进行编码。但这种实现路径存在以下问题:

1、大量重复工作

但是,如果你有多个数据需要导出为xlsx文件,每个数据都需要单独调用xlsx单元格写入API,这非常枯燥且效率低下。

2、大量重复代码

导出为xlsx文件,除了xlsx格式相关的代码外,还有文件保存、保存文件路径检测等各种需要处理的问题。

3、无法聚焦数据差异性

实际上导出Excel xlsx文件时,主要是各种数据的差异性,xlsx相关的写入操作是类似的,手工实现时,在考虑数据的差异性时,还需要考虑xlsx的操作等,无法集中处理。

4、分组输出工作量大

无法自动按某一数据字段将数据分组输出到不同的xlsx文件中,硬编码实现需要处理大量的文件初始化和写入逻辑。

二、我们干什么

本工具库帮助您集中处理数据导出到xlsx的编辑,可以在导出时自动按某一字段对数据进行分割导出到不同的xlsx文件中,同时支持xlsx加密、自动添加导出日期等功能。

主要由两部分组成:

(一)核心库用法

个性化特性

  • date 自动在文件名称中加入导出日期
  • encrypt 支持xlsx文件加密,添加该特性后,向接口会增加密码参数

1、 添加依赖

[dependencies.xlsx_group_write]
version = "1"
# 根据需要添加对应特性
feature = ["date","encrypt"]

2、 定义您的导出数据结构,并实现XlsxGroupWrite,并根据需要使用不同的方法和属性

3、 调用不同的write2xlsx_方法导出数据到xlsx文件

支持灵活的自动分组导出和汇总导出数据

  • write2xlsx_group_only
    • 只导出分组数据
    • 按分组ID将不同数据导出到不同的xlsx文件中
    • 如果添加了encrypt特性,该函数会增加一个加密密码参数
    • 如果数据为&[&Self}则调用:write2xlsx_group_only_ref
    • 如果要额外传递个性化参数到文件名获取器或写入其他信息到xlsx文件中,则调用:write2xlsx_group_only_with_extra_arg或write2xlsx_group_only_ref_with_extra_arg
  • write2xlsx_merge_only
    • 所有数据导入到同一个汇总xlsx文件
    • 如果仅使用该函数,可不设置group_maker
    • 如果添加了encrypt特性,该函数会增加一个加密密码参数
    • 如果数据为&[&Self}则调用:write2xlsx_merge_only_ref
    • 如果要额外传递个性化参数到文件名获取器或写入其他信息到xlsx文件中,则调用:write2xlsx_merge_only_with_extra_arg或write2xlsx_merge_only_ref_with_extra_arg
  • write2xlsx_all
    • 以上同时导出
    • 如果添加了encrypt特性,该函数会增加一个加密密码参数
    • 如果数据为&[&Self}则调用:write2xlsx_all_ref
    • 如果要额外传递个性化参数到文件名获取器或写入其他信息到xlsx文件中,则调用:write2xlsx_all_with_extra_arg或write2xlsx_all_ref_with_extra_arg

(二)派生宏用法

use xlsx_group_write_macro_derive::XlsxGroupWriteQuicker;

#[derive(XlsxGroupWriteQuicker)]
#[xlsx_group_write(
    // 快捷行数据写入定义
    // 各列以逗号分隔,可使用两个函数参数:
    // * line_index 为行号
    // * &self 为待写入的数据
    // 每个列数据定义为value:type
    // * value 要写入的数据,可为表达式,如:line_index-1,表示当前待写入数据行号-1
    // * type 数据类型,number/string,不指定则为string类型
    line_writer_simple = "line_index-1:number,&self.name,&self.tel,&self.dep",
    // 个性化行数据写入,调用对应函数进行写入行数据
    // 与line_writer_simple互斥,line_writer_simple优先
    // 该函数签名:fn write_xlsx_line(line_data:&MyAdvanceData, sheet: &mut Worksheet, line_index: u32,) -> Option<XlsxLineAdvanceWriterResult>
    line_writer_advance = "write_xlsx_line",
    // 个性化行数据写入,调用对应函数进行写入行数据,同时附加额外的参数
    // 与line_writer_simple互斥,line_writer_simple优先
    // 该函数签名:fn write_xlsx_line(line_data:&MyAdvanceData, sheet: &mut Worksheet, line_index: u32,extra_arg: Option<Self::ExtraArgType>,) -> Option<XlsxLineAdvanceWriterResult>
    line_writer_advance_with_extra_arg = "write_xlsx_line_with_extra_arg",
    // 通过模板文件生成表头,设置列宽等
    // 设置格式:模板文件位置,数据起始写入行行号(第一行序号从1开始)
    // 与template_simple、template_getter互斥,template_simple优先
    template_advance = "/home/feiy/Desktop/temp.xlsx,4",
    // 通过函数获取模板信息
    // 用于模板文件动态确定场景
    // 与template_simple、template_advance互斥,template_simple优先、template_advance次之
    // 该函数签名:fn get_template() -> xlsx_group_write::data::XlsxInitTemplet;     
    template_getter = "my_template_getter",
    // 分组id指定,如无需分组输出xlsx文件,可不指定。示例为使用写入数据的dep属性,
    group_maker = "&self.dep",
    // 快捷表头设置,各列以逗号分隔
    template_simple = "序号,姓名,手机,部门"
    // 通过模板文件生成表头,设置列宽等
    // 设置格式:模板文件位置,数据起始写入行行号(第一行序号从1开始)
    // 与template_simple互斥,template_simple优先
    template_advance = "/home/feiy/Desktop/temp.xlsx,4",
    // 表格个性化信息添加器设置
    // 在表格中添加自定义个性化信息
    // 该函数签名:fn add_custom_info(sheet: &mut Worksheet, group_id: &str)
    // 如果设置了额外的个性化参数extra_arg_type属性,函数签名为:
    // 该函数签名:fn add_custom_info(sheet: &mut Worksheet, group_id: &str,extra_arg:Option<&Self::ExtraArgType>) 
    custom_info_adder = "add_custom_info",
    // 输出的xlsx文件名前缀,含有完整路径,实际输出会自动添加.xlsx后缀
    output_file_name_simple = "/tmp/test",
    // 设置自动在输出文件名中添加当前日期
    // 需与output_file_name_simple一起使用,
    // 最终生成文件名称为类似:/tmp/test-2023-04-23.xlsx
    output_file_name_add_date = "true",
    // 个性化文件名称生成器,将调用指定的函数获取输出文件名称
    // 该函数签名:fn get_output_file_name_advance(group_id: &str) -> String
    // 如果设置了额外的个性化参数extra_arg_type属性,函数签名为:
    // 该函数签名:fn get_output_file_name_advance(group_id: &str,extra_arg:Option<&Self::ExtraArgType>) -> String 
    output_file_name_advance = "get_output_file_name_advance",
    // 个性化信息类型
    // 输出xlsx文件时,向文件名称生成器函数和custom_info_add传入个性化信息
    extra_arg_type ="String",
)]

推荐使用xlsx_group_write_macro_derive

三、使用示例

(一)示例 1

最快捷使用场景:自动生成表头,各行数据自动写入。

1、 示例 1 - 宏版本(推荐用法)

use xlsx_group_write::prelude::*;

#[derive(XlsxGroupWriteQuicker)]
#[xlsx_group_write(
    line_writer_simple = "line_index-1:number,&self.name,&self.tel,&self.dep",
    group_maker = "&self.dep",
    template_simple = "序号,姓名,手机,部门",
    output_file_name_simple = "/tmp/test"
)]
struct MySimpleData {
    pub name: String,
    pub tel: String,
    pub dep: String,
}
impl MySimpleData {
    pub fn new(name: &str, tel: &str, dep: &str) -> Self {
        Self {
            name: name.into(),
            tel: tel.into(),
            dep: dep.into(),
        }
    }
}

#[test]
fn test_simple() {
    // 初始化待导出数据
    let data = vec![
        MySimpleData::new("张三", "185xxxx2228", "网金部"),
        MySimpleData::new("李四", "185xxxx2229", "运管部"),
        MySimpleData::new("王二", "185xxxx2230", "网金部"),
    ];
    // 导出数据到xlsx文件
    // 自动按照部门分别导出,同时还汇总导出一个汇总xlsx文件
    // 成功导出返回对应的文件信息和分组id
    let resp = MySimpleData::write2xlsx_all(&data);
    println!("resp:{resp:#?}");
}

2、 示例1 - 自行实现trait版本

use xlsx_group_write::*;
use xlsx_group_write::data::XlsxColValueType;

/// 将要导出为xlsx文件的数据结构
struct MySimpleData {
    pub name: String,
    pub tel: String,
    pub dep: String,
}

impl MySimpleData {
    pub fn new(name: &str, tel: &str, dep: &str) -> Self {
        Self {
            name: name.into(),
            tel: tel.into(),
            dep: dep.into(),
        }
    }
}
// 实现trait,以便快捷导出为xlsx
impl XlsxGroupWrite for MySimpleData {
    /// 每行数据写入方式为简便模式
    /// 即直接根据line_writer_simple返回的对照表自动写入数据
    /// 无需了解xlsx写入api
    const LINE_WRITER_MODEL: XlsxLineWriterModel = XlsxLineWriterModel::Simple;

    /// 每一行写入四列数据,分别为:序号、姓名,手机和部门.
    /// # 参数说明
    /// 
    /// * index 为正在写入行的行号,行号从1开始计数。
    fn line_writer_simple(&self, index: u32) -> Vec<XlsxColValue> {
        vec![
            // 因为第一行是自动生成的表头,因此序号-1
            XlsxColValue::new(index - 1, XlsxColValueType::NumberValue),
            XlsxColValue::new(&self.name, XlsxColValueType::StringValue),
            XlsxColValue::new(&self.tel, XlsxColValueType::StringValue),
            XlsxColValue::new(&self.dep, XlsxColValueType::StringValue),
        ]
    }
    /// 设置分组id
    /// 
    /// 按照部门对数据进行分组
    /// 如网金部,写入到网金部文件,运管部写入到运管部文件中。
    fn group_make(&self) -> String {
        self.dep.clone()
    }
    /// 设置表头内容
    /// 列之间使用英文,分隔,将自动写入表格第一行。
    /// 
    /// 设置四列表头分别为:序号、姓名、手机和部门。
    fn get_template() -> XlsxInitTemplet {
        XlsxInitTemplet::new_header("序号,姓名,手机,部门")
    }

    /// 输出xlsx文件名设置
    /// 生成的文件名称为:/tmp/test-分组id.xlsx
    const OUTPUT_FILE_NAME_GETTER_SIMPLE: Option<OutputFileNameSimpleGetter> =
        Some(OutputFileNameSimpleGetter::new(
            "/tmp/test",
        ));
}

#[test]
fn test_simple_temp() {
    // 初始化待导出数据
    let data = vec![
        MySimpleData::new("张三", "185xxxx2228", "网金部"),
        MySimpleData::new("李四", "185xxxx2229", "运管部"),
        MySimpleData::new("王二", "185xxxx2230", "网金部"),
    ];
    // 导出数据到xlsx文件
    // 自动按照部门分别导出,同时还汇总导出一个汇总xlsx文件
    // 成功导出返回对应的文件信息和分组id
    let resp = MySimpleData::write2xlsx_all(
        &data
    );
    /*
    输出:
    Ok(
        [
            XlsxGroupWriteResp {
                group_id: "网金部",
                file_name: "/tmp/test-网金部.xlsx",
            },
            XlsxGroupWriteResp {
                group_id: "运管部",
                file_name: "/tmp/test-运管部.xlsx",
            },
            XlsxGroupWriteResp {
                group_id: "合并",
                file_name: "/tmp/test-合并.xlsx",
            },
        ],
    )
     */
    println!("{resp:#?}");
}

(二)示例 2

我们可以先自己编辑好一个Excel表格的样式,设置好标题、表头、字体等,然后以此xlsx文件为模板,生成表标题和表头,并在表格添加自定义信息,各行数据自动写入

1、 示例2-宏版本(推荐用法)

use umya_spreadsheet::Worksheet;
use xlsx_group_write::prelude::*;

#[derive(XlsxGroupWriteQuicker)]
#[xlsx_group_write(
    line_writer_simple = "line_index-1:number,&self.name,&self.tel,&self.dep",
    group_maker = "&self.dep",
    template_advance = "/home/feiy/Desktop/temp.xlsx,4",
    custom_info_adder = "Self::add_custom_info",
    output_file_name_simple = "/tmp/test",
)]
struct MySimpleDataWithTemplate {
    pub name: String,
    pub tel: String,
    pub dep: String,
}
impl MySimpleDataWithTemplate {
    pub fn new(name: &str, tel: &str, dep: &str) -> Self {
        Self {
            name: name.into(),
            tel: tel.into(),
            dep: dep.into(),
        }
    }

    /// 对导出的xlsx文件进行一些个性化修改
    /// 比如需要在第二行插入对应分组名称,插入报表日期等信息
    ///
    /// 将在xlsx初始化完成后自动调用该方法
    ///
    /// # 参数说明
    ///
    /// * sheet 要修改的xlsx工作表
    /// * group_id 该xlsx对应的分组id
    fn add_custom_info(sheet: &mut Worksheet, group_id: &str) {
        sheet
            .get_cell_mut((&1, &2))
            .set_value_string(&format!("{group_id},报表日期:2023年4月20日"));
    }
}

#[test]
fn test_simple_with_template() {
    // 初始化待导出数据
    let data = vec![
        MySimpleDataWithTemplate::new("张三", "185xxxx2228", "网金部"),
        MySimpleDataWithTemplate::new("李四", "185xxxx2229", "运管部"),
        MySimpleDataWithTemplate::new("王二", "185xxxx2230", "网金部"),
    ];
    // 导出数据到xlsx文件
    // 自动按照部门分别导出,同时还汇总导出一个汇总xlsx文件
    // 成功导出返回对应的文件信息和分组id
    let resp = MySimpleDataWithTemplate::write2xlsx_all(&data);
    println!("resp:{resp:#?}");
}

1.1、 示例2.1-宏版本(完全个性化模板)

动态确定xlsx文件模板,生成表标题和表头,并在表格添加自定义信息,各行数据自动写入

#[derive(XlsxGroupWriteQuicker)]
#[xlsx_group_write(
    line_writer_simple = "line_index-1:number,&self.name,&self.tel,&self.dep",
    group_maker = "&self.dep",
    template_getter = "Self::get_cur_template",
    custom_info_adder = "Self::add_custom_info",
    output_file_name_simple = "/tmp/test",
    output_file_name_add_date = "true"
)]
struct MySimpleDataWithTemplateGetter {
    pub name: String,
    pub tel: String,
    pub dep: String,
}
impl MySimpleDataWithTemplateGetter {
    pub fn new(name: &str, tel: &str, dep: &str) -> Self {
        Self {
            name: name.into(),
            tel: tel.into(),
            dep: dep.into(),
        }
    }
    /// 动态确定模板文件
    fn get_cur_template() -> XlsxInitTemplet{
        XlsxInitTemplet::new_advance("/home/feiy/Desktop/template.xlsx", 3)
    }
    /// 对导出的xlsx文件进行一些个性化修改
    /// 比如需要在第二行插入对应分组名称,插入报表数据范围等信息
    ///
    /// 将在xlsx初始化完成后自动调用该方法
    ///
    /// # 参数说明
    ///
    /// * sheet 要修改的xlsx工作表
    /// * group_id 该xlsx对应的分组id
    fn add_custom_info(sheet: &mut Worksheet, group_id: &str) {
        sheet
            .get_cell_mut((&1, &2))
            .set_value_string(&format!("{group_id},报表日期:2023年4月20日"));
    }
}

#[test]
fn test_simple_with_template_getter() {
    // 初始化待导出数据
    let data = vec![
        MySimpleDataWithTemplateGetter::new("张三", "185xxxx2228", "网金部"),
        MySimpleDataWithTemplateGetter::new("李四", "185xxxx2229", "运管部"),
        MySimpleDataWithTemplateGetter::new("王二", "185xxxx2230", "网金部"),
    ];
    // 导出数据到xlsx文件
    // 自动按照部门分别导出,同时还汇总导出一个汇总xlsx文件
    // 成功导出返回对应的文件信息和分组id
    let resp = MySimpleDataWithTemplateGetter::write2xlsx_all(&data);
    println!("resp:{resp:#?}");
}

2、 示例2 - 自行实现trait版本


use umya_spreadsheet::Worksheet;
use xlsx_group_write::prelude::*;

struct MyData {
    pub name: String,
    pub tel: String,
    pub dep: String,
}

impl MyData {
    pub fn new(name: &str, tel: &str, dep: &str) -> Self {
        Self {
            name: name.into(),
            tel: tel.into(),
            dep: dep.into(),
        }
    }
}


impl XlsxGroupWrite for MyData {
    const LINE_WRITER_MODEL: XlsxLineWriterModel = XlsxLineWriterModel::Simple;

    fn line_writer_simple(&self, index: u32) -> Vec<XlsxColValue> {
        vec![
            XlsxColValue::new(index - 3, XlsxColValueType::NumberValue),
            XlsxColValue::new(&self.name, XlsxColValueType::StringValue),
            XlsxColValue::new(&self.tel, XlsxColValueType::StringValue),
            XlsxColValue::new(&self.dep, XlsxColValueType::StringValue),
        ]
    }

    fn group_make(&self) -> String {
        self.dep.clone()
    }
    /// 设置以/home/feiy/Desktop/temp.xlsx为模板创建导出xlsx文件,
    /// 该模板中从第4行开始写入实际需要导出的数据。
    fn get_template() -> XlsxInitTemplet {
        XlsxInitTemplet::new_advance("/home/feiy/Desktop/temp.xlsx", 4)
    }
    /// 对生成的excel表格增加个性化信息
    /// 
    /// 在第二行加入,部门信息和导出日期信息
    /// 
    /// # 参数说明
    ///
    /// * sheet 要修改的xlsx工作表
    /// * group_id 该xlsx对应的分组id
    fn add_custom_info_to_sheet(sheet: &mut Worksheet, group_id: &str) {
        sheet
            .get_cell_mut((&1, &2))
            .set_value_string(&format!("{group_id},报表日期:2023年4月20日"));
    }

    const OUTPUT_FILE_NAME_GETTER_SIMPLE: Option<OutputFileNameSimpleGetter> =
        Some(OutputFileNameSimpleGetter::new(
            "/tmp/test",
        ));
}


#[test]
fn test_custom_info() {
    let data = vec![
        MyData::new("张三", "185xxxx2228", "网金部"),
        MyData::new("李四", "185xxxx2229", "运管部"),
        MyData::new("王二", "185xxxx2230", "网金部"),
    ];
    // 只分组导出对应部门xlsx文件,不导出所有数据合并到一个文件的汇总文件
    let resp = MyData::write2xlsx_group_only(
        &data,
    );
    println!("{resp:#?}");
}

(三)示例 3

自动生成表头,各行数据调用个性化写入函数write_xlsx_line写入 如果您的数据并不是一个字段写入一列的,需要合并写入,或者需要自定义单元格样式。

1、 示例3-宏版本(推荐用法)

use umya_spreadsheet::Worksheet;
use xlsx_group_write::prelude::*;

#[derive(XlsxGroupWriteQuicker)]
#[xlsx_group_write(
    line_writer_advance = "Self::write_xlsx_line",
    group_maker = "&self.dep",
    template_simple = "序号,姓名,手机,部门",
    output_file_name_simple = "/tmp/test",
)]
struct MyAdvanceData {
    pub name: String,
    pub tel: String,
    pub dep: String,
}
impl MyAdvanceData {
    pub fn new(name: &str, tel: &str, dep: &str) -> Self {
        Self {
            name: name.into(),
            tel: tel.into(),
            dep: dep.into(),
        }
    }
    /// 个性化写入行数据
    fn write_xlsx_line(
        line_data:&MyAdvanceData,
        sheet: &mut Worksheet,
        line_index: u32,
    ) -> Option<XlsxLineAdvanceWriterResult> {
        XlsxWriterTool::set_excel_cell_value_number(sheet, 1, line_index, line_index - 1);
        XlsxWriterTool::set_excel_cell_value_str(sheet, 2, line_index, &format!("ad-{}",&line_data.name));
        XlsxWriterTool::set_excel_cell_value_str(sheet, 3, line_index, &line_data.tel);
        XlsxWriterTool::set_excel_cell_value_str(sheet, 4, line_index, &line_data.dep);
        None
    }
}

#[test]
fn test_advance_write() {
    // 初始化待导出数据
    let data = vec![
        MyAdvanceData::new("张三", "185xxxx2228", "网金部"),
        MyAdvanceData::new("李四", "185xxxx2229", "运管部"),
        MyAdvanceData::new("王二", "185xxxx2230", "网金部"),
    ];
    // 导出数据到xlsx文件
    // 自动按照部门分别导出,同时还汇总导出一个汇总xlsx文件
    // 成功导出返回对应的文件信息和分组id
    let resp = MyAdvanceData::write2xlsx_all(&data);
    println!("resp:{resp:#?}");
}

2、 示例3 - 自行实现trait版本

use xlsx_group_write::*;
use xlsx_group_write::data::XlsxColValueType;

struct MyData3 {
    pub name: String,
    pub tel: String,
    pub dep: String,
}

impl MyData3 {
    pub fn new(name: &str, tel: &str, dep: &str) -> Self {
        Self {
            name: name.into(),
            tel: tel.into(),
            dep: dep.into(),
        }
    }
}

impl XlsxGroupWrite for MyData3 {
    /// 行写入模式为高级写入模式,完全自定义
    const LINE_WRITER_MODEL: XlsxLineWriterModel = XlsxLineWriterModel::Advance;
    /// 使用xlsx api 写入每行的数据
    fn line_writer_advance(
        &self,
        sheet: &mut Worksheet,
        index: u32,
    ) -> Option<XlsxLineAdvanceWriterResult> {
        xlsx_util::XlsxWriterTool::set_excel_cell_value_number(sheet, 1, index, index - 2);
        xlsx_util::XlsxWriterTool::set_excel_cell_value_str(sheet, 2, index, &self.name);
        xlsx_util::XlsxWriterTool::set_excel_cell_value_str(sheet, 3, index, &self.tel);
        xlsx_util::XlsxWriterTool::set_excel_cell_value_str(sheet, 4, index, &self.dep);
        None
    }
    /// 设置表头内容
    /// 列之间使用英文,分隔,将自动写入表格第一行。
    /// 
    /// 设置四列表头分别为:序号、姓名、手机和部门。
    fn get_template() -> XlsxInitTemplet {
        XlsxInitTemplet::new_header("序号,姓名,手机,部门")
    }
    /// 输出文件位置和文件名称由函数自行控制,比如每个部门文件存放不同的路径中等
    fn get_output_file_name_advance(groupt_id: &str) -> String {
        format!("/tmp/advance-{groupt_id}.xlsx")
    }
    fn add_custom_info_to_sheet(sheet: &mut Worksheet, group_id: &str) {
        sheet
            .get_cell_mut((&1, &2))
            .set_value_string(&format!("{group_id},报表日期:2023年4月20日"));
    }
}

#[test]
fn test_advance_write() {
    let data = vec![
        MyData3::new("张三", "185xxxx2228", "网金部"),
        MyData3::new("李四", "185xxxx2229", "运管部"),
        MyData3::new("王二", "185xxxx2230", "网金部"),
    ];
    let resp = MyData3::write2xlsx_merge_only(
        &data
    );
    println!("{resp:#?}");
}

(四)示例 4

自动生成表头,各行数据自动写入,输出文件名称通过函数获取

1、 示例4-宏版本(推荐用法)

use umya_spreadsheet::Worksheet;
use xlsx_group_write::prelude::*;

#[derive(XlsxGroupWriteQuicker)]
#[xlsx_group_write(
    line_writer_simple = "line_index-1:number,&self.name,&self.tel,&self.dep",
    group_maker = "&self.dep",
    template_simple = "序号,姓名,手机,部门",
    output_file_name_advance = "Self::get_output_file_name_advance",
)]
struct MyFileNameData {
    pub name: String,
    pub tel: String,
    pub dep: String,
}
impl MyFileNameData {
    pub fn new(name: &str, tel: &str, dep: &str) -> Self {
        Self {
            name: name.into(),
            tel: tel.into(),
            dep: dep.into(),
        }
    }

    /// 完全自定义的输出文件名称名称生成器
    ///
    /// 参数说明:
    ///
    /// * groupt_id :当期数据的分组id
    #[allow(dead_code)]
    fn get_output_file_name_advance(group_id: &str) -> String {
        format!("/tmp/f-{group_id}.xlsx")
    }

}

#[test]
fn test_advance_file_name() {
    // 初始化待导出数据
    let data = vec![
        MyFileNameData::new("张三", "185xxxx2228", "网金部"),
        MyFileNameData::new("李四", "185xxxx2229", "运管部"),
        MyFileNameData::new("王二", "185xxxx2230", "网金部"),
    ];
    // 导出数据到xlsx文件
    // 自动按照部门分别导出,同时还汇总导出一个汇总xlsx文件
    // 成功导出返回对应的文件信息和分组id
    let resp = MyFileNameData::write2xlsx_all(&data);
    println!("resp:{resp:#?}");
}

2、 示例4 - 自行实现trait版本

use xlsx_group_write::*;
use xlsx_group_write::data::XlsxColValueType;

struct MyData3 {
    pub name: String,
    pub tel: String,
    pub dep: String,
}

impl MyData3 {
    pub fn new(name: &str, tel: &str, dep: &str) -> Self {
        Self {
            name: name.into(),
            tel: tel.into(),
            dep: dep.into(),
        }
    }
}

impl XlsxGroupWrite for MyData3 {
    const LINE_WRITER_MODEL: XlsxLineWriterModel = XlsxLineWriterModel::Simple;

    fn line_writer_simple(&self, index: u32) -> Vec<XlsxColValue> {
        vec![
            XlsxColValue::new(index - 1, XlsxColValueType::NumberValue),
            XlsxColValue::new(&self.name, XlsxColValueType::StringValue),
            XlsxColValue::new(&self.tel, XlsxColValueType::StringValue),
            XlsxColValue::new(&self.dep, XlsxColValueType::StringValue),
        ]
    }
    /// 设置表头内容
    /// 列之间使用英文,分隔,将自动写入表格第一行。
    /// 
    /// 设置四列表头分别为:序号、姓名、手机和部门。
    fn get_template() -> XlsxInitTemplet {
        XlsxInitTemplet::new_header("序号,姓名,手机,部门")
    }
    /// 输出文件位置和文件名称由函数自行控制,比如每个部门文件存放不同的路径中等
    fn get_output_file_name_advance(groupt_id: &str) -> String {
        format!("/tmp/advance-{groupt_id}.xlsx")
    }
}

#[test]
fn test_advance_write() {
    let data = vec![
        MyData3::new("张三", "185xxxx2228", "网金部"),
        MyData3::new("李四", "185xxxx2229", "运管部"),
        MyData3::new("王二", "185xxxx2230", "网金部"),
    ];
    let resp = MyData3::write2xlsx_merge_only(
        &data
    );
    println!("{resp:#?}");
}

(四)示例 5

自动生成表头,各行数据个性化写入,传递额外参数到文件名称,写入个性化信息到xlsx表中

use xlsx_group_write::*;

#[derive(XlsxGroupWriteQuicker)]
#[xlsx_group_write(
    line_writer_advance_with_extra_arg = "Self::write_xlsx_line",
    group_maker = "&self.dep",
    template_simple = "序号,姓名,手机,部门",
    extra_arg_type ="String",
    custom_info_adder = "Self::add_custom_info",
    output_file_name_advance = "Self::get_output_file_name_advance",
)]
struct MySimpleDataWithExtraArg {
    pub name: String,
    pub tel: String,
    pub dep: String,
}
impl MySimpleDataWithExtraArg {
    pub fn new(name: &str, tel: &str, dep: &str) -> Self {
        Self {
            name: name.into(),
            tel: tel.into(),
            dep: dep.into(),
        }
    }
    /// 写入行数据,传入额外参数
    fn write_xlsx_line(
        line_data:&MySimpleDataWithExtraArg,
        sheet: &mut Worksheet,
        line_index: u32,
        extra_arg: Option<&String>,
    ) -> Option<XlsxLineAdvanceWriterResult> {
        XlsxWriterTool::set_excel_cell_value_number(sheet, 1, line_index, line_index - 1);
        XlsxWriterTool::set_excel_cell_value_str(sheet, 2, line_index, &format!("ad-{}",&line_data.name));
        XlsxWriterTool::set_excel_cell_value_str(sheet, 3, line_index, &line_data.tel);
        XlsxWriterTool::set_excel_cell_value_str(sheet, 4, line_index, &line_data.dep);
        if let Some(extra_arg) = extra_arg{
            XlsxWriterTool::set_excel_cell_value_str(sheet, 5, line_index, extra_arg);
        }
        None
    }
    /// 对导出的xlsx文件进行一些个性化修改
    /// 比如需要在第二行插入对应分组名称,插入报表数据范围等信息
    ///
    /// 将在xlsx初始化完成后自动调用该方法
    ///
    /// # 参数说明
    ///
    /// * sheet 要修改的xlsx工作表
    /// * group_id 该xlsx对应的分组id
    fn add_custom_info(sheet: &mut Worksheet, group_id: &str,org_name:Option<&String>) {
        if let Some(org_name) = org_name{
            sheet
                .get_cell_mut((&1, &5))
                .set_value_string(&format!("{org_name} {group_id},报表日期:2023年4月20日"));
        }else{
            sheet
                .get_cell_mut((&1, &5))
                .set_value_string(&format!("{group_id},报表日期:2023年4月20日"));
        }
    }
    /// 完全自定义的输出文件名称名称生成器
    ///
    /// 参数说明:
    ///
    /// * groupt_id :当期数据的分组id
    #[allow(dead_code)]
    fn get_output_file_name_advance(group_id: &str,org_name:Option<&String>) -> String {
        if let Some(org_name) = org_name{
            format!("/tmp/f-{org_name}-{group_id}.xlsx")
        }else{
            format!("/tmp/f-{group_id}.xlsx")
        }
    }
}

#[test]
fn test_simple_with_extra_arg() {
    // 初始化待导出数据
    let data = vec![
        MySimpleDataWithExtraArg::new("张三", "185xxxx2228", "网金部"),
        MySimpleDataWithExtraArg::new("李四", "185xxxx2229", "运管部"),
        MySimpleDataWithExtraArg::new("王二", "185xxxx2230", "网金部"),
    ];
    // 导出汇总数据到xlsx文件
    // 成功导出返回对应的文件信息和分组id
    // 传入个性化参数
    let resp = MySimpleDataWithExtraArg::write2xlsx_merge_only_with_extra_arg(&data,Some(String::from("个性化参数")));
    println!("resp:{resp:#?}");
}

依赖项

~37MB
~490K SLoC