Bootstrap

springboot使用freemarker进行复杂Excel导出(多sheet页)

springboot使用freemarker进行复杂Excel导出(多sheet页)

下面是Excel部门sheet展示

上图中的配置率,挂职及培养锻炼后配置率,借用后配置率会根据规则进行表格颜色的填充

freemarker常用标签及语法了解

内容来自:freemarker常见语法大全

FTL模板编写

先在Excel中编写所需的设置和样式等等,如果有多个sheet页一次性编写完即可,然后点击文件–另存为(格式选择为XML)
在这里插入图片描述上面表格中${xxx.xxx}是在生成Excel时取数据时的格式,数据要是前准备好了就可以直接写上去(在FTL文件中改比较麻烦…),然后在ftl文件中根据freemarker语法进行取值就可以了

另存为XML
在这里插入图片描述

代码示例

  • 引入依赖
<dependency>
      <groupId>org.freemarker</groupId>
      <artifactId>freemarker</artifactId>
</dependency>
  • 修改配置文件
spring:
  freemarker:
    template-loader-path: classpath:/template
    suffix: .ftl
  • 将上述另存的xml文件保存在项目当中,记得后缀改为 .ftl。这里的文件路径要和配置中所写的保持一致
    在这里插入图片描述

Freemarker工具类

import freemarker.template.Configuration;
import freemarker.template.Template;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.URLEncoder;
import java.util.Map;


public class FreeMarkerUtil {

    /**
     * @title: ExportExcel
     * @description: 导出excel
     * @author:
     * @date: 2023/4/24
     * @param: fileName
     * @param: templateName
     * @param: data
     * @return: void
     */
    public static void ExportExcel(String fileName, String templateName, Map<String,Object> data) throws IOException {
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        response.reset();
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/vnd.ms-excel");
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
        OutputStreamWriter writer = null;
        try {
            Template template = FreeMarkerUtil.getTemplate(templateName);
            writer = new OutputStreamWriter(response.getOutputStream(), "UTF-8");
            template.process(data, writer);
            writer.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }

    private static Template getTemplate(String templateName) throws IOException {
        Configuration configuration = new Configuration(Configuration.VERSION_2_3_26);
        configuration.setDefaultEncoding("utf-8");
        configuration.setClassForTemplateLoading(FreeMarkerUtil.class,"/template");
        return configuration.getTemplate(templateName);
    }

}

导出调用–具体的方法跟自己的情况来定

