Skip to content

表达式

表达式是 Luck-Report 的计算核心,除最常见的表达式类型单元格外,条件属性、图片与二维码的数据来源、链接参数等处均可书写表达式。掌握表达式是制作复杂报表的前提。

基本语法

表达式支持的数据类型如下:

类型说明示例
数字整数或小数11230.121331
字符串单引号或双引号包裹"Luck-Report"'教程'
布尔值只有 truefalsetrue
空值nullnull

算术运算符:

运算符说明示例
+数字求和;有一方是字符串时做连接21 + 31 得 52;"值:" + 331 得 "值:331"
-求差21 - 31 得 -10
*求积3 * 6 得 18
/求商,除不尽保留 10 位小数6 / 3 得 2
%求余5 % 3 得 2

比较运算符,多用于条件判断:

运算符说明示例
==等于A1 == 100
!=不等于A1 != 100
>大于A1 > 100
>=大于等于A1 >= 100
<小于A1 < 100
<=小于等于A1 <= 100
in值在集合中A1 in "1,2,3"
not in值不在集合中A1 not in "1,2,3"
like左值包含右值即为真A1 like '北京'

多个条件用 and&&)、or||)连接。

变量定义与赋值

var 定义变量并在后续表达式中使用:

var 变量名 = 表达式

规则要点:

  1. var 可省略,a = 100 同样合法;
  2. 行尾分号 ; 可省略;
  3. 变量名以字母或中文开头,可含数字、下划线、中划线,支持中文;
  4. 单元格表达式中可写多条语句,自上而下依次执行,最终输出最后一条语句的值,而非按 return 结算;赋值语句不产生返回值,所以最后一条必须是普通表达式;
  5. 赋值表达式计算结果为 null 时,不会覆盖变量的原有值。

表达式案例如下:

var a = 10
var b = a * 2
var c = a + b
c

上面例子的输出为 30。变量赋值也可写在 if、case、三元表达式的块中(见下文)。

条件判断

三种形式:三元表达式、if、case。

三元表达式

结构为"条件 ? 真值 : 假值",条件部分可用 and/or 组合多个条件。

三元表达式语法结构
示例说明
A1 > 1000 ? "正常值" : "低值"A1 大于 1000 返回 "正常值",否则返回 "低值"
A1 > 1000 and A1 < 20000 ? "正常值" : "修正值:" + (A1 + 100)条件不满足时返回拼接值,如 A1 为 2000 则返回 "修正值:2100"
var a = A1 * 2
A1 > 1000 ? "正常值:" + a : "低值"
先定义变量,再在三元表达式中引用

if 判断

由一个 if 部分、若干可选的 else if 部分和一个可选的 else 部分构成,结构类似 Java/JavaScript。return 关键字与行尾分号均可省略。

if判断语法结构
示例说明
if(A1 > 1000){return "正常值"}条件满足返回 "正常值",否则无输出
if(A1 > 1000){return "正常值"}else{"低值"}else 块中的 return 同样可省略
if(A1 > 1000 and A1 < 20000){return "正常值:" + A1}else if(A1 > 20000 and A1 < 40000){return "超高值"}else{"低值"}组合条件与多重 else if 判断

if 块中可以先定义变量再返回:

if(A1 > 1000){
    var s = ds1.sum(salary, month == 1)
    "一月合计:" + s
}

case 判断

case 用大括号包裹若干"条件 + 返回值",多组之间用逗号分隔,书写上比 if 更紧凑。return 关键字可省略。

case判断语法结构
示例说明
case{A1 == 100 return "正常值",A1 > 100 and A1 < 1000 return '偏高'}两组条件分别返回不同值
case{A1 == 100 return "正常值",A1 > 100 and A1 < 1000 '偏高'}省略 return 的写法

case 没有默认分支:case 的每个分支都必须带条件,不支持 default/else。所有条件都不满足时单元格输出为空。如需默认值,改用带 else 的 if,或在 case 末尾加一个恒真条件兜底(如 1 == 1 return "默认值")。

单元格引用

单元格多数与数据绑定,运行时一个单元格往往展开为多个,因此引用规则以当前单元格为基准。引用方式就是直接写单元格名称。

