杰理 SDK 文档中心
首页
首页
  • 项目概览与入门

    • 项目概述
    • 快速开始
  • OTA SDK 核心库

    • RCSP 认证库(jl_auth)
    • OTA 流程库(jl_ota)
    • RCSP-OTA 协议库(jl_rcsp_ota)
    • OTAWrapper 高层封装
  • 蓝牙通信与设备管理

    • 蓝牙连接生命周期管理
    • BLE 数据发送与 MTU 管理
    • 自动回连机制
  • 参考 Demo 小程序

    • 应用入口与页面导航
    • 设备连接页(pageConnect)
    • 固件升级页(pageUpdate)
    • 设置与调试页(pageSetting)
    • 自定义 UI 组件
    • 固件文件解析工具(upgradeFileUtil)
    • 日志系统

应用入口与页面导航

杰理 OTA 微信小程序(JLOTA)的应用启动入口与页面导航体系:包含 app.json 全局注册、自定义 tabBar 切换机制、页面层级关系以及蓝牙定位权限等全局配置。

Purpose and Scope

本页面向开发者完整说明 JLOTA 小程序的应用入口与页面导航机制,覆盖:

  • miniprogram/app.json 中的页面注册(pages)、自定义 tabBar(tabBar)、窗口样式(window)、蓝牙定位权限(permission)等全局入口配置;
  • custom-tab-bar 自定义导航栏组件的实现与切换逻辑(wx.switchTab);
  • 三大一级页面(连接 pageConnect、升级 pageUpdate、设置 pageSetting)与两个二级页面(pageBLEDataSet、pageCustomCmd)之间的导航层级关系。

与页面导航相关的蓝牙连接流程、OTA 升级状态机、BLE 数据设置 / 自定义指令的具体业务逻辑属于对应页面的能力,请参见仓库中 pages/pageConnect、pages/pageUpdate、pages/pageSetting 相关实现;底层协议库(lib/jl_lib/jl_ota_2.1.1.js、jl_rcsp_ota_2.1.1.js、jl_auth_2.0.0.js)属于 SDK 层,不在本页展开。

概述

JLOTA 是运行在微信小程序平台上的 OTA(Over-The-Air)升级工具,核心链路为「蓝牙连接设备 → 获取固件 → 执行升级」。整个应用以 3 个 tab 页 + 2 个设置二级页 组织:

  1. 连接页(pageConnect):应用首屏,负责扫描并连接蓝牙设备,是 OTA 链路的数据源头;
  2. 升级页(pageUpdate):展示固件升级进度与状态,依赖连接页建立的设备链路;
  3. 设置页(pageSetting):入口页,可继续下钻到 BLE 数据设置(pageBLEDataSet) 与 自定义指令(pageCustomCmd) 两个二级页面。

导航体系采用自定义 tabBar(tabBar.custom: true),由 custom-tab-bar 组件接管三个一级页面的切换,使 UI 与业务解耦、切换逻辑可控。入口配置集中声明在 app.json,微信小程序框架在启动时按 pages 数组顺序加载首屏页面,并按 tabBar.list 渲染底部导航。

架构

flowchart TD
    subgraph sg_Entry["入口层 (app.json)"]
        AppJson["app.json<br/>页面注册 / tabBar / window / permission"]
        Sitemap["sitemap.json<br/>搜索索引"]
    end

    subgraph sg_TabBar["导航层 (custom-tab-bar)"]
        TabBar["custom-tab-bar/index.js<br/>Component 自定义 tabBar"]
        SwitchTab["switchTab()<br/>wx.switchTab"]
    end

    subgraph sg_Pages["页面层 (pages)"]
        PageConnect["pages/pageConnect<br/>连接 (tab 0)"]
        PageUpdate["pages/pageUpdate<br/>升级 (tab 1)"]
        PageSetting["pages/pageSetting<br/>设置 (tab 2)"]
        PageBLEDataSet["pages/pageSetting/pageBLEDataSet<br/>BLE 数据设置 (二级页)"]
        PageCustomCmd["pages/pageSetting/pageCustomCmd<br/>自定义指令 (二级页)"]
    end

    subgraph sg_Lib["支撑层 (lib / components)"]
        OtaLib["lib/jl_lib/jl_ota_2.1.1.js"]
        RcspLib["lib/jl_lib/jl_rcsp_ota_2.1.1.js"]
        AuthLib["lib/jl_lib/jl_auth_2.0.0.js"]
        Util["lib/util.js"]
        Components["components/<br/>otaProgressView / timesSelectView / waittingView"]
    end

    AppJson -->|"声明 pages["0"] 为首屏"| PageConnect
    AppJson -->|"tabBar.custom: true"| TabBar
    TabBar -->|"switchTab 切换"| PageConnect
    TabBar -->|"switchTab 切换"| PageUpdate
    TabBar -->|"switchTab 切换"| PageSetting
    PageSetting -->|"navigateTo 下钻"| PageBLEDataSet
    PageSetting -->|"navigateTo 下钻"| PageCustomCmd
    PageConnect --> OtaLib
    PageUpdate --> OtaLib
    PageUpdate --> RcspLib
    PageConnect --> AuthLib
    PageConnect --> Util
    PageUpdate --> Components
    PageSetting --> Components

