使用 XSL 和 CSS 实现分页的 HTML 报告






3.96/5 (12投票s)
构建具有动态页数 HTML 报表的一种可能方法。
报告预览,版本 A
报告预览,版本 B
引言
本文描述了一种构建具有动态页数的 HTML 报告的可能方法。我在开发 Web 应用程序上的 XSL 报告时,使用这种技术解决了分页问题。
背景
布局设计用于在 A4 纸张大小上打印。存储在 XML 文件中的数据作为输入传递给 XSL 转换。
代码
我们可以分析同一发票报告的两种情况,其中页数取决于订单数量。第一种情况是包含文档页眉(收件人地址)在所有页面的报告。在第二种情况下,文档页眉仅在第一页上。这两种情况之间的主要区别是详细部分(在本示例中为 OrderRows
)的大小计算。
为了获得报告,我编写了如下代码
ReportHeader
- XSL 变量,包含图像徽标和文档的Title
DocumentRecipient
- XSL 变量,包含有关客户的信息OrderHeader
- XSL 变量,包含有关订单的信息OrderRowsHeader
- XSL 变量,包含有关订购产品的标题表OrderRows
- XSL for-each,用于订单行数据。报告版本 A 每页正好 40 行,报告版本 B 在第一页上 40 行,在所有后续页面上 46 行。Filler
- XSL 模板,包含达到页面大小所需的空白行ReportFooter
- XSL 变量,包含报告的页脚(公司地址)OrderTotals
- XSL 变量,包含发票的总金额(仅在最后一页上打印)
CSS 分页命令
<style type="text/css">
.pagebreak {page-break-after: always;}
</style>
关注点
分页的使用方法:如何计算行数以及确切地放置分页命令。
报告版本 A 的情况
<xsl:for-each select="Order/OrderRows/OrderRow">
<table class="tabledetails" cellspacing="0" style="table-layout:fixed">
<tr>
<td class="tdmargin" />
<td style="width:70px" align="right" class="blueline">
<xsl:value-of select="ProductID" />
<xsl:value-of select="translate(' ', ' ', ' ')"/>
</td>
<td class="blueline" style="width:220px" >
<xsl:value-of select="ProductName" />
<xsl:value-of select="translate(' ', ' ', ' ')"/>
</td>
<td style="width:50px" align="right" class="blueline">
<xsl:value-of select="Quantity" />
<xsl:value-of select="translate(' ', ' ', ' ')"/>
</td>
<td style="width:50px" align="right" class="blueline">
<xsl:value-of select="concat('$ ', UnitPrice)" />
<xsl:value-of select="translate(' ', ' ', ' ')"/>
</td>
<td style="width:50px" align="right" class="blueline">
<xsl:value-of select="concat(Discount, ' %')" />
<xsl:value-of select="translate(' ', ' ', ' ')"/>
</td>
<td style="width:100px" align="right" class="blueline">
<xsl:value-of select="concat('$ ', ExtendedPrice)" />
<xsl:value-of select="translate(' ', ' ', ' ')"/>
</td>
<td class="tdmargin" />
</tr>
</table>
<xsl:if test="(position() mod 40) = 0 ">
<!--40 rows per page-->
<xsl:call-template name="Filler">
<xsl:with-param name="fillercount" select="1" />
</xsl:call-template>
<xsl:copy-of select="$ReportFooter" />
<br class="pagebreak" />
<xsl:copy-of select="$ReportHeader" />
<xsl:call-template name="Filler">
<xsl:with-param name="fillercount" select="1" />
</xsl:call-template>
<xsl:copy-of select="$OrderRecipient"/>
<xsl:call-template name="Filler">
<xsl:with-param name="fillercount" select="1" />
</xsl:call-template>
<xsl:copy-of select="$OrderHeader"/>
<xsl:call-template name="Filler">
<xsl:with-param name="fillercount" select="1" />
</xsl:call-template>
<xsl:copy-of select="$OrderRowsHeader"/>
</xsl:if>
</xsl:for-each>
报告版本 B 的情况
<xsl:for-each select="Order/OrderRows/OrderRow">
<!-- ... -->
<xsl:choose>
<!-- case of only one page-->
<xsl:when test="(position() mod 40 = 0 and position() = 40)">
<!--40 rows per page-->
<xsl:call-template name="Filler">
<xsl:with-param name="fillercount" select="1" />
</xsl:call-template>
<xsl:copy-of select="$ReportFooter" />
<br class="pagebreak" />
<xsl:copy-of select="$ReportHeader" />
<xsl:call-template name="Filler">
<xsl:with-param name="fillercount" select="2" />
</xsl:call-template>
<xsl:copy-of select="$OrderRowsHeader"/>
</xsl:when>
<!-- case of more than one page-->
<xsl:otherwise>
<xsl:if test="(40 - position() mod 46) = 0 and
position() > (40 + position() mod 40)">
<!--46 rows per page-->
<xsl:call-template name="Filler">
<xsl:with-param name="fillercount" select="1" />
</xsl:call-template>
<xsl:copy-of select="$ReportFooter" />
<br class="pagebreak" />
<xsl:copy-of select="$ReportHeader" />
<xsl:call-template name="Filler">
<xsl:with-param name="fillercount" select="2" />
</xsl:call-template>
<xsl:copy-of select="$OrderRowsHeader"/>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
填充模板
<!-- Template Filler-->
<xsl:template name="Filler">
<xsl:param name="fillercount" select="1"/>
<xsl:if test="$fillercount > 0">
<table class="tabledetails">
<tr>
<td>
<xsl:value-of select="translate(' ', ' ', ' ')"/>
</td>
</tr>
</table>
<xsl:call-template name="Filler">
<xsl:with-param name="fillercount" select="$fillercount - 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
填充模板的简单用法:添加空白行
<!-- Add two blank row -->
<xsl:call-template name="Filler">
<xsl:with-param name="fillercount" select="2" />
</xsl:call-template>
填充模板的使用方法:如何计算最后一页上的空白行数
<xsl:choose>
<!-- case of only one page-->
<xsl:when test="count(Order/OrderRows/OrderRow) <= 40">
<xsl:call-template name="Filler">
<xsl:with-param name="fillercount"
select="40 - (count(Order/OrderRows/OrderRow))"/>
</xsl:call-template>
</xsl:when>
<!-- case of more than one page-->
<xsl:otherwise>
<xsl:call-template name="Filler">
<!--(Rows per page = 40) - (Rows in current page) -
(Total Order rows = 3 ) - (Filler Row = 1)-->
<xsl:with-param name="fillercount" select="40 -
( ( count(Order/OrderRows/OrderRow)-40 ) mod 40 ) - 3 - 1"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>