生态产品多个设计器现已发布候选版本

查看生态总览
跳转到内容

报表结构 ​

一份报表定义 ReportDocument 由三部分组成:数据集描述数据长什么样,报表参数让看报表的人筛选,主体决定画成什么。它是纯 JSON,可以原样存进数据库、从接口取回,或在设计器里编辑。全部字段见报表定义类型。

字段内容
schemaVersion报表定义的版本,写 REPORT_SCHEMA_VERSION
id、title、description报表的身份、标题与说明
datasets数据集:字段与数据集参数,不含数据
parameters报表参数:查看器在报表上方排出参数栏
body主体:明细表、分组报表、交叉表或看板

数据集 ​

数据集只描述字段,数据由取数回调按请求交回。字段的 type 取 string、number、boolean、date、datetime 之一,决定能用哪些聚合与运算符、怎么显示;title 是表头与设计器里显示的名字。

计算字段 ​

字段写了 formula 就是计算字段:取回数据后,在每一行上按公式算出它的值,可以引用同一行的其它字段,也可以用 $参数名 引用报表参数。下面的单价由金额除以数量得到:

ts
const sales = {
  ...salesDataset,
  fields: [
    ...salesDataset.fields,
    { name: 'unitPrice', type: 'number', title: '单价', formula: 'amount / quantity' },
  ],
} as const

公式是受限表达式,不执行任何代码,写法见 XiHan.UI 的表达式文法。用到公式时要给查看器(和设计器)一个表达式引擎:

ts
import { createExpressionEngine } from '@xihan-ui/expression'

const viewer = document.querySelector('xh-report-viewer')!
viewer.expressionEngine = createExpressionEngine()

计算字段只在浏览器里算。筛选引用了计算字段时,取数请求里不带筛选,由 core 在取回的明细行上筛。

数据集参数 ​

数据集还可以声明自己的参数 parameters,表示取数时要由宿主带进查询的值,例如只查某个门店。视图或卡片用 parameters 把它们绑定到字面值或报表参数上,取数请求的 parameters 里就是解析好的取值,宿主据此查数:

ts
export const stores: DatasetSchema = {
  id: 'sales',
  fields: [
    { name: 'orderId', type: 'string' },
    { name: 'amount', type: 'number' },
  ],
  parameters: [{ name: 'store', type: 'string', required: true }],
}

export const byStore: TableView = {
  type: 'table',
  dataset: 'sales',
  parameters: { store: { parameter: 'store' } },
  columns: [
    { id: 'orderId', field: 'orderId' },
    { id: 'amount', field: 'amount' },
  ],
}

报表参数 ​

报表参数由看报表的人填写,查看器在主体上方按参数类型排出参数栏:

参数控件
有 options下拉选择,multiple 时可多选
boolean开关
date、datetime日期选择,datetime 精确到秒
multiple 且没有 options标签输入
number、string数字框、文本框;失焦或回车时才提交,不按每次击键重算
ts
parameters: [
  {
    name: 'region',
    type: 'string',
    title: '区域',
    options: [{ value: '华东' }, { value: '华北' }, { value: '华南' }],
  },
  { name: 'since', type: 'date', title: '起始日期', defaultValue: '2026-04-01' },
],
  • options 是固定的候选项,每项 { value, label?, disabled? }:value 的类型要与参数一致,date 写 YYYY-MM-DD,datetime 写不带时区的 YYYY-MM-DDTHH:mm[:ss],同一个参数里不能重复;label 不写按取值显示;写了 disabled 的照样列出,但不能选。
  • defaultValue 是没给值时的取值;required 的参数没有值时查看器不取数,显示「报表参数缺少或取值不合格」。
  • 筛选条件的操作数写 { parameter: '名字' } 就引用参数;参数没有值时这个条件不参与筛选,所以上面的「区域」不选就是全部区域。
  • 公式里用 $名字 引用参数。
  • 参数栏改了取值,查看器派发 parameters-change 并重新取数。宿主要自己管参数时写 parameters 属性,这时查看器只派发事件、不自己改,宿主把新取值写回去;hide-parameters 不显示参数栏。

