v65/v66:六个空壳模块接线 + 桌面服务卡片

用户 2026-10-20:
「做几个和 syncCalendar 差不多的卡片。另外其他的功能也逐步完成……旅程的
新建旅程啊,车辆管理、出行方式、节点设置……资源也点不进去……任务也点不
进去,也没法完成……纪念日、新增纪念日……财务的显示也有问题……笔记现在
也点不进去,记笔记也记不了。」

v65 —— 六个模块(任务 / 资源 / 纪念日 / 笔记 / 账本 / 旅程)原本是**同一套
空壳模板**复制出来的:`FbPrimaryButton` 没有 `onTap`、`ListItem` 没有
`onClick`、设置菜单的 `action` 全是空串。这一版把它们全部接上真实接口:

· 任务:新建 / 编辑(`FbTaskEditSheet` + `FbTaskRepo`)、列表可点、勾完成
  (走 `/tasks/<id>/status` —— 服务端 `PATCH` **显式忽略** status,走 Patch
  会得到"看起来成功的假成功")、按看板筛选
· 资源:新增 / 查看 / 改 / 删(`FbResourceSheet` + `FbResourceRepo`);
  过滤维度从**并不存在的**「证件 / 资料」改成按权限与归属
  (`resources` 表没有 category 列,那个筛选一直是筛空的)
· 纪念日:新增 / 编辑(`FbDayEditSheet` + `FbDayRepo`);派生生日
  (id=0、来自名册)只给提示不给编辑 —— 点开 `/days/0` 必然 404
· 笔记:记一条 / 打开 / 改(`FbNoteSheet` + `FbNoteRepo`),正文按 blocks 存
· 账本:记一笔(`FbLedgerEntrySheet` + `FbLedgerRepo`);修掉财务显示自相
  矛盾 —— 资产/负债改回**服务端 summary**(原来端上按金额正负号重算,与
  服务端"负债取绝对值"的口径打架,会算出 净资产 ≠ 资产 − 负债)
· 旅程:题图卡片(封面 + 标题 + 城市)+ 行程 + 新建旅程 + 新建节点

服务端同时修掉两处**静默数据丢失**(网页端因提交格式凑巧没暴露,只有走
API 的客户端会中招):
· 新建笔记传的是 `body`,而 `Note::create()` 只读 `blocks` ⇒ 新笔记只有标题
· 改笔记时 `blocks` 恒为空,而 `Note::update()` 是整组替换 ⇒ 只改标题就把
  正文全删光;另修 `annivPeopleOf()`(`find()` 的 SQL 不含 people,原来那个
  兜底恒为空 ⇒ 只改纪念日标题会清空关联人)

v66 —— 桌面服务卡片(用户选了「桌面服务卡片」这一项):
· `FbFormAbility`(FormExtensionAbility)+ `form_config.json` 三块尺寸:
  2×2 今日概览 / 2×4 左右分栏 / 4×4 今日全景
· `FbCardData` 只打 `/home` 一个接口(与网页首页同源),数据**扁平化**后
  由 `formBindingData` 推给卡片;断网时用上次成功的缓存并注明"离线"
· 卡片是后台进程 ⇒ 请求带 `authProbe: true`:401 **不能**触发全局"踢回
  登录页"(那会顺手把正在用应用的人踢下线,v62 那次事故同一个坑)
· 点卡片 → `postCardAction(router, fbModule)` → `EntryAbility` 读 Want 参数
  直接落到对应模块;补了 `onNewWant`(没有它,第二次点卡片"什么也没发生")
· 4×4 右上角 ↻ 走 `postAction(message)` → `onFormEvent` 重取数再推回

