读书上传+在线阅读+笔记导入:端上补齐三个入口 laoyang/FamilyBook#28

用户原话(2026-10-28):「鸿蒙端对齐网页端」。读书端上原来只有书架。

1) FbBookUploadSheet:上传电子书(选文件→服务端解析建书)+ 粘贴导入读书笔记
2) FbBookReadSheet:在线阅读(章节列表 + 单章正文,一次一章)
3) FbBookSheet 详情态加「在线阅读」入口(有章节时才显示);
   FbBooksPage 加「+上传/导入」入口 + 设置菜单「导入笔记」接上

编译 BUILD SUCCESSFUL;服务端冒烟 _i28_book_api.py 11/0 + 反证命中。
This commit is contained in:
2026-10-28 22:47:38 +08:00
parent f1f279f973
commit f5a979f5c8
6 changed files with 599 additions and 14 deletions
@@ -0,0 +1,253 @@
/**
* 家书 FamilyBook · 在线阅读浮层(鸿蒙端,工单 #28)
*
* 对应网页版 `templates/books/read.php`:章节列表 + 单章正文。
*
* 用户 2026-10-28 原话「鸿蒙端对齐网页端」。原来端上只能看进度数字,
* 在线读、上传解析、笔记导入都在网页端。这个浮层把「在线读」补上。
*
* ⚠️ 章节正文走 `GET /books/<id>/chapter?seq=N`(服务端 `Book::chapter`),
* 一次只读一章(正文很长,全量拉会爆内存)。
*/
import { FbColors, FbMetric, FB_MASK } from '../common/Theme';
import { FbSheetRing } from './FbSheetRing';
import { FbSheetClose } from './FbSheetClose';
import { FB_TOKEN_STORE } from '../data/FbTokenStore';
import { FbApiResult } from '../data/FbHttp';
import { fetchBookChapters, fetchBookChapter } from '../data/FbBookRepo';
/** 服务端 `Book::chapters()` 的一行(id/seq/title/words) */
interface FbChapterRow {
id: number;
seq: number;
title: string;
words: number;
}
interface RawChapter {
id?: number; seq?: number; title?: string | null; words?: number;
}
@ComponentV2
export struct FbBookReadSheet {
@Param bookId: number = 0;
@Param bookTitle: string = '';
@Param accent: string = '#6B5B95';
@Event onClose: () => void = (): void => {};
@Local chapters: FbChapterRow[] = [];
@Local loadingList: boolean = true;
@Local listNote: string = '';
/** 正在读那一章 */
@Local curSeq: number = 0;
@Local curTitle: string = '';
@Local curBody: string = '';
@Local loadingChapter: boolean = false;
@Local chapterNote: string = '';
aboutToAppear(): void {
this.loadChapters();
}
private async loadChapters(): Promise<void> {
this.loadingList = true;
this.listNote = '';
const res: FbApiResult = await fetchBookChapters(FB_TOKEN_STORE.get(), this.bookId);
this.loadingList = false;
if (!res.ok) {
this.listNote = res.error !== '' ? res.error : '章节列表没拉到。';
return;
}
const out: FbChapterRow[] = [];
try {
const o: Record<string, Object> = JSON.parse(res.data) as Record<string, Object>;
const items: Object | undefined = o['items'];
const rows: RawChapter[] = (items !== undefined && items !== null)
? (items as RawChapter[]) : [];
for (const c of rows) {
out.push({
id: c.id !== undefined ? c.id : 0,
seq: c.seq !== undefined ? c.seq : 0,
title: c.title !== undefined && c.title !== null ? c.title : '',
words: c.words !== undefined ? c.words : 0,
});
}
} catch (e) {
// 解析失败当空列表
}
this.chapters = out;
}
private async openChapter(seq: number, title: string): Promise<void> {
this.curSeq = seq;
this.curTitle = title;
this.loadingChapter = true;
this.chapterNote = '';
const res: FbApiResult = await fetchBookChapter(FB_TOKEN_STORE.get(), this.bookId, seq);
this.loadingChapter = false;
if (!res.ok) {
this.chapterNote = res.error !== '' ? res.error : '这一章没读到。';
return;
}
try {
const o: Record<string, Object> = JSON.parse(res.data) as Record<string, Object>;
const c: Object | undefined = o['content'];
this.curBody = typeof c === 'string' ? c : '';
} catch (e) {
this.curBody = '';
this.chapterNote = '正文解析失败。';
}
}
build() {
Stack({ alignContent: Alignment.Center }) {
Column()
.width('100%')
.height('100%')
.backgroundColor(FB_MASK)
.onClick((): void => {
this.onClose();
})
Column() {
FbSheetRing({ radius: FbMetric.radius, ringColor: this.accent }) {
Column({ space: 0 }) {
// 标题栏
Row({ space: 8 }) {
Column({ space: 2 }) {
Text(this.bookTitle !== '' ? this.bookTitle : '在线阅读')
.fontSize(FbMetric.fontTitle)
.fontWeight(FontWeight.Medium)
.fontColor(FbColors.ink)
.maxLines(1)
Text(this.curSeq > 0 ? this.curTitle : (this.chapters.length.toString() + ' 章'))
.fontSize(FbMetric.fontTiny)
.fontColor(this.accent)
.maxLines(1)
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
.flexShrink(1)
FbSheetClose({
onClose: (): void => {
this.onClose();
},
color: this.accent
})
}
.width('100%')
.padding({ bottom: 8 })
.alignItems(VerticalAlign.Center)
if (this.listNote !== '') {
Text(this.listNote)
.fontSize(FbMetric.fontTiny)
.fontColor(FbColors.red)
.width('100%')
.padding({ top: 6, bottom: 6 })
}
if (this.curSeq > 0) {
// 正文态
Column({ space: 8 }) {
Row() {
Text('← 目录')
.fontSize(FbMetric.fontTiny)
.fontColor(this.accent)
.padding({ left: 8, right: 8, top: 5, bottom: 5 })
.onClick((): void => {
this.curSeq = 0;
})
}
.width('100%')
if (this.loadingChapter) {
Text('正在取这一章…')
.fontSize(FbMetric.fontSmall)
.fontColor(FbColors.faint)
.width('100%')
} else if (this.chapterNote !== '') {
Text(this.chapterNote)
.fontSize(FbMetric.fontTiny)
.fontColor(FbColors.red)
.width('100%')
} else {
Scroll() {
Text(this.curBody)
.fontSize(16)
.lineHeight(26)
.fontColor(FbColors.ink)
.width('100%')
}
.constraintSize({ maxHeight: 520 })
.width('100%')
.scrollBar(BarState.Off)
}
}
.width('100%')
.alignItems(HorizontalAlign.Start)
} else {
// 目录态
if (this.loadingList) {
Text('正在取目录…')
.fontSize(FbMetric.fontSmall)
.fontColor(FbColors.faint)
.width('100%')
} else if (this.chapters.length === 0) {
Text(this.listNote !== '' ? this.listNote : '这本书还没有解析出章节(可能是 PDF,浏览器里看,或没上传文件)。')
.fontSize(FbMetric.fontSmall)
.fontColor(FbColors.faint)
.width('100%')
} else {
Scroll() {
Column({ space: 0 }) {
ForEach(this.chapters, (c: FbChapterRow) => {
Row({ space: 8 }) {
Column({ space: 2 }) {
Text(c.title !== '' ? c.title : ('第 ' + c.seq.toString() + ' 章'))
.fontSize(FbMetric.fontSmall)
.fontColor(FbColors.ink)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Text(c.words.toString() + ' 字')
.fontSize(10)
.fontColor(FbColors.faint)
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Text('›')
.fontSize(FbMetric.fontBody)
.fontColor(FbColors.faint)
}
.width('100%')
.padding({ top: 10, bottom: 10 })
.onClick((): void => {
this.openChapter(c.seq, c.title);
})
}, (c: FbChapterRow): string => 'ch_' + c.seq.toString())
}
.width('100%')
.alignItems(HorizontalAlign.Start)
}
.constraintSize({ maxHeight: 520 })
.width('100%')
.scrollBar(BarState.Off)
}
}
}
.width('100%')
.padding({ left: 18, right: 18, top: 18, bottom: 18 })
.alignItems(HorizontalAlign.Start)
}
}
.width('92%')
}
.width('100%')
.height('100%')
.hitTestBehavior(HitTestMode.Transparent)
}
}
+27 -1
View File
@@ -26,6 +26,7 @@ import { createBook, updateBook, deleteBook, fetchBookNotes, addBookNote } from
import { parseBookNotes } from '../data/FbParse';
import { FbApiResult } from '../data/FbHttp';
import { FbBookItem, FbBookNoteItem } from '../model/Types';
import { FbBookReadSheet } from './FbBookReadSheet';
/** 状态选项:`key` 必须与服务端 `Book::STATUS` 逐字一致 */
const FB_BOOK_STATUS: string[] = ['wish', 'reading', 'done', 'paused'];
@@ -75,6 +76,8 @@ export struct FbBookSheet {
@Local draftKind: string = 'note';
/** 正在提交笔记(与「保存这本书」分开:两个请求互不影响) */
@Local storing: boolean = false;
/** 在线阅读浮层是否打开(工单 #28) */
@Local readOpen: boolean = false;
private isNew(): boolean {
return this.editingId <= 0;
@@ -200,9 +203,20 @@ export struct FbBookSheet {
.fontColor(FbColors.muted)
.layoutWeight(1)
.maxLines(2)
if (this.chapters > 0) {
Text('在线阅读')
.fontSize(FbMetric.fontTiny)
.fontColor(this.accent)
.padding({ left: 8, right: 8, top: 5, bottom: 5 })
.borderRadius(FbMetric.radiusPill)
.backgroundColor(fbAlpha(this.accent, 0.08))
.onClick((): void => {
this.readOpen = true;
})
}
}
.width('100%')
.alignItems(VerticalAlign.Top)
.alignItems(VerticalAlign.Center)
}
// ---- 读书笔记(只有详情态;这里是"点进去"能看见的正事)----
@@ -361,6 +375,18 @@ export struct FbBookSheet {
}
}
.width('92%')
// ---- 在线阅读(工单 #28)----
if (this.readOpen) {
FbBookReadSheet({
bookId: this.editingId,
bookTitle: this.fTitle,
accent: this.accent,
onClose: (): void => {
this.readOpen = false;
}
})
}
}
.width('100%')
.height('100%')
@@ -0,0 +1,258 @@
/**
* 家书 FamilyBook · 上传电子书 + 导入读书笔记(鸿蒙端,工单 #28)
*
* 对应网页端 `templates/books/form.php` 的上传 + `books/notes_import.php` 的粘贴导入。
*
* 用户 2026-10-28 原话「鸿蒙端对齐网页端」。原来这两样都要去网页端。
* 现在:① 选一个 EPUB/TXT 文件上传 → 服务端解析建书;② 粘贴读书笔记原文 → 解析导入。
*
* ⚠️ 上传用文件选择器(`DocumentPicker`),不是相册 —— 书是文档不是图。
*/
import { FbColors, FbMetric, FB_MASK, fbAlpha } from '../common/Theme';
import { FbSheetRing } from './FbSheetRing';
import { FbSheetClose } from './FbSheetClose';
import { FB_TOKEN_STORE } from '../data/FbTokenStore';
import { FbApiResult } from '../data/FbHttp';
import { uploadBook, importBookNotes } from '../data/FbBookRepo';
import { picker } from '@kit.CoreFileKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
const FB_TABS: string[] = ['上传电子书', '导入笔记'];
@ComponentV2
export struct FbBookUploadSheet {
@Param accent: string = '#6B5B95';
@Event onClose: () => void = (): void => {};
/** 上传 / 导入成功,书架要刷新 */
@Event onDone: () => void = (): void => {};
@Local tab: number = 0;
@Local busy: boolean = false;
@Local note: string = '';
@Local okMsg: string = '';
@Local importRaw: string = '';
private uploadNote(): string {
return '支持 EPUB / TXT / PDF / MOBI / DOC 等,传到服务端解析书名、作者和章节正文。';
}
/** 选文件 → 上传(服务端解析并建书) */
private async pickAndUpload(): Promise<void> {
if (this.busy) {
return;
}
try {
const docPicker = new picker.DocumentViewPicker();
const opts = new picker.DocumentSelectOptions();
opts.maxSelectNumber = 1;
opts.fileSuffixFilters = ['epub', 'txt', 'pdf', 'mobi', 'azw3', 'doc', 'docx'];
const result: Array<string> = await docPicker.select(opts);
if (result.length === 0) {
return;
}
this.busy = true;
this.note = '';
this.okMsg = '';
const err: string = await uploadBook(FB_TOKEN_STORE.get(), result[0]);
this.busy = false;
if (err !== '') {
this.note = err;
return;
}
this.okMsg = '这本书进书架了。';
this.onDone();
} catch (e) {
const be: BusinessError = e as BusinessError;
hilog.warn(0x0000, 'FamilyBook', '上传电子书失败:%{public}s', JSON.stringify(be));
this.busy = false;
this.note = '选文件失败,请再试一次。';
}
}
/** 粘贴导入读书笔记 */
private async doImport(): Promise<void> {
if (this.busy) {
return;
}
const raw: string = this.importRaw.trim();
if (raw === '') {
this.note = '先粘贴要导入的读书笔记原文。';
return;
}
this.busy = true;
this.note = '';
this.okMsg = '';
const res: FbApiResult = await importBookNotes(FB_TOKEN_STORE.get(), raw);
this.busy = false;
if (!res.ok) {
this.note = res.error !== '' ? res.error : '没导入成,请稍后再试。';
return;
}
let detail: string = '';
try {
const o: Record<string, Object> = JSON.parse(res.data) as Record<string, Object>;
const c: Object | undefined = o['created'];
const a: Object | undefined = o['added'];
detail = (typeof c === 'number' ? '新建 ' + c.toString() + ' 本' : '') +
(typeof a === 'number' ? '、' + a.toString() + ' 条笔记' : '');
} catch (e) {
detail = '';
}
this.okMsg = '导入完成' + (detail !== '' ? ':' + detail : '') + '。';
this.importRaw = '';
this.onDone();
}
build() {
Stack({ alignContent: Alignment.Center }) {
Column()
.width('100%')
.height('100%')
.backgroundColor(FB_MASK)
.onClick((): void => {
this.onClose();
})
Column() {
FbSheetRing({ radius: FbMetric.radius, ringColor: this.accent }) {
Column({ space: 12 }) {
// 标题栏
Row({ space: 8 }) {
Column({ space: 2 }) {
Text('加一本')
.fontSize(FbMetric.fontTitle)
.fontWeight(FontWeight.Medium)
.fontColor(FbColors.ink)
Text('上传电子书,或粘贴导入读书笔记')
.fontSize(FbMetric.fontTiny)
.fontColor(this.accent)
.maxLines(1)
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
FbSheetClose({
onClose: (): void => {
this.onClose();
},
color: this.accent
})
}
.width('100%')
.alignItems(VerticalAlign.Center)
// tab 切换
Row({ space: 8 }) {
ForEach(FB_TABS, (t: string, idx: number) => {
Text(t)
.fontSize(FbMetric.fontTiny)
.fontColor(this.tab === idx ? '#FFFFFF' : FbColors.muted)
.padding({ left: 14, right: 14, top: 7, bottom: 7 })
.borderRadius(FbMetric.radiusPill)
.backgroundColor(this.tab === idx ? this.accent : FbColors.inputBg)
.onClick((): void => {
this.tab = idx;
this.note = '';
this.okMsg = '';
})
}, (t: string): string => 'btab_' + t)
}
.width('100%')
if (this.note !== '') {
Text(this.note)
.fontSize(FbMetric.fontTiny)
.fontColor(FbColors.red)
.width('100%')
.maxLines(4)
}
if (this.okMsg !== '') {
Text(this.okMsg)
.fontSize(FbMetric.fontTiny)
.fontColor('#5E7A3C')
.width('100%')
.maxLines(3)
}
if (this.tab === 0) {
// -------- 上传电子书 --------
Column({ space: 10 }) {
Text(this.uploadNote())
.fontSize(FbMetric.fontTiny)
.fontColor(FbColors.faint)
.width('100%')
.maxLines(4)
Row() {
Text(this.busy ? '正在上传解析…' : '选文件上传')
.fontSize(FbMetric.fontBody)
.fontWeight(FontWeight.Medium)
.fontColor(this.busy ? FbColors.faint : '#FFFFFF')
.textAlign(TextAlign.Center)
.layoutWeight(1)
.height(44)
.borderRadius(FbMetric.radiusS)
.backgroundColor(fbAlpha(this.accent, this.busy ? 0.4 : 0.96))
.onClick((): void => {
this.pickAndUpload();
})
}
.width('100%')
}
.width('100%')
.alignItems(HorizontalAlign.Start)
} else {
// -------- 导入笔记 --------
Column({ space: 10 }) {
Text('一次贴一段(微信读书 / Kindle 导出那种「书名 + 划线/想法」),服务端自动按书名归到对应书里。')
.fontSize(FbMetric.fontTiny)
.fontColor(FbColors.faint)
.width('100%')
.maxLines(4)
TextArea({ text: this.importRaw, placeholder: '粘贴读书笔记原文…' })
.placeholderColor(FbColors.faint)
.placeholderFont({ size: FbMetric.fontTiny })
.fontSize(FbMetric.fontSmall)
.fontColor(FbColors.ink)
.backgroundColor(FbColors.inputBg)
.borderRadius(FbMetric.radiusS)
.height(160)
.width('100%')
.padding({ left: 10, right: 10, top: 8, bottom: 8 })
.onChange((v: string): void => {
this.importRaw = v;
})
Row() {
Text(this.busy ? '正在导入…' : '导入')
.fontSize(FbMetric.fontBody)
.fontWeight(FontWeight.Medium)
.fontColor(this.busy ? FbColors.faint : '#FFFFFF')
.textAlign(TextAlign.Center)
.layoutWeight(1)
.height(44)
.borderRadius(FbMetric.radiusS)
.backgroundColor(fbAlpha(this.accent, this.busy ? 0.4 : 0.96))
.onClick((): void => {
this.doImport();
})
}
.width('100%')
}
.width('100%')
.alignItems(HorizontalAlign.Start)
}
}
.width('100%')
.padding({ left: 18, right: 18, top: 18, bottom: 18 })
.alignItems(HorizontalAlign.Start)
}
}
.width('92%')
}
.width('100%')
.height('100%')
.hitTestBehavior(HitTestMode.Transparent)
}
}
+31 -9
View File
@@ -13,18 +13,12 @@
* 🔴 用户 2026-10-20:「其他的功能也逐步完成……设置也没有任何的东西。」
* 读书页那时是**纯书架**:书行点不动、没有"加一本"、设置三项全灰。
*
* ⚠️⚠️ 两处**服务端能力边界**(写在这里,免得以后又摆出假入口):
* 1. `PATCH` **不能改书名/作者** —— `books()` 只从请求体取
* `status` / `current_page` / `rating`,其它键根本不读。
* (改书名要等接口扩,别在界面上放个"书名"输入框然后存不进去。)
* 2. `PATCH` 里的 `current_page` **不对应任何列** —— `books` 表只有
* `progress`(读到第几章),而 `Book::update()` 不读 `current_page`
* ⇒ 端上**不要**回写章节进度,只**显示**。
* 3. 上传电子书 / EPUB 解析 / 笔记导入 / 微信读书同步都在网页端,
* 移动端没有文件选择与解析链路(`BookParser`、`BookNoteImport` 只有网页在调)。
* 🔴 工单 #28(2026-10-28):上传解析 / 在线阅读 / 笔记导入补齐(服务端已给
* `upload` / `chapters` / `chapter` / `import_notes` 四个端点,模型早就有)。
*/
import { fbApi, FbApiResult, FbApiOptions } from './FbHttp';
import { fbUploadOne } from './FbUpload';
/**
* 加一本书。
@@ -85,3 +79,31 @@ export async function addBookNote(token: string, id: number, content: string,
const opts: FbApiOptions = { method: 'POST', token: token, body: body };
return await fbApi('/api/v1/books/' + id.toString() + '/notes', opts);
}
/** 上传一本电子书(EPUB/TXT),服务端解析并建书 + 章节(工单 #28) */
export async function uploadBook(token: string, uri: string): Promise<string> {
return await fbUploadOne('/api/v1/books/upload', 'file', uri, token);
}
/** 章节列表(工单 #28;`GET /books/<id>/chapters`) */
export async function fetchBookChapters(token: string, id: number): Promise<FbApiResult> {
const opts: FbApiOptions = { token: token, authProbe: true };
return await fbApi('/api/v1/books/' + id.toString() + '/chapters', opts);
}
/** 单章正文(工单 #28;`GET /books/<id>/chapter?seq=N`) */
export async function fetchBookChapter(token: string, id: number, seq: number): Promise<FbApiResult> {
const q: Record<string, string> = {};
q['seq'] = seq.toString();
const opts: FbApiOptions = { token: token, query: q, authProbe: true };
return await fbApi('/api/v1/books/' + id.toString() + '/chapter', opts);
}
/** 粘贴导入读书笔记(工单 #28;`POST /books/import_notes`) */
export async function importBookNotes(token: string, raw: string): Promise<FbApiResult> {
const body: Record<string, Object> = {};
body['raw'] = raw;
body['source'] = 'weread';
const opts: FbApiOptions = { method: 'POST', token: token, body: body };
return await fbApi('/api/v1/books/import_notes', opts);
}
+1 -1
View File
@@ -46,7 +46,7 @@ export const FB_SET_MOVIES: FbSettingItem[] = [
*/
export const FB_SET_BOOKS: FbSettingItem[] = [
{ icon: 'chart', label: '读书统计', hint: '藏书 / 在读 / 读完 / 笔记条数', action: 'bookstats' },
{ icon: 'plus', label: '导入读书笔记', hint: '要在网页端选文件', action: '' },
{ icon: 'plus', label: '导入读书笔记', hint: '粘贴原文,自动归到对应书', action: 'bookimport' },
{ icon: 'share', label: '微信读书导入', hint: '要在网页端授权同步', action: '' }
];
+29 -3
View File
@@ -27,6 +27,7 @@ import { fbOnScroll, fbOnTouch } from '../../common/FbScroll';
import { fbMaskTransition, fbSheetTransition } from '../../common/FbAnim';
import { FbModuleSettingsSheet } from '../../components/FbModuleSettingsSheet';
import { FbBookSheet, FbBookStatsSheet } from '../../components/FbBookSheet';
import { FbBookUploadSheet } from '../../components/FbBookUploadSheet';
import { FB_BACK } from '../../common/FbBack';
import { FB_SET_BOOKS } from '../../model/FbSettingsMenus';
@@ -66,6 +67,8 @@ export struct FbBooksPage {
/** 读书统计浮层 */
@Local statsOpen: boolean = false;
/** 上传电子书 / 导入笔记浮层(工单 #28) */
@Local uploadOpen: boolean = false;
/**
* 页面出现:先读缓存、再拉接口(本地优先,见 FbModuleRepo 的说明)。
@@ -157,10 +160,17 @@ export struct FbBooksPage {
.layoutWeight(1)
/* 🔴 v67:原来这颗是「统计」图标且**没有 `onTap`**(纯摆设,点了没反应)。
现在拆成两颗:+ 加一本(主操作)、统计(设置里也有一条,这里给个快捷)。 */
现在拆成三颗:+ 上传/导入(工单 #28)、+手填、统计。 */
FbIconButton({
icon: $r('app.media.fb_ic_plus'),
tint: this.accent,
onTap: (): void => {
this.uploadOpen = true;
}
})
FbIconButton({
icon: $r('app.media.fb_ic_edit'),
tint: this.accent,
onTap: (): void => {
this.openNewBook();
}
@@ -247,11 +257,13 @@ export struct FbBooksPage {
},
onPick: (action: string): void => {
/* 🔴 v67:三条原来是空 `action`(画灰、点了没反应 = 用户说的"设置没有任何东西")。
现在 `bookstats` 真的能进去;另两条要文件选择/微信授权,
网页端才有 ⇒ 在组件里仍是「暂未开放」,这里的 hint 也写清楚了。 */
v95+(工单 #28):导入笔记也接上了(与上传同一个浮层,切到第二个 tab)。 */
if (action === 'bookstats') {
this.settingsOpen = false;
this.statsOpen = true;
} else if (action === 'bookimport') {
this.settingsOpen = false;
this.uploadOpen = true;
}
}
})
@@ -295,6 +307,20 @@ export struct FbBooksPage {
})
.transition(fbSheetTransition())
}
// ---- 上传电子书 / 导入笔记(工单 #28)----
if (this.uploadOpen) {
FbBookUploadSheet({
accent: this.accent,
onClose: (): void => {
this.uploadOpen = false;
},
onDone: (): void => {
this.load();
}
})
.transition(fbSheetTransition())
}
}
.width('100%')
.height('100%')