跨端: 鸿蒙日历写侧(新建/编辑/删除/暂停恢复)+ 表单不再是一面滚轮墙
用户:「是的去吧」(批准上一封列出的下一步)。
- CalendarApi:createEvent / updateEvent / deleteEvent(POST /calendar/events、
PUT/DELETE /calendar/events/{id});新建与编辑发**同一份** CalendarEventInput
—— 服务端两个端点同形(CreateCalendarEvent 里那个 Status 字段的注释就写着这句),
拆两份会在严格解码下 400
- CalendarPage:写侧表单(新建与编辑共用一份)、二次确认删除、暂停/恢复走 update
(调度器只触发 active;没有暂停就只能删掉重加,而那会丢 event_id 与历史)
- 服务端 400 文案原样显示(收件地址无法解析:…)—— 建事件时就校验地址,
吞成保存失败等于让人猜,而猜的代价是以为设好了、实际永远发不出去
- 空标题/空收件人在本地就挡住,不发注定 400 的请求
- 提交的是**时间戳**(isoTimestampOfLocal),不是日期键;编辑回填走 localDateOf
- ★ 表单形状:日期/时间选择器合起来 ~1400px,展开着放会把标题与收件人挤出屏幕 ——
改成一行摘要 + 点开展开(默认收起)。这不是美观问题:展开态下保存在屏幕外,
要跨过一整面滚轮才够得着
- 判据 +7(端点/载荷/前置校验/文案/二次确认/时间戳往返/变异自检),23 条全绿
- 真机实测(hvigorw + hdc + uitest 逐字段输入 + 逐键点击,每一步都回库核对):
建 → 库里 event_time 是 2026-09-28T01:00Z(= 本地 09:00 整,默认值正确)、
暂停 → status=paused、删除第一下只变确认删除(库里还在)、第二下才真删(库里 0 条)
This commit is contained in:
@ -182,3 +182,27 @@ export function hhmmAtOffset(timestamp: string, offsetMinutes: number): string {
|
||||
const shifted = new Date(ms + offsetMinutes * 60000);
|
||||
return `${pad2(shifted.getUTCHours())}:${pad2(shifted.getUTCMinutes())}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 本地年月日 + 时:分 → RFC3339 时间戳(**时间戳,不是日期键**)。
|
||||
*
|
||||
* 为什么不用字符串拼 + 时区偏移:设备时区可能是 +08:00 / -05:00 / UTC,手拼偏移
|
||||
* 就要自己处理夏令时与半小时时区(+05:30)。`Date` 的本地构造 + `toISOString()`
|
||||
* 拿到的是同一个**瞬间**的 UTC 表示,服务端 `time.Parse(RFC3339)` 直接吃得下。
|
||||
*
|
||||
* 与"取日期键"的区别(那条禁令针对的是 `.toISOString().slice(0,10)`):
|
||||
* 日期键必须是**本地**年月日,时间戳必须是**绝对瞬间** —— 两者混用的那一天,
|
||||
* 事件会被存到错误的小时上,而界面看起来完全正常。
|
||||
*/
|
||||
export function isoTimestampOfLocal(year: number, month: number, day: number, hour: number, minute: number): string {
|
||||
return new Date(year, month - 1, day, hour, minute, 0, 0).toISOString();
|
||||
}
|
||||
|
||||
/** RFC3339 时间戳 → 本地 `Date`(编辑表单回填用:日期与时间选择器都要 `Date`)。 */
|
||||
export function localDateOf(timestamp: string): Date {
|
||||
const ms: number = Date.parse(timestamp);
|
||||
if (Number.isNaN(ms)) {
|
||||
return new Date();
|
||||
}
|
||||
return new Date(ms);
|
||||
}
|
||||
|
||||
@ -266,3 +266,33 @@ export class CalendarEvent {
|
||||
export class CalendarListResponse {
|
||||
events: CalendarEvent[] = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 新建/编辑日程的请求体。
|
||||
*
|
||||
* **一份类型给两个端点用**(`POST /calendar/events` 与 `PUT /calendar/events/{id}`)——
|
||||
* 服务端也是这么设计的(`CreateCalendarEvent` 里那个 `Status` 字段的注释写着:
|
||||
* "前端的 CalendarEventInput 是**一份**类型,新建与编辑发的是同一个对象")。
|
||||
* 拆成两份的后果在严格解码下是新建直接 400。
|
||||
*
|
||||
* 服务端必填:`title`、`event_time`、以及至少一个收件人(`recipients` 之一)。
|
||||
* `reminder_text` 空则由服务端填默认模板(**变量形式**,触发时才渲染成本次时间)。
|
||||
*/
|
||||
export class CalendarEventInput {
|
||||
title: string = '';
|
||||
description: string = '';
|
||||
reminder_text: string = '';
|
||||
/** 完整三维地址串;这一版只发一个(多收件人与 separate/together 是下一步) */
|
||||
recipients: string[] = [];
|
||||
delivery_mode: string = 'separate';
|
||||
/** RFC3339 时间戳(不是日期键)—— 由 `Calendar.ts` 的 isoTimestampOfLocal 生成 */
|
||||
event_time: string = '';
|
||||
remind_before: number = 0;
|
||||
recurrence: string = 'none';
|
||||
status: string = 'active';
|
||||
}
|
||||
|
||||
/** 删除日程的响应(服务端给 `{status}`) */
|
||||
export class CalendarDeleteResponse {
|
||||
status: string = '';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user