验证:`hvigorw assembleHap` BUILD SUCCESSFUL;打包后的 hap 里确认
`FbFormAbility`(type=form)与 `$profile:form_config` 都在,卡片代码进了
`ets/widgets.abc`。静态守卫 730 条全绿,反向验证 110 条注入全部能让守卫变红。
⚠️ 设备仍断连(`hdc list targets` 为空),真机尚未验收。
This commit is contained in:
2026-10-20 15:27:32 +08:00
parent 3b7238a418
commit b3e1330244
32 changed files with 5572 additions and 227 deletions
+76
View File
@@ -0,0 +1,76 @@
/**
* 家书 FamilyBook · 任务的**写操作**(鸿蒙端)
*
* 对应服务端 `app/ApiHandler.php` 的 `tasks()`:
* POST /api/v1/tasks 新建(title / note / due_at / board_id / status)
* PATCH /api/v1/tasks/<id> 改(标题/备注/截止/看板/负责人/分类)
* POST /api/v1/tasks/<id>/status 改状态(todo / doing / done)
* DELETE /api/v1/tasks/<id> 删
*
* 🔴 用户 2026-10-20:「任务也是一样啊,任务也点不进去,也没法那个完成,
* 新建任务、设置这些东西都这都没有。」
* ⇒ 端上原来这三件事**一件都没接**(按钮没 onTap、列表项没 onClick)。
*
* ⚠️ **状态只走 `/status` 子动作**:服务端 `PATCH /tasks/<id>` 里
* `'status' => (string)$t['status']` 是**原样回填**,显式忽略请求里的 status
* (见 ApiHandler 那行注释「状态只走 /status 子动作」)。
* 所以端上 toggle 完成必须打 `/status`,打 PATCH 会"看起来成功但状态没变"。
*/
import { fbApi, FbApiResult, FbApiOptions } from './FbHttp';
/** 新建/编辑任务提交的字段 */
export interface FbTaskDraft {
title: string;
note: string;
/** `YYYY-MM-DD HH:MM`;空串 = 没有期限 */
dueAt: string;
/** 看板 id(0 = 不归指定看板 → 服务端落到「其他」那一列) */
boardId: number;
/** todo / doing / done(只在**新建**时生效;改状态走 `setTaskStatus`) */
status: string;
}
/** 新建任务 */
export async function createTask(token: string, d: FbTaskDraft): Promise<FbApiResult> {
/* ⚠️ ArkTS 的 `arkts-no-untyped-obj-literals`:请求体必须
`Record<string, Object> = {}` 逐键赋值(见 FbTripRepo 同一说明)。 */
const body: Record<string, Object> = {};
body['title'] = d.title;
body['note'] = d.note;
body['due_at'] = d.dueAt;
body['board_id'] = d.boardId;
body['status'] = d.status;
const opts: FbApiOptions = { method: 'POST', token: token, body: body };
return await fbApi('/api/v1/tasks', opts);
}
/**
* 改任务(标题 / 备注 / 截止 / 看板)。
*
* ⚠️ **不带 status**:服务端 PATCH 会忽略它(见文件头说明),
* 带上只会让人以为"改成功了",实际状态没动。
*/
export async function updateTask(token: string, id: number, d: FbTaskDraft): Promise<FbApiResult> {
const body: Record<string, Object> = {};
body['title'] = d.title;
body['note'] = d.note;
body['due_at'] = d.dueAt;
body['board_id'] = d.boardId;
const opts: FbApiOptions = { method: 'PATCH', token: token, body: body };
return await fbApi('/api/v1/tasks/' + id.toString(), opts);
}
/** 改状态(勾完成 / 取消完成 / 标记进行中) */
export async function setTaskStatus(token: string, id: number, status: string): Promise<FbApiResult> {
const body: Record<string, Object> = {};
body['status'] = status;
const opts: FbApiOptions = { method: 'POST', token: token, body: body };
return await fbApi('/api/v1/tasks/' + id.toString() + '/status', opts);
}
/** 删任务 */
export async function deleteTask(token: string, id: number): Promise<FbApiResult> {
const opts: FbApiOptions = { method: 'DELETE', token: token };
return await fbApi('/api/v1/tasks/' + id.toString(), opts);
}