架构说明:

  • 入口层:app.json 是微信小程序框架的启动契约,声明了全部 5 个页面、自定义 tabBar 的三项配置、全局窗口样式与蓝牙定位权限;sitemap.json 控制页面是否可被微信索引。
  • 导航层:custom-tab-bar 是一个全局自定义组件,微信框架会在 tabBar.custom: true 时自动渲染它;其 switchTab 方法通过 wx.switchTab 完成三个一级页面之间的切换,并同步维护高亮索引 selected。
  • 页面层:三个 tab 页构成应用主框架;两个二级页从设置页 navigateTo 进入,属于下钻型页面(不在 tabBar 中)。
  • 支撑层:连接/升级页依赖 lib/jl_lib 下的认证与 OTA 协议库,页面内复用了 components 下的进度视图、时间选择、等待视图等 UI 组件——它们不参与导航,但为页面能力提供支撑。

核心流程

sequenceDiagram
    participant F as 微信框架 (WeChat Runtime)
    participant A as app.json
    participant T as custom-tab-bar
    participant P0 as pageConnect (连接)
    participant P1 as pageUpdate (升级)
    participant P2 as pageSetting (设置)

    F->>A: 启动时读取全局配置
    A-->>F: pages / tabBar / window / permission
    F->>P0: 加载 pages[0] 作为首屏
    F->>T: tabBar.custom=true,实例化自定义导航
    T-->>F: 渲染 3 个 tab 项 (list + selected=0)

    Note over P0,P1: 用户点击 tab 项
    T->>T: switchTab(e) 读取 dataset.path
    T->>F: wx.switchTab(url=../../pages/...)
    F->>P1: 切换并缓存页面栈 (仅保留 tab 页)
    T->>T: setData(selected: index) 更新高亮

    Note over P2: 用户从设置页下钻
    P2->>F: wx.navigateTo(二级页)
    F->>P2: 压栈打开 pageBLEDataSet / pageCustomCmd

流程要点:

  1. 小程序冷启动时,微信框架解析 app.json,按 pages 数组顺序注册页面,并将 pages[0](pageConnect)作为默认首页渲染。
  2. 由于 tabBar.custom 为 true,框架不再使用内置 tabBar,而是寻找 custom-tab-bar 目录下的自定义组件进行渲染。
  3. 用户点击 tab 项触发 switchTab:读取 dataset.path 拼出页面路径后调用 wx.switchTab。switchTab 会关闭所有非 tab 页面并把页面栈收敛到目标 tab 页,保证 tab 间不累积页面栈。
  4. 切换成功后组件通过 setData({ selected: index }) 更新选中态,配合 selectedColor(#398BFF)高亮当前 tab。
  5. 设置页内的二级页(pageBLEDataSet、pageCustomCmd)不在 tabBar 中,使用普通压栈导航(navigateTo),可通过返回手势/按钮回到设置页。

应用入口:app.json 全局注册

miniprogram/app.json 是整个小程序的入口契约,微信框架启动时首先解析该文件。其完整内容如下:

{
  "pages": [
    "pages/pageConnect/pageConnect",
    "pages/pageUpdate/pageUpdate",
    "pages/pageSetting/pageSetting",
    "pages/pageSetting/pageBLEDataSet/pageBLEDataSet",
    "pages/pageSetting/pageCustomCmd/pageCustomCmd"
  ],
  "tabBar": {
    "custom": true,
    "color": "#808080",
    "selectedColor": "#398BFF",
    "list": [
      {
        "pagePath": "pages/pageConnect/pageConnect",
        "text": "连接",
        "iconPath": "/images/tab_icon_bt_nol.png",
        "selectedIconPath": "/images/tab_icon_bt_sel.png"
      },
      {
        "pagePath": "pages/pageUpdate/pageUpdate",
        "text": "升级",
        "iconPath": "/images/tab_icon_update_nol.png",
        "selectedIconPath": "/images/tab_icon_update_sel.png"
      },
      {
        "pagePath": "pages/pageSetting/pageSetting",
        "text": "设置",
        "iconPath": "/images/tab_icon_settle_nol.png",
        "selectedIconPath": "/images/tab_icon_settle_sel.png"
      }
    ]
  },
  "window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#fff",
    "navigationBarTitleText": "杰理OTA升级",
    "navigationBarTextStyle": "black"
  },
  "permission": {
    "scope.userLocation": {
      "desc": "你的位置信息将用于发现蓝牙设备"
    }
  },
  "style": "v2",
  "sitemapLocation": "sitemap.json"
}

