只读日历的日程在首页仍显示「编辑日程」按钮,点进去改完保存才报无权限 #31

Closed
opened 2026-10-29 12:33:55 +00:00 by laoyang · 1 comment
Owner

现象

真机(DEL-AL20 / HarmonyOS 7.0.0.109)实测:

首页「今天的日程」里点开条目「生成式人工智能」→ 详情卡底部出现「编辑日程」按钮 →
点进编辑表单,改标题 → 点「保存」→ 报错:

保存失败:这本日历你没有写权限。

该日程属于「工作学习」日历本,当前账号对其没有写权限。

诉求

只读日程不应出现「编辑入口」。 用户明确选择「只读日程隐藏编辑按钮」。

理由:现在等于让用户白改一遍才告知没权限,体验很差;应在入口处就挡住。

根因(代码级)

FbHomePage.ets:1501 把 canEdit 写死成 true:

FbEventSheet({
  canEdit: true,     // ← 无条件给编辑入口
  onEdit: (): void => { this.editEventFromDetail(); }
})

FbEventSheet.ets:427 只按这个布尔值渲染:

if (this.canEdit) { Text('编辑日程').onClick(() => this.onEdit()) }

对比日历页(做对了一半):

// FbCalendarPage.ets:902
canEdit: this.detailBase !== null && isEditableEvent(this.detailBase),

而 isEditableEvent()(Types.ets:640)只判 kind/id/calId,不判写权限:

export function isEditableEvent(e: FbCalEvent): boolean {
  return e.kind === 'custom' && e.id > 0 && e.calId > 0;
}

所以即便照搬日历页的写法,只读本上的日程仍会被判为可编辑。

写权限在 FbCalBook.canWrite(Types.ets:650),由
FbCalRepo.fetchBooks() 依据 /api/v1/calendar/books 的 kind + perm 算出
(canWrite: !isReadonlyKind(kind) && perm !== 'read')。

修复方案(已确认)

按日程所属日历本的 canWrite 决定是否显示编辑入口:

  • 首页 canEdit 改为「是自定义日程 + 该日程的 calId 在我可写的日历本集合里」
  • 日历页同步补上 canWrite 判定,两处口径一致
  • 详情卡打开时若 calBooks 尚未加载,需先加载(当前是懒加载,只在新建/编辑时才拉)

验收标准

  1. 只读日历本里的日程,详情卡不出现「编辑日程」(首页 + 日历页一致)
  2. 自己有写权限的日程,「编辑日程」正常出现,编辑保存成功
  3. 重新编译通过,官方 hap-sign-tool 验签通过
  4. 真机复验两条路径

关联

  • 由 #30(WSL 编译 + 真机测试)实测发现
  • 与 #29(资源模块详情/编辑分离)同源思路:能做什么,在入口就说清

为什么单独建单

这是日程模块的功能缺陷(只读日历不该给编辑入口),需改 ArkTS 代码并重新编译;#30 是 WSL 编译环境迁移,本缺陷只是在那次真机测试中被发现,#29 是资源模块的交互分离,三者改的文件和验收标准都不同。若并入 #30 会让环境迁移单无法收尾。

