Sprucely title background

集成

您可以通过三种方式,将交互式的 Sprucely.io 仪表板集成到您自己的网站或 Web 应用中:使用标准 HTML iframe 进行嵌入、通过独立的 JavaScript 运行时原生渲染,或挂载独立的 React 组件。iframe 上手最快;独立运行时会将仪表板直接绘制在您的页面中,并允许您在运行时推送新数据。独立运行时读取的 JSON 结构——仪表板、图表和数据集——完整记录在数据格式参考文档中。若需了解 AI 驱动的仪表板自动化,请参阅MCP API章节。

在 HTML 中嵌入仪表板

最简单的集成方式是使用标准的 HTML iframe 元素,适用于任何网站,甚至本地 HTML 页面。仪表板必须先设置为共享(无论是云端还是本地部署)才能成功加载。请注意,本地部署的仪表板只有在访问该仪表板的客户端处于同一企业网络内时才能加载。

操作步骤:

1) 获取仪表板嵌入链接 - 在数据看板页面中,确认您的仪表板已启用共享,然后点击该仪表板对应的 图标。页面会弹出绿色提示条,告知链接已复制到剪贴板。您需要用它替换下方 HTML iframe 中 src 的内容。

2) 以固定尺寸嵌入仪表板

<html>
  <head>
    <title>Sprucely.io Dashboard</title>
  </head>
  <body>
    <h1>Sprucely.io Dashboard</h1>
    <iframe src="https://www.sprucely.io/service/dashboards/embed/[userId]/[dashboardId]/"
            allow="clipboard-write"
            style="height: 500px; width: 700px;"
            title="Sprucely.io Dashboard"></iframe>
  </body>
</html>

(或)以动态宽度嵌入仪表板 - 根据父文档的宽度自动调整仪表板尺寸

您可以使用 aspect-ratio 样式参数,根据宽度自动重新计算高度。

<html>
  <head>
    <title>Sprucely.io Dashboard</title>
  </head>
  <body>
    <h1>Sprucely.io Dashboard</h1>
    <iframe src="https://www.sprucely.io/service/dashboards/embed/[userId]/[dashboardId]/"
            allow="clipboard-write"
            scrolling="no"
            style="width: 100%; aspect-ratio: 1.5; border: none;"
            title="Sprucely.io Dashboard"></iframe>
  </body>
</html>

3) 其他实用的嵌入选项

您可以在 iframe 元素的 style 代码块中添加自定义样式修改,用于定制该框架的外观。这些参数遵循标准的HTML CSS 规范。下面的示例为仪表板添加了一圈灰色边框:

<html>
  <head>
    <title>Sprucely.io Dashboard</title>
  </head>
  <body>
    <h1>Sprucely.io Dashboard</h1>
    <iframe src="https://www.sprucely.io/service/dashboards/embed/[userId]/[dashboardId]/"
            allow="clipboard-write"
            style="height: 100%; width: 100%; border: 2px solid grey;"
            title="Sprucely.io Dashboard"></iframe>
  </body>
</html>

使用独立 JavaScript 嵌入

独立运行时会将仪表板直接渲染在您的页面中——无需 iframe。您的数据会被加载到浏览器内数据库中,并绘制为完全交互式的图表;该运行时仅会联系 Sprucely.io 以验证您的访问令牌。

操作步骤:

1) 创建访问令牌 - 在您个人资料的访问令牌部分,为您页面所在的来源创建一个访问令牌(例如 https://www.yourdomain.com)。渲染之前,运行时会验证嵌入页面的来源与该令牌是否匹配。

2) 加载运行时并渲染仪表板 - 将运行时脚本添加到您的页面中,使用 sprucely_db 连接一次浏览器内数据库,然后使用 sprucely_create 渲染各个仪表板。下面的完整页面示例还接入了一个按钮,用于追加更多行:

<html>
  <head>
    <title>Orders Dashboard</title>
  </head>
  <body>
    <div id="database"></div>
    <div id="orders_dashboard"></div>
    <button onclick="addRows()">Add rows</button>

    <script src="https://www.sprucely.io/cross-origin/sprucely-runtime.min.js"></script>
    <script>
      const data = {
        name:    "Orders",
        headers: ["region", "amount", "items"],
        types:   ["VARCHAR", "FLOAT", "INTEGER"],
        entries: [
          ["North", 120.50, 2],
          ["South",  89.95, 1],
          ["North", 432.00, 5],
          ["South",  74.50, 1]
        ]
      };

      const dashboard = {
        config: {
          type: "dashboard",
          style: {
            widget: {
              color: "#667FFF",
              backgroundColor: "#0C0B29",
              primaryColor: "#01EEAE",
              primaryColorSubtle: "#C70584",
              secondaryColor: "#2C2B49"
            }
          }
        },
        data: {
          type: "dash",
          children: [
            {
              type: "dash_stacker_hor",
              children: [
                { type: "dash_chart", datasetId: "Orders", x: "region", chartType: "bar", dataFunction: "count" },
                { type: "dash_chart", datasetId: "Orders", x: "amount", y: "items", chartType: "hexbin", dataFunction: "count" }
              ]
            }
          ]
        }
      };

      sprucely_db({
        id: "database",
        host: "https://www.yourdomain.com",
        accessToken: "YOUR_ACCESS_TOKEN"
      });
      sprucely_create({ id: "orders_dashboard", dashboard, data });

      function addRows() {
        sprucely_add({
          name:    "Orders",
          headers: ["region", "amount", "items"],
          types:   ["VARCHAR", "FLOAT", "INTEGER"],
          entries: [
            ["East", 310.40, 3],
            ["West", 129.95, 1]
          ]
        });
      }
    </script>
  </body>
