style(粘贴/待办卡片): 粘贴按钮与「地图」同款外观;待办卡片对齐 App 内样式

## 1. 粘贴按钮改成与「地图」一样

原来只用系统默认样式,与旁边的「地图」按钮风格割裂。
安全控件支持 fontSize / fontColor / backgroundColor / borderRadius / padding
(见 security_component.d.ts),所以可以完全对齐:

  13 号字 · brand_text 色 · card_bg 底 · 圆角 12 · 高 44

与「地图」并排、同高、同样留白。文字内容由系统枚举决定
(`PasteDescription` 只有 `PASTE` 一个值,即「粘贴」)。

真机实测两个按钮并排:
  PasteButton  [680,1802][837,1940]  文字='粘贴'
  Text(地图)   [862,1802][1018,1940]  文字='地图'
同高同排,13 号字。

## 2. 待办卡片列表对齐 App 内样式

用户指出:卡片里的待办样式(灰勾选框 + 色条)与 App 内待办列表不一致,
两套设计没必要。App 内 `Index.todoRow` 的样式是:

  - 勾选圈 **颜色跟随日历本**(未完成=空心环 2px,已完成=实心+✓)
  - 标题 15 号,已完成加删除线
  - 副行 12 号 = 截止时间 · 日历本
  - 行背景 card_bg,圆角 12,1px 边框

卡片侧改为同一套结构,只按卡片尺寸把号数收小:

| 卡片 | 圈 | 标题 | 副行 |
|---|---|---|---|
| 4×4 / 6×4 | 18 | 13 | 10 |
| 2×4 | 16 | 12 | 9 |

去掉原来的色条和灰色勾选框 —— 用日历本色圆圈,与 App 内视觉一致。

数据侧给 `TodoCardItem` 增加 `subText`(「截止时间 · 日历本」拼好的副行文字),
由数据层统一拼接,避免三张卡片各拼一遍导致不一致。

## 一致性校验
逐行 diff 确认 4×4 与 6×4 的 `todoRow` **完全一致**(归一化类型名后逐字符相等);
2×4 结构相同,仅尺寸小一档。

## 编译
`BUILD SUCCESSFUL`

