news 2026/10/8 14:23:35

Evidence 交互式销售分析仪表板实战:用 SQL + Markdown 构建可过滤的数据可视化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Evidence 交互式销售分析仪表板实战:用 SQL + Markdown 构建可过滤的数据可视化
  • 数据分析
  • 数据可视化
  • 前端

【免费下载链接】evidence

Business intelligence as code: build fast, interactive data visualizations in SQL and markdown

项目地址:https://gitcode.com/gh_mirrors/ev/evidence
点击查看免费下载

本文以 Evidence 仓库内的cli/test-environment/pages/test.md页面为核心样例,逐段拆解一个完整销售分析仪表板的写法——从下拉筛选器、KPI 卡片、趋势折线图,到透视表、迷你趋势线与同比对比,覆盖 Evidence 核心组件的真实用法。读完本文,你将掌握如何仅用一个 Markdown 文件 + 少量组件语法,组合出具备交互筛选能力的业务报表,并理解这些组件背后的查询与渲染机制。

一、先认识 test.md:一个可运行的全功能示例页

在 Evidence 中,一个*.md文件就是一张页面。仓库 cli/test-environment/pages/test.md 是 CLI 开发期用来验证组件能力的"组件测试页",其页面标题为Sales Analytics Dashboard(销售分析仪表板),实际演示了以下能力的组合:

  • 交互筛选:dropdown(下拉框)+button_group(按钮组)+option(选项)
  • KPI 卡片:big_value(大数字指标卡)+ 内嵌sparkline(迷你趋势图)
  • 趋势可视化:line_chart(折线图)+series(分组系列)+date_grain(时间粒度)
  • 表格体系:table+dimension(维度)/pivot(透视)/measure(度量),支持viz="bar"、viz="color"、viz="sparkline"三种单元格可视化
  • 周期对比:date_range(时间窗口)+comparison(对比)+viz="delta"(增量角标)

这份页面依赖两张示例数据表:demo_daily_orders(每日订单事实表,含date、category、total_sales、transactions、avg_transaction_value等字段)与demo_items(商品表,含category、item_name、base_price)。与同目录的 order-analysis.md(订单分析)和 home.md(CLI 导航首页)相比,test.md 的定位更偏向"组件能力覆盖测试"——每一类组件都安排了至少一个典型场景。

该目录的定位在 cli/test-environment/README.md 中有说明:这是 CLI 开发时用于验证命令的本地 Evidence 工程(CLI dev playground)。因此test.md既可作为入门学习模板,也可作为你新建项目页面的起点。

二、如何运行这份页面

要在本地把 test.md 渲染出来,只需要在仓库根目录执行:

# 默认针对 cli/test-environment 目录执行 CLI 命令 pnpm evd help pnpm evd query --sql "select 1" # 启动开发服务器(底层走 vite dev,无需编译二进制) pnpm evd dev

依据 cli/test-environment/README.md 的说明,这些脚本并不会cd进该目录,而是通过--project cli/test-environment把项目路径传给 CLI,从而保证process.cwd()语义正确;想指向其他项目时追加--project ./other/path即可。validate、docs等命令走 SvelteKit HTTP 层(依赖 vite-only 结构),需要先执行pnpm cli:build编译或保持开发服务器运行。

需要连接真实数仓时,在该目录下放置一个connection.yaml(已被 gitignore)指向你的开发数仓,示例查询便会从该数据源取数。

三、搭建页面筛选器:dropdown 与 button_group

一个交互式报表的第一步通常是定义"用户能按什么维度筛"。test.md 的## Filters小节演示了两种筛选器:

3.1 下拉筛选:从数据列取值

{% dropdown id="category_filter" data="demo_daily_orders" value_column="category" title="Category" initial_value="All" /%}

要点:

  • id:筛选器唯一标识,后续所有图表通过filters=["category_filter"]引用它;
  • data+value_column:指定从哪张表、哪一列去枚举下拉选项(去重后的 distinct 值);
  • title:显示在下拉框上方的标题文本;
  • initial_value="All":初始选中值。当数据中恰好存在All这个值时,它代表"全量"语义;在 test.md 中它与图表的联动方式是filters属性(而非where字符串),当选中All时相当于不过滤。

依据 docs/components/dropdown.mdx 的属性定义,dropdown还支持label_column(选项标签列)、order(排序,如"category desc")、multiple(多选)、default_top_n(多选时预选前 N 项)、search(选项搜索,默认true)、where(自定义 WHERE 条件)等进阶参数。两个下拉框若互相把id放进对方的filters数组,即可形成级联筛选(Cascading Dropdowns)。

3.2 按钮组:固定选项的粒度切换

{% button_group id="time_grain" title="Time Grain" %} {% option value="month" label="Monthly" /%} {% option value="quarter" label="Quarterly" /%} {% option value="year" label="Yearly" /%} {% /button_group %}