</html>

3) 在运行时追加数据 - 随时调用 sprucely_add 并传入额外的行数据。所有使用该数据集的仪表板都会自动刷新:

sprucely_add({
  name:    "Orders",
  headers: ["region", "amount", "items"],
  types:   ["VARCHAR", "FLOAT", "INTEGER"],
  entries: [
    ["East", 310.40, 3],
    ["West", 129.95, 1]
  ]
});

函数参考

  • sprucely_db({ id, host, accessToken }) - 连接浏览器内数据库,并根据页面来源验证您的访问令牌,然后将状态提示条渲染到 id 所标识的元素中。每个页面只需调用一次,且需在创建仪表板之前调用。
  • sprucely_create({ id, dashboard, data }) - 将一个仪表板渲染到 id 所标识的元素中。dashboard 参数定义布局与样式;data 提供数据集本身,包括其名称、表头、列类型(VARCHARINTEGERFLOATTIMESTAMP)以及行数据——详见数据格式参考文档。
  • sprucely_add(data) - 将行数据追加到名称与某个已加载数据集相匹配的数据集中,然后刷新所有使用该数据集的仪表板。

使用独立 React 嵌入

如果您的网站使用 React 构建,您可以将仪表板渲染为组件,而无需手动加载脚本。这些组件使用与独立 JavaScript 运行时相同的访问令牌、仪表板定义和数据集格式。

操作步骤:

1) 创建访问令牌 - 与独立 JavaScript 相同,在您个人资料的访问令牌部分,为您网站的来源创建一个访问令牌。

2) 渲染仪表板组件 - 从 https://www.sprucely.io/cross-origin/sprucely-runtime.min.cjs.js 获取运行时,并将其放入您的项目中。每个页面挂载一个 Sprucely.Database 组件,每个仪表板挂载一个 Sprucely.Dashboard 组件。在数据库验证访问令牌之前,仪表板会显示加载指示器,验证完成后才会渲染。下面的完整应用示例渲染了来自两个不同数据集的仪表板,并在五秒后为第一个数据集追加新的行数据:

// Get https://www.sprucely.io/cross-origin/sprucely-runtime.min.cjs.js
// and place it in your project, next to this file.

import React, { useEffect } from "react";
import { createRoot } from "react-dom/client";
import Sprucely, { sprucely_add } from "./sprucely-runtime.min.cjs.js";

const style = {
  widget: {
    color: "#667FFF",
    backgroundColor: "#0C0B29",
    primaryColor: "#01EEAE",
    primaryColorSubtle: "#C70584",
    secondaryColor: "#2C2B49"
  }
};

const orders = {
  name:    "Orders",
  headers: ["region", "amount", "items"],
  types:   ["VARCHAR", "FLOAT", "INTEGER"],
  entries: [
    ["North", 120.50, 2],
    ["South",  89.95, 1],
    ["North", 432.00, 5],
    ["South",  74.50, 1]
  ]
};

const returns = {
  name:    "Returns",
  headers: ["reason", "amount", "days"],
  types:   ["VARCHAR", "FLOAT", "INTEGER"],
  entries: [
    ["Damaged", 45.00, 3],
    ["Late",    12.50, 8],
    ["Changed", 99.90, 2]
  ]
};

const moreOrders = {
  name:    "Orders",
  headers: ["region", "amount", "items"],
  types:   ["VARCHAR", "FLOAT", "INTEGER"],
  entries: [
    ["East", 310.40, 3],
    ["West", 129.95, 1]
  ]
};

const ordersDashboard = {
  config: { type: "dashboard", style },
  data: {
    type: "dash",
    children: [
      {
        type: "dash_stacker_hor",
        children: [
          { type: "dash_chart", datasetId: "Orders", x: "region", chartType: "bar", dataFunction: "count" },
          { type: "dash_chart", datasetId: "Orders", x: "amount", y: "items", chartType: "hexbin", dataFunction: "count" }
        ]
      }
    ]
  }
};