## 未验证
粘贴按钮已确认渲染与布局(dump 坐标/文字),视觉观感需上机目视;
待办卡片需删除桌面旧卡片后重新添加才能看到新样式。
This commit is contained in:
2026-11-03 23:23:25 +08:00
parent b8d5aa630c
commit 8b8b69b287
5 changed files with 98 additions and 80 deletions
@@ -43,6 +43,11 @@ export class TodoCardItem {
title: string = ''; title: string = '';
/** 截止时间文字:'今天 18:00' / '明天' / '无日期' */ /** 截止时间文字:'今天 18:00' / '明天' / '无日期' */
dueText: string = ''; dueText: string = '';
/**
* 副行文字:截止时间 · 日历本(与 App 内 todoSubtitle 同款呈现)。
* 在数据侧拼好,卡片侧直接显示,避免各卡片各拼一遍导致不一致。
*/
subText: string = '';
color: string = '#007DFF'; color: string = '#007DFF';
calName: string = ''; calName: string = '';
/** 是否已完成(已完成的不出现在待办卡片列表里,此字段留作扩展) */ /** 是否已完成(已完成的不出现在待办卡片列表里,此字段留作扩展) */
@@ -521,6 +526,8 @@ export class CardDataService {
it.dueText = `${due.getMonth() + 1}/${due.getDate()}${timePart}`; it.dueText = `${due.getMonth() + 1}/${due.getDate()}${timePart}`;
} }
} }
// 副行:截止时间 · 日历本(本名为空时只显示截止时间)
it.subText = it.calName !== '' ? `${it.dueText} · ${it.calName}` : it.dueText;
items.push(it); items.push(it);
} }
+6 -5
View File
@@ -1750,16 +1750,17 @@ struct EventEditPage {
// (无需申请 READ_PASTEBOARD —— 那是受限权限,普通应用申请会被拒) // (无需申请 READ_PASTEBOARD —— 那是受限权限,普通应用申请会被拒)
// //
// ⚠️ icon/text/buttonType **只能走构造参数**,写成链式属性会编译报错。 // ⚠️ icon/text/buttonType **只能走构造参数**,写成链式属性会编译报错。
// ⚠️ 不要自定义背景色:SDK 明确把"字体颜色与背景相近"列为样式违规 // 文字内容由系统枚举决定(PasteDescription 只有 PASTE 一个值,即「粘贴」)。
// (错误码 2),会让人字形灰淡难辨、甚至授权失败。 // 外观与右边的「地图」按钮保持一致:同字号、同色、同底色、同圆角、同高度。
// 交给系统默认样式即可,只调尺寸与圆角。
PasteButton({ PasteButton({
icon: PasteIconStyle.LINES,
text: PasteDescription.PASTE, text: PasteDescription.PASTE,
buttonType: ButtonType.Capsule buttonType: ButtonType.Capsule
}) })
.fontSize(13)
.fontColor($r('app.color.brand_text'))
.backgroundColor($r('app.color.card_bg'))
.height(44) .height(44)
.padding({ left: 10, right: 14 }) .padding({ left: 12, right: 12 })
.borderRadius(12) .borderRadius(12)
.onClick(() => { .onClick(() => {
this.pastePlace(); this.pastePlace();
@@ -8,6 +8,8 @@ class TodoItem2x4 {
id: number = 0; id: number = 0;
title: string = ''; title: string = '';
dueText: string = ''; dueText: string = '';
/** 副行:截止时间 · 日历本 */
subText: string = '';
color: string = '#007DFF'; color: string = '#007DFF';
calName: string = ''; calName: string = '';
completed: boolean = false; completed: boolean = false;
@@ -78,45 +80,46 @@ struct TodoWidget2x4Card {
}); });
} }
/** 圆形勾选框(未完成状态:空心圆) */
@Builder
checkbox(id: number) {
// 同 4x4:不用 SDK 26 的 Circle().stroke(),改用 border 圆角
Column()
.width(18)
.height(18)
.borderRadius(9)
.border({ width: 1.5, color: '#B0B0B0' })
.backgroundColor(Color.Transparent)
.onClick(() => this.checkTodo(id))
}
/** 右侧单条待办:勾选框 + 标题 + 截止时间 */ /** 右侧单条待办:勾选框 + 标题 + 截止时间 */
/**
* 单条待办:与 4×4 / 6×4 同款结构(圆圈用日历本色 + 标题 + 副行),
* 尺寸再收一档 —— 2×4 只有 2 格高,圈 16 / 标题 12 / 副行 9。
*/
@Builder @Builder
todoRow(item: TodoItem2x4) { todoRow(item: TodoItem2x4) {
Row({ space: 6 }) { Row({ space: 6 }) {
this.checkbox(item.id) // 勾选圈:颜色用日历本色,点击即完成
Row() {
Text('')
.width(16)
.height(16)
.borderRadius(8)
.border({ width: 2, color: item.color })
}
.padding({ right: 2 })
.onClick(() => {
this.checkTodo(item.id);
})
Column({ space: 1 }) { Column({ space: 1 }) {
// 标题限 1 行:2x4 高度只有 2 格,2 行会让一屏只放得下 1 条多,滑动失去意义
Text(item.title) Text(item.title)
.fontSize(12) .fontSize(12)
.fontColor('#1A1A1A') .fontColor('#1A1A1A')
.maxLines(1) .maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis }) .textOverflow({ overflow: TextOverflow.Ellipsis })
.width('100%') Text(item.subText)
Text(item.dueText)
.fontSize(9) .fontSize(9)
.fontColor(item.dueText.indexOf('逾期') >= 0 ? '#FF3B30' : '#8A8A8A') .fontColor('#8A8A8A')
.maxLines(1) .maxLines(1)
.width('100%') .textOverflow({ overflow: TextOverflow.Ellipsis })
} }
.layoutWeight(1) .layoutWeight(1)
.alignItems(HorizontalAlign.Start) .alignItems(HorizontalAlign.Start)
} }
.alignItems(VerticalAlign.Center) .alignItems(VerticalAlign.Center)
.width('100%') .width('100%')
.padding({ left: 2, right: 6, top: 4, bottom: 4 }) .padding({ left: 4, right: 8, top: 5, bottom: 5 })
.borderRadius(8) .borderRadius(9)
.backgroundColor('#F5F7FA') .backgroundColor('#F5F7FA')
} }
@@ -8,6 +8,8 @@ class TodoItem4x4 {
id: number = 0; id: number = 0;
title: string = ''; title: string = '';
dueText: string = ''; dueText: string = '';
/** 副行:截止时间 · 日历本 */
subText: string = '';
color: string = '#007DFF'; color: string = '#007DFF';
calName: string = ''; calName: string = '';
completed: boolean = false; completed: boolean = false;
@@ -83,43 +85,48 @@ struct TodoWidget4x4Card {
.onClick(() => this.checkTodo(id)) .onClick(() => this.checkTodo(id))
} }
/** 单条待办:勾选框 + 左色竖条 + 标题/截止时间 + 日历本名 */ /**
* 单条待办:与 App 内 Index.todoRow 同款结构 ——
* 圆圈(颜色跟随日历本)+ 标题 + 副行「截止时间 · 日历本」
*
* ⚠️ 未完成=空心环、已完成=实心+✓,这套语义与 App 内一致;
* 卡片上只显示未完成,所以这里主要是空心环。
* ⚠️ 4×4 与 6×4 的 todoRow 必须**完全一致**(用户要求),改一处要同步另一处。
*/
@Builder @Builder
todoRow(item: TodoItem4x4) { todoRow(item: TodoItem4x4) {
Row({ space: 8 }) { Row({ space: 8 }) {
this.checkbox(item.id) // 勾选圈:颜色用日历本色(与 App 内一致),点击即完成
Column() Row() {
.width(3) Text('')
.height(32) .width(18)
.borderRadius(2) .height(18)
.backgroundColor(item.color) .borderRadius(9)
Column({ space: 3 }) { .border({ width: 2, color: item.color })
}
.padding({ right: 2 })
.onClick(() => {
this.checkTodo(item.id);
})
Column({ space: 2 }) {
Text(item.title) Text(item.title)
.fontSize(13) .fontSize(13)
.fontColor('#1A1A1A') .fontColor('#1A1A1A')
.maxLines(2) .maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Text(item.subText)
.fontSize(10)
.fontColor('#8A8A8A')
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis }) .textOverflow({ overflow: TextOverflow.Ellipsis })
Row({ space: 6 }) {
Text(item.dueText)
.fontSize(10)
.fontColor(item.dueText.indexOf('逾期') >= 0 ? '#FF3B30' : '#8A8A8A')
.maxLines(1)
if (item.calName !== '') {
Text(item.calName)
.fontSize(10)
.fontColor(item.color)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.constraintSize({ maxWidth: '55%' })
}
}
} }
.layoutWeight(1)
.alignItems(HorizontalAlign.Start) .alignItems(HorizontalAlign.Start)
.layoutWeight(1)
} }
.alignItems(VerticalAlign.Center) .alignItems(VerticalAlign.Center)
.width('100%') .width('100%')
.padding({ left: 4, right: 10, top: 6, bottom: 6 }) .padding({ left: 6, right: 8, top: 6, bottom: 6 })
.borderRadius(10) .borderRadius(10)
.backgroundColor('#F5F7FA') .backgroundColor('#F5F7FA')
} }
@@ -8,6 +8,8 @@ class TodoItem6x4 {
id: number = 0; id: number = 0;
title: string = ''; title: string = '';
dueText: string = ''; dueText: string = '';
/** 副行:截止时间 · 日历本 */
subText: string = '';
color: string = '#007DFF'; color: string = '#007DFF';
calName: string = ''; calName: string = '';
completed: boolean = false; completed: boolean = false;
@@ -84,49 +86,47 @@ struct TodoWidget6x4Card {
} }
/** /**
* 单条待办:勾选框 + 色条 + 标题 + 副行(截止时间 · 日历本)。 * 单条待办:与 App 内 Index.todoRow 同款结构 ——
* 圆圈(颜色跟随日历本)+ 标题 + 副行「截止时间 · 日历本」
* *
* ⚠️ 与 TodoWidget4x4 的 todoRow **保持完全一致**(用户要求两个卡片显示效果一模一样)。 * ⚠️ 未完成=空心环、已完成=实心+✓,这套语义与 App 内一致;
* 改这里必须同步改那边,否则又会不一致。 * 卡片上只显示未完成,所以这里主要是空心环。
* 选这个结构而不是"独立列 + 固定宽度":标题 layoutWeight(1) 能吃满剩余宽度, * ⚠️ 4×4 与 6×4 的 todoRow 必须**完全一致**(用户要求),改一处要同步另一处。
* 在窄卡片里也不会被挤出屏幕。
*/ */
@Builder @Builder
todoRow(item: TodoItem6x4) { todoRow(item: TodoItem6x4) {
Row({ space: 8 }) { Row({ space: 8 }) {
this.checkbox(item.id) // 勾选圈:颜色用日历本色(与 App 内一致),点击即完成
Column() Row() {
.width(3) Text('')
.height(32) .width(18)
.borderRadius(2) .height(18)
.backgroundColor(item.color) .borderRadius(9)
Column({ space: 3 }) { .border({ width: 2, color: item.color })
}
.padding({ right: 2 })
.onClick(() => {
this.checkTodo(item.id);
})
Column({ space: 2 }) {
Text(item.title) Text(item.title)
.fontSize(13) .fontSize(13)
.fontColor('#1A1A1A') .fontColor('#1A1A1A')
.maxLines(2) .maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Text(item.subText)
.fontSize(10)
.fontColor('#8A8A8A')
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis }) .textOverflow({ overflow: TextOverflow.Ellipsis })
Row({ space: 6 }) {
Text(item.dueText)
.fontSize(10)
.fontColor(item.dueText.indexOf('逾期') >= 0 ? '#FF3B30' : '#8A8A8A')
.maxLines(1)
if (item.calName !== '') {
Text(item.calName)
.fontSize(10)
.fontColor(item.color)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.constraintSize({ maxWidth: '55%' })
}
}
} }
.layoutWeight(1)
.alignItems(HorizontalAlign.Start) .alignItems(HorizontalAlign.Start)
.layoutWeight(1)
} }
.alignItems(VerticalAlign.Center) .alignItems(VerticalAlign.Center)
.width('100%') .width('100%')
.padding({ left: 4, right: 10, top: 6, bottom: 6 }) .padding({ left: 6, right: 8, top: 6, bottom: 6 })
.borderRadius(10) .borderRadius(10)
.backgroundColor('#F5F7FA') .backgroundColor('#F5F7FA')
} }