主体 ​

明细表 table ​

逐行列出数据。每一列 columns 对应数据集的一个字段,可以写标题、值格式、列宽、对齐与冻结(sticky 固定在行首侧或行尾侧)。summary 写聚合方式或公式,在末尾出合计行。sort 是明细的排序,limit 是行数上限;行数多时表格自动改为按需渲染可见的行。

分组报表 group ​

按 groups 由外到内逐级分组,组内列出明细,组尾写小计,末尾写总计:

ts
body: {
  type: 'group',
  dataset: 'sales',
  groups: [
    { id: 'region', field: 'region', title: '区域', sort: { by: 'amount', direction: 'desc' } },
    { id: 'category', field: 'category', title: '品类' },
  ],
  columns: [
    { id: 'orderId', field: 'orderId', width: 150 },
    { id: 'date', field: 'date', width: 120 },
    { id: 'channel', field: 'channel' },
    { id: 'unitPrice', field: 'unitPrice', align: 'end', format: money },
    { id: 'quantity', field: 'quantity', align: 'end', summary: { aggregate: 'sum' } },
    { id: 'amount', field: 'amount', align: 'end', format: money, summary: { aggregate: 'sum' } },
  ],
  filter: {
    logic: 'and',
    conditions: [
      { id: 'region', field: 'region', operator: 'eq', value: { parameter: 'region' } },
      { id: 'since', field: 'date', operator: 'gte', value: { parameter: 'since' } },
    ],
  },
  sort: [{ field: 'date' }],
},
  • 每级分组可以写日期粒度 bucket(按年、季度、月、周、日、小时取键),header 与 subtotal 控制组头与组小计,缺省都显示。
  • 组的次序缺省按键升序;sort.by 写一个带汇总的列 id 时按该列的组小计排,上面按金额从高到低排区域。
  • detail: false 只留组头与小计,grandTotal: false 不写总计。

交叉表与看板 ​

交叉表 crosstab 按行、列两组维度透视出度量,见透视与交叉表;看板 dashboard 把图表、指标与表格卡片排在网格上,见看板与卡片。扩展还可以登记新的主体视图种类。

筛选 ​

视图、卡片的查询与看板联动都用同一种筛选树 FilterGroup:logic 取 and(全部满足)或 or(任一满足),conditions 里是条件,也可以嵌套带 id 的子组。一个条件写字段、运算符与操作数:

ts
export const filter: FilterGroup = {
  logic: 'and',
  conditions: [
    { id: 'channel', field: 'channel', operator: 'in', value: ['线上', '门店'] },
    { id: 'big', field: 'amount', operator: 'gte', value: 1000 },
    { id: 'month', field: 'date', bucket: 'month', operator: 'eq', value: '2026-03' },
  ],
}
  • 运算符见 FilterOperator:between 要两个值,in、notIn 要一组值,isEmpty、isNotEmpty 不要操作数;字符串运算符可以写 ignoreCase。
  • 写了 bucket 的条件先把字段值按日期粒度取键再比较,操作数直接写键。
  • 否定运算符(neq、notIn、notContains)匹配缺失值;操作数为空的条件不参与筛选,组里的条件都不参与时整组不筛选。

值格式 ​

列、维度、度量与坐标轴的 format 用 XiHan.UI 的值格式说明,取 number、percent、date、rmbUpper 几种,十进制精确舍入,写法见格式化:

ts
export const money: FormatSpec = { kind: 'number', decimals: 2, grouping: true }
export const share: FormatSpec = { kind: 'percent', decimals: 1 }
export const day: FormatSpec = { kind: 'date', pattern: 'yyyy年M月d日' }

没写格式时,数值按界面语言加千分位,日期写成 YYYY-MM-DD,日期时间写到秒。要换掉全部值的显示方式,给查看器一个 formatter。

Released under The MIT License