diff --git a/entry/src/main/ets/components/FbBookReadSheet.ets b/entry/src/main/ets/components/FbBookReadSheet.ets new file mode 100644 index 0000000..c443cf5 --- /dev/null +++ b/entry/src/main/ets/components/FbBookReadSheet.ets @@ -0,0 +1,253 @@ +/** + * 家书 FamilyBook · 在线阅读浮层(鸿蒙端,工单 #28) + * + * 对应网页版 `templates/books/read.php`:章节列表 + 单章正文。 + * + * 用户 2026-10-28 原话「鸿蒙端对齐网页端」。原来端上只能看进度数字, + * 在线读、上传解析、笔记导入都在网页端。这个浮层把「在线读」补上。 + * + * ⚠️ 章节正文走 `GET /books//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 { + 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 = JSON.parse(res.data) as Record; + 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 { + 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 = JSON.parse(res.data) as Record; + 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) + } +} \ No newline at end of file diff --git a/entry/src/main/ets/components/FbBookSheet.ets b/entry/src/main/ets/components/FbBookSheet.ets index f491ec1..f93b6bf 100644 --- a/entry/src/main/ets/components/FbBookSheet.ets +++ b/entry/src/main/ets/components/FbBookSheet.ets @@ -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%') diff --git a/entry/src/main/ets/components/FbBookUploadSheet.ets b/entry/src/main/ets/components/FbBookUploadSheet.ets new file mode 100644 index 0000000..7537ce2 --- /dev/null +++ b/entry/src/main/ets/components/FbBookUploadSheet.ets @@ -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 { + 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 = 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 { + 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 = JSON.parse(res.data) as Record; + 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) + } +} \ No newline at end of file diff --git a/entry/src/main/ets/data/FbBookRepo.ets b/entry/src/main/ets/data/FbBookRepo.ets index 874006c..12f061d 100644 --- a/entry/src/main/ets/data/FbBookRepo.ets +++ b/entry/src/main/ets/data/FbBookRepo.ets @@ -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 { + return await fbUploadOne('/api/v1/books/upload', 'file', uri, token); +} + +/** 章节列表(工单 #28;`GET /books//chapters`) */ +export async function fetchBookChapters(token: string, id: number): Promise { + const opts: FbApiOptions = { token: token, authProbe: true }; + return await fbApi('/api/v1/books/' + id.toString() + '/chapters', opts); +} + +/** 单章正文(工单 #28;`GET /books//chapter?seq=N`) */ +export async function fetchBookChapter(token: string, id: number, seq: number): Promise { + const q: Record = {}; + 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 { + const body: Record = {}; + body['raw'] = raw; + body['source'] = 'weread'; + const opts: FbApiOptions = { method: 'POST', token: token, body: body }; + return await fbApi('/api/v1/books/import_notes', opts); +} diff --git a/entry/src/main/ets/model/FbSettingsMenus.ets b/entry/src/main/ets/model/FbSettingsMenus.ets index 766ac12..11ea1e2 100644 --- a/entry/src/main/ets/model/FbSettingsMenus.ets +++ b/entry/src/main/ets/model/FbSettingsMenus.ets @@ -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: '' } ]; diff --git a/entry/src/main/ets/pages/books/FbBooksPage.ets b/entry/src/main/ets/pages/books/FbBooksPage.ets index 8b89a29..8b40950 100644 --- a/entry/src/main/ets/pages/books/FbBooksPage.ets +++ b/entry/src/main/ets/pages/books/FbBooksPage.ets @@ -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%')