feat(待办): 选择器改为居中弹层 + 对勾改矢量绘制 (#50)
## 1. 时间选择器改为屏幕居中弹出
### 用户诉求
「点完开始之后,在中间弹出一个框来选择时间,操作起来更方便」
### 为什么不用 bindSheet
查 SDK `SheetOptions`(common.d.ts:11753)确认:**没有 position / alignment 参数**,
bindSheet 只能从底部弹,无法居中。
### 做法:自绘居中覆盖层
把 build() 根节点改为 `Stack`:
Stack(alignContent: Center) {
Column { 顶部栏 + Scroll 表单 } ← 原有内容
if (sheetShow) {
遮罩(半透明,点空白关闭)
centerDialog() ← 居中卡片
}
}
选这条路而不是 CustomDialogController,依据是本工程 MEMORY:
系统弹层在本环境曾"静默失败"(Index.ets:1421 已记录),
首启引导页 tipsOverlay 就是改用自绘覆盖层才可靠的。
## 2. 对勾改为矢量 Path
### 问题
原来用字符 `✓`(U+2713),三个毛病:
- 字体依赖:不同设备粗细/形状不同
- 粗细不可控:fontWeight 对符号字形影响有限
- 居中不稳:字形基线导致视觉重心偏移
### 改法
用 `Path` 画折线,配 strokeWidth + 圆头圆角:
commands('M6 12.5 L9.5 16 L16.5 8') // 22vp 列表
commands('M5 9.5 L7.5 12.5 L13 6.5') // 18vp 时间轴
得到粗细均匀、端点圆润、可精确居中的勾。
两处统一:待办列表勾选圈、时间轴待办区勾选圈。
## 编译
BUILD SUCCESSFUL
## 真机验证(5NC0225B27004718)
| 验证项 | 实测 |
|---|---|
| 弹层居中 | 弹层 y 750~1728,中心 **1239**;屏幕中心 1222,偏差仅 17px |
| 对比改造前 | 中心 1800(偏下 578px)→ 现在偏 17px |
| 日期/时间滚轮 | `DatePicker 2026年11月5日` + `TimePicker 09:00` 正常渲染 |
| 对勾 Path 数量 | 1 个 `[96,2013][165,2082]` |
| 字符 ✓ 残留 | 0 个 |
This commit is contained in:
@@ -3498,14 +3498,24 @@ struct Index {
|
|||||||
// 勾选框(颜色跟随日历本)—— **只有这块触发完成切换**
|
// 勾选框(颜色跟随日历本)—— **只有这块触发完成切换**
|
||||||
Row() {
|
Row() {
|
||||||
if (t.completed) {
|
if (t.completed) {
|
||||||
Text('✓')
|
// ⭐ 用矢量 Path 画勾(#50):字符 '✓' 依赖字体,
|
||||||
.fontSize(13)
|
// 不同设备下粗细/居中不一致,视觉上"不精致"。
|
||||||
.fontColor($r('app.color.button_text'))
|
// Path 可精确控制线宽、端点圆角与几何比例。
|
||||||
.width(22)
|
Stack() {
|
||||||
.height(22)
|
Path()
|
||||||
.textAlign(TextAlign.Center)
|
.width(22)
|
||||||
.borderRadius(11)
|
.height(22)
|
||||||
.backgroundColor(this.colorOfCal(t.calKey))
|
.commands('M6 12.5 L9.5 16 L16.5 8')
|
||||||
|
.stroke($r('app.color.button_text'))
|
||||||
|
.strokeWidth(2.2)
|
||||||
|
.strokeLineCap(LineCapStyle.Round)
|
||||||
|
.strokeLineJoin(LineJoinStyle.Round)
|
||||||
|
.fill(Color.Transparent)
|
||||||
|
}
|
||||||
|
.width(22)
|
||||||
|
.height(22)
|
||||||
|
.borderRadius(11)
|
||||||
|
.backgroundColor(this.colorOfCal(t.calKey))
|
||||||
} else {
|
} else {
|
||||||
Text('')
|
Text('')
|
||||||
.width(22)
|
.width(22)
|
||||||
@@ -4594,18 +4604,28 @@ struct DayTimelineView {
|
|||||||
ForEach(this.timeline.todos, (td: DisplayEvent) => {
|
ForEach(this.timeline.todos, (td: DisplayEvent) => {
|
||||||
Row({ space: 8 }) {
|
Row({ space: 8 }) {
|
||||||
// 勾选圈:点击切换完成(与待办列表同一回调)
|
// 勾选圈:点击切换完成(与待办列表同一回调)
|
||||||
Text(td.completed ? '✓' : '')
|
// 同列表:矢量勾(#50)
|
||||||
.fontSize(12)
|
Stack() {
|
||||||
.fontColor(td.completed ? '#FFFFFF' : 'rgba(0,0,0,0)')
|
if (td.completed) {
|
||||||
.width(18)
|
Path()
|
||||||
.height(18)
|
.width(18)
|
||||||
.textAlign(TextAlign.Center)
|
.height(18)
|
||||||
.borderRadius(9)
|
.commands('M5 9.5 L7.5 12.5 L13 6.5')
|
||||||
.backgroundColor(td.completed ? td.color : 'rgba(0,0,0,0)')
|
.stroke('#FFFFFF')
|
||||||
.border({ width: 1.5, color: td.color })
|
.strokeWidth(2)
|
||||||
.onClick(() => {
|
.strokeLineCap(LineCapStyle.Round)
|
||||||
this.onToggleTodo(td);
|
.strokeLineJoin(LineJoinStyle.Round)
|
||||||
})
|
.fill(Color.Transparent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.width(18)
|
||||||
|
.height(18)
|
||||||
|
.borderRadius(9)
|
||||||
|
.backgroundColor(td.completed ? td.color : 'rgba(0,0,0,0)')
|
||||||
|
.border({ width: 1.5, color: td.color })
|
||||||
|
.onClick(() => {
|
||||||
|
this.onToggleTodo(td);
|
||||||
|
})
|
||||||
Text(td.title === '' ? '(无标题待办)' : td.title)
|
Text(td.title === '' ? '(无标题待办)' : td.title)
|
||||||
.fontSize(12)
|
.fontSize(12)
|
||||||
.fontColor($r('app.color.text_primary'))
|
.fontColor($r('app.color.text_primary'))
|
||||||
|
|||||||
@@ -521,7 +521,8 @@ struct TodoEditPage {
|
|||||||
|
|
||||||
// ---------- UI ----------
|
// ---------- UI ----------
|
||||||
build() {
|
build() {
|
||||||
Column({ space: 14 }) {
|
Stack({ alignContent: Alignment.Center }) {
|
||||||
|
Column({ space: 14 }) {
|
||||||
// ===== 固定顶部栏:取消 · 标题 · 保存 =====
|
// ===== 固定顶部栏:取消 · 标题 · 保存 =====
|
||||||
Row({ space: 6 }) {
|
Row({ space: 6 }) {
|
||||||
Text('取消')
|
Text('取消')
|
||||||
@@ -850,35 +851,54 @@ struct TodoEditPage {
|
|||||||
.scrollBar(BarState.Off)
|
.scrollBar(BarState.Off)
|
||||||
.edgeEffect(EdgeEffect.Spring)
|
.edgeEffect(EdgeEffect.Spring)
|
||||||
.align(Alignment.Top)
|
.align(Alignment.Top)
|
||||||
|
}
|
||||||
|
.width('100%')
|
||||||
|
.height('100%')
|
||||||
|
.padding({ top: 12 })
|
||||||
|
.backgroundColor($r('app.color.page_bg'))
|
||||||
|
// ⭐ 沉浸式:背景延伸到状态栏/导航条区域(与 EventEditPage 一致)
|
||||||
|
// 只扩绘制范围、不移动子组件 → 状态栏区域同色,不再是割裂的白条
|
||||||
|
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
|
||||||
|
|
||||||
|
// ===== 居中弹层(浮在内容之上)=====
|
||||||
|
if (this.sheetShow) {
|
||||||
|
// 遮罩:半透明,点空白关闭
|
||||||
|
Column()
|
||||||
|
.width('100%')
|
||||||
|
.height('100%')
|
||||||
|
.backgroundColor('#66000000')
|
||||||
|
.onClick(() => {
|
||||||
|
this.sheetShow = false;
|
||||||
|
})
|
||||||
|
// 居中卡片
|
||||||
|
this.centerDialog()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.width('100%')
|
.width('100%')
|
||||||
.height('100%')
|
.height('100%')
|
||||||
.padding({ top: 12 })
|
|
||||||
.backgroundColor($r('app.color.page_bg'))
|
|
||||||
// ⭐ 沉浸式:背景延伸到状态栏/导航条区域(与 EventEditPage 一致)
|
|
||||||
// 只扩绘制范围、不移动子组件 → 状态栏区域同色,不再是割裂的白条
|
|
||||||
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
|
.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 切换内容 */
|
/**
|
||||||
|
* 居中弹层(自绘覆盖层,不用 bindSheet)。
|
||||||
|
*
|
||||||
|
* 为什么不用 bindSheet:SDK 的 SheetOptions 没有 position/alignment,
|
||||||
|
* 只能从底部弹,无法居中。而用户要求"在中间弹出来更方便"。
|
||||||
|
* 本工程既有的 tipsOverlay 也是自绘覆盖层(系统弹层在本环境不可靠),
|
||||||
|
* 这里沿用同一套做法。
|
||||||
|
*/
|
||||||
@Builder
|
@Builder
|
||||||
bottomSheet() {
|
centerDialog() {
|
||||||
if (this.sheetMode === 'parent') {
|
if (this.sheetMode === 'parent') {
|
||||||
this.parentSheet()
|
this.parentDialog()
|
||||||
} else {
|
} else {
|
||||||
this.dueSheet()
|
this.dueDialog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 截止时间选择弹层(日期 + 时间) */
|
/** 截止时间选择弹层(居中卡片:日期 + 时间) */
|
||||||
@Builder
|
@Builder
|
||||||
dueSheet() {
|
dueDialog() {
|
||||||
Column({ space: 14 }) {
|
Column({ space: 14 }) {
|
||||||
Text('选择截止时间')
|
Text('选择截止时间')
|
||||||
.fontSize(16)
|
.fontSize(16)
|
||||||
@@ -935,13 +955,15 @@ struct TodoEditPage {
|
|||||||
this.sheetShow = false;
|
this.sheetShow = false;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
.width('100%')
|
.width('86%')
|
||||||
.padding({ left: 16, right: 16, bottom: 24 })
|
.padding(18)
|
||||||
|
.borderRadius(16)
|
||||||
|
.backgroundColor($r('app.color.card_bg'))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 父任务选择弹层 */
|
/** 父任务选择弹层(居中卡片) */
|
||||||
@Builder
|
@Builder
|
||||||
parentSheet() {
|
parentDialog() {
|
||||||
Column({ space: 10 }) {
|
Column({ space: 10 }) {
|
||||||
Row({ space: 8 }) {
|
Row({ space: 8 }) {
|
||||||
Text('选择父任务')
|
Text('选择父任务')
|
||||||
@@ -1011,9 +1033,10 @@ struct TodoEditPage {
|
|||||||
this.sheetShow = false;
|
this.sheetShow = false;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
.width('100%')
|
.width('86%')
|
||||||
.padding({ left: 16, right: 16, bottom: 24 })
|
.padding(18)
|
||||||
.layoutWeight(1)
|
.borderRadius(16)
|
||||||
|
.backgroundColor($r('app.color.card_bg'))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 确认截止时间:把滚轮值合成毫秒 */
|
/** 确认截止时间:把滚轮值合成毫秒 */
|
||||||
|
|||||||
Reference in New Issue
Block a user