用户 2026-10-23 原话:「日程,没有新建日程按钮,名册,也没有新增成员这个按钮。」
「首页上的日程,红色线不对,变短了左侧缺了一块。在日程模块中,那个红线左侧
是不是还有一个红点?如果有,去掉。」
① 首页日程区块加「+ 新建」(FbHomePage / Widgets.FbSectionTitle)
- FbSectionTitle 扩两个可选参数 addText / onAdd,带默认值 ⇒ 老调用点一行不改
- 按需懒拉 /calendar/books,缓存进 calBooks,同一会话只拉一次
- todayIso() 用本地年月日拼,**不用 toISOString**(UTC 会让早 8 点前算成昨天)
② 时间红线两端统一成「一根满宽线、无端点装饰、无行首文字」(FbDayTimeline)
- 老写法 `Row { Text(时间) width(34); 线; 圆点 }` ⇒ 行首 34vp 把线顶掉一块
(与网页端 .ag-now「双重缩进」同一类病),行尾 6x6 圆点就是用户问的红点
- 改成满高 Column + Blank 撑高 + 满宽 2px 线,与卡片端同一写法
③ 🔴 真机实测发现的两处可见性问题(FbEventEditSheet)
- 「加入哪本日历」原来排在「提醒」之后 ⇒ 表单高于卡片(maxHeight 86%),
它被推到滚动区下方,首屏只看到被底边切掉半行的标题(实测 bounds 高 7px)。
它是必选项(没选就「请选一本日历」存不上)⇒ 挪到「全天」之后、日期时间之前。
- 错误提示原来在滚动区**内部最底部** ⇒ 用户在顶部点「创建」时提示在几百 px
之下看不见;就算滚到底也被 Scroll 底边切半行。现象是「点了创建什么都没发生」。
⇒ 挪到滚动区外面、按钮区上方(实测:Scroll 底 1953,提示 1984~2024,按钮 2074)。
真机验收(设备 5NC0225B27004718,线上 family.yangyq.net):
- 静态守卫 1112/0;BUILD SUCCESSFUL;install bundle successfully
- 「+ 新建」bounds [868,931][1018,1045],点击真的弹出「新建日程」
- 诊断确认 books=15 canWrite=6(数据层完全正常,之前的「选不了日历本」是可见性问题)
- 日历本挪位后首屏可见:标题高 40px(原 7px),10 本 chip 从 838 排到 1358
- 错误提示挪位后不滚动即可见:命中「日程得有个标题」
- **端到端建卡成功**:线上库确认 id=2411 cal=5 20:00~21:00,首页 1 条→2 条;
测试数据已按 id 精确删除(remaining_test_rows=0),重启后恢复 1 条
493 lines
18 KiB
Plaintext
493 lines
18 KiB
Plaintext
/**
|
||
* 家书 FamilyBook · 日程「时间轴」视图(鸿蒙端)
|
||
*
|
||
* 🔴 用户 2026-10-16:「**没有时间轴展示**……你这不是说让你把 SyncCalendar
|
||
* 里面的功能搬过来吗?」
|
||
* ⇒ 照 SyncCalendar 的 `DayTimelineView`(在 `Index.ets` 里)移植:
|
||
* 左侧小时刻度 + 右侧**三层堆叠**渲染。
|
||
*
|
||
* 为什么是"三层堆叠"(照 SyncCalendar 的设计,别改):
|
||
* · 第 1 层 **整点网格**:每小时一格、格底一条灰线;
|
||
* · 第 2 层 **日程色块**:按冲突组分列、纵向用 `Blank` 定位;
|
||
* · 第 3 层 **当前时间红线**:浮在最上面。
|
||
* ⚠️ 三层尺寸与分格完全一致、上层透明 ⇒
|
||
* **色块不会被整点线或红线"切开"**,红线也永远压在最上面。
|
||
* 如果图省事把线直接画进日程层,一条日程就会被切成两段。
|
||
*
|
||
* 🔴 为什么是"组件"而不是 `@Builder`:
|
||
* `@Builder` 的**值类型参数不参与状态观察**(见 `FbTagChip.ets` 的说明、
|
||
* SDK 规则 `r/quick-rules/05-state.md`)—— 本项目的日程页正是因此
|
||
* 出现"点日历下面的日程不变"。这里用 `@ComponentV2` + `@Param`,
|
||
* `timeline` 一变整棵子树就重建。
|
||
*/
|
||
|
||
import { FbColors, FbMetric } from '../common/Theme';
|
||
import { FbCalEvent } from '../model/Types';
|
||
import {
|
||
FbTimelineData, FbTimeBlock, FbTimeGroup, FbTimeRow, FbTimelineUtil, FbViewRange
|
||
} from '../common/FbTimeline';
|
||
|
||
/** 「当前时间红线」的颜色(照 SyncCalendar:系统红) */
|
||
const FB_NOW_RED: string = '#FF3B30';
|
||
|
||
@ComponentV2
|
||
export struct FbDayTimeline {
|
||
/** 一天的布局(**必须由父组件按选中日期算好传进来**) */
|
||
@Param timeline: FbTimelineData = new FbTimelineData();
|
||
/** 是不是"今天"(决定要不要画红线) */
|
||
@Param isToday: boolean = false;
|
||
/** 当前时刻(当天第几分钟) */
|
||
@Param nowMin: number = 0;
|
||
/** 点一条日程 */
|
||
@Event onPick: (e: FbCalEvent) => void = (e: FbCalEvent): void => {
|
||
};
|
||
|
||
/** 1 小时的高度(vp) */
|
||
private hourUnit: number = FbTimelineUtil.HOUR_UNIT;
|
||
/** 极短色块的可读高度下限(一行"标题 + 时间") */
|
||
private minBlockVp: number = 16;
|
||
/** 能放下两行的高度门槛 */
|
||
private twoLineVp: number = 30;
|
||
/** 能放下三行的高度门槛 */
|
||
private threeLineVp: number = 46;
|
||
|
||
// ---- 视窗 ----
|
||
private range(): FbViewRange {
|
||
return FbTimelineUtil.viewRange(this.timeline.blocks);
|
||
}
|
||
|
||
private viewTop(): number {
|
||
return this.range().startHour / 24;
|
||
}
|
||
|
||
private viewBot(): number {
|
||
return this.range().endHour / 24;
|
||
}
|
||
|
||
/** 视窗总高(vp) */
|
||
private viewH(): number {
|
||
const r: FbViewRange = this.range();
|
||
return (r.endHour - r.startHour) * this.hourUnit;
|
||
}
|
||
|
||
/** 整日高度(vp) */
|
||
private totalH(): number {
|
||
return this.hourUnit * 24;
|
||
}
|
||
|
||
/** 视窗内的小时刻度 */
|
||
private hourLabels(): number[] {
|
||
const r: FbViewRange = this.range();
|
||
const out: number[] = [];
|
||
for (let h = r.startHour; h < r.endHour; h++) {
|
||
out.push(h);
|
||
}
|
||
return out;
|
||
}
|
||
|
||
private hourText(h: number): string {
|
||
return h < 10 ? ('0' + h.toString() + ':00') : (h.toString() + ':00');
|
||
}
|
||
|
||
/** 当前小时是否高亮(仅今天) */
|
||
private isNowHour(h: number): boolean {
|
||
return this.isToday && Math.floor(FbTimelineUtil.nowRatio(this.nowMin) * 24) === h;
|
||
}
|
||
|
||
/** 红线是否可见(今天 + 落在视窗内) */
|
||
private nowVisible(): boolean {
|
||
if (!this.isToday) {
|
||
return false;
|
||
}
|
||
const r: number = FbTimelineUtil.nowRatio(this.nowMin);
|
||
return r >= this.viewTop() - 0.0001 && r <= this.viewBot() + 0.0001;
|
||
}
|
||
|
||
/** 红线距层顶的偏移(vp) */
|
||
private nowOffsetVp(): number {
|
||
const y: number = (FbTimelineUtil.nowRatio(this.nowMin) - this.viewTop()) * this.totalH() - 1;
|
||
return y < 0 ? 0 : y;
|
||
}
|
||
|
||
// ---- 行序列(空隙 + 冲突组)----
|
||
|
||
private groups(): FbTimeGroup[] {
|
||
return FbTimelineUtil.groupsOf(this.timeline.blocks);
|
||
}
|
||
|
||
/**
|
||
* 纵向序列:只看视窗内的部分。
|
||
* **本层只有两样东西:空隙(Blank)与冲突组行** ——
|
||
* 整点线与红线交给另外两层,所以每条日程永远是完整连续的色块。
|
||
*/
|
||
private buildRows(): FbTimeRow[] {
|
||
const rows: FbTimeRow[] = [];
|
||
const gs: FbTimeGroup[] = this.groups();
|
||
const top: number = this.viewTop();
|
||
const bot: number = this.viewBot();
|
||
const dk: string = this.range().startHour.toString() + '_' + this.range().endHour.toString();
|
||
let cursor: number = top;
|
||
for (const g of gs) {
|
||
const gsx: number = g.startRatio > top ? g.startRatio : top;
|
||
const gex: number = g.endRatio < bot ? g.endRatio : bot;
|
||
if (gex - gsx <= 0.0002) {
|
||
continue; // 整组都在视窗外
|
||
}
|
||
this.pushRow(rows, cursor, gsx, null, dk);
|
||
this.pushRow(rows, gsx, gex, g, dk);
|
||
cursor = gex;
|
||
}
|
||
this.pushRow(rows, cursor, bot, null, dk);
|
||
return rows;
|
||
}
|
||
|
||
private pushRow(rows: FbTimeRow[], from: number, to: number,
|
||
g: FbTimeGroup | null, dk: string): void {
|
||
if (to - from <= 0.0002) {
|
||
return;
|
||
}
|
||
const r: FbTimeRow = new FbTimeRow();
|
||
r.topRatio = from;
|
||
r.botRatio = to;
|
||
if (g === null) {
|
||
r.kind = 0;
|
||
r.key = 'g_' + dk + '_' + rows.length.toString() + '_'
|
||
+ Math.round((to - from) * 100000).toString();
|
||
} else {
|
||
r.kind = 1;
|
||
r.group = g;
|
||
let sig: string = '';
|
||
for (const b of g.blocks) {
|
||
sig = sig + '_' + b.key;
|
||
}
|
||
r.key = 'b_' + dk + '_' + rows.length.toString() + sig;
|
||
}
|
||
rows.push(r);
|
||
}
|
||
|
||
/** 某一列中、与本行窗口有交集的色块 */
|
||
private laneBlocks(r: FbTimeRow, lane: FbTimeBlock[]): FbTimeBlock[] {
|
||
const out: FbTimeBlock[] = [];
|
||
for (const b of lane) {
|
||
const be: number = b.topRatio + b.heightRatio;
|
||
if (be > r.topRatio + 0.0001 && b.topRatio < r.botRatio - 0.0001) {
|
||
out.push(b);
|
||
}
|
||
}
|
||
return out;
|
||
}
|
||
|
||
/** 列内第 index 个色块之前要空出的高度(vp) */
|
||
private lanePadH(r: FbTimeRow, lane: FbTimeBlock[], index: number): number {
|
||
const arr: FbTimeBlock[] = this.laneBlocks(r, lane);
|
||
let prevEnd: number = r.topRatio;
|
||
if (index > 0) {
|
||
const p: FbTimeBlock = arr[index - 1];
|
||
const pe: number = p.topRatio + p.heightRatio;
|
||
prevEnd = pe > r.topRatio ? pe : r.topRatio;
|
||
}
|
||
const b: FbTimeBlock = arr[index];
|
||
const top: number = b.topRatio > r.topRatio ? b.topRatio : r.topRatio;
|
||
const d: number = (top - prevEnd) * this.totalH();
|
||
return d < 0 ? 0 : d;
|
||
}
|
||
|
||
/** 色块实际渲染高度(vp):极短的尽量垫到可读高度,但不越过同列下一块 */
|
||
private blockVp(r: FbTimeRow, lane: FbTimeBlock[], index: number): number {
|
||
const arr: FbTimeBlock[] = this.laneBlocks(r, lane);
|
||
const b: FbTimeBlock = arr[index];
|
||
const real: number = b.heightRatio * this.totalH();
|
||
if (real >= this.minBlockVp) {
|
||
return real;
|
||
}
|
||
let room: number = 0;
|
||
if (index + 1 < arr.length) {
|
||
room = this.lanePadH(r, lane, index + 1);
|
||
} else {
|
||
const tail: number = (r.botRatio - (b.topRatio + b.heightRatio)) * this.totalH();
|
||
room = tail > 0 ? tail : 0;
|
||
}
|
||
const h: number = real + room;
|
||
return h > this.minBlockVp ? this.minBlockVp : h;
|
||
}
|
||
|
||
/** 色块信息行数(按渲染高度判定) */
|
||
private blockLines(r: FbTimeRow, lane: FbTimeBlock[], index: number, b: FbTimeBlock): number {
|
||
const h: number = this.blockVp(r, lane, index);
|
||
if (h >= this.threeLineVp && b.place !== '') {
|
||
return 3;
|
||
}
|
||
if (h >= this.twoLineVp) {
|
||
return 2;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/** 两行色块的第二行:`09:00 - 10:30 · 地点` */
|
||
private blockMeta(b: FbTimeBlock): string {
|
||
if (b.place !== '') {
|
||
return b.timeText + ' · ' + b.place;
|
||
}
|
||
return b.timeText;
|
||
}
|
||
|
||
/** 色块 key 的内容签名(内容变了必须重建,否则 ArkUI 复用旧子组件) */
|
||
private blockSig(r: FbTimeRow, lane: FbTimeBlock[], index: number, b: FbTimeBlock): string {
|
||
return b.title.length.toString() + '_' + b.place.length.toString() + '_'
|
||
+ Math.round(this.blockVp(r, lane, index)).toString();
|
||
}
|
||
|
||
// ============================================================
|
||
// 三层
|
||
// ============================================================
|
||
|
||
/** 第 1 层:整点网格(每小时一格,格底一条灰线) */
|
||
@Builder
|
||
gridLayer() {
|
||
Column() {
|
||
ForEach(this.hourLabels(), (h: number) => {
|
||
Column()
|
||
.width('100%')
|
||
.height(this.hourUnit)
|
||
.border({ width: { bottom: 0.5 }, color: { bottom: FbColors.line2 } })
|
||
.hitTestBehavior(HitTestMode.None)
|
||
}, (h: number): string => 'gd_' + h.toString())
|
||
}
|
||
.width('100%')
|
||
.height('100%')
|
||
.hitTestBehavior(HitTestMode.None) // 装饰层:点击穿透到色块
|
||
}
|
||
|
||
/** 第 2 层:日程色块(冲突组分行 + 组内分列,纵向用 Blank 定位) */
|
||
@Builder
|
||
eventLayer() {
|
||
Column() {
|
||
ForEach(this.buildRows(), (r: FbTimeRow) => {
|
||
if (r.kind === 1 && r.group !== null) {
|
||
Row() {
|
||
ForEach(r.group!.lanes, (lane: FbTimeBlock[], li: number) => {
|
||
Column() {
|
||
ForEach(this.laneBlocks(r, lane), (b: FbTimeBlock, index: number) => {
|
||
Blank().height(this.lanePadH(r, lane, index))
|
||
Column({ space: 1 }) {
|
||
if (this.blockLines(r, lane, index, b) === 1) {
|
||
// 一行:标题 · 时间(地点放不下)
|
||
Row({ space: 4 }) {
|
||
Text(b.title)
|
||
.fontSize(10)
|
||
.fontWeight(FontWeight.Medium)
|
||
.fontColor(Color.White)
|
||
.maxLines(1)
|
||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||
.constraintSize({ maxWidth: '50%' })
|
||
Text(b.timeText)
|
||
.fontSize(9)
|
||
.fontColor('#E6FFFFFF')
|
||
.maxLines(1)
|
||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||
.constraintSize({ maxWidth: '55%' })
|
||
}
|
||
.width('100%')
|
||
.alignItems(VerticalAlign.Center)
|
||
} else {
|
||
// 第一行:标题
|
||
Text(b.title)
|
||
.fontSize(11)
|
||
.fontWeight(FontWeight.Medium)
|
||
.fontColor(Color.White)
|
||
.maxLines(1)
|
||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||
.width('100%')
|
||
if (this.blockLines(r, lane, index, b) >= 3) {
|
||
Text(b.timeText)
|
||
.fontSize(9)
|
||
.fontColor('#E6FFFFFF')
|
||
.maxLines(1)
|
||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||
.width('100%')
|
||
Text(b.place)
|
||
.fontSize(9)
|
||
.fontColor('#B3FFFFFF')
|
||
.maxLines(1)
|
||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||
.width('100%')
|
||
} else {
|
||
Text(this.blockMeta(b))
|
||
.fontSize(9)
|
||
.fontColor('#E6FFFFFF')
|
||
.maxLines(1)
|
||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||
.width('100%')
|
||
}
|
||
}
|
||
}
|
||
.alignItems(HorizontalAlign.Start)
|
||
.justifyContent(FlexAlign.Start)
|
||
.padding({ left: 6, right: 3, top: 2, bottom: 2 })
|
||
.borderRadius(6)
|
||
.backgroundColor(b.color)
|
||
.clip(true)
|
||
.width('100%')
|
||
.height(this.blockVp(r, lane, index))
|
||
.onClick((): void => {
|
||
if (b.ev !== null) {
|
||
this.onPick(b.ev);
|
||
}
|
||
})
|
||
}, (b: FbTimeBlock, index: number): string =>
|
||
'blk_' + r.key + '_' + b.key + '_' + this.blockSig(r, lane, index, b))
|
||
}
|
||
.layoutWeight(1)
|
||
.height('100%')
|
||
.padding({ right: 2 })
|
||
.clip(true)
|
||
}, (lane: FbTimeBlock[], li: number): string => 'ln_' + r.key + '_' + li.toString())
|
||
}
|
||
.width('100%')
|
||
.height((r.botRatio - r.topRatio) * this.totalH())
|
||
.alignItems(VerticalAlign.Top)
|
||
} else {
|
||
// 空隙:纯占位
|
||
Blank().height((r.botRatio - r.topRatio) * this.totalH())
|
||
}
|
||
}, (r: FbTimeRow): string => r.key)
|
||
}
|
||
.width('100%')
|
||
.height('100%')
|
||
}
|
||
|
||
/**
|
||
* 第 3 层:当前时间红线(浮在最上层)
|
||
*
|
||
* 🔴🔴 v84(用户 2026-10-23,同一条消息里两处):首页的这条线「红色线不对,
|
||
* 变短了左侧缺了一块」,日程页那条「红线左侧是不是还有一个红点?如果有,去掉。」
|
||
*
|
||
* 两个抱怨其实是同一段代码的两个缺陷,都在这里:
|
||
* ① 老写法是 `Row { Text('07:02') width(34); 线(layoutWeight 1); 圆点(6) }`
|
||
* —— **行首的 34vp 时间文字**把线往右顶掉一整块,用户看到的就是
|
||
* "左侧缺一块"(与网页端 `.ag-now` 那次「双重缩进」是同一类病:
|
||
* 线的左沿被别的东西占了)。
|
||
* ② 行尾那个 `6×6 borderRadius(3)` 就是用户问的**红点**。
|
||
*
|
||
* ⇒ 两端统一成**一根满宽的红线,没有任何端点装饰、没有文字**。
|
||
* 这正是用户 v44 定下的口径:「直接把红点去掉就行了,直接一根红线就可以了」,
|
||
* 也是网页端 `.ag-now { left: 0; right: 0; border-top: 2px }` 的等价写法。
|
||
*
|
||
* ⚠️ 别把时间文字"加回来":只要 Row 里有任何子元素,线就一定短一截。
|
||
* 想知道现在几点,看左侧刻度列——那里 `isNowHour` 的那个小时文字**已经是红的**
|
||
* (见 `hourLabels()` 那段的 `fontColor`),信息并没有丢。
|
||
* ⚠️ 别用 `Row { 线(layoutWeight 1) }` 的写法包一层 —— 能跑,但多一层无谓嵌套,
|
||
* 且下次有人往这个 Row 里塞东西,缺一块的 bug 就又回来了。
|
||
* ⚠️ `hitTestBehavior(HitTestMode.None)` 必须留:装饰层不能挡住点色块。
|
||
*/
|
||
@Builder
|
||
nowLayer() {
|
||
/* ⚠️ 外层必须是**满高的 Column**:`Stack` 的几个子层是「叠」不是「排」,
|
||
直接给线写 `.margin({top})` 在部分版本上会因对齐基准不同而漂移。
|
||
用 `Blank` 撑开 = 与卡片端 `FbCard4x4.nowLayer()` **同一个写法**,
|
||
两端行为一致,改一处就知道另一处该怎么改。 */
|
||
Column() {
|
||
if (this.nowVisible()) {
|
||
Blank().height(this.nowOffsetVp()).hitTestBehavior(HitTestMode.None)
|
||
Column()
|
||
.width('100%')
|
||
.height(2)
|
||
.borderRadius(1)
|
||
.backgroundColor(FB_NOW_RED)
|
||
}
|
||
}
|
||
.width('100%')
|
||
.height('100%')
|
||
.hitTestBehavior(HitTestMode.None)
|
||
}
|
||
|
||
build() {
|
||
Column({ space: 0 }) {
|
||
// ---- 全天 / 跨天:画在 0 点之前,用色块展示 ----
|
||
if (this.timeline.allDay.length > 0) {
|
||
Column({ space: 4 }) {
|
||
ForEach(this.timeline.allDay, (e: FbCalEvent) => {
|
||
Row({ space: 6 }) {
|
||
Text('全天')
|
||
.fontSize(9)
|
||
.fontColor(Color.White)
|
||
.backgroundColor('#26000000')
|
||
.borderRadius(6)
|
||
.padding({ left: 5, right: 5, top: 1, bottom: 1 })
|
||
Text(e.title !== '' ? e.title
|
||
: (e.calName !== '' ? e.calName : '未命名日程'))
|
||
.fontSize(12)
|
||
.fontWeight(FontWeight.Medium)
|
||
.fontColor(Color.White)
|
||
.maxLines(1)
|
||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||
.layoutWeight(1)
|
||
if (e.place !== '') {
|
||
Text(e.place)
|
||
.fontSize(10)
|
||
.fontColor('#D9FFFFFF')
|
||
.maxLines(1)
|
||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||
.constraintSize({ maxWidth: 110 })
|
||
}
|
||
}
|
||
.alignItems(VerticalAlign.Center)
|
||
.width('100%')
|
||
.height(26)
|
||
.padding({ left: 8, right: 8 })
|
||
.borderRadius(8)
|
||
.backgroundColor(e.color)
|
||
.onClick((): void => {
|
||
this.onPick(e);
|
||
})
|
||
}, (e: FbCalEvent): string => 'al_' + FbTimelineUtil.keyOf(e))
|
||
}
|
||
.width('100%')
|
||
.padding({ bottom: 6 })
|
||
}
|
||
|
||
// ---- 时间轴主体 ----
|
||
Row() {
|
||
// 左侧小时刻度
|
||
Column() {
|
||
ForEach(this.hourLabels(), (h: number) => {
|
||
Text(this.hourText(h))
|
||
.fontSize(10)
|
||
.fontColor(this.isNowHour(h) ? FB_NOW_RED : FbColors.faint)
|
||
.width(36)
|
||
.height(this.hourUnit)
|
||
.textAlign(TextAlign.End)
|
||
.padding({ right: 4, top: 2 })
|
||
.border({ width: { bottom: 0.5 }, color: { bottom: FbColors.line2 } })
|
||
}, (h: number): string => 'h' + h.toString())
|
||
}
|
||
.width(36)
|
||
|
||
// 右侧内容区:三层堆叠
|
||
Stack() {
|
||
this.gridLayer()
|
||
this.eventLayer()
|
||
this.nowLayer()
|
||
}
|
||
.layoutWeight(1)
|
||
.height(this.viewH())
|
||
.alignContent(Alignment.TopStart)
|
||
.border({ width: { left: 0.5 }, color: { left: FbColors.line2 } })
|
||
}
|
||
.width('100%')
|
||
.alignItems(VerticalAlign.Top)
|
||
|
||
// ---- 当天没有带时间的日程 ----
|
||
if (!this.timeline.hasTimed && this.timeline.allDay.length === 0) {
|
||
Text('这天没有安排')
|
||
.fontSize(FbMetric.fontSmall)
|
||
.fontColor(FbColors.faint)
|
||
.width('100%')
|
||
.textAlign(TextAlign.Center)
|
||
.padding({ top: 20, bottom: 20 })
|
||
}
|
||
}
|
||
.width('100%')
|
||
.alignItems(HorizontalAlign.Start)
|
||
}
|
||
} |