单元格引用示例模板

在 D1 中输入 A1,由于 D1 是 A1 的子格且 A1 绑定的是分组数据,每个 D1 会取到其对应分组下 A1 的值:

D1引用A1运行结果

若在 D1 中输入 B1,则取到的效果如下;输入 C1 时,则是取每个 D1 所在行的 C1 值:

D1引用B1运行结果

如果目标单元格与当前单元格不在同一行或列,情况就不同了。下面的模板中,C2 的表达式引用 B1,两者不同行也不同列,但它们拥有共同的间接父格 A1(C2 的左父格是 B2,B2 的左父格是 A1),因此 C2 会取共同父格 A1 下所有 B1 的值:

非同行同列单元格引用模板
C2引用B1运行结果

多个值的输出:表达式取到多个值时,输出以 "," 分隔。

目标格取值原则:优先取与当前单元格同行或同列的目标格;不同行列时,取与当前单元格有共同父格的目标格(有共同上父格和共同左父格时取二者交集);没有共同父格时,取展开后的所有目标格。

仍以上面的模板为例,若 C2 中输入 C1,由于 C1 是 C2 的上父格,将直接取同列上父格的值:

C2引用C1运行结果

再看一例,下面的模板中 B2 与 C1 既不同行列也无共同父格,因此 B2 会取到所有 C1 的值:

无共同父格引用模板
B2引用C1运行结果

通过更改父格控制取值:调整当前单元格的上父格或左父格,可以改变它与目标格的父子关系,从而控制取值范围,这是报表计算模型中的常用手法。

单元格坐标

对于更复杂的引用场景,可用单元格坐标精确定位展开后的某个单元格。坐标同样基于当前单元格计算,格式如下:

单元格名称 [L左父格:序号,...;T上父格:序号,...]

L 部分约束左父格,T 部分约束上父格;序号为该父格展开后的位置,负值表示相对当前单元格向上位移,正值向下。只有左父格时直接写 L 部分;只有上父格时前面加分号 ";" 再写 T 部分。大括号中的条件可选,用于对坐标取到的多个单元格做进一步过滤,多条件间用 and/or 连接。

示例说明
C1[A1:2,B1:1]先定位 A1 展开后的第 2 格,再定位其下 B1 展开后的第 1 格,取该 B1 对应的 C1
C2[A1:2,B1:2;C1:3]左父格定位到"第 2 个 A1 下的第 2 个 B1",上父格定位到 C1 展开后的第 3 格,取二者交集下的 C2
C2[A1:2,B2:2]{C2 > 1000}定位"第 2 个 A1 下第 2 个 B2"下的所有 C2,再过滤出值大于 1000 的
C2[A1:2,B2:2]{C2 > 1000 and C2 < 10000}同上,进一步限定值在 1000 到 10000 之间

具体示例,模板如下:

单元格坐标示例模板

B2 中输入 C1[A1:2,B1:1],即取"第 2 个 A1 下第 1 个 B1"对应的 C1 值,运行效果:

单元格坐标运行结果

在交叉表中,通过左父格与上父格的坐标约束可以定位到任意特定单元格。实际应用中,坐标常用于同比、环比类统计报表——用负数序号相对当前单元格位移,实现各种比值计算。

引用所有单元格:单元格名称后跟 [] 表示引用展开后的全部值,如 A1[]。还可加条件过滤,如 A1[]{@ > 1000 and @ < 10000},其中 @ 代表条件循环中当前遍历到的对象。

环比

环比示例模板

D2 的表达式为 C2 - C2[A2:-1]:前一个 C2 与 D2 同行,直接取当前行的值;C2[A2:-1] 中的负序号表示取上一行 A2 对应的 C2,二者相减即环比:

环比运行结果

第一行环比为 0,因为对首行 D2 来说上一行不存在,系统默认取了第一个 C2 的值。

同比

同比示例模板

D2 的表达式为 C2 - C2[A2:-1]{B2==$B2}:先取当前行的 C2,再通过坐标 A2:-1 定位上一条 A2 记录下的 C2;由于其下 C2 仍有多个,加条件 B2==$B2 过滤——条件中的 B2 是当前单元格所在行的 B2 值,$B2 是坐标定位后目标 C2 对应的 B2 值,两者相等即月份相同,从而实现同比:

