笔记图片块:展示真图 + 插图 + 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
+42 -5
View File
@@ -17,7 +17,7 @@
import {
FbAccount, FbAnniversaryItem, FbBookItem, FbBookNoteItem, FbCalEvent, FbHomeData, FbKinChip,
FbMark,
FbLedgerBook, FbLedgerDetail, FbLedgerEntry, FbNoteItem, FbNotificationItem, FbResourceItem,
FbLedgerBook, FbLedgerDetail, FbLedgerEntry, FbNoteItem, FbNoteBlock, FbNotificationItem, FbResourceItem,
FbTaskItem, FbTripItem, FbWeather, FbStat, FbMemoryItem, FbRecentDay,
FbTaskAttach, FbTaskComment, FbTaskDetail, FbTaskKid, FbTaskMeta, FbCatOpt, FbBoardOpt,
} from '../model/Types';
@@ -614,6 +614,7 @@ export function parseNotes(json: string): FbNoteItem[] {
/* ⚠️ 服务端列表已带 `blocks` / `tags` / `excerpt`(`Note::attachChildren`),
这里把它们都取出来 —— 原来只拿 blocks 算了个摘要,正文和标签全丢了。 */
const body: string = noteBodyOf(r.blocks);
const blocks: FbNoteBlock[] = noteBlocksOf(r.blocks);
const tags: string[] = [];
for (const t of arr<string>(r.tags)) {
tags.push(s(t));
@@ -628,6 +629,7 @@ export function parseNotes(json: string): FbNoteItem[] {
shared: n(r.is_shared) !== 0,
attachCount: 0,
body: body,
blocks: blocks,
mine: n(r.is_mine) !== 0,
});
}
@@ -635,10 +637,34 @@ export function parseNotes(json: string): FbNoteItem[] {
}
/**
* `note_blocks` → 一段纯文本。
* `note_blocks` → 端上块数组(工单 #23:图文混排要原始块,别只留一张拼好的文本)。
*
* ⚠️ 图片块的内容是**附件 key**(不是正文),拼进来会变成一串乱码 key
* ⇒ 记成「[图片]」,用户一眼知道那里有张图。
* ⚠️ 只保留白名单里的 `text` / `image`(服务端 `Note::BLOCK_KINDS`),
* 其它一律归一成 text 并交给 noteBodyOf 语义处理;这里只吐给展示/编辑用。
*/
function noteBlocksOf(blocks: RawNoteBlock[] | undefined): FbNoteBlock[] {
const rows: RawNoteBlock[] = arr<RawNoteBlock>(blocks);
const out: FbNoteBlock[] = [];
for (const bk of rows) {
const kind: string = s(bk.kind);
if (kind !== 'text' && kind !== 'image') {
continue;
}
out.push({
kind: kind,
content: s(bk.content),
position: n(bk.position),
});
}
return out;
}
/**
* `note_blocks` → 一段纯文本(**只拼 text 块**,图片块跳过)。
*
* 🔴 工单 #23:原来图片块被记成「[图片]」占位字,正文框显示它、保存还会把
* 它当正文写回去。现在图片块由 `FbNoteItem.blocks` 单独承载,正文只留纯文本。
* 这样:① 显示真图不靠占位字;② 保存时用 `blocks` 传法,图片不会被覆盖丢。
*/
function noteBodyOf(blocks: RawNoteBlock[] | undefined): string {
const rows: RawNoteBlock[] = arr<RawNoteBlock>(blocks);
@@ -646,7 +672,6 @@ function noteBodyOf(blocks: RawNoteBlock[] | undefined): string {
for (const b of rows) {
const kind: string = s(b.kind);
if (kind === 'image') {
parts.push('[图片]');
continue;
}
const txt: string = s(b.content).trim();
@@ -657,6 +682,18 @@ function noteBodyOf(blocks: RawNoteBlock[] | undefined): string {
return parts.join('\n\n');
}
/**
* 图片块 content → 纯净附件 key(剥掉「`|宽度`」后缀)。
*
* 与 Web 端 `Note::imageKey` 同语义:图片块 content 可能是 `key` 或 `key|50`,
* 拼图片 URL 时必须取 `|` 之前的 key,否则带后缀的那张图永远加载不出来。
*/
export function noteImageKey(content: string): string {
const c: string = content.trim();
const i: number = c.indexOf('|');
return i >= 0 ? c.substring(0, i).trim() : c;
}
/** 通知 → FbNotificationItem[] */
export function parseNotifications(json: string): FbNotificationItem[] {
const root: Record<string, Object> | null = asObject(json);