## 现象 真机(DEL-AL20 / HarmonyOS 7.0.0.109)实测: 首页「今天的日程」里点开条目「生成式人工智能」→ 详情卡底部**出现「编辑日程」按钮** → 点进编辑表单,改标题 → 点「保存」→ 报错: ``` 保存失败:这本日历你没有写权限。 ``` 该日程属于「工作学习」日历本,当前账号对其没有写权限。 ## 诉求 **只读日程不应出现「编辑入口」。** 用户明确选择「只读日程隐藏编辑按钮」。 理由:现在等于让用户白改一遍才告知没权限,体验很差;应在入口处就挡住。 ## 根因(代码级) `FbHomePage.ets:1501` 把 `canEdit` 写死成 `true`: ```ts FbEventSheet({ canEdit: true, // ← 无条件给编辑入口 onEdit: (): void => { this.editEventFromDetail(); } }) ``` `FbEventSheet.ets:427` 只按这个布尔值渲染: ```ts if (this.canEdit) { Text('编辑日程').onClick(() => this.onEdit()) } ``` 对比**日历页**(做对了一半): ```ts // FbCalendarPage.ets:902 canEdit: this.detailBase !== null && isEditableEvent(this.detailBase), ``` 而 `isEditableEvent()`(`Types.ets:640`)**只判 kind/id/calId,不判写权限**: ```ts export function isEditableEvent(e: FbCalEvent): boolean { return e.kind === 'custom' && e.id > 0 && e.calId > 0; } ``` 所以即便照搬日历页的写法,只读本上的日程仍会被判为可编辑。 写权限在 `FbCalBook.canWrite`(`Types.ets:650`),由 `FbCalRepo.fetchBooks()` 依据 `/api/v1/calendar/books` 的 `kind` + `perm` 算出 (`canWrite: !isReadonlyKind(kind) && perm !== 'read'`)。 ## 修复方案(已确认) 按日程所属日历本的 `canWrite` 决定是否显示编辑入口: - 首页 `canEdit` 改为「是自定义日程 + 该日程的 calId 在我可写的日历本集合里」 - 日历页同步补上 `canWrite` 判定,两处口径一致 - 详情卡打开时若 `calBooks` 尚未加载,需先加载(当前是懒加载,只在新建/编辑时才拉) ## 验收标准 1. 只读日历本里的日程,详情卡**不出现**「编辑日程」(首页 + 日历页一致) 2. 自己有写权限的日程,「编辑日程」正常出现,编辑保存成功 3. 重新编译通过,官方 hap-sign-tool 验签通过 4. 真机复验两条路径 ## 关联 - 由 #30(WSL 编译 + 真机测试)实测发现 - 与 #29(资源模块详情/编辑分离)同源思路:能做什么,在入口就说清 --- ### 为什么单独建单 这是日程模块的功能缺陷(只读日历不该给编辑入口),需改 ArkTS 代码并重新编译;#30 是 WSL 编译环境迁移,本缺陷只是在那次真机测试中被发现,#29 是资源模块的交互分离,三者改的文件和验收标准都不同。若并入 #30 会让环境迁移单无法收尾。
Author
Owner

已修复并真机验证通过

commit f90cae0(已推送 fd3ea53..f90cae0 main -> main),3 文件 +54/-4。

改法

1. Types.ets 新增 isEventWritable(e, books)

export function isEventWritable(e: FbCalEvent, books: FbCalBook[]): boolean {
  if (!isEditableEvent(e)) { return false; }
  for (const b of books) {
    if (b.id === e.calId) { return b.canWrite; }
  }
  return false;   // 找不到这本 ⇒ 不认(权限被撤 / 本被删 / books 未加载)
}

刻意复用 FbCalBook.canWrite,不自建一套权限推导 ——
那个值由 FbCalRepo.fetchBooks() 依 /calendar/books 的 kind + perm 算出
(canWrite: !isReadonlyKind(kind) && perm !== 'read'),
两处各写一遍必然漂移。

2. FbHomePage.ets:canEdit 写死 true → 按写权限判定

-          canEdit: true,
+          canEdit: this.evtBase !== null && isEventWritable(this.evtBase, this.calBooks),

3. FbHomePage.ets:openEvent 里预加载 calBooks

这一步是必须的,否则会从"该显示的不显示"翻车成另一个 bug:

calBooks 原本是懒加载(只在新建/编辑时才拉),刚打开详情时它还是空数组,
isEventWritable 一律返回 false ⇒ 本来能编辑的日程也会被误判成只读。
所以在 openEvent 开头补一次拉取(ensureCalBooks 内部有缓存判断,拉过就复用),
不 await —— 详情内容先出来,按钮随 books 到位再显示,不拖慢打开速度。

4. FbCalendarPage.ets:同步改(原来只判 isEditableEvent,同样缺写权限判定)

-          canEdit: this.detailBase !== null && isEditableEvent(this.detailBase),
+          canEdit: this.detailBase !== null && isEventWritable(this.detailBase, this.books),

本页 books 在页面加载时就拉了(loadBooks),不会有空数组误判。

编译与签名

> hvigor BUILD SUCCESSFUL in 15 s 517 ms
33 tasks in total: 17 executed, 16 up-to-date
Digest verify result: true, DigestAlgorithm: SHA-256
verify: Verify success
verify-app success

真机复验(DEL-AL20 / HarmonyOS 7.0.0.109)

装包:install bundle successfully。

① 只读日程(「生成式人工智能」/ 工作学习本)

🔴 核心验证:『编辑日程』按钮是否还存在 ? False
详情卡按钮区: ['导航', '导航']

只剩「导航」,编辑入口已消失。✅

② 有写权限的日程(「清理万向轮充电座」/ 我的日程本)

🔴 可写日程应有『编辑日程』? True
   「编辑日程」 (540,1449)

没有被误杀,编辑入口正常。✅

③ 端到端保存 + 持久化

标题改为「清理万向轮充电座-V31」→ 保存 → 无报错 → 杀进程重启:

🔴 重启后仍含 V31 ? ['清理万向轮充电座-V31']

确认真写入服务端(不是本地乐观更新)。✅

④ 测试数据已还原

