ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

帆软JS开发:控件获取与单元格操作全解析

帆软JS开发:控件获取与单元格操作全解析 做了几年帆软报表开发回头看写得最多的其实不是复杂SQL也不是花哨的图表配置而是JavaScript里那几行getWidgetByName和getCellValue。参数联动要取控件值按钮点击要把结果写进单元格表单提交前要做校验甚至下载文件前还要从报表里带几个参数过去绕来绕去都绕不开“从JS里拿到某个控件”和“读取或写入某个单元格”这两件事。这篇文章就把帆软JS开发里最常用的控件与单元格操作从头到尾梳理一遍内容包括对象模型、控件获取的几种方式、单元格取值赋值的完整写法、三个可以直接抄的实战场景以及我实际调试中踩过的坑。刚接触帆软JS的人可以照着敲写过一阵子但对API还比较模糊的人也可以当一份查漏补缺的手册。1. 先搞清楚帆软JS的对象模型很多新手一上来就搜代码看到contentPane和_g()混着用越看越乱。其实这两者是同一套前端对象体系下的不同入口理解清楚之后再写基本不会跑偏。1.1 两个入口对象contentPane 与 _g()contentPane是普通报表.cpt模板在Web端渲染后的前端报表对象可以理解为整个报表页面的“引擎”。我们常用的contentPane.curLGP是当前报表处理上下文单元格的取值、赋值、定位基本都挂在它下面。所以在普通报表的按钮事件、单元格事件里经常看到这样的写法var cp contentPane.curLGP; var value cp.getCellValue(A1);_g()是帆软内置的全局方法返回当前页面的报表对象。它的覆盖范围比contentPane更广在决策报表.frm表单里能用在普通报表里也能用在参数面板控件事件、body控件事件、模板加载结束事件里都能用。决策报表里最常见的控件获取写法就是var widget _g().getWidgetByName(控件名);简单记忆单元格值操作优先用contentPane.curLGP控件获取优先用_g()配合不同入口。两个对象在大部分场景互通但语义不同混用的时候要多留个心眼。1.2 JS写在哪儿决定了 this 是谁帆软里能写JS的地方太多了参数面板控件的编辑后事件、body上按钮的点击事件、单元格控件的初始化事件、模板加载结束事件、超级链接的JavaScript事件。同一个方法在不同事件位置写this指向完全不一样这也是代码报错的一大来源。以我比较常用的几个位置为例参数面板里某个下拉框的“编辑后”事件this指向当前控件本身可以直接this.getValue()。决策报表body上按钮的“点击”事件this也指向当前按钮控件。单元格里插入的文本控件它的“编辑结束”事件中this仍然指向这个控件。模板加载结束事件this指向全局环境此时想拿控件就必须用_g()这类全局API。所以写之前先问自己一句这段代码是写在控件身上的还是写在独立事件里的事件上下文不一样第一行就该不一样。最忌讳的是把参数面板控件里this.options.form.getWidgetByName的写法原封不动搬到决策报表body控件里然后开始怀疑API有问题。2. 控件获取从参数面板到决策报表控件是帆软页面交互的核心。下拉框、复选框组、日期控件、按钮、报表块归根到底都是控件对象。问题是怎么在任意一个事件里快速、准确地拿到它们。2.1 参数面板控件_g().parameterEl.getWidgetByName()普通报表的参数面板本质是一个独立的面板容器。要拿参数面板上的控件官方且稳定的写法是var widget _g().parameterEl.getWidgetByName(控件名);举个例子参数面板上有一个下拉框控件控件名叫area想在另一个控件的编辑后事件里拿到它的选中值并做点处理var areaWidget _g().parameterEl.getWidgetByName(area); var areaValue areaWidget.getValue(); console.log(areaValue);如果是写在参数面板某个控件自己的事件里比如“编辑后”事件还有一个更省事的写法var cityWidget this.options.form.getWidgetByName(city); cityWidget.setValue();这里的this.options.form就是参数面板所在的表单对象。很多从旧版本升级上来的项目还在用contentPane.parameterEl.getWidgetByName在FR 10之后依然兼容但新项目我建议统一用_g().parameterEl更接近官方推荐路径。这里要特别提醒一个容易搞混的点控件名称和参数名称不是一回事。参数名是给SQL查询、数据集过滤用的逻辑名字控件名是页面上这个输入框的实例名称。两者经常一致容易让人误以为是一个东西。一旦你发现getWidgetByName(xxx)返回null先去看一下参数面板属性框里控件名到底填的是什么。2.2 决策报表表单里的控件_g().getWidgetByName()决策报表的控件获取比普通报表更直接页面上的所有内容都是控件直接通过_g()拿var widget _g().getWidgetByName(按钮名); var checkboxList _g().getWidgetByName(城市多选);拿到控件之后操作都是一样的取值用getValue()赋值用setValue()显隐用setVisible()可用性用setEnable()。比较特殊的是“报表块”。决策报表里放的报表块本质也是一个控件但它内部还包裹着一个独立报表要操作里面的单元格必须再进一层var reportWidget _g().getWidgetByName(report0); var cellValue reportWidget.curLGP.getCellValue(A1);默认新建的报表块通常叫report0、report1属性面板里能看到实际名字。这个习惯很重要因为我在多个项目里见过同事拿_g().getWidgetByName(report0)返回的对象直接当成普通控件去调getValue()结果自然是报错。报表块拿到手之后先想清楚你要的是“报表块对象”还是“报表块里的单元格数据”后者必须走curLGP。2.3 拿到控件之后常用的取值与操作不同控件类型getValue()返回的数据结构差别很大这是很多校验逻辑写错的关键点。我整理了一个常用对照控件类型getValue() 返回内容常见坑文本控件字符串空值时是空字符串不是null数字控件数字可能返回科学计数法字符串下拉框选中值字符串/数字未选择时返回null复选框组数组必须用indexOf或includes判断单选按钮组选中值字符串空数组和空字符串要分开处理日期控件字符串如2025-01-01格式取决于控件配置按钮无实际value主要用点击事件复选框组是我遇到坑最多的控件。它的getValue()返回的是数组比如[华东,华南]如果你直接拿去和字符串比较永远不等。正确写法var values widget.getValue(); if (values.indexOf(华东) -1) { // 包含华东 }拿控件不只是为了取值还经常要动态改状态。比如根据权限控制控件显隐_g().getWidgetByName(审核按钮).setVisible(false); _g().getWidgetByName(备注输入).setEnable(false);这种操作放在模板加载结束事件里比较合适页面所有控件都渲染完了再统一做状态控制逻辑清晰也好维护。3. 单元格获取与值操作控件管交互单元格管数据。帆软报表里最常干的活儿就是从单元格里读数据做校验、汇总或者把计算结果写回某个单元格。3.1 读取单元格值getCellValue普通报表中读取单元格核心一行var v contentPane.curLGP.getCellValue(A1);注意这里的A1是报表设计器里的单元格坐标不是Excel的A1但概念很像。对非扩展单元格而言这个写法直接返回该单元格的真实值。更常见的是读取扩展后的单元格。比如A1单元格绑定了数据列会扩展出多行getCellValue(A1)只返回扩展后的第一行值想遍历所有行要加第二个参数——行索引var cp contentPane.curLGP; var firstRowValue cp.getCellValue(A1, 0); var secondRowValue cp.getCellValue(A1, 1);这个索引从0开始不是报表里显示的第几行。很多新手拿Excel的行号逻辑去套取出来的数据永远是错位的。我实际项目里最常用的遍历写法是这样的var cp contentPane.curLGP; var i 0; while (true) { var v cp.getCellValue(A1, i); if (v null || v undefined) { break; } console.log(第 (i 1) 行值 v); i; }用while(true)一直往后取取到空值说明扩展行已经取完了主动跳出。这个方法在做列汇总、批量校验时特别顺手。还容易忽略的一点getCellValue返回的是单元格的真实值不是显示值。如果一个单元格是公式计算出来的或者设置了数据字典映射显示成“华东”“华南”这类文本但真实值是“01”“02”那getCellValue返回的是后者。想要显示值需要在设计器里对单元格内容做预处理或者单独再放一个隐藏单元格存显示文本。3.2 给单元格赋值setCellValue写单元格比读稍复杂一点因为必须明确是“给哪个报表对象”的哪个单元格赋值。普通报表里最直接的写法var cp contentPane.curLGP; cp.setCellValue(C1, 新值);也有人习惯这样写with (contentPane.curLGP) { setCellValue(C1, 新值); }两种都可以但后面这种写法在压缩混淆时偶尔会有兼容问题所以我一般不用with直接加对象前缀代码可读性也更好。setCellValue最典型的应用场景是页面上用户操作按钮后把计算结果写回报表空白单元格再触发后续操作。比如点一下“汇总”按钮把两列数据加总后写到C1var cp contentPane.curLGP; var total 0; var i 0; while (true) { var v cp.getCellValue(B1, i); if (v null || v undefined) { break; } total parseFloat(v); i; } cp.setCellValue(C1, total);设置单元格值之后单元格会立即显示新值不需要手动刷新整个报表。但要注意如果目标单元格本身绑定了数据列或者单元格里有公式赋值可能被数据层覆盖。我一般建议把这种“动态写入”的单元格放在报表末尾或单独区域设计成纯粹的空白单元格避免被引擎重新计算。3.3 单元格定位与取值组合还有一类常见需求是要根据当前操作的行动态读取该行其他列的值。比如表格每行后面加一个按钮点哪一行就读取哪一行的订单号。帆软本身没有特别直观的“当前行号”API我常用的做法是在单元格里放一个超级链接用JavaScript事件并把当前行的值通过$$$或参数传进去。$$$在帆软里表示当前单元格的值这是官方预留的变量。举个例子在订单号单元格上设置超级链接选择JavaScript事件传递参数时可以写var orderNo ${orderNo}; var current $$$;然后脚本里可以这么用var orderNo $$$; var orderName contentPane.curLGP.getCellValue(B1, 0);这里只是展示组合思路。实际项目里更稳妥的做法是先用参数把当前行主键传进JS再用主键去对应的扩展单元格里找数据或者直接再发一次Ajax请求到后端取详细数据。这样能避免“行号索引”和“扩展顺序”不一致带来的隐患。4. 实战案例三个最常见的业务联动场景光讲API比较干我把工作中高频遇到的三个场景完整写一遍代码可以直接复制到对应事件里用。4.1 参数联动下拉框A选中后清空下拉框B场景是普通报表参数面板上有两个下拉框一个“大区”一个“城市”。切换大区时城市的值要清空避免用户用上一个城市的旧值去过滤。在“大区”下拉框的“编辑后”事件里写var areaValue this.getValue(); console.log(当前选择的大区 areaValue); var cityWidget this.options.form.getWidgetByName(city); cityWidget.setValue();这里的this就是大区控件自身this.options.form拿参数面板表单对象然后定位到城市控件清空。如果还想顺手清空更多控件让整个参数面板重置一部分状态可以继续追加this.options.form.getWidgetByName(year).setValue();这段代码放在参数面板里绝对安全因为this.options.form是参数面板控件事件里的标准上下文。4.2 按钮写入把多选结果拼成字符串写进报表块单元格场景是决策报表页面上有一个复选框组“城市多选”一个报表块report0报表块里有几个空白单元格用来接收用户选择结果。点击“确认选择”按钮后把勾选的城市拼成逗号分隔的字符串写入报表块B2单元格。在按钮的“点击”事件里写var cityWidget _g().getWidgetByName(城市多选); var selected cityWidget.getValue(); // selected 是数组比如 [上海, 北京, 广州] var result selected.join(,); // 写入报表块 report0 的 B2 单元格 var report0 _g().getWidgetByName(report0); report0.curLGP.setCellValue(B2, result);这里有个细节复选框组没勾选任何项时getValue()返回的是空数组join()出来是空字符串业务上可以接受。如果希望没勾选时给个提示可以在拼接前判断长度if (selected.length 0) { FR.Msg.alert(提示, 请至少选择一个城市); return; }FR.Msg.alert是帆软封装的消息弹窗比alert()在报表页面上更统一也避免被浏览器拦截。4.3 行级汇总遍历扩展单元格做整列统计场景是普通报表里B1单元格绑定了“数量”数据列会扩展N行。页面底部有个“金额统计”按钮点击后把所有数量加起来弹窗显示。在按钮的“点击”事件里写var cp contentPane.curLGP; var total 0; var i 0; while (true) { var v cp.getCellValue(B1, i); if (v null || v undefined) { break; } var num parseFloat(v); if (!isNaN(num)) { total num; } i; } FR.Msg.alert(统计结果, 数量合计 total);如果你不确定扩展行到底有多少行用while(true)循环最省事。想要限制最大行数可以在while条件里加一个上限比如i 1000防止脏数据导致死循环。我在生产环境里习惯设一个上限纯粹是防御性写法扩展行再多也不会超过几百行。5. 帆软JS开发最常见的报错与排查手册这部分是纯经验总结。帆软JS报错和普通前端不一样很多时候控制台不直接告诉你“哪一行写错了”而是弹一个笼统的提示。我把自己遇到最多的问题整理成速查表。现象可能原因排查方向getWidgetByName 返回 null控件名拼写错误、大小写不一致打开属性面板复制控件名别手敲返回 null 且控件名正确页面还没加载完事件触发太早把代码放到模板加载结束事件里this.getValue 报错this 不是控件对象确认代码写在控件事件里而不是模板事件参数面板控件取不到值用了决策报表的 _g().getWidgetByName参数面板控件要用 _g().parameterElgetCellValue 一直返回空单元格是空白单元格或没有扩展行先在设计器里确认单元格有内容setCellValue 写了没反应目标单元格有公式或数据列换成空白单元格或先清空公式扩展单元格取值串行行索引用错记住索引从0开始且与扩展顺序一致复选框组判断失效拿数组和字符串直接比较用 indexOf 或 includes 判断排查有一个很笨但很有效的方法在写复杂逻辑之前先打一行console.log把取到的对象或值打印出来然后用浏览器F12打开控制台去看。帆软页面本身也是网页所有前端对象都能在控制台里展开检查。我经常干的事是直接打印_g()看整个报表对象树控件在哪一层、叫什么名字一目了然。还有一个容易被忽视的点控件名里的空格和特殊字符。设计器里允许控件名带空格但写到JS里getWidgetByName( myWidget )就很容易出错。规范一点控件名一律用字母开头不加空格不用中文和特殊符号。这是我在团队里定下的硬性要求。另外一个日常习惯是把常用的控件获取封装成公共方法。比如我在公共JS里放一个全局函数function getWidget(widgetName) { return _g().getWidgetByName(widgetName); }然后业务事件里统一写var cityVal getWidget(城市多选).getValue();好处有两个一是模板里少写很多重复的_g().getWidgetByName二是万一以后帆软版本升级改了API改公共JS一个地方就能全局同步。我个人的体会是帆软JS的入门门槛不在语法而在对象模型。只要搞清楚了“页面上有哪些对象、怎么定位、怎么取值”后面业务逻辑都是体力活。这篇梳理基于我常用的FR 10/11版本新版FVS在部分接口上更严格但_g()、getWidgetByName、contentPane.curLGP这套基础API依然是通用的。写的时候多用控制台验证别怕报错报错越多对对象结构的理解越深。
返回列表