13.其它

13.1 如何使用数据源

一旦建立好数据源，报表就可以使用数据源来填充数据了。打开工具条上的“工具箱”()按钮，切换到“数据源”选项卡，即可看到数据源的列结构：
上图选项卡中，数据源的结构树分为“表”和“单个数据”, 可以用鼠标直接将其下级内容直接拖拽至报表中，“表” 下的 “标题行” 和 “数据行”、以及 “单个数据” ，都是有很大差别的，分别说明如下。
13.1.1 datarow( )
将 “表” 的 “数据行” 拖到报表的某个单元格，该单元格将会自动生成一个计算公式如"=datarow('ds1')", 表示以该单元格作为左上角，将数据源填充至报表。由于数据源的总行数是不确定的，所以报表将动态插入新的临时行。
“数据行"(datarow 函数)导致的整表填充，填充是规则的，规则和某些设计技巧说明如下：
规则1: 数据源的第一行直接填充到本行;
规则2: 报表动态插入的临时行，用于容纳数据源的其它行，这些动态行的行号在标尺中略显蓝色并倾斜显示, 如下图的行号，从 5 开始:
规则3: 从左向右的填充规则为：逐个单元格顺序填充，但遇到如下情况，将跨过这些单元格:
1.单元格是被组合的、不是主单元格;
2.单元格内有其它计算函数;
此外，数据源菜单中的这些功能比较有用，能定制列的左右次序、以及是否填充:
要保存设计好的模版时，建议用上图中的 "清除从数据源填入的数据" 菜单功能清除临时新增行:
13.1.2 headrow( )
将 “表” 的 “标题行” 拖到报表的某个单元格，该单元格将会自动生成一个计算公式如 "=headrow('ds1')", 表示从该单元格开始，将数据源的标题从左向右填充。
headrow( ) 通常是位于 datarow( ) 上方的一行，建议您对照一下在线演示中的例子。
headrow( ) 不是必须的，它的好处就是您不必一个个单元格去输入字段的标题文字了。
此外，headrow( ) 还有个好处：鼠标点击标题单元格可以实现即时排序。要开通这个功能，需要您在上图的 “自定义排序” 对话框的 “高级” 功能中，去指定哪些列允许点击排序.
13.1.3 data( )
将 “单个数据” 下的的某个字段拖入报表的某个单元格(或文本框), 该单元格(或文本框)将自动生成一个计算公式如 "=data('ds1', 1, 'price')", 表示仅从数据源取得单个数据，填充到本单元格(或文本框), 这个函数的参数用于指定数据源的行号和列名。
本方法通常用于有表头表体(master-detail)的报表，典型的如单据，数据库的表头就一行，明细有多行，但在报表的显示中，表头是显示在不同的行，单元格的分布可能会比较随意，所以此时必须要用 data( ) 函数了。
data( ) 的例子请参看在线演示 “47.套打(一)”、“48.套打(二)”。

13.2 报表文件

报表最终的存储格式是 XML 格式，您可以将报表保存在本地，也可以保存到服务器。
将报表保存到服务器，通常是通过 js 调用 GetFileXML 函数实现的:

    var xml = AF.func("GetFileXML", "");

上例中的这个名为"xml"的变量，其内容是一个大串，您可以通过 ajax 将它 POST 给应用服务器，让应用服务器去解决最终的存储。
至于如何在 html 页面上安排具体的上传功能，可以有如下几种方案:
方案1：在页面中增加一个“上传”的按钮，在按钮的 onClick 事件中调用 GetFileXML( ) 函数，将 XML 串 POST 给服务器;
方案2：监听工具条上的“保存”按钮： 在OnEvent( ) 事件函数中处理“Saved”事件, 在事件中执行 GetFileXML( ) 函数，将 XML 串 POST 给服务器;
报表组件从1.0.81.0版开始，支持以 zip 方式压缩的 XML 文件了，该支持包括 2 个方面：
1.Build( ) 函数能直接读入 zip 二进制文件或 http 流，例如 http://mysite/reports/abc1.zip , 当然也支持动态的 aspx/jsp 的 URL, 只要这个 URL 返回 zip 文件的纯二进制内容即可;
2.将报表 XML 压缩成 zip 文件，并以 http POST 将二进制发送给服务器, 具体的实现方法如下：
步骤1. 通过 callfunc 函数调用 104 功能号 (104功能号的说明在"4.工具条功能号"中)，生成当前报表的 zip 文件，如：

    AF.func("callfunc", "104 \r\n c:\\report3.zip");

上例中，文件名是写死的，不太符合实际开发习惯，所以建议使用全局缓存函数 CacheDirUtility，该函数能动态生成唯一的文件名，将程序修改成如下：

    var filename = AF.func("CacheDirUtility", "isCreateTempFile=true; ext=zip");
    AF.func("callfunc", "104 \r\n" + filename);

