笔记图片块:展示真图 + 插图 + 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:
@@ -42,15 +42,72 @@ const TAG: string = 'FbUpload';
|
||||
*/
|
||||
export async function fbUploadOne(path: string, field: string, uri: string,
|
||||
token: string): Promise<string> {
|
||||
const r: FbUploadRaw = await fbUploadRaw(path, field, uri, token);
|
||||
return r.ok ? '' : r.error;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传图片并**拿回响应载荷**(工单 #23 用)。
|
||||
*
|
||||
* 笔记插图要拿到服务端刚存的那张图的附件 key(存进 `image` 块),
|
||||
* 而 `fbUploadOne` 只返回"成没成"、把 key 丢了。这里复用同一个
|
||||
* multipart 核心(`fbUploadRaw`),成功时把信封里的 `key` 吐出来。
|
||||
*
|
||||
* @returns key = 附件相对键;空串且 error 非空 = 失败
|
||||
*/
|
||||
export interface FbUploadKey {
|
||||
/** 附件 key(成功时有值) */
|
||||
key: string;
|
||||
/** 错误说明(空 = 成功) */
|
||||
error: string;
|
||||
}
|
||||
|
||||
export async function fbUploadKey(path: string, field: string, uri: string,
|
||||
token: string): Promise<FbUploadKey> {
|
||||
const r: FbUploadRaw = await fbUploadRaw(path, field, uri, token);
|
||||
if (!r.ok) {
|
||||
return { key: '', error: r.error };
|
||||
}
|
||||
try {
|
||||
const o: Record<string, Object> = JSON.parse(r.body) as Record<string, Object>;
|
||||
const k: Object | undefined = o['key'];
|
||||
if (typeof k === 'string' && k !== '') {
|
||||
return { key: k, error: '' };
|
||||
}
|
||||
// 没拿到 key:再按信封的 `data` 兜一把(不同端点习惯不同)
|
||||
const d: Object | undefined = o['data'];
|
||||
if (d !== undefined && typeof d === 'object' && d !== null) {
|
||||
const dk: Object | undefined = (d as Record<string, Object>)['key'];
|
||||
if (typeof dk === 'string' && dk !== '') {
|
||||
return { key: dk, error: '' };
|
||||
}
|
||||
}
|
||||
return { key: '', error: '服务端没回图片地址' };
|
||||
} catch (err) {
|
||||
return { key: '', error: '响应不是合法 JSON' };
|
||||
}
|
||||
}
|
||||
|
||||
/** `fbUploadOne` 与 `fbUploadKey` 共用的 multipart 核心(勿复制,见文件头说明) */
|
||||
interface FbUploadRaw {
|
||||
ok: boolean;
|
||||
/** 响应正文(成功时是非空 JSON) */
|
||||
body: string;
|
||||
/** 给人看的错误说明 */
|
||||
error: string;
|
||||
}
|
||||
|
||||
async function fbUploadRaw(path: string, field: string, uri: string,
|
||||
token: string): Promise<FbUploadRaw> {
|
||||
const base: string = fbApiBase();
|
||||
if (base === '') {
|
||||
return '还没有设置服务器地址';
|
||||
return { ok: false, body: '', error: '还没有设置服务器地址' };
|
||||
}
|
||||
if (token === '') {
|
||||
return '还没登录';
|
||||
return { ok: false, body: '', error: '还没登录' };
|
||||
}
|
||||
if (uri === '') {
|
||||
return '没选到文件';
|
||||
return { ok: false, body: '', error: '没选到文件' };
|
||||
}
|
||||
let buf: ArrayBuffer;
|
||||
try {
|
||||
@@ -61,7 +118,7 @@ export async function fbUploadOne(path: string, field: string, uri: string,
|
||||
fileIo.closeSync(file);
|
||||
} catch (err) {
|
||||
hilog.warn(DOMAIN, TAG, '读文件失败:%{public}s', JSON.stringify(err));
|
||||
return '读不出这张图';
|
||||
return { ok: false, body: '', error: '读不出这张图' };
|
||||
}
|
||||
|
||||
const boundary: string = '----FbUpload' + Date.now().toString();
|
||||
@@ -85,7 +142,7 @@ export async function fbUploadOne(path: string, field: string, uri: string,
|
||||
expectDataType: http.HttpDataType.STRING
|
||||
});
|
||||
if (resp.responseCode >= 200 && resp.responseCode < 300) {
|
||||
return '';
|
||||
return { ok: true, body: typeof resp.result === 'string' ? resp.result : '', error: '' };
|
||||
}
|
||||
const body: string = typeof resp.result === 'string' ? resp.result : '';
|
||||
// 服务端的错误说明是 JSON 信封里的 `error` —— 尽量把它抠出来给人话
|
||||
@@ -99,11 +156,11 @@ export async function fbUploadOne(path: string, field: string, uri: string,
|
||||
} catch (err) {
|
||||
// 不是 JSON(如 Apache 的错误页)⇒ 保留 HTTP 码
|
||||
}
|
||||
return msg;
|
||||
return { ok: false, body: body, error: msg };
|
||||
} catch (err) {
|
||||
const e: BusinessError = err as BusinessError;
|
||||
hilog.warn(DOMAIN, TAG, '上传失败 %{public}s:%{public}s', path, JSON.stringify(e));
|
||||
return '网络异常';
|
||||
return { ok: false, body: '', error: '网络异常' };
|
||||
} finally {
|
||||
req.destroy();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user