Files
FamilyBook_harmonyos/entry/src/main/ets/pages/card/FbTaskCard4x4.ets
T

218 lines
6.5 KiB
Plaintext
Raw Normal View History

/**
* 家书 FamilyBook · 4×4 服务卡片(任务 · 上概述/新建,下滚动列表 + 卡上完成)
*
* 用户 2026-10-25:「任务卡片,也需要4x4和4x6,跟任务模块类似,列出所有任务,
* 可以在卡片直接点击完成。有新建按钮。」
* ⇒ 上半:标题 + 更新时间 + 「+ 新建」(路由进 app 直开「新建任务」);
* 下半:`List` 装全部待办(数据侧排序:逾期 > 进行中 > 普通),
* 每行右侧一颗「✓ 完成」→ `postCardAction({action:'message', taskDone:id})`
* → `FbFormAbility.markTaskDone()` 调接口 → 重算重推。
*
* ⚠️ 卡片不能自己发网络请求,完成动作必须借道 FormExtensionAbility。
* ⚠️ ForEach key 带 version 前缀(v89 教训)。
*/
let storageTkB = new LocalStorage();
/** 列表一行 */
class FbTaskRow9a {
id: number = 0;
key: string = '';
title: string = '';
dueText: string = '';
status: string = '';
boardColor: string = '#A8574F';
overdue: boolean = false;
}
@Entry(storageTkB)
@Component
struct FbTaskCard4x4 {
@LocalStorageProp('state') state: string = 'loading';
@LocalStorageProp('hint') hint: string = '';
@LocalStorageProp('updatedAt') updatedAt: string = '';
@LocalStorageProp('openCount') openCount: number = 0;
@LocalStorageProp('overdueCount') overdueCount: number = 0;
@LocalStorageProp('allJson') allJson: string = '[]';
@LocalStorageProp('version') version: string = '';
private rows(): FbTaskRow9a[] {
try {
const all: FbTaskRow9a[] = JSON.parse(this.allJson) as FbTaskRow9a[];
return all;
} catch (err) {
return [];
}
}
private openApp(): void {
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: { fbModule: 'tasks' }
});
}
/** 「+ 新建」→ 进 app 直开「新建任务」浮层(EntryAbility → modCreate 通道) */
private addOne(): void {
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: { fbModule: 'tasks', widgetAction: 'add' }
});
}
/** 卡上直接完成(id 必须转字符串:param 数值按 int32 传) */
private complete(item: FbTaskRow9a): void {
postCardAction(this, {
action: 'message',
params: { taskDone: `${item.id}` }
});
}
/** 列表单行:色条 + 标题/截止 + ✓ 完成 */
@Builder
listRow(item: FbTaskRow9a) {
Row({ space: 8 }) {
Column()
.width(3)
.height(34)
.borderRadius(2)
.backgroundColor(item.overdue ? '#C0533F' : item.boardColor)
Column({ space: 2 }) {
Text(item.title)
.fontSize(13)
.fontColor('#2C231D')
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Row({ space: 4 }) {
if (item.overdue) {
Text('逾期')
.fontSize(9)
.fontColor('#C0533F')
.maxLines(1)
}
if (item.dueText !== '') {
Text(item.dueText)
.fontSize(9)
.fontColor(item.overdue ? '#C0533F' : '#A8988A')
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.layoutWeight(1)
} else {
Text(item.status === 'doing' ? '进行中' : '待办')
.fontSize(9)
.fontColor('#A8988A')
.maxLines(1)
}
}
.width('100%')
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
/* ⭐ 卡上直接点完成(子组件 onClick 优先于整卡,不会连带进 app) */
Text('✓')
.fontSize(14)
.fontColor('#FFFFFF')
.width(26)
.height(26)
.textAlign(TextAlign.Center)
.borderRadius(13)
.backgroundColor(item.overdue ? '#C0533F' : '#A8574F')
.onClick((): void => this.complete(item))
}
.alignItems(VerticalAlign.Center)
.width('100%')
.padding({ left: 8, right: 8, top: 5, bottom: 5 })
.borderRadius(8)
.backgroundColor(item.overdue ? '#FBEDEA' : '#F7F2EC')
}
/** 空态 */
@Builder
emptyHint() {
Column() {
Text(this.hint !== '' ? this.hint : '没有待办任务')
.fontSize(12)
.fontColor(this.hint !== '' ? '#C0533F' : '#A8988A')
.maxLines(3)
.textAlign(TextAlign.Center)
}
.width('100%')
.layoutWeight(1)
.justifyContent(FlexAlign.Center)
}
build() {
Column({ space: 6 }) {
// ===== 顶栏:标题 + 更新时间 + + 新建 =====
Row({ space: 6 }) {
Text('任务')
.fontSize(15)
.fontWeight(FontWeight.Bold)
.fontColor('#2C231D')
.maxLines(1)
.layoutWeight(1)
Text(this.updatedAt)
.fontSize(9)
.fontColor('#A8988A')
.maxLines(1)
Button('+')
.fontSize(13)
.fontColor('#FFFFFF')
.width(26)
.height(26)
.borderRadius(13)
.padding(0)
.backgroundColor('#A8574F')
.onClick((): void => this.addOne())
}
.width('100%')
// ===== 概述行 =====
Row({ space: 8 }) {
Text(`待办 ${this.openCount} 条`)
.fontSize(11)
.fontColor('#A8574F')
.fontWeight(FontWeight.Medium)
Text(this.hint !== '' ? this.hint
: (this.overdueCount > 0 ? '逾期 ' + this.overdueCount.toString() + ' 条'
: '点 ✓ 直接完成'))
.fontSize(10)
.fontColor(this.hint !== '' || this.overdueCount > 0 ? '#C0533F' : '#A8988A')
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.layoutWeight(1)
}
.width('100%')
Divider()
.strokeWidth(0.5)
.color('#EFE6D8')
// ===== 全部待办(可滚动,可直接完成)=====
if (this.rows().length === 0) {
this.emptyHint()
} else {
List({ space: 5 }) {
ForEach(this.rows(), (item: FbTaskRow9a) => {
ListItem() {
this.listRow(item)
}
}, (item: FbTaskRow9a): string => `tl_${this.version}_${item.key}`)
}
.width('100%')
.layoutWeight(1)
.scrollBar(BarState.Off)
}
}
.width('100%')
.height('100%')
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(16)
// ⭐ 整卡可点
.onClick((): void => this.openApp())
}
}