From 79bc593d4343bc7f5afcf548a9f5760729144cb8 Mon Sep 17 00:00:00 2001 From: laoyang Date: Thu, 5 Nov 2026 20:15:25 +0800 Subject: [PATCH] =?UTF-8?q?=E9=B8=BF=E8=92=99=E7=AB=AF=EF=BC=9A=E5=88=86?= =?UTF-8?q?=E7=B1=BB=E3=80=81=E5=BE=AA=E7=8E=AF=E6=94=B9=E6=88=90=E5=8F=AF?= =?UTF-8?q?=E7=BC=96=E8=BE=91=20(#83)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户 2026-11-05:「分类和重复,也就是循环,都需要加上,鸿蒙端也得加上。」 原来这两个只在**有值**时才画一行只读文字 (`if (this.categoryOf() !== )` / `if (this.repeatOf() !== )`)—— 用户不仅改不了,连"这里本来有这一项"都看不出来。 (这与 #76 里网页端"负责人为空就整行消失"是同一个毛病。) 改法:与截止/负责人同形态(标签一行 + 控件一行),**始终显示**。 ① FbTaskPanel 新增 `@Param cats: FbCatOpt[]` ⚠️ 与 boards 同一理由:**不能从任务里反推** —— 反推只能看见"已经被用过的"分类,新建的分类在端上永远选不到。 ② 下拉用 ArkUI 原生 `Select`,第 0 项固定「未分类」/「不循环」 - 分类:index 0 → category_id = 0;index N → cats[N-1].id - 循环:index 0 → (不循环);index N → REPEAT_KEYS[N-1] ⚠️ REPEAT_KEYS 与 REPEAT_WORDS **必须严格对齐**, 错一位就是"选每天存成每周"这种静默错数据。 ③ `inlineBusy` 时退回只读文字:请求在飞的时候不给选, 否则用户连点两下会发两次相反的值。 ④ 首页 / 任务页两处宿主传 `cats`(tskCats / metaCats)。 ⑤ 服务端 API 早就支持这两个字段(`Api::str(repeat_rule, ...)`), 本轮只补了 `Task::setAttrs` 的 repeat_rule 分支(见服务端那次提交)。 编译通过,已装真机。 --- entry/src/main/ets/components/FbTaskPanel.ets | 146 +++++++++++++++++- entry/src/main/ets/pages/home/FbHomePage.ets | 1 + .../src/main/ets/pages/tasks/FbTasksPage.ets | 1 + 3 files changed, 142 insertions(+), 6 deletions(-) diff --git a/entry/src/main/ets/components/FbTaskPanel.ets b/entry/src/main/ets/components/FbTaskPanel.ets index 33c11ce..63765e0 100644 --- a/entry/src/main/ets/components/FbTaskPanel.ets +++ b/entry/src/main/ets/components/FbTaskPanel.ets @@ -31,7 +31,7 @@ import { FbColors, FbMetric, FB_MASK, fbAlpha, fbTint } from '../common/Theme'; import { - FbBoardOpt, FbTaskAttach, FbTaskComment, FbTaskDetail, FbTaskItem, FbTaskKid, FbTaskShare, + FbBoardOpt, FbCatOpt, FbTaskAttach, FbTaskComment, FbTaskDetail, FbTaskItem, FbTaskKid, FbTaskShare, } from '../model/Types'; import { FbSheetRing } from './FbSheetRing'; import { FbSheetClose } from './FbSheetClose'; @@ -69,6 +69,16 @@ export struct FbTaskPanel { @Param seed: FbTaskItem | null = null; /** 全部看板(含空看板)—— 父页面用 `?view=boards` 拿,别从任务里反推 */ @Param boards: FbBoardOpt[] = []; + /** + * 全部分类(`id=0` 由这里补「未分类」,所以传进来的是"真实分类")。 + * + * 🔴 #83(用户 2026-11-05):「分类和重复,也就是循环,都需要加上, + * 鸿蒙端也得加上。」 + * + * ⚠️ 与 boards 同一理由:**不能从任务里反推** —— + * 反推只能看见"已经被用过的"分类,新建的分类在端上永远选不到。 + */ + @Param cats: FbCatOpt[] = []; /** 模块主题色(任务 = 赭红) */ @Param accent: string = '#A8574F'; /** @@ -1445,13 +1455,77 @@ export struct FbTaskPanel { .width('100%') .alignItems(HorizontalAlign.Start) - /* ---- 分类 / 循环(同样上下两行)---- */ - if (this.categoryOf() !== '') { - this.kvRow('分类', this.categoryOf()) + /* ---- 分类 ---- */ + /* 🔴 #83(用户 2026-11-05):「分类和重复,也就是循环,都需要加上。」 + 原来这两个只在**有值**时才画一行只读文字 —— + 用户不仅改不了,连"这里本来有这一项"都看不出来。 + ⇒ 改成与截止/负责人同形态(标签一行 + 控件一行),且始终显示。 */ + Column({ space: 6 }) { + this.fieldLb('分类') + if (this.inlineBusy) { + Text(this.categoryOf() !== '' ? this.categoryOf() : '未分类') + .fontSize(FbMetric.fontSmall) + .fontColor(FbColors.faint) + .width('100%') + .height(40) + .padding({ left: 12, right: 12 }) + .backgroundColor(FbColors.inputBg) + .borderRadius(FbMetric.radiusS) + } else { + Select(this.catOptions()) + .selected(this.catIndex()) + .value(this.categoryOf() !== '' ? this.categoryOf() : '未分类') + .font({ size: FbMetric.fontSmall }) + .fontColor(FbColors.ink) + .selectedOptionFont({ size: FbMetric.fontSmall }) + .optionFont({ size: FbMetric.fontSmall }) + .width('100%') + .height(40) + .backgroundColor(FbColors.inputBg) + .borderRadius(FbMetric.radiusS) + .onSelect((idx: number): void => { + /* index 0 = 「未分类」→ category_id 传 0 */ + const cid: number = idx === 0 ? 0 + : (idx - 1 < this.cats.length ? this.cats[idx - 1].id : 0); + this.saveField('category_id', '', cid); + }) + } } - if (this.repeatOf() !== '') { - this.kvRow('循环', this.repeatOf()) + .width('100%') + .alignItems(HorizontalAlign.Start) + + /* ---- 循环 ---- */ + Column({ space: 6 }) { + this.fieldLb('循环') + if (this.inlineBusy) { + Text(this.repeatOf() !== '' ? this.repeatOf() : '不循环') + .fontSize(FbMetric.fontSmall) + .fontColor(FbColors.faint) + .width('100%') + .height(40) + .padding({ left: 12, right: 12 }) + .backgroundColor(FbColors.inputBg) + .borderRadius(FbMetric.radiusS) + } else { + Select(this.repeatOptions()) + .selected(this.repeatIndex()) + .value(this.repeatOf() !== '' ? this.repeatWordOf(this.repeatOf()) : '不循环') + .font({ size: FbMetric.fontSmall }) + .fontColor(FbColors.ink) + .selectedOptionFont({ size: FbMetric.fontSmall }) + .optionFont({ size: FbMetric.fontSmall }) + .width('100%') + .height(40) + .backgroundColor(FbColors.inputBg) + .borderRadius(FbMetric.radiusS) + .onSelect((idx: number): void => { + const rk: string = idx === 0 ? '' : FbTaskPanel.REPEAT_KEYS[idx - 1]; + this.saveField('repeat_rule', rk, -1); + }) + } } + .width('100%') + .alignItems(HorizontalAlign.Start) /* ---- 备注 ---- */ /* 🔴 #77n(用户 2026-11-05):「包括下面的备注也是一样」 @@ -1488,6 +1562,66 @@ export struct FbTaskPanel { .padding({ top: 4 }) } + /* ---------------- 分类 / 循环 的下拉选项 ---------------- */ + + /** + * 循环的可选键(与编辑表单 `FbTaskEditSheet.repeatChip` 同一套)。 + * ⚠️ 顺序必须与 `REPEAT_WORDS` 严格对齐 —— 两边错一位就会"选每天存成每周"。 + */ + private static readonly REPEAT_KEYS: string[] = ['day', 'week', 'month', 'year']; + private static readonly REPEAT_WORDS: string[] = ['每天', '每周', '每月', '每年']; + + /** 分类下拉的选项文本:第 0 项固定是「未分类」(对应 category_id = 0) */ + private catOptions(): SelectOption[] { + const out: SelectOption[] = [{ value: '未分类' }]; + for (let i = 0; i < this.cats.length; i++) { + out.push({ value: this.cats[i].name }); + } + return out; + } + + /** 当前选中的下标(0 = 未分类) */ + private catIndex(): number { + const d: FbTaskDetail | null = this.detail; + const cid: number = d !== null ? d.categoryId : 0; + if (cid <= 0) { + return 0; + } + for (let i = 0; i < this.cats.length; i++) { + if (this.cats[i].id === cid) { + return i + 1; + } + } + return 0; // 分类被删了/不在这份清单里 → 显示未分类 + } + + private repeatOptions(): SelectOption[] { + const out: SelectOption[] = [{ value: '不循环' }]; + for (let i = 0; i < FbTaskPanel.REPEAT_WORDS.length; i++) { + out.push({ value: FbTaskPanel.REPEAT_WORDS[i] }); + } + return out; + } + + private repeatIndex(): number { + const r: string = this.repeatOf(); + for (let i = 0; i < FbTaskPanel.REPEAT_KEYS.length; i++) { + if (FbTaskPanel.REPEAT_KEYS[i] === r) { + return i + 1; + } + } + return 0; + } + + private repeatWordOf(key: string): string { + for (let i = 0; i < FbTaskPanel.REPEAT_KEYS.length; i++) { + if (FbTaskPanel.REPEAT_KEYS[i] === key) { + return FbTaskPanel.REPEAT_WORDS[i]; + } + } + return '不循环'; + } + /** 截止时间的时分(`HH:mm`),给日期浮层做初值;没有期限就给当前时刻 */ private dueTimeOf(): string { const t: FbTaskItem | null = this.cur(); diff --git a/entry/src/main/ets/pages/home/FbHomePage.ets b/entry/src/main/ets/pages/home/FbHomePage.ets index f0d4e57..c2691b5 100644 --- a/entry/src/main/ets/pages/home/FbHomePage.ets +++ b/entry/src/main/ets/pages/home/FbHomePage.ets @@ -1950,6 +1950,7 @@ export struct FbHomePage { taskId: this.tskBase.id, seed: this.tskBase, boards: this.tskBoards, + cats: this.tskCats, accent: this.taskAccent(), refreshTick: this.tskTick, onClose: (): void => { diff --git a/entry/src/main/ets/pages/tasks/FbTasksPage.ets b/entry/src/main/ets/pages/tasks/FbTasksPage.ets index 6771f4a..e0ae2aa 100644 --- a/entry/src/main/ets/pages/tasks/FbTasksPage.ets +++ b/entry/src/main/ets/pages/tasks/FbTasksPage.ets @@ -547,6 +547,7 @@ export struct FbTasksPage { taskId: this.panelId, seed: this.panelSeed, boards: this.boardOpts(), + cats: this.metaCats, accent: this.accent, refreshTick: this.panelTick, onClose: (): void => {