Cal.com QR Code 应用集成指南:为事件类型链接生成可打印、可分享、可嵌入的二维码
导读
本文基于 cal.diy 仓库中 packages/app-store/qr_code 应用展开,讲解如何在 Cal.com 的事件类型(Event Type) 上集成 QR Code 应用,一键生成可用于打印、分享或嵌入的二维码,并支持通过附加 URL 参数定制二维码指向的链接。读者学完后,将掌握该应用的安装机制、事件类型设置界面、二维码生成原理(含三种尺寸与样式细节)、底层数据结构(Zod schema)以及把二维码用于线下物料投放的完整实战方法。
一、应用定位:为“事件类型链接”生成二维码
QR Code 应用在仓库中的官方描述只有一句话,但定位非常精准:
Easily generate a QR code for your links to print, share, or embed. (轻松为你的链接生成二维码,用于打印、分享或嵌入。)
其核心价值在于:Cal.com 的每一个事件类型(如 30 分钟咨询、60 分钟会议)都拥有一个独立的预订链接(eventType.URL)。线下场景(名片、海报、易拉宝、会议桌牌)无法直接点击链接,QR Code 应用将这一业务链接转化为物理世界可扫描的二维码,实现“线下扫码 → 在线预订”的闭环。同时通过附加参数(如 UTM 来源、渠道标记、?guest=1 等),可以追踪不同投放渠道的预订转化效果。
从元数据看(见 config.json),这是一个典型的 EventType 扩展型应用:
| 配置项 | 值 | 含义 |
|---|---|---|
slug |
qr_code |
应用唯一标识,不可修改 |
type |
qr_code_other |
应用类型标识,供 API 层识别 |
variant |
other |
应用变体分类 |
categories |
["other"] |
应用市场中的分类归属 |
extendsFeature |
EventType |
核心特征:扩展的是事件类型而非全局功能 |
isOAuth |
false |
无需 OAuth 授权流程 |
publisher |
Cal.com, Inc. |
发布方 |
extendsFeature: "EventType" 是关键——它决定了该应用出现在事件类型配置页的应用卡片区域,而不是用户级或团队级设置中心。这也是后文所有安装、配置、渲染逻辑的落点。
二、安装机制:无密钥的默认安装(API 层)
QR Code 应用不依赖任何第三方凭证或 OAuth 令牌,因此其安装流程是最简化的“默认安装”。应用入口文件 index.ts 仅做了一件事:导出 api 命名空间:
export * as api from "./api";
而 api/index.ts 只暴露了一个 add handler(即“添加/安装”处理函数),具体实现在 api/add.ts:
import type { AppDeclarativeHandler } from "@calcom/types/AppHandler";
import { createDefaultInstallation } from "../../_utils/installation";
import appConfig from "../config.json";
const handler: AppDeclarativeHandler = {
appType: appConfig.type,
variant: appConfig.variant,
slug: appConfig.slug,
supportsMultipleInstalls: false,
handlerType: "add",
createCredential: ({ appType, user, slug, teamId }) =>
createDefaultInstallation({ appType, user: user, slug, key: {}, teamId }),
};
export default handler;
这段代码的几个要点值得展开:
handlerType: "add"声明这是一个安装型 handler;AppDeclarativeHandler是 Cal.com app-store 声明的通用接口(类型定义见 types/AppHandler.d.ts)。createDefaultInstallation(来自 packages/app-store/_utils/installation.ts)是 Cal.com 为无凭证应用提供的标准安装函数:传入空对象key: {}即可建立一条 Credential 记录,表示“该事件类型已启用 QR Code 应用”。supportsMultipleInstalls: false表明每个用户/团队只能安装一次,符合“工具型”应用的特征——不需要重复安装多份。- 因为
key为空、appKeysSchema为空对象(见下文),所以整个安装过程不产生任何敏感配置或密钥,这也是它成为入门级、零门槛应用的原因。
三、事件类型设置界面:附加参数 + 三档尺寸二维码
安装后,QR Code 应用会以 AppCard 形式呈现在事件类型配置页中。外层卡片组件 EventTypeAppCardInterface.tsx 使用了 Cal.com 的标准基础设施:
useIsAppEnabled(app):读取/切换应用启用状态,控制卡片右上角开关;useAppContextWithSchema<typeof appDataSchema>():基于 Zod schema 读写事件类型metadata.apps中的应用数据,disabled状态会联动传递给内层设置组件。
真正的功能逻辑在 EventTypeAppSettingsInterface.tsx 中,其 UI 由两部分组成。
3.1 附加 URL 参数输入框
界面顶部是一个 TextField(标签为 additional_url_parameters,即“附加 URL 参数”),允许用户为二维码指向的链接追加查询参数:
const [additionalParameters, setAdditionalParameters] = useState("");
const query = additionalParameters !== "" ? `?${additionalParameters}` : "";
const eventTypeURL = eventType.URL + query;
行为逻辑为:
- 输入为空 →
query为空字符串,二维码指向原始事件类型链接eventType.URL; - 输入非空 → 自动拼接
?前缀,得到eventType.URL?<你的参数>。
典型用法示例:在门店桌牌投放时输入 utm_source=qr-table&utm_medium=offline,即可在预订分析中区分不同渠道来源;需要携带与会者身份时也可输入如 guest=1 等平台支持的查询参数。注意参数需符合 URL 编码规范,多个参数用 & 连接。
3.2 三档尺寸二维码生成
设置界面底部一次性渲染三个不同尺寸的二维码,供不同投放场景选用:
function QRCode({ size, data }: { size: number; data: string }) {
const QR_URL = `https://api.qrserver.com/v1/create-qr-code/?size=${size}&data=${data}`;
return (
<Tooltip content={eventTypeURL}>
<a download href={QR_URL} target="_blank" rel="noreferrer">
<img
className={classNames(
"hover:bg-cal-muted border-default border transition hover:shadow-sm",
size >= 256 && "min-h-32"
)}
style={{ padding: size / 16, borderRadius: size / 20 }}
width={size}
src={QR_URL}
alt={eventTypeURL}
/>
</a>
</Tooltip>
);
}
// 渲染处
<QRCode size={256} data={eventTypeURL} />
<QRCode size={128} data={eventTypeURL} />
<QRCode size={64} data={eventTypeURL} />
从源码可以提炼出该实现的几个关键设计:
- 生成方式:二维码图由
api.qrserver.com的create-qr-code接口按需生成,size与data直接作为查询参数传给该服务,<img>的src即指向生成的图片地址——这是一个无需后端、纯前端的二维码方案,部署成本为零。 - 尺寸档位:
256px(适合海报、易拉宝等远距离扫描)、128px(适合名片、宣传单页)、64px(适合屏幕嵌入、邮件签名等近距离小场景)。 - 下载与提示:整个
<img>包裹在带download属性的<a>标签内,用户点击即可直接下载 PNG 图片用于印刷;鼠标悬停时通过Tooltip显示二维码指向的完整事件类型 URL,方便校对。 - 视觉样式:内边距为
size / 16(如 256px 时为 16px),圆角为size / 20,保证二维码留白符合规范且观感统一;size >= 256时追加min-h-32类避免大图在布局中塌陷。 - 语义化:
alt={eventTypeURL}使二维码的可访问性与 SEO 语义都指向目标预订链接。
四、数据结构:极简的 Zod Schema 与事件类型元数据
QR Code 应用的数据层非常干净,见 zod.ts:
import { z } from "zod";
import { eventTypeAppCardZod } from "../eventTypeAppCardZod";
export const appDataSchema = eventTypeAppCardZod;
export const appKeysSchema = z.object({});
其中 appDataSchema 直接复用所有 EventType 应用共享的 eventTypeAppCardZod.ts:
export const eventTypeAppCardZod = z.object({
enabled: z.boolean().optional(),
credentialId: z.number().optional(),
appCategories: z.array(z.string()).optional(),
});
这意味着该应用在事件类型 metadata.apps 中存储的数据只有三个可选字段:enabled(开关状态)、credentialId(安装凭证 ID)、appCategories(所属分类)。附加参数输入框中的内容仅存在于组件本地 useState,不会持久化到数据库——每次进入配置页都从空字符串开始,这是该实现的一个值得注意的行为(若希望跨会话保留参数,需要二次开发)。
而 appKeysSchema(应用级密钥 Schema)为空对象,从数据结构层面再次印证:QR Code 应用不持有任何密钥、Token 或敏感配置,是一个完全公开、零权限要求的轻量应用。
五、应用市场的注册与分发位置
作为 app-store 生态的一员,QR Code 应用通过 packages/app-store/apps.metadata.generated.ts 等生成文件被注册进应用市场列表;其扩展了事件类型,因此遵循 extendsFeature: "EventType" 的通用注册规则。对该应用的源码进行二次开发或本地调试时,可参考 packages/app-store/CONTRIBUTING.md 了解 app-store 目录的组织约定,以及同目录下其他 EventType 扩展类应用(如 giphy、hitpay、qr_code 等)作为对照样本。
六、适用场景与限制总结
综合 DESCRIPTION.md 与源码实现,QR Code 应用最适合以下场景:
- 线下物料投放:打印 256px 二维码到海报/桌牌/易拉宝,客人扫码直达预订页;
- 内容分享:在邮件签名、即时通讯、社交平台分享 128px 或 64px 二维码;
- 嵌入第三方页面:将
<img>形式的二维码嵌入官网、活动页、PPT 等任意支持图片的载体; - 渠道归因:利用附加 URL 参数区分不同投放渠道的预订来源。
使用时需注意的边界:
- 二维码渲染依赖外部
api.qrserver.com服务,离线或该服务不可达时无法出图(从源码结构可以推断这是当前实现的固有限制); - 附加参数仅存于前端状态,不持久化;
- 应用无密钥、无 OAuth,因此也没有权限配置或企业级管控能力,适合轻量使用而非复杂场景。
七、结语
QR Code 应用是 Cal.com app-store 中“小而完整”的典型样本:一条安装 handler、一张事件类型卡片、一个设置界面,即完成了“事件类型链接 → 可打印/分享/嵌入二维码”的完整闭环。它既是一个开箱即用的功能,也为开发者理解 EventType 扩展型应用(extendsFeature: "EventType"、共享 eventTypeAppCardZod、createDefaultInstallation 安装范式)提供了极佳的参考实现。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00