button_group适合选项数量少且含义固定的场景(这里用来切换时间聚合粒度)。与dropdown从数据列取值不同,它的选项由内部option子组件静态声明:value是实际传给图表的取值,label是用户看到的文本。结合 docs/components/button_group.mdx 的文档,它还支持orientation="vertical"纵向排列、initial_value与multiple等属性;若改为从数据列取值,也可以像 dropdown 一样使用data+value_column组合。

3.3 筛选值如何被引用

这两种筛选器在 test.md 中有两种被消费的方式:

  1. 声明式filters属性(test.md 的主力用法):图表与big_value直接写filters=["category_filter"],由 Evidence 自动把选中值拼进查询;
  2. {{id}}模板变量:如date_grain={{time_grain}},把按钮组的选中值(month/quarter/year)作为字符串注入图表属性,实现粒度动态切换。

从源码结构看,这类变量插值由 core/src/filter-variables/VariableProcessor.ts 与 core/src/Filter.svelte.ts 组成的筛选子系统处理。dropdown筛选值还暴露多种属性(如{{category_filter.filter}}返回可直接用于 WHERE 的 SQL 片段、{{category_filter.selected}}返回带引号的值、{{category_filter.literal}}返回原始值、{{category_filter.label}}返回显示标签),详见 docs/components/dropdown.mdx 的 "Using the Filter Variable" 一节——这意味着你既可以让组件自动过滤,也可以把筛选值手写进 SQL 或where属性中。

四、KPI 卡片区:big_value + sparkline

## Key Metrics小节用{% row %}栅格包裹三张 KPI 卡:

{% row %} {% big_value data="demo_daily_orders" value="sum(total_sales)" title="Total Sales" fmt="usd1m" filters=["category_filter"] sparkline={ type="area" x="date" } /%} {% big_value data="demo_daily_orders" value="sum(transactions)" title="Total Transactions" fmt="num0" filters=["category_filter"] sparkline={ type="bar" x="date" } /%} {% big_value data="demo_daily_orders" value="avg(avg_transaction_value)" title="Avg Transaction Value" fmt="usd2" filters=["category_filter"] /%} {% /row %}

三个卡片展示了 KPI 卡的核心参数组合:

参数作用示例
data查询的数据表demo_daily_orders
value要展示的 SQL 聚合表达式sum(total_sales)、count(*)、avg(...)
title卡片标题Total Sales
fmt数值格式化代码usd1m(百万美元/1 位小数)、num0(整数)、usd2(美元/2 位小数)
filters绑定的筛选器 id 数组["category_filter"]
sparkline迷你趋势图对象,type可选area/bar/line,x指定时间轴列{ type="area" x="date" }

fmt体系是 Evidence 数值格式化的核心,完整格式定义见 docs/core-concepts/value-formatting.mdx 及 core/src/user-components/formatValue.ts:usd系处理货币与单位换算(usd1m表示以百万为单位、保留 1 位小数),num0表示千分位整数,pct系为百分比。

关于 sparkline 的实现细节:迷你趋势图本质是查询时按x列聚合出的系列数据。从 core/src/connectors/normalize-sparkline-rows.ts 的源码可以看到,各数仓方言生成 sparkline 列的方式不同——ClickHouse 用groupArray((x, y))、Snowflake 用ARRAY_AGG(ARRAY_CONSTRUCT(x, y)),而 BigQuery 输出的是 JSON 字符串,需由normalizeSparklineRows在结果侧统一JSON.parse成[[x, y], ...]的元组数组后再交给图表渲染。这说明 sparkline 不是"重查一遍",而是与主查询共用一次取数、在列级别追加聚合,代价极低。

五、趋势分析:line_chart 的四种组合

5.1 分组面积折线 + 动态粒度

{% line_chart data="demo_daily_orders" x="date" y="sum(total_sales)" series="category" date_grain={{time_grain}} y_fmt="usd" title="Sales Over Time by Category" subtitle="Interactive: select a category above to filter" filters=["category_filter"] /%}

这是整页最典型的趋势图:series="category"按品类拆分多条折线;date_grain={{time_grain}}用按钮组的取值动态切换月/季/年聚合;y_fmt="usd"格式化 Y 轴;filters让折线图随下拉框即时重查。

依据 docs/components/line_chart.mdx,date_grain的合法取值非常丰富,除了month/quarter/year,还包括day of week(一周内按星期几聚合)、month of year(一年内按月聚合)、quarter of year、week of year、day of month等——它们正是下方"季节性与周期性"小节用到的核心能力。

5.2 三图并排:同一数据源的多种指标视角