同比运行结果

关于 $ 前缀:单元格名称前加 $ 表示取坐标定位后目标单元格所对应的单元格值,多用于条件比较,如 C2[A2:-1]{B2==$B2} 中的 $B2

第一个分组 2000 下同比值全为 0,因为该分组下不存在 A2:-1 坐标(无法上移),系统默认取了当前记录自身。若不想显示 0,可加 if 判断:位于第一组时输出空字符串,否则输出实际计算值。修改后的模板与表达式:

同比改进模板
同比改进后的D2表达式
同比改进运行结果

这里的 &A2 表示 A2 单元格展开后的序号。写成 if、三元或 case 均可。

关于 & 标记&单元格名称 取目标单元格展开后的序号,要求当前单元格必须是目标单元格的子格或间接子格(如上例中 D2 是 A2 的间接子格)。此外,序号以共同父格为基准编排——有共同父格时,以该父格下目标格的数量编号,这一特性常用于明细型主从报表的从表编号。

逐层累加

逐层累加模板
逐层累加D2表达式

&A2 为 1(首行)时取当前行 C2;否则取当前行 C2 加上一行的 D2 值,实现逐层累加(同样可换成三元或 case 判断):

逐层累加运行结果

数据集表达式

除了在设计器中双击数据集字段建立绑定,还可以直接在单元格表达式里书写数据集表达式:

数据集名称.聚合方式(字段名 [,条件] [,排序方式])

聚合方式与属性面板中的聚合类型一致:selectgroupcustomgroupregroupreselectsumavgmaxmincount

字段名必须直接书写标识符,不能用引号包裹ds1.select(username) 正确,ds1.select("username") 会在解析阶段直接报错。字段名支持中文,如 ds1.select(部门)

select(罗列数据)

datasetName.select(propertyName,filter,order)

filter 为过滤条件,多个条件用 and/or 连接;order 为排序方式,取 asc 或 desc。

示例说明
ds1.select(username)取所有 username 值
ds1.select(username,age>18)取 age>18 的 username 值
ds1.select(username,age>18,desc)取 age>18 的 username 值并倒序
ds1.select(username,age>18 and age<60,asc)组合条件过滤后正序

group(分组罗列数据)

datasetName.group(propertyName,filter,order)

参数含义与 select 相同,作用是对字段分组。字段值相同的行会合并为一组,常用于按分类、部门等维度汇总展示。

示例说明
ds1.group(degree)对 degree 字段分组,学历相同的合并为一行
ds1.group(degree,age>18)对 age>18 的 degree 分组
ds1.group(degree,age>18,asc)分组后按 degree 正序

假设数据集中 degree 字段值为:初中、初中、初中、高中、高中,则 ds1.group(degree) 输出两行:初中、高中。

sum(累加)

datasetName.sum(propertyName,filter)

目标字段必须是数字类型,否则报错。累加结果唯一,无需排序参数。

示例说明
ds1.sum(salary)salary 全量累加
ds1.sum(salary,age>30)age>30 的 salary 累加

count(计数)

datasetName.count(filter)

统计对象数量,无需字段名与排序。

示例说明
ds1.count()统计对象总数
ds1.count(age>30)统计 age>30 的对象数

max / min(极值)

datasetName.max(propertyName,filter)
datasetName.min(propertyName,filter)

结果唯一,无需排序参数。

示例说明
ds1.max(salary)salary 最大值
ds1.min(salary,age>30)age>30 的 salary 最小值

avg(平均值)

datasetName.avg(propertyName,filter)

除不尽时保留 8 位小数。

示例说明
ds1.avg(salary)salary 平均值
ds1.avg(salary,age>30)age>30 的 salary 平均值

取当前单元格值

# 表示当前单元格自身的值。典型场景是配置链接时把当前值作为参数传递:

链接参数中使用当前单元格值

# 后接 . 和属性名可取当前单元格绑定对象的属性,如 #.id 取绑定对象的 id 值。当前单元格不是数据集类型时返回 null;绑定值有多个时只取第一个对象的属性值。

Luck-Report 报表引擎