1. 计划库宿主
插件化手册
  • FH2 插件开发手册
    • 产品简介
    • 典型场景
    • 发版说明
    • 快速开始
      • 快速开始总览
      • 第一个插件
      • 后端插件
      • 全栈闭环
    • AI辅助开发
      • AI 开发 Skill
      • 安装与复验
      • 第一个工程
      • 真实需求示例:现场问题上报
      • 改造已有工程
      • 调试与交付
    • 开发指南
      • 开发指南
      • 基础概念
        • 基础概念总览
        • 什么是插件开发
        • 平台架构
        • 前端宿主、扩展点与能力
        • 后端运行时、入口与权限
        • 开发流程
        • 安全与隔离
        • 支持范围与问题反馈
      • 开发指南
        • 开发指南总览
        • 前端插件
          • 前端插件深度指南
          • 前端工程结构
          • 插件清单(fh2-plugin.json)
          • 入口与生命周期
          • 界面与状态
          • 多宿主插件
          • 跨宿主通信服务
          • HTTP、WebSocket 与事件
          • 存储、Blob 与定时器
          • 工具链与本地调试页面
        • 后端插件
          • 后端开发深度指南
          • 后端插件的结构
          • 后端源清单 fh2-plugin.json
          • HTTP 接口处理函数
          • 消息订阅与事件处理函数
          • ctx 与能力注入
          • 数据存储与缓存
          • 流程编排
          • 触发方式
          • 连接外部系统
          • 流程编排练习(DAG)
        • 前后端协作
          • 前后端协作总览
          • 前后端共享契约
          • 端到端幂等
          • 事件、通知与状态
          • 统一错误模型
          • 链路标识与诊断日志
        • 完整练习
          • 环境准备
          • 第一个前端插件
          • 第一个后端插件
          • 第一个全栈插件
      • 能力总览
        • 能力地图
        • 能力可用性矩阵
        • 前端能力
          • 平台通用能力
            • 平台通用能力总览
            • 基础运行时:HTTP、事件、WebSocket 与存储
            • 导航、位置与通知
            • 弹窗、音频与剪贴板
            • 地图能力
            • 项目设备能力
            • WPMZ 与航线预览
            • 受控对象上传
          • 项目宿主
            • 项目宿主总览
            • 项目上下文
            • 项目侧边栏入口
            • 项目插件路由
            • 项目工作区
          • 消息宿主
            • 消息宿主总览
            • 消息告警弹层
            • 消息导航与通知
          • 计划库宿主
            • 计划库宿主总览
            • 计划库表单上下文
            • 计划库表单扩展
            • 校验、提交与 `biz_ext`
          • 驾驶舱
            • 驾驶舱插件能力总览
            • 驾驶舱扩展点
            • 上下文与遥测
            • 相机、检测与 AI
            • 场景几何与直播指针
            • 飞控、云台与任务
            • 驾驶舱平台事件
        • 后端能力
          • 后端能力地图
          • 后端能力总览
          • 配置、受管出网与集成连接
          • 事件与流程
          • 飞行任务与违停巡逻
          • 数据存储
          • 媒体与前端通知
          • 资源租约
          • 作业、目标轨迹与视频处理
      • 调试与测试
        • 调试与测试总览
        • 快速入门排错
        • 本地开发
        • Web IDE:打开工程与保存改动
        • 工作流编辑器:节点、接线与交付
        • 知识与 MCP:先判断需要哪一层事实
        • 前端本地调试
        • 后端运行时与流程调试
        • 模拟数据与测试数据
        • 集成测试与 E2E
        • 日志与链路追踪
        • 常见问题排查
      • 完整示例
        • 示例与案例总览
        • 项目侧边栏与工作区
        • 消息告警卡片
        • 计划表单任务清单
        • 驾驶舱动作与左侧面板
        • 最小后端插件
        • 最小全栈插件
        • 多宿主插件实战
        • 事故快处
        • 交通抓拍
        • 违停巡逻完整案例
      • 构建发布与安全
        • 构建、发布与安全
        • 质量门禁
        • 行业包构建与打包
        • 能力版本发布
        • 安装、升级与回滚
        • 安全基线
      • API参考
        • API 参考总览
        • 前端
          • 前端 API 参考
          • 前端插件清单结构
          • 前端 SDK API
          • 前端扩展点目录
          • 前端能力目录
          • 前端平台事件目录
          • 跨宿主通信服务 API
          • 工具链命令行参考(`fh2-plugin`)
        • 后端
          • 后端 API 参考
          • 后端插件源清单
          • 后端 HTTP 接口参考
          • 后端事件消费参考
          • 后端脚本上下文(`ctx`)
          • 后端数据存储
          • 流程编排语言
          • 流程触发器参考
          • 后端能力目录
          • 后端 HTTP API
        • 共享契约
          • 共享契约参考
          • 类型与契约目录
          • 错误模型
          • 版本兼容性
      • 附录
        • 错误码
        • 术语表
        • 常见问题
  1. 计划库宿主

