v73:桌面卡片刷新修复 + 首页日程/任务就地弹详情卡 + 一键导航
用户 2026-10-21 三条:
「卡片里面的数据并不刷新……应用里面的日程已经有了,但卡片上的日程却并不刷新」
「首页上的那个日程和任务,点击之后都应该像日程和任务里面的卡片一样弹出来,
不要进入到日程里面……有地址的还可以直接一键导航」
「任务也是一样,点击任务之后弹出来详情,可以标记它是不是完成了」
① 卡片不刷新(根因)
卡片进程(FormExtensionAbility)与应用进程靠 preferences 传 formIds / 翻页偏移。
SDK 明文禁止跨进程用 preferences,且 getPreferences() 在**进程内**缓存实例、
同名再取不读盘 ⇒ 应用进程可能永久持有空注册表 ⇒ pushAll() 首行
`if (list.length === 0) return` 直接返回 ⇒ 卡片只靠系统 30 分钟自刷。
修:store() 每次先 removePreferencesFromCacheSync 再取;并写死单写者纪律
(register/unregister/setOffset/clearOffset 只由卡片进程调,应用进程只读)。
② 首页点日程 → 弹 FbEventSheet(canEdit: false,首页不给编辑入口)
有地址的行整行可点 + 右侧「➤ 导航」胶囊;「编辑日程」才跳日历模块
(编辑是日历页的职责,不在首页复制第二份表单)。
③ 首页点任务 → 新的 FbTaskDetailSheet
状态圆可点(勾完成)、标题带删除线、底部「标记为已完成/未完成」+「改一改」。
状态**必须走 POST /tasks/<id>/status**(PATCH 会忽略 status)。
④ 一键导航 FbNav.ets:startAbility 隐式 Want(不是 openLink,那是 App Linking);
canOpenLink 需 module.json5 声明 querySchemes(未声明恒 false 且不抛错);
高德/百度/花瓣/腾讯按序探测,一个地图都没装则复制地点到剪贴板 + Toast。
⑤ 首页登记 FB_BACK(与其余 10 个模块一致),根容器 Stack(浮层是兄弟节点,
不能当 Column 子项排,否则卡片被挤出屏幕)。
静态守卫 905/0、v73 反向验证 17/0、assembleHap 通过、已装机启动。
This commit is contained in:
@@ -21,6 +21,7 @@ import { FbCalBook } from '../model/Types';
|
||||
import { FbDot } from './Widgets';
|
||||
import { FbSheetRing } from './FbSheetRing';
|
||||
import { FbSheetClose } from './FbSheetClose';
|
||||
import { fbCanNavigate } from '../common/FbNav';
|
||||
|
||||
const FB_SHEET_BOTTOM_GAP: number = 16;
|
||||
|
||||
@@ -44,6 +45,19 @@ export struct FbEventSheet {
|
||||
@Param accent: string = '';
|
||||
/** 能不能编辑(不能写的日历本不给"编辑"按钮) */
|
||||
@Param canEdit: boolean = false;
|
||||
/**
|
||||
* 点「导航」。
|
||||
*
|
||||
* 🔴 v73(用户 2026-10-21「有地址的可以一键导航」):
|
||||
* 地点那一行从"纯文字"变成"可点"。
|
||||
* ⚠️ 为什么把动作**抛给宿主**而不是在这里直接拉地图:
|
||||
* 拉起应用要 `UIAbilityContext`,而组件里 `getUIContext().getHostContext()`
|
||||
* 拿到的是 `common.Context`,还得强转 —— 那个强转在**卡片/浮层重建**时可能拿到
|
||||
* undefined。宿主页本来就有可靠的上下文来源,且"要不要拉地图"属于导航决策
|
||||
* (跟"当前在哪个模块"有关),不是这张卡的职责。
|
||||
*/
|
||||
@Event onNavigate: () => void = (): void => {
|
||||
};
|
||||
@Event onClose: () => void = (): void => {
|
||||
};
|
||||
@Event onEdit: () => void = (): void => {
|
||||
@@ -58,6 +72,21 @@ export struct FbEventSheet {
|
||||
return m !== undefined ? fbThemeVars(m.color).brand : FbTint.brand;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取主题色的**半透明版本**(八位色 `#AARRGGBB`)。
|
||||
*
|
||||
* 🔴 本项目踩过:ArkUI 的八位色是 **alpha 在前**(`#AARRGGBB`),
|
||||
* 与 CSS 的 `#RRGGBBAA` **相反** ⇒ 直接把 CSS 那套搬过来会得到
|
||||
* "几乎透明的怪色"或者干脆渲染成黑色。
|
||||
* ⚠️ 还要兼容"传进来的本身就是八位色"(模块色可能带 alpha):
|
||||
* 先剥掉已有的 alpha,再拼新的,否则会得到 10 位非法色值。
|
||||
*/
|
||||
private accentAlpha(aa: string): string {
|
||||
const c: string = this.accentColor().replace('#', '');
|
||||
const rgb: string = c.length >= 8 ? c.substring(2) : c;
|
||||
return '#' + aa + rgb;
|
||||
}
|
||||
|
||||
/**
|
||||
* 标题文字。
|
||||
*
|
||||
@@ -195,6 +224,57 @@ export struct FbEventSheet {
|
||||
.alignItems(VerticalAlign.Top)
|
||||
}
|
||||
|
||||
/**
|
||||
* 「地点」这一行 —— 比普通 `row()` 多一颗「导航」标签,整行可点。
|
||||
*
|
||||
* ⚠️ 为什么单独抽一个 Builder 而不是给 `row()` 加参数:
|
||||
* `row()` 有 2 个参数,加个 `tappable: boolean` 就变成 3 个 ——
|
||||
* 3 个参数的 @Builder 更危险(多值参数不参与状态观察,本项目已踩 6+ 次)。
|
||||
* 这里的数据是**每次打开浮层才建一次**(详情卡整体由 `if (detailOpen())`
|
||||
* 重建),不存在"数据变了行不刷新"的场景,所以用 Builder 是安全的;
|
||||
* 但仍保持**单参数**,把风险面压到最小。
|
||||
*/
|
||||
@Builder
|
||||
navRow(place: string) {
|
||||
Row({ space: 10 }) {
|
||||
Text('地点')
|
||||
.fontSize(FbMetric.fontTiny)
|
||||
.fontColor(FbColors.faint)
|
||||
.width(56)
|
||||
|
||||
Text(place)
|
||||
.fontSize(FbMetric.fontSmall)
|
||||
.fontColor(FbColors.ink)
|
||||
.layoutWeight(1)
|
||||
.maxLines(2)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
|
||||
/* 标签用模块色(与「编辑日程」按钮同色系),一眼看出"这是这颗票能做的事"。
|
||||
⚠️ 底色用**极浅的主题色**(`fbAlpha` 那套思路:八位色 `#AARRGGBB`,
|
||||
alpha 在前)—— 深底白字会把这一行压得比标题还重。 */
|
||||
if (fbCanNavigate(place)) {
|
||||
Row({ space: 3 }) {
|
||||
Text('➤')
|
||||
.fontSize(10)
|
||||
.fontColor(this.accentColor())
|
||||
Text('导航')
|
||||
.fontSize(FbMetric.fontTiny)
|
||||
.fontColor(this.accentColor())
|
||||
}
|
||||
.padding({ left: 8, right: 8, top: 3, bottom: 3 })
|
||||
.borderRadius(FbMetric.radiusPill)
|
||||
.backgroundColor(this.accentAlpha('1A'))
|
||||
.border({ width: 1, color: this.accentAlpha('33') })
|
||||
}
|
||||
}
|
||||
.width('100%')
|
||||
.padding({ top: 8, bottom: 8 })
|
||||
.alignItems(VerticalAlign.Center)
|
||||
.onClick((): void => {
|
||||
this.onNavigate();
|
||||
})
|
||||
}
|
||||
|
||||
build() {
|
||||
// FB_WRAP_CENTER:把卡片**垂直居中**(用户 2026-10-16 要求:浮在屏幕中间)。
|
||||
// ⚠️ 居中放在**组件内部**做,而不是改宿主的 Stack 对齐 ——
|
||||
@@ -280,7 +360,15 @@ export struct FbEventSheet {
|
||||
this.row('提醒', this.remindText())
|
||||
}
|
||||
if (this.event.location !== '') {
|
||||
this.row('地点', this.event.location)
|
||||
/* 🔴 v73(用户 2026-10-21「有地址的还可以直接一键导航」):
|
||||
地点不再是纯文字 —— **整行可点**,右侧带一颗「导航」小标签。
|
||||
⚠️ 为什么整行可点而不是只让那颗小标签可点:
|
||||
小标签只有 40vp 宽,手指点不中会以为"没反应";
|
||||
整行(含地点文字)都是热区,命中率高得多。
|
||||
⚠️ 但**视觉上**仍要有那颗「导航」标签 —— 只有"整行可点"
|
||||
而没有任何标记的话,用户根本不知道地点能点
|
||||
(本项目踩过:卡尾"查看行程"是纯文本,用户以为按钮坏了)。 */
|
||||
this.navRow(this.event.location)
|
||||
}
|
||||
if (this.event.note !== '') {
|
||||
this.row('备注', this.event.note)
|
||||
@@ -312,22 +400,46 @@ export struct FbEventSheet {
|
||||
}
|
||||
|
||||
// ---- 底部按钮(只有动作,关闭统一到右上角 ✕)----
|
||||
if (this.canEdit && this.event !== null) {
|
||||
/* 🔴 v73(用户 2026-10-21「有地址的还可以直接一键导航」):
|
||||
有地点时底部多一颗「导航」—— 与上面那行可点的地点**并存**,
|
||||
是有意的:地点行是"看的时候顺手点",底部按钮是"看完了决定要去"。
|
||||
两个入口做同一件事,比只留一个更好用(日期详情卡不长,
|
||||
地点行可能已经滚出视野)。
|
||||
⚠️ 判据用 `event.location`,不用别的字段 —— 那是唯一权威的地点来源。
|
||||
⚠️ 两个按钮用 `layoutWeight(1)` 均分宽度;只有一个时它自己占满。 */
|
||||
if (this.event !== null
|
||||
&& (fbCanNavigate(this.event.location) || this.canEdit)) {
|
||||
Row({ space: 10 }) {
|
||||
/* 🔴 用户 2026-10-19:「点击日程弹出的框,编辑按钮也颜色也要修改。」
|
||||
⇒ 原来写死名册青 #4A7C82,在日程页里跟模块色(棕)不一致。
|
||||
改成 `accentColor()`(= 日程模块主题色,与卡片外环同色)。 */
|
||||
Text('编辑日程')
|
||||
.fontSize(FbMetric.fontSmall)
|
||||
.fontColor(Color.White)
|
||||
.textAlign(TextAlign.Center)
|
||||
.layoutWeight(1)
|
||||
.height(42)
|
||||
.backgroundColor(this.accentColor())
|
||||
.borderRadius(FbMetric.radiusS)
|
||||
.onClick((): void => {
|
||||
this.onEdit();
|
||||
})
|
||||
if (fbCanNavigate(this.event.location)) {
|
||||
Text('导航')
|
||||
.fontSize(FbMetric.fontSmall)
|
||||
.fontColor(this.accentColor())
|
||||
.textAlign(TextAlign.Center)
|
||||
.layoutWeight(1)
|
||||
.height(42)
|
||||
.backgroundColor(this.accentAlpha('14'))
|
||||
.borderRadius(FbMetric.radiusS)
|
||||
.border({ width: 1, color: this.accentAlpha('33') })
|
||||
.onClick((): void => {
|
||||
this.onNavigate();
|
||||
})
|
||||
}
|
||||
if (this.canEdit) {
|
||||
/* 🔴 用户 2026-10-19:「点击日程弹出的框,编辑按钮也颜色也要修改。」
|
||||
⇒ 原来写死名册青 #4A7C82,在日程页里跟模块色(棕)不一致。
|
||||
改成 `accentColor()`(= 日程模块主题色,与卡片外环同色)。 */
|
||||
Text('编辑日程')
|
||||
.fontSize(FbMetric.fontSmall)
|
||||
.fontColor(Color.White)
|
||||
.textAlign(TextAlign.Center)
|
||||
.layoutWeight(1)
|
||||
.height(42)
|
||||
.backgroundColor(this.accentColor())
|
||||
.borderRadius(FbMetric.radiusS)
|
||||
.onClick((): void => {
|
||||
this.onEdit();
|
||||
})
|
||||
}
|
||||
}
|
||||
.width('100%')
|
||||
.margin({ top: 14 })
|
||||
|
||||
@@ -0,0 +1,387 @@
|
||||
/**
|
||||
* 家书 FamilyBook · 任务详情卡(鸿蒙端)
|
||||
*
|
||||
* 🔴 用户 2026-10-21 原话:
|
||||
* 「任务也是一样,点击任务之后弹出来详情,可以标记可以标记它是不是完成了什么之类的。」
|
||||
* ⇒ 首页「近期任务」里点一行 → **就地弹出详情卡**(不跳任务模块),
|
||||
* 卡上能直接勾「完成 / 未完成」。
|
||||
*
|
||||
* ⚠️ 为什么单独一张卡,而不是复用 `FbTaskEditSheet`(任务页的编辑浮层):
|
||||
* 那是**表单**(标题框 / 备注框 / 截止时间 / 看板选择 / 保存按钮),
|
||||
* 适合"我要改这个任务";用户此刻要的是"**看看它是什么、顺手勾一下完成**"。
|
||||
* 拿表单当详情,点开就有五个可编辑控件 + 一颗"保存",观感是"我是不是要改什么"——
|
||||
* 而且误触一下就把标题改了。两件事用两个界面。
|
||||
*
|
||||
* ⚠️ 与 `FbEventSheet`(日程详情)同一套视觉语言:
|
||||
* 外圈主题色描边环(`FbSheetRing`)、关闭统一右上角 ✕(`FbSheetClose`)、
|
||||
* 底部只放动作按钮、`92%` 宽居中、内容超高才滚动。
|
||||
* ⇒ 用户在首页连点一个日程、一条任务,两张卡长得像"同一个应用的弹框"。
|
||||
*
|
||||
* 🔴 两处容易做错的地方(都已按正确方式实现,别改回去):
|
||||
* ① 完成状态**必须走 `/tasks/<id>/status`**(`setTaskStatus`),
|
||||
* 不能走 `PATCH /tasks/<id>` —— 服务端 PATCH 会把 status 原样忽略,
|
||||
* 表现是"提示保存成功、但状态没变"(`FbTaskRepo` 文件头写着)。
|
||||
* ② 勾完**立刻**把卡片上的状态改掉(本地先改),再让宿主去重拉对齐派生字段
|
||||
* (逾期、子任务进度这些是服务端算的)。等网络回来再改,用户会以为没点上。
|
||||
*/
|
||||
|
||||
import { FbColors, FbMetric, FbTint } from '../common/Theme';
|
||||
import { FbModuleDef, fbModuleOf, fbThemeVars } from '../model/Modules';
|
||||
import { FbTaskItem } from '../model/Types';
|
||||
import { FbSheetRing } from './FbSheetRing';
|
||||
import { FbSheetClose } from './FbSheetClose';
|
||||
|
||||
const FB_SHEET_BOTTOM_GAP: number = 16;
|
||||
|
||||
@ComponentV2
|
||||
export struct FbTaskDetailSheet {
|
||||
/**
|
||||
* 这条任务(首页给的那份 `FbTaskItem`)。
|
||||
*
|
||||
* ⚠️ 用 `| null` 而不是给一份对象字面量默认值 —— 与本工程其它浮层
|
||||
* (`FbEventSheet.event`)同一个写法。理由:
|
||||
* ① 对象字面量默认值在 `@Param` 上会走 `arkts-no-untyped-obj-literals` 那条规则;
|
||||
* ② 这份数据**永远由宿主传入**(开着卡就一定有它),默认值只是"没给时不崩",
|
||||
* 给一个空壳反而会画出「未命名任务」这种假内容。
|
||||
*/
|
||||
@Param task: FbTaskItem | null = null;
|
||||
/**
|
||||
* 卡上显示的**当前状态**(`todo` / `doing` / `done`)。
|
||||
*
|
||||
* ⚠️ 从 `task.status` 拷一份出来放在宿主那边管,是因为"勾完"之后要**立刻**变:
|
||||
* 如果把 `task` 整个换掉,宿主得重建这份数据(还要重拉接口),中间会有一次闪烁。
|
||||
* 这里只用一个字符串,宿主那边改一次就行。
|
||||
*/
|
||||
@Param status: string = 'todo';
|
||||
/** 正在提交(按钮变灰,防连点) */
|
||||
@Param busy: boolean = false;
|
||||
/** 提交失败/成功的说明(空 = 不显示) */
|
||||
@Param note: string = '';
|
||||
/** 环色 —— 任务模块主题色(与任务页浮层一致) */
|
||||
@Param accent: string = '';
|
||||
/** 点「完成 / 取消完成」 */
|
||||
@Event onToggle: () => void = (): void => {
|
||||
};
|
||||
/** 点「改一改」(跳任务页的编辑浮层) */
|
||||
@Event onEdit: () => void = (): void => {
|
||||
};
|
||||
@Event onClose: () => void = (): void => {
|
||||
};
|
||||
|
||||
private accentColor(): string {
|
||||
if (this.accent !== '') {
|
||||
return this.accent;
|
||||
}
|
||||
const m: FbModuleDef | undefined = fbModuleOf('tasks');
|
||||
return m !== undefined ? fbThemeVars(m.color).brand : FbTint.brand;
|
||||
}
|
||||
|
||||
/** 取主题色的半透明版本(八位色 `#AARRGGBB`:**alpha 在前**) */
|
||||
private accentAlpha(aa: string): string {
|
||||
const c: string = this.accentColor().replace('#', '');
|
||||
const rgb: string = c.length >= 8 ? c.substring(2) : c;
|
||||
return '#' + aa + rgb;
|
||||
}
|
||||
|
||||
private isDone(): boolean {
|
||||
return this.status === 'done';
|
||||
}
|
||||
|
||||
/* ---- 取值器 ----
|
||||
⚠️ 为什么要一串 `xxxOf()` 而不是在 `build()` 里直接写 `this.task!.xxx`:
|
||||
① `build()` 里**不能声明变量**(只有 UI 组件语法),条件与文案都得靠方法;
|
||||
② `task` 是可空类型,每个访问点都写 `!` 会让"哪些地方依赖它"看不出来;
|
||||
③ 这些取值器同时是**兜底**(空 → 空串),漏传一份任务也不会崩。
|
||||
⚠️ 不要把它们改成 `@Param` 多参数 `@Builder` —— 那会踩"多值参数不参与观察"。 */
|
||||
private titleOf(): string {
|
||||
const t: FbTaskItem | null = this.task;
|
||||
return t !== null && t.title !== '' ? t.title : '未命名任务';
|
||||
}
|
||||
|
||||
private boardOf(): string {
|
||||
const t: FbTaskItem | null = this.task;
|
||||
return t !== null ? t.board : '';
|
||||
}
|
||||
|
||||
private boardColorOf(): string {
|
||||
const t: FbTaskItem | null = this.task;
|
||||
return t !== null && t.boardColor !== '' ? t.boardColor : '#9AA0A6';
|
||||
}
|
||||
|
||||
private dueOf(): string {
|
||||
const t: FbTaskItem | null = this.task;
|
||||
return t !== null ? t.dueText : '';
|
||||
}
|
||||
|
||||
private overdueOf(): boolean {
|
||||
const t: FbTaskItem | null = this.task;
|
||||
return t !== null && t.overdue && !this.isDone();
|
||||
}
|
||||
|
||||
private assigneeOf(): string {
|
||||
const t: FbTaskItem | null = this.task;
|
||||
return t !== null ? t.assignee : '';
|
||||
}
|
||||
|
||||
private childOf(): number {
|
||||
const t: FbTaskItem | null = this.task;
|
||||
return t !== null ? t.childTotal : 0;
|
||||
}
|
||||
|
||||
private childText(): string {
|
||||
const t: FbTaskItem | null = this.task;
|
||||
if (t === null) {
|
||||
return '';
|
||||
}
|
||||
return t.childDone.toString() + ' / ' + t.childTotal.toString() + ' 已完成';
|
||||
}
|
||||
|
||||
private noteOf(): string {
|
||||
const t: FbTaskItem | null = this.task;
|
||||
return t !== null ? t.note : '';
|
||||
}
|
||||
|
||||
/** 状态中文(`待办` / `进行中` / `已完成`) */
|
||||
private statusText(): string {
|
||||
if (this.status === 'done') {
|
||||
return '已完成';
|
||||
}
|
||||
return this.status === 'doing' ? '进行中' : '待办';
|
||||
}
|
||||
|
||||
/**
|
||||
* 状态的强调色。
|
||||
*
|
||||
* ⚠️ 完成用**绿**、逾期用**红** —— 与任务页/网页版同一套语义。
|
||||
* 这里不跟模块主题色走:状态是"事实",不是"我在哪个模块"。
|
||||
*/
|
||||
private statusTint(): string {
|
||||
if (this.status === 'done') {
|
||||
return '#3F7D5A';
|
||||
}
|
||||
return this.status === 'doing' ? '#A8574F' : '#7C8AA0';
|
||||
}
|
||||
|
||||
private statusWash(): string {
|
||||
if (this.status === 'done') {
|
||||
return '#EAF3EE';
|
||||
}
|
||||
return this.status === 'doing' ? '#FBEFEE' : '#F1F3F5';
|
||||
}
|
||||
|
||||
@Builder
|
||||
row(label: string, value: string) {
|
||||
Row({ space: 10 }) {
|
||||
Text(label)
|
||||
.fontSize(FbMetric.fontTiny)
|
||||
.fontColor(FbColors.faint)
|
||||
.width(56)
|
||||
Text(value)
|
||||
.fontSize(FbMetric.fontSmall)
|
||||
.fontColor(FbColors.ink)
|
||||
.layoutWeight(1)
|
||||
}
|
||||
.width('100%')
|
||||
.padding({ top: 8, bottom: 8 })
|
||||
.alignItems(VerticalAlign.Top)
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
/* 🔴 包裹这层 `Column` 只为了挂 `.width('92%')`。
|
||||
⚠️ **不能**把属性直接挂在 `FbSheetRing` 上(`FbSheetRing(...) { … }.width(...)`):
|
||||
带**尾随闭包**的自定义组件后面接属性方法,ArkTS 编译器会报
|
||||
「Declaration or statement expected」外加一条「Cannot find name 'width'」,
|
||||
位置正好落在那行属性的开头 —— 看着像"括号没配平",其实是语法不支持。
|
||||
⇒ 照 `FbEventSheet` / `FbMovieDetailSheet` 的既有形态:环组件**自身不挂属性**,
|
||||
外面包一层容器,把宽度/边距挂到容器上。
|
||||
(编译实测:不包这层就红在 `FbTaskDetailSheet.ets:370`。) */
|
||||
Column() {
|
||||
FbSheetRing({ radius: FbMetric.radius, ringColor: this.accentColor() }) {
|
||||
Column({ space: 0 }) {
|
||||
// ---- 标题区:状态徽标 + 标题 + 关闭 ----
|
||||
Row({ space: 10 }) {
|
||||
/* 左侧那颗**可点的状态圆**:点一下就是"完成/取消完成"。
|
||||
🔴 用户要的就是"可以标记它是不是完成了"——
|
||||
把这件事做成**标题左边那颗圈**(与任务页列表里那颗同一个交互),
|
||||
而不是只在底部放个按钮:这是用户最习惯的位置。 */
|
||||
Stack() {
|
||||
Column()
|
||||
.width(22)
|
||||
.height(22)
|
||||
.borderRadius(11)
|
||||
.backgroundColor(this.isDone() ? this.statusTint() : Color.Transparent)
|
||||
.border({ width: 1.5, color: this.isDone() ? this.statusTint() : '#C9CDD2' })
|
||||
if (this.isDone()) {
|
||||
Text('✓')
|
||||
.fontSize(13)
|
||||
.fontColor(Color.White)
|
||||
.fontWeight(FontWeight.Medium)
|
||||
}
|
||||
}
|
||||
.width(30)
|
||||
.height(30)
|
||||
.onClick((): void => {
|
||||
if (!this.busy) {
|
||||
this.onToggle();
|
||||
}
|
||||
})
|
||||
|
||||
Text(this.titleOf())
|
||||
.fontSize(FbMetric.fontTitle)
|
||||
.fontWeight(FontWeight.Medium)
|
||||
.fontColor(this.isDone() ? FbColors.faint : FbColors.ink)
|
||||
.decoration({
|
||||
type: this.isDone() ? TextDecorationType.LineThrough : TextDecorationType.None,
|
||||
color: FbColors.faint
|
||||
})
|
||||
.maxLines(2)
|
||||
.layoutWeight(1)
|
||||
|
||||
Text(this.statusText())
|
||||
.fontSize(FbMetric.fontTiny)
|
||||
.fontColor(this.statusTint())
|
||||
.padding({ left: 8, right: 8, top: 3, bottom: 3 })
|
||||
.backgroundColor(this.statusWash())
|
||||
.borderRadius(FbMetric.radiusPill)
|
||||
|
||||
FbSheetClose({
|
||||
onClose: (): void => {
|
||||
this.onClose();
|
||||
},
|
||||
color: ''
|
||||
})
|
||||
}
|
||||
.width('100%')
|
||||
.alignItems(VerticalAlign.Top)
|
||||
|
||||
Divider()
|
||||
.color(FbColors.line)
|
||||
.margin({ top: 12, bottom: 4 })
|
||||
|
||||
Scroll() {
|
||||
Column({ space: 0 }) {
|
||||
if (this.boardOf() !== '') {
|
||||
// 看板:左侧带看板本色的小点(与首页那行同一个色,视觉上能对上号)
|
||||
Row({ space: 10 }) {
|
||||
Text('看板')
|
||||
.fontSize(FbMetric.fontTiny)
|
||||
.fontColor(FbColors.faint)
|
||||
.width(56)
|
||||
Row({ space: 6 }) {
|
||||
Column()
|
||||
.width(8)
|
||||
.height(8)
|
||||
.borderRadius(4)
|
||||
.backgroundColor(this.boardColorOf())
|
||||
Text(this.boardOf())
|
||||
.fontSize(FbMetric.fontSmall)
|
||||
.fontColor(FbColors.ink)
|
||||
}
|
||||
.layoutWeight(1)
|
||||
}
|
||||
.width('100%')
|
||||
.padding({ top: 8, bottom: 8 })
|
||||
.alignItems(VerticalAlign.Center)
|
||||
}
|
||||
if (this.dueOf() !== '') {
|
||||
Row({ space: 10 }) {
|
||||
Text('截止')
|
||||
.fontSize(FbMetric.fontTiny)
|
||||
.fontColor(FbColors.faint)
|
||||
.width(56)
|
||||
Text(this.dueOf())
|
||||
.fontSize(FbMetric.fontSmall)
|
||||
.fontColor(this.overdueOf() ? '#C0533F' : FbColors.ink)
|
||||
.layoutWeight(1)
|
||||
if (this.overdueOf()) {
|
||||
Text('已逾期')
|
||||
.fontSize(FbMetric.fontTiny)
|
||||
.fontColor('#C0533F')
|
||||
.padding({ left: 7, right: 7, top: 2, bottom: 2 })
|
||||
.backgroundColor('#FDECEC')
|
||||
.borderRadius(FbMetric.radiusPill)
|
||||
}
|
||||
}
|
||||
.width('100%')
|
||||
.padding({ top: 8, bottom: 8 })
|
||||
.alignItems(VerticalAlign.Center)
|
||||
}
|
||||
if (this.assigneeOf() !== '') {
|
||||
this.row('负责人', this.assigneeOf())
|
||||
}
|
||||
if (this.childOf() > 0) {
|
||||
this.row('子任务', this.childText())
|
||||
}
|
||||
if (this.noteOf() !== '') {
|
||||
this.row('备注', this.noteOf())
|
||||
}
|
||||
if (this.note !== '') {
|
||||
Text(this.note)
|
||||
.fontSize(FbMetric.fontTiny)
|
||||
.fontColor(this.statusTint())
|
||||
.width('100%')
|
||||
.padding({ top: 8 })
|
||||
}
|
||||
Row().height(8)
|
||||
}
|
||||
.width('100%')
|
||||
.alignItems(HorizontalAlign.Start)
|
||||
}
|
||||
/* 与 `FbEventSheet` 同一条纪律:
|
||||
`flexShrink(1)` 而不是 `layoutWeight(1)` —— 后者会让短内容也把卡片顶到 72%,
|
||||
底下留一大片空白。 */
|
||||
.flexShrink(1)
|
||||
.width('100%')
|
||||
.scrollBar(BarState.Off)
|
||||
.align(Alignment.TopStart)
|
||||
|
||||
// ---- 底部:主操作(勾完成)+ 次操作(改一改)----
|
||||
Row({ space: 10 }) {
|
||||
Text(this.busy ? '处理中…' : (this.isDone() ? '标记为未完成' : '标记为已完成'))
|
||||
.fontSize(FbMetric.fontSmall)
|
||||
.fontColor(Color.White)
|
||||
.textAlign(TextAlign.Center)
|
||||
.layoutWeight(1)
|
||||
.height(42)
|
||||
/* 已完成时按钮**不用主色实底**:那时候它是"撤销"而不是"完成",
|
||||
视觉强度该低一档(与 `FbTaskEditSheet` 删除按钮的取舍同一个思路)。 */
|
||||
.backgroundColor(this.busy ? '#C9CDD2'
|
||||
: (this.isDone() ? this.statusTint() : this.accentColor()))
|
||||
.borderRadius(FbMetric.radiusS)
|
||||
.onClick((): void => {
|
||||
if (!this.busy) {
|
||||
this.onToggle();
|
||||
}
|
||||
})
|
||||
|
||||
Text('改一改')
|
||||
.fontSize(FbMetric.fontSmall)
|
||||
.fontColor(this.accentColor())
|
||||
.textAlign(TextAlign.Center)
|
||||
.width(88)
|
||||
.height(42)
|
||||
.backgroundColor(this.accentAlpha('14'))
|
||||
.borderRadius(FbMetric.radiusS)
|
||||
.border({ width: 1, color: this.accentAlpha('33') })
|
||||
.onClick((): void => {
|
||||
this.onEdit();
|
||||
})
|
||||
}
|
||||
.width('100%')
|
||||
.margin({ top: 14 })
|
||||
}
|
||||
.width('100%')
|
||||
.constraintSize({ maxHeight: '72%' })
|
||||
.padding({ left: 18, right: 18, top: 18, bottom: 20 + FB_SHEET_BOTTOM_GAP })
|
||||
.alignItems(HorizontalAlign.Start)
|
||||
}
|
||||
}
|
||||
.width('92%')
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.justifyContent(FlexAlign.Center)
|
||||
.hitTestBehavior(HitTestMode.Transparent)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user