Page 1 of 1

PanelOutdoorCycle和AiEarphoneTemplate渲染失败

Posted: 2026年 Aug 12日 14:56
by epanda

渲染这两个模板的时候失败,让AI分析的时候给出的结论:

一、PanelOutdoorCycle:schema.ts 只声明了 1 个 DP,与代码实际使用的 DP 无交集

模板PanelOutdoorCyclepublic-sdm-template@1.0.2
下载地址https://static1.tuyacn.com/static/txp-raytpl-PanelOutdoorCycle/template.tar

症状

面板能渲染——地图、电量、"更多"入口都正常显示——但功能控件区整片空白
一个控件都不出现。

原因

src/devices/schema.ts 全文只声明了一个 DP:

Code: Select all

export const defaultSchema = [
  {
    id: 1,
    code: 'switch_1',
    name: '开关1',
    type: 'obj',
    property: { type: 'bool' },
    mode: 'rw',
    // ...
  },
] as const;

而同一模板的 src/constant/dpCodes.ts 列举了 40 个 DP code:

Code: Select all

mileageTotal: 'mileage_total',            // 总里程
enduranceMileage: 'endurance_mileage',    // 续航里程
blelockSwitch: 'blelock_switch',          // 防盗开关
batteryPercentage: 'battery_percentage',  // 电池电量
headlightSwitch: 'headlight_switch',      // 大灯开关
level: 'level',                           // 档位切换
mode: 'mode',                             // 模式切换
// ... 共 40 条

代码里有 24 处 supportDp(dpCodes.xxx) 调用,按这些 code 判断该不该渲染对应控件,
覆盖 mileageTotal / blelockSwitch / headlightSwitch / level / mode /
autoLightSwitch / moveAlarm / bucketLock 等。

两边没有任何交集 —— switch_1 不在那 40 个 code 里,那 40 个也全都不在 schema 里。
因此每一个 supportDp() 都返回 false,控件区必然为空。

期望

补全 src/devices/schema.ts,把代码实际用到的那些 DP(至少是 24 处 supportDp()
覆盖到的约 18 个 code)按完整结构声明进去,含 id / code / type / mode /
property


二、AiEarphoneTemplate:在 App 作用域调用了页面级 hook,导致整个面板白屏

模板AiEarphoneTemplateai-earphone-template@1.0.0
下载地址https://static1.tuyacn.com/static/txp-raytpl-AiEarphoneTemplate/template.tar
相关依赖@ray-js/panel-sdk@1.16.1 → 内嵌 @ray-core/framework-shared@0.3.9

症状

整页白屏,React 树完全没有挂载。控制台首个错误:

Code: Select all

TypeError: Cannot read properties of null (reading 'addLifecycle')
    at onLaunch → _render → updateContainer

原因

src/app.tsx:44,在 App 组件里调用了 usePanelConfig()

Code: Select all

const App = ({ children }) => {
  const [initialized, setInitialized] = useState(false);
  const [offlineUsage, setOfflineUsage] = useState(false);
  const panelConfig = hooks.usePanelConfig();   // ← 这里

usePanelConfig() 内部第一件事就是注册页面生命周期:

Code: Select all

// @ray-js/panel-sdk/lib/hooks/usePanelConfig/index.js
export function usePanelConfig() {
  const [panelConfig, setPanelConfig] = useState(__panelConfig);
  // ...
  usePageEvent('onReady', () => { ... });

usePageEvent 来自 @ray-core/framework-shared@0.3.9

Code: Select all

// esm/hooks.js
export function usePageEvent(eventName, callback) {
    var pageInstance = useContext(PageInstanceContext);
    useLayoutEffect(function () {
        if ('function' === typeof pageInstance.addLifecycle) {   // ← pageInstance 为 null
// esm/PageInstanceContext.js
var PageInstanceContext = React.createContext(null);   // ← 默认值就是 null

PageInstanceContext 的 Provider 只由页面包装器提供。App 在 onLaunch 阶段执行,
那时一个页面实例都还不存在,useContext 拿到默认值 null
typeof null.addLifecycle 直接抛 TypeError。异常发生在 updateContainer 期间,
整棵 React 树没能挂载,所以是白屏而不是局部报错。

期望

usePanelConfig()App 里移到页面组件中调用(或改用不依赖页面实例的方式
获取 panelConfig)。这是页面级 hook 的使用位置问题,与运行环境无关。

说明

这条调用链完全不经过 ty.*,所以任何小程序运行时 mock 都触及不到,
在真机上是否表现相同取决于时序,但从代码看 App 阶段必然拿不到页面实例。