Source: app.json

页面注册(pages)

pages 数组声明了应用的全部 5 个页面,数组第一项即冷启动默认首页:

顺序页面路径角色是否在 tabBar
0pages/pageConnect/pageConnect蓝牙连接(首屏)✅ tab 0「连接」
1pages/pageUpdate/pageUpdate固件升级✅ tab 1「升级」
2pages/pageSetting/pageSetting设置入口✅ tab 2「设置」
3pages/pageSetting/pageBLEDataSet/pageBLEDataSetBLE 数据设置(二级页)❌
4pages/pageSetting/pageCustomCmd/pageCustomCmd自定义指令(二级页)❌

设计意图:将「连接 → 升级」这两个 OTA 强相关流程放在最前两个 tab,符合用户使用动线——先连设备、再升级;设置页收纳低频操作并继续下钻两个二级页,避免 tabBar 拥挤。

自定义 tabBar 开关(tabBar.custom)

tabBar.custom: true 是关键开关:它指示微信框架不渲染内置 tabBar,而是查找根目录下的 custom-tab-bar 自定义组件来接管导航 UI。与此同时,color / selectedColor 仍作为「未选中 / 选中」文字颜色基线(与组件内 data 保持一致),list 中每项声明了 pagePath(页面路径)、text(文案)与 iconPath / selectedIconPath(未选中/选中图标)。

全局窗口与权限

  • window.navigationBarTitleText: "杰理OTA升级" 为所有页面提供默认导航栏标题;navigationBarBackgroundColor: "#fff" 配 navigationBarTextStyle: "black" 为白底黑字。
  • permission.scope.userLocation 声明了地理位置授权用途:「你的位置信息将用于发现蓝牙设备」。这是因为 iOS 上扫描 BLE 外设依赖系统定位权限,此声明是小程序蓝牙发现能力的合规前置条件。
  • style: "v2" 启用新版组件样式;sitemapLocation: "sitemap.json" 指向搜索索引配置。

自定义 tabBar 导航实现

custom-tab-bar/index.js 是导航层核心,它是一个 Component 定义的自定义组件,逻辑如下:

Component({
  data: {
    selected: 0,
    enable:true,
    "color": "#808080",
    "selectedColor": "#398BFF",
    list:[{
      "pagePath": "pages/pageConnect/pageConnect",
      "text": "连接",
      "iconPath": "/images/tab_icon_bt_nol.png",
      "selectedIconPath": "/images/tab_icon_bt_sel.png"
    },
    {
      "pagePath": "pages/pageUpdate/pageUpdate",
      "text": "升级",
      "iconPath": "/images/tab_icon_update_nol.png",
      "selectedIconPath": "/images/tab_icon_update_sel.png"
    },
    {
      "pagePath": "pages/pageSetting/pageSetting",
      "text": "设置",
      "iconPath": "/images/tab_icon_settle_nol.png",
      "selectedIconPath": "/images/tab_icon_settle_sel.png"
    }]
  },
  attached() {
  },
  methods: {
    switchTab(e) {
      if(!this.data.enable)return
      const data = e.currentTarget.dataset
      const url = data.path
      wx.switchTab({url:"../../"+url})
      this.setData({
        selected: data.index
      })
    }
  }
})

Source: custom-tab-bar/index.js

