## 1. 粘贴按钮文字看不清(EventEditPage)
根因不是"忘了设文字"——空参时系统本就给默认样式(LINES 图标 + PASTE 文案)。
真正原因是我**给了一个和系统文字颜色太接近的背景色**(card_bg 近乎白)。
查 SDK 类型定义 `paste_button.d.ts`,错误码 2 明确列出:
「字体或图标颜色与组件背景颜色相近」「文字超出组件背景区域」等
安全组件创建时会做样式校验,对比度不足会让文字灰淡难辨、甚至授权失败。
同时发现 icon/text/buttonType **只能走构造参数**,不是链式属性
(`.text()` 会编译报 "Property 'text' does not exist")。
改为:
```
PasteButton({
icon: PasteIconStyle.LINES, // 该枚举只有 LINES
text: PasteDescription.PASTE, // 该枚举只有 PASTE
buttonType: ButtonType.Capsule
})
```
并**去掉自定义背景色**,交给系统默认样式,只保留尺寸与圆角。
## 2. 待办卡片显示 0 条(CardDataService)
`pushToAllForms()` 是同步完成后刷新所有卡片的主路径,
但它只调 `buildCardData()`(日程),**从未调 `buildTodoCardData()`** ⇒
推给卡片的 binding 里 todoCount / todoListJson 全是默认值。
而 EntryFormAbility.pushData() 里合并了待办 —— 两条路径不一致,
所以表现成"有时对、有时 0"。
修:抽出 `mergeTodoInto()` 供两处共用;待办数据与 dayOffset 无关,
整个 pushToAllForms 只构建一次。
真机日志:`待办卡片数据刷新:未完成 5 条…列表输出 5 条`(原为 0)。
## 3. 待办卡片右上角 + 点了没反应(EntryAbility + Index)
待办卡片发的是 `widgetAction: 'addTodo'` / `'openTodo'`,
但 EntryAbility 只判断 `raw === 'add'`,Index 也只处理 `'add'`
⇒ 参数被静默丢弃。
修:两端都补上 addTodo / openTodo。
addTodo → 切待办 Tab + 打开新建待办
openTodo → 切待办 Tab
## 4. 日程 2×4 卡片右侧可滚动,15 条(CardDataService + Widget4x2)
数据侧 `slice(0, 2)` → `slice(0, 15)`
视图侧 `Column + ForEach` → `List + ListItem`(虚拟化 + 滚动惯性 + 回弹)
真机日志:`进行中 15 条`,确认上限生效。
## 5. 待办 4×4 / 6×4 列表样式统一,全部可滚动
按用户选择,**统一成 4×4 样式**(勾选框 + 色条 + 标题 + 副行「截止时间 · 日历本」)。
6×4 原来是「标题 + 独立截止时间列 + 独立日历本列」,窄卡片里会把标题挤没,
改为与 4×4 完全相同的结构。已用逐行 diff 校验:两卡 todoRow **36 行完全一致**。
`MAX_TODO_ITEMS` 20 → 15,与日程卡片口径一致(所有待办卡片都已可滚动,
不必再按卡片尺寸给不同上限)。
## 编译
`BUILD SUCCESSFUL`
## 未验证
真机仅验证了「待办卡片数据不再为 0」与「日程 15 条上限」两项。
粘贴按钮视觉、卡片内滚动、卡片 + 跳转、两卡样式一致性需上机目视确认。
217 lines
6.6 KiB
Plaintext
217 lines
6.6 KiB
Plaintext
// entry/src/main/ets/pages/widget/Widget4x2.ets
|
||
// 2x4 服务卡片:2x2 的横向扩展。左侧 = 日期/星期/农历 + 今日日程条数(与 2x2 一致);
|
||
// 右侧 = **当前时间之后最近的两条**日程(不含全天/跨天,不显示"进行中"等字样,不显示日历本名);
|
||
// 整卡点击进入 App,右上角 + 直接新建日程
|
||
let storage2x4 = new LocalStorage();
|
||
|
||
/** 与 eventsJson 同结构的卡片单条日程(本卡片只用到标题/时间/日历色/进行中标记) */
|
||
class CardItem2x4 {
|
||
title: string = '';
|
||
time: string = '';
|
||
endTime: string = '';
|
||
calName: string = '';
|
||
color: string = '#007DFF';
|
||
isNow: boolean = false;
|
||
}
|
||
|
||
@Entry(storage2x4)
|
||
@Component
|
||
struct Widget4x2Card {
|
||
@LocalStorageProp('dateMd') dateMd: string = '';
|
||
@LocalStorageProp('dateText') dateText: string = '';
|
||
@LocalStorageProp('weekday') weekday: string = '';
|
||
@LocalStorageProp('lunarText') lunarText: string = '';
|
||
@LocalStorageProp('todayCount') todayCount: number = 0;
|
||
@LocalStorageProp('ongoingJson') ongoingJson: string = '[]';
|
||
/** 卡片当前显示的那一天 0 点毫秒 —— 点卡片进 App 时定位到这一天 */
|
||
@LocalStorageProp('selectedDate') selectedDate: number = 0;
|
||
|
||
/** 右侧列表数据(可滚动;数据侧已按时间升序并限制在 15 条内) */
|
||
private parseOngoing(): CardItem2x4[] {
|
||
try {
|
||
return JSON.parse(this.ongoingJson) as CardItem2x4[];
|
||
} catch (err) {
|
||
return [];
|
||
}
|
||
}
|
||
|
||
/** 右上角添加按钮:拉起 App 直接进入新建日程页(兄弟节点布局,点击不会冒泡到左侧日期区) */
|
||
@Builder
|
||
addButton() {
|
||
Button() {
|
||
Text('+')
|
||
.fontSize(20)
|
||
.fontWeight(FontWeight.Medium)
|
||
.fontColor('#FFFFFF')
|
||
}
|
||
.width(30)
|
||
.height(30)
|
||
.borderRadius(15)
|
||
.padding(0)
|
||
.backgroundColor('#007DFF')
|
||
.onClick(() => {
|
||
postCardAction(this, {
|
||
action: 'router',
|
||
abilityName: 'EntryAbility',
|
||
params: { widgetAction: 'add' }
|
||
});
|
||
})
|
||
}
|
||
|
||
/** 打开 App 并**定位到卡片当前显示的那一天**(用户可能已翻到其他日期) */
|
||
private openApp(): void {
|
||
postCardAction(this, {
|
||
action: 'router',
|
||
abilityName: 'EntryAbility',
|
||
params: { openDate: `${this.selectedDate}` }
|
||
});
|
||
}
|
||
|
||
/** 右侧单条:左色竖条 + 起止时间 + 标题。
|
||
* 不显示"进行中 / 即将开始"字样,也不显示日历本名 */
|
||
@Builder
|
||
ongoingRow(item: CardItem2x4) {
|
||
Row({ space: 8 }) {
|
||
Column()
|
||
.width(3)
|
||
.height(40)
|
||
.borderRadius(2)
|
||
.backgroundColor(item.color)
|
||
Column({ space: 2 }) {
|
||
Text(item.time)
|
||
.fontSize(10)
|
||
.fontWeight(FontWeight.Medium)
|
||
.fontColor('#333333')
|
||
.maxLines(1)
|
||
if (item.endTime !== '' && item.endTime !== '全天') {
|
||
Text(item.endTime)
|
||
.fontSize(9)
|
||
.fontColor('#999999')
|
||
.maxLines(1)
|
||
}
|
||
}
|
||
.width(36)
|
||
.alignItems(HorizontalAlign.Start)
|
||
Text(item.title)
|
||
.fontSize(13)
|
||
.fontColor('#1A1A1A')
|
||
.maxLines(2)
|
||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||
.layoutWeight(1)
|
||
}
|
||
.alignItems(VerticalAlign.Center)
|
||
.width('100%')
|
||
.padding({ left: 6, right: 8, top: 4, bottom: 4 })
|
||
.borderRadius(8)
|
||
.backgroundColor('#F5F7FA')
|
||
}
|
||
|
||
/** 右侧空态:今日无日程 / 日程已全部结束 */
|
||
@Builder
|
||
emptyHint() {
|
||
Column() {
|
||
Text(this.todayCount === 0 ? '今日暂无日程' : '今日日程已结束')
|
||
.fontSize(12)
|
||
.fontColor('#8A8A8A')
|
||
.maxLines(2)
|
||
.textAlign(TextAlign.Center)
|
||
}
|
||
.width('100%')
|
||
.layoutWeight(1)
|
||
.justifyContent(FlexAlign.Center)
|
||
}
|
||
|
||
build() {
|
||
Row({ space: 10 }) {
|
||
// ===== 左半:与 2x2 一致(日期 / 星期 + 农历 / 今日条数大数字) =====
|
||
Column({ space: 6 }) {
|
||
Row({ space: 6 }) {
|
||
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)
|
||
.fontWeight(FontWeight.Bold)
|
||
.fontColor('#007DFF')
|
||
Column({ space: 2 }) {
|
||
Row({ space: 4 }) {
|
||
Text('今日日程')
|
||
.fontSize(11)
|
||
.fontColor('#1A1A1A')
|
||
.maxLines(1)
|
||
}
|
||
Text(this.todayCount === 0 ? '点击添加' : '点击查看')
|
||
.fontSize(9)
|
||
.fontColor('#8A8A8A')
|
||
.maxLines(1)
|
||
}
|
||
.alignItems(HorizontalAlign.Start)
|
||
}
|
||
.width('100%')
|
||
.layoutWeight(1)
|
||
.alignItems(VerticalAlign.Center)
|
||
}
|
||
.layoutWeight(1)
|
||
.height('100%')
|
||
|
||
Divider()
|
||
.vertical(true)
|
||
.height('92%')
|
||
.strokeWidth(0.5)
|
||
.color('#E5E5E5')
|
||
|
||
// ===== 右半:未来日程列表(可滚动,最多 15 条)=====
|
||
// ⭐ 原来固定 Column + 只取 2 条,用户想看后天的安排就看不到。
|
||
// 改为 List:自带虚拟化与滚动惯性,条目多也不占内存。
|
||
Column() {
|
||
if (this.parseOngoing().length === 0) {
|
||
this.emptyHint()
|
||
} else {
|
||
List({ space: 4 }) {
|
||
ForEach(this.parseOngoing(), (item: CardItem2x4, idx: number) => {
|
||
ListItem() {
|
||
this.ongoingRow(item)
|
||
}
|
||
}, (item: CardItem2x4, idx: number) => `o${idx}_${item.time}_${item.title}`)
|
||
}
|
||
.width('100%')
|
||
.layoutWeight(1)
|
||
.scrollBar(BarState.Off)
|
||
.edgeEffect(EdgeEffect.Spring)
|
||
}
|
||
}
|
||
.layoutWeight(1)
|
||
.height('100%')
|
||
}
|
||
.width('100%')
|
||
.height('100%')
|
||
.padding(12)
|
||
.backgroundColor('#FFFFFF')
|
||
.borderRadius(16)
|
||
// ⭐ 整卡可点:左右空白处也能进 App 并定位到当前显示日
|
||
.onClick(() => this.openApp())
|
||
}
|
||
}
|