diff --git a/entry/src/main/ets/components/FbEventSheet.ets b/entry/src/main/ets/components/FbEventSheet.ets index baccd4b..fd1f45f 100644 --- a/entry/src/main/ets/components/FbEventSheet.ets +++ b/entry/src/main/ets/components/FbEventSheet.ets @@ -56,7 +56,16 @@ export struct FbEventSheet { * undefined。宿主页本来就有可靠的上下文来源,且"要不要拉地图"属于导航决策 * (跟"当前在哪个模块"有关),不是这张卡的职责。 */ - @Event onNavigate: () => void = (): void => { + /** + * 点某一行位置信息去导航。 + * + * 🔴 #69(用户 2026-11-05):原来是**无参**的 —— 宿主只能自己猜"该导航到哪", + * 实际上它读的是 `event.location`。而行程条目根本没有 location + * (服务端 trip 段不输出那个字段),于是详情卡里既没有位置信息, + * 就算硬加一行也没法指定导航到出发地还是目的地。 + * ⇒ 改成带 `navTarget`:点哪一行就把那一行的地名/坐标交出去。 + */ + @Event onNavigate: (navTarget: string) => void = (navTarget: string): void => { }; @Event onClose: () => void = (): void => { }; @@ -234,10 +243,27 @@ export struct FbEventSheet { * 重建),不存在"数据变了行不刷新"的场景,所以用 Builder 是安全的; * 但仍保持**单参数**,把风险面压到最小。 */ + /** + * 算出交给 `onNavigate` 的导航目标(#69)。 + * + * 有坐标就带坐标:`lat,lng?地名` —— 同名地点不会搜错城市 + * (旅程模块踩过:全国搜「珠江路1018号」搜到了江苏南通,差 500 公里)。 + * 没有坐标就只给地名,让 `FbNav` 自己走系统地理编码。 + * + * ⚠️ 这是个**纯函数**(不读 this),所以放在 Builder 外面, + * 不会引入"@Builder 里调方法导致不刷新"的问题。 + */ + private navTargetOf(place: string, lng: string, lat: string): string { + if (lng !== '' && lat !== '') { + return lat + ',' + lng + '?' + place; + } + return place; + } + @Builder - navRow(place: string) { + navRowRaw(label: string, place: string, navTarget: string) { Row({ space: 10 }) { - Text('地点') + Text(label) .fontSize(FbMetric.fontTiny) .fontColor(FbColors.faint) .width(56) @@ -271,7 +297,7 @@ export struct FbEventSheet { .padding({ top: 8, bottom: 8 }) .alignItems(VerticalAlign.Center) .onClick((): void => { - this.onNavigate(); + this.onNavigate(navTarget); }) } @@ -359,6 +385,26 @@ export struct FbEventSheet { if (this.remindText() !== '') { this.row('提醒', this.remindText()) } + /* 🔴 #69(用户 2026-11-05):「点击行程里旅行日历本里的信息时, + 弹出来的那个框…还是没有出发地和目的地。」 + + ⇒ 行程派生条目(kind='trip')服务端**不给 `location`** —— + 行程没有单一"地点"概念,有的是"从哪到哪"。 + 所以这里必须按 fromPlace / toPlace 分开画两行。 + + 三者互斥地覆盖所有情况: + · 行程条目 → 出发地 + 目的地 + · 普通日程 → 地点 + 不会同时出现(行程的 location 恒为空串, + 普通日程的 fromPlace/toPlace 恒为空串)。 */ + if (this.event.fromPlace !== '') { + this.navRowRaw('出发地', this.event.fromPlace, + this.navTargetOf(this.event.fromPlace, this.event.fromLng, this.event.fromLat)) + } + if (this.event.toPlace !== '') { + this.navRowRaw('目的地', this.event.toPlace, + this.navTargetOf(this.event.toPlace, this.event.toLng, this.event.toLat)) + } if (this.event.location !== '') { /* 🔴 v73(用户 2026-10-21「有地址的还可以直接一键导航」): 地点不再是纯文字 —— **整行可点**,右侧带一颗「导航」小标签。 @@ -368,7 +414,8 @@ export struct FbEventSheet { ⚠️ 但**视觉上**仍要有那颗「导航」标签 —— 只有"整行可点" 而没有任何标记的话,用户根本不知道地点能点 (本项目踩过:卡尾"查看行程"是纯文本,用户以为按钮坏了)。 */ - this.navRow(this.event.location) + this.navRowRaw('地点', this.event.location, + this.navTargetOf(this.event.location, '', '')) } if (this.event.note !== '') { this.row('备注', this.event.note) @@ -421,7 +468,9 @@ export struct FbEventSheet { .borderRadius(FbMetric.radiusS) .border({ width: 1, color: this.accentAlpha('33') }) .onClick((): void => { - this.onNavigate(); + /* #69:底部这颗按钮不带具体行 —— 交给宿主自己挑一个 + (传空串 = "按老规则推导":location 优先) */ + this.onNavigate(''); }) } if (this.canEdit) { diff --git a/entry/src/main/ets/data/FbCalRepo.ets b/entry/src/main/ets/data/FbCalRepo.ets index bba057c..138b464 100644 --- a/entry/src/main/ets/data/FbCalRepo.ets +++ b/entry/src/main/ets/data/FbCalRepo.ets @@ -42,6 +42,25 @@ export interface FbCalEventFull { transp: boolean; /** 创建者 person_id */ created_by: number; + /** + * 出发地 / 目的地(#69)。 + * + * 🔴 用户 2026-11-05:「点击行程里旅行日历本里的信息时,弹出来的那个框… + * 还是没有出发地和目的地。」 + * + * 行程派生条目(kind='trip')服务端**不输出 `location`** —— + * 行程没有单一"地点"概念,它有的是"从哪到哪"。 + * ⇒ 详情卡必须靠这几个字段才能显示,不能只读 `location`。 + * + * 普通日程一律空串。 + */ + fromPlace: string; + toPlace: string; + /** 坐标(导航用;老数据可能为空) */ + fromLng: string; + fromLat: string; + toLng: string; + toLat: string; } /** 写日程时要发的字段(全部可选 —— 只传变化的) */ @@ -226,7 +245,15 @@ export class FbCalRepo { reminder_minutes: this.remStr(o, 'reminder_minutes'), status: this.str(o, 'status') !== '' ? this.str(o, 'status') : 'confirmed', transp: o['transp'] === true || o['transp'] === 1, - created_by: this.num(o, 'created_by') + created_by: this.num(o, 'created_by'), + /* #69:行程条目的「从哪到哪」。接口里只有行程派生条目才给这几个字段, + 普通日程取到的是默认空串。 */ + fromPlace: this.str(o, 'from_place'), + toPlace: this.str(o, 'to_place'), + fromLng: this.str(o, 'from_lng'), + fromLat: this.str(o, 'from_lat'), + toLng: this.str(o, 'to_lng'), + toLat: this.str(o, 'to_lat') }; return e; } diff --git a/entry/src/main/ets/pages/calendar/FbCalendarPage.ets b/entry/src/main/ets/pages/calendar/FbCalendarPage.ets index 9780605..c870c80 100644 --- a/entry/src/main/ets/pages/calendar/FbCalendarPage.ets +++ b/entry/src/main/ets/pages/calendar/FbCalendarPage.ets @@ -1022,13 +1022,17 @@ export struct FbCalendarPage { * 浮层那边对空串就是"不显示这一行",不会画出假信息。 * (宁可少一行,也不要说"不重复" —— 那是错的。) */ + /** #69:与首页 `FbHomePage.shellOf()` 同一套(两处独立实现,改一处容易漏)。 */ private shellOf(b: FbCalEvent): FbCalEventFull { const shell: FbCalEventFull = { id: b.id, calendar_id: b.calId, title: b.title, location: b.place, starts_at: b.date + ' ' + (b.allDay ? '00:00:00' : (b.time + ':00')), ends_at: b.endTime !== '' ? (b.date + ' ' + b.endTime + ':00') : '', all_day: b.allDay, note: b.note, rrule: '', reminder_minutes: '', - status: 'confirmed', transp: false, created_by: 0 + status: 'confirmed', transp: false, created_by: 0, + /* #69:与首页 shellOf 同一套(两处独立实现,容易只改一处) */ + fromPlace: b.fromPlace, toPlace: b.toPlace, + fromLng: b.fromLng, fromLat: b.fromLat, toLng: b.toLng, toLat: b.toLat }; return shell; } @@ -1073,10 +1077,19 @@ export struct FbCalendarPage { * ⚠️ 拿 `UIAbilityContext`(`startAbility` 只在这个上下文上有, * 用 `common.Context` 编译就报错 —— 名册拨号那次实测过)。 */ - private navigateDetail(): void { + /** + * 去导航(#69:改成接受 `navTarget`)。 + * + * 原来无参、只读 `detailFull.location`。行程条目没有 location, + * 且详情卡现在有「出发地」「目的地」两行,要能区分点的是哪一行。 + */ + private navigateDetail(navTarget: string): void { const placeOfFull: string = this.detailFull !== null ? this.detailFull.location : ''; const placeOfBase: string = this.detailBase !== null ? this.detailBase.place : ''; - const place: string = placeOfFull !== '' ? placeOfFull : placeOfBase; + /* 优先用详情卡传来的目标;为空才退回原来那套推导 */ + const place: string = navTarget !== '' + ? navTarget + : (placeOfFull !== '' ? placeOfFull : placeOfBase); const title: string = this.detailFull !== null ? this.detailFull.title : (this.detailBase !== null ? this.detailBase.title : ''); const ctx: common.UIAbilityContext | undefined = @@ -1226,8 +1239,8 @@ export struct FbCalendarPage { 用户改完点保存才报「这本日历你没有写权限」。 与首页 `FbHomePage` 的 canEdit 保持同一口径。 */ canEdit: this.detailBase !== null && isEventWritable(this.detailBase, this.books), - onNavigate: (): void => { - this.navigateDetail(); + onNavigate: (navTarget: string): void => { + this.navigateDetail(navTarget); }, onClose: (): void => { this.closeDetail(); diff --git a/entry/src/main/ets/pages/home/FbHomePage.ets b/entry/src/main/ets/pages/home/FbHomePage.ets index 0a6d330..f0d4e57 100644 --- a/entry/src/main/ets/pages/home/FbHomePage.ets +++ b/entry/src/main/ets/pages/home/FbHomePage.ets @@ -513,13 +513,34 @@ export struct FbHomePage { } /** 列表那条 → 完整字段的壳(只垫列表**已知**的字段,别编) */ + /** + * 把列表条目垫成一份"详情壳"。 + * + * 🔴 #69(用户 2026-11-05):「点击行程里旅行日历本里的信息时,弹出来的那个框… + * 还是没有出发地和目的地。」 + * + * 为什么这个壳很重要:`openEvent()` 里 + * `if (!isEditableEvent(e)) { this.evtFull = this.shellOf(e); return; }` + * —— 行程条目的 `kind` 是 `'trip'` 不是 `'custom'`, + * **永远走这条捷径、压根不请求详情接口**(而且也没有意义: + * 行程节点的数据在 `trip_nodes` 表里,`Calendar::event($id)` 查的是 + * `calendar_events`,对节点 id 必然查不到)。 + * + * ⇒ 位置信息**只能**靠这个壳带过去。原来只塞了 `location: b.place`, + * 而行程节点的 `place` 是空串(它的位置概念是"从哪到哪"), + * 于是详情卡里一行位置信息都没有。 + */ private shellOf(b: FbCalEvent): FbCalEventFull { const shell: FbCalEventFull = { id: b.id, calendar_id: b.calId, title: b.title, location: b.place, starts_at: b.date + ' ' + (b.allDay ? '00:00:00' : (b.time + ':00')), ends_at: b.endTime !== '' ? (b.date + ' ' + b.endTime + ':00') : '', all_day: b.allDay, note: b.note, rrule: '', reminder_minutes: '', - status: 'confirmed', transp: false, created_by: 0 + status: 'confirmed', transp: false, created_by: 0, + /* #69:行程条目的「从哪到哪」—— 服务端不给 location, + 不给这几个字段的话详情卡里就没有任何位置信息 */ + fromPlace: b.fromPlace, toPlace: b.toPlace, + fromLng: b.fromLng, fromLat: b.fromLat, toLng: b.toLng, toLat: b.toLat }; return shell; } @@ -533,10 +554,20 @@ export struct FbHomePage { } /** 详情卡上点「导航」/ 点地点行 → 拉起地图 */ - private navigateEvent(): void { + /** + * 去导航(#69:改成接受 `navTarget`)。 + * + * 原来是无参的 —— 只读 `evtFull.location`。行程条目没有 location, + * 且详情卡里现在有「出发地」「目的地」两行,得知道点的是哪一行。 + * ⇒ 详情卡把那一行的地名(带坐标时是 `lat,lng?地名`)直接交进来。 + */ + private navigateEvent(navTarget: string): void { const placeOfFull: string = this.evtFull !== null ? this.evtFull.location : ''; const placeOfBase: string = this.evtBase !== null ? this.evtBase.place : ''; - const place: string = placeOfFull !== '' ? placeOfFull : placeOfBase; + /* 优先用详情卡传来的目标;它为空(老路径 / 极少数情况)才退回原来那套推导 */ + const place: string = navTarget !== '' + ? navTarget + : (placeOfFull !== '' ? placeOfFull : placeOfBase); const title: string = this.evtFull !== null ? this.evtFull.title : (this.evtBase !== null ? this.evtBase.title : ''); /* ⚠️ 必须是 `UIAbilityContext`(`startAbility` 只在这个上下文上有; @@ -1846,8 +1877,8 @@ export struct FbHomePage { v86 开编辑入口的决定本身没错(编辑就地改、不跳页), 错在没把"没有写权限的本"排除掉 ⇒ 改为按 calWrite 判定。 */ canEdit: this.evtBase !== null && isEventWritable(this.evtBase, this.calBooks), - onNavigate: (): void => { - this.navigateEvent(); + onNavigate: (navTarget: string): void => { + this.navigateEvent(navTarget); }, onClose: (): void => { this.closeEvent();