步骤2. 通过全局函数 HttpPostLocalFile 将该文件上传，如：

    AF.func("HttpPostLocalFile", "http://mysite/reports/rec.aspx?name=rpt1&org=23 \r\n" + filename);

当然，上面的这个 "rec.aspx" 接收程序得由您自己写，由您决定把 Http Body 中的二进制内容写到服务器文件或存入数据库.

13.3 异步计算

通过API函数Calc( )，或者工具条上的“计算”按钮，可以触发报表的填数计算。如果计算过程比较漫长(例如后端计算性能、数据量等因素导致)，浏览器表现为锁死, 甚至无法切换Tab选项卡，必须要等到整个计算过程结束才能恢复可操作。
硕正报表从1.0.66.0版开始，增加了异步计算功能，解决了计算过程中的浏览器僵死问题。
如果Calc( )函数的参数设为 "mode=asynch", 就表示采用异步计算模式，计算过程被安排在另一个线程中，而该函数则立即返回，当计算真正结束时，报表组件会通过“Calced”事件通知浏览器。
从启动异步计算，到发出“Calced”事件之间，你不应该调用该控件的任何API函数，否则异步将无意义。

工具条上的计算按钮()，也是采用异步计算的.

13.4 自定义打印纸

在打印设置对话框中，有自定义打印纸尺寸的选项按钮，关于自定义打印纸这个功能，有必要在此分析一下其原理。
首先，硕正报表的打印是基于Windows的常规API打印的，也就是说是基于页式打印，不可能去调用具体的打印机驱动程序做诸如逐行即打即停的功能的。
这个“页式打印”就是以打印纸为单位，每完成一个页任务，打印机就应该吐出一张打印纸来，所以，对于大多数激光、喷墨打印机而言，自定义打印纸恐怕是毫无意义的。
所以，通常所说的“自定义打印纸”是用于针式打印机的，针式打印机一般都允许自定义打印纸。
如果在自定义打印纸过程中出现如下错误：
有2种可能的原因：
原因1 - 所选的打印机本身就不支持自定义纸张；
原因2 - 操作系统没有给IE控件足够的权限，具体的解决办法是在IE的“Internet选项”的“安全”中降低安全等级，待打印纸自定义完毕，再恢复安全等级；
如果权限问题总是无法解决，那么建议您到“控制面板”- “设备和打印机”中去手动设置，事实上，用硕正控件设置自定义打印纸和手动自定义打印纸是没区别的。
一旦打印纸自定义成功，那么在“打印纸”的下拉选择框中，就会自动包含有刚才自定义的那种打印纸，这个自定义信息是登记在操作系统注册表中的，一次定义好后，今后都能用。
由于自定义打印纸是和具体的打印机相关联的，所以还建议您进一步勾选对话框中的“认定使用这台打印机”的选项，然后保存模板。
最后，我们谈谈自定义打印纸的另一个常见话题：打印模板发布后，网络上其它用户都需要逐个去设置自定义打印纸吗？
首先，我们模板中是包含了自定义打印纸的完整的自定义信息的，比如纸名、设定的尺寸。当其它用户打开该模板后，报表控件首先会检查本地是否安装了同样的打印机，如果能找到同名的打印机，那么它会进一步去检查是否已经有这种自定义打印纸了，如果没有，它会自动创建的。所以这个过程是自动的。
当然，在自动创建的过程中，也同样可能遇到权限不足的问题，遇到此问题，您只好按上面的解决办法手工去解决了。

13.5 条形码

报表中的单元格和文本框都可以作为条形码显示，操作方法为：
1.在工具箱中切换到“显示格式”选项卡;
2.将数据类型改成“字符型”;
3.在"其它显示形式" 选 "以条形码(第三方组件)显示"， 如果您需要的是二维码，可以选“以二维码(内置)显示”, 因为“内置”表示直接就有，不需要下载;
4.条形码更多的选项在 "条形码选项";

第三方组件条形码：请从硕正官网下载区底部 “其它第三方功能组件” 下载, 客户端需要手工安装这个组件的.

内置二维码： 硕正核心程序自带，直接能用，不需要安装第三方组件.

13.6 百度 ECharts 统计图

