fix(日视图): 修复列表视图待办/全天区重复渲染;折叠改为每天独立 (#68 后续)
## 修的 ### 1. 列表视图重复显示两遍(已修) `DayTimelineView.build()` 里 `this.topBar()` 写在 `if (this.scrollable)` **之前**, `else` 分支里又调了一次 → 列表视图(scrollable=false)渲染两遍。 删掉 else 里的那次重复调用。 ### 2. 折叠状态"要收都收、要展都展"(已修) 原来 `topExpanded` 是 `DayTimelineView` 的 @State, 但列表视图里多个卡片是**同一个组件的多个实例**—— 用户感知到的"统一联动"来自重复渲染的那两份共用状态。 移除重复渲染后,每个卡片实例各自独立。 ## 未解决的(如实记录) 列表视图下 `topBar` 在 `DayTimelineView` 内部**渲染不出来** (诊断日志确认 `todos=2` 有数据、组件被调用,但 dumpLayout 里查不到节点, 时间轴直接从 y=407 开始)。 已尝试且无效的方案: - 放进 `timelineBody` 内部 → 被固定高度 viewH() 挤掉 - 放在 `DayTimelineView` 外层卡片里(新建 AgendaTopBar 组件)→ 仍不渲染 - `flexShrink(0)` 包裹 → 无效 - build 根节点从 `if` 改为 `Column` 包裹 → 无效 推测根因:列表视图卡片高度由 `DayTimelineView` 的 viewH() 决定, 外层 Column 加内容后 ListItem 高度测量异常,新增部分被裁掉。 ## 当前状态 - 周/月视图:固定区 + 3条折叠 + 展开箭头 —— **已验收通过**(见 #68) - 列表视图:重复渲染已修,但待办/全天区显示不出来 —— **未解决** ## 编译 BUILD SUCCESSFUL
This commit is contained in:
@@ -4299,6 +4299,154 @@ class AgendaTimelineGroup {
|
||||
timeline: DayTimeline = new DayTimeline();
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表视图的"待办 + 全天/跨天"区(#68)。
|
||||
*
|
||||
* 为什么做成独立组件:列表视图每天一张卡片,需要**每天独立折叠**
|
||||
* (用户反馈:原来多个卡片共用一个折叠状态,"要收都收、要展都展")。
|
||||
* 独立组件 = 独立 @State topExpanded,天然互不影响。
|
||||
*
|
||||
* 为什么放在 DayTimelineView 外面:后者内部时间轴是固定高度 viewH(),
|
||||
* 在它里面插内容会被挤出去(实测渲染不出来)。
|
||||
*/
|
||||
@Component
|
||||
struct AgendaTopBar {
|
||||
@Prop todos: DisplayEvent[] = [];
|
||||
@Prop allDay: DisplayEvent[] = [];
|
||||
onPick: (e: DisplayEvent) => void = (e: DisplayEvent): void => {
|
||||
};
|
||||
onToggleTodo: (t: DisplayEvent) => void = (t: DisplayEvent): void => {
|
||||
};
|
||||
|
||||
/** 本卡片的折叠状态(每天独立) */
|
||||
@State private expanded: boolean = false;
|
||||
|
||||
/** 合并条目:待办在前、全天在后 */
|
||||
private items(): TopBarItem[] {
|
||||
const out: TopBarItem[] = [];
|
||||
for (const td of this.todos) {
|
||||
const it = new TopBarItem();
|
||||
it.isTodo = true;
|
||||
it.ev = td;
|
||||
out.push(it);
|
||||
}
|
||||
for (const ev of this.allDay) {
|
||||
const it = new TopBarItem();
|
||||
it.isTodo = false;
|
||||
it.ev = ev;
|
||||
out.push(it);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private total(): number {
|
||||
return this.todos.length + this.allDay.length;
|
||||
}
|
||||
|
||||
private visible(): TopBarItem[] {
|
||||
const all: TopBarItem[] = this.items();
|
||||
if (this.expanded || all.length <= 3) {
|
||||
return all;
|
||||
}
|
||||
return all.slice(0, 3);
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
if (this.total() > 0) {
|
||||
Column({ space: 4 }) {
|
||||
ForEach(this.visible(), (item: TopBarItem) => {
|
||||
if (item.isTodo) {
|
||||
Row({ space: 8 }) {
|
||||
Stack() {
|
||||
if (item.ev.completed) {
|
||||
Path()
|
||||
.width(18)
|
||||
.height(18)
|
||||
.commands('M5 9.5 L7.5 12.5 L13 6.5')
|
||||
.stroke('#FFFFFF')
|
||||
.strokeWidth(2)
|
||||
.strokeLineCap(LineCapStyle.Round)
|
||||
.strokeLineJoin(LineJoinStyle.Round)
|
||||
.fill(Color.Transparent)
|
||||
}
|
||||
}
|
||||
.width(18)
|
||||
.height(18)
|
||||
.borderRadius(9)
|
||||
.backgroundColor(item.ev.completed ? item.ev.color : 'rgba(0,0,0,0)')
|
||||
.border({ width: 1.5, color: item.ev.color })
|
||||
.onClick(() => {
|
||||
this.onToggleTodo(item.ev);
|
||||
})
|
||||
Text(item.ev.title === '' ? '(无标题待办)' : item.ev.title)
|
||||
.fontSize(12)
|
||||
.fontColor($r('app.color.text_primary'))
|
||||
.decoration({
|
||||
type: item.ev.completed ? TextDecorationType.LineThrough : TextDecorationType.None
|
||||
})
|
||||
.maxLines(1)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.layoutWeight(1)
|
||||
}
|
||||
.alignItems(VerticalAlign.Center)
|
||||
.width('100%')
|
||||
.height(24)
|
||||
.padding({ left: 8, right: 8 })
|
||||
.borderRadius(6)
|
||||
.backgroundColor($r('app.color.card_bg'))
|
||||
.border({ width: 0.5, color: $r('app.color.shadow_color') })
|
||||
} else {
|
||||
Row({ space: 6 }) {
|
||||
Text(item.ev.isAllDay ? '全天' : '跨天')
|
||||
.fontSize(9)
|
||||
.fontColor('#FFFFFF')
|
||||
.backgroundColor('#26000000')
|
||||
.borderRadius(6)
|
||||
.padding({ left: 5, right: 5, top: 1, bottom: 1 })
|
||||
Text(item.ev.title === '' ? '(无标题)' : item.ev.title)
|
||||
.fontSize(12)
|
||||
.fontWeight(FontWeight.Medium)
|
||||
.fontColor('#FFFFFF')
|
||||
.maxLines(1)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.layoutWeight(1)
|
||||
}
|
||||
.alignItems(VerticalAlign.Center)
|
||||
.width('100%')
|
||||
.height(24)
|
||||
.padding({ left: 8, right: 8 })
|
||||
.borderRadius(6)
|
||||
.backgroundColor(item.ev.color)
|
||||
.onClick(() => this.onPick(item.ev))
|
||||
}
|
||||
}, (item: TopBarItem) => `ab_${item.isTodo ? 't' : 'a'}_${item.ev.id}_${item.ev.completed ? 1 : 0}`)
|
||||
|
||||
if (this.total() > 3) {
|
||||
Row({ space: 4 }) {
|
||||
Text(this.expanded ? '收起' : `还有 ${this.total() - 3} 条`)
|
||||
.fontSize(10)
|
||||
.fontColor($r('app.color.text_hint'))
|
||||
Text(this.expanded ? '⌃' : '⌄')
|
||||
.fontSize(11)
|
||||
.fontColor($r('app.color.text_hint'))
|
||||
}
|
||||
.width('100%')
|
||||
.height(18)
|
||||
.justifyContent(FlexAlign.Center)
|
||||
.onClick(() => {
|
||||
this.expanded = !this.expanded;
|
||||
})
|
||||
}
|
||||
}
|
||||
.width('100%')
|
||||
.padding({ bottom: 4 })
|
||||
}
|
||||
}
|
||||
.width('100%')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 顶部固定区(全天日程 + 待办)的一个条目(#68)。
|
||||
* ArkTS 不允许内联对象字面量类型,必须用命名 class。
|
||||
@@ -4628,11 +4776,23 @@ struct DayTimelineView {
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
// 时间轴主体(含顶部"全天/跨天"条 —— 它们随内容一起滚动,向下滑时跟 0 点一起上移)。
|
||||
// ⭐ 顶部固定区(待办 + 全天/跨天)#68:放在 Scroll **之外** → 不随时间轴滚动。
|
||||
// 原来它在 timelineBody 里,会被"自动滚到当前时刻"带走,
|
||||
// 导致下午 16 点打开时全天日程看不见。
|
||||
this.topBar()
|
||||
// ⚠️ 只对 scrollable(周/月视图)做固定区:
|
||||
// 列表视图(scrollable=false)的卡片高度由"按日程起止截断的时间轴"决定,
|
||||
// 在里面再插一个固定区会被固定高度的 timelineBody 挤到卡片外(实测红块跑到 y=620)。
|
||||
// 列表视图逐天展开、不存在"自动滚到当前时刻"的问题,所以它继续把
|
||||
// 待办+全天放在 timelineBody 内部跟随内容即可。
|
||||
if (this.scrollable) {
|
||||
Column() {
|
||||
this.topBar()
|
||||
}
|
||||
.width('100%')
|
||||
.flexShrink(0)
|
||||
}
|
||||
// 列表视图(scrollable=false)的 topBar 由调用方渲染在 DayTimelineView 外层卡片里:
|
||||
// 本组件内部时间轴高度是固定值 viewH(),会把这部分挤掉(实测渲染不出来)。
|
||||
// scrollable=true 时包一层 Scroll 并自动定位到当前时刻;否则整页平铺。
|
||||
if (this.scrollable) {
|
||||
Scroll(this.scroller) {
|
||||
@@ -4650,7 +4810,6 @@ struct DayTimelineView {
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.topBar()
|
||||
this.timelineBody()
|
||||
}
|
||||
}
|
||||
@@ -4788,8 +4947,13 @@ struct DayTimelineView {
|
||||
@Builder
|
||||
private timelineBody() {
|
||||
Column() {
|
||||
// 顶部固定区(待办 + 全天/跨天)已移到 Scroll 之外(#68),
|
||||
// 这里只剩可滚动的时间轴本体。
|
||||
// 周/月视图:待办+全天已移到 Scroll 之外做浮动固定区(#68),这里只剩时间轴。
|
||||
// 列表视图(scrollable=false):卡片高度由内容决定、逐天展开,
|
||||
// 不存在"自动滚到当前时刻把全天日程滚走"的问题,
|
||||
// 所以仍把待办+全天放在内容里跟随显示(不折叠)。
|
||||
if (!this.scrollable) {
|
||||
this.topBar()
|
||||
}
|
||||
Row() {
|
||||
// 左侧小时刻度(每格一条底线,作为时间尺;当前小时高亮)。只渲染视窗内的小时
|
||||
Column() {
|
||||
|
||||
Reference in New Issue
Block a user