const returnsDashboard = {
  config: { type: "dashboard", style },
  data: {
    type: "dash",
    children: [
      {
        type: "dash_stacker_hor",
        children: [
          { type: "dash_chart", datasetId: "Returns", x: "amount", y: "days", d: "amount", r: "days", chartType: "dot", dataFunction: "average" }
        ]
      }
    ]
  }
};

const App = () => {
  useEffect(() => {
    const timer = setTimeout(() => { sprucely_add(moreOrders); }, 5000);
    return () => clearTimeout(timer);
  }, []);

  return (
    <>
      <Sprucely.Database host="https://www.yourdomain.com" accessToken="YOUR_ACCESS_TOKEN" />
      <Sprucely.Dashboard id="orders_dashboard" dashboard={ordersDashboard} data={orders} />
      <Sprucely.Dashboard id="returns_dashboard" dashboard={returnsDashboard} data={returns} />
    </>
  );
};

createRoot(document.getElementById("root")).render(<App />);

签发短期密钥

您在上文创建的访问令牌是长期有效的,理应作为服务器端密钥妥善保管,不对外泄露。如果嵌入页面是由您自己的后端提供服务——而非一个自身没有后端的静态页面——那么您完全不需要将这个长期令牌放入页面中:您的后端可以在提供每个页面之前,通过服务器到服务器的方式,将其兑换为一个短期(15 分钟)有效的渲染令牌,只有这个渲染令牌才会到达浏览器。即便访客从页面中提取出渲染令牌,其有效期也只有短短几分钟,而非无限期。

操作步骤:

1) 将访问令牌兑换为渲染令牌 - 从您的后端调用 POST https://www.sprucely.io/api/auth/render_token,将访问令牌以 Bearer 凭证的形式传入。响应中会返回新生成的令牌、其绑定的主机,以及以秒为单位的有效期:

// On your server, immediately before serving each embedding page:
const response = await fetch("https://www.sprucely.io/api/auth/render_token", {
  method:  "POST",
  headers: { Authorization: "Bearer " + process.env.SPRUCELY_ACCESS_TOKEN }
});
const { token, expires_in } = await response.json(); // expires_in: 900 (15 minutes)

// Send only the render token to the browser - never the long-lived access
// token itself:
res.send(`
  <script src="https://www.sprucely.io/cross-origin/sprucely-runtime.min.js"></script>
  <script>
    sprucely_db({
      id: "database",
      host: "https://www.yourdomain.com",
      accessToken: "${token}"
    });
  </script>
`);

2) 将渲染令牌提供给浏览器 - 在调用 sprucely_db 或挂载 Sprucely.Database 时,像使用访问令牌一样直接使用它。请在提供每个页面之前重复执行该兑换流程(如果您缓存了渲染后的页面,则可以按计时器定期执行),确保浏览器收到的令牌有效期永远不会超过 15 分钟。

函数参考

  • POST /api/auth/render_token - 将以 Authorization: Bearer <token> 形式发送的有效访问令牌,兑换为一个渲染令牌。返回 { token, host, expires_in },其中 expires_in 固定为 900 秒。渲染令牌无法再兑换出另一个渲染令牌——只有长期有效的访问令牌才能发起此项请求。

安全设置

如果您的网站强制执行内容安全策略(CSP),在上述独立 JavaScript 和 React 集成能够正常加载仪表板之前,需要先放行若干来源——运行时会从 Sprucely.io 加载脚本,开启一个基于 WebAssembly 的浏览器内数据库,并向我们的 API 验证您的访问令牌,这三者都需要明确的 CSP 许可。如果您的网站未使用 CSP,可以跳过本节。

将以下来源添加到您现有的策略中——这些是需要合并进去的指令,而不是用来替换您现有策略的完整策略:

script-src   'self' https://www.sprucely.io 'wasm-unsafe-eval';
connect-src  'self' https://www.sprucely.io;
worker-src   'self' blob: https://www.sprucely.io;

各指令的作用:

  • script-src - https://www.sprucely.io 用于加载运行时脚本。编译并运行基于 WebAssembly 构建的浏览器内数据库,需要 'wasm-unsafe-eval'
  • connect-src - 系统会联系 https://www.sprucely.io 以验证您的访问令牌,并加载数据库的 WebAssembly 与工作线程文件。
  • worker-src - 浏览器内数据库会在一个后台线程上运行查询,该线程通过 blob: URL 创建。

以上各项均不需要 'unsafe-inline'——Sprucely 运行时本身从不需要它。如果您将自己的仪表板与数据集定义保存在内联 <script> 标签中(如上方独立 JavaScript 示例所示),或者您自己的页面使用了内联样式,请将 'unsafe-inline' 添加到 script-srcstyle-src 中,或考虑改用 CSP nonce。