---
name: vjvp
description: Use when working with 唯杰 AI 可视化平台 (VP / appvisual) — building dashboards in the editor, embedding screens into your own web app with the vjvpsdk runtime (createVpViewer, ctx.setData push-data, on/emit behavior events, add/remove widgets, windows, custom components, code-panel scripts), writing .vpproj.json project bodies by hand, composing multi-page control platforms with the SDK scaffold (config layer + code layer, sim engine, 3D/scada/map scene recipes), deploy/publish/iframe-embed, VP MCP/AI integration. Keywords 可视化平台 appvisual vjvpsdk 大屏 看板 驾驶舱 createVpViewer setData 组态 数字孪生 vpproj. IMPORTANT: vjvpsdk is NOT published to npm — obtain it via private registry or local package path; do not invent public npm install instructions.
metadata:
  product: vp
  package: vjvpsdk
  packageVersion: "1.0.0"
  npmPublished: false
  docsMcp: https://vjmap.com/server/vp/mcp
---

# vjvp — 唯杰 AI 可视化平台二次开发

```yaml
pkg: vjvpsdk@1.0.0        # 未发布到 npm：私有 registry 或本地路径安装
peer: vue@^3.5            # vue 由宿主工程提供
docsMcp: https://vjmap.com/server/vp/mcp
```

| 场景 | 入口 |
|---|---|
| 官方文档/示例 MCP | `https://vjmap.com/server/vp/mcp`（平台自带 MCP：`search_widgets` / `get_widget_metadata` / `search_documentation` / `get_doc` / `list_examples` / `get_example` / `validate_project`，见 [platform/mcp-and-ai.md](references/platform/mcp-and-ai.md)） |
| SDK 类型定义（唯一权威） | 包内 `dist/vp-runtime.d.ts` |
| 组件 options 选项名字典（唯一权威） | [references/widget-schema.generated.json](references/widget-schema.generated.json) |
| 手写工程 JSON 校验器 | `node scripts/validate-project-json.mjs <工程.json>` |

## 安装（重要：vjvpsdk 未发布到 npm）

```bash
# 方式一：本地目录（把拿到的 vjvpsdk 包放到 vendor/ 下）
npm i ./vendor/vjvpsdk
# package.json 里等价写法："vjvpsdk": "file:./vendor/vjvpsdk"

# 方式二：私有 registry（按唯杰提供的地址配置 .npmrc 后）
npm i vjvpsdk

npm i vue@^3.5   # peer 依赖，必须装
```

**不要** `npm i vjvpsdk` 指望公网 npm 能装到。包结构：ESM `dist/vp-runtime.mjs` + 类型 `dist/vp-runtime.d.ts` + 样式 `dist/vp-runtime.css`。

⚠️ **`file:` 依赖 + vite 的坑**：npm `file:` 是符号链接，vite 默认按**真实路径**解析 `vp-runtime.mjs` 里的 `import "vue"`——包在 `vendor/` 之外（如工程目录之外的另一个磁盘目录）时会报 `Failed to resolve import "vue"`（HTTP 500）。修法二选一：① `vite.config` 加 `resolve.preserveSymlinks: true`（且 `server.fs.allow` 放行真实路径）；② 把包**复制**进工程的 `vendor/` 下（在 workspace 内即可正常解析）。

## 两种嵌入方式怎么选

- **只要"把发布好的大屏放进页面"** → iframe `<vp平台地址>/view?project=<id>`，零依赖（跨站嵌入若被拒载，检查平台响应头 `X-Frame-Options` / `frame-ancestors`，需部署侧放行）。模板见 `templates/iframe-embed/`。
- **需要推数 / 事件 / 程序化改组件** → SDK `createVpViewer`。模板见 `templates/vite-app/`。

## SDK 最小骨架（已按 vp-runtime.d.ts 核对）

```ts
import { createVpViewer, type VpProjectBody } from "vjvpsdk";
import "vjvpsdk/vp-runtime.css";  // exports 只放 .、./vp-runtime.css、./style.css（别名同文件）、./package.json；其余深路径会被拒

// body = 编辑器"导出工程"得到的 { project, connections, connectionData, dataRules }
const body: VpProjectBody = await (await fetch("/dashboards/sales.vpproj.json")).json();

const ctx = createVpViewer("#app", body, {
  params: { theme: "dark" },
  // ⚠️「导出工程」的 .vpproj.json 是精简形态（connectionData 省略 tableName/fields），SDK 不补齐，
  // 直挂会让柱/线/双轴图抛 TypeError（getFieldMeta 读 fields.find）；挂载前补齐（完整 body 上为空操作）
  transformData(b: any) {
    for (const c of b.connections ?? []) for (const t of c.tables ?? []) {
      const d = b.connectionData?.[c.uid]?.find((x: any) => x.tableId === t.uid);
      if (d) { d.tableName ??= t.name; d.fields ??= t.fields; }
    }
  },
  onReady(ctx) {
    // 推数即重渲：ctx.setData(connectionUid, tableId, rows)
    setInterval(async () => ctx.setData("conn_sales", "tbl_sales", await fetchRows()), 5000);
    // 大屏「发送行为事件」→ 宿主；payload = 触发器 payload（itemClicked 为 {别名:值}，click 为 undefined；该动作无 data 选项）
    ctx.on("open-detail", (_e, payload) => router.push(`/detail/${(payload as any)?.id}`));
  },
});
// 程序化改组件：ctx.getWidget("kpi_total")?.setOption("unit-text", "万元")
// 地图/组态句柄：ctx.getMap(uid) / ctx.getScada(uid)；窗口：ctx.openWindow(uid)
```

