Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

微信小程序:把完整组件树接到 XML

开始本章前,Web 端应当已经完成 Group + Todo + Upsert。小程序不是重新写 CRUD,而是为同一组 OakComponent 增加 index.xmlindex.lessindex.config.ts

1. 添加 workspace

npx oak-cli add mp wechatMp
npm install

生成脚本后可以运行:

npm run start:mp:wechatMp
npm run build:mp:wechatMp

2. 保持同一棵节点树

frontend/home Group ListNode
└── <group-panel oakPath="...groupId">
    └── <todo-list oakPath="...todo$group">
        └── <todo-upsert oakPath="...todoId">

小程序页面仍然复用 Web 已写好的 index.ts。只为事件对象增加窄类型适配,例如:

type MpDatasetEvent = {
    currentTarget: {
        dataset: {
            id?: string;
        };
    };
};

不要使用 any 接收小程序事件。

3. 配置页面组件

frontend/home/index.config.tsmp 中声明:

mp: {
    navigationBarTitleText: '任务清单',
    enablePullDownRefresh: false,
    usingComponents: {
        'group-panel': '../../../components/group/panel/index',
        'group-upsert': '../../../components/group/upsert/index',
    },
},

组件自己的配置使用 CreateComponentConfig

import { CreateComponentConfig } from '@oak-frontend-base/config';

export default CreateComponentConfig({
    mp: {
        usingComponents: {
            'todo-list': '../../todo/list/index',
        },
    },
});

不要手改生成后的页面 JSON。

4. 首页 tabs 与 Group Modal

核心 XML:

<scroll-view scroll-x="{{true}}">
    <button
        wx:for="{{groups}}"
        wx:key="id"
        data-id="{{item.id}}"
        bindtap="selectGroupFromMp"
    >
        {{item.name || t('unnamedGroup')}}
    </button>
</scroll-view>

<group-panel
    wx:if="{{selectedGroupId && !editingGroupId}}"
    oakPath="{{oakFullpath + '.' + selectedGroupId}}"
/>

<view wx:if="{{editingGroupId}}" class="modalMask">
    <group-upsert oakPath="{{oakFullpath + '.' + editingGroupId}}" />
    <button bindtap="cancelGroupFromMp">{{t('cancel')}}</button>
    <button bindtap="saveGroupFromMp">{{t('save')}}</button>
</view>

selectedGroupIdeditingGroupId 必须在 data 中预声明。XML 检查不会接受模板中凭空出现的字段。

5. Upsert XML

Group Upsert 的 index.ts 同时声明初始 data 和 formData:

data: {
    name: '',
},
formData({ data }) {
    return { name: data?.name || '' };
},

XML:

<input
    value="{{name}}"
    placeholder="请输入分组名称"
    bindinput="setNameFromMp"
/>

Todo Upsert 同理声明 titlecompleted,再使用 <input><switch>

6. Todo 关系列表

GroupPanel:

<todo-list oakPath="{{oakFullpath + '.todo$group'}}" />

TodoList 新增后挂 Upsert:

<todo-upsert
    wx:if="{{editingTodoId}}"
    oakPath="{{oakFullpath + '.' + editingTodoId}}"
/>

保存仍调用共享的 saveTodo(),由它执行父 Group path。小程序端不要另外手工拼 groupId

7. 构建检查

npm run build
npm run build:mp:wechatMp

目标输出应包含:

[oak-xml-check] ... 0 XML diagnostics, 0 TypeScript diagnostics
0 个提示

重点处理:

  • OAK_MP_XML_UNDECLARED_DATA:字段没有在 data/properties/formData 合同中声明;
  • 组件属性不存在:检查 usingComponents 指向的真实 OakComponent 及其 properties
  • 事件不存在:检查 XML 的 bind* 名称是否与 methods 一致;
  • oakPath 拼错:检查最终树路径,尤其是 todo$group

8. 验证清单

在微信开发者工具中逐项验证:

  1. 新增 Group 并重进页面;
  2. tabs 切换后 Todo 相互隔离;
  3. 新增 Todo 后数据库得到正确 groupId
  4. 编辑、完成切换、删除均持久化;
  5. 取消 Modal 不产生记录。

完成后提交:

git add .
git commit -m "feat: add grouped Todo mini-program UI"