为日程列表增加了一条当前时间的横线,用来表示当前进度。
Signed-off-by: Yang Yongquan <i@yangyq.net>
This commit is contained in:
@@ -17,6 +17,13 @@ export class CardItem {
|
|||||||
showDate: boolean = false; // 是否为当天分组的第一条(卡片上渲染日期头)
|
showDate: boolean = false; // 是否为当天分组的第一条(卡片上渲染日期头)
|
||||||
calName: string = ''; // 所属日历本名(右侧显示,颜色同日历色)
|
calName: string = ''; // 所属日历本名(右侧显示,颜色同日历色)
|
||||||
color: string = '#007DFF';
|
color: string = '#007DFF';
|
||||||
|
// 当前时间红线:start/end 为日程实际起止毫秒(用于定位"现在"位置);
|
||||||
|
// showNowLine 表示该日程上方应绘制红线,isNow 表示该日程正在进行中
|
||||||
|
startMs: number = 0;
|
||||||
|
endMs: number = 0;
|
||||||
|
showNowLine: boolean = false; // 红线画在该日程上方
|
||||||
|
isNow: boolean = false; // 该日程正在进行中
|
||||||
|
nowLineBelow: boolean = false; // 今天所有有时间日程已结束:红线画在最后一条下方
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 卡片整体数据 */
|
/** 卡片整体数据 */
|
||||||
@@ -28,6 +35,7 @@ export class CardData {
|
|||||||
dateMd: string = '';
|
dateMd: string = '';
|
||||||
weekday: string = '';
|
weekday: string = '';
|
||||||
todayCount: number = 0;
|
todayCount: number = 0;
|
||||||
|
ongoingCount: number = 0; // 此刻正在进行的日程条数(2x2 卡片红点提示用)
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CardDataService {
|
export class CardDataService {
|
||||||
@@ -192,9 +200,47 @@ export class CardDataService {
|
|||||||
item.showDate = i === 0; // 当天分组第一条 → 卡片上显示日期头
|
item.showDate = i === 0; // 当天分组第一条 → 卡片上显示日期头
|
||||||
item.calName = e.calName;
|
item.calName = e.calName;
|
||||||
item.color = e.color;
|
item.color = e.color;
|
||||||
|
// 红线定位:记录实际起止毫秒(用于卡片渲染时判断"现在"位置)
|
||||||
|
item.startMs = e.startTime;
|
||||||
|
item.endMs = e.endTime;
|
||||||
items.push(item);
|
items.push(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 红线逻辑:仅对"今天"分组、且为"有时间"的日程生效(全天/跨天按全天展示,不参与时间轴定位)
|
||||||
|
const nowMs: number = Date.now();
|
||||||
|
let linePlaced: boolean = false;
|
||||||
|
// 1) 正在进行的日程(start<=now<end)→ 红线画在其上方,并标记 isNow
|
||||||
|
for (const it of items) {
|
||||||
|
if (it.date === '今天' && it.time !== '全天' && it.startMs <= nowMs && nowMs < it.endMs) {
|
||||||
|
it.isNow = true;
|
||||||
|
it.showNowLine = true;
|
||||||
|
linePlaced = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 2) 否则:红线画在"下一个尚未开始"的有时间日程上方
|
||||||
|
if (!linePlaced) {
|
||||||
|
for (const it of items) {
|
||||||
|
if (it.date === '今天' && it.time !== '全天' && it.startMs > nowMs) {
|
||||||
|
it.showNowLine = true;
|
||||||
|
linePlaced = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 3) 否则:今天所有有时间日程均已结束 → 红线画在最后一条下方(标记"今日已结束")
|
||||||
|
if (!linePlaced) {
|
||||||
|
for (let k = items.length - 1; k >= 0; k--) {
|
||||||
|
const it = items[k];
|
||||||
|
if (it.date === '今天' && it.time !== '全天') {
|
||||||
|
it.nowLineBelow = true;
|
||||||
|
linePlaced = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 进行中条数:用于 2x2 卡片红点提示
|
||||||
|
data.ongoingCount = items.filter((i: CardItem): boolean => i.isNow).length;
|
||||||
data.eventsJson = JSON.stringify(items);
|
data.eventsJson = JSON.stringify(items);
|
||||||
// 今日日程条数:item.date === '今天' 的均为今天分组(含全天/跨天进行中)
|
// 今日日程条数:item.date === '今天' 的均为今天分组(含全天/跨天进行中)
|
||||||
data.todayCount = items.filter((i: CardItem): boolean => i.date === '今天').length;
|
data.todayCount = items.filter((i: CardItem): boolean => i.date === '今天').length;
|
||||||
|
|||||||
@@ -54,12 +54,14 @@ struct Index {
|
|||||||
private swiperController: SwiperController = new SwiperController();
|
private swiperController: SwiperController = new SwiperController();
|
||||||
private swiperGuard: boolean = false;
|
private swiperGuard: boolean = false;
|
||||||
private autoSyncTimer: number = -1;
|
private autoSyncTimer: number = -1;
|
||||||
|
private nowTimer: number = -1; // 红线位置"播放"计时器(每 30s 刷新 nowMs)
|
||||||
private lastSyncTime: number = 0;
|
private lastSyncTime: number = 0;
|
||||||
private permissionAsked: boolean = false;
|
private permissionAsked: boolean = false;
|
||||||
@State detailShow: boolean = false; // 只读日程详情半屏弹层
|
@State detailShow: boolean = false; // 只读日程详情半屏弹层
|
||||||
@State detailEvent: DisplayEvent | null = null;
|
@State detailEvent: DisplayEvent | null = null;
|
||||||
// 服务卡片"添加"按钮深链:EntryAbility 写入,本页读取后拉起新建日程页
|
// 服务卡片"添加"按钮深链:EntryAbility 写入,本页读取后拉起新建日程页
|
||||||
@Watch('onWidgetActionChange') @StorageLink('widgetAction') widgetAction: string = '';
|
@Watch('onWidgetActionChange') @StorageLink('widgetAction') widgetAction: string = '';
|
||||||
|
@State nowMs: number = Date.now(); // 当前时刻毫秒(驱动红线"播放"位置,每 30s 更新)
|
||||||
|
|
||||||
aboutToAppear(): void {
|
aboutToAppear(): void {
|
||||||
const ctx = this.getUIContext().getHostContext();
|
const ctx = this.getUIContext().getHostContext();
|
||||||
@@ -74,6 +76,10 @@ struct Index {
|
|||||||
this.rebuildPages();
|
this.rebuildPages();
|
||||||
this.initLandscapeListener();
|
this.initLandscapeListener();
|
||||||
this.initPermissionAndLoad();
|
this.initPermissionAndLoad();
|
||||||
|
// 红线"播放"效果:每 30s 更新 nowMs,让当前时间红线随真实时间推进
|
||||||
|
this.nowTimer = setInterval((): void => {
|
||||||
|
this.nowMs = Date.now();
|
||||||
|
}, 30000);
|
||||||
}
|
}
|
||||||
|
|
||||||
aboutToDisappear(): void {
|
aboutToDisappear(): void {
|
||||||
@@ -81,6 +87,10 @@ struct Index {
|
|||||||
clearInterval(this.autoSyncTimer);
|
clearInterval(this.autoSyncTimer);
|
||||||
this.autoSyncTimer = -1;
|
this.autoSyncTimer = -1;
|
||||||
}
|
}
|
||||||
|
if (this.nowTimer !== -1) {
|
||||||
|
clearInterval(this.nowTimer);
|
||||||
|
this.nowTimer = -1;
|
||||||
|
}
|
||||||
if (this.landscapeListener !== null) {
|
if (this.landscapeListener !== null) {
|
||||||
this.landscapeListener.off('change');
|
this.landscapeListener.off('change');
|
||||||
this.landscapeListener = null;
|
this.landscapeListener = null;
|
||||||
@@ -279,6 +289,50 @@ struct Index {
|
|||||||
this.startOfDay(e.startTime) <= dateMs && e.endTime >= dateMs);
|
this.startOfDay(e.startTime) <= dateMs && e.endTime >= dateMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 红线日程唯一 key(与 ForEach key 保持一致) */
|
||||||
|
private nowLineKey(e: DisplayEvent): string {
|
||||||
|
return `${e.isSystem ? 's' : 'l'}${e.id}_${e.startTime}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 计算某日列表中"当前时间红线"位置:返回应画在上方/下方的日程 key,及正在进行的日程 key 集合。
|
||||||
|
* 仅 isToday 为 true 时生效(红线表达的是"此刻",非今天无意义)。 */
|
||||||
|
private computeNowLine(list: DisplayEvent[], isToday: boolean): NowLineInfo {
|
||||||
|
const res: NowLineInfo = new NowLineInfo();
|
||||||
|
if (!isToday) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
const now: number = this.nowMs;
|
||||||
|
const timed: DisplayEvent[] = list
|
||||||
|
.filter((e: DisplayEvent): boolean => !e.isAllDay && !this.spansDays(e))
|
||||||
|
.sort((a: DisplayEvent, b: DisplayEvent): number => a.startTime - b.startTime);
|
||||||
|
// 1) 正在进行的(start<=now<end)→ 红线画在其上方
|
||||||
|
let firstNow: DisplayEvent | undefined = undefined;
|
||||||
|
for (const e of timed) {
|
||||||
|
if (e.startTime <= now && now < e.endTime) {
|
||||||
|
res.nowKeys.add(this.nowLineKey(e));
|
||||||
|
if (firstNow === undefined) {
|
||||||
|
firstNow = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (firstNow !== undefined) {
|
||||||
|
res.aboveKey = this.nowLineKey(firstNow);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
// 2) 下一个尚未开始的 → 红线画在其上方
|
||||||
|
for (const e of timed) {
|
||||||
|
if (e.startTime > now) {
|
||||||
|
res.aboveKey = this.nowLineKey(e);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 3) 全部已结束 → 红线画在最后一条下方
|
||||||
|
if (timed.length > 0) {
|
||||||
|
res.belowKey = this.nowLineKey(timed[timed.length - 1]);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
private switchMonth(delta: number): void {
|
private switchMonth(delta: number): void {
|
||||||
const nm = this.addMonths(this.displayYear, this.displayMonth, delta);
|
const nm = this.addMonths(this.displayYear, this.displayMonth, delta);
|
||||||
this.displayYear = nm[0];
|
this.displayYear = nm[0];
|
||||||
@@ -1526,7 +1580,19 @@ struct Index {
|
|||||||
Scroll() {
|
Scroll() {
|
||||||
Column({ space: 8 }) {
|
Column({ space: 8 }) {
|
||||||
ForEach(this.eventsOfDate(this.selectedDate), (e: DisplayEvent) => {
|
ForEach(this.eventsOfDate(this.selectedDate), (e: DisplayEvent) => {
|
||||||
this.eventRow(e)
|
Column() {
|
||||||
|
if (this.computeNowLine(this.eventsOfDate(this.selectedDate),
|
||||||
|
this.selectedDate === this.startOfDay(this.nowMs)).aboveKey === this.nowLineKey(e)) {
|
||||||
|
NowLineMarker()
|
||||||
|
}
|
||||||
|
this.eventRow(e, this.computeNowLine(this.eventsOfDate(this.selectedDate),
|
||||||
|
this.selectedDate === this.startOfDay(this.nowMs)).nowKeys.has(this.nowLineKey(e)))
|
||||||
|
if (this.computeNowLine(this.eventsOfDate(this.selectedDate),
|
||||||
|
this.selectedDate === this.startOfDay(this.nowMs)).belowKey === this.nowLineKey(e)) {
|
||||||
|
NowLineMarker()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.width('100%')
|
||||||
}, (e: DisplayEvent) => `${e.isSystem ? 's' : 'l'}${e.id}_${e.startTime}`)
|
}, (e: DisplayEvent) => `${e.isSystem ? 's' : 'l'}${e.id}_${e.startTime}`)
|
||||||
if (this.eventsOfDate(this.selectedDate).length === 0 && !this.loading) {
|
if (this.eventsOfDate(this.selectedDate).length === 0 && !this.loading) {
|
||||||
Text('当天没有日程')
|
Text('当天没有日程')
|
||||||
@@ -1577,7 +1643,17 @@ struct Index {
|
|||||||
}
|
}
|
||||||
ForEach(group.items, (e: DisplayEvent) => {
|
ForEach(group.items, (e: DisplayEvent) => {
|
||||||
ListItem() {
|
ListItem() {
|
||||||
this.eventRow(e)
|
Column() {
|
||||||
|
if (this.computeNowLine(group.items, group.label === '今天').aboveKey === this.nowLineKey(e)) {
|
||||||
|
NowLineMarker()
|
||||||
|
}
|
||||||
|
this.eventRow(e, this.computeNowLine(group.items, group.label === '今天')
|
||||||
|
.nowKeys.has(this.nowLineKey(e)))
|
||||||
|
if (this.computeNowLine(group.items, group.label === '今天').belowKey === this.nowLineKey(e)) {
|
||||||
|
NowLineMarker()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.width('100%')
|
||||||
}
|
}
|
||||||
}, (e: DisplayEvent) => `${e.isSystem ? 's' : 'l'}${e.id}_${e.startTime}_${e.title}`)
|
}, (e: DisplayEvent) => `${e.isSystem ? 's' : 'l'}${e.id}_${e.startTime}_${e.title}`)
|
||||||
}, (group: AgendaGroup) => group.label)
|
}, (group: AgendaGroup) => group.label)
|
||||||
@@ -1744,24 +1820,24 @@ struct Index {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Builder
|
@Builder
|
||||||
eventRow(e: DisplayEvent) {
|
eventRow(e: DisplayEvent, isNow: boolean = false) {
|
||||||
Row({ space: 10 }) {
|
Row({ space: 10 }) {
|
||||||
Column()
|
Column()
|
||||||
.width(4)
|
.width(4)
|
||||||
.height(38)
|
.height(38)
|
||||||
.borderRadius(2)
|
.borderRadius(2)
|
||||||
.backgroundColor(e.color)
|
.backgroundColor(isNow ? '#FF3B30' : e.color)
|
||||||
Column({ space: 3 }) {
|
Column({ space: 3 }) {
|
||||||
Text(e.title === '' ? '(无标题)' : e.title)
|
Text(e.title === '' ? '(无标题)' : e.title)
|
||||||
.fontSize(15)
|
.fontSize(15)
|
||||||
.fontColor($r('app.color.text_primary'))
|
.fontColor(isNow ? '#FF3B30' : $r('app.color.text_primary'))
|
||||||
.maxLines(1)
|
.maxLines(1)
|
||||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||||
Row({ space: 6 }) {
|
Row({ space: 6 }) {
|
||||||
Text(e.isAllDay || this.spansDays(e)
|
Text(e.isAllDay || this.spansDays(e)
|
||||||
? '全天' : `${this.fmtTime(e.startTime)} - ${this.fmtTime(e.endTime)}`)
|
? '全天' : `${this.fmtTime(e.startTime)} - ${this.fmtTime(e.endTime)}`)
|
||||||
.fontSize(12)
|
.fontSize(12)
|
||||||
.fontColor($r('app.color.text_secondary'))
|
.fontColor(isNow ? '#FF3B30' : $r('app.color.text_secondary'))
|
||||||
if (e.recurring) {
|
if (e.recurring) {
|
||||||
Text('↻ 重复')
|
Text('↻ 重复')
|
||||||
.fontSize(10)
|
.fontSize(10)
|
||||||
@@ -1770,6 +1846,14 @@ struct Index {
|
|||||||
.borderRadius(4)
|
.borderRadius(4)
|
||||||
.backgroundColor($r('app.color.chip_off_bg'))
|
.backgroundColor($r('app.color.chip_off_bg'))
|
||||||
}
|
}
|
||||||
|
if (isNow) {
|
||||||
|
Text('● 进行中')
|
||||||
|
.fontSize(10)
|
||||||
|
.fontColor('#FF3B30')
|
||||||
|
.padding({ left: 4, right: 4, top: 1, bottom: 1 })
|
||||||
|
.borderRadius(4)
|
||||||
|
.backgroundColor('#FFECEA')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (e.location !== '') {
|
if (e.location !== '') {
|
||||||
Row({ space: 4 }) {
|
Row({ space: 4 }) {
|
||||||
@@ -1873,3 +1957,51 @@ class AgendaGroup {
|
|||||||
label: string = '';
|
label: string = '';
|
||||||
items: DisplayEvent[] = [];
|
items: DisplayEvent[] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 某日列表中"当前时间红线"位置信息(避免内联对象字面量类型,ArkTS 不允许) */
|
||||||
|
class NowLineInfo {
|
||||||
|
aboveKey: string = ''; // 红线画在该 key 对应日程的上方
|
||||||
|
belowKey: string = ''; // 今天所有有时间日程已结束:红线画在该 key 对应日程的下方
|
||||||
|
nowKeys: Set<string> = new Set<string>(); // 正在进行的日程 key 集合
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 当前时间红线标记(带呼吸/播放效果):左侧红点 + 贯穿整行的红色细线。
|
||||||
|
* 自带动画(opacity 往复),不依赖外部状态刷新,避免整页重绘。 */
|
||||||
|
@Component
|
||||||
|
struct NowLineMarker {
|
||||||
|
@State private dim: boolean = false;
|
||||||
|
private timer: number = -1;
|
||||||
|
|
||||||
|
aboutToAppear(): void {
|
||||||
|
// 每 ~1.1s 切换一次,配合 .animation 形成呼吸式"播放"效果
|
||||||
|
this.timer = setInterval((): void => {
|
||||||
|
this.dim = !this.dim;
|
||||||
|
}, 1100);
|
||||||
|
}
|
||||||
|
|
||||||
|
aboutToDisappear(): void {
|
||||||
|
if (this.timer !== -1) {
|
||||||
|
clearInterval(this.timer);
|
||||||
|
this.timer = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
Row() {
|
||||||
|
Column()
|
||||||
|
.width(6)
|
||||||
|
.height(6)
|
||||||
|
.borderRadius(3)
|
||||||
|
.backgroundColor('#FF3B30')
|
||||||
|
Column()
|
||||||
|
.height(2)
|
||||||
|
.layoutWeight(1)
|
||||||
|
.backgroundColor('#FF3B30')
|
||||||
|
.borderRadius(1)
|
||||||
|
}
|
||||||
|
.width('100%')
|
||||||
|
.padding({ top: 3, bottom: 3 })
|
||||||
|
.opacity(this.dim ? 0.45 : 1)
|
||||||
|
.animation({ duration: 1000 })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ struct Widget2x2Card {
|
|||||||
@LocalStorageProp('dateMd') dateMd: string = '';
|
@LocalStorageProp('dateMd') dateMd: string = '';
|
||||||
@LocalStorageProp('weekday') weekday: string = '';
|
@LocalStorageProp('weekday') weekday: string = '';
|
||||||
@LocalStorageProp('todayCount') todayCount: number = 0;
|
@LocalStorageProp('todayCount') todayCount: number = 0;
|
||||||
|
@LocalStorageProp('ongoingCount') ongoingCount: number = 0; // 此刻正在进行的日程条数
|
||||||
|
|
||||||
/** 右上角添加按钮:拉起 App 直接进入新建日程页(阻止冒泡,避免同时打开 App 首页) */
|
/** 右上角添加按钮:拉起 App 直接进入新建日程页(阻止冒泡,避免同时打开 App 首页) */
|
||||||
@Builder
|
@Builder
|
||||||
@@ -78,9 +79,18 @@ struct Widget2x2Card {
|
|||||||
.fontWeight(FontWeight.Bold)
|
.fontWeight(FontWeight.Bold)
|
||||||
.fontColor('#007DFF')
|
.fontColor('#007DFF')
|
||||||
Column({ space: 2 }) {
|
Column({ space: 2 }) {
|
||||||
Text('今日日程')
|
Row({ space: 4 }) {
|
||||||
.fontSize(12)
|
if (this.ongoingCount > 0) {
|
||||||
.fontColor('#1A1A1A')
|
Column()
|
||||||
|
.width(8)
|
||||||
|
.height(8)
|
||||||
|
.borderRadius(4)
|
||||||
|
.backgroundColor('#FF3B30')
|
||||||
|
}
|
||||||
|
Text(this.ongoingCount > 0 ? `进行中 ${this.ongoingCount}` : '今日日程')
|
||||||
|
.fontSize(12)
|
||||||
|
.fontColor(this.ongoingCount > 0 ? '#FF3B30' : '#1A1A1A')
|
||||||
|
}
|
||||||
Text(this.todayCount === 0 ? '点击添加' : '点击查看')
|
Text(this.todayCount === 0 ? '点击添加' : '点击查看')
|
||||||
.fontSize(10)
|
.fontSize(10)
|
||||||
.fontColor('#8A8A8A')
|
.fontColor('#8A8A8A')
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ class CardItem2x4 {
|
|||||||
showDate: boolean = false;
|
showDate: boolean = false;
|
||||||
calName: string = '';
|
calName: string = '';
|
||||||
color: string = '#007DFF';
|
color: string = '#007DFF';
|
||||||
|
// 红线:startMs/endMs 实际起止;showNowLine 上方画红线;isNow 正在进行;nowLineBelow 画在底部
|
||||||
|
startMs: number = 0;
|
||||||
|
endMs: number = 0;
|
||||||
|
showNowLine: boolean = false;
|
||||||
|
isNow: boolean = false;
|
||||||
|
nowLineBelow: boolean = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Entry(storage2x4)
|
@Entry(storage2x4)
|
||||||
@@ -88,6 +94,25 @@ struct Widget4x2Card {
|
|||||||
.backgroundColor('#F5F7FA')
|
.backgroundColor('#F5F7FA')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 当前时间红线标记:左侧红点 + 贯穿整行的红色细线 */
|
||||||
|
@Builder
|
||||||
|
nowLine() {
|
||||||
|
Row() {
|
||||||
|
Column()
|
||||||
|
.width(6)
|
||||||
|
.height(6)
|
||||||
|
.borderRadius(3)
|
||||||
|
.backgroundColor('#FF3B30')
|
||||||
|
Column()
|
||||||
|
.height(2)
|
||||||
|
.layoutWeight(1)
|
||||||
|
.backgroundColor('#FF3B30')
|
||||||
|
.borderRadius(1)
|
||||||
|
}
|
||||||
|
.width('100%')
|
||||||
|
.padding({ top: 3, bottom: 3 })
|
||||||
|
}
|
||||||
|
|
||||||
/** 有时间事件行(时间轴):圆角矩形 + 左色竖条 + 开始时间(上)-竖线-结束时间(下) + 标题 */
|
/** 有时间事件行(时间轴):圆角矩形 + 左色竖条 + 开始时间(上)-竖线-结束时间(下) + 标题 */
|
||||||
@Builder
|
@Builder
|
||||||
buildTimedRow(item: CardItem2x4) {
|
buildTimedRow(item: CardItem2x4) {
|
||||||
@@ -96,13 +121,13 @@ struct Widget4x2Card {
|
|||||||
.width(3)
|
.width(3)
|
||||||
.height(36)
|
.height(36)
|
||||||
.borderRadius(2)
|
.borderRadius(2)
|
||||||
.backgroundColor(item.color)
|
.backgroundColor(item.isNow ? '#FF3B30' : item.color)
|
||||||
// 时间列:开始时间在上、结束时间在下、中间竖线连接
|
// 时间列:开始时间在上、结束时间在下、中间竖线连接
|
||||||
Column({ space: 2 }) {
|
Column({ space: 2 }) {
|
||||||
Text(item.time)
|
Text(item.time)
|
||||||
.fontSize(10)
|
.fontSize(10)
|
||||||
.fontWeight(FontWeight.Medium)
|
.fontWeight(FontWeight.Medium)
|
||||||
.fontColor('#333333')
|
.fontColor(item.isNow ? '#FF3B30' : '#333333')
|
||||||
Column()
|
Column()
|
||||||
.width(1.5)
|
.width(1.5)
|
||||||
.layoutWeight(1)
|
.layoutWeight(1)
|
||||||
@@ -117,10 +142,18 @@ struct Widget4x2Card {
|
|||||||
.height(36)
|
.height(36)
|
||||||
Text(item.title)
|
Text(item.title)
|
||||||
.fontSize(12)
|
.fontSize(12)
|
||||||
.fontColor('#1A1A1A')
|
.fontColor(item.isNow ? '#FF3B30' : '#1A1A1A')
|
||||||
.maxLines(2)
|
.maxLines(2)
|
||||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||||
.layoutWeight(1)
|
.layoutWeight(1)
|
||||||
|
if (item.isNow) {
|
||||||
|
Text('● 进行中')
|
||||||
|
.fontSize(9)
|
||||||
|
.fontColor('#FF3B30')
|
||||||
|
.padding({ left: 4, right: 4, top: 1, bottom: 1 })
|
||||||
|
.borderRadius(4)
|
||||||
|
.backgroundColor('#FFECEA')
|
||||||
|
}
|
||||||
if (item.calName !== '') {
|
if (item.calName !== '') {
|
||||||
Text(item.calName)
|
Text(item.calName)
|
||||||
.fontSize(9)
|
.fontSize(9)
|
||||||
@@ -134,7 +167,7 @@ struct Widget4x2Card {
|
|||||||
.width('100%')
|
.width('100%')
|
||||||
.padding({ left: 8, right: 8, top: 4, bottom: 4 })
|
.padding({ left: 8, right: 8, top: 4, bottom: 4 })
|
||||||
.borderRadius(8)
|
.borderRadius(8)
|
||||||
.backgroundColor('#F5F7FA')
|
.backgroundColor(item.isNow ? '#FFF1F0' : '#F5F7FA')
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 卡片"打开 App":点击日期区或日程列表触发;添加按钮是 header 行的兄弟节点,其点击不会冒泡到这里 */
|
/** 卡片"打开 App":点击日期区或日程列表触发;添加按钮是 header 行的兄弟节点,其点击不会冒泡到这里 */
|
||||||
@@ -190,11 +223,17 @@ struct Widget4x2Card {
|
|||||||
.fontColor('#666666')
|
.fontColor('#666666')
|
||||||
.width('100%')
|
.width('100%')
|
||||||
}
|
}
|
||||||
|
if (item.showNowLine && !item.nowLineBelow) {
|
||||||
|
this.nowLine()
|
||||||
|
}
|
||||||
if (item.time === '全天') {
|
if (item.time === '全天') {
|
||||||
this.buildAllDayRow(item)
|
this.buildAllDayRow(item)
|
||||||
} else {
|
} else {
|
||||||
this.buildTimedRow(item)
|
this.buildTimedRow(item)
|
||||||
}
|
}
|
||||||
|
if (item.nowLineBelow) {
|
||||||
|
this.nowLine()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.width('100%')
|
.width('100%')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ class CardItem4x4 {
|
|||||||
showDate: boolean = false;
|
showDate: boolean = false;
|
||||||
calName: string = '';
|
calName: string = '';
|
||||||
color: string = '#007DFF';
|
color: string = '#007DFF';
|
||||||
|
// 红线:startMs/endMs 实际起止;showNowLine 上方画红线;isNow 正在进行;nowLineBelow 画在底部
|
||||||
|
startMs: number = 0;
|
||||||
|
endMs: number = 0;
|
||||||
|
showNowLine: boolean = false;
|
||||||
|
isNow: boolean = false;
|
||||||
|
nowLineBelow: boolean = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Entry(storage4x4)
|
@Entry(storage4x4)
|
||||||
@@ -87,6 +93,25 @@ struct Widget4x4Card {
|
|||||||
.backgroundColor('#F5F7FA')
|
.backgroundColor('#F5F7FA')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 当前时间红线标记:左侧红点 + 贯穿整行的红色细线 */
|
||||||
|
@Builder
|
||||||
|
nowLine() {
|
||||||
|
Row() {
|
||||||
|
Column()
|
||||||
|
.width(6)
|
||||||
|
.height(6)
|
||||||
|
.borderRadius(3)
|
||||||
|
.backgroundColor('#FF3B30')
|
||||||
|
Column()
|
||||||
|
.height(2)
|
||||||
|
.layoutWeight(1)
|
||||||
|
.backgroundColor('#FF3B30')
|
||||||
|
.borderRadius(1)
|
||||||
|
}
|
||||||
|
.width('100%')
|
||||||
|
.padding({ top: 3, bottom: 3 })
|
||||||
|
}
|
||||||
|
|
||||||
/** 有时间事件行(时间轴):圆角矩形 + 左色竖条 + 开始时间(上)-竖线-结束时间(下) + 标题 */
|
/** 有时间事件行(时间轴):圆角矩形 + 左色竖条 + 开始时间(上)-竖线-结束时间(下) + 标题 */
|
||||||
@Builder
|
@Builder
|
||||||
buildTimedRow(item: CardItem4x4) {
|
buildTimedRow(item: CardItem4x4) {
|
||||||
@@ -95,13 +120,13 @@ struct Widget4x4Card {
|
|||||||
.width(3)
|
.width(3)
|
||||||
.height(38)
|
.height(38)
|
||||||
.borderRadius(2)
|
.borderRadius(2)
|
||||||
.backgroundColor(item.color)
|
.backgroundColor(item.isNow ? '#FF3B30' : item.color)
|
||||||
// 时间列:开始时间在上、结束时间在下、中间竖线连接
|
// 时间列:开始时间在上、结束时间在下、中间竖线连接
|
||||||
Column({ space: 2 }) {
|
Column({ space: 2 }) {
|
||||||
Text(item.time)
|
Text(item.time)
|
||||||
.fontSize(10)
|
.fontSize(10)
|
||||||
.fontWeight(FontWeight.Medium)
|
.fontWeight(FontWeight.Medium)
|
||||||
.fontColor('#333333')
|
.fontColor(item.isNow ? '#FF3B30' : '#333333')
|
||||||
Column()
|
Column()
|
||||||
.width(1.5)
|
.width(1.5)
|
||||||
.layoutWeight(1)
|
.layoutWeight(1)
|
||||||
@@ -116,10 +141,18 @@ struct Widget4x4Card {
|
|||||||
.height(38)
|
.height(38)
|
||||||
Text(item.title)
|
Text(item.title)
|
||||||
.fontSize(12)
|
.fontSize(12)
|
||||||
.fontColor('#1A1A1A')
|
.fontColor(item.isNow ? '#FF3B30' : '#1A1A1A')
|
||||||
.maxLines(2)
|
.maxLines(2)
|
||||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||||
.layoutWeight(1)
|
.layoutWeight(1)
|
||||||
|
if (item.isNow) {
|
||||||
|
Text('● 进行中')
|
||||||
|
.fontSize(9)
|
||||||
|
.fontColor('#FF3B30')
|
||||||
|
.padding({ left: 4, right: 4, top: 1, bottom: 1 })
|
||||||
|
.borderRadius(4)
|
||||||
|
.backgroundColor('#FFECEA')
|
||||||
|
}
|
||||||
if (item.calName !== '') {
|
if (item.calName !== '') {
|
||||||
Text(item.calName)
|
Text(item.calName)
|
||||||
.fontSize(9)
|
.fontSize(9)
|
||||||
@@ -133,7 +166,7 @@ struct Widget4x4Card {
|
|||||||
.width('100%')
|
.width('100%')
|
||||||
.padding({ left: 8, right: 8, top: 5, bottom: 5 })
|
.padding({ left: 8, right: 8, top: 5, bottom: 5 })
|
||||||
.borderRadius(8)
|
.borderRadius(8)
|
||||||
.backgroundColor('#F5F7FA')
|
.backgroundColor(item.isNow ? '#FFF1F0' : '#F5F7FA')
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 卡片"打开 App":点击日期区或日程列表触发;添加按钮是 header 行的兄弟节点,其点击不会冒泡到这里 */
|
/** 卡片"打开 App":点击日期区或日程列表触发;添加按钮是 header 行的兄弟节点,其点击不会冒泡到这里 */
|
||||||
@@ -192,11 +225,17 @@ struct Widget4x4Card {
|
|||||||
.fontColor('#666666')
|
.fontColor('#666666')
|
||||||
.width('100%')
|
.width('100%')
|
||||||
}
|
}
|
||||||
|
if (item.showNowLine && !item.nowLineBelow) {
|
||||||
|
this.nowLine()
|
||||||
|
}
|
||||||
if (item.time === '全天') {
|
if (item.time === '全天') {
|
||||||
this.buildAllDayRow(item)
|
this.buildAllDayRow(item)
|
||||||
} else {
|
} else {
|
||||||
this.buildTimedRow(item)
|
this.buildTimedRow(item)
|
||||||
}
|
}
|
||||||
|
if (item.nowLineBelow) {
|
||||||
|
this.nowLine()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.width('100%')
|
.width('100%')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ class CardItem6x4 {
|
|||||||
showDate: boolean = false;
|
showDate: boolean = false;
|
||||||
calName: string = '';
|
calName: string = '';
|
||||||
color: string = '#007DFF';
|
color: string = '#007DFF';
|
||||||
|
// 红线:startMs/endMs 实际起止;showNowLine 上方画红线;isNow 正在进行;nowLineBelow 画在底部
|
||||||
|
startMs: number = 0;
|
||||||
|
endMs: number = 0;
|
||||||
|
showNowLine: boolean = false;
|
||||||
|
isNow: boolean = false;
|
||||||
|
nowLineBelow: boolean = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Entry(storage6x4)
|
@Entry(storage6x4)
|
||||||
@@ -87,6 +93,25 @@ struct Widget6x4Card {
|
|||||||
.backgroundColor('#F5F7FA')
|
.backgroundColor('#F5F7FA')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 当前时间红线标记:左侧红点 + 贯穿整行的红色细线 */
|
||||||
|
@Builder
|
||||||
|
nowLine() {
|
||||||
|
Row() {
|
||||||
|
Column()
|
||||||
|
.width(6)
|
||||||
|
.height(6)
|
||||||
|
.borderRadius(3)
|
||||||
|
.backgroundColor('#FF3B30')
|
||||||
|
Column()
|
||||||
|
.height(2)
|
||||||
|
.layoutWeight(1)
|
||||||
|
.backgroundColor('#FF3B30')
|
||||||
|
.borderRadius(1)
|
||||||
|
}
|
||||||
|
.width('100%')
|
||||||
|
.padding({ top: 3, bottom: 3 })
|
||||||
|
}
|
||||||
|
|
||||||
/** 有时间事件行(时间轴):圆角矩形 + 左色竖条 + 开始时间(上)-竖线-结束时间(下) + 标题 */
|
/** 有时间事件行(时间轴):圆角矩形 + 左色竖条 + 开始时间(上)-竖线-结束时间(下) + 标题 */
|
||||||
@Builder
|
@Builder
|
||||||
buildTimedRow(item: CardItem6x4) {
|
buildTimedRow(item: CardItem6x4) {
|
||||||
@@ -95,13 +120,13 @@ struct Widget6x4Card {
|
|||||||
.width(3)
|
.width(3)
|
||||||
.height(38)
|
.height(38)
|
||||||
.borderRadius(2)
|
.borderRadius(2)
|
||||||
.backgroundColor(item.color)
|
.backgroundColor(item.isNow ? '#FF3B30' : item.color)
|
||||||
// 时间列:开始时间在上、结束时间在下、中间竖线连接
|
// 时间列:开始时间在上、结束时间在下、中间竖线连接
|
||||||
Column({ space: 2 }) {
|
Column({ space: 2 }) {
|
||||||
Text(item.time)
|
Text(item.time)
|
||||||
.fontSize(10)
|
.fontSize(10)
|
||||||
.fontWeight(FontWeight.Medium)
|
.fontWeight(FontWeight.Medium)
|
||||||
.fontColor('#333333')
|
.fontColor(item.isNow ? '#FF3B30' : '#333333')
|
||||||
Column()
|
Column()
|
||||||
.width(1.5)
|
.width(1.5)
|
||||||
.layoutWeight(1)
|
.layoutWeight(1)
|
||||||
@@ -116,10 +141,18 @@ struct Widget6x4Card {
|
|||||||
.height(38)
|
.height(38)
|
||||||
Text(item.title)
|
Text(item.title)
|
||||||
.fontSize(12)
|
.fontSize(12)
|
||||||
.fontColor('#1A1A1A')
|
.fontColor(item.isNow ? '#FF3B30' : '#1A1A1A')
|
||||||
.maxLines(2)
|
.maxLines(2)
|
||||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||||
.layoutWeight(1)
|
.layoutWeight(1)
|
||||||
|
if (item.isNow) {
|
||||||
|
Text('● 进行中')
|
||||||
|
.fontSize(9)
|
||||||
|
.fontColor('#FF3B30')
|
||||||
|
.padding({ left: 4, right: 4, top: 1, bottom: 1 })
|
||||||
|
.borderRadius(4)
|
||||||
|
.backgroundColor('#FFECEA')
|
||||||
|
}
|
||||||
if (item.calName !== '') {
|
if (item.calName !== '') {
|
||||||
Text(item.calName)
|
Text(item.calName)
|
||||||
.fontSize(9)
|
.fontSize(9)
|
||||||
@@ -133,7 +166,7 @@ struct Widget6x4Card {
|
|||||||
.width('100%')
|
.width('100%')
|
||||||
.padding({ left: 8, right: 8, top: 5, bottom: 5 })
|
.padding({ left: 8, right: 8, top: 5, bottom: 5 })
|
||||||
.borderRadius(8)
|
.borderRadius(8)
|
||||||
.backgroundColor('#F5F7FA')
|
.backgroundColor(item.isNow ? '#FFF1F0' : '#F5F7FA')
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 卡片"打开 App":点击日期区或日程列表触发;添加按钮是 header 行的兄弟节点,其点击不会冒泡到这里 */
|
/** 卡片"打开 App":点击日期区或日程列表触发;添加按钮是 header 行的兄弟节点,其点击不会冒泡到这里 */
|
||||||
@@ -192,11 +225,17 @@ struct Widget6x4Card {
|
|||||||
.fontColor('#666666')
|
.fontColor('#666666')
|
||||||
.width('100%')
|
.width('100%')
|
||||||
}
|
}
|
||||||
|
if (item.showNowLine && !item.nowLineBelow) {
|
||||||
|
this.nowLine()
|
||||||
|
}
|
||||||
if (item.time === '全天') {
|
if (item.time === '全天') {
|
||||||
this.buildAllDayRow(item)
|
this.buildAllDayRow(item)
|
||||||
} else {
|
} else {
|
||||||
this.buildTimedRow(item)
|
this.buildTimedRow(item)
|
||||||
}
|
}
|
||||||
|
if (item.nowLineBelow) {
|
||||||
|
this.nowLine()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.width('100%')
|
.width('100%')
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user