计划库表单扩展

plan.form-extensions 让插件在飞行计划表单里加一块自定义区域:新建/复制计划时显示你的开关或配置,用户点确定时你的校验和提交逻辑会参与流程。这是扩展点,在清单 extensions 里声明,运行时用 ctx.extensions.contribute() 提交。
fh2-plugin-sdk 是插件开发 SDK;下文模块名用于说明,实际导入路径保留生成工程配置。模块名不表示已发布的 npm 包名。

提交语义(先读这个)#

模式渲染 UIvalidateonSubmitonEditSubmit
create / copy✅✅✅不调用
edit❌ 不渲染❌❌✅ 只调用这个
airport-test / workflow-*❌❌❌❌
即:新建和复制时你的界面、校验、补丁全流程参与;编辑已有计划时不渲染任何 UI,也不调 validate/onSubmit,只对实现了 onEditSubmit 的扩展按顺序调用它。编辑场景的数据在 form.biz_ext 里,插件要自己判断归属。

字段清单#

字段层说明
anchor清单,可选form-end(默认)或 task-strategy:after
planType清单,必填all / normal / atob / manual;all 匹配全部
render()代码,必填返回扩展区域界面
validate(input)代码,必填返回 { valid: true } 或 { valid: false, message }
onSubmit(input)代码,必填返回 Record<string, PluginValue> 字段补丁
onEditSubmit(input)代码,可选编辑模式的隐藏提交钩子,返回 void
静态字段(anchor、planType)只写清单,不传给 contribute()。
界面位置:task-strategy:after 在任务策略配置区之后;form-end 在所有原生字段之后、取消/确定按钮之前。anchor 只影响界面位置,不影响校验和提交顺序。

biz_ext 规则#

onSubmit 最常见的用途是往任务的 biz_ext 写入插件自己的业务数据。约束:
单插件最多 1 个顶层 key;同一任务最多 1 个插件声明非空 biz_ext(互斥,冲突会整体阻断提交)。
key 长度 ≤ 64 字符;整体 JSON 大小 ≤ 4KB(当前按字符串长度 4096 计算,含中文时留足余量)。
{ biz_ext: {} } 视为未声明归属。
补丁 key 与原生字段或其他插件冲突时,整个提交被阻断,不要返回 name、task_type 等原生字段。
完整冲突规则、校验顺序见校验、提交与 biz_ext。

提交入参结构#

interface PlanFormSubmitInput {
  context: {
    projectId: string
    mode: 'create' | 'copy'
    planType: 'normal' | 'atob' | 'manual'
    isMultiWayline: boolean
  }
  form: PlanFormDraft
}
PlanFormEditSubmitInput.context.mode 固定为 edit。PlanFormDraft 常用字段(只读):
核心:name、task_type、repeat_type、wayline_uuid、sn、landing_dock_sn、connect_sn?、out_of_control_action_in_flight、media_folder_id_paths
时间与重复:interval?、month?、dom?、dow?、wom?、begin_at?、end_at?、latest_begin_at?、time_zone?、continuous_task_periods? 等
航线与飞行:wayline_precision_type?、rth_altitude?、min_battery_capacity?、resumable_status?、breakpoint_resume_mode?、rth_mode?、wayline_list?、not_exceed_agl_enabled? 等
设备:device_type?、signal_type?、drone_sn?、payload_index?、wayline_start_position? 等
其他:biz_ext?、tags?、labels? 等
wayline_list 单项含 wayline_uuid、wayline_duration、wayline_distance、total_waypoints、wayline_name;wayline_start_position 含 longitude、latitude、height?。
注意:task_type 是数字枚举(1 立即、2 定时重复、3 单次定时、4 重复子任务、5 连续、6 连续子任务、7 机场试飞);repeat_type 同样是数字(0 不重复、1 秒、2 分钟、3 小时、4 天、5 周、6 按日期月、7 按星期月、8 年)。直接比较数字,不要自己重新解释。

