From 007c112af21a0f0d40577f0aa65ecacf400a9ef8 Mon Sep 17 00:00:00 2001 From: laoyang Date: Thu, 5 Nov 2026 09:47:45 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E5=BE=85=E5=8A=9E):=20=E8=A1=A5=E4=B8=8A?= =?UTF-8?q?=E6=88=AA=E6=AD=A2=E6=97=B6=E9=97=B4=E9=80=89=E6=8B=A9=E5=99=A8?= =?UTF-8?q?=E5=92=8C=E7=88=B6=E4=BB=BB=E5=8A=A1=E9=80=89=E6=8B=A9=E5=99=A8?= =?UTF-8?q?=E5=BC=B9=E5=B1=82=20(#49)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 现象 新建待办时点「截止时间 - 修改」没反应;点「父任务 - 选择」也没反应。 ## 根因 迁移到独立页面(#47)时,只搬了字段 UI 和状态变量, **两个选择器弹层本身漏写了**: `pickShow` / `parentPickShow` 只被赋值为 true, 但 build() 里没有任何 UI 消费它们 → 赋值等于空操作。 对比原弹层版本:时间选择器是叠在弹层之上的一个 if 块; 迁到页面后需要改成 bindSheet,这一步遗漏了。 ## 修复 按 EventEditPage 的做法补两个弹层,**共用唯一一个 bindSheet**: ⚠️ 关键约束(EventEditPage:105 记载的坑): ArkUI 同一组件挂多个 bindSheet 时,后一个会覆盖前一个, 实测表现就是"白框一闪而过、什么都没出来"。 所以用一个 `sheetShow` + `sheetMode`: sheetMode='due' → DatePicker + TimePicker + 确定 sheetMode='parent' → 父任务列表 + 无父任务 + 取消 ## 改动点 - 加 `sheetShow` / `sheetMode` 状态 - 「修改」→ sheetMode='due' - 「选择」/整行 → sheetMode='parent' - 根容器挂唯一 bindSheet,`bottomSheet()` 按 mode 分发 - 新增 `confirmDue()`:把 DatePicker/TimePicker 的值合成毫秒并置 hasDue=true ## 编译 BUILD SUCCESSFUL ## 静态自检 - bindSheet 实际调用 1 处(861 行;79 行是注释) - DatePicker ×1、TimePicker ×1 - confirmDue 定义 + 调用 - sheetMode 赋值 3 处(默认值 + 两个入口) ## 未验证 真机(安装时设备离线)。需验证:时间滚轮能弹、选完确定后截止时间更新。 --- entry/src/main/ets/pages/TodoEditPage.ets | 181 +++++++++++++++++++++- 1 file changed, 178 insertions(+), 3 deletions(-) diff --git a/entry/src/main/ets/pages/TodoEditPage.ets b/entry/src/main/ets/pages/TodoEditPage.ets index 9336667..6af0aae 100644 --- a/entry/src/main/ets/pages/TodoEditPage.ets +++ b/entry/src/main/ets/pages/TodoEditPage.ets @@ -75,6 +75,14 @@ struct TodoEditPage { // ---------- 截止时间选择器 ---------- @State pickShow: boolean = false; + /** + * ⭐ 页面唯一底部弹层(ArkUI 同一页面挂多个 bindSheet 会互相覆盖, + * 实测表现为"白框一闪而过")。用 sheetMode 切换内容: + * 'due' → 截止时间(日期 + 时间滚轮) + * 'parent' → 父任务选择 + */ + @State sheetShow: boolean = false; + @State sheetMode: string = 'due'; @State pickDate: Date = new Date(); @State pickHour: number = 9; @State pickMin: number = 0; @@ -615,7 +623,8 @@ struct TodoEditPage { .borderRadius(10) .backgroundColor($r('app.color.card_bg')) .onClick(() => { - this.pickShow = true; + this.sheetMode = 'due'; + this.sheetShow = true; }) if (this.hasDue) { Text('清除') @@ -745,7 +754,8 @@ struct TodoEditPage { .fontSize(13) .fontColor($r('app.color.brand')) .onClick(() => { - this.parentPickShow = true; + this.sheetMode = 'parent'; + this.sheetShow = true; }) } } @@ -754,7 +764,8 @@ struct TodoEditPage { .borderRadius(10) .backgroundColor($r('app.color.card_bg')) .onClick(() => { - this.parentPickShow = true; + this.sheetMode = 'parent'; + this.sheetShow = true; }) // 附件 @@ -847,6 +858,170 @@ struct TodoEditPage { // ⭐ 沉浸式:背景延伸到状态栏/导航条区域(与 EventEditPage 一致) // 只扩绘制范围、不移动子组件 → 状态栏区域同色,不再是割裂的白条 .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]) + .bindSheet($$this.sheetShow, this.bottomSheet(), { + height: this.sheetMode === 'parent' ? 400 : 380, + showClose: false, + dragBar: true, + backgroundColor: $r('app.color.card_bg') + }) + } + + /** 唯一底部弹层:按 sheetMode 切换内容 */ + @Builder + bottomSheet() { + if (this.sheetMode === 'parent') { + this.parentSheet() + } else { + this.dueSheet() + } + } + + /** 截止时间选择弹层(日期 + 时间) */ + @Builder + dueSheet() { + Column({ space: 14 }) { + Text('选择截止时间') + .fontSize(16) + .fontWeight(FontWeight.Bold) + .fontColor($r('app.color.text_primary')) + .width('100%') + .textAlign(TextAlign.Center) + .padding({ top: 12 }) + Row({ space: 6 }) { + DatePicker({ + start: new Date(2000, 0, 1), + end: new Date(2050, 11, 31), + selected: this.pickDate + }) + .onDateChange((value: Date) => { + this.pickDate = value; + }) + // 滚轮文字显式指定颜色:默认取系统主题色,深色模式下会与弹层底色撞色 + .textStyle({ color: $r('app.color.text_secondary'), font: { size: 14 } }) + .selectedTextStyle({ + color: $r('app.color.text_primary'), + font: { size: 16, weight: FontWeight.Medium } + }) + .disappearTextStyle({ color: $r('app.color.text_hint'), font: { size: 14 } }) + .layoutWeight(1) + .height(210) + TimePicker({ + selected: new Date(2000, 0, 1, this.pickHour, this.pickMin) + }) + .onChange((value: TimePickerResult) => { + this.pickHour = value.hour; + this.pickMin = value.minute; + }) + .textStyle({ color: $r('app.color.text_secondary'), font: { size: 14 } }) + .selectedTextStyle({ + color: $r('app.color.text_primary'), + font: { size: 16, weight: FontWeight.Medium } + }) + .disappearTextStyle({ color: $r('app.color.text_hint'), font: { size: 14 } }) + .layoutWeight(1) + .height(210) + } + .width('100%') + + Button('确定') + .width('100%') + .height(44) + .borderRadius(12) + .fontSize(15) + .fontColor($r('app.color.button_text')) + .backgroundColor($r('app.color.brand')) + .onClick(() => { + this.confirmDue(); + this.sheetShow = false; + }) + } + .width('100%') + .padding({ left: 16, right: 16, bottom: 24 }) + } + + /** 父任务选择弹层 */ + @Builder + parentSheet() { + Column({ space: 10 }) { + Row({ space: 8 }) { + Text('选择父任务') + .fontSize(16) + .fontWeight(FontWeight.Bold) + .fontColor($r('app.color.text_primary')) + Blank() + Text('无父任务') + .fontSize(14) + .fontColor($r('app.color.brand')) + .onClick(() => { + this.parentUid = ''; + this.sheetShow = false; + }) + } + .width('100%') + .padding({ top: 12 }) + + if (this.parentCandidates().length === 0) { + Text('没有可选的顶层任务') + .fontSize(13) + .fontColor($r('app.color.text_hint')) + .padding(20) + } else { + List({ space: 6 }) { + ForEach(this.parentCandidates(), (c: DisplayEvent) => { + ListItem() { + Row({ space: 8 }) { + Circle().width(8).height(8).fill(c.color) + Text(c.title === '' ? '(无标题待办)' : c.title) + .fontSize(14) + .fontColor($r('app.color.text_primary')) + .maxLines(1) + .textOverflow({ overflow: TextOverflow.Ellipsis }) + .layoutWeight(1) + if (this.parentUid === c.uid) { + Text('✓') + .fontSize(14) + .fontColor($r('app.color.brand')) + } + } + .width('100%') + .padding(12) + .borderRadius(10) + .backgroundColor($r('app.color.chip_off_bg')) + } + .width('100%') + .onClick(() => { + this.parentUid = c.uid; + this.sheetShow = false; + }) + }, (c: DisplayEvent) => `pc_${c.uid}`) + } + .width('100%') + .layoutWeight(1) + .scrollBar(BarState.Off) + } + + Button('取消') + .width('100%') + .height(44) + .borderRadius(12) + .fontSize(15) + .fontColor($r('app.color.text_primary')) + .backgroundColor($r('app.color.chip_off_bg')) + .onClick(() => { + this.sheetShow = false; + }) + } + .width('100%') + .padding({ left: 16, right: 16, bottom: 24 }) + .layoutWeight(1) + } + + /** 确认截止时间:把滚轮值合成毫秒 */ + private confirmDue(): void { + const d: Date = this.pickDate; + this.dueMs = new Date(d.getFullYear(), d.getMonth(), d.getDate(), + this.pickHour, this.pickMin).getTime(); + this.hasDue = true; } /** 字段小标题 */