关键实现细节

  • data 自包含:list、color、selectedColor 在组件内重复声明了与 app.json 一致的数据。原因是自定义 tabBar 的 data 会直接用于渲染,微信官方推荐在组件内维护 list 副本(也可在 attached 中从 app.json 读取)。这里选择静态冗余,保证渲染数据不依赖运行时读取。
  • switchTab(e):从 e.currentTarget.dataset 取 path 与 index(由 wxml 侧绑定,如 data-path="pages/pageUpdate/pageUpdate" data-index="1")。先用 enable 标志做开关闸门(if(!this.data.enable)return,可用于升级中锁定导航),再以 wx.switchTab({url: "../../" + url}) 完成页面切换——前缀 ../../ 是因为组件位于 custom-tab-bar/ 目录,页面路径需回退两级到 miniprogram/ 根再拼接。
  • 选中态同步:切换后 setData({ selected: data.index }) 更新高亮索引,由 wxml 配合 selectedColor 渲染选中样式。
  • attached 为空:生命周期钩子留空,说明组件不依赖异步初始化,启动即就绪。

为什么要用自定义 tabBar

  1. 可控的导航闸门:enable 标志可在升级进行中等场景下拦截 tab 切换,内置 tabBar 无法做到;
  2. UI 自由度:图标、文案、高亮样式完全由组件 wxml/wxss 控制,与品牌风格一致;
  3. 与业务状态联动:可在 switchTab 前后插入业务逻辑(校验、统计、重置状态),将导航收敛为可编程行为。

页面层级与导航关系

三个 tab 页与两个二级页的导航关系如下:

flowchart LR
    subgraph sg_Tabs["tab 一级页 (switchTab)"]
        PC["pageConnect 连接"]
        PU["pageUpdate 升级"]
        PS["pageSetting 设置"]
    end
    subgraph sg_Sub["二级页 (navigateTo)"]
        BD["pageBLEDataSet BLE数据设置"]
        CC["pageCustomCmd 自定义指令"]
    end
    PC <--> PU
    PC <--> PS
    PU <--> PS
    PS -->|"下钻"| BD
    PS -->|"下钻"| CC
    BD -.->|"返回"| PS
    CC -.->|"返回"| PS
  • 一级页之间:仅通过 custom-tab-bar 的 wx.switchTab 切换,页面栈中同时只保留 tab 页,切换时非 tab 页面全部销毁——这是微信框架对 switchTab 的固有语义,天然避免内存泄漏。
  • 一级页 → 二级页:设置页通过 wx.navigateTo 压栈打开 pageBLEDataSet / pageCustomCmd;二级页不注册进 tabBar,因此拥有独立导航栏(可返回),与 tab 切换互不干扰。
  • 业务依赖:pageUpdate 依赖 pageConnect 建立的 BLE 连接链路(连接数据保存在全局/缓存中),因此把「连接」放在 tab 0 作为动线起点;pageBLEDataSet / pageCustomCmd 为开发者调试与协议测试提供入口,属低频工具页。

配置选项

app.json 全局配置

配置项类型默认值(本仓库实际值)说明
pagesstring[]5 个页面路径全部页面注册,首项为启动首页
tabBar.custombooleantrue启用自定义 tabBar(custom-tab-bar 组件接管渲染)
tabBar.colorstring"#808080"未选中 tab 文字颜色
tabBar.selectedColorstring"#398BFF"选中 tab 文字颜色(品牌蓝)
tabBar.list[].pagePathstring见上表tab 对应的页面路径(必须已在 pages 注册)
tabBar.list[].textstring连接 / 升级 / 设置tab 文案
tabBar.list[].iconPathstring/images/tab_icon_*_nol.png未选中图标
tabBar.list[].selectedIconPathstring/images/tab_icon_*_sel.png选中图标
window.navigationBarTitleTextstring"杰理OTA升级"全局默认导航栏标题
window.navigationBarBackgroundColorstring"#fff"导航栏背景色
window.navigationBarTextStylestring"black"导航栏文字颜色
window.backgroundTextStylestring"light"下拉背景样式
permission.scope.userLocation.descstring"你的位置信息将用于发现蓝牙设备"定位权限用途说明(BLE 扫描前置条件)
stylestring"v2"新版组件样式
sitemapLocationstring"sitemap.json"搜索索引配置位置

custom-tab-bar 组件 data

字段类型默认值说明
selectednumber0当前高亮 tab 索引(与 list 下标对应)
enablebooleantrue导航闸门;为 false 时 switchTab 直接返回,可阻止切换
colorstring"#808080"未选中颜色(与 app.json 保持一致)
selectedColorstring"#398BFF"选中颜色(与 app.json 保持一致)
listobject[]3 个 tab 项每项含 pagePath/text/iconPath/selectedIconPath

