From 19e13339580f2eff2f13289f2a268cd8903c7ad8 Mon Sep 17 00:00:00 2001 From: laoyang Date: Fri, 6 Nov 2026 11:56:05 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A6=96=E9=A1=B5=E8=81=94=E7=B3=BB=E4=BA=BA?= =?UTF-8?q?=EF=BC=9A=E7=82=B9=E7=BC=96=E8=BE=91=E5=B0=B1=E5=9C=B0=E5=BC=B9?= =?UTF-8?q?=E6=A1=86=EF=BC=8C=E4=B8=8D=E5=86=8D=E8=B7=B3=E5=90=8D=E5=86=8C?= =?UTF-8?q?=20(#90)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户:现在在首页上,弹出名册信息之后,点击编辑,怎么跳转到名册去了? 点击编辑应该直接弹框编辑。 改前 onEdit = closePersonDetail() + switchModule(people) —— 用户被甩到 名册模块,还得自己重新找到那个人再点编辑,绕了一圈。 当时注释的理由"首页没有人员编辑表单"站不住:首页**已经**在用 FbPersonEditSheet 做新增成员,同一个组件换参数就是编辑模式。 ① onEdit 改为就地弹表单(同一个 FbPersonEditSheet,不另写一份, 否则两处字段/校验/保存逻辑必分叉); ② 新增 @Local pfEditing: FbRosterPerson | null —— 用对象不用 boolean (表单要拿它回填,只存 true/false 还得再查一次库)。 与 pfCreating 分开两个状态:新建时 person = null 是合法值, 合并成一个 boolean + person? 会让两种情形撞车; ③ 取消 ⇒ 回详情卡(不是直接回首页)。用户刚才是从详情进来的, 取消只是"我不改了",不该连"我在看谁"一起丢掉(与名册页同口径); ④ 保存 ⇒ 关表单 + reloadSideCards + refreshPersonDetail(id)。 这里有个必须处理的坑:编辑表单 save() **只调接口、不写回本地库**, 直接把编辑前快照塞回 pfDetail 会让用户看到"改完还是旧名字"。 ⇒ 新增 refreshPersonDetail(),两级刷新(与 FbPeoplePage.openDetail 同一思路): ① byId() 读本地库(快,可能是旧值) ② fetchDetail() 打 GET /people/(服务端权威值,回来原地替换) 断网保持①、不弹错;读不到就什么都不做,不拿 null 覆盖正常的详情卡。 验证(真机 5NC0225B27004718): - 首页点爸爸→详情卡→点编辑 → **就地弹出编辑表单**,页面仍是首页 (下方最近观影/联系人/往年的今天都在); - 表单标题「编辑资料」、姓名回填「杨培振」、生日(阳历)1955-03-30; - 点取消 → 回到详情卡(含手机号/拨打/编辑),不是直接回首页。 hvigorw assembleHap BUILD SUCCESSFUL。 ⚠️ 刻意没执行"保存":这是用户真实联系人数据;保存路径与名册页完全 同一份代码(同组件同 onSaved),无需在首页重复踩。refreshPersonDetail 的两级刷新未走真机保存验证 —— 若日后发现"保存后详情显示旧值"先查这里。 --- entry/src/main/ets/pages/home/FbHomePage.ets | 128 ++++++++++++++++++- 1 file changed, 125 insertions(+), 3 deletions(-) diff --git a/entry/src/main/ets/pages/home/FbHomePage.ets b/entry/src/main/ets/pages/home/FbHomePage.ets index 3768bd5..1867875 100644 --- a/entry/src/main/ets/pages/home/FbHomePage.ets +++ b/entry/src/main/ets/pages/home/FbHomePage.ets @@ -363,6 +363,21 @@ export struct FbHomePage { /** 「新增成员」表单开着 */ @Local pfCreating: boolean = false; + /** + * 正在**编辑**的那个人(null = 编辑浮层没开)。 + * + * 🔴 用户 2026-11-06:「现在在首页上,弹出名册信息之后,点击编辑, + * 怎么跳转到名册去了?点击编辑应该直接弹框编辑。」 + * ⇒ 原来 `onEdit` 是"关掉详情卡 + `switchModule('people')`", + * 用户被整个甩到另一个模块,还得自己再找一遍那个人 —— 很难用。 + * 现在改成就地打开同一张表单(与名册页同一个组件 `FbPersonEditSheet`)。 + * + * ⚠️ 用**对象**而不是 `boolean`:编辑表单要拿它回填(姓名/电话/生日/工作经历…), + * 只存 true/false 还得再去库里查一次。 + * ⚠️ 与 `pfCreating` 分开两个状态,不要合并成一个 `boolean + person?`: + * 新建时 `person = null` 是合法值(不是"没开"),合并后这两种情形会撞车。 + */ + @Local pfEditing: FbRosterPerson | null = null; /** 联系人卡要显示的人(本地库前 N 个) */ @Local pfRecent: FbRosterPerson[] = []; /** 名册总人数(卡片底部"共 N 人"用) */ @@ -737,6 +752,48 @@ export struct FbHomePage { this.pfDetail = null; } + /** + * 按 id 重读一个人,并(若该人正是当前详情卡)把详情卡换成最新值。 + * + * 🔴 2026-11-06:首页支持就地编辑后必须补这一步。 + * 编辑表单 `FbPersonEditSheet.save()` **只调接口、不写回本地库** + * (见那里的实现:PATCH 完直接 `onSaved()`)。所以: + * 把编辑前那份快照塞回 `pfDetail` 会让用户看到"改完还是旧名字"。 + * + * ⇒ **两级刷新**,与名册页 `FbPeoplePage.openDetail()` 同一思路: + * ① `byId()` 先读本地库 —— 快,但可能是旧值(编辑不一定同步了库); + * ② `fetchDetail()` 再打 `GET /people/` —— 服务端权威值, + * 回来就原地替换。断网时保持 ① 的显示,不弹错误。 + * + * ⚠️ 读不到(那人被删了、或令牌为空)就**什么都不做**, + * 不要拿 null 去覆盖一个正常打开的详情卡。 + * ⚠️ 用 `pfDetailId` 防"改 A 期间又去改 B"——响应回来时若详情卡 + * 已经换成别人,这次结果就该丢弃(名册页用 detailToken 干这件事)。 + */ + private async refreshPersonDetail(id: number): Promise { + if (id <= 0) { + return; + } + try { + /* ① 本地库(快) */ + const local: FbRosterPerson | null = await FbPersonRepo.inst().byId(id); + if (local !== null && this.pfDetail !== null && this.pfDetail.id === id) { + this.pfDetail = local; + } + /* ② 服务端权威值(准) */ + const token: string = FB_TOKEN_STORE.get(); + if (token === '') { + return; + } + const full: FbRosterPerson | null = await FbPersonRepo.inst().fetchDetail(id, token); + if (full !== null && this.pfDetail !== null && this.pfDetail.id === id) { + this.pfDetail = full; + } + } catch (err) { + hilog.warn(DOMAIN, TAG, '重读联系人失败(id=%{public}d):%{public}s', id, JSON.stringify(err)); + } + } + /** * 拨号(点联系人详情里的手机号时)。 * @@ -2321,10 +2378,15 @@ export struct FbHomePage { this.closePersonDetail(); }, onEdit: (): void => { - /* 关掉详情卡,跳到名册去改 —— 首页没有人员编辑表单, - 与其在这里硬塞一份,不如交给名册(那是它的主场)。 */ + /* 🔴 用户 2026-11-06:「点击编辑应该直接弹框编辑。」 + 改前这里是「关掉详情卡 + `switchModule('people')`」—— + 把用户整个甩到名册模块,他还得自己再找一遍那个人。 + ⇒ 现在就地打开同一张表单(`FbPersonEditSheet`,与名册页同一个组件)。 + ⚠️ 先把详情卡收起来:两层浮层叠着会互相抢点击, + 而且用户改完最想看到的是"改对了没"(见 `onSaved`)。 */ + const who: FbRosterPerson | null = this.pfDetail; this.closePersonDetail(); - this.app.switchModule('people'); + this.pfEditing = who; }, /* 🔴 用户 2026-11-06:「点开以后的弹窗,点击手机号可以直接拨打电话。」 ⚠️ 卡片只发号码,真正拨号走本页 `dial()`(理由见那里的注释)。 */ @@ -2482,6 +2544,66 @@ export struct FbHomePage { }) .transition(fbSheetTransition()) } + + // ==================== 联系人**编辑**(#89 后续)==================== + /* 🔴 用户 2026-11-06:「现在在首页上,弹出名册信息之后,点击编辑, + 怎么跳转到名册去了?点击编辑应该直接弹框编辑。」 + + 改前 `onEdit` 是 `switchModule('people')` —— 把用户甩到名册模块。 + 现在就地弹表单,用**同一个组件** `FbPersonEditSheet`(与名册页一致): + 不另写一份,否则两处字段/校验/保存逻辑必然分叉。 + + ⚠️ 遮罩 + 卡片成对写(本项目所有浮层的约定): + 遮罩在前、卡片在后,点遮罩等于取消。 + ⚠️ `isCreate` 不传(默认 false)⇒ 编辑模式,表单会用 `person` 回填。 */ + if (this.pfEditing !== null) { + Column() + .width('100%') + .height('100%') + .backgroundColor(FB_MASK) + .transition(fbMaskTransition()) + .onClick((): void => { + this.pfEditing = null; + }) + + FbPersonEditSheet({ + person: this.pfEditing, + /* 已有标签供点选(与新建同一个来源;不传的话用户只能手打, + 很容易出现"同事"和"同 事"两个标签) */ + knownTags: this.personKnownTags(), + accent: this.peopleAccent(), + onCancel: (): void => { + /* 🔴 取消 ⇒ **回到详情卡**(不是直接回首页)。 + 用户刚才是从详情进来的,取消只是"我不改了", + 不该连"我在看谁"一起丢掉 —— 与名册页 `onCancel` 同口径。 */ + const who: FbRosterPerson | null = this.pfEditing; + this.pfEditing = null; + this.pfDetail = who; + }, + onSaved: (): void => { + /* 保存成功 ⇒ 关表单、重读卡片数据,并把详情卡重新打开。 + ⚠️ 重新打开用的是**编辑前那份快照**(`pfEditing`), + 它可能是旧值(比如刚改了昵称)。所以紧接着要按 id + 从本地库重读一次,拿服务端权威值盖掉 —— 否则用户会看到 + "改完还是旧名字"(服务端已入库,界面却是旧的)。 */ + const who: FbRosterPerson | null = this.pfEditing; + this.pfEditing = null; + this.reloadSideCards(); + if (who !== null) { + /* ⚠️ 不能 await:`onSaved` 是同步回调(`() => void`)。 + 用 `.catch()` 显式兜住,别让一个失败的读库变成未捕获的 Promise 拒绝。 */ + this.refreshPersonDetail(who.id).catch((): void => { + }); + } + }, + onDeleted: (): void => { + /* 首页这张卡**不给删**(`canDelete: false`,避免误删), + 走不到这里;置 null 只是兜底,别留一个打不开的浮层。 */ + this.pfEditing = null; + } + }) + .transition(fbSheetTransition()) + } } .width('100%') .height('100%')