v79 端上:任务面板(首页/任务页共用)+ 任务模块功能补齐
用户原话:「鸿蒙端的功能,也需要同样适配,在首页上可以直接修改编辑任务,
另外任务模块的功能需要完善。」
端上原来只有"看列表 / 勾完成 / 新建改标题",网页 v78 那套「就地处理」
(改状态、挪分组、子任务、附件、留言、删除)一件都没有;首页点任务
弹的还是 v73 的只读卡。
一、新增 FbTaskPanel(首页与任务页共用同一组件)
· 改状态三段(待办/进行中/已完成)+ 标题左侧状态圈
· 挪看板 chips、勾/加子任务、加图片附件并删、发留言
· 编辑 → 交给宿主开 FbTaskEditSheet;删除要**点两次**(端上没有 undo)
· 每个动作成功后再 getTask() 重拉一次:一次动作会牵动子任务计数、
附件张数、看板色,逐处同步 DOM 必然漏一处
二、退役 FbTaskDetailSheet(v73 只读卡,387 行)
⚠️ 它已无人挂载,而守卫还在逐条"通过" —— 等于给死代码盖章。
三、任务模块补齐
· 卡片点击 → 面板(不再直接进编辑表单:误触一下就开始改标题,
且子任务/附件/留言没地方放)
· 「只看已完成」改真打 ?view=done:看板视图服务端就写死
status <> 'done',在客户端筛永远是空
· 看板列表改走 ?view=boards:原来从任务反推 ⇒ 空看板永远选不到
· 编辑表单补分类/循环两排
四、顺带修掉一个自查发现的真 bug
任务页里 sheetCount 有**两个** @Monitor 都监听 boardSheetOpen、都写同一个值
⇒ 关掉设置浮层时谁最后执行不确定,会把还开着的面板"顺便让位结束",
底部悬浮栏浮上来盖住面板按钮。已并成一个取并集的 monitor。
服务端(另一仓):新增 GET /tasks?view=boards、POST/DELETE /tasks/<id>/attach,
详情补 board_name/board_color_calc,越权删别条任务的附件 → 404。
验证:静态守卫 1055/0(新增 v79 一组,含"全页只许一个 sheetCount 赋值点")、
反向验证 215/0(新增 15 条注入,逐条证明新断言会红)、
服务端 e2e 1558/0、_fb_v79_api.py 26/0、编译通过并已装机。
This commit is contained in:
@@ -39,24 +39,26 @@
|
||||
|
||||
import { FbColors, FbMetric, FB_MASK } from '../../common/Theme';
|
||||
import { FbAppState, fbAppState } from '../../common/Store';
|
||||
import { FbCalEvent, FbHomeData, FbMark, FbMemoryItem, FbRecentDay, FbTaskItem,
|
||||
import { FbCalEvent, FbCatOpt, FbHomeData, FbMark, FbMemoryItem, FbRecentDay, FbTaskItem,
|
||||
FbBoardOpt, FbTaskDetail, FbTaskMeta,
|
||||
FbWeather, isEditableEvent } from '../../model/Types';
|
||||
import { common } from '@kit.AbilityKit';
|
||||
import { BusinessError } from '@kit.BasicServicesKit';
|
||||
import { hilog } from '@kit.PerformanceAnalysisKit';
|
||||
import { parseHome } from '../../data/FbParse';
|
||||
import { loadModule } from '../../data/FbModuleLoad';
|
||||
import { loadModule, loadPath } from '../../data/FbModuleLoad';
|
||||
import { FB_CAL_SETTINGS, FbCalStyle } from '../../data/FbCalSettings';
|
||||
import { FbCalRepo, FbCalEventFull } from '../../data/FbCalRepo';
|
||||
import { FB_TOKEN_STORE } from '../../data/FbTokenStore';
|
||||
import { setTaskStatus } from '../../data/FbTaskRepo';
|
||||
import { parseTaskMeta } from '../../data/FbParse';
|
||||
import { FB_CARD_FORMS } from '../../data/FbCardForms';
|
||||
import { fbApi, FbApiResult } from '../../data/FbHttp';
|
||||
import { FbSectionTitle } from '../../components/Widgets';
|
||||
import { FbStatCell } from '../../components/FbStatCell';
|
||||
import { FbDayEventPanel } from '../../components/FbDayEventPanel';
|
||||
import { FbEventSheet } from '../../components/FbEventSheet';
|
||||
import { FbTaskDetailSheet } from '../../components/FbTaskDetailSheet';
|
||||
import { FbTaskPanel } from '../../components/FbTaskPanel';
|
||||
import { FbTaskEditSheet } from '../../components/FbTaskEditSheet';
|
||||
import { FB_DOCK_RESERVE } from '../../components/FloatDock';
|
||||
import { fbOnScroll, fbOnTouch } from '../../common/FbScroll';
|
||||
import { fbMaskTransition, fbSheetTransition } from '../../common/FbAnim';
|
||||
@@ -142,14 +144,36 @@ export struct FbHomePage {
|
||||
/** 丢弃在途请求的令牌(连点两条日程时,先回来的那条不该覆盖后点的) */
|
||||
@Local evtToken: number = 0;
|
||||
|
||||
/** 摘要卡显示的那条任务(非 null = 任务详情卡开着) */
|
||||
/** 摘要卡显示的那条任务(非 null = 任务面板开着) */
|
||||
@Local tskBase: FbTaskItem | null = null;
|
||||
/** 任务卡上显示的当前状态(勾完**立刻**变,不等网络) */
|
||||
@Local tskStatus: string = 'todo';
|
||||
/** 任务状态正在提交(防连点) */
|
||||
@Local tskBusy: boolean = false;
|
||||
/** 任务卡的提示(失败原因 / 成功说明) */
|
||||
@Local tskNote: string = '';
|
||||
/**
|
||||
* v79:面板里能用的**全部**看板 / 分类(`?view=boards`)。
|
||||
*
|
||||
* 🔴 为什么不在页面加载时就拉:首页 `/home` 不返回看板表,
|
||||
* 而"挪分组/选分类"只有点开任务后才用得上 —— 按需拉更省一次请求。
|
||||
* ⚠️ 拉的是**全部看板**(含空看板),不是从任务里反推
|
||||
* (反推会让新建的空看板永远选不到)。
|
||||
*/
|
||||
@Local tskBoards: FbBoardOpt[] = [];
|
||||
@Local tskCats: FbCatOpt[] = [];
|
||||
/** 任务编辑表单是否打开(v79:在首页直接改,不再跳任务页) */
|
||||
@Local tskEditOpen: boolean = false;
|
||||
/** 编辑表单回填的字段 */
|
||||
@Local tskEditId: number = 0;
|
||||
@Local tskEditTitle: string = '';
|
||||
@Local tskEditNote: string = '';
|
||||
@Local tskEditDueAt: string = '';
|
||||
@Local tskEditBoardId: number = 0;
|
||||
@Local tskEditCatId: number = 0;
|
||||
@Local tskEditRepeat: string = '';
|
||||
/**
|
||||
* 面板的"重画脉冲"(v79)。
|
||||
*
|
||||
* 🔴 为什么需要:编辑表单保存成功后,面板里的标题/截止/分类都变了,
|
||||
* 但面板的数据是它**自己**拉的 —— 宿主只重拉首页列表,它不会跟着变。
|
||||
* ⚠️ 别用"关掉再打开"代替:会闪一下、还会丢掉滚动位置。
|
||||
*/
|
||||
@Local tskTick: number = 0;
|
||||
|
||||
/**
|
||||
* 详情卡开着 ⇒ 底部悬浮快捷栏让位。
|
||||
@@ -158,9 +182,9 @@ export struct FbHomePage {
|
||||
* `Index` 的 Stack** 里,层级比本页高 ⇒ 不让位就会盖住卡片底部的按钮、
|
||||
* 还把点击抢走(名册/日程页都踩过)。
|
||||
*/
|
||||
@Monitor('evtBase', 'tskBase')
|
||||
@Monitor('evtBase', 'tskBase', 'tskEditOpen')
|
||||
onDetailChanged(): void {
|
||||
this.app.sheetCount = (this.evtBase !== null || this.tskBase !== null) ? 1 : 0;
|
||||
this.app.sheetCount = (this.evtBase !== null || this.tskBase !== null || this.tskEditOpen) ? 1 : 0;
|
||||
}
|
||||
|
||||
aboutToAppear(): void {
|
||||
@@ -181,6 +205,12 @@ export struct FbHomePage {
|
||||
|
||||
/** 返回手势:先关详情卡,再交给 `Index`(返回 true = 已消费) */
|
||||
private backOnce(): boolean {
|
||||
/* v79:**后打开的先关** —— 编辑表单是压在面板之上的,返回一次只该关它。
|
||||
顺序反了会"按一次返回,两层一起没了"(用户会以为点错了)。 */
|
||||
if (this.tskEditOpen) {
|
||||
this.tskEditOpen = false;
|
||||
return true;
|
||||
}
|
||||
if (this.evtBase !== null) {
|
||||
this.closeEvent();
|
||||
return true;
|
||||
@@ -333,69 +363,50 @@ export struct FbHomePage {
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// v73:任务详情卡
|
||||
// v79:任务面板(v73 的"详情卡"升级成可就地处理的面板)
|
||||
// ============================================================
|
||||
|
||||
private openTask(t: FbTaskItem): void {
|
||||
this.tskBase = t;
|
||||
this.tskStatus = t.status;
|
||||
this.tskBusy = false;
|
||||
this.tskNote = '';
|
||||
/* 看板/分类表按需拉一次(拉过就不再拉;它是"元数据",几条而己) */
|
||||
this.loadTaskMeta();
|
||||
}
|
||||
|
||||
private closeTask(): void {
|
||||
this.tskBase = null;
|
||||
this.tskBusy = false;
|
||||
this.tskNote = '';
|
||||
this.tskEditOpen = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 勾完成 / 取消完成。
|
||||
*
|
||||
* 🔴🔴 **必须走 `/tasks/<id>/status`**(`setTaskStatus`)——
|
||||
* 服务端 `PATCH /tasks/<id>` 会把请求里的 status **原样忽略**
|
||||
* (见 `FbTaskRepo` 文件头),打 PATCH 会"看起来成功、状态没变"。
|
||||
* ⚠️ 这是本项目已经踩过一次的坑,别改成 updateTask。
|
||||
*
|
||||
* ⚠️ 已完成 → 取消完成统一回 `todo`(**不回 `doing`**):
|
||||
* 用户只想撤销一次误勾,把它标成"进行中"是替他做了他没表达的决定。
|
||||
* (与任务页 `toggleDone` 同一套规则。)
|
||||
*/
|
||||
private async toggleTask(): Promise<void> {
|
||||
const t: FbTaskItem | null = this.tskBase;
|
||||
if (t === null || this.tskBusy) {
|
||||
/** 拉全部看板 + 分类(面板的"挪分组"、表单的"分类"都要用) */
|
||||
private async loadTaskMeta(): Promise<void> {
|
||||
if (this.tskBoards.length > 0) {
|
||||
return;
|
||||
}
|
||||
this.tskBusy = true;
|
||||
this.tskNote = '';
|
||||
const next: string = this.tskStatus === 'done' ? 'todo' : 'done';
|
||||
const res: FbApiResult = await setTaskStatus(FB_TOKEN_STORE.get(), t.id, next);
|
||||
this.tskBusy = false;
|
||||
if (!res.ok) {
|
||||
this.tskNote = res.error !== '' ? res.error : '改状态失败,请稍后再试。';
|
||||
return;
|
||||
}
|
||||
/* 本地先把状态改掉(卡上立刻有反馈),再重拉首页数据对齐派生字段
|
||||
(逾期、看板计数、"近期任务"里这条还在不在,都是服务端算的)。
|
||||
⚠️ 顺序不能反 —— 等网络回来再改,用户会以为"没点上"又点一次。 */
|
||||
this.tskStatus = next;
|
||||
this.tskNote = next === 'done' ? '已标记完成' : '已改回未完成';
|
||||
this.load();
|
||||
// 桌面卡片上的数字(今天日程/在做/待办)也可能受影响 —— 顺手推一次
|
||||
this.pushCards();
|
||||
await loadPath<FbTaskMeta>('tasks_meta', '/api/v1/tasks?view=boards', parseTaskMeta,
|
||||
(m: FbTaskMeta): void => {
|
||||
this.tskBoards = m.boards;
|
||||
this.tskCats = m.cats;
|
||||
},
|
||||
(note: string): void => {
|
||||
/* ⚠️ 失败**不拦路**:面板里状态/子任务/留言都还能用,
|
||||
只是"换看板"那一排会少几个(甚至只剩默认看板)。 */
|
||||
this.syncNote = note;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务卡上点「改一改」→ 切到任务模块。
|
||||
*
|
||||
* ⚠️ 与日程同理:改标题/改截止/看板/负责人是一整套表单,属于任务页的职责。
|
||||
* 用户要的"弹详情、能勾完成"这里已经给了;"改内容"跳过去是对的。
|
||||
*/
|
||||
private editTaskFromDetail(): void {
|
||||
this.closeTask();
|
||||
this.app.switchModule('tasks');
|
||||
/** 面板里点「编辑」→ 在首页就地打开编辑表单(v79,不再跳任务页) */
|
||||
private openTaskEdit(d: FbTaskDetail): void {
|
||||
this.tskEditId = d.base.id;
|
||||
this.tskEditTitle = d.base.title;
|
||||
this.tskEditNote = d.base.note;
|
||||
this.tskEditDueAt = d.base.dueAt;
|
||||
this.tskEditBoardId = d.base.boardId;
|
||||
this.tskEditCatId = d.categoryId;
|
||||
this.tskEditRepeat = d.repeat;
|
||||
this.tskEditOpen = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 把最新数据推给桌面上所有日程卡片。
|
||||
*
|
||||
@@ -873,31 +884,61 @@ export struct FbHomePage {
|
||||
.transition(fbSheetTransition())
|
||||
}
|
||||
|
||||
// ==================== 任务详情卡(v73)====================
|
||||
// ==================== 任务面板(v79:可就地处理)====================
|
||||
/* 🔴 用户 2026-10-22:「在首页上可以直接修改编辑任务。」
|
||||
⇒ v73 的"只读详情卡"升级成面板:状态 / 看板 / 子任务 / 附件 / 留言
|
||||
全在这张卡里做完,**不跳任务模块**(跳模块是用户明确否掉的)。
|
||||
⚠️ 面板根节点自带遮罩与 `Stack`,这里**不要**再包一层遮罩 ——
|
||||
包了就会有两层暗色叠在一起(越点越黑)。 */
|
||||
if (this.tskBase !== null) {
|
||||
Column()
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.backgroundColor(FB_MASK)
|
||||
.transition(fbMaskTransition())
|
||||
.onClick((): void => {
|
||||
this.closeTask();
|
||||
})
|
||||
|
||||
FbTaskDetailSheet({
|
||||
task: this.tskBase,
|
||||
status: this.tskStatus,
|
||||
busy: this.tskBusy,
|
||||
note: this.tskNote,
|
||||
FbTaskPanel({
|
||||
taskId: this.tskBase.id,
|
||||
seed: this.tskBase,
|
||||
boards: this.tskBoards,
|
||||
accent: this.taskAccent(),
|
||||
onToggle: (): void => {
|
||||
this.toggleTask();
|
||||
},
|
||||
onEdit: (): void => {
|
||||
this.editTaskFromDetail();
|
||||
},
|
||||
refreshTick: this.tskTick,
|
||||
onClose: (): void => {
|
||||
this.closeTask();
|
||||
},
|
||||
onEdit: (d: FbTaskDetail): void => {
|
||||
this.openTaskEdit(d);
|
||||
},
|
||||
onChanged: (): void => {
|
||||
/* 一次动作会牵动"近期任务"这张卡的排序/计数(服务端算的)⇒ 重拉首页 */
|
||||
this.load();
|
||||
this.pushCards();
|
||||
},
|
||||
onDeleted: (): void => {
|
||||
this.closeTask();
|
||||
this.load();
|
||||
this.pushCards();
|
||||
}
|
||||
})
|
||||
.transition(fbSheetTransition())
|
||||
}
|
||||
|
||||
// ==================== 任务编辑表单(v79:就地改,不跳任务页)====================
|
||||
if (this.tskEditOpen) {
|
||||
FbTaskEditSheet({
|
||||
accent: this.taskAccent(),
|
||||
boards: this.tskBoards,
|
||||
cats: this.tskCats,
|
||||
editingId: this.tskEditId,
|
||||
editingTitle: this.tskEditTitle,
|
||||
editingNote: this.tskEditNote,
|
||||
editingDueAt: this.tskEditDueAt,
|
||||
editingBoardId: this.tskEditBoardId,
|
||||
editingCatId: this.tskEditCatId,
|
||||
editingRepeat: this.tskEditRepeat,
|
||||
onClose: (): void => {
|
||||
this.tskEditOpen = false;
|
||||
},
|
||||
onSaved: (): void => {
|
||||
this.tskEditOpen = false;
|
||||
/* 面板还开着 ⇒ 让它自己重拉一次(标题/截止/分类都变了) */
|
||||
this.tskTick = this.tskTick + 1;
|
||||
this.load();
|
||||
this.pushCards();
|
||||
}
|
||||
})
|
||||
.transition(fbSheetTransition())
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
|
||||
import { FbColors, FbMetric, fbTint, FbTint, FB_MASK, fbAlpha } from '../../common/Theme';
|
||||
import { FbAppState, fbAppState } from '../../common/Store';
|
||||
import { FbBoardOpt, FbGroup, FbTaskItem } from '../../model/Types';
|
||||
import { FbBoardOpt, FbCatOpt, FbGroup, FbTaskItem, FbTaskDetail, FbTaskMeta } from '../../model/Types';
|
||||
import { fbGroupTask } from '../../model/DemoData';
|
||||
import { parseTasks } from '../../data/FbParse';
|
||||
import { loadModule } from '../../data/FbModuleLoad';
|
||||
import { parseTasks, parseTaskList, parseTaskMeta } from '../../data/FbParse';
|
||||
import { loadModule, loadPath } from '../../data/FbModuleLoad';
|
||||
import { FbCheckCircle, FbDot, FbEmptyBox, FbIconButton, FbPrimaryButton, FbTag } from '../../components/Widgets';
|
||||
import { FB_DOCK_RESERVE } from '../../components/FloatDock';
|
||||
import { fbOnScroll, fbOnTouch, fbOnHScroll } from '../../common/FbScroll';
|
||||
@@ -25,6 +25,7 @@ import { FbSheetClose } from '../../components/FbSheetClose';
|
||||
import { FB_BACK } from '../../common/FbBack';
|
||||
import { FB_SET_TASKS } from '../../model/FbSettingsMenus';
|
||||
import { FbTaskEditSheet } from '../../components/FbTaskEditSheet';
|
||||
import { FbTaskPanel } from '../../components/FbTaskPanel';
|
||||
import { setTaskStatus } from '../../data/FbTaskRepo';
|
||||
import { FB_TOKEN_STORE } from '../../data/FbTokenStore';
|
||||
import { FbApiResult } from '../../data/FbHttp';
|
||||
@@ -77,12 +78,32 @@ export struct FbTasksPage {
|
||||
/** `2026-10-25 18:00`;空 = 没有期限 */
|
||||
@Local editingDueAt: string = '';
|
||||
@Local editingBoardId: number = 0;
|
||||
/** v79:分类 / 循环(编辑时回填;0 / '' = 不分类、不循环) */
|
||||
@Local editingCatId: number = 0;
|
||||
@Local editingRepeat: string = '';
|
||||
/** 正在改状态的那条(防连点打重复请求) */
|
||||
@Local busyId: number = 0;
|
||||
/** 「分类与看板」浮层是否打开 */
|
||||
@Local boardSheetOpen: boolean = false;
|
||||
/** 操作失败时的一行红字(如勾完成失败) */
|
||||
@Local opNote: string = '';
|
||||
/* ---- v79:任务面板(点卡片打开;与首页共用 `FbTaskPanel`)---- */
|
||||
/** 面板正在看的那条(0 = 面板关着) */
|
||||
@Local panelId: number = 0;
|
||||
/** 先用列表里那一份渲染(不空白等网络) */
|
||||
@Local panelSeed: FbTaskItem | null = null;
|
||||
/** 面板重画脉冲(编辑保存后 +1) */
|
||||
@Local panelTick: number = 0;
|
||||
/**
|
||||
* 全部看板 / 分类(`?view=boards`)。
|
||||
*
|
||||
* 🔴 原来 `boardOpts()` 是**从已加载任务里反推**的 ⇒ 只能看见"有任务的看板",
|
||||
* 新建的空看板在端上永远选不到(网页能建、端上挪不过去)。
|
||||
*/
|
||||
@Local metaBoards: FbBoardOpt[] = [];
|
||||
@Local metaCats: FbCatOpt[] = [];
|
||||
/** 已完成列表(`?view=done`;看板视图**不含 done**,所以必须单独拉) */
|
||||
@Local doneTasks: FbTaskItem[] = [];
|
||||
|
||||
/**
|
||||
* 页面出现:先读缓存、再拉接口(本地优先,见 FbModuleRepo 的说明)。
|
||||
@@ -101,11 +122,17 @@ export struct FbTasksPage {
|
||||
|
||||
private backOnce(): boolean {
|
||||
/* v65:返回手势要**先关新的浮层**,再关设置菜单 ——
|
||||
顺序按"后打开的在前",否则按一次返回会跳关两层。 */
|
||||
顺序按"后打开的在前",否则按一次返回会跳关两层。
|
||||
v79:面板与编辑表单又叠了一层(编辑在面板之上),所以顺序是
|
||||
编辑 → 面板 → 分类与看板 → 设置。 */
|
||||
if (this.editOpen) {
|
||||
this.editOpen = false;
|
||||
return true;
|
||||
}
|
||||
if (this.panelId > 0) {
|
||||
this.closePanel();
|
||||
return true;
|
||||
}
|
||||
if (this.boardSheetOpen) {
|
||||
this.boardSheetOpen = false;
|
||||
return true;
|
||||
@@ -120,9 +147,30 @@ export struct FbTasksPage {
|
||||
aboutToAppear(): void {
|
||||
FB_BACK.register('tasks', (): boolean => this.backOnce());
|
||||
this.load();
|
||||
this.loadMeta();
|
||||
}
|
||||
|
||||
/** 拉全部看板 + 分类(筛选条、编辑表单、面板的"挪分组"共用一份) */
|
||||
private async loadMeta(): Promise<void> {
|
||||
await loadPath<FbTaskMeta>('tasks_meta', '/api/v1/tasks?view=boards', parseTaskMeta,
|
||||
(m: FbTaskMeta): void => {
|
||||
this.metaBoards = m.boards;
|
||||
this.metaCats = m.cats;
|
||||
},
|
||||
(note: string): void => {
|
||||
this.syncNote = note;
|
||||
});
|
||||
}
|
||||
|
||||
private async load(): Promise<void> {
|
||||
/* 🔴 v79 修一个真 bug:设置里的「只看已完成」以前筛的是**看板视图**的结果,
|
||||
而看板视图在服务端就写死了 `status <> 'done'`(`Task::boardView` 那条 SQL)
|
||||
⇒ 筛出来永远是空,用户看到的是"已完成里一条都没有"。
|
||||
现在改成真的打 `?view=done`(网页 `Task::doneList` 同一口径)。 */
|
||||
if (this.statusFilter === 'done') {
|
||||
await this.loadDone();
|
||||
return;
|
||||
}
|
||||
await loadModule<FbTaskItem[]>('tasks', parseTasks,
|
||||
(rows: FbTaskItem[]): void => {
|
||||
this.tasks = rows;
|
||||
@@ -132,31 +180,38 @@ export struct FbTasksPage {
|
||||
});
|
||||
}
|
||||
|
||||
/** 已完成列表(单独缓存键:与看板视图**形状不同**,不能共用 'tasks' 那个键) */
|
||||
private async loadDone(): Promise<void> {
|
||||
await loadPath<FbTaskItem[]>('tasks_done', '/api/v1/tasks?view=done', parseTaskList,
|
||||
(rows: FbTaskItem[]): void => {
|
||||
this.doneTasks = rows;
|
||||
},
|
||||
(note: string): void => {
|
||||
this.syncNote = note;
|
||||
});
|
||||
}
|
||||
|
||||
/** 顶部提示(同步失败 / 未登录时显示一行小字) */
|
||||
@Local syncNote: string = '';
|
||||
|
||||
|
||||
/**
|
||||
* 设置浮层开着 ⇒ 让底部悬浮快捷栏让位。
|
||||
* 本页任何一个浮层开着 ⇒ 底部悬浮快捷栏让位。
|
||||
*
|
||||
* 🔴 悬浮栏在**更外层 Index 的 Stack** 里,层级比本页高 ⇒
|
||||
* 浮层底部的按钮会被它盖住、点击被抢走(名册/日程页都踩过)。
|
||||
*
|
||||
* ⚠️🔴 本页所有浮层开关(设置 / 分类看板 / 新建编辑 / 任务面板)**只能有
|
||||
* **这一个** `@Monitor` 赋 `sheetCount`,且必须**取并集**。
|
||||
* 曾经这里是两个函数(一个只管 `boardSheetOpen`、一个管四个)——
|
||||
* 两个都监听同一个字段、都写同一个值,关掉其中一个时谁最后执行不确定
|
||||
* ⇒ 会把另一个还开着的浮层"顺便让位结束",悬浮栏浮上来盖住面板。
|
||||
* (2026-10-22 自查发现;守卫里有断言防回退)
|
||||
*/
|
||||
@Monitor('settingsOpen')
|
||||
onSettingsChanged(): void {
|
||||
fbAppState().sheetCount = this.settingsOpen ? 1 : 0;
|
||||
}
|
||||
|
||||
/** 新建/编辑浮层开着 ⇒ 底部悬浮快捷栏也要让位(与 `settingsOpen` 不会同时开) */
|
||||
@Monitor('editOpen')
|
||||
onEditChanged(): void {
|
||||
fbAppState().sheetCount = this.editOpen ? 1 : 0;
|
||||
}
|
||||
|
||||
/** 「分类与看板」浮层同理 */
|
||||
@Monitor('boardSheetOpen')
|
||||
onBoardSheetChanged(): void {
|
||||
fbAppState().sheetCount = this.boardSheetOpen ? 1 : 0;
|
||||
@Monitor('settingsOpen', 'boardSheetOpen', 'editOpen', 'panelId')
|
||||
onAnySheetChanged(): void {
|
||||
const any: boolean = this.settingsOpen || this.boardSheetOpen || this.editOpen || this.panelId > 0;
|
||||
fbAppState().sheetCount = any ? 1 : 0;
|
||||
}
|
||||
|
||||
/** 打开「新建」浮层(清空回填字段) */
|
||||
@@ -166,17 +221,46 @@ export struct FbTasksPage {
|
||||
this.editingNote = '';
|
||||
this.editingDueAt = '';
|
||||
this.editingBoardId = 0;
|
||||
this.editingCatId = 0;
|
||||
this.editingRepeat = '';
|
||||
this.opNote = '';
|
||||
this.editOpen = true;
|
||||
}
|
||||
|
||||
/** 打开「编辑」浮层(把这条任务的字段回填进去) */
|
||||
private openEdit(t: FbTaskItem): void {
|
||||
this.editingId = t.id;
|
||||
this.editingTitle = t.title;
|
||||
this.editingNote = t.note;
|
||||
this.editingDueAt = t.dueAt;
|
||||
this.editingBoardId = t.boardId;
|
||||
/**
|
||||
* 打开任务面板(v79)。
|
||||
*
|
||||
* 🔴 用户 2026-10-22:「任务模块的功能需要完善。」
|
||||
* 以前点卡片**直接进编辑表单** —— 想勾个子任务、看留言、加个照片都没地方去;
|
||||
* 而且误触一下卡片就开始改标题。现在点卡片先开面板(只读 + 就地动作),
|
||||
* 面板里的「编辑」才进表单。
|
||||
*/
|
||||
private openPanel(t: FbTaskItem): void {
|
||||
this.panelId = t.id;
|
||||
this.panelSeed = t;
|
||||
this.opNote = '';
|
||||
}
|
||||
|
||||
private closePanel(): void {
|
||||
this.panelId = 0;
|
||||
this.panelSeed = null;
|
||||
this.editOpen = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开「编辑」浮层(回填字段)。
|
||||
*
|
||||
* ⚠️ 入参是**详情**不是列表行:分类/循环只有详情接口才给,
|
||||
* 拿列表行回填会把这两项当成"空"提交 ⇒ 一保存就清空(v77 同类坑)。
|
||||
*/
|
||||
private openEdit(d: FbTaskDetail): void {
|
||||
this.editingId = d.base.id;
|
||||
this.editingTitle = d.base.title;
|
||||
this.editingNote = d.base.note;
|
||||
this.editingDueAt = d.base.dueAt;
|
||||
this.editingBoardId = d.base.boardId;
|
||||
this.editingCatId = d.categoryId;
|
||||
this.editingRepeat = d.repeat;
|
||||
this.opNote = '';
|
||||
this.editOpen = true;
|
||||
}
|
||||
@@ -220,8 +304,16 @@ export struct FbTasksPage {
|
||||
this.load();
|
||||
}
|
||||
|
||||
/** 看板列表(从已加载的任务里反推:`/tasks` 的 boards 里没任务的那些画出来也没用) */
|
||||
/**
|
||||
* 看板列表:**优先用元数据**(`?view=boards`,含空看板)。
|
||||
*
|
||||
* ⚠️ 元数据还没回来时退回"从任务里反推"(老行为),这样首屏不会空着;
|
||||
* 但**真正可选**的看板以元数据为准 —— 反推漏掉空看板是老 bug。
|
||||
*/
|
||||
private boardOpts(): FbBoardOpt[] {
|
||||
if (this.metaBoards.length > 0) {
|
||||
return this.metaBoards;
|
||||
}
|
||||
const out: FbBoardOpt[] = [];
|
||||
const seen: number[] = [];
|
||||
for (const t of this.tasks) {
|
||||
@@ -326,6 +418,7 @@ export struct FbTasksPage {
|
||||
.borderRadius(FbMetric.radiusPill)
|
||||
.onClick((): void => {
|
||||
this.statusFilter = '';
|
||||
this.load();
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -405,6 +498,8 @@ export struct FbTasksPage {
|
||||
if (action === 'taskdone') {
|
||||
this.statusFilter = 'done';
|
||||
this.settingsOpen = false;
|
||||
/* 🔴 必须重拉:看板视图里根本没有 done,本地筛只会得到空列表 */
|
||||
this.load();
|
||||
return;
|
||||
}
|
||||
if (action === 'taskboards') {
|
||||
@@ -417,16 +512,46 @@ export struct FbTasksPage {
|
||||
.transition(fbSheetTransition())
|
||||
}
|
||||
|
||||
// ---- 任务面板(v79:点卡片打开,可就地处理)----
|
||||
/* ⚠️ 面板自带遮罩与 Stack,这里**不要**再包一层 —— 两层暗色会叠在一起。 */
|
||||
if (this.panelId > 0) {
|
||||
FbTaskPanel({
|
||||
taskId: this.panelId,
|
||||
seed: this.panelSeed,
|
||||
boards: this.boardOpts(),
|
||||
accent: this.accent,
|
||||
refreshTick: this.panelTick,
|
||||
onClose: (): void => {
|
||||
this.closePanel();
|
||||
},
|
||||
onEdit: (d: FbTaskDetail): void => {
|
||||
this.openEdit(d);
|
||||
},
|
||||
onChanged: (): void => {
|
||||
/* 状态/看板/子任务都可能改变排序与计数 ⇒ 重拉列表 */
|
||||
this.load();
|
||||
},
|
||||
onDeleted: (): void => {
|
||||
this.closePanel();
|
||||
this.load();
|
||||
}
|
||||
})
|
||||
.transition(fbSheetTransition())
|
||||
}
|
||||
|
||||
// ---- 新建 / 编辑任务浮层(v65)----
|
||||
if (this.editOpen) {
|
||||
FbTaskEditSheet({
|
||||
accent: this.accent,
|
||||
boards: this.boardOpts(),
|
||||
cats: this.metaCats,
|
||||
editingId: this.editingId,
|
||||
editingTitle: this.editingTitle,
|
||||
editingNote: this.editingNote,
|
||||
editingDueAt: this.editingDueAt,
|
||||
editingBoardId: this.editingBoardId,
|
||||
editingCatId: this.editingCatId,
|
||||
editingRepeat: this.editingRepeat,
|
||||
onClose: (): void => {
|
||||
this.editOpen = false;
|
||||
},
|
||||
@@ -434,6 +559,8 @@ export struct FbTasksPage {
|
||||
this.editOpen = false;
|
||||
// 服务端会派生逾期/看板色,保存后重拉一遍
|
||||
this.load();
|
||||
// 面板还开着 ⇒ 让它自己重拉(标题/截止/分类都变了)
|
||||
this.panelTick = this.panelTick + 1;
|
||||
}
|
||||
})
|
||||
.transition(fbSheetTransition())
|
||||
@@ -663,14 +790,19 @@ export struct FbTasksPage {
|
||||
.borderRadius(FbMetric.radius)
|
||||
.margin({ bottom: 8 })
|
||||
.alignItems(HorizontalAlign.Start)
|
||||
/* 🔴 v65:整卡可点 → 打开编辑(原来列表项**没有 `onClick`** ⇒ "点不进去") */
|
||||
/* 🔴 v65:整卡可点 ⇒ "点得进去";v79 起进的是**面板**(不是直接进编辑表单) */
|
||||
.onClick((): void => {
|
||||
this.openEdit(t);
|
||||
this.openPanel(t);
|
||||
})
|
||||
}
|
||||
|
||||
/** 当前数据源:看板视图 或 已完成列表(两者的形状不同,不能混用) */
|
||||
private source(): FbTaskItem[] {
|
||||
return this.statusFilter === 'done' ? this.doneTasks : this.tasks;
|
||||
}
|
||||
|
||||
private filtered(): FbTaskItem[] {
|
||||
let list: FbTaskItem[] = this.tasks;
|
||||
let list: FbTaskItem[] = this.source();
|
||||
if (this.boardFilter.length > 0) {
|
||||
list = list.filter((t: FbTaskItem): boolean => t.board === this.boardFilter);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user