注意:tabBar.list 与组件 data.list 是两处独立的数据源,修改 tab 项时必须同步更新 app.json 与 custom-tab-bar/index.js(及其 wxml/wxss 的图标资源),否则会出现「框架注册页与渲染项不一致」的问题。

失败模式、边界情况与并发

  • wx.switchTab 路径前缀错误:switchTab 要求 url 必须以 / 开头或为合法相对路径。组件中拼接 "../../" + url(如 ../../pages/pageUpdate/pageUpdate),若移动 custom-tab-bar 目录位置或修改页面路径,前缀需同步调整,否则 wx.switchTab 直接失败并抛出 fail 回调。
  • tab 页必须注册且路径一致:tabBar.list[].pagePath 引用的页面若未出现在 pages 数组中,或大小写/路径不一致,框架启动即报错;switchTab 目标也必须是 tab 页,否则调用无效。
  • 升级中的导航竞态:enable 标志为导航提供了互斥闸门。升级进行中(pageUpdate 处于升级状态机)若业务将 enable 置为 false,用户点击其他 tab 会被静默忽略(return),避免升级流程被页面切换打断导致状态丢失。这是典型的「UI 事件与后台任务互斥」设计。
  • 自定义 tabBar 高亮不同步:由于 selected 由组件内部维护,若用户通过其他入口(如 wx.reLaunch)直接跳转 tab 页,组件 selected 不会自动跟随,可能出现高亮与实际页面不符。解决方向是在页面 onShow 中向组件同步 selected(本仓库 attached 为空、未做同步,属于已知简化点)。
  • iOS 定位权限缺失:permission.scope.userLocation 仅完成声明,若用户在系统设置中拒绝定位授权,iOS 上 BLE 扫描将无法发现外设——这是连接页(tab 0)的硬前置条件,失败需在页面层给出引导。
  • switchTab 的页面栈语义:switchTab 会关闭所有非 tab 页面并保留目标 tab 页,因此不能用它打开 pageBLEDataSet / pageCustomCmd 这类二级页;二级页必须走 navigateTo。若误用会得到「fail: can not switch to non-tabBar page」错误。

性能与运维注意点

  • tab 页常驻内存:三个 tab 页在首次打开后常驻页面栈,升级流程(pageUpdate)中若有高频 UI 刷新(如 OTA 进度条),注意避免在非活动 tab 页中做无效渲染;组件 otaProgressView 等被设计为可复用视图,正是为降低页面复杂度。
  • 静态冗余的成本:custom-tab-bar 内重复维护 list/颜色配置是「以少量重复换取渲染确定性」,但新增 tab 或改图标时需三处联动(app.json、组件 js、组件 wxml/wxss),建议在代码注释中标注同步点。
  • 首屏策略:pages[0](pageConnect)为冷启动首页,蓝牙扫描初始化较重,若首屏白屏时间过长,可考虑在 pageConnect 的 onLoad 中延迟初始化扫描、优先渲染 UI。

扩展点

  • 导航闸门(enable):switchTab 已内置 enable 拦截点,可在升级状态机运行期间由 pageUpdate 通过全局事件或 getApp() 访问组件实例并置 enable=false,实现「升级中禁止切页」。
  • tab 项扩展:在 app.json 的 tabBar.list 与组件 data.list 中同步追加项即可扩展 tab(上限 5 个),并补充对应页面与图标资源。
  • 选中态同步:如需让 selected 跟随任意入口跳转,可在每个 tab 页 onShow 中调用 this.getTabBar().setData({ selected: N }),这是微信官方推荐的自定义 tabBar 同步模式,可作为本仓库的后续增强。
  • 二级页扩展:设置页下钻模式(navigateTo)可继续扩展更多调试页(如日志页、设备信息页),只需在 pages 数组中注册新路径并保持 pageSetting 作为唯一入口。

相关链接

  • app.json(应用入口配置)
  • custom-tab-bar/index.js(自定义导航实现)
  • custom-tab-bar/index.json
  • sitemap.json(搜索索引)
  • pageConnect 页面配置
  • pageUpdate 页面配置
  • pageSetting 页面配置
  • pageBLEDataSet 页面配置
  • pageCustomCmd 页面配置
  • project.config.json(项目/开发者工具配置)

相关能力页面:蓝牙连接流程(pageConnect)、OTA 升级状态机(pageUpdate)、BLE 数据设置与自定义指令(pageSetting 二级页)的深入实现请查看对应页面源码。

Next
设备连接页(pageConnect)