Files
SyncCalendar/entry/src/main/ets/pages/widget/Widget4x2.ets
T

217 lines
6.6 KiB
Plaintext
Raw Normal View History

2026-09-13 15:50:37 +08:00
// entry/src/main/ets/pages/widget/Widget4x2.ets
2026-09-14 11:09:15 +08:00
// 2x4 服务卡片:2x2 的横向扩展。左侧 = 日期/星期/农历 + 今日日程条数(与 2x2 一致);
// 右侧 = **当前时间之后最近的两条**日程(不含全天/跨天,不显示"进行中"等字样,不显示日历本名);
// 整卡点击进入 App,右上角 + 直接新建日程
2026-09-13 15:50:37 +08:00
let storage2x4 = new LocalStorage();
2026-09-14 11:09:15 +08:00
/** 与 eventsJson 同结构的卡片单条日程(本卡片只用到标题/时间/日历色/进行中标记) */
2026-09-13 15:50:37 +08:00
class CardItem2x4 {
title: string = '';
time: string = '';
endTime: string = '';
calName: string = '';
color: string = '#007DFF';
isNow: boolean = false;
2026-09-13 15:50:37 +08:00
}
@Entry(storage2x4)
@Component
struct Widget4x2Card {
2026-09-14 11:09:15 +08:00
@LocalStorageProp('dateMd') dateMd: string = '';
2026-09-13 15:50:37 +08:00
@LocalStorageProp('dateText') dateText: string = '';
2026-09-14 11:09:15 +08:00
@LocalStorageProp('weekday') weekday: string = '';
2026-09-13 15:50:37 +08:00
@LocalStorageProp('lunarText') lunarText: string = '';
2026-09-14 11:09:15 +08:00
@LocalStorageProp('todayCount') todayCount: number = 0;
@LocalStorageProp('ongoingJson') ongoingJson: string = '[]';
/** 卡片当前显示的那一天 0 点毫秒 —— 点卡片进 App 时定位到这一天 */
@LocalStorageProp('selectedDate') selectedDate: number = 0;
2026-09-13 15:50:37 +08:00
/** 右侧列表数据(可滚动;数据侧已按时间升序并限制在 15 条内) */
2026-09-14 11:09:15 +08:00
private parseOngoing(): CardItem2x4[] {
2026-09-13 15:50:37 +08:00
try {
return JSON.parse(this.ongoingJson) as CardItem2x4[];
2026-09-13 15:50:37 +08:00
} catch (err) {
return [];
}
}
2026-09-14 11:09:15 +08:00
/** 右上角添加按钮:拉起 App 直接进入新建日程页(兄弟节点布局,点击不会冒泡到左侧日期区) */
2026-09-14 09:07:47 +08:00
@Builder
addButton() {
Button() {
Text('+')
2026-09-14 11:09:15 +08:00
.fontSize(20)
2026-09-14 09:07:47 +08:00
.fontWeight(FontWeight.Medium)
.fontColor('#FFFFFF')
}
2026-09-14 11:09:15 +08:00
.width(30)
.height(30)
.borderRadius(15)
2026-09-14 09:07:47 +08:00
.padding(0)
.backgroundColor('#007DFF')
.onClick(() => {
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: { widgetAction: 'add' }
});
})
}
/** 打开 App 并**定位到卡片当前显示的那一天**(用户可能已翻到其他日期) */
2026-09-14 09:07:47 +08:00
private openApp(): void {
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: { openDate: `${this.selectedDate}` }
2026-09-14 09:07:47 +08:00
});
}
/** 右侧单条:左色竖条 + 起止时间 + 标题。
* 不显示"进行中 / 即将开始"字样,也不显示日历本名 */
2026-09-14 11:09:15 +08:00
@Builder
ongoingRow(item: CardItem2x4) {
Row({ space: 8 }) {
2026-09-14 11:09:15 +08:00
Column()
.width(3)
.height(40)
2026-09-14 11:09:15 +08:00
.borderRadius(2)
.backgroundColor(item.color)
2026-09-14 11:09:15 +08:00
Column({ space: 2 }) {
Text(item.time)
.fontSize(10)
2026-09-14 11:09:15 +08:00
.fontWeight(FontWeight.Medium)
.fontColor('#333333')
2026-09-14 11:09:15 +08:00
.maxLines(1)
if (item.endTime !== '' && item.endTime !== '全天') {
Text(item.endTime)
.fontSize(9)
.fontColor('#999999')
.maxLines(1)
}
}
.width(36)
2026-09-14 11:09:15 +08:00
.alignItems(HorizontalAlign.Start)
Text(item.title)
.fontSize(13)
.fontColor('#1A1A1A')
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.layoutWeight(1)
2026-09-14 11:09:15 +08:00
}
.alignItems(VerticalAlign.Center)
.width('100%')
.padding({ left: 6, right: 8, top: 4, bottom: 4 })
2026-09-14 11:09:15 +08:00
.borderRadius(8)
.backgroundColor('#F5F7FA')
2026-09-14 11:09:15 +08:00
}
/** 右侧空态:今日无日程 / 日程已全部结束 */
@Builder
emptyHint() {
Column() {
Text(this.todayCount === 0 ? '今日暂无日程' : '今日日程已结束')
.fontSize(12)
.fontColor('#8A8A8A')
.maxLines(2)
.textAlign(TextAlign.Center)
}
.width('100%')
.layoutWeight(1)
.justifyContent(FlexAlign.Center)
}
2026-09-13 15:50:37 +08:00
build() {
2026-09-14 11:09:15 +08:00
Row({ space: 10 }) {
// ===== 左半:与 2x2 一致(日期 / 星期 + 农历 / 今日条数大数字) =====
Column({ space: 6 }) {
2026-09-14 09:07:47 +08:00
Row({ space: 6 }) {
2026-09-14 11:09:15 +08:00
Column({ space: 2 }) {
Text(this.dateMd === '' ? this.dateText : this.dateMd)
.fontSize(15)
.fontWeight(FontWeight.Bold)
.fontColor('#1A1A1A')
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Text(`${this.weekday === '' ? '' : this.weekday + ' '}${this.lunarText}`)
.fontSize(10)
.fontColor('#8A8A8A')
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
this.addButton()
}
.width('100%')
Divider()
.strokeWidth(0.5)
.color('#E5E5E5')
Row({ space: 6 }) {
Text(`${this.todayCount}`)
.fontSize(38)
2026-09-14 09:07:47 +08:00
.fontWeight(FontWeight.Bold)
2026-09-14 11:09:15 +08:00
.fontColor('#007DFF')
Column({ space: 2 }) {
Row({ space: 4 }) {
Text('今日日程')
2026-09-14 11:09:15 +08:00
.fontSize(11)
.fontColor('#1A1A1A')
2026-09-14 11:09:15 +08:00
.maxLines(1)
2026-09-13 15:50:37 +08:00
}
2026-09-14 11:09:15 +08:00
Text(this.todayCount === 0 ? '点击添加' : '点击查看')
.fontSize(9)
.fontColor('#8A8A8A')
.maxLines(1)
}
.alignItems(HorizontalAlign.Start)
2026-09-13 15:50:37 +08:00
}
.width('100%')
.layoutWeight(1)
2026-09-14 11:09:15 +08:00
.alignItems(VerticalAlign.Center)
2026-09-13 15:50:37 +08:00
}
2026-09-14 11:09:15 +08:00
.layoutWeight(1)
.height('100%')
Divider()
.vertical(true)
.height('92%')
.strokeWidth(0.5)
.color('#E5E5E5')
// ===== 右半:未来日程列表(可滚动,最多 15 条)=====
// ⭐ 原来固定 Column + 只取 2 条,用户想看后天的安排就看不到。
// 改为 List:自带虚拟化与滚动惯性,条目多也不占内存。
2026-09-14 11:09:15 +08:00
Column() {
if (this.parseOngoing().length === 0) {
this.emptyHint()
} else {
List({ space: 4 }) {
2026-09-14 11:09:15 +08:00
ForEach(this.parseOngoing(), (item: CardItem2x4, idx: number) => {
ListItem() {
this.ongoingRow(item)
}
}, (item: CardItem2x4, idx: number) => `o${idx}_${item.time}_${item.title}`)
2026-09-14 11:09:15 +08:00
}
.width('100%')
.layoutWeight(1)
.scrollBar(BarState.Off)
.edgeEffect(EdgeEffect.Spring)
2026-09-14 11:09:15 +08:00
}
}
.layoutWeight(1)
.height('100%')
2026-09-13 15:50:37 +08:00
}
.width('100%')
.height('100%')
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(16)
// ⭐ 整卡可点:左右空白处也能进 App 并定位到当前显示日
.onClick(() => this.openApp())
2026-09-13 15:50:37 +08:00
}
}