v91 观影/任务卡加 4x4/6x4(滚动列表+新建+卡上完成任务);修任务卡无数据(解析器用错:看板嵌套需 parseTasks)
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* 家书 FamilyBook · 4×4 服务卡片(观影 · 上概述/新建,下滚动列表)
|
||||
*
|
||||
* 用户 2026-10-25:「观影卡片,加4x4,和4x6,上面概述,加新建按钮,
|
||||
* 下面瀑布流,就跟观影模块类似,可以滑下去看全部电影。」
|
||||
* ⇒ 上半:标题 + 更新时间 + 「+ 新建」(路由进 app 直开「记一场」);
|
||||
* 下半:`List`(卡片**不支持 Scroll、只支持 List**,见 FbCard4x4 的注释)
|
||||
* 装全部观影(数据侧倒序裁到 30 条),整卡可点进模块。
|
||||
*
|
||||
* ⚠️ 卡片不能自己发网络请求:只画 `fbMovieCardBuild()` 推过来的字段。
|
||||
* ⚠️ ForEach key 带 version 前缀(v89 教训,不重绘就是它的锅)。
|
||||
*/
|
||||
|
||||
let storageMvB = new LocalStorage();
|
||||
|
||||
/** 列表一行 */
|
||||
class FbMovieRow9a {
|
||||
key: string = '';
|
||||
title: string = '';
|
||||
dateText: string = '';
|
||||
cinema: string = '';
|
||||
rating: string = '';
|
||||
}
|
||||
|
||||
@Entry(storageMvB)
|
||||
@Component
|
||||
struct FbMovieCard4x4 {
|
||||
@LocalStorageProp('state') state: string = 'loading';
|
||||
@LocalStorageProp('hint') hint: string = '';
|
||||
@LocalStorageProp('updatedAt') updatedAt: string = '';
|
||||
@LocalStorageProp('yearCount') yearCount: number = 0;
|
||||
@LocalStorageProp('totalCount') totalCount: number = 0;
|
||||
@LocalStorageProp('listJson') listJson: string = '[]';
|
||||
@LocalStorageProp('version') version: string = '';
|
||||
|
||||
private rows(): FbMovieRow9a[] {
|
||||
try {
|
||||
const all: FbMovieRow9a[] = JSON.parse(this.listJson) as FbMovieRow9a[];
|
||||
return all;
|
||||
} catch (err) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
private openApp(): void {
|
||||
postCardAction(this, {
|
||||
action: 'router',
|
||||
abilityName: 'EntryAbility',
|
||||
params: { fbModule: 'movies' }
|
||||
});
|
||||
}
|
||||
|
||||
/** 「+ 新建」→ 进 app 直开「记一场」浮层(EntryAbility → modCreate 通道) */
|
||||
private addOne(): void {
|
||||
postCardAction(this, {
|
||||
action: 'router',
|
||||
abilityName: 'EntryAbility',
|
||||
params: { fbModule: 'movies', widgetAction: 'add' }
|
||||
});
|
||||
}
|
||||
|
||||
/** 列表单行:日期 + 片名/影院(+评分) */
|
||||
@Builder
|
||||
listRow(item: FbMovieRow9a) {
|
||||
Row({ space: 8 }) {
|
||||
Text(item.dateText !== '' ? item.dateText : '—')
|
||||
.fontSize(9)
|
||||
.fontColor('#A8988A')
|
||||
.width(46)
|
||||
.maxLines(1)
|
||||
Column({ space: 2 }) {
|
||||
Text(item.title)
|
||||
.fontSize(13)
|
||||
.fontColor('#2C231D')
|
||||
.maxLines(1)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
if (item.cinema !== '') {
|
||||
Text(item.cinema)
|
||||
.fontSize(9)
|
||||
.fontColor('#A8988A')
|
||||
.maxLines(1)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
}
|
||||
}
|
||||
.layoutWeight(1)
|
||||
.alignItems(HorizontalAlign.Start)
|
||||
if (item.rating !== '') {
|
||||
Text(item.rating)
|
||||
.fontSize(10)
|
||||
.fontColor('#C08A3E')
|
||||
.maxLines(1)
|
||||
}
|
||||
}
|
||||
.alignItems(VerticalAlign.Center)
|
||||
.width('100%')
|
||||
.padding({ left: 8, right: 8, top: 5, bottom: 5 })
|
||||
.borderRadius(8)
|
||||
.backgroundColor('#F6F2F7')
|
||||
}
|
||||
|
||||
/** 空态 */
|
||||
@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)
|
||||
/* ⭐ 新建按钮(子组件 onClick 优先于整卡,不会连带进 app) */
|
||||
Button('+')
|
||||
.fontSize(13)
|
||||
.fontColor('#FFFFFF')
|
||||
.width(26)
|
||||
.height(26)
|
||||
.borderRadius(13)
|
||||
.padding(0)
|
||||
.backgroundColor('#7A4E8C')
|
||||
.onClick((): void => this.addOne())
|
||||
}
|
||||
.width('100%')
|
||||
|
||||
// ===== 概述行 =====
|
||||
Row({ space: 8 }) {
|
||||
Text(`今年 ${this.yearCount} 场`)
|
||||
.fontSize(11)
|
||||
.fontColor('#7A4E8C')
|
||||
.fontWeight(FontWeight.Medium)
|
||||
Text(`共 ${this.totalCount} 条`)
|
||||
.fontSize(10)
|
||||
.fontColor(this.hint !== '' ? '#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: FbMovieRow9a) => {
|
||||
ListItem() {
|
||||
this.listRow(item)
|
||||
}
|
||||
}, (item: FbMovieRow9a): string => `ml_${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())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* 家书 FamilyBook · 6×4 服务卡片(观影 · 上概述/新建,下滚动列表)
|
||||
*
|
||||
* 用户 2026-10-25:「观影卡片,加4x4,和4x6,上面概述,加新建按钮,
|
||||
* 下面瀑布流,就跟观影模块类似,可以滑下去看全部电影。」
|
||||
* ⇒ 上半:标题 + 更新时间 + 「+ 新建」(路由进 app 直开「记一场」);
|
||||
* 下半:`List`(卡片**不支持 Scroll、只支持 List**,见 FbCard4x4 的注释)
|
||||
* 装全部观影(数据侧倒序裁到 30 条),整卡可点进模块。
|
||||
*
|
||||
* ⚠️ 卡片不能自己发网络请求:只画 `fbMovieCardBuild()` 推过来的字段。
|
||||
* ⚠️ ForEach key 带 version 前缀(v89 教训,不重绘就是它的锅)。
|
||||
*/
|
||||
|
||||
let storageMvB2 = new LocalStorage();
|
||||
|
||||
/** 列表一行 */
|
||||
class FbMovieRow9b {
|
||||
key: string = '';
|
||||
title: string = '';
|
||||
dateText: string = '';
|
||||
cinema: string = '';
|
||||
rating: string = '';
|
||||
}
|
||||
|
||||
@Entry(storageMvB2)
|
||||
@Component
|
||||
struct FbMovieCard6x4 {
|
||||
@LocalStorageProp('state') state: string = 'loading';
|
||||
@LocalStorageProp('hint') hint: string = '';
|
||||
@LocalStorageProp('updatedAt') updatedAt: string = '';
|
||||
@LocalStorageProp('yearCount') yearCount: number = 0;
|
||||
@LocalStorageProp('totalCount') totalCount: number = 0;
|
||||
@LocalStorageProp('listJson') listJson: string = '[]';
|
||||
@LocalStorageProp('version') version: string = '';
|
||||
|
||||
private rows(): FbMovieRow9b[] {
|
||||
try {
|
||||
const all: FbMovieRow9b[] = JSON.parse(this.listJson) as FbMovieRow9b[];
|
||||
return all;
|
||||
} catch (err) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
private openApp(): void {
|
||||
postCardAction(this, {
|
||||
action: 'router',
|
||||
abilityName: 'EntryAbility',
|
||||
params: { fbModule: 'movies' }
|
||||
});
|
||||
}
|
||||
|
||||
/** 「+ 新建」→ 进 app 直开「记一场」浮层(EntryAbility → modCreate 通道) */
|
||||
private addOne(): void {
|
||||
postCardAction(this, {
|
||||
action: 'router',
|
||||
abilityName: 'EntryAbility',
|
||||
params: { fbModule: 'movies', widgetAction: 'add' }
|
||||
});
|
||||
}
|
||||
|
||||
/** 列表单行:日期 + 片名/影院(+评分) */
|
||||
@Builder
|
||||
listRow(item: FbMovieRow9b) {
|
||||
Row({ space: 8 }) {
|
||||
Text(item.dateText !== '' ? item.dateText : '—')
|
||||
.fontSize(9)
|
||||
.fontColor('#A8988A')
|
||||
.width(46)
|
||||
.maxLines(1)
|
||||
Column({ space: 2 }) {
|
||||
Text(item.title)
|
||||
.fontSize(13)
|
||||
.fontColor('#2C231D')
|
||||
.maxLines(1)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
if (item.cinema !== '') {
|
||||
Text(item.cinema)
|
||||
.fontSize(9)
|
||||
.fontColor('#A8988A')
|
||||
.maxLines(1)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
}
|
||||
}
|
||||
.layoutWeight(1)
|
||||
.alignItems(HorizontalAlign.Start)
|
||||
if (item.rating !== '') {
|
||||
Text(item.rating)
|
||||
.fontSize(10)
|
||||
.fontColor('#C08A3E')
|
||||
.maxLines(1)
|
||||
}
|
||||
}
|
||||
.alignItems(VerticalAlign.Center)
|
||||
.width('100%')
|
||||
.padding({ left: 8, right: 8, top: 5, bottom: 5 })
|
||||
.borderRadius(8)
|
||||
.backgroundColor('#F6F2F7')
|
||||
}
|
||||
|
||||
/** 空态 */
|
||||
@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)
|
||||
/* ⭐ 新建按钮(子组件 onClick 优先于整卡,不会连带进 app) */
|
||||
Button('+')
|
||||
.fontSize(13)
|
||||
.fontColor('#FFFFFF')
|
||||
.width(26)
|
||||
.height(26)
|
||||
.borderRadius(13)
|
||||
.padding(0)
|
||||
.backgroundColor('#7A4E8C')
|
||||
.onClick((): void => this.addOne())
|
||||
}
|
||||
.width('100%')
|
||||
|
||||
// ===== 概述行 =====
|
||||
Row({ space: 8 }) {
|
||||
Text(`今年 ${this.yearCount} 场`)
|
||||
.fontSize(11)
|
||||
.fontColor('#7A4E8C')
|
||||
.fontWeight(FontWeight.Medium)
|
||||
Text(`共 ${this.totalCount} 条`)
|
||||
.fontSize(10)
|
||||
.fontColor(this.hint !== '' ? '#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: FbMovieRow9b) => {
|
||||
ListItem() {
|
||||
this.listRow(item)
|
||||
}
|
||||
}, (item: FbMovieRow9b): string => `ml_${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())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
/**
|
||||
* 家书 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())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
/**
|
||||
* 家书 FamilyBook · 6×4 服务卡片(任务 · 上概述/新建,下滚动列表 + 卡上完成)
|
||||
*
|
||||
* 用户 2026-10-25:「任务卡片,也需要4x4和4x6,跟任务模块类似,列出所有任务,
|
||||
* 可以在卡片直接点击完成。有新建按钮。」
|
||||
* ⇒ 上半:标题 + 更新时间 + 「+ 新建」(路由进 app 直开「新建任务」);
|
||||
* 下半:`List` 装全部待办(数据侧排序:逾期 > 进行中 > 普通),
|
||||
* 每行右侧一颗「✓ 完成」→ `postCardAction({action:'message', taskDone:id})`
|
||||
* → `FbFormAbility.markTaskDone()` 调接口 → 重算重推。
|
||||
*
|
||||
* ⚠️ 卡片不能自己发网络请求,完成动作必须借道 FormExtensionAbility。
|
||||
* ⚠️ ForEach key 带 version 前缀(v89 教训)。
|
||||
*/
|
||||
|
||||
let storageTkB2 = new LocalStorage();
|
||||
|
||||
/** 列表一行 */
|
||||
class FbTaskRow9b {
|
||||
id: number = 0;
|
||||
key: string = '';
|
||||
title: string = '';
|
||||
dueText: string = '';
|
||||
status: string = '';
|
||||
boardColor: string = '#A8574F';
|
||||
overdue: boolean = false;
|
||||
}
|
||||
|
||||
@Entry(storageTkB2)
|
||||
@Component
|
||||
struct FbTaskCard6x4 {
|
||||
@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(): FbTaskRow9b[] {
|
||||
try {
|
||||
const all: FbTaskRow9b[] = JSON.parse(this.allJson) as FbTaskRow9b[];
|
||||
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: FbTaskRow9b): void {
|
||||
postCardAction(this, {
|
||||
action: 'message',
|
||||
params: { taskDone: `${item.id}` }
|
||||
});
|
||||
}
|
||||
|
||||
/** 列表单行:色条 + 标题/截止 + ✓ 完成 */
|
||||
@Builder
|
||||
listRow(item: FbTaskRow9b) {
|
||||
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: FbTaskRow9b) => {
|
||||
ListItem() {
|
||||
this.listRow(item)
|
||||
}
|
||||
}, (item: FbTaskRow9b): 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())
|
||||
}
|
||||
}
|
||||
@@ -211,6 +211,24 @@ export struct FbMoviesPage {
|
||||
aboutToAppear(): void {
|
||||
FB_BACK.register('movies', (): boolean => this.backOnce());
|
||||
this.bootstrap();
|
||||
/* 🔴 v91:桌面观影卡的「+ 新建」→ 冷启动直接开「记一场」浮层
|
||||
(热启动走下面的 @Monitor)。用掉就清 key:否则用户关掉浮层后
|
||||
每次回本页都会自己弹出来(日程页踩过)。 */
|
||||
this.applyCardAdd();
|
||||
}
|
||||
|
||||
/** 消费卡片带来的"要开新建浮层"意图(有则用掉并打开) */
|
||||
private applyCardAdd(): void {
|
||||
if (fbAppState().modCreateKey === 'movies') {
|
||||
fbAppState().modCreateKey = '';
|
||||
this.newOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
/** 卡片在应用已开着时被点(`onNewWant` 改了全局状态)→ 这里响应 */
|
||||
@Monitor('app.modCreateSeq')
|
||||
onCardAdd(): void {
|
||||
this.applyCardAdd();
|
||||
}
|
||||
|
||||
/** 该电影的海报是否已确认加载失败 */
|
||||
|
||||
@@ -148,6 +148,24 @@ export struct FbTasksPage {
|
||||
FB_BACK.register('tasks', (): boolean => this.backOnce());
|
||||
this.load();
|
||||
this.loadMeta();
|
||||
/* 🔴 v91:桌面任务卡的「+ 新建」→ 冷启动直接开「新建任务」浮层
|
||||
(热启动走下面的 @Monitor)。用掉就清 key:否则用户关掉浮层后
|
||||
每次回本页都会自己弹出来(日程页踩过)。 */
|
||||
this.applyCardAdd();
|
||||
}
|
||||
|
||||
/** 消费卡片带来的"要开新建浮层"意图(有则用掉并打开) */
|
||||
private applyCardAdd(): void {
|
||||
if (fbAppState().modCreateKey === 'tasks') {
|
||||
fbAppState().modCreateKey = '';
|
||||
this.openNew();
|
||||
}
|
||||
}
|
||||
|
||||
/** 卡片在应用已开着时被点(`onNewWant` 改了全局状态)→ 这里响应 */
|
||||
@Monitor('app.modCreateSeq')
|
||||
onCardAdd(): void {
|
||||
this.applyCardAdd();
|
||||
}
|
||||
|
||||
/** 拉全部看板 + 分类(筛选条、编辑表单、面板的"挪分组"共用一份) */
|
||||
|
||||
Reference in New Issue
Block a user