日程行程条目端上同步:字段、列表卡「出发地→目的地」可导航 (#67)
同步服务端 #67 的新字段与三处改动: 数据层 - RawCalEvent / FbCalEvent 加 fromPlace/toPlace/fromLng/fromLat/toLng/toLat - FbParse 解析赋值;新增 numStr() 把 DECIMAL 可能给的 number 转成字符串 (`s()` 签名只收 string|null|undefined,坐标字段会报类型错) - FbCalStore.toModels、DemoData 的 8 条演示数据同步补字段 渲染 - FbDayEventPanel 列表卡片:在时间行**上面**加一行「出发地 → 目的地」, 只对两端都有值的条目画(普通日程 fromPlace/toPlace 都是空串,不画)。 目的地用日历本色 + 下划线,点击 fbOpenNavigation: 有坐标走 `lat,lng?地名`(同名地点不会搜错城市),没有就让 FbNav 地理编码。 不塞进时间行是因为那里已有「时刻 + 重复徽标 + 地点」,窄屏会挤爆。 - FbDayTimeline 全天块:一行文字「出发地 → 目的地」,替代原来的 place。 不给点击 —— 26vp 高的块切两个热区点不准,导航入口在详情卡。 图标与 ✓ 的改动在服务端(Trip::leadIcon / 删 ✓ ),端上只消费 title, 所以不需要额外处理。
This commit is contained in:
@@ -19,7 +19,9 @@
|
|||||||
* (传 `events` 还有个好处:每次都是新数组引用 ⇒ 一定触发重建。)
|
* (传 `events` 还有个好处:每次都是新数组引用 ⇒ 一定触发重建。)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import common from '@ohos.app.ability.common';
|
||||||
import { FbColors, FbMetric } from '../common/Theme';
|
import { FbColors, FbMetric } from '../common/Theme';
|
||||||
|
import { fbOpenNavigation } from '../common/FbNav';
|
||||||
import { FbCalEvent } from '../model/Types';
|
import { FbCalEvent } from '../model/Types';
|
||||||
import { FbCalStyle } from '../data/FbCalSettings';
|
import { FbCalStyle } from '../data/FbCalSettings';
|
||||||
import { FbTimelineUtil } from '../common/FbTimeline';
|
import { FbTimelineUtil } from '../common/FbTimeline';
|
||||||
@@ -163,6 +165,45 @@ export struct FbDayEventPanel {
|
|||||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||||
.width('100%')
|
.width('100%')
|
||||||
|
|
||||||
|
/* 🔴 #67(用户 2026-11-05):「如果行程里面有地址,也就是有出发地和目的地,
|
||||||
|
应该像行程里显示的那样,把它显示在下面,这样一点就可以导航。」
|
||||||
|
|
||||||
|
⇒ 单独一行,放在时间行**上面**(先看到"从哪到哪",再看几点)。
|
||||||
|
只对行程条目画(两端都有值才画,缺一端就没意义)。
|
||||||
|
⚠️ 不塞进时间行:那里已经有「时刻 + 重复徽标 + 地点」,窄屏会挤爆。
|
||||||
|
|
||||||
|
颜色用 `ev.color`(该日历本的主题色)而不是写死 —— 用户可以在
|
||||||
|
「日历本设置」里改色,写死就不跟着变了。 */
|
||||||
|
if (ev.fromPlace !== '' && ev.toPlace !== '') {
|
||||||
|
Row({ space: 4 }) {
|
||||||
|
Text(ev.fromPlace)
|
||||||
|
.fontSize(FbMetric.fontTiny)
|
||||||
|
.fontColor(FbColors.muted)
|
||||||
|
.maxLines(1)
|
||||||
|
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||||
|
.constraintSize({ maxWidth: '42%' })
|
||||||
|
Text('→')
|
||||||
|
.fontSize(FbMetric.fontTiny)
|
||||||
|
.fontColor(FbColors.faint)
|
||||||
|
Text(ev.toPlace)
|
||||||
|
.fontSize(FbMetric.fontTiny)
|
||||||
|
.fontColor(ev.color)
|
||||||
|
.maxLines(1)
|
||||||
|
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||||
|
.decoration({ type: TextDecorationType.Underline, color: ev.color })
|
||||||
|
}
|
||||||
|
.width('100%')
|
||||||
|
.onClick((): void => {
|
||||||
|
/* 有坐标就带坐标(同名地点不会搜错城市);没有就让 FbNav 自己地理编码 */
|
||||||
|
const hasPos: boolean = ev.toLng !== '' && ev.toLat !== '';
|
||||||
|
const target: string = hasPos
|
||||||
|
? (ev.toLat + ',' + ev.toLng + '?' + ev.toPlace)
|
||||||
|
: ev.toPlace;
|
||||||
|
fbOpenNavigation(getContext(this) as common.UIAbilityContext,
|
||||||
|
target, ev.toPlace);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
Row({ space: 6 }) {
|
Row({ space: 6 }) {
|
||||||
Text(ev.allDay ? '全天'
|
Text(ev.allDay ? '全天'
|
||||||
: (ev.endTime !== '' ? (ev.time + ' - ' + ev.endTime) : ev.time))
|
: (ev.endTime !== '' ? (ev.time + ' - ' + ev.endTime) : ev.time))
|
||||||
|
|||||||
@@ -422,7 +422,22 @@ export struct FbDayTimeline {
|
|||||||
.maxLines(1)
|
.maxLines(1)
|
||||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||||
.layoutWeight(1)
|
.layoutWeight(1)
|
||||||
if (e.place !== '') {
|
/* 🔴 #67(用户 2026-11-05):「如果行程里面有地址,也就是有出发地和目的地,
|
||||||
|
应该像行程里显示的那样,把它显示在下面。」
|
||||||
|
⇒ 行程条目优先画「出发地 → 目的地」,普通日程仍画自己的 place。
|
||||||
|
|
||||||
|
⚠️ 这里**只画文字不给点击**:全天块整块高 26vp,再切成两个热区
|
||||||
|
会小到点不准(而且整块已经绑了 `onPick` 去看详情)。
|
||||||
|
导航入口放在详情卡里 —— 那里空间够,也符合"先看再操作"。
|
||||||
|
⚠️ 两个地名都截断到单行:这一行宽度本来就紧张(左侧还有「全天」徽标)。 */
|
||||||
|
if (e.fromPlace !== '' && e.toPlace !== '') {
|
||||||
|
Text(e.fromPlace + ' → ' + e.toPlace)
|
||||||
|
.fontSize(10)
|
||||||
|
.fontColor('#D9FFFFFF')
|
||||||
|
.maxLines(1)
|
||||||
|
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||||
|
.constraintSize({ maxWidth: 150 })
|
||||||
|
} else if (e.place !== '') {
|
||||||
Text(e.place)
|
Text(e.place)
|
||||||
.fontSize(10)
|
.fontSize(10)
|
||||||
.fontColor('#D9FFFFFF')
|
.fontColor('#D9FFFFFF')
|
||||||
|
|||||||
@@ -277,7 +277,14 @@ export class FbCalStore {
|
|||||||
allDay: r.num('all_day', 0) === 1,
|
allDay: r.num('all_day', 0) === 1,
|
||||||
place: r.strOr('place', ''),
|
place: r.strOr('place', ''),
|
||||||
note: r.strOr('note', ''),
|
note: r.strOr('note', ''),
|
||||||
kind: r.strOr('kind', '')
|
kind: r.strOr('kind', ''),
|
||||||
|
/* #67:行程条目的「从哪到哪」(普通日程一律空串,界面按空串不画) */
|
||||||
|
fromPlace: r.strOr('from_place', ''),
|
||||||
|
toPlace: r.strOr('to_place', ''),
|
||||||
|
fromLng: r.strOr('from_lng', ''),
|
||||||
|
fromLat: r.strOr('from_lat', ''),
|
||||||
|
toLng: r.strOr('to_lng', ''),
|
||||||
|
toLat: r.strOr('to_lat', '')
|
||||||
};
|
};
|
||||||
out.push(e);
|
out.push(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,14 @@ export function s(v: string | null | undefined): string {
|
|||||||
return v !== null && v !== undefined ? v : '';
|
return v !== null && v !== undefined ? v : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 把 `string | number | null | undefined` 统一成字符串(#67:坐标字段可能是 number) */
|
||||||
|
function numStr(v: string | number | null | undefined): string {
|
||||||
|
if (v === null || v === undefined) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return String(v);
|
||||||
|
}
|
||||||
|
|
||||||
/** 安全转数字 */
|
/** 安全转数字 */
|
||||||
export function n(v: number | null | undefined): number {
|
export function n(v: number | null | undefined): number {
|
||||||
return v !== null && v !== undefined ? v : 0;
|
return v !== null && v !== undefined ? v : 0;
|
||||||
@@ -111,6 +119,13 @@ interface RawCalEvent {
|
|||||||
date?: string; time?: string; end_time?: string; all_day?: boolean | number;
|
date?: string; time?: string; end_time?: string; all_day?: boolean | number;
|
||||||
kind?: string; ref_id?: number; location?: string; note?: string;
|
kind?: string; ref_id?: number; location?: string; note?: string;
|
||||||
repeat?: string; occ?: string;
|
repeat?: string; occ?: string;
|
||||||
|
/* 🔴 #67(用户 2026-11-05):「如果行程里面有地址,也就是有出发地和目的地,
|
||||||
|
应该像行程里显示的那样,把它显示在下面,这样一点就可以导航。」
|
||||||
|
⇒ 服务端 `Calendar::window()` 的 trip 派生段现在会带出这六个字段。
|
||||||
|
坐标单独带是因为导航要用:只给地名的话,同名地点会搜错城市。 */
|
||||||
|
from_place?: string; to_place?: string;
|
||||||
|
from_lng?: string | number; from_lat?: string | number;
|
||||||
|
to_lng?: string | number; to_lat?: string | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RawTrip {
|
interface RawTrip {
|
||||||
@@ -311,7 +326,16 @@ function parseCalRows(rows: RawCalEvent[]): FbCalEvent[] {
|
|||||||
allDay: b(r.all_day),
|
allDay: b(r.all_day),
|
||||||
place: s(r.location),
|
place: s(r.location),
|
||||||
note: s(r.note),
|
note: s(r.note),
|
||||||
kind: s(r.kind)
|
kind: s(r.kind),
|
||||||
|
/* #67:行程条目才有值,普通日程是空串 */
|
||||||
|
/* ⚠️ 坐标字段服务端可能给 number(DECIMAL),`s()` 只收 string
|
||||||
|
⇒ 统一走 numStr() 转成字符串,避免类型报错 */
|
||||||
|
fromPlace: s(r.from_place),
|
||||||
|
toPlace: s(r.to_place),
|
||||||
|
fromLng: numStr(r.from_lng),
|
||||||
|
fromLat: numStr(r.from_lat),
|
||||||
|
toLng: numStr(r.to_lng),
|
||||||
|
toLat: numStr(r.to_lat)
|
||||||
};
|
};
|
||||||
out.push(e);
|
out.push(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -268,17 +268,17 @@ export const FB_DEMO_CAL_BOOKS: FbCalBook[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const FB_DEMO_EVENTS_TODAY: FbCalEvent[] = [
|
export const FB_DEMO_EVENTS_TODAY: FbCalEvent[] = [
|
||||||
{ id: 1, title: '儿子数学兴趣班', date: '2026-10-11', time: '09:00', calName: '孩子的课表', color: '#4A7C82', allDay: false, place: '小区活动中心', calId: 0, endTime: '', note: '', kind: 'custom' },
|
{ id: 1, title: '儿子数学兴趣班', date: '2026-10-11', time: '09:00', calName: '孩子的课表', color: '#4A7C82', allDay: false, place: '小区活动中心', calId: 0, endTime: '', note: '', kind: 'custom', fromPlace: '', toPlace: '', fromLng: '', fromLat: '', toLng: '', toLat: '' },
|
||||||
{ id: 2, title: '家庭大扫除', date: '2026-10-11', time: '10:30', calName: '家庭日程', color: '#B0763A', allDay: false, place: '家', calId: 0, endTime: '', note: '', kind: 'custom' },
|
{ id: 2, title: '家庭大扫除', date: '2026-10-11', time: '10:30', calName: '家庭日程', color: '#B0763A', allDay: false, place: '家', calId: 0, endTime: '', note: '', kind: 'custom', fromPlace: '', toPlace: '', fromLng: '', fromLat: '', toLng: '', toLat: '' },
|
||||||
{ id: 3, title: '晚上看电影', date: '2026-10-11', time: '19:30', calName: '观影', color: '#7A4E8C', allDay: false, place: '家里', calId: 0, endTime: '', note: '', kind: 'custom' },
|
{ id: 3, title: '晚上看电影', date: '2026-10-11', time: '19:30', calName: '观影', color: '#7A4E8C', allDay: false, place: '家里', calId: 0, endTime: '', note: '', kind: 'custom', fromPlace: '', toPlace: '', fromLng: '', fromLat: '', toLng: '', toLat: '' },
|
||||||
{ id: 4, title: '妈妈体检预约', date: '2026-10-11', time: '', calName: '家庭日程', color: '#B0763A', allDay: true, place: '市立医院', calId: 0, endTime: '', note: '', kind: 'custom' },
|
{ id: 4, title: '妈妈体检预约', date: '2026-10-11', time: '', calName: '家庭日程', color: '#B0763A', allDay: true, place: '市立医院', calId: 0, endTime: '', note: '', kind: 'custom', fromPlace: '', toPlace: '', fromLng: '', fromLat: '', toLng: '', toLat: '' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const FB_DEMO_EVENTS_SOON: FbCalEvent[] = [
|
export const FB_DEMO_EVENTS_SOON: FbCalEvent[] = [
|
||||||
{ id: 11, title: '女儿钢琴课', date: '10-12', time: '16:00', calName: '孩子的课表', color: '#4A7C82', allDay: false, place: '琴行', calId: 0, endTime: '', note: '', kind: 'custom' },
|
{ id: 11, title: '女儿钢琴课', date: '10-12', time: '16:00', calName: '孩子的课表', color: '#4A7C82', allDay: false, place: '琴行', calId: 0, endTime: '', note: '', kind: 'custom', fromPlace: '', toPlace: '', fromLng: '', fromLat: '', toLng: '', toLat: '' },
|
||||||
{ id: 12, title: '结婚周年', date: '10-17', time: '', calName: '家庭日程', color: '#B0763A', allDay: true, place: '', calId: 0, endTime: '', note: '', kind: 'custom' },
|
{ id: 12, title: '结婚周年', date: '10-17', time: '', calName: '家庭日程', color: '#B0763A', allDay: true, place: '', calId: 0, endTime: '', note: '', kind: 'custom', fromPlace: '', toPlace: '', fromLng: '', fromLat: '', toLng: '', toLat: '' },
|
||||||
{ id: 13, title: '周末露营', date: '10-18', time: '08:00', calName: '家庭日程', color: '#B0763A', allDay: false, place: '仰口', calId: 0, endTime: '', note: '', kind: 'custom' },
|
{ id: 13, title: '周末露营', date: '10-18', time: '08:00', calName: '家庭日程', color: '#B0763A', allDay: false, place: '仰口', calId: 0, endTime: '', note: '', kind: 'custom', fromPlace: '', toPlace: '', fromLng: '', fromLat: '', toLng: '', toLat: '' },
|
||||||
{ id: 14, title: '家长会', date: '10-20', time: '18:30', calName: '孩子的课表', color: '#4A7C82', allDay: false, place: '学校', calId: 0, endTime: '', note: '', kind: 'custom' },
|
{ id: 14, title: '家长会', date: '10-20', time: '18:30', calName: '孩子的课表', color: '#4A7C82', allDay: false, place: '学校', calId: 0, endTime: '', note: '', kind: 'custom', fromPlace: '', toPlace: '', fromLng: '', fromLat: '', toLng: '', toLat: '' },
|
||||||
];
|
];
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|||||||
@@ -677,6 +677,23 @@ export interface FbCalEvent {
|
|||||||
note: string;
|
note: string;
|
||||||
/** 条目类型:`custom` = 用户自己的日程;其余是衍生条目(农历/节气/节假日/纪念日…) */
|
/** 条目类型:`custom` = 用户自己的日程;其余是衍生条目(农历/节气/节假日/纪念日…) */
|
||||||
kind: string;
|
kind: string;
|
||||||
|
/**
|
||||||
|
* 出发地 / 目的地(#67)。
|
||||||
|
*
|
||||||
|
* 🔴 用户 2026-11-05:「如果行程里面有地址,也就是有出发地和目的地,
|
||||||
|
* 应该像行程里显示的那样,把它显示在下面,这样一点就可以导航。」
|
||||||
|
*
|
||||||
|
* 只有 `kind === 'trip'` 的条目才有值,普通日程一律空串。
|
||||||
|
* ⚠️ 与 `place` 不是一回事:`place` 是日程自己的「地点」字段(用户手填的),
|
||||||
|
* 这两个是行程节点上的「从哪到哪」,语义不同、来源也不同。
|
||||||
|
*/
|
||||||
|
fromPlace: string;
|
||||||
|
toPlace: string;
|
||||||
|
/** 出发地 / 目的地坐标(导航用;可能为空 —— 老数据没存过) */
|
||||||
|
fromLng: string;
|
||||||
|
fromLat: string;
|
||||||
|
toLng: string;
|
||||||
|
toLat: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 这个条目能不能当"用户日程"来编辑(只有 custom 才算) */
|
/** 这个条目能不能当"用户日程"来编辑(只有 custom 才算) */
|
||||||
|
|||||||
Reference in New Issue
Block a user