`VpViewerContext` 主要成员（`vp-runtime.d.ts`）：`screens`/`getScreen`/`setActiveScreen`、`windows`/`openWindow`/`closeWindow`、`getWidget`/`findWidgets`/`getWidgetsByType`/`addWidget`/`removeWidget`、`setData`/`getData`/`refreshData`/`onDataChange`、`getParams`/`setParams`/`onParamsChange`、`on`/`emit`/`startEventInbound`/`onScript`/`onRequest`、`reload`/`destroy`。

## 按问题找文档

| 要做什么 | 去读 |
|---|---|
| 编辑器搭屏/绑数据/配交互/导入导出（.vpproj.json/.vpproj.zip/.vp.tsx） | [platform/editor-guide.md](references/platform/editor-guide.md) |
| `createVpViewer` 嵌入、ctx 上下文、推数、事件、自定义组件 VpWidget、代码面板 | [platform/sdk.md](references/platform/sdk.md) |
| 发布快照 / `/view` 观看 / iframe / 二次开发工程包 / 自行托管 | [platform/deploy-and-integrate.md](references/platform/deploy-and-integrate.md) |
| MCP 与 AI（search_widgets / get_widget_metadata / get_doc / validate_project） | [platform/mcp-and-ai.md](references/platform/mcp-and-ai.md) |
| 编辑器内已打开页面的 TSX 生成/改写环路（page_export_tsx / page_apply_project） | [page-live-loop.md](references/page-live-loop.md) |
| 用 SDK 做完整管控平台：配置层+代码层、目录脚手架 | [sdk-app/scaffold.md](references/sdk-app/scaffold.md) |
| 实时模拟引擎、告警/工单状态机、数据建模、推数节流 | [sdk-app/sim-pattern.md](references/sdk-app/sim-pattern.md) |
| 三维/组态/地图场景配方、跨页联动 | [sdk-app/scene-recipes.md](references/sdk-app/scene-recipes.md) |
| **手写 .vpproj.json 工程体**：envelope 结构 / 数据连接 / 交互 / 视觉风格 / 坑 | [project-index.md](references/project-index.md) → envelope.md · data.md · interactions.md · visual-style.md · gotchas.md |
| 各组件家族 options 速查（15 篇） | [widgets/index.md](references/widgets/index.md) |
| VP-TSX 写法（.vp.tsx 整工程代码） | [vptsx.md](references/vptsx.md) |
| 11 个最小示例工程（vpproj.json + vp.tsx 对照，逐对回编译等值验证；成品大屏见 catalog 里的 data/ 路径） | [examples/catalog.md](examples/catalog.md) |

## 手写工程 JSON 校验

```bash
node vjvp/scripts/validate-project-json.mjs data/sales.vpproj.json
# ERROR 退出码 1；WARN 仅提示。不带参数时校验 examples/*.json
```

校验项：组件 type 合法性、`position`/`size` 必填、字段绑定三元组与 uid、`connections`↔`connectionData` 一致性、`connectionData.fields` 完整形态（缺了图表整块空白）、map-2d 的 map-body、dataRules 引用、动态数据项、条件样式。

## VP-TSX 离线编译链（已内置于 `scripts/vptsx/`）

```bash
# 类型检查（值/枚举/元组）。注意 tsc -p vjvp/scripts/vptsx/tsconfig.json 只查 runtime 与 scripts/vptsx/examples，不含你的文件
pnpm exec tsc --noEmit --strict --skipLibCheck --jsx react --jsxFactory h --jsxFragmentFactory Fragment --target ES2022 --module ESNext --moduleResolution Bundler <工程.vp.tsx>
pnpm exec tsx vjvp/scripts/vptsx/compile.mts <工程.vp.tsx>   # 执行 + 选项名检查 + 校验终审
```

`runtime.ts`（`defineProject`/`h`/`bind`/`jsonConnection`/`interactions`/`on`/`act`）+ `types.generated.ts`（80 组件 + 108 piece 全量类型，由 `gen-types.mts` 从 schema 确定性再生）+ 示例 `scripts/vptsx/examples/energy-mini.vp.tsx`。外部工作区需 `npm i -D tsx typescript`。写法权威见 [references/vptsx.md](references/vptsx.md)。组态场景体检：`node vjvp/scripts/check-scada-scene.mjs <工程.vpproj.json>`。

## 模板

- `templates/vite-app/` — SDK 嵌入工程（vjvpsdk 走 `file:` 本地依赖，[README](templates/vite-app/README.md)）
- `templates/iframe-embed/` — 零依赖 iframe 嵌入

## 红旗

1. 把 `vjvpsdk` 当已发布包直接 `npm i` → 装不到；走私有 registry 或本地 `file:` 路径。
2. iframe 能满足的场景引入 60MB SDK → 先问是否要推数/事件。
3. 组件选项名臆造（如 `background-colour`）→ 一律查 `widget-schema.generated.json`。
4. `connectionData` 写成精简形态缺 `fields` → 图表整块空白（SDK 直挂柱/线/双轴图抛 TypeError）；校验器会 WARN。编辑器「导出工程」的 .vpproj.json 就是精简形态，SDK 挂载前用上面骨架里的 `transformData` 补齐。
5. `basic-graphical` 不是容器——`widgets` 只能放 `screen`/`group-panel`/`group-tabs`/`group-popup`/`layout-*`（`data-repeater`/`window-instance`/`scada-2d` 的派生子级不走 `node.widgets`）。
6. 工程 JSON / 代码里不写 token、内网地址。