    /**
     * 
     * @param excelParamVO 这个参数用于判断是否需要导出相关的sheet以级对表格中表头是否展示进行控住
     */
public void exportExcel(ComplexExcelParamVO excelParamVO){
        Map<String, Object> excelMap =  new HashMap<>();
        excelMap.put("excelParam",excelParamVO);
        if (excelParamVO.isStatisticSheet()){
            /*编制报表*/
            Map<String, Object> map = queryStatisticSheet(excelParamVO.isStaffCell(),excelParamVO.isExpertCell());
            excelMap.putAll(map);
        }
        if (excelParamVO.isStaffInfoSheet()){
            /*员工*/
            Map<String, Object> map = queryStaffInfoSheet();
            excelMap.putAll(map);
        }
        if (excelParamVO.isThirdLevelStaffSheet()){
            /*一二三级员工*/
        }
        if (excelParamVO.isOwnerDeptInfoSheet()){
            /*本部及本部花名册*/
        }
        if (excelParamVO.isFiveLevelStaffSheet()){
            /*四五级职员*/
        }
        if (excelParamVO.isSpecificRetireSheet()){
            /*专责退休*/
        }
        if (excelParamVO.isHangAndFosterSheet()){
            /*挂职和培养锻炼*/
        }
        if (excelParamVO.isBorrowStaffSheet()){
            /*借用人员名册*/
        }

        try {
            FreeMarkerUtil.ExportExcel("test","allData.ftl",excelMap);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /*
    * 编制报表数据
    * */
    public Map<String, Object> queryStatisticSheet(boolean staffCell,boolean expertCell){
        Map<String, Object> excelData = new HashMap<>();
        Map<String, Object> unitConfigList = getUnitConfigList(new EmpBasicVo());
        List<PrepareStatementVo> unitList = (List<PrepareStatementVo>) unitConfigList.get("unitList");
        List<PrepareStatementVo> deptList = (List<PrepareStatementVo>) unitConfigList.get("deptList");
        for (PrepareStatementVo prepareStatementVo : unitList) {
            String id = prepareStatementVo.getId();
            for (PrepareStatementVo statementVo : deptList) {
                String pid = statementVo.getPid();
                if (StringUtils.equals(id,pid)){
                    prepareStatementVo.getChildren().add(statementVo);
                }
            }
        }
        PrepareStatementVo totalcontData = (PrepareStatementVo) unitConfigList.get("totalcontData");
        excelData.put("dataList",unitList);
        excelData.put("totalcontData",totalcontData);
        excelData.put("staff",staffCell);
        excelData.put("expert",expertCell);
        return excelData;
    }
    /*
    * 员工数据组装
    * */
    public Map<String,Object> queryStaffInfoSheet(){
        Map<String,Object> resultMap =  new HashMap<>();
        /*查询员工信息根据处室分组,人员信息必须带有岗位信息*/
        List<StaffInfoSheetVo> staffInfoSheetVos = empBasicMapper.queryStaffInfoSheet();
        Map<String, List<StaffInfoSheetVo>> collect = staffInfoSheetVos.stream().collect(Collectors.groupingBy(StaffInfoSheetVo::getDeptId));
        Map<String, Object> map = queryStatisticSheet(false, false);
        List<PrepareStatementVo> unitList = (List<PrepareStatementVo>) map.get("dataList");
        for (PrepareStatementVo prepareStatementVo : unitList) {
            String unitId = prepareStatementVo.getId();
            List<StaffInfoSheetVo> unitStaffInfo = collect.get(unitId);
            prepareStatementVo.setStaffList(unitStaffInfo);
            List<PrepareStatementVo> children = prepareStatementVo.getChildren();
            for (PrepareStatementVo child : children) {
                /*部门id*/
                String id = child.getId();
                List<StaffInfoSheetVo> deptStaffInfo = collect.get(id);
                if (isNotEmpty(deptStaffInfo)){
                    Map<String, List<StaffInfoSheetVo>> collect1 = deptStaffInfo.stream().collect(Collectors.groupingBy(StaffInfoSheetVo::getDeptId));
                }
                child.setStaffList(deptStaffInfo);
            }
        }
        resultMap.put("staffInfoSheet",unitList);
        return resultMap;
    }

以下是一些相关的实体类 代码中未引入lombok 使用的是get和set方法此处进行了省略


import com.bms.project.govern.vo.excelVo.StaffInfoSheetVo;

import java.util.ArrayList;
import java.util.List;

/**
 * 编制报表数据VO
 */
public class PrepareStatementVo {
    /*部门id*/
    private String id;
    /*部门父id*/
    private String pid;
    /*部门名称*/
    private String name;
    /*公司简称-部门简称*/
    private String deptName;
    /*编制*/
    private Integer count2;
    /*实配*/
    private Integer count3;
    /*专责*/
    private Integer count4;
    /*一二三级职员*/
    private Integer count5;
    /*四级职员*/
    private Integer count6;
    /*五级职员*/
    private Integer count7;
    /*挂职和培养锻炼*/
    private Integer count8;
    /*借用*/
    private Integer count9;
    /*超缺*/
    private Integer count10;
    /*配置率颜色*/
    private String rateColor;
    /*挂职及培养锻炼后配置率颜色*/
    private String hangrateColor;
    /*借用后配置率颜色*/
    private String borrowrateColor;
    /*配置率*/
    private String rate;
    /*挂职及培养锻炼配置率*/
    private String hangrate;
    /*借用配置率*/
    private String borrowrate;

    private List<PrepareStatementVo> children = new ArrayList<>();

    private List<StaffInfoSheetVo> staffList;
}

前端请求参数实体类(前端的实现是一个多选框,下面实体类中的staffCell和expertCell用于控制statisticSheet编制报表sheet中相关表头的动态显示)。字段值为TRUE则是需要导出的sheet页

/*
* 总表自定义导出请求参数VO
* */
public class ComplexExcelParamVO {
    /*
    * 编制报表sheet
    * */
    private boolean statisticSheet;
    /*
    * 四五级职员表格
    * */
    private boolean staffCell;
    /*
    * 四五级专家表格
    * */
    private boolean expertCell;
    /*
    * 员工sheet
    * */
    private boolean staffInfoSheet;
    /*
    * 一二三级员工
    * */
    private boolean thirdLevelStaffSheet;
    /*
    * 本部及本部花名册
    * */
    private boolean ownerDeptInfoSheet;
    /*
     * 四五级职员
     * */
    private boolean fiveLevelStaffSheet;
    /*
    * 专责退休
    * */
    private boolean specificRetireSheet;
    /*
    * 挂职和培养锻炼
    * */
    private boolean hangAndFosterSheet;
    /*
    * 借用人员名册
    * */
    private boolean borrowStaffSheet;
}

FTL文件部分展示

部门标签说明
<#-- 
ss:MergeDown="1" 向下合并一行
ss:MergeAcross="5" 向右合并5行 包括本单元格一共是6个单元格进行合并的
-->
					 <#if officeInfo.type?? && officeInfo.type == '1'>
					<#--  officeInfo.type不为空且等于1 -->
                        <Data ss:Type="String"></Data>
                    <#else>
                    <#-- 否则 -->
                        <Data ss:Type="String">${officeInfo.officeName}</Data>
                    </#if>
                   
<#--  此处为判断是否需要次sheet文件  -->
<#if excelParam.thirdLevelStaffSheet >
	<Worksheet ss:Name="sheetName" .....>
</#if>

<#--  下面的<#if staff >为判断是否需要展示这些表头  -->
<#--  ss:StyleID="s208" 为对应的样式id  -->
			<Row ss:Height="24">
                <#if staff >
                <Cell ss:Index="13" ss:StyleID="s208">
                    <Data ss:Type="String">四级职员编制</Data>
                </Cell>
                <Cell ss:StyleID="s208">
                    <Data ss:Type="String">四级职员实配</Data>
                </Cell>
                <Cell ss:StyleID="s209">
                    <Data ss:Type="String">四级职员配置率</Data>
                </Cell>
                <Cell ss:StyleID="s209">
                    <Data ss:Type="String">五级职员编制</Data>
                </Cell>
                <Cell ss:StyleID="s209">
                    <Data ss:Type="String">五级职员实配</Data>
                </Cell>
                <Cell ss:StyleID="s211">
                    <Data ss:Type="String">五级职员配置率</Data>
                </Cell>
                </#if>
                <#if expert >
                <Cell ss:Index="20" ss:StyleID="s208">
                    <Data ss:Type="String">四级专家编制</Data>
                </Cell>
                <Cell ss:StyleID="s208">
                    <Data ss:Type="String">四级专家实配</Data>
                </Cell>
                <Cell ss:StyleID="s209">
                    <Data ss:Type="String">四级专家配置率</Data>
                </Cell>
                <Cell ss:StyleID="s209">
                    <Data ss:Type="String">五级专家编制</Data>
                </Cell>
                <Cell ss:StyleID="s209">
                    <Data ss:Type="String">五级专家实配</Data>
                </Cell>
                <Cell ss:StyleID="s211">
                    <Data ss:Type="String">五级专家配置率</Data>
                </Cell>
                </#if>
            </Row>
循环赋值
<#--  dataList 就是调用工具类是传递的map中的key  -->
<#-- <Cell ss:StyleID="${item.id}+'rate'"> 这样写是为了给单元格填充背景色 -->
<#list dataList as units>
            <Row ss:Height="57">
                <Cell ss:StyleID="s215" ss:MergeAcross="1">
                    <Data ss:Type="String">${units.name}</Data>
                </Cell>
                <Cell ss:StyleID="s190">
                    <Data ss:Type="String">${units.count2}</Data>
                </Cell>
                <Cell ss:StyleID="s190">
                    <Data ss:Type="String">${units.count3}</Data>
                </Cell>
                <Cell ss:StyleID="s190">
                    <Data ss:Type="String">${units.count4}</Data>
                </Cell>
                <Cell ss:StyleID="s190">
                    <Data ss:Type="String">${units.count10}</Data>
                </Cell>
                <Cell ss:StyleID="s190">
                    <Data ss:Type="String">${units.rate}</Data>
                </Cell>
                <Cell ss:StyleID="s190">
                    <Data ss:Type="String">${units.count8}</Data>
                </Cell>
                <Cell ss:StyleID="s190">
                    <Data ss:Type="String">${units.hangrate}</Data>
                </Cell>
                <Cell ss:StyleID="s190">
                    <Data ss:Type="String">${units.count9}</Data>
                </Cell>
                <Cell ss:StyleID="s190">
                    <Data ss:Type="String">${units.borrowrate}</Data>
                </Cell>
                <Cell ss:StyleID="s190">
                    <Data ss:Type="String">${units.count5}</Data>
                </Cell>
                <#if staff >
                <Cell ss:StyleID="s190"/>
                <Cell ss:StyleID="s190">
                    <Data ss:Type="String">${units.count6}</Data>
                </Cell>
                <Cell ss:StyleID="s190"/>
                <Cell ss:StyleID="s190"/>
                <Cell ss:StyleID="s190">
                    <Data ss:Type="String">${units.count7}</Data>
                </Cell>
                <Cell ss:StyleID="s190"/>
                </#if>
                <Cell ss:StyleID="s190"/>
                <#if expert >
                <Cell ss:StyleID="s190"/>
                <Cell ss:StyleID="s190"/>
                <Cell ss:StyleID="s190"/>
                <Cell ss:StyleID="s190"/>
                <Cell ss:StyleID="s190"/>
                <Cell ss:StyleID="s190"/>
                </#if>
            </Row>
			<#list units.children as item>
            <Row ss:Height="14.25">
                <Cell ss:StyleID="s191">
                    <Data ss:Type="String">${item_index+1}</Data>
                </Cell>
                <Cell ss:StyleID="s191">
                    <Data ss:Type="String">${item.name}</Data>
                </Cell>
                <Cell ss:StyleID="s192">
                    <Data ss:Type="String">${item.count2}</Data>
                </Cell>
                <Cell ss:StyleID="s191">
                    <Data ss:Type="String">${item.count3}</Data>
                </Cell>
                <Cell ss:StyleID="s193">
                    <Data ss:Type="String">${item.count4}</Data>
                </Cell>
                <Cell ss:StyleID="s194">
                    <Data ss:Type="String">${item.count10}</Data>
                </Cell>
                <Cell ss:StyleID="${item.id}+'rate'">
                    <Data ss:Type="String">${item.rate}</Data>
                </Cell>
                <Cell ss:StyleID="s191">
                    <Data ss:Type="String">${item.count8}</Data>
                </Cell>
                <Cell ss:StyleID="${item.id}+'hangrate'">
                    <Data ss:Type="String">${item.hangrate}</Data>
                </Cell>
                <Cell ss:StyleID="s191">
                    <Data ss:Type="String">${item.count9}</Data>
                </Cell>
                <Cell ss:StyleID="${item.id}+'borrowrate'">
                    <Data ss:Type="String">${item.borrowrate}</Data>
                </Cell>
                <Cell ss:StyleID="s191">
                    <Data ss:Type="String">${item.count5}</Data>
                </Cell>
                <#if staff >
                <Cell ss:StyleID="s191"/>
                <Cell ss:StyleID="s191">
                    <Data ss:Type="String">${item.count6}</Data>
                </Cell>
                <Cell ss:StyleID="s191"/>
                <Cell ss:StyleID="s191"/>
                <Cell ss:StyleID="s191">
                    <Data ss:Type="String">${item.count7}</Data>
                </Cell>
                <Cell ss:StyleID="s191"/>
                </#if>
                <Cell ss:StyleID="s191"/>
                <#if expert >
                <Cell ss:StyleID="s191"/>
                <Cell ss:StyleID="s191"/>
                <Cell ss:StyleID="s191"/>
                <Cell ss:StyleID="s191"/>
                <Cell ss:StyleID="s191"/>
                <Cell ss:StyleID="s191"/>
                </#if>
            </Row>
			 </#list>
            </#list>
单元格样式控制
<#-- 我在组装数据是已经根据规则计算了相关单元格要展示的颜色,字段为xxColor 颜色的格式为:#ADD88D -->
<#list dataList as units>
        <#list units.children as item>
		<#if item.rateColor?? >
			<Style ss:ID="${item.id}+'rate'">
            <Alignment ss:Horizontal="Center" ss:Vertical="Center"/>
				<Borders>
					<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
				</Borders>
            <Font ss:FontName="宋体" x:CharSet="134" ss:Size="12" ss:Bold="1"/>
            <Interior ss:Color="${item.rateColor}" ss:Pattern="Solid"/>
			</Style>
			
			<Style ss:ID="${item.id}+'borrowrate'">
            <Alignment ss:Horizontal="Center" ss:Vertical="Center"/>
				<Borders>
					<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
				</Borders>
            <Font ss:FontName="宋体" x:CharSet="134" ss:Size="12" ss:Bold="1"/>
            <Interior ss:Color="#ADD88D" ss:Pattern="Solid"/>
			</Style>
			
			<Style ss:ID="${item.id}+'hangrate'">
            <Alignment ss:Horizontal="Center" ss:Vertical="Center"/>
				<Borders>
					<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
				</Borders>
            <Font ss:FontName="宋体" x:CharSet="134" ss:Size="12" ss:Bold="1"/>
            <Interior ss:Color="#ADD88D" ss:Pattern="Solid"/>
			</Style>
		<#elseif item.borrowrateColor?? >
			<Style ss:ID="${item.id}+'rate'">
            <Alignment ss:Horizontal="Center" ss:Vertical="Center"/>
				<Borders>
					<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
				</Borders>
            <Font ss:FontName="宋体" x:CharSet="134" ss:Size="12" ss:Bold="1"/>
            <Interior ss:Color="#ADD88D" ss:Pattern="Solid"/>
			</Style>
		
			<Style ss:ID="${item.id}+'borrowrate'">
            <Alignment ss:Horizontal="Center" ss:Vertical="Center"/>
				<Borders>
					<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
				</Borders>
            <Font ss:FontName="宋体" x:CharSet="134" ss:Size="12" ss:Bold="1"/>
            <Interior ss:Color="${item.borrowrateColor}" ss:Pattern="Solid"/>
			</Style>
			
			<Style ss:ID="${item.id}+'hangrate'">
            <Alignment ss:Horizontal="Center" ss:Vertical="Center"/>
				<Borders>
					<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
				</Borders>
            <Font ss:FontName="宋体" x:CharSet="134" ss:Size="12" ss:Bold="1"/>
            <Interior ss:Color="#ADD88D" ss:Pattern="Solid"/>
			</Style>
		<#else>
			<Style ss:ID="${item.id}+'rate'">
            <Alignment ss:Horizontal="Center" ss:Vertical="Center"/>
				<Borders>
					<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
				</Borders>
            <Font ss:FontName="宋体" x:CharSet="134" ss:Size="12" ss:Bold="1"/>
            <Interior ss:Color="#ADD88D" ss:Pattern="Solid"/>
			</Style>
		
			<Style ss:ID="${item.id}+'borrowrate'">
            <Alignment ss:Horizontal="Center" ss:Vertical="Center"/>
				<Borders>
					<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
				</Borders>
            <Font ss:FontName="宋体" x:CharSet="134" ss:Size="12" ss:Bold="1"/>
            <Interior ss:Color="#ADD88D" ss:Pattern="Solid"/>
			</Style>
			
			<Style ss:ID="${item.id}+'hangrate'">
            <Alignment ss:Horizontal="Center" ss:Vertical="Center"/>
				<Borders>
					<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
					<Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
				</Borders>
            <Font ss:FontName="宋体" x:CharSet="134" ss:Size="12" ss:Bold="1"/>
            <Interior ss:Color="${item.hangrateColor}" ss:Pattern="Solid"/>
			</Style>
		</#if>
        
		</#list>
        </#list>
数据不是list则不需要循环直接赋值即可,ftl模板内容如下
<Row ss:Height="14.25">
                <Cell ss:StyleID="s216" ss:MergeAcross="1">
                    <Data ss:Type="String">合计</Data>
                </Cell>
                <Cell ss:StyleID="s198">
                    <Data ss:Type="String">${totalcontData.count2}</Data>
                </Cell>
                <Cell ss:StyleID="s198">
                    <Data ss:Type="String">${totalcontData.count3}</Data>
                </Cell>
                <Cell ss:StyleID="s198">
                    <Data ss:Type="String">${totalcontData.count4}</Data>
                </Cell>
                <Cell ss:StyleID="s198">
                    <Data ss:Type="String">${totalcontData.count10}</Data>
                </Cell>
                <Cell ss:StyleID="s199">
                    <Data ss:Type="String">${totalcontData.rate}</Data>
                </Cell>
                <Cell ss:StyleID="s199">
                    <Data ss:Type="String">${totalcontData.count8}</Data>
                </Cell>
                <Cell ss:StyleID="s198">
                    <Data ss:Type="String">${totalcontData.hangrate}</Data>
                </Cell>
                <Cell ss:StyleID="s198">
                    <Data ss:Type="String">${totalcontData.count9}</Data>
                </Cell>
                <Cell ss:StyleID="s199">
                    <Data ss:Type="String">${totalcontData.borrowrate}</Data>
                </Cell>
                <Cell ss:StyleID="s198">
                    <Data ss:Type="String">${totalcontData.count5}</Data>
                </Cell>
                <#if staff >
                <Cell ss:StyleID="s198"/>
                <Cell ss:StyleID="s198">
                    <Data ss:Type="String">${totalcontData.count6}</Data>
                </Cell>
                <Cell ss:StyleID="s198"/>
                <Cell ss:StyleID="s199"/>
                <Cell ss:StyleID="s198">
                    <Data ss:Type="String">${totalcontData.count7}</Data>
                </Cell>
                <Cell ss:StyleID="s199"/>
                </#if>
                <Cell ss:StyleID="s198"/>
                <#if expert >
                <Cell ss:StyleID="s198"/>
                <Cell ss:StyleID="s198"/>
                <Cell ss:StyleID="s198"/>
                <Cell ss:StyleID="s199"/>
                <Cell ss:StyleID="s198"/>
                <Cell ss:StyleID="s199"/>
                </#if>
            </Row>
表头窗口冻结标签
		....上面的内容省略 主要在WorksheetOptions中设置 这块是表头未冻结的
        </Table>
        <WorksheetOptions
            xmlns="urn:schemas-microsoft-com:office:excel">
            <PageSetup>
                <Header/>
                <Footer/>
            </PageSetup>
            <Selected/>
            <TopRowVisible>0</TopRowVisible>
            <LeftColumnVisible>0</LeftColumnVisible>
            <PageBreakZoom>100</PageBreakZoom>
            <Panes>
                <Pane>
                    <Number>3</Number>
                    <ActiveRow>24</ActiveRow>
                    <ActiveCol>3</ActiveCol>
                    <RangeSelection>R25C4</RangeSelection>
                </Pane>
            </Panes>
            <ProtectObjects>False</ProtectObjects>
            <ProtectScenarios>False</ProtectScenarios>
        </WorksheetOptions>
    </Worksheet>

表头窗口冻结

		...上面部分省略
        </Table>
        <WorksheetOptions
            xmlns="urn:schemas-microsoft-com:office:excel">
            <PageSetup>
                <Header/>
                <Footer/>
            </PageSetup>
            <Selected/>
            <TopRowVisible>0</TopRowVisible>
            <LeftColumnVisible>0</LeftColumnVisible>
            <PageBreakZoom>100</PageBreakZoom>
            <FreezePanes/>
            <FrozenNoSplit/>
            <SplitHorizontal>3</SplitHorizontal>
            <TopRowBottomPane>3</TopRowBottomPane>
            <ActivePane>2</ActivePane>
            <Panes>
                <Pane>
                    <Number>3</Number>
                </Pane>
                <Pane>
                    <Number>2</Number>
                    <ActiveRow>24</ActiveRow>
                    <ActiveCol>3</ActiveCol>
                    <RangeSelection>R25C4</RangeSelection>
                </Pane>
            </Panes>
            <ProtectObjects>False</ProtectObjects>
            <ProtectScenarios>False</ProtectScenarios>
        </WorksheetOptions>
    </Worksheet>

这是修改添加的地方
			<FreezePanes/>
            <FrozenNoSplit/>
            <SplitHorizontal>3</SplitHorizontal>
            <TopRowBottomPane>3</TopRowBottomPane>
            <ActivePane>2</ActivePane>
             <Panes>
                <Pane>
                    <Number>3</Number>
                </Pane>
                <Pane>
                    <Number>2</Number>
                    <ActiveRow>24</ActiveRow>
                    <ActiveCol>3</ActiveCol>
                    <RangeSelection>R25C4</RangeSelection>
                </Pane>
            </Panes>
下面是一些关键设置的解释:
ExcelXML架构中,<FreezePanes/><FrozenNoSplit/>是与窗口冻结和分割相关的设置。
<FreezePanes/>:
这个元素用于指定是否冻结工作表中的某些行或列。当冻结窗格时,指定的行或列将保持可见,即使你滚动工作表的其他部分。这通常用于在查看大量数据时保持标题行或列始终可见。
<FrozenNoSplit/>:
这个元素与冻结窗格的功能相关,但它具体控制的是当冻结窗格时,是否允许分割窗口。如果设置为True,即使冻结了窗格,用户也不能分割窗口。如果设置为False或未设置,用户可以在冻结窗格的同时分割窗口。
简而言之,<FreezePanes/>用于控制是否冻结工作表中的行或列,而<FrozenNoSplit/>用于控制当窗格被冻结时,是否允许用户分割窗口。这两个设置通常一起使用,以提供对工作表视图和窗口行为的精细控制。


<PageSetup>:
<Header/><Footer/>:这两个元素定义了工作表打印时的页眉和页脚。在这个例子中,它们都是空的,意味着没有设置页眉和页脚。
<TopRowVisible>:
设置为0,表示第一行是可见的。如果设置为1,则表示第一行是隐藏的。
<LeftColumnVisible>:
设置为0,表示第一列是可见的。如果设置为1,则表示第一列是隐藏的。
<PageBreakZoom>:
设置为100,表示页面在打印预览或页面布局视图中的缩放比例为100%,即正常大小。
<SplitHorizontal>:
设置为3,表示工作表在水平方向上被分割,分割点位于第3行。这通常用于在Excel中同时查看工作表的不同部分。
<TopRowBottomPane>:
设置为3,表示在分割窗口的底部窗格中,第一行可见的行是第3行。
<ActivePane>:
设置为2,表示当前激活的窗格是第二个窗格。在Excel中,窗格是指分割后的视图区域。
<Panes>:
定义了工作表中存在的窗格及其属性。在这个例子中,有两个窗格,第二个窗格有特定的活动行、列和范围选择。
<ProtectObjects><ProtectScenarios>:
这两个都设置为False,表示工作表中的对象和方案都没有被保护。如果设置为True,则它们会被保护,无法编辑。
总的来说,这个XML片段定义了一个Excel工作表的打印和视图设置,包括页眉和页脚、行和列的可见性、缩放比例、窗口分割和窗格设置,以及对象和方案的保护状态。

;