硕正插件企业版从 108 版本开始支持百度 ECharts 统计图了, 108版仅支持其中主要的几种如折线图、柱状图、饼图、仪表盘、地图(部分), 今后将逐步增加统计图类型。
硕正报表中的 ECharts 统计图的容器是 IE Web Browser, 即内置了另一个单独的 IE 控件子窗口, 统计图以 javascript 展现和渲染。
ECharts 的 javascript 库、Theme 主题库以及模板文件，都部署在服务器端的 binary 目录下。客户端浏览器在展现 ECharts 时，会自动从该目录下载的.
在报表设计过程中，当您添加 ECharts 统计图时, 会弹出一个需要访问 ECharts Example 网站的对话框, 以选择统计图类型，如图：
图中地址栏 URL 默认是指向硕正官网的某个虚拟目录的, 我们强烈建议您将 ECharts Example 网站部署到您自己的服务器上, 不要访问硕正官网, 这样一来可以不受局域网的限制, 二来不受硕正官网正常与否的限制。ECharts Example 服务 zip 包请从百度网盘下载, 下载地址是 https://pan.baidu.com/s/1BahAyewDfyj1kMRbXjKdIg ，解压后可直接部署，因为里面的文件都是静态文件，可以部署在任何类型的 Web 服务器上。
当然, 您也可以从百度 ECharts 官网下载此 zip 包, 但要注意下载后需要修改 echarts-2.2.7/doc/asset/js/echartsExample.js 文件，在文件末尾处添加二行 js:

     function getTextArea() { return editor.doc.getValue(); }
     function setTextArea(s) { editor.doc.setValue(s); }

最后，请修改服务器上的 binary/supcan.xml 文件，将 URL 改成您自己的 URL, 例如:

     ...
     <echarts>
      <echarts_server>http://192.168.1.1/echarts-2.2.7/doc/example.html</echarts_server>
     </echarts>
     ...

这样您下次打开浏览器，弹出此窗口时, 它的地址栏 URL 就变成上面的 URL 了.

服务器专版部署提醒： 需要将 binary 下的 echarts 目录，完整拷贝到 Temp 目录下.

13.7 保存数据到数据库

相对于硕正树列表(Treelist)，要将报表中的某些单元格保存到数据库，是个挺复杂的过程，因为报表格式太灵活了，需要明确行、列和数据库字段的映射关系。
有如下 5 种实现方案可供选择。
方案一.上报模式
请参见文档 “11.上报汇总模式”，在线演示中，也有好几个例子供您参考。
方案二.数据源模式
创建参数 WorkMode 专门有一个针对数据源的模式：InputDSRunTime 模式，加载数据源后，允许您对数据源内容加以修改，并整体保存。请参见在线演示例子 “37.提交修改内容”。
方案三.CollectXML 函数模式
该函数比较简单，它专门收集报表中某些特殊标记的单元格内容，比如别名、底色，辅之以函数 GetChangedCells、ResetChanged，也能实现较复杂的应用。
方案四.原始模式
报表的 GetCellData 函数可以获取单元格的数据、用 GetCellProp 函数可以获取单元格的属性(包括自定义属性)，你用 js 写循环语句去遍历所有单元格吧，方法比较笨，但也确实是一个办法。
方案五.后端解析模式
通过 GetFileXML 函数取得报表XML串，将该串抛给应用服务器，后端Java/C#解析这个XML，从中提取出数据来，这是最无奈的办法。

13.8 内嵌树列表(Treelist)

从1.0.72.0版开始，工具条中增加了树列表的图标，您可以把一个树列表直接加入到报表中。在上报模式中，树列表也能够以“指标”的方式被预先定义在指标库中。
内嵌树列表只有在运行时(RunTime)才是真正可用的。 而在设计时(DesignTime), 树列表始终处于不活动的状态，您看到的树列表只是一幅图，供您拖拽布局：
内嵌树列表 和 普通的页面中的树列表，完全是同一种东西，但是访问报表中的内嵌树列表，通常是先获得树列表的句柄，然后以扩展函数方式调用，如下例：

    //先获得树列表的句柄 (注："m010" 是树列表的别名)
    var h = AF.func("GetHandle", "m010");

    //以扩展方式执行该树列表的函数
    var xml = AF.func(h + "GetChangedXML", "level=0;isIgnoreChange=true");

此外，也可以采用另一种更简洁的写法：

    var xml = AF.func("m010.GetChangedXML", "level=0;isIgnoreChange=true");  //"m010" 是树列表的别名

通过上述的这2种扩展方式，您可以访问树列表的几乎所有函数。
在运行时(RunTime), 报表内嵌的树列表能向页面抛出 OnEvent(id, Event, p1, p2, p3, p4) 事件，但是和普通的页面树列表有一个差别：Event(事件名)前面带有树列表的别名，例如:

    function OnEvent(id, Event, p1, p2, p3, p4)
    {
     if(id=='AF') { 	//id不变
       //树列表的双击事件，其中 "m010" 是触发了事件的树列表的别名
       if(Event == "m010.DblClicked") {
        ...
       }
     }
    }

不推荐使用内嵌树列表，打印会成为问题。