{% row %} {% line_chart data="demo_daily_orders" x="date" y="sum(transactions)" date_grain={{time_grain}} y_fmt="num0" title="Transaction Volume" filters=["category_filter"] /%} {% line_chart data="demo_daily_orders" x="date" y="avg(avg_transaction_value)" date_grain={{time_grain}} y_fmt="usd2" title="Average Transaction Value" filters=["category_filter"] /%} {% /row %}

row组件把多个图表按栅格等宽并排,保持视觉对齐。这里演示了同一张明细表如何从"单量(num0)"与"客单价(usd2)"两个角度分别刻画趋势,且都共享同一筛选上下文。

5.3 季节性与周期性分析

{% row %} {% line_chart data="demo_daily_orders" x="date" y="sum(total_sales)" y_fmt="usd" date_grain="day of week" title="Sales by Day of Week" filters=["category_filter"] /%} {% line_chart data="demo_daily_orders" x="date" y="sum(total_sales)" y_fmt="usd" date_grain="month of year" title="Seasonality (Month of Year)" filters=["category_filter"] /%} {% /row %}

date_grain="day of week"与date_grain="month of year"是 Evidence 处理周期性规律的快捷方式:前者自动把时间轴折叠为周一到周日 7 个桶,后者折叠为 1–12 月 12 个桶,无需手写extract(dow from date)。这是从明细日期列直接生成周期洞察的低成本写法,docs/components/line_chart.mdx 中另有quarter of year、week of year、day of month等粒度可进一步扩展。

六、表格体系:dimension / pivot / measure 与单元格可视化

test.md 后半段全部围绕table组件展开,展示了该组件的四种典型形态。

6.1 透视表:维度 × 年份列 × 多度量

{% table data="demo_daily_orders" filters=["category_filter"] %} {% dimension value="category" /%} {% pivot value="date" date_grain="year" /%} {% measure value="sum(total_sales)" title="Total Sales" fmt="usd1m" viz="bar" bar_options={ bar_color="#3b82f6" } /%} {% measure value="sum(transactions)" title="Transactions" fmt="num0" viz="color" /%} {% measure value="sum(total_sales) / sum(transactions) as avg_order" title="Avg Order Value" fmt="usd2" /%} {% /table %}

