Cognos Analytics 11.1.7IF9报表单值复制功能实现遇阻求助
在网上搜索“Cognos report copy”会得到很多关于将报表规范复制到剪贴板的结果,但这并非本次问题的关注点。
使用Cognos Analytics的交互式查看器时,用户无法选择报表中的文本并复制到其他应用。有用户提出需求,希望能将报表输出中的单个值复制到剪贴板。
由于报表具备一些交互功能,优先选择将报表生成为HTML输出格式,生成PDF会增加额外工作量。
我可以编写一个包含脚本的网页,实现点击指定元素将其内容复制到剪贴板的功能:
<!DOCTYPE html> <html> <head> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> <script> $(document).ready(function(){ var arr = ["city", "country"]; arr.forEach(function(e){ $("span[lid='" + e + "']").click(function() { navigator.clipboard.writeText($(this).html()); }); }); }); </script> </head> <body> <span lid="city">Click me to copy my City contents to the clipboard.</span><br /><br /> <span lid="country">Click me to copy my Country contents to the clipboard.</span><br /><br /> <span lid="region">Region contents are not copied</span><br /><br /> <span>I also do nothing</span><br /><br /> </body> </html>
但在Cognos中尝试此方法并未成功。我创建了一个示例报表:
<report xmlns="http://developer.cognos.com/schemas/report/15.5/" useStyleVersion="11.6" expressionLocale="en-us"> <drillBehavior/> <layouts> <layout> <reportPages> <page name="Page1"> <style> <defaultStyles> <defaultStyle refStyle="pg"/> </defaultStyles> </style> <pageBody> <style> <defaultStyles> <defaultStyle refStyle="pb"/> </defaultStyles> </style> <contents> <list horizontalPagination="true" name="List1" refQuery="Query1" rowsPerPage="9999"> <noDataHandler> <contents> <block> <contents> <textItem> <dataSource> <staticValue>No Data Available</staticValue> </dataSource> </textItem> </contents> <style> <CSS value="padding:16px;"/> </style> </block> </contents> </noDataHandler> <style> <CSS value="border-collapse:collapse"/> <defaultStyles> <defaultStyle refStyle="ls"/> </defaultStyles> </style> <listColumns> <listColumn> <listColumnTitle> <style> <defaultStyles> <defaultStyle refStyle="lt"/> </defaultStyles> </style> <contents> <textItem> <dataSource> <dataItemLabel refDataItem="City"/> </dataSource> </textItem> </contents> </listColumnTitle> <listColumnBody> <style> <defaultStyles> <defaultStyle refStyle="lc"/> </defaultStyles> </style> <contents> <textItem name="city"> <dataSource> <dataItemValue refDataItem="City"/> </dataSource> </textItem> </contents> </listColumnBody> </listColumn> <listColumn> <listColumnTitle> <style> <defaultStyles> <defaultStyle refStyle="lt"/> </defaultStyles> </style> <contents> <textItem> <dataSource> <dataItemLabel refDataItem="Year"/> </dataSource> </textItem> </contents> </listColumnTitle> <listColumnBody> <style> <defaultStyles> <defaultStyle refStyle="lc"/> </defaultStyles> </style> <contents> <textItem> <dataSource> <dataItemValue refDataItem="Year"/> </dataSource> </textItem> </contents> </listColumnBody> </listColumn> <listColumn> <listColumnTitle> <style> <defaultStyles> <defaultStyle refStyle="lt"/> </defaultStyles> </style> <contents> <textItem> <dataSource> <dataItemLabel refDataItem="Revenue"/> </dataSource> </textItem> </contents> </listColumnTitle> <listColumnBody> <style> <defaultStyles> <defaultStyle refStyle="lm"/> </defaultStyles> </style> <contents> <textItem> <dataSource> <dataItemValue refDataItem="Revenue"/> </dataSource> </textItem> </contents> </listColumnBody> </listColumn> </listColumns> <sortList> <sortItem refDataItem="City"/> <sortItem refDataItem="Year"/> </sortList> </list> <customControl path="/CognosScripts/Copy.js"> <configuration>{ "SpansToCopy": [ "city" ] }</configuration> </customControl> </contents> </pageBody> <XMLAttributes> <XMLAttribute output="no" name="RS_legacyDrillDown" value="0"/> </XMLAttributes> </page> </reportPages> </layout> </layouts> <XMLAttributes> <XMLAttribute output="no" name="RS_CreateExtendedDataItems" value="true"/> <XMLAttribute output="no" name="listSeparator" value=","/> <XMLAttribute output="no" name="decimalSeparator" value="."/> <XMLAttribute output="no" name="RS_modelModificationTime" value="2015-11-25T21:38:24.820Z"/> </XMLAttributes> <queries> <query name="Query1"> <source> <model/> </source> <selection> <dataItem aggregate="none" rollupAggregate="none" name="City"> <expression>[Sales (query)].[Branch].[City]</expression> <XMLAttributes> <XMLAttribute output="no" name="RS_dataType" value="3"/> <XMLAttribute output="no" name="RS_dataUsage" value="0"/> </XMLAttributes> </dataItem> <dataItem aggregate="total" name="Revenue"> <expression>[Sales (query)].[Sales].[Revenue]</expression> <XMLAttributes> <XMLAttribute output="no" name="RS_dataType" value="2"/> <XMLAttribute output="no" name="RS_dataUsage" value="2"/> </XMLAttributes> </dataItem> <dataItem aggregate="none" rollupAggregate="none" name="Year"> <expression>[Sales (query)].[Time].[Year]</expression> <XMLAttributes> <XMLAttribute output="no" name="RS_dataType" value="1"/> <XMLAttribute output="no" name="RS_dataUsage" value="0"/> </XMLAttributes> </dataItem> </selection> </query> </queries> <modelPath>/content/folder[@name=&apos;Samples&apos;]/folder[@name=&apos;Models&apos;]/package[@name=&apos;GO sales (query)&apos;]/model[@name=&apos;model&apos;]</modelPath> <reportName>copy</reportName> </report>
...并添加了一个自定义控件:
define( ["jquery"], function() { "use strict"; var log = function (label, message) { console.log(" **** Copy : " + label + " : " + message); }; function Copy() {}; Copy.prototype.initialize = function( oControlHost, fnDoneInitializing ) { log("Copy", "Control.initialize" ); /* Sample Configuration: { "SpansToCopy": ["city", "country"] } If I name a text item in the report, the resulting span is given an attribute named "lid". */ this.controlHost = oControlHost; this.oConfig = this.controlHost.configuration; $(document).ready(function(){ this.oConfig.SpansToCopy.forEach(function(e){ $("span[lid='" + e + "']").click(function() { navigator.clipboard.writeText($(this).html()); }); }); }); fnDoneInitializing(); }; Copy.prototype.draw = function( oControlHost ) {}; return Copy; });
运行时出现以下错误:
jQuery.Deferred exception: e is not a function TypeError: e is not a function
该错误在$(document).ready函数运行时触发。
问题解答
1. 核心错误:作用域问题
在$(document).ready的回调函数中,this指向的是document对象,而非Copy控件实例,导致this.oConfig为undefined。调用undefined.SpansToCopy.forEach时自然抛出错误(本质是对undefined调用forEach方法)。
修复方法:提前保存当前实例的引用
Copy.prototype.initialize = function( oControlHost, fnDoneInitializing ) { log("Copy", "Control.initialize" ); this.controlHost = oControlHost; this.oConfig = this.controlHost.configuration; var self = this; // 保存当前实例引用 $(document).ready(function(){ self.oConfig.SpansToCopy.forEach(function(e){ $("span[lid='" + e + "']").click(function() { navigator.clipboard.writeText($(this).html()); }); }); }); fnDoneInitializing(); };
若Cognos环境支持ES6,也可使用箭头函数(继承外部作用域的this):
$(document).ready(() => { this.oConfig.SpansToCopy.forEach(e => { $("span[lid='" + e + "']").click(() => { navigator.clipboard.writeText($(this).html()); }); }); });
2. jQuery版本兼容性说明
Cognos Analytics 11.1.7内置的jQuery通常为1.x或2.x,你测试用的是3.6.0,但本次错误并非版本兼容导致。不过需注意:
- 内置jQuery可能不支持箭头函数,优先用保存
self变量的方式。 navigator.clipboard是HTML5 API,部分旧浏览器不支持,可改用传统复制方法:
$("span[lid='" + e + "']").click(function() { var $temp = $("<textarea>"); $("body").append($temp); $temp.val($(this).text()).select(); document.execCommand("copy"); $temp.remove(); });
3. 其他实现复制需求的方案
除修复自定义控件,还有以下可行方法:
- 添加复制按钮:在每个可复制字段旁添加自定义按钮,点击触发复制逻辑,避免和报表原有交互冲突,更直观。
- 启用文本选择:调整报表HTML输出的样式,移除
user-select:none这类禁止文本选择的CSS规则,让用户直接选中文本复制。 - 导出到Excel/CSV:提供导出选项,用户导出数据后再复制所需值,适合批量复制场景。
- Cognos SDK定制:通过SDK编写服务器端逻辑,实现点击复制指定字段值,不过开发成本较高。
内容的提问来源于stack exchange,提问作者dougp

