旨在为 Cognos 报表开发人员提供一种 Report Studio 结合 JavaScript 实现钻取、过滤、动态列等效果的方法参考。ReportStudio 中通过引入 js 能大大丰富报表的交互方式和增强报表的展示效果。最终效果如图 1 所示:
功能说明:
本文实例基于 IBM Cognos 10.2.1 版本编写,并测试通过。
通过多维模型能方便的实现下钻和上卷,但是在实践中发现当报表中使用维度数过多时,基于多维模型的报表响应速度将会大大降低。而使用 Cognos Cube 作为数据源的报表当报表当使用维度数太多时,不仅报表响应速度会下降而且会出现 crossjoin set 的报错。此时通过本文介绍的方法基于关系模型来制作报表既能在一张报表上实现多个维度的钻取和过滤,又能达到相对满意的报表响应速度 。
用户对报表的交互方式的需求越来越高,越来越个性化。通过本文读者可以举一反三地将 js 方法运用到 Report Studio 报表的开发中以满足用户不同的需求。
拖入查询计算项,通过 case..when 语句实现同一列,根据参数不同展示不同内容。以产品为例内容如下(其他的内容类似):
上面的表达式根据参数的不同而展示产品维度下的 3 个不同层级。(如果数据模型是多维模型请将查询项定义为“结果集”)
默认选项设置为 1
为了方便后面的 JavaScript 代码与值提示进行交互,请将这 4 个值提示的名称依次改为 DIM1、DIM2、DIM3、DIM4。
完成后,您将可以通过单选按钮控制下面的产品这一列显示的内容。
同理将其他几个维度加上去,添加完维度后可以通过单选按钮切换列表显示的内容,效果如下:
此时用户可以自定义不同维度不同层级的组合来查看数据。
为了丰富功能和更友好的交互,下面将介绍用 js 实现钻取和过滤。
原理:report studio 中 JavaScript 是不能直接和报表进行数据交互的,要通过值提示组件作为中介进行。下钻的动作就是把单选按钮组的当前选项设置为下一个,上卷则设置为上一个。过滤就是通过 JavaScript 将对应的值赋给文本提示框。
<styletype="text/css"> .cMenu{ BORDER-RIGHT:#0000001pxsolid; BORDER-TOP:#0000001pxsolid; FONT-WEIGHT:normal; FONT-SIZE:14px; VISIBILITY:hidden; BORDER-LEFT:#0000001pxsolid; WIDTH:150px; CURSOR:default; COLOR:#000000; LINE-HEIGHT:20px; BORDER-BOTTOM:#0000001pxsolid; FONT-FAMILY:Verdana,Arial,Helvetica,sans-serif; POSITION:absolute; BACKGROUND-COLOR:#eeeeff } .menuitems{ PADDING-RIGHT:5px; PADDING-LEFT:5px } </style> <divid='contextMenu'class="cMenu"style="position:absolute;left:100px;top:100px"> <DIVclass=menuitemsonclick="UnflodDim();"onmouseover="highLightMenu()"onmouseout="LowMenu()">展开维度</DIV> <DIVclass=menuitemsonclick="FilterData();"onmouseover="highLightMenu()"onmouseout="LowMenu()">只看选中的数据</DIV> <DIVclass=menuitemsonclick="FlodDim();"onmouseover="highLightMenu()"onmouseout="LowMenu()">返回并取消过滤</DIV> <HRcolor=#aaaaaasize="0"> <DIVclass=menuitemsonclick="hideContexMenu();"onmouseover="highLightMenu()"onmouseout="LowMenu()">关闭菜单</DIV> </div> <scriptlanguage="javascript"> //自定义右键菜单对象 varcontexMenu=document.getElementById('contextMenu'); //获取 COGNOS 对象后缀 functiongetPostFix() { varpostFix; postFix=document.getElementById('cv.id'); returnpostFix; } //隐藏 COGNOS 右键菜单 functionhideContexMenu() { contexMenu.style.visibility="hidden"; } //显示 cognos 右键菜单 functionshowContexMenu() { contexMenu.style.visibility="visible"; //确定菜单显示位置 contexMenu.style.left=event.x; contexMenu.style.top=event.y; returnclearCognosMenu(); } //强制清除自带右键菜单 functionclearCognosMenu() { varpostFix=getPostFix(); returneval("oCV"+postFix.value+".bCanUseCognosViewerContextMenu=false"); } //高亮显示菜单 functionhighLightMenu() { varmenuItem=event.srcElement; if(menuItem.className="menuitems") { menuItem.style.backgroundColor="highlight"; menuItem.style.color="white"; } } //不高亮显示 functionLowMenu() { varmenuItem=event.srcElement; if(menuItem.className="menuitems") { menuItem.style.backgroundColor=""; menuItem.style.color="black"; } } document.body.onload=clearCognosMenu; </script>
给列表中的内容添加点击事件,在解锁的前提下向维度单元格中拖入 4 个 html 项目。其中第二个 html 项目定义改为数据项。
四个 html 项目内容分别为:
1 <uonclick="ShowPan(' 2 [查询 1].[产品]+'#DIM1' 3 ')"> 4 </u>
同理将所有的维度都加上上面的四个 html。
<script> varrealValues; varflag; //获取 cognos 中的值提示对象 varform=getFormWarpRequest(); functionShowPan(cellValue){ //截取最后 4 位以确定点击的是哪个维度 flag=cellValue.slice(-4); realValues=cellValue.slice(0,-5); showContexMenu(); } //-------------------展开所选择的维度--------------- functionUnflodDim(){ vardim1A; if(flag=='DIM1'){ dim1A=form._oLstChoicesDIM1; }elseif(flag=='DIM2') { dim1A=form._oLstChoicesDIM2; }elseif(flag=='DIM3') { dim1A=form._oLstChoicesDIM3; }elseif(flag=='DIM4'){ dim1A=form._oLstChoicesDIM4; } for(i=0;i<=dim1A.length;i++){ if(dim1A[i].selected){ if(i==dim1A.length-1){ alert('该层已经是最小层级,不能被展开'); break; }else{ dim1A[i+1].checked=true; promptButtonFinish(); break; } } } } //-----------返回上一级并取消该维度上的过滤条件------------ functionFlodDim(){ //接收按钮组和文本提示的两个对象 vardim1A1; vartextpar; //清空过滤条件 if(flag=='DIM1'){ //form._oLstChoices 后面是单选按钮组的名称 dim1A1=form._oLstChoicesDIM1; for(i=0;i<dim1A1.length;i++){ if(dim1A1[i].selected){ if((i-1)==1){ //form._textEditBox 后面是文本提示框的名称 textpar=form._textEditBoxPCPXL; textpar.value=''; break; }elseif((i-1)==2){ textpar=form._textEditBoxPCPLX; textpar.value=''; break; }elseif((i-1)==3){ textpar=form._textEditBoxPCP; textpar.value=''; break; } } } }elseif(flag=='DIM2') { dim1A1=form._oLstChoicesDIM2; textpar=form._textEditBoxPDGFFLX; textpar.value=''; }elseif(flag=='DIM3') { dim1A1=form._oLstChoicesDIM3; for(i=0;i<dim1A1.length;i++){ if(dim1A1[i].selected){ if((i-1)==1){ textpar=form._textEditBoxPLSSQY; textpar.value=''; break; }elseif((i-1)==2){ textpar=form._textEditBoxPLSSGJ; textpar.value=''; break; }elseif((i-1)==3){ textpar=form._textEditBoxPLSS; textpar.value=''; break; } } } }elseif(flag=='DIM4') { dim1A1=form._oLstChoicesDIM4; for(i=0;i<dim1A1.length;i++){ if(dim1A1[i].selected){ if((i-1)==1){ textpar=form._textEditBoxPYEAR; textpar.value=''; break; }elseif((i-1)==2){ textpar=form._textEditBoxPQUAR; textpar.value=''; break; }elseif((i-1)==3){ textpar=form._textEditBoxPMONTH; textpar.value=''; break; } } } } //返回上一级 for(i=0;i<dim1A1.length;i++){ if(dim1A1[i].selected){ if(i==0){ alert('根节点不能向上返回'); break; }else{ dim1A1[i-1].checked=true; promptButtonFinish(); break; } } } } //-----------------过滤数据------------ functionFilterData(){ vardim1A; varPcp; if(flag=='DIM1'){ dim1A=form._oLstChoicesDIM1; for(i=0;i<dim1A.length;i++){ if(dim1A[i].selected){ if(i==1){ Pcp=form._textEditBoxPCPXL; dim1A[i+1].checked=true; }elseif(i==2){ Pcp=form._textEditBoxPCPLX; dim1A[i+1].checked=true; }elseif(i==3){ Pcp=form._textEditBoxPCP; }elseif(i==0){ alert('根节点不能作为过滤条件'); } } } Pcp.value=realValues; }elseif(flag=='DIM2') { dim1A=form._oLstChoicesDIM2; Pcp=form._textEditBoxPDGFFLX; Pcp.value=realValues; }elseif(flag=='DIM3') { dim1A=form._oLstChoicesDIM3; for(i=0;i<dim1A.length;i++){ if(dim1A[i].selected){ if(i==1){ Pcp=form._textEditBoxPLSSQY; dim1A[i+1].checked=true; }elseif(i==2){ Pcp=form._textEditBoxPLSSGJ; dim1A[i+1].checked=true; }elseif(i==3){ Pcp=form._textEditBoxPLSS; }elseif(i==0){ alert('根节点不能作为过滤条件'); } } } Pcp.value=realValues; }elseif(flag=='DIM4') { dim1A=form._oLstChoicesDIM4; for(i=0;i<dim1A.length;i++){ if(dim1A[i].selected){ if(i==1){ Pcp=form._textEditBoxPYEAR; dim1A[i+1].checked=true; }elseif(i==2){ Pcp=form._textEditBoxPQUAR; dim1A[i+1].checked=true; }elseif(i==3){ Pcp=form._textEditBoxPMONTH; }elseif(i==0){ alert('根节点不能作为过滤条件'); } } } Pcp.value=realValues; } promptButtonFinish(); } </script>
经过以上步骤钻取过滤功能已经实现。
为了用户交互界面更加友好,这里制作一个动态列的效果,实现根据用户自定义要显示哪些列。
在报表页面,选中列后进行以下操作
对每一列进行类似操作,完成之后您将可以控制列的显示和隐藏。
1 <label><inputid=1type="checkbox"checked="checked"value=0onclick="ChangeIt(this)"/>产品</label> <label><inputid=2type="checkbox"checked="checked"value=1onclick="ChangeIt(this)"/>订购方法</label> <label><inputid=3type="checkbox"checked="checked"value=2onclick="ChangeIt(this)"/>零售商</label> <label><inputid=4type="checkbox"checked="checked"value=3onclick="ChangeIt(this)"/>时段</label> <label><inputid=5type="checkbox"checked="checked"value=4onclick="ChangeIt(this)"/>数量</label> <label><inputid=6type="checkbox"checked="checked"value=5onclick="ChangeIt(this)"/>收入</label> <label><inputid=7type="checkbox"checked="checked"value=6onclick="ChangeIt(this)"/>毛利润</label> 2 <divstyle="display:none"> 3 <script> varform=getFormWarpRequest(); vardim1=form._oLstChoicesAAA; if(!dim1[0].selected){ document.getElementById("1").checked=false; } if(!dim1[1].selected){ document.getElementById("2").checked=false; } if(!dim1[2].selected){ document.getElementById("3").checked=false; } if(!dim1[3].selected){ document.getElementById("4").checked=false; } if(!dim1[4].selected){ document.getElementById("5").checked=false; } if(!dim1[5].selected){ document.getElementById("6").checked=false; } if(!dim1[6].selected){ document.getElementById("7").checked=false; } functionChangeIt(obj){ //如果要选中 if(obj.checked){ varn=obj.value; dim1[n].checked=true; promptButtonFinish(); }else{ //如果要取消选中 varn=obj.value; dim1[n].checked=false; promptButtonFinish(); } } </script> </div> <br> <br>
至此开发完成。读者也可以下载附件中本报表对应的 xml 文件,通过 Cognos 自带的示例包进行还原,能看到更具体的实现步骤。
随着客户对交互性要求的提高,单纯通过 report studio 的组件难以满足客户需求,运用本文介绍的引入 JavaScript 方法举一反三地将大大提高报表开发灵活性和丰富报表的交互方式。对于任何技术在实施之前,都要在测试环境测试验证,以确保技术可靠性。
描述 | 名字 | 大小 |
---|---|---|
Javascript钻取过滤demo | jsfilterdemo.zip | 5k |