笔记图片块:展示真图 + 插图 + blocks 保存(图文混排后半程)laoyang/FamilyBook#23

用户原话(2026-10-28):「鸿蒙端对齐网页端」,笔记端上只支持纯文本。
这半程改鸿蒙端,配合服务端 cd56a9d 补的 /notes/upload:

1) 展示:FbNoteItem 新增 blocks(FbNoteBlock[]),parseNotes 把原始块
   带进来;FbNoteSheet 里 image 块真画图(fbImageSrc),不再显示「[图片]」。
2) 插图:正文区下加「+ 插图」,选图 → fbUploadKey 上传拿 key → 加入
   待保存列表;每张图带「×」可删。
3) 保存(P0 修复):带图时改走 saveNoteWithBlocks —— 正文 text 块 + 图片
   image 块整组提交。原来只传 body,服务端折成单个 text 块整组替换,
   **编辑带图笔记会把图片块静默删光**。
4) fbUploadKey 是 fbUpload 抽出的第二出口:复用同一 multipart 核心拿 key,
   不复制第二份实现(本项目"两份实现漂移"栽过多次)。

编译 BUILD SUCCESSFUL;守卫 _fb_arkts_guard.js 1324/0;服务端冒烟
_i23_note_image_api.py 11/0(含 PATCH 后图片块不被删)+ 反证命中 2/2。
This commit is contained in:
2026-10-28 21:35:11 +08:00
parent 3e910f1ca0
commit 97846029c7
7 changed files with 342 additions and 59 deletions
+5 -5
View File
@@ -361,27 +361,27 @@ export const FB_DEMO_NOTES: FbNoteItem[] = [
{
id: 1, title: '阳台改造清单', excerpt: '1. 换推拉门(断桥铝,考虑隔音)2. 加一组置物架 3. 补两盆绿萝…',
tags: ['家务', '装修'], updatedAt: '10-11 09:12', shared: false, attachCount: 2,
body: '1. 换推拉门(断桥铝,考虑隔音)\n2. 加一组置物架\n3. 补两盆绿萝', mine: true
body: '1. 换推拉门(断桥铝,考虑隔音)\n2. 加一组置物架\n3. 补两盆绿萝', blocks: [], mine: true
},
{
id: 2, title: '读书摘抄 · 置身事内', excerpt: '「地方政府的行为,要从它面对的制度约束去理解。」……',
tags: ['读书'], updatedAt: '10-10 22:40', shared: true, attachCount: 0,
body: '「地方政府的行为,要从它面对的制度约束去理解。」', mine: true
body: '「地方政府的行为,要从它面对的制度约束去理解。」', blocks: [], mine: true
},
{
id: 3, title: '儿子的兴趣班时间表', excerpt: '周一 16:30 数学;周三 17:00 游泳;周六 09:00 钢琴…',
tags: ['孩子'], updatedAt: '10-09 18:05', shared: true, attachCount: 1,
body: '周一 16:30 数学\n周三 17:00 游泳\n周六 09:00 钢琴', mine: false
body: '周一 16:30 数学\n周三 17:00 游泳\n周六 09:00 钢琴', blocks: [], mine: false
},
{
id: 4, title: '崂山两日流水', excerpt: '第一天仰口 → 太清宫;第二天巨峰。民宿 680,两餐 218…',
tags: ['旅行', '记账'], updatedAt: '10-04 20:30', shared: false, attachCount: 6,
body: '第一天仰口 → 太清宫;第二天巨峰。民宿 680,两餐 218', mine: true
body: '第一天仰口 → 太清宫;第二天巨峰。民宿 680,两餐 218', blocks: [], mine: true
},
{
id: 5, title: '车里该备的东西', excerpt: '拖车绳、搭电线、应急灯、保温毯、常用药…',
tags: ['车'], updatedAt: '09-25 07:48', shared: false, attachCount: 0,
body: '拖车绳、搭电线、应急灯、保温毯、常用药', mine: true
body: '拖车绳、搭电线、应急灯、保温毯、常用药', blocks: [], mine: true
}
];
+23 -1
View File
@@ -1004,18 +1004,40 @@ export interface FbNoteItem {
/** 附件数 */
attachCount: number;
/**
* 正文**纯文本**(把 `note_blocks` 里 text 块拼起来,图片块记成「[图片]」)。
* 正文**纯文本**(只把 `note_blocks` 里的 text 块拼起来,图片块不进正文)。
*
* 🔴 v65(用户 2026-10-20「笔记现在也点不进去,记笔记也记不了」):
* 服务端 `/notes` 列表其实**已经带 `blocks`**(`Note::attachChildren` 一次性挂上),
* 只是端上原来只用它算了个摘要扔掉 ⇒ 点进详情时正文没了。
* 这里存下来,详情直接显示,不必再打一次 `/notes/<id>`。
* 🔴 v95+(工单 #23):图片块另存 `blocks` 字段,不再挤进正文当「[图片]」。
*/
body: string;
/**
* 正文的**块**(服务端 `note_blocks` 的镜像,工单 #23)。
*
* 🔴 为什么要在 `body`(纯文本摘要)之外再存一份块:
* 端上要"图文混排"就不能只拿一条拼好的文本 —— 图片块的内容是
* 附件 key,`body` 里被记成了「[图片]」;要真显示图、以及编辑时
* **不把图片块清掉**,都得拿到原始块(`kind` + `content` + `position`)。
* 服务端列表(`Note::all` + `attachChildren`)本就下发了 `blocks`,
* 这里不再用一次就扔。
*/
blocks: FbNoteBlock[];
/** 是不是我写的(不是的话只能看,服务端也只让作者删) */
mine: boolean;
}
/** 一条笔记正文块(kind ∈ text / image) */
export interface FbNoteBlock {
/** text / image */
kind: string;
/** text = 文本;image = 附件 key(可能带 `|宽度` 后缀,取图时先剥) */
content: string;
/** 块顺序 */
position: number;
}
// ============================================================
// 通知(notifications)
// ============================================================