这里的结构是:dimension定义行维度(category),pivot把date按年转置为列(date_grain="year"),三个measure分别定义数值单元格。值得注意的进阶能力:

  • 度量即表达式:第三个度量直接写了带别名的新聚合sum(total_sales) / sum(transactions) as avg_order,说明measure的value支持任意 SQL 表达式而不限于单个聚合;
  • 单元格可视化:viz="bar"在单元格内画迷你条形(bar_options.bar_color指定#3b82f6蓝色条),viz="color"用背景色深浅表示数值高低,两者都不改表格布局。

依据 docs/components/table.mdx,measure还支持date_range(列级时间窗口)、comparison(列级对比)、sparkline_options(迷你趋势列)等配置,pivot除date_grain外也可按普通维度列转置。

6.2 带迷你趋势的明细表

{% table data="demo_daily_orders" filters=["category_filter"] %} {% dimension value="category" /%} {% measure value="sum(total_sales)" title="Total Sales" fmt="usd1m" /%} {% measure value="sum(total_sales)" title="Sales Trend" viz="sparkline" sparkline_options={ x="date" type="area" } /%} {% measure value="sum(transactions)" title="Transactions" fmt="num0" /%} {% measure value="sum(transactions)" title="Transaction Trend" viz="sparkline" sparkline_options={ x="date" type="bar" } /%} {% /table %}

这段代码展示了"数值 + 趋势"并列的经典打法:对同一指标声明两条measure——一条显示汇总值,另一条用viz="sparkline"+sparkline_options={ x="date" type="area" }显示该维度随时间的变化曲线(与big_value的sparkline参数同构)。数据底层同样由normalizeSparklineRows这类方言适配层统一成[[x, y], ...]结构,无需额外请求。

6.3 同比对比表:date_range + comparison + delta

{% table data="demo_daily_orders" filters=["category_filter"] %} {% dimension value="category" /%} {% measure value="sum(total_sales)" title="Sales (Last 12 Months)" fmt="usd1m" date_range={ range="last 12 months" date="date" } comparison={ compare_vs="prior year" } viz="delta" /%} {% measure value="sum(transactions)" title="Transactions (Last 12 Months)" fmt="num0" date_range={ range="last 12 months" date="date" } comparison={ compare_vs="prior year" } viz="delta" /%} {% /table %}

这是"同比分析"的标准模板,三个参数缺一不可:

参数作用
date_range限定统计窗口,range取值如last 12 months、month to date、last 30 days、自定义区间2020-01-01 to 2023-03-01或开区间from .../until ...;date指定表内用于过滤的日期列(多日期列时必须显式给出)
comparison对比基准,compare_vs="prior year"表示与去年同期(上一自然年同期窗口)比较,另有prior quarter、target(与固定目标值比较)等选项
viz="delta"把"当前值 vs 对比值"渲染为带向上/向下箭头的增量角标,配合fmt显示绝对值与变化方向

date_range与comparison同样可用在big_value与delta组件上(见 docs/components/big_value.mdx 与 docs/components/delta.mdx),使"近 12 个月销售额 vs 上年"这类 KPI 可以一句话写出。

6.4 商品目录表:多维度 + 条件条形

{% table data="demo_items" %} {% dimension value="category" /%} {% dimension value="item_name" title="Product" /%} {% measure value="max(base_price)" title="Price" fmt="usd2" viz="bar" bar_options={ bar_color="#10b981" } /%} {% /table %}

这里换用了第二张数据表demo_items,演示两点:一是table允许多个dimension构成多级行分组(品类 → 商品名,title="Product"可覆盖列标题);二是viz="bar"的单元格条形与big_value场景无关,是独立于 KPI 卡的通用能力,此处换成绿色#10b981以区分页面内其他图表的配色。

七、从示例页到真实项目:改造清单

把 test.md 改造成你自己的分析页面,核心步骤是"换表、换列、换参数":

  1. 确认数据源:在connection.yaml中配置数仓连接,确认目标表的字段名与 test.md 使用的date/category/total_sales/transactions等一致或做相应替换;
  2. 替换数据表与字段:所有组件里的data=、value_column=、value=、x=、series=、pivot value=、date_range.date=逐一映射到你的真实字段;
  3. 调整筛选器:dropdown的value_column换成你想让用户筛选的维度列;button_group的option换成符合业务语义的粒度;
  4. 统一格式化:按指标含义选fmt(金额用usd系、数量用num0系、比例用pct系);
  5. 本地验证:运行pnpm evd dev后,通过浏览器观察筛选器联动、粒度切换与同比角标是否符合预期,控制台会输出对应的查询执行日志(cli/test-environment/README.md 明确提到"Check the console for query execution logs")。

若你的团队需要按 SQL 文件组织查询,可参照 docs/features/sql-files.mdx 把指标查询沉淀为独立 SQL;需要语义指标复用则可使用metric属性(如{% big_value metric="revenue" /%}),其定义与约束见 docs/core-concepts/metrics.mdx。

八、小结

test.md用不到 260 行 Markdown 覆盖了 Evidence 交互式报表的绝大多数高频组件与参数组合:dropdown/button_group负责输入,big_value/line_chart/table负责呈现,date_grain处理时间粒度与周期洞察,date_range+comparison+delta完成同比分析,sparkline与单元格viz在不增加查询次数的前提下补齐趋势细节。它既是 CLI 团队验证组件的"测试页",也是一份可以直接对照学习的组件速查模板——把其中的数据表与字段替换成你自己的数据,一个具备完整交互能力、用 SQL 和 Markdown 编写的销售分析仪表板就能在本地跑起来。

  • 数据分析
  • 数据可视化
  • 前端

【免费下载链接】evidence

Business intelligence as code: build fast, interactive data visualizations in SQL and markdown

项目地址:https://gitcode.com/gh_mirrors/ev/evidence
点击查看免费下载

相关推荐

上一篇:90DaysOfDevOps 开源贡献工作流实战:从 Fork 到 Pull Request 的完整指南(Day 41)
下一篇:Nub架构深度剖析:Rust如何通过Node的5大公开扩展面增强原版运行时

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/8 14:23:00

text-to-cad 实战:从自然语言到 STEP 参数化建模全链路

1. 从一句话到三维模型:text-to-cad 到底在解决什么问题第一次听到 text-to-cad 这个词,我脑子里蹦出来的画面是:对着电脑敲一句“给我画一个 80x60x20 的法兰盘,中心开 30 的通孔,四角各一个 M6 沉头孔”,…

作者头像 李华
网站建设 2026/10/8 14:20:18

MonkeyCode实践:AI编程从失控到企业级流水线

前几天研发周会上,有个同事很兴奋地演示他新写的模块——用AI编程工具,一个下午搞定了平时两三天的活。代码评审的时候,我翻了翻他提交的内容,发现几个边界条件没处理,依赖版本也引错了,还有一段逻辑明显是…

作者头像 李华
网站建设 2026/10/8 14:17:49

某纯电牵引车整车控制系统

一、整车控制系统VCU主要功能 VCU接收来自驾驶员的开关信号,如钥匙开关信号、油门位置、刹车、档位、制动等等,然后通过计算和处理,来实现对整车驱动控制及其它控制功能。1、电机控制: 通过接收驾驶员指令,以及整车相关…

作者头像 李华