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 一致);
|
|
|
|
|
|
// 右侧 = 当前正在进行(或下一个)的 1~3 条日程,不做滚动;整卡点击进入 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';
|
2026-09-14 10:18:31 +08:00
|
|
|
|
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('ongoingCount') ongoingCount: number = 0;
|
|
|
|
|
|
@LocalStorageProp('ongoingJson') ongoingJson: string = '[]';
|
2026-09-13 15:50:37 +08:00
|
|
|
|
|
2026-09-14 11:09:15 +08:00
|
|
|
|
/** 右侧最多渲染 3 条(数据侧已裁剪,这里再兜底截断) */
|
|
|
|
|
|
private parseOngoing(): CardItem2x4[] {
|
2026-09-13 15:50:37 +08:00
|
|
|
|
try {
|
2026-09-14 11:09:15 +08:00
|
|
|
|
const all: CardItem2x4[] = JSON.parse(this.ongoingJson) as CardItem2x4[];
|
|
|
|
|
|
return all.slice(0, 3);
|
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' }
|
|
|
|
|
|
});
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-14 11:09:15 +08:00
|
|
|
|
/** 卡片"打开 App":点击左侧日期区、今日条数区、右侧日程区触发;添加按钮是 header 行兄弟节点,不冒泡 */
|
2026-09-14 09:07:47 +08:00
|
|
|
|
private openApp(): void {
|
|
|
|
|
|
postCardAction(this, {
|
|
|
|
|
|
action: 'router',
|
|
|
|
|
|
abilityName: 'EntryAbility',
|
|
|
|
|
|
params: {}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-14 11:09:15 +08:00
|
|
|
|
/** 右侧"正在进行 / 下一个"单条:左色竖条 + 时间列 + 标题 + 日历本名;进行中用红色高亮 */
|
|
|
|
|
|
@Builder
|
|
|
|
|
|
ongoingRow(item: CardItem2x4) {
|
|
|
|
|
|
Row({ space: 7 }) {
|
|
|
|
|
|
Column()
|
|
|
|
|
|
.width(3)
|
|
|
|
|
|
.height(34)
|
|
|
|
|
|
.borderRadius(2)
|
|
|
|
|
|
.backgroundColor(item.isNow ? '#FF3B30' : item.color)
|
|
|
|
|
|
Column({ space: 2 }) {
|
|
|
|
|
|
Text(item.time)
|
|
|
|
|
|
.fontSize(9)
|
|
|
|
|
|
.fontWeight(FontWeight.Medium)
|
|
|
|
|
|
.fontColor(item.isNow ? '#FF3B30' : '#333333')
|
|
|
|
|
|
.maxLines(1)
|
|
|
|
|
|
if (item.endTime !== '' && item.endTime !== '全天') {
|
|
|
|
|
|
Text(item.endTime)
|
|
|
|
|
|
.fontSize(9)
|
|
|
|
|
|
.fontColor('#999999')
|
|
|
|
|
|
.maxLines(1)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
.width(34)
|
|
|
|
|
|
.alignItems(HorizontalAlign.Start)
|
|
|
|
|
|
Column({ space: 2 }) {
|
|
|
|
|
|
Text(item.title)
|
|
|
|
|
|
.fontSize(12)
|
|
|
|
|
|
.fontWeight(item.isNow ? FontWeight.Medium : FontWeight.Normal)
|
|
|
|
|
|
.fontColor(item.isNow ? '#FF3B30' : '#1A1A1A')
|
|
|
|
|
|
.maxLines(1)
|
|
|
|
|
|
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
|
|
|
|
|
.width('100%')
|
|
|
|
|
|
Row({ space: 4 }) {
|
|
|
|
|
|
if (item.isNow) {
|
|
|
|
|
|
Text('● 进行中')
|
|
|
|
|
|
.fontSize(8)
|
|
|
|
|
|
.fontColor('#FF3B30')
|
|
|
|
|
|
.padding({ left: 4, right: 4, top: 0, bottom: 0 })
|
|
|
|
|
|
.borderRadius(4)
|
|
|
|
|
|
.backgroundColor('#FFECEA')
|
|
|
|
|
|
} else {
|
|
|
|
|
|
Text('即将开始')
|
|
|
|
|
|
.fontSize(8)
|
|
|
|
|
|
.fontColor('#8A8A8A')
|
|
|
|
|
|
.padding({ left: 4, right: 4, top: 0, bottom: 0 })
|
|
|
|
|
|
.borderRadius(4)
|
|
|
|
|
|
.backgroundColor('#F0F1F3')
|
|
|
|
|
|
}
|
|
|
|
|
|
if (item.calName !== '') {
|
|
|
|
|
|
Text(item.calName)
|
|
|
|
|
|
.fontSize(8)
|
|
|
|
|
|
.fontColor(item.color)
|
|
|
|
|
|
.maxLines(1)
|
|
|
|
|
|
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
|
|
|
|
|
.constraintSize({ maxWidth: '55%' })
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
.layoutWeight(1)
|
|
|
|
|
|
.alignItems(HorizontalAlign.Start)
|
|
|
|
|
|
}
|
|
|
|
|
|
.alignItems(VerticalAlign.Center)
|
|
|
|
|
|
.width('100%')
|
|
|
|
|
|
.padding({ left: 7, right: 7, top: 4, bottom: 4 })
|
|
|
|
|
|
.borderRadius(8)
|
|
|
|
|
|
.backgroundColor(item.isNow ? '#FFF5F4' : '#F5F7FA')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 右侧空态:今日无日程 / 日程已全部结束 */
|
|
|
|
|
|
@Builder
|
|
|
|
|
|
emptyHint() {
|
|
|
|
|
|
Column() {
|
|
|
|
|
|
Text(this.todayCount === 0 ? '今日暂无日程' : '今日日程已结束')
|
|
|
|
|
|
.fontSize(12)
|
|
|
|
|
|
.fontColor('#8A8A8A')
|
|
|
|
|
|
.maxLines(2)
|
|
|
|
|
|
.textAlign(TextAlign.Center)
|
|
|
|
|
|
}
|
|
|
|
|
|
.width('100%')
|
|
|
|
|
|
.layoutWeight(1)
|
|
|
|
|
|
.justifyContent(FlexAlign.Center)
|
|
|
|
|
|
.onClick(() => this.openApp())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
.onClick(() => this.openApp())
|
|
|
|
|
|
|
|
|
|
|
|
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 }) {
|
|
|
|
|
|
if (this.ongoingCount > 0) {
|
|
|
|
|
|
Column()
|
|
|
|
|
|
.width(7)
|
|
|
|
|
|
.height(7)
|
|
|
|
|
|
.borderRadius(4)
|
|
|
|
|
|
.backgroundColor('#FF3B30')
|
2026-09-13 15:50:37 +08:00
|
|
|
|
}
|
2026-09-14 11:09:15 +08:00
|
|
|
|
Text(this.ongoingCount > 0 ? `进行中 ${this.ongoingCount}` : '今日日程')
|
|
|
|
|
|
.fontSize(11)
|
|
|
|
|
|
.fontColor(this.ongoingCount > 0 ? '#FF3B30' : '#1A1A1A')
|
|
|
|
|
|
.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-14 09:07:47 +08:00
|
|
|
|
.onClick(() => this.openApp())
|
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')
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 右半:正在进行 / 下一个(不滚动,最多 3 条)=====
|
|
|
|
|
|
Column() {
|
|
|
|
|
|
if (this.parseOngoing().length === 0) {
|
|
|
|
|
|
this.emptyHint()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
Column({ space: 5 }) {
|
|
|
|
|
|
ForEach(this.parseOngoing(), (item: CardItem2x4, idx: number) => {
|
|
|
|
|
|
this.ongoingRow(item)
|
|
|
|
|
|
}, (item: CardItem2x4, idx: number) => `${idx}_${item.title}_${item.time}`)
|
|
|
|
|
|
}
|
|
|
|
|
|
.width('100%')
|
|
|
|
|
|
.layoutWeight(1)
|
|
|
|
|
|
.justifyContent(FlexAlign.Center)
|
|
|
|
|
|
.onClick(() => this.openApp())
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
.layoutWeight(1)
|
|
|
|
|
|
.height('100%')
|
2026-09-13 15:50:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
.width('100%')
|
|
|
|
|
|
.height('100%')
|
|
|
|
|
|
.padding(12)
|
|
|
|
|
|
.backgroundColor('#FFFFFF')
|
|
|
|
|
|
.borderRadius(16)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|