可照抄示例#

fh2-plugin.json:
{
  "id": "exp_quality_plan",
  "name": "质检计划",
  "version": "1.0.0",
  "entry": "./plugin.js",
  "permissions": ["plan:form-context"],
  "extensions": [
    {
      "id": "quality-form",
      "point": "plan.form-extensions",
      "planType": "normal",
      "anchor": "task-strategy:after"
    }
  ],
  "views": {
    "quality-form": { "entry": "./views/quality-form.js" }
  }
}
// src/hosts/plan/activate.ts —— 在入口 plugin.ts 的计划宿主分支里调用(见「入口与生命周期」)
import { createViewState, definePluginView } from 'fh2-plugin-sdk'
import type { PluginContext } from 'fh2-plugin-sdk'
import type { PlanFormContextState } from 'fh2-plugin-sdk'

interface QualityFormContract {
  state: { enabled: boolean; contextAvailable: boolean }
  methods: { setEnabled(input: { enabled: boolean }): void }
}
const qualityForm = definePluginView<QualityFormContract>('quality-form')
const BIZ_KEY = 'quality-inspection'

export async function activatePlan (ctx: PluginContext) {
  const state = createViewState<QualityFormContract['state']>({
    enabled: false,
    contextAvailable: false,
  })
  const formContext = await ctx.capabilities.get('plan:form-context')
  let currentFormInstanceId = ''
  function applyContext (next: PlanFormContextState) {
    if (!next.available) {
      state.update(current => ({ ...current, contextAvailable: false }))
      return
    }
    const newForm = Boolean(
      next.formInstanceId && next.formInstanceId !== currentFormInstanceId,
    )
    if (newForm) currentFormInstanceId = next.formInstanceId ?? ''
    state.update(current => ({
      contextAvailable: true,
      enabled: newForm
        ? Boolean(next.sourceBizExt && BIZ_KEY in next.sourceBizExt)
        : current.enabled,
    }))
  }
  if (formContext) applyContext(await formContext.getState())
  const contextSubscription = formContext
    ? await formContext.onStateChanged(applyContext)
    : undefined

  await qualityForm.handle(ctx, {
    state,
    methods: {
      setEnabled: ({ enabled }) => {
        state.update(current => current.contextAvailable
          ? { ...current, enabled }
          : current)
      },
    },
  })

  await ctx.extensions.contribute('quality-form', {
    render: () => qualityForm.get(ctx),
    validate: async ({ context, form }) => {
      if (!state.get().enabled) return { valid: true }
      if (context.isMultiWayline) {
        return { valid: false, message: '质检任务暂不支持多航线' }
      }
      if (!form.wayline_uuid) {
        return { valid: false, message: '请先选择航线' }
      }
      return { valid: true }
    },
    onSubmit: async () => state.get().enabled
      ? { biz_ext: { [BIZ_KEY]: { version: 1 } } }
      : {},
    onEditSubmit: async ({ form }) => {
      if (!form.biz_ext || !(BIZ_KEY in form.biz_ext)) return
      ctx.logger.info('正在编辑本插件拥有的质检任务')
    },
  })

  return {
    async dispose () {
      await contextSubscription?.dispose()
    },
  }
}
完整示例见计划表单扩展示例。

常见问题#

现象原因与处理
编辑计划时插件区域不显示设计如此:edit 模式不渲染 UI,只调 onEditSubmit
点确定后提示字段冲突onSubmit 返回的 key 撞了原生字段或其他插件;只返回 biz_ext 等自有 key
复制计划后数据丢了sourceBizExt 只用于初始化界面;提交时必须在 onSubmit 重新写入
校验通过但任务没建成两个插件都写了非空 biz_ext 会整体阻断;只保留一个归属
validate 里的网络校验拖慢提交校验必须无副作用、快速返回;耗时检查放到 onSubmit 并保证幂等
界面渲染了但 planType 不匹配planType 是清单静态字段;确认清单声明与目标计划类型一致
修改于 2026-10-22 07:06:54
上一页
计划库表单上下文
下一页
校验、提交与 `biz_ext`
Built with