2026-10-20 07:35:37 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 家书 FamilyBook · 「日期 + 时间」选择(鸿蒙端,**日程与观影共用**)
|
|
|
|
|
|
*
|
|
|
|
|
|
* 🔴 v64(用户 2026-10-19)原话:
|
|
|
|
|
|
* 「开始时间和结束时间,应该都是**时间选择框**才对,跟那个日程里面的是一样的,
|
|
|
|
|
|
* 保持一致。」
|
|
|
|
|
|
*
|
|
|
|
|
|
* ⇒ 观影的「记一场」原来把开始/结束做成了**两个纯文本输入框**
|
|
|
|
|
|
* (要用户手打 `2026-10-19 19:30` 这种串,还得自己记得格式),
|
|
|
|
|
|
* 而日程那边早就有一个"点开 → 日期与时间并排滚轮 → 确定回填"的控件。
|
|
|
|
|
|
* 用户要求两处**同一种控件**。
|
|
|
|
|
|
*
|
|
|
|
|
|
* ⚠️ 为什么不复制一份到观影里(这是本文件存在的唯一理由):
|
|
|
|
|
|
* 复制 = 两份实现,改一处忘一处。本项目已经为"两份实现必然漂移"
|
|
|
|
|
|
* 栽过好几次(弹框描边环、关闭按钮、地址格式化都是)。所以抽成共用组件:
|
|
|
|
|
|
* · `FbDateTimeRow` —— 表单里的那一整行(显示 `10月16日 周五 20:00`)
|
|
|
|
|
|
* · `FbDateTimePicker` —— 点开后的弹层(日期与时间**并排** + 确定/取消)
|
|
|
|
|
|
* · 一组 `fb*` 换算函数 —— `cnDate` / `parseDate` / `parseTime` 等,
|
|
|
|
|
|
* 格式化规则也只有这一份("10月16日 周五"这种写法两处必须一模一样)
|
|
|
|
|
|
*
|
|
|
|
|
|
* ⚠️ 为什么**弹层不放进 `FbDateTimeRow` 内部**:
|
|
|
|
|
|
* Row 一定位于浮层卡片**内部**(卡片才 92% 宽),弹层若渲染在 Row 里,
|
|
|
|
|
|
* 就只能撑到卡片的尺寸 ⇒ 变成"卡中卡",与全应用"居中大卡 + 整屏遮罩"
|
|
|
|
|
|
* 的浮层形态不一致(真机上看着像被挤扁了)。
|
|
|
|
|
|
* ⇒ 弹层由**宿主页面**放到它自己的 `Stack` 里(与 `editCard` 同级、叠在它上面),
|
|
|
|
|
|
* 这样它跟其它浮层长得一样。宿主只需要管一个 `pickOpen` 布尔。
|
|
|
|
|
|
*
|
|
|
|
|
|
* ⚠️ 弹层内部用**草稿**(`draftDate`/`draftHour`/`draftMin`):点了「取消」不写回。
|
|
|
|
|
|
* 若直接改宿主的真实值,"取消"就变成不可撤销了。
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
import { FbColors, FbMetric, FB_MASK, FbTint } from '../common/Theme';
|
|
|
|
|
|
import { FbSheetRing } from './FbSheetRing';
|
|
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
// 换算 / 格式化(**全应用唯一一份**)
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
|
|
/** 补零到两位 */
|
|
|
|
|
|
export function fbPad2(n: number): string {
|
|
|
|
|
|
return n < 10 ? ('0' + n.toString()) : n.toString();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** `YYYY-MM-DD` → `10月16日 周五`(比裸日期好读,与时刻并排也不长) */
|
|
|
|
|
|
export function fbCnDate(iso: string): string {
|
|
|
|
|
|
if (iso.length < 10) {
|
|
|
|
|
|
return iso;
|
|
|
|
|
|
}
|
|
|
|
|
|
const y: number = parseInt(iso.substring(0, 4), 10);
|
|
|
|
|
|
const m: number = parseInt(iso.substring(5, 7), 10);
|
|
|
|
|
|
const d: number = parseInt(iso.substring(8, 10), 10);
|
|
|
|
|
|
const w: string[] = ['日', '一', '二', '三', '四', '五', '六'];
|
|
|
|
|
|
return m.toString() + '月' + d.toString() + '日 周' + w[new Date(y, m - 1, d).getDay()];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 今天 `YYYY-MM-DD` */
|
|
|
|
|
|
export function fbToday(): string {
|
|
|
|
|
|
const n: Date = new Date();
|
|
|
|
|
|
return fbPad2(n.getFullYear()) + '-' + fbPad2(n.getMonth() + 1) + '-' + fbPad2(n.getDate());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** `YYYY-MM-DD` → `Date`(解析不出给"今天") */
|
|
|
|
|
|
export function fbParseDate(s: string): Date {
|
|
|
|
|
|
if (s.length < 10) {
|
|
|
|
|
|
return new Date();
|
|
|
|
|
|
}
|
|
|
|
|
|
const y: number = parseInt(s.substring(0, 4), 10);
|
|
|
|
|
|
const m: number = parseInt(s.substring(5, 7), 10) - 1;
|
|
|
|
|
|
const d: number = parseInt(s.substring(8, 10), 10);
|
|
|
|
|
|
return new Date(y, m, d);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** `HH:mm` → `Date`(只取时分;解析不出给 09:00) */
|
|
|
|
|
|
export function fbParseTime(s: string): Date {
|
|
|
|
|
|
const out: Date = new Date();
|
|
|
|
|
|
if (s.length >= 5) {
|
|
|
|
|
|
out.setHours(parseInt(s.substring(0, 2), 10));
|
|
|
|
|
|
out.setMinutes(parseInt(s.substring(3, 5), 10));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
out.setHours(9);
|
|
|
|
|
|
out.setMinutes(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
return out;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** `YYYY-MM-DD HH:MM:SS` → 日期段 `YYYY-MM-DD` */
|
|
|
|
|
|
export function fbDateOf(dt: string, fallback: string): string {
|
|
|
|
|
|
return dt.length >= 10 ? dt.substring(0, 10) : fallback;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** `YYYY-MM-DD HH:MM:SS` → `HH:mm` */
|
|
|
|
|
|
export function fbTimeOf(dt: string, fallback: string): string {
|
|
|
|
|
|
return dt.length >= 16 ? dt.substring(11, 16) : fallback;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** `Date` 的年月日 → `YYYY-MM-DD` */
|
|
|
|
|
|
export function fbIsoOf(d: Date): string {
|
|
|
|
|
|
return fbPad2(d.getFullYear()) + '-' + fbPad2(d.getMonth() + 1) + '-' + fbPad2(d.getDate());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** `HH:mm` 往后推 1 小时(结束时间没填时的兜底) */
|
|
|
|
|
|
export function fbAddHour(time: string): string {
|
|
|
|
|
|
const hh: number = time.length >= 5 ? parseInt(time.substring(0, 2), 10) : 9;
|
|
|
|
|
|
const mm: string = time.length >= 5 ? time.substring(3, 5) : '00';
|
|
|
|
|
|
return fbPad2(Math.min(hh + 1, 23)) + ':' + mm;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-11-01 09:38:14 +08:00
|
|
|
|
/** `fbPlusHours()` 的返回形状(ArkTS 不允许匿名对象字面量作为返回值类型书写,单独给个接口) */
|
|
|
|
|
|
export interface FbDateTime {
|
|
|
|
|
|
date: string;
|
|
|
|
|
|
time: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* `YYYY-MM-DD` + `HH:mm` 往后推 N 小时 ⇒ `{ date, time }`(**跨天自动进位**)。
|
|
|
|
|
|
*
|
|
|
|
|
|
* 起源:工单 #34 追加需求(2026-11-01)——
|
|
|
|
|
|
* 「当我选择了开始时间并确定之后,结束时间要跟着开始时间变成开始时间加1个小时。
|
|
|
|
|
|
* 也就是说,所有时间我们都默认它是一个小时。如果不是一个小时,再让他自己去改。」
|
|
|
|
|
|
*
|
|
|
|
|
|
* ⚠️ 为什么不复用上面的 `fbAddHour()`:那只返回时间、不带日期,
|
|
|
|
|
|
* 而且用 `Math.min(hh+1, 23)` **封顶 23 点** ⇒ 23:30 加一小时还是 23:30。
|
|
|
|
|
|
* 日程跨天是常态(20:00 → 次日 02:00 是真实用例),必须有日期。
|
|
|
|
|
|
*
|
|
|
|
|
|
* ⚠️ 用 `Date` 做进位而不是自己算:跨月、跨年、闰年、月末天数全交给运行时。
|
|
|
|
|
|
* 自己 `if (hh >= 24) { dd += 1 }` 看着简单,但"11 月 30 日 + 1 天"就会错成 11 月 31 日。
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function fbPlusHours(date: string, time: string, hours: number): FbDateTime {
|
|
|
|
|
|
const d: Date = fbParseDate(date);
|
|
|
|
|
|
const h: number = time.length >= 5 ? parseInt(time.substring(0, 2), 10) : 9;
|
|
|
|
|
|
const m: number = time.length >= 5 ? parseInt(time.substring(3, 5), 10) : 0;
|
|
|
|
|
|
const out: Date = new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
|
|
|
|
|
out.setHours(h + hours, m, 0, 0);
|
|
|
|
|
|
return {
|
|
|
|
|
|
date: fbIsoOf(out),
|
|
|
|
|
|
time: fbPad2(out.getHours()) + ':' + fbPad2(out.getMinutes())
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-20 07:35:37 +08:00
|
|
|
|
/** 日期 + 时分拼成服务端认的 `YYYY-MM-DD HH:MM`(秒由服务端补) */
|
|
|
|
|
|
export function fbJoinDateTime(date: string, time: string): string {
|
|
|
|
|
|
return date + ' ' + time;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
// ① 表单里的那一行
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
|
|
@ComponentV2
|
|
|
|
|
|
export struct FbDateTimeRow {
|
|
|
|
|
|
/** 左侧标签(「开始」/「结束」) */
|
|
|
|
|
|
@Param label: string = '开始';
|
|
|
|
|
|
/** 日期段 `YYYY-MM-DD` */
|
|
|
|
|
|
@Param date: string = '';
|
|
|
|
|
|
/** 时分 `HH:mm`(`allDay` 为真时不显示) */
|
|
|
|
|
|
@Param time: string = '';
|
|
|
|
|
|
/** 全天:只显示日期(日程用) */
|
|
|
|
|
|
@Param allDay: boolean = false;
|
|
|
|
|
|
/** 主题色(右侧 `›` 与文字对齐无关,留着给以后的强调态用) */
|
|
|
|
|
|
@Param accent: string = '';
|
|
|
|
|
|
/** 点这一行(宿主把它接到"打开弹层") */
|
|
|
|
|
|
@Param onTap: () => void = (): void => {
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
build() {
|
2026-10-20 08:55:27 +08:00
|
|
|
|
/*
|
|
|
|
|
|
* ⚠️ `.onClick` 挂在**根 `Row`** 上(整行可点),不是挂在里面那个
|
|
|
|
|
|
* `layoutWeight(1)` 的 `Row` 上 —— 2026-10-20 真机实测:
|
|
|
|
|
|
* 挂内层时,那一行会被表单所在的**两列容器**(dump 里两个
|
|
|
|
|
|
* `x1=50..521 / 559..1030、y1=602..1500` 的可点节点)盖住,
|
|
|
|
|
|
* 点击**穿透**到底下的观影列表(表现为"点日期行,列表反而响应了")。
|
|
|
|
|
|
* 根 `Row`(`.width('100%')`)与 `FbCalOptionRow` 同一形态,热区完整。
|
|
|
|
|
|
*/
|
2026-10-20 07:35:37 +08:00
|
|
|
|
Row({ space: 10 }) {
|
|
|
|
|
|
Text(this.label)
|
|
|
|
|
|
.fontSize(FbMetric.fontTiny)
|
|
|
|
|
|
.fontColor(FbColors.faint)
|
|
|
|
|
|
.width(28)
|
|
|
|
|
|
|
2026-10-20 08:55:27 +08:00
|
|
|
|
// 日期与时间在**同一个控件**里,中间不再分两块
|
2026-10-20 07:35:37 +08:00
|
|
|
|
Row() {
|
|
|
|
|
|
Text(fbCnDate(this.date))
|
|
|
|
|
|
.fontSize(FbMetric.fontSmall)
|
|
|
|
|
|
.fontColor(FbColors.ink)
|
|
|
|
|
|
if (!this.allDay) {
|
|
|
|
|
|
Text(' ' + this.time)
|
|
|
|
|
|
.fontSize(FbMetric.fontSmall)
|
|
|
|
|
|
.fontWeight(FontWeight.Medium)
|
|
|
|
|
|
.fontColor(FbColors.ink)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
.layoutWeight(1)
|
|
|
|
|
|
.height(42)
|
|
|
|
|
|
.padding({ left: 12, right: 12 })
|
|
|
|
|
|
.backgroundColor(FbColors.inputBg)
|
|
|
|
|
|
.borderRadius(FbMetric.radiusS)
|
|
|
|
|
|
.alignItems(VerticalAlign.Center)
|
|
|
|
|
|
|
|
|
|
|
|
// 右侧小箭头:暗示"点开会选"
|
|
|
|
|
|
Text('›')
|
|
|
|
|
|
.fontSize(FbMetric.fontBody)
|
|
|
|
|
|
.fontColor(FbColors.faint)
|
|
|
|
|
|
}
|
|
|
|
|
|
.width('100%')
|
|
|
|
|
|
.alignItems(VerticalAlign.Center)
|
2026-10-20 08:55:27 +08:00
|
|
|
|
.onClick((): void => {
|
|
|
|
|
|
this.onTap();
|
|
|
|
|
|
})
|
2026-10-20 07:35:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
// ② 点开后的弹层
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
|
|
|
|
@ComponentV2
|
|
|
|
|
|
export struct FbDateTimePicker {
|
|
|
|
|
|
/** 弹层标题(「选择开始时间」/「选择结束时间」) */
|
|
|
|
|
|
@Param title: string = '选择时间';
|
|
|
|
|
|
/** 打开时的初始日期 */
|
|
|
|
|
|
@Param date: string = '';
|
|
|
|
|
|
/** 打开时的初始时分 */
|
|
|
|
|
|
@Param time: string = '09:00';
|
|
|
|
|
|
/** 全天:只画日期滚轮 */
|
|
|
|
|
|
@Param allDay: boolean = false;
|
|
|
|
|
|
/** 主题色(环色 + 确定按钮底色) */
|
|
|
|
|
|
@Param accent: string = '';
|
|
|
|
|
|
/** 可选日期范围(默认 2000~2035:观影可能是很多年前看的老片子) */
|
|
|
|
|
|
@Param minDate: string = '2000-01-01';
|
|
|
|
|
|
@Param maxDate: string = '2035-12-31';
|
|
|
|
|
|
|
|
|
|
|
|
/** 「确定」:回填(日期 `YYYY-MM-DD`、时分 `HH:mm`) */
|
|
|
|
|
|
@Event onConfirm: (date: string, time: string) => void = (): void => {
|
|
|
|
|
|
};
|
|
|
|
|
|
/** 「取消」/ 点遮罩 */
|
|
|
|
|
|
@Event onCancel: () => void = (): void => {
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// ---- 草稿(取消则丢弃,不碰宿主的值)----
|
|
|
|
|
|
@Local draftDate: Date = new Date();
|
|
|
|
|
|
@Local draftHour: number = 9;
|
|
|
|
|
|
@Local draftMin: number = 0;
|
|
|
|
|
|
|
|
|
|
|
|
aboutToAppear(): void {
|
|
|
|
|
|
this.draftDate = fbParseDate(this.date);
|
|
|
|
|
|
const tm: Date = fbParseTime(this.time);
|
|
|
|
|
|
this.draftHour = tm.getHours();
|
|
|
|
|
|
this.draftMin = tm.getMinutes();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
build() {
|
2026-10-20 08:55:27 +08:00
|
|
|
|
/*
|
|
|
|
|
|
* 🔴🔴 必须是 `Stack`,**不能**是 `Column`(2026-10-20 真机实测抓到的真 bug)。
|
|
|
|
|
|
*
|
|
|
|
|
|
* 错误形态(原先):
|
|
|
|
|
|
* Column() { // 根:线性布局
|
|
|
|
|
|
* Column().width('100%').height('100%') // ← 全屏遮罩
|
|
|
|
|
|
* Column() { ...卡片... } // ← 被挤到屏幕外
|
|
|
|
|
|
* }
|
|
|
|
|
|
* `Column` 是**线性**布局:遮罩占满整高之后,卡片只能排在它**下面**,
|
|
|
|
|
|
* 于是落到屏幕之外 —— 弹层"点了没反应"。
|
|
|
|
|
|
*
|
|
|
|
|
|
* 症状极具误导性(本轮排查花了很久,逐条记下):
|
|
|
|
|
|
* · 编译通过、无告警;`aboutToAppear` **确实被调用**(组件实例化了);
|
|
|
|
|
|
* · `dumpLayout` 里**完全看不到**弹层的任何节点(连插桩的 30 号红字都没有);
|
|
|
|
|
|
* · 点日期行会"穿透"到底下的观影列表(因为卡片不在屏幕内,点到的其实是列表);
|
|
|
|
|
|
* · 同文件的 `FbDateTimeRow`(没有遮罩)一直正常 —— 差异只在遮罩这一层。
|
|
|
|
|
|
* ⇒ 教训:**全屏遮罩与卡片要"叠"(Stack),不能"排"(Column)**。
|
|
|
|
|
|
* 项目里能用的浮层(`FbMovieInsightSheet`、`FbMovieDetailSheet`)都是
|
|
|
|
|
|
* `Column{ 卡片 }` 或 `Stack{ ... }`,**没有**把全屏遮罩当 Column 子项排的。
|
|
|
|
|
|
*/
|
|
|
|
|
|
Stack({ alignContent: Alignment.Center }) {
|
2026-10-20 07:35:37 +08:00
|
|
|
|
// 遮罩:整屏铺一层,点它 = 取消。
|
|
|
|
|
|
// ⚠️ 必须**有**遮罩:没有的话,点弹层外面会直接点到下面那张编辑卡片上
|
|
|
|
|
|
// (真机上表现为"随手一点,表单被改了")。
|
|
|
|
|
|
Column()
|
|
|
|
|
|
.width('100%')
|
|
|
|
|
|
.height('100%')
|
|
|
|
|
|
.backgroundColor(FB_MASK)
|
|
|
|
|
|
.onClick((): void => {
|
|
|
|
|
|
this.onCancel();
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// ---- 居中卡片 ----
|
|
|
|
|
|
Column() {
|
|
|
|
|
|
FbSheetRing({ radius: FbMetric.radius, ringColor: this.accentColor() }) {
|
|
|
|
|
|
Column({ space: 14 }) {
|
|
|
|
|
|
Text(this.title)
|
|
|
|
|
|
.fontSize(FbMetric.fontTitle)
|
|
|
|
|
|
.fontWeight(FontWeight.Medium)
|
|
|
|
|
|
.fontColor(FbColors.ink)
|
|
|
|
|
|
.width('100%')
|
|
|
|
|
|
.textAlign(TextAlign.Center)
|
|
|
|
|
|
.padding({ top: 12 })
|
|
|
|
|
|
|
|
|
|
|
|
Row({ space: 6 }) {
|
|
|
|
|
|
DatePicker({
|
|
|
|
|
|
start: fbParseDate(this.minDate),
|
|
|
|
|
|
end: fbParseDate(this.maxDate),
|
|
|
|
|
|
selected: this.draftDate
|
|
|
|
|
|
})
|
|
|
|
|
|
.onDateChange((v: Date): void => {
|
|
|
|
|
|
this.draftDate = v;
|
|
|
|
|
|
})
|
|
|
|
|
|
.textStyle({ color: FbColors.faint, font: { size: 14 } })
|
|
|
|
|
|
.selectedTextStyle({ color: FbColors.ink, font: { size: 16, weight: FontWeight.Medium } })
|
|
|
|
|
|
.disappearTextStyle({ color: FbColors.faint, font: { size: 14 } })
|
|
|
|
|
|
.layoutWeight(1)
|
|
|
|
|
|
.height(210)
|
|
|
|
|
|
|
|
|
|
|
|
if (!this.allDay) {
|
|
|
|
|
|
TimePicker({ selected: new Date(2000, 0, 1, this.draftHour, this.draftMin) })
|
|
|
|
|
|
.useMilitaryTime(true)
|
|
|
|
|
|
.onChange((v: TimePickerResult): void => {
|
|
|
|
|
|
// ⚠️ `hour`/`minute` 是**数字**;存草稿数字,拼 `HH:MM` 时再补零
|
|
|
|
|
|
this.draftHour = v.hour;
|
|
|
|
|
|
this.draftMin = v.minute;
|
|
|
|
|
|
})
|
|
|
|
|
|
.textStyle({ color: FbColors.faint, font: { size: 14 } })
|
|
|
|
|
|
.selectedTextStyle({ color: FbColors.ink, font: { size: 16, weight: FontWeight.Medium } })
|
|
|
|
|
|
.disappearTextStyle({ color: FbColors.faint, font: { size: 14 } })
|
|
|
|
|
|
.layoutWeight(1)
|
|
|
|
|
|
.height(210)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
.width('100%')
|
|
|
|
|
|
|
|
|
|
|
|
Row({ space: 10 }) {
|
|
|
|
|
|
Text('取消')
|
|
|
|
|
|
.fontSize(FbMetric.fontSmall)
|
|
|
|
|
|
.fontColor(FbColors.muted)
|
|
|
|
|
|
.textAlign(TextAlign.Center)
|
|
|
|
|
|
.height(44)
|
|
|
|
|
|
.padding({ left: 18, right: 18 })
|
|
|
|
|
|
.backgroundColor(FbColors.inputBg)
|
|
|
|
|
|
.borderRadius(FbMetric.radiusS)
|
|
|
|
|
|
.onClick((): void => {
|
|
|
|
|
|
this.onCancel();
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
Text(this.allDay ? '确定日期' : '确定')
|
|
|
|
|
|
.fontSize(FbMetric.fontSmall)
|
|
|
|
|
|
.fontColor(Color.White)
|
|
|
|
|
|
.textAlign(TextAlign.Center)
|
|
|
|
|
|
.layoutWeight(1)
|
|
|
|
|
|
.height(44)
|
|
|
|
|
|
.backgroundColor(this.accentColor())
|
|
|
|
|
|
.borderRadius(FbMetric.radiusS)
|
|
|
|
|
|
.onClick((): void => {
|
|
|
|
|
|
this.onConfirm(fbIsoOf(this.draftDate),
|
|
|
|
|
|
fbPad2(this.draftHour) + ':' + fbPad2(this.draftMin));
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
.width('100%')
|
|
|
|
|
|
}
|
|
|
|
|
|
.width('100%')
|
|
|
|
|
|
.padding({ left: 18, right: 18, top: 8, bottom: 18 })
|
|
|
|
|
|
.alignItems(HorizontalAlign.Start)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
.width('92%')
|
|
|
|
|
|
}
|
|
|
|
|
|
.width('100%')
|
|
|
|
|
|
.height('100%')
|
|
|
|
|
|
.hitTestBehavior(HitTestMode.Transparent)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 环色/按钮底色(没传 accent 时回落到品牌色的 hex) */
|
|
|
|
|
|
private accentColor(): string {
|
|
|
|
|
|
// ⚠️ 用 `FbTint.brand`(hex)而不是 `FbColors.brand`:
|
|
|
|
|
|
// `FbColors` 那一套是 `Resource`,参与不了 `.backgroundColor(string)`;
|
|
|
|
|
|
// 本组件要与别的浮层行为一致(环色/按钮底色都是可运算的 hex 串)。
|
|
|
|
|
|
return this.accent !== '' ? this.accent : FbTint.brand;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|