改完直接从库里改回原名(calendar_events.id=1056),
手机端复核 含 V31 残留? 无 ✓、该条标题: ['清理万向轮充电座']。
临时脚本已删,服务器和本地都没留档。

顺带确认的一条

之前真机上「保存失败:这本日历你没有写权限。」的提示文案是准确的 ——
问题不在服务端(它拒得对),纯粹是端上不该给这个入口。

收尾

工单验收 4 条全部满足,关闭。

另:这一轮实测还顺带验证了「编辑就地改、不跳页」(v86 的设计)确实生效 ——
点「编辑日程」后表单叠在详情卡上,页面没跳。

## 已修复并真机验证通过 commit `f90cae0`(已推送 `fd3ea53..f90cae0 main -> main`),3 文件 +54/-4。 ### 改法 **1. `Types.ets` 新增 `isEventWritable(e, books)`** ```ts export function isEventWritable(e: FbCalEvent, books: FbCalBook[]): boolean { if (!isEditableEvent(e)) { return false; } for (const b of books) { if (b.id === e.calId) { return b.canWrite; } } return false; // 找不到这本 ⇒ 不认(权限被撤 / 本被删 / books 未加载) } ``` 刻意**复用 `FbCalBook.canWrite`**,不自建一套权限推导 —— 那个值由 `FbCalRepo.fetchBooks()` 依 `/calendar/books` 的 `kind` + `perm` 算出 (`canWrite: !isReadonlyKind(kind) && perm !== 'read'`), 两处各写一遍必然漂移。 **2. `FbHomePage.ets`:`canEdit` 写死 true → 按写权限判定** ```diff - canEdit: true, + canEdit: this.evtBase !== null && isEventWritable(this.evtBase, this.calBooks), ``` **3. `FbHomePage.ets`:`openEvent` 里预加载 `calBooks`** 这一步是必须的,否则会**从"该显示的不显示"翻车成另一个 bug**: `calBooks` 原本是懒加载(只在新建/编辑时才拉),刚打开详情时它还是空数组, `isEventWritable` 一律返回 false ⇒ **本来能编辑的日程也会被误判成只读**。 所以在 `openEvent` 开头补一次拉取(`ensureCalBooks` 内部有缓存判断,拉过就复用), 不 await —— 详情内容先出来,按钮随 books 到位再显示,不拖慢打开速度。 **4. `FbCalendarPage.ets`:同步改**(原来只判 `isEditableEvent`,同样缺写权限判定) ```diff - canEdit: this.detailBase !== null && isEditableEvent(this.detailBase), + canEdit: this.detailBase !== null && isEventWritable(this.detailBase, this.books), ``` 本页 `books` 在页面加载时就拉了(`loadBooks`),不会有空数组误判。 ### 编译与签名 ``` > hvigor BUILD SUCCESSFUL in 15 s 517 ms 33 tasks in total: 17 executed, 16 up-to-date ``` ``` Digest verify result: true, DigestAlgorithm: SHA-256 verify: Verify success verify-app success ``` ### 真机复验(DEL-AL20 / HarmonyOS 7.0.0.109) 装包:`install bundle successfully`。 **① 只读日程(「生成式人工智能」/ 工作学习本)** ``` 🔴 核心验证:『编辑日程』按钮是否还存在 ? False 详情卡按钮区: ['导航', '导航'] ``` 只剩「导航」,编辑入口已消失。✅ **② 有写权限的日程(「清理万向轮充电座」/ 我的日程本)** ``` 🔴 可写日程应有『编辑日程』? True 「编辑日程」 (540,1449) ``` 没有被误杀,编辑入口正常。✅ **③ 端到端保存 + 持久化** 标题改为「清理万向轮充电座-V31」→ 保存 → **无报错** → 杀进程重启: ``` 🔴 重启后仍含 V31 ? ['清理万向轮充电座-V31'] ``` 确认真写入服务端(不是本地乐观更新)。✅ **④ 测试数据已还原** 改完直接从库里改回原名(`calendar_events.id=1056`), 手机端复核 `含 V31 残留? 无 ✓`、`该条标题: ['清理万向轮充电座']`。 临时脚本已删,服务器和本地都没留档。 ### 顺带确认的一条 之前真机上「保存失败:这本日历你没有写权限。」的提示文案是准确的 —— 问题不在服务端(它拒得对),纯粹是端上不该给这个入口。 ### 收尾 工单验收 4 条全部满足,关闭。 > 另:这一轮实测还顺带验证了「编辑就地改、不跳页」(v86 的设计)确实生效 —— > 点「编辑日程」后表单叠在详情卡上,页面没跳。
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: laoyang/FamilyBook#31