Windows 鸿蒙 PC 应用开发:Electron 桌面级电子书阅读器开发实战指南

摘要:Windows 环境下开发鸿蒙 PC 应用时,采用 Electron 作为核心业务层、鸿蒙 HAP 仅作为壳工程的架构模式,可以充分发挥 Web 技术栈的优势,实现跨平台桌面应用的高效开发。本文以电子书阅读器为实际案例,从零讲解基于 Electron 框架开发鸿蒙 PC 应用的完整流程------项目架构设计、核心模块实现、字体自适应算法、CSS 变量主题系统,以及过程中会遇到的典型问题和解决方案。方法具有通用性,适用于任何桌面级应用的开发。

欢迎加入开源鸿蒙PC社区:https://harmonypc.csdn.net/

欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper

AtomGit 仓库地址:https://atomgit.com/OpenHarmonyPCDeveloper/ohos_ebook_reader_electron

HarmonyOS Electron 环境搭建全流程

华为云官方开源electron开源仓库:👉点击跳转

桌面解压后的文件

  • lib.unstripped:存放未剥离调试符号的完整库文件,用于开发调试、崩溃堆栈分析、性能排查,不能直接打包发布
  • ohos_hap:是鸿蒙应用打包工程目录,集成 Electron 运行库,直接编译可生成.hap格式鸿蒙安装包,用于正式发布安装
  • lib.unstripped目录下的文件

libelectron.so(未剥离版):带完整调试符号、源码行号、函数名的 Electron 主内核库,可精准定位 Chromium 内核、Node.js 层的崩溃堆栈与异常问题

libadapter.so(未剥离版):带调试符号的鸿蒙系统适配桥接库,用于排查 Electron 调用鸿蒙系统 API 时的适配层崩溃、权限、窗口兼容类问题

  • ohos_hap目录下的文件

AppScope:存放鸿蒙应用全局配置、资源文件与权限声明,是应用整体作用域的配置载体

docs:存放 Electron 鸿蒙适配工程的说明文档、适配指南等相关资料

electron:核心业务代码目录,用于放置 Electron 应用的前端页面、脚本等业务逻辑

hvigor:鸿蒙官方构建工具相关目录,负责应用的编译、打包、依赖管理

web_engine:网页引擎配置目录,关联并调用底层 libelectron.solibadapter.so 运行库

.gitignore:定义 Git 版本控制中需要忽略提交的文件和目录规则

build-profile.json5:鸿蒙应用编译配置文件,管控编译模式、依赖、签名等打包核心参数

hvigorfile.ts:应用构建脚本文件,定义打包编译的流程、任务与执行逻辑

oh-package.json5:鸿蒙应用包配置文件,声明应用名称、版本、依赖、权限等基础信息

DevEco Studio中打开ohos_hap目录

so 文件是 Electron 鸿蒙版应用运行的核心依赖不可缺失,已预先集成在 ohos_hap 工程对应的 web_engine 模块路径中,DevEco Studio 打开该目录后会自动识别加载 so 文件,编译打包时自动将其嵌入应用安装包,因此无需开发者手动添加或配置

DevEco Studio调试签名配置(DevEco Studio 开发鸿蒙应用必须配置调试签名才能真机 / 模拟器运行调试,该 Electron 鸿蒙工程也不例外,可使用 IDE 自动生成的调试证书完成快速签名,用于日常编译、调试运行;若要打包发布正式.hap安装包,则需要手动申请官方正式签名证书,调试阶段仅配置自动调试签名即可正常使用,不会影响 so 运行库加载与 Electron 功能调试)

Tip:electron框架暂时不支持模拟器运行

华为官网配置调试签名文档https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ide-signing

DevEco Studio Windows 连接鸿蒙 MateBook Pro 【USB 有线步骤】

  • MateBook Pro 鸿蒙端设置
  1. 打开设置 → 关于本机
  2. 连续点击软件版本 / HarmonyOS 版本 7 次,激活开发者模式
  3. 返回设置,找到开发者选项并进入
  4. 开启:USB 调试、允许调试安装
  • USB 有线连接
  1. 使用支持数据传输的 USB-C 数据线,连接 Windows 电脑与 MateBook Pro
  2. 笔记本弹出 USB 调试授权弹窗,点击允许
  • DevEco 操作
  1. 打开 DevEco Studio,右上角设备下拉列表
  2. 选择已识别的 MateBook Pro 真机设备
  3. 配置必选项:自动签名(File → Project Structure → Signing Configs)勾选自动生成签名,登录华为开发者账号,保存
  • 运行项目
  1. 直接点击运行按钮 或 按下快捷键 Shift+F10
  2. 自动安装、调试、运行应用,全程无需模拟器

运行项目

成功配置环境

Electron 桌面级电子书阅读器开发

电子书阅读器项目概述

本文以 电子书阅读器 为实操示例,选择该应用作为演示载体,核心原因如下:

  • 功能完整,涵盖书架管理、阅读器、设置、文件导入等核心模块,能全面展示 Electron 应用开发的完整流程
  • 技术丰富,涉及字体自适应算法、CSS 变量主题系统、Hash 路由等前端核心技术,集成后可快速验证技术方案的可行性
  • 架构清晰,Electron 主进程与渲染进程的职责划分明确,数据流向清晰易懂,开发者可轻松理解并复用该架构模式

需提前说明的是,本项目的业务逻辑完全基于 Electron + Web 技术栈实现,鸿蒙 HAP 仅作为壳工程提供运行环境。这种架构设计使得业务代码与平台完全解耦,Electron 部分可以在任何平台独立运行,鸿蒙壳仅负责适配和打包。

环境信息
项目 版本/信息
操作系统 Windows 10
核心框架 Electron (Node.js + Chromium)
技术栈 HTML5/CSS3/Vanilla JavaScript
数据存储 LocalStorage (浏览器本地存储)
目标设备 鸿蒙PC
目标架构 arm64-v8a
项目类型 Electron 核心 + 鸿蒙壳工程
文件集成:将 Electron 应用放入 DevEco 项目

讲解拿到 Electron 项目后,需要在 DevEco Studio 项目中创建哪些目录、把文件放在什么位置

DevEco 鸿蒙壳工程的关键目录

首先在 DevEco Studio 中创建一个 Empty Ability 模板项目。创建后的项目结构中,与 Electron 集成相关的关键目录如下:

bash 复制代码
ohos_hap/
├── web_engine/                              ← Web 引擎模块(鸿蒙壳)
│   ├── src/main/resources/resfile/
│   │   └── resources/app/                  ← Electron 应用目录
│   │       ├── main.js                     ← ★ Electron 主进程入口
│   │       └── ebook-reader/               ← 电子书阅读器应用
│   │           ├── index.html              ← ★ 主页面入口
│   │           ├── css/                    ← 样式文件
│   │           │   ├── style.css
│   │           │   └── reader.css
│   │           ├── js/                     ← 业务逻辑
│   │           │   ├── app.js              ← 应用路由与主题管理
│   │           │   ├── storage.js          ← LocalStorage 数据持久化
│   │           │   ├── bookshelf.js        ← 书架管理(10本示例书籍)
│   │           │   ├── reader.js           ← 阅读器与字体自适应
│   │           │   ├── settings.js         ← 设置管理
│   │           │   └── import.js           ← TXT 文件导入
│   │           └── images/                 ← 静态资源
│   └── oh-package.json5                    ← 鸿蒙模块配置
├── electron/                                ← Electron 原生模块
│   ├── libs/arm64-v8a/                     ← 原生库(已预置)
│   │   ├── libelectron.so                  ← Electron 核心库
│   │   ├── libadapter.so                   ← 适配层库
│   │   └── libffmpeg.so                    ← 多媒体库
│   └── build-profile.json5                 ← Electron 构建配置
├── oh_modules/                              ← 全局依赖
├── build-profile.json5                      ← 全局构建配置
└── oh-package.json5                         ← 全局包管理
第一步:放置 Electron 主进程文件
bash 复制代码
web_engine/src/main/resources/resfile/resources/app/
├── main.js                    ← Electron 主进程入口
├── ebook-reader/              ← 电子书阅读器应用目录
│   ├── index.html
│   ├── css/
│   ├── js/
│   └── images/

为什么放这里?

  • web_engine 是 Electron 运行时的载体模块
  • resfile/resources/app/ 是 Electron 应用的约定路径
  • 构建 HAP 时,DevEco 会自动将该目录打包进安装包
  • 运行时 Electron 能正确加载 Web 页面

如果你的 Electron 项目包含 node_modules,也需要一并复制进去。

第二步:检查 Electron 原生库
bash 复制代码
electron/libs/arm64-v8a/
├── libelectron.so          ← Electron 核心库(已预置)
├── libadapter.so           ← 适配层库(已预置)
└── libffmpeg.so            ← 多媒体库(已预置)

为什么这些库已预置?

  • electron/libs/ 是鸿蒙 Electron 工程的约定路径
  • 这些 .so 文件是 Electron 运行的核心依赖,不可缺失
  • DevEco 打开项目后会自动识别加载,编译时自动打包
  • 开发者无需手动添加或配置

操作方式:无需操作,检查文件是否存在即可。

文件集成小结

总共需要放置两类文件:

文件类型 放置位置 作用
Electron 主进程 web_engine/.../app/main.js Electron 核心入口
Electron 应用文件 web_engine/.../app/ebook-reader/ 业务逻辑和UI渲染
Electron 原生库 electron/libs/arm64-v8a/ 运行时核心依赖(已预置)
代码配置:Electron 核心模块解析

文件放好后,无需修改任何鸿蒙原生代码,只需要确保 Electron 应用本身的代码正确即可

main.js ------ Electron 主进程入口

文件位置:web_engine/src/main/resources/resfile/resources/app/main.js

这是整个 Electron 应用的核心入口文件。负责创建和管理应用窗口,处理生命周期事件。

实际项目代码及含义

bash 复制代码
const { app, BrowserWindow, Tray, nativeImage, Menu } = require('electron');
const path = require('path');

let mainWindow, tray;

function createWindow() {
    tray = new Tray(nativeImage.createFromPath(path.join(__dirname, 'electron_white.png')));
    mainWindow = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            nodeIntegration: true,
            contextIsolation: false
        }
    });
    mainWindow.setWindowButtonVisibility(true);
    // 加载本地电子书阅读器应用
    mainWindow.loadURL(`file://${path.join(__dirname, 'ebook-reader', 'index.html')}`);
    
    // 开发模式下可以取消注释下面这行来加载远程页面
    // mainWindow.loadURL('http://localhost:3000');
}
app.whenReady().then(createWindow);

逐行解释为什么要这样配置:

步骤 代码 为什么需要
引入模块 require('electron') 获取 Electron 的 API,包括 Tray(系统托盘)、nativeImage(图标处理)
创建系统托盘 new Tray() 在系统托盘显示应用图标,支持后台运行
创建窗口 new BrowserWindow() 创建应用窗口,配置尺寸 800x600
窗口偏好 webPreferences 启用 nodeIntegration,允许渲染进程使用 Node.js API
加载页面 loadURL() 使用 file:// 协议加载本地 index.html
监听 ready app.whenReady() 确保 Electron 准备就绪后再创建窗口

通用性说明:如果要开发其他类型的 Electron 应用,只需修改窗口尺寸、标题和加载路径,整体结构完全一致。

storage.js ------ LocalStorage 数据持久化

文件位置:web_engine/src/main/resources/resfile/resources/app/ebook-reader/js/storage.js

LocalStorage 是浏览器原生提供的本地存储 API,我们需要封装它来实现:

  1. 书架数据的持久化
  2. 阅读进度的自动保存
  3. 用户设置的持久化

实际项目代码结构

bash 复制代码
// 本地存储管理模块
class StorageManager {
  constructor() {
    this.BOOKS_KEY = 'ebook_books';
    this.SETTINGS_KEY = 'ebook_settings';
    this.READING_PROGRESS_KEY = 'ebook_reading_progress';
  }

  // 保存书籍列表
  saveBooks(books) {
    try {
      localStorage.setItem(this.BOOKS_KEY, JSON.stringify(books));
      console.log('书籍列表保存成功');
    } catch (error) {
      console.error('保存书籍列表失败:', error);
    }
  }

  // 加载书籍列表
  loadBooks() {
    try {
      const booksStr = localStorage.getItem(this.BOOKS_KEY);
      if (booksStr) {
        return JSON.parse(booksStr);
      }
      return [];
    } catch (error) {
      console.error('加载书籍列表失败:', error);
      return [];
    }
  }

  // 保存阅读设置
  saveSettings(settings) {
    try {
      localStorage.setItem(this.SETTINGS_KEY, JSON.stringify(settings));
      console.log('阅读设置保存成功');
    } catch (error) {
      console.error('保存阅读设置失败:', error);
    }
  }

  // 加载阅读设置
  loadSettings() {
    try {
      const settingsStr = localStorage.getItem(this.SETTINGS_KEY);
      if (settingsStr) {
        return JSON.parse(settingsStr);
      }
      // 返回默认设置
      return {
        fontSize: 16,
        theme: 'light',
        lineHeight: 1.6,
        paragraphSpacing: 16,
        autoAdjustFontSize: true,
        baseFontSize: 16
      };
    } catch (error) {
      console.error('加载阅读设置失败:', error);
      return {
        fontSize: 16,
        theme: 'light',
        lineHeight: 1.6,
        paragraphSpacing: 16,
        autoAdjustFontSize: true,
        baseFontSize: 16
      };
    }
  }

  // 保存阅读进度
  saveReadingProgress(bookId, progress) {
    try {
      let progressData = {};
      const progressStr = localStorage.getItem(this.READING_PROGRESS_KEY);
      if (progressStr) {
        progressData = JSON.parse(progressStr);
      }
      progressData[bookId] = progress;
      localStorage.setItem(this.READING_PROGRESS_KEY, JSON.stringify(progressData));
      console.log('阅读进度保存成功');
    } catch (error) {
      console.error('保存阅读进度失败:', error);
    }
  }

  // 加载阅读进度
  loadReadingProgress(bookId) {
    try {
      const progressStr = localStorage.getItem(this.READING_PROGRESS_KEY);
      if (progressStr) {
        const progressData = JSON.parse(progressStr);
        return progressData[bookId] || { chapterIndex: 0, scrollPosition: 0 };
      }
      return { chapterIndex: 0, scrollPosition: 0 };
    } catch (error) {
      console.error('加载阅读进度失败:', error);
      return { chapterIndex: 0, scrollPosition: 0 };
    }
  }

  // 添加书籍
  addBook(book) {
    const books = this.loadBooks();
    books.push(book);
    this.saveBooks(books);
  }

  // 更新书籍
  updateBook(bookId, updates) {
    const books = this.loadBooks();
    const index = books.findIndex(b => b.id === bookId);
    if (index !== -1) {
      books[index] = { ...books[index], ...updates };
      this.saveBooks(books);
    }
  }

  // 删除书籍
  deleteBook(bookId) {
    const books = this.loadBooks();
    const filteredBooks = books.filter(b => b.id !== bookId);
    this.saveBooks(filteredBooks);
  }

  // 获取书籍(包括用户导入和示例书籍)
  getBook(bookId) {
    // 先从 localStorage 中查找
    const books = this.loadBooks();
    const userBook = books.find(b => b.id === bookId);
    if (userBook) {
      return userBook;
    }
    
    // 如果没找到,尝试从示例书籍中查找
    if (bookshelfPage && bookshelfPage.sampleBooks) {
      const sampleBook = bookshelfPage.sampleBooks.find(b => b.id === bookId);
      if (sampleBook) {
        return sampleBook;
      }
    }
    
    return null;
  }
}

// 创建全局实例
const storage = new StorageManager();

LocalStorage 开发要点总结

概念 说明
localStorage.setItem 保存数据,接受键和值(字符串)
localStorage.getItem 读取数据,返回字符串或 null
localStorage.removeItem 删除单个键值对
JSON.stringify 将对象转为 JSON 字符串,用于存储
JSON.parse 将 JSON 字符串转为对象,用于读取
错误处理 所有操作都需要 try-catch,防止存储空间满或权限问题
默认值 loadSettings() 返回完整的默认配置,包括 fontSize、theme、lineHeight 等
bookshelf.js ------ 书架数据管理

文件位置:web_engine/src/main/resources/resfile/resources/app/ebook-reader/js/bookshelf.js

这个文件负责书架页面的数据管理和 UI 渲染,包含10本精选示例书籍的完整内容(每本10章)。实际项目使用对象****字面量而非类实现。

实际项目代码结构:

bash 复制代码
// 书架管理模块
const bookshelfPage = {
  // 示例书籍数据(10本不同类型的书籍)
  sampleBooks: [
    {
      id: 'sample1',
      title: '百年孤独',
      author: '加西亚·马尔克斯',
      cover: '#667eea',
      progress: 35,
      lastRead: '2026-05-20',
      chapters: [
        { title: '第一章:马孔多的建立', content: '多年以后,面对行刑队,奥雷里亚诺·布恩迪亚上校将会回想起父亲带他去见识冰块的那个遥远的下午。那时的马孔多是一个二十户人家的村落,泥巴和芦苇盖成的屋子沿河岸排开,湍急的河水清澈见底,河床里卵石洁白光滑宛如史前巨蛋。\n\n世界新生伊始,许多事物还没有名字,提到的时候尚需用手指指点点。每年三月前后,一家衣衫褴褛的吉卜赛人都会来到村边扎下帐篷,击鼓鸣笛,在喧闹中介绍新近的发明。他们先带去了磁铁。一个身材高大的吉卜赛人,自称梅尔基亚德斯,留着蓬乱的胡须,手指纤细如鸟爪。他向村民们展示了磁铁的神奇力量,能够从远处吸引金属物品。\n\n何塞·阿尔卡蒂奥·布恩迪亚对此着迷,他用磁铁交换了自己的几只山羊和一头猪。接下来,吉卜赛人又带来了望远镜、放大镜和假牙等新奇物品。每一次,何塞·阿尔卡蒂奥都会用家中的物品去交换,试图理解这些科学奇迹。\n\n然而,最让他震撼的是梅尔基亚德斯带来的炼金实验室。在那里,他看到了金属的熔化、颜色的变化,以及物质本质的转化。这个充满好奇心的男人,注定要带领他的家族走向一段传奇而孤独的旅程。\n\n何塞·阿尔卡蒂奥的妻子乌尔苏拉是个勤劳能千的女人。她日夜操持家务,饲养牲畜,种植木薯和玉米。她常常抱怨丈夫沉迷于那些不切实际的幻想,把家产都败光了。但她不知道,丈夫的心里装着整个世界的秘密。\n\n"这个世界太新了,很多东西还没有名字,要叫它们还得用手指头 pointing。"何塞·阿尔卡蒂奥常常这样说。他的眼睛里闪烁着狂热的光芒,仿佛看到了一个全新的世界正在眼前展开。\n\n马孔多的居民们过着平静而简单的生活。他们日出而作,日落而息,遵循着祖辈传下来的生活方式。但何塞·阿尔卡蒂奥的到来,打破了这种平静。他带来了外界的消息,带来了新奇的想法,也带来了无法预知的命运。\n\n梅尔基亚德斯每次来都会在村子里引起轰动。孩子们围着他的帐篷奔跑,大人们则用敬畏的眼神看着那些神奇的物品。有人说他是魔鬼的使者,有人说他是上帝的信使。但何塞·阿尔卡蒂奥知道,他只是一个探索者,和他们一样渴望理解这个世界的奥秘。\n\n在那个遥远的下午,当何塞·阿尔卡蒂奥第一次看到冰块时,他激动得流下了眼泪。"这是我们这个时代最伟大的发明!"他大声喊道。村民们围过来,小心翼翼地触摸着这块透明的、冰冷的、神奇的物质。他们无法想象,水竟然可以变成固体,可以像石头一样坚硬。\n\n这就是马孔多的开端,一个充满奇迹和幻想的小村庄。没有人能够预见,这个小小的村落将会经历怎样的兴衰,这个家族将会遭遇怎样的命运。一切都还刚刚开始,而结局早已在梅尔基亚德斯的手稿中写好。' },
        { title: '第二章:失眠症的蔓延', content: '当何塞·阿尔卡蒂奥·布恩迪亚还在为吉卜赛人的发明而着迷时,一场可怕的灾难悄然降临马孔多。失眠症像一阵风一样席卷了整个村庄。起初,人们只是难以入睡,随后开始遗忘事物的名称和用途。\n\n奥雷里亚诺·布恩迪亚发明了给物品贴标签的方法,试图抵抗遗忘。他们在桌子上贴上"桌子",在门上贴上"门",在牛身上贴上"牛,每天要挤奶"。这种方法最初似乎奏效了,人们看到标签就能想起事物的用途。\n\n但这种方法只能治标不能治本。随着病情加重,人们连标签上的文字也开始遗忘。更可怕的是,失眠症伴随着记忆的丧失。人们开始忘记自己的过去,忘记亲人的面孔,甚至忘记自己是谁。\n\n丽贝卡是最先感染失眠症的人之一。她是从远方来的孤儿,带着一个帆布包,里面装着她父母的骨殖。她有一种吃泥土和墙皮怪癖,夜深人静时会发出奇怪的哭声。现在,她连自己是谁都忘记了。\n\n乌尔苏拉努力维持着家庭的秩序。她每天重复着同样的工作,试图通过习惯来抵抗遗忘。但她也发现,自己的记忆越来越模糊。她开始忘记丈夫的名字,忘记孩子们的面孔,忘记自己已经做过的菜。\n\n梅尔基亚德斯再次出现,带来了一种能够治愈失眠症的药水。那是一种带有苦味的液体,喝下去后会让人感到极度的疲倦。"喝了这个,你们就能睡着了。"他说。但为时已晚,整个马孔多已经陷入了集体的记忆丧失。\n\n村子里的老人们坐在阳光下,试图回忆起年轻时的事情,但脑海中只有一片空白。孩子们不再认识自己的父母,恋人不再记得彼此的名字。整个村庄变成了一座记忆的坟墓,埋葬着所有人的过去。\n\n何塞·阿尔卡蒂奥把自己关在炼金实验室里,疯狂地书写着笔记。他试图把自己知道的一切都记录下来,以防自己也忘记。但他的字迹越来越潦草,思路越来越混乱。最终,他也无法逃脱遗忘的命运。\n\n这场灾难让布恩迪亚家族意识到,记忆是人类最宝贵的财富。没有了记忆,人就失去了身份,失去了历史,失去了一切。当梅尔基亚德斯的药水终于发挥作用时,马孔多的居民们沉沉睡去,在梦中重新找回了失去的记忆。\n\n但当他们醒来时,发现世界已经变了。失眠症虽然治愈了,但有些东西永远失去了。那些被遗忘的事情,就像从未存在过一样。马孔多的人们学会了珍惜记忆,但他们不知道,更大的灾难还在后面等待着他们。' },
        { title: '第三章:战争的阴影', content: '奥雷里亚诺·布恩迪亚上校发动了三十二次武装起义,三十二次都遭到了失败。他跟十七个女人在短期内生了十七个儿子,这些儿子都在一夜之间相继遭到暗杀。他躲过了十四次暗杀、七十三次埋伏和一次枪决。\n\n战争改变了他,从一个安静的炼金术士变成了冷酷的军事领袖。他忘记了为什么而战,只知道必须战斗。他的父亲曾告诫他:"你太骄傲了,这会让你毁灭。"但他听不进去,被权力的幻觉所蒙蔽。\n\n年轻的奥雷里亚诺曾经是个温和的年轻人。他在父亲的炼金实验室里度过了大部分时光,学习如何制作小金鱼,如何观察金属的变化。他对政治毫无兴趣,只想安静地做自己的事情。\n\n但命运把他推向了战争。当他看到保守党的不公,看到穷人被压迫,他心中的正义感被唤醒了。他组织了一支军队,开始了漫长的战斗。他的军事天赋很快显现出来,他能够在最不可能的情况下找到胜利的机会。\n\n然而,战争也改变了他。他变得冷酷无情,签署了处决自己朋友的命令。他曾经深爱着雷梅黛丝,但她的死让他彻底失去了感受爱的能力。他在战争的泥潭中越陷越深,忘记了最初的目标。\n\n"我不是为了自由而战,"他曾经说,"我只是为了骄傲而战。"这句话揭示了他内心深处的真相。战争已经不是关于理想,而是关于个人的尊严和骄傲。\n\n在战争的岁月中,他失去了所有的朋友和亲人,只剩下无尽的孤独。最终,他签署了停战协议,回到了马孔多。他拒绝了政府授予的荣誉和权力,选择回到那个小小的炼金实验室。\n\n他在炼金实验室中度过了余生,制作小金鱼,然后熔化,再制作,周而复始,直到生命的尽头。他说:"战争已经结束了,但我的战争才刚刚开始。"他的战争,是与自己的孤独和绝望的战争。\n\n多年后,当他面对行刑队时,他可能会回想起那些战争岁月,回想起那些死去的朋友和儿子。但他已经不再悲伤,因为他知道,这就是布恩迪亚家族的命运------在孤独中战斗,在战斗中孤独。' },
        { title: '第四章:美丽的蕾梅黛丝', content: '美人儿蕾梅黛丝是布恩迪亚家族中最不可思议的存在。她拥有令人窒息的美貌,却对世俗的一切毫无兴趣。\n\n她天真无邪,如同伊甸园中的夏娃,不懂得掩饰和伪装。她裸体在河中沐浴,毫不在意旁人的目光。\n\n追求她的人都遭遇了不幸。有人说她是被诅咒的,有人说她是超凡脱俗的存在。\n\n她无法理解世间的复杂和虚伪,最终在一个下午,抓着白色的床单升上了天空,永远离开了这个喧嚣的世界。\n\n她的离去让所有人都感到震惊,但也许这是她最好的归宿------一个不属于凡间的人,终究要回到天上。' },
        { title: '第五章:家族的衰落', content: '布恩迪亚家族的第六代人奥雷里亚诺第二,是这个家族最后繁荣的见证者。\n\n他是个挥霍无度的人,每天举办盛大的宴会,邀请全镇的人参加。他的牲畜繁殖得惊人,土地广阔无边。\n\n但他的繁荣是短暂的。一场持续了四年十一个月零两天的大雨摧毁了一切。\n\n雨水淹没了土地,牲畜死去,财富消散。奥雷里亚诺第二在贫困中死去,他的家族也随之走向终结。\n\n最后一代奥雷里亚诺在破译吉卜赛人梅尔基亚德斯的手稿时,发现了家族命运的密码。' },
        { title: '第六章:时间的循环', content: '在马孔多,时间似乎不是线性的,而是循环的。历史在不断重复,命运在轮回中上演。\n\n奥雷里亚诺们都是孤独的,阿尔卡蒂奥们都充满冲动。名字在重复,性格在重复,命运也在重复。\n\n何塞·阿尔卡蒂奥·布恩迪亚被绑在栗树下死去,而多年后,另一个奥雷里亚诺也在同一棵树下度过了余生。\n\n这种循环让人绝望,仿佛整个家族都被困在了一个无法逃脱的时间牢笼中。\n\n梅尔基亚德斯的手稿早已预言了一切:"家族的第一个人被捆在树上,最后一个人正被蚂蚁吃掉。"' },
        { title: '第七章:爱情的幻灭', content: '费尔南达·德尔·卡皮奥是奥雷里亚诺第二的妻子,一个来自没落贵族家庭的女人。\n\n她带着严苛的礼仪和宗教信仰嫁入布恩迪亚家族,试图改变这个家族的放纵和混乱。但她失败了。\n\n她的努力如同在沙地上建城堡,一切都是徒劳。她终生都在等待一封永远不会到来的信。\n\n她的悲剧在于,她试图用规矩和秩序来约束一个注定要毁灭的家族,却不知道自己也是这个悲剧的一部分。\n\n爱情在马孔多总是以幻灭告终。每一段感情都充满了激情,但最终都归于孤独和绝望。' },
        { title: '第八章:吉卜赛人的秘密', content: '梅尔基亚德斯是马孔多的见证者和记录者。他是吉卜赛人,却拥有超越时代的智慧。\n\n他发明了炼金术,预见了未来,写下了布恩迪亚家族百年历史的密码。他死了一次,但因为无法忍受孤独而复活。\n\n他继续在马孔多游荡,记录着一切。他的手稿用梵文写成,包含了过去、现在和未来的所有秘密。\n\n只有家族的最后一个人能够破译。当他预言的一切成为现实时,手稿也随之消失,如同马孔多一样,从未存在过。' },
        { title: '第九章:孤独的遗产', content: '布恩迪亚家族的每个人都被孤独所困扰。这种孤独不是物理上的孤立,而是灵魂深处的隔绝。\n\n何塞·阿尔卡蒂奥·布恩迪亚在疯狂中孤独,奥雷里亚诺上校在战争中孤独,阿玛兰妲在爱情中孤独。\n\n他们每个人都试图找到对抗孤独的方法,但都失败了。炼金术、战争、爱情、权力,一切都是徒劳。\n\n加西亚·马尔克斯通过这个故事告诉我们:孤独是人类无法逃避的命运,而爱可能是唯一的救赎。\n\n但布恩迪亚家族不懂得如何去爱,他们只会索取、占有、伤害,最终失去一切。' },
        { title: '第十章:马孔多的消失', content: '当奥雷里亚诺·巴比伦终于破译出梅尔基亚德斯手稿的最后一页时,飓风已经席卷了马孔多。\n\n"镜面之城"马孔多,这个被香蕉公司剥削过、被大雨淹没过、被内战摧残过的小镇,即将从地球上一扫而光。\n\n奥雷里亚诺看到了自己的命运:他正在阅读自己的死亡预言。当他读到手稿结尾的那一刻,他将被飓风带走。\n\n"因为注定经受百年孤独的家族不会有第二次机会在大地上出现。"\n\n随着最后一行文字被读懂,马孔多在飓风中化为灰烬,布恩迪亚家族的百年历史成为永恒的传说。' }
      ]
    },
    {
      id: 'sample2',
      title: '三体',
      author: '刘慈欣',
      cover: '#764ba2',
      progress: 60,
      lastRead: '2026-05-18',
      chapters: [
        { title: '第一章:科学边界', content: '汪淼觉得,来找他的这四个人是一个奇怪的组合:两名警察和两名军人。如果那两个军人是武警还算正常,但这是两名陆军军官。\n\n汪淼第一眼就对来找他的警察没有好感。其实那名穿警服的年轻人还行,举止很有礼貌,但那位便衣就让人讨厌了。这人长得五大三粗,一脸横肉,穿着件脏兮兮的皮夹克,浑身烟味,说话粗声大嗓,是最令汪淼反感和警惕的那类人。\n\n"汪教授,我们需要你的帮助。"那个便衣直截了当地说,"你最近的研究,可能涉及到一些你不了解的事情。"\n\n汪淼是纳米材料专家,他的研究怎么会和警察、军人扯上关系?他感到困惑和不安。他的实验室在清华大学,主要研究纳米飞刃,一种直径只有头发丝十分之一的超细材料。这项技术可以用于制造更轻更强的材料,在航天、医疗等领域有广泛应用。\n\n"最近,科学界发生了一些奇怪的事情。"警察开始解释,"很多科学家都声称看到了一个倒计时,出现在他们的视野中。"\n\n汪淼皱起眉头。倒计时?这是什么意思?他的第一反应是这些人可能精神出了问题。科学研究压力大,导致幻觉,这种事情并不罕见。\n\n"不是幻觉。"那个年轻的警察似乎看穿了他的想法,"我们调查了很多案例,包括国内外的科学家。他们都看到了同样的东西------一个精确到毫秒的倒计时,悬浮在视野中,无法摆脱。"\n\n汪淼感到一阵寒意。这听起来太不可思议了。科学是理性的,是可以用逻辑和实验验证的。这种超自然现象不应该存在。但他不知道的是,他的世界观即将被彻底颠覆。\n\n"我们希望你能参加一个会议。"陆军军官终于开口了,"在会上,你会了解更多的情况。这关系到整个科学界的未来,甚至......人类的未来。"\n\n汪淼犹豫了。他是个纯粹的科学家,不想卷入这些复杂的事情。但好奇心最终战胜了一切。他答应了他们的请求,却不知道这个决定会把他带向怎样的深渊。\n\n那天晚上,汪淼在实验室里工作时,第一次看到了倒计时。它出现在视野的右上角,精确到毫秒,永不停息。他揉了揉眼睛,倒计时还在。他闭上眼睛,倒计时依然在黑暗中闪烁。\n\n汪淼感到恐惧,但也感到好奇。这个倒计时到底意味着什么?是谁在向他们传递这个信息?他决定继续研究,即使这可能带来危险。因为他知道,这可能关系到整个人类文明的命运。' },
        { title: '第二章:倒计时', content: '汪淼开始在他眼中看到那个倒计时。它悬浮在视野的右上角,精确到毫秒,永不停息。\n\n他去看医生,医生说他眼睛没问题。他去检查大脑,结果也正常。但倒计时就在那里,挥之不去。\n\n更奇怪的是,他发现这个倒计时似乎与某种宇宙现象有关。当他进行纳米材料实验时,倒计时的速度会发生变化。\n\n他开始研究这个问题,发现越来越多的科学家都在经历同样的事情。有人崩溃了,有人疯了,有人自杀了。\n\n汪淼感到恐惧,但也感到好奇。这个倒计时到底意味着什么?是谁在向他们传递这个信息?\n\n他决定继续研究,即使这可能带来危险。因为他知道,这可能关系到整个人类文明的命运。' },
        { title: '第三章:三体游戏', content: '汪淼被邀请参加一个名为"三体"的虚拟现实游戏。这个游戏模拟了一个拥有三个太阳的 alien 行星。\n\n在这个世界中,三个太阳的运动是完全不可预测的。有时候,三个太阳同时升起,行星被烤成焦土;有时候,三个太阳同时落下,行星陷入永恒的严寒。\n\n游戏中的文明经历了数百次毁灭和重生。他们发展出各种方式来应对这种恶劣环境,但都无法长久存活。\n\n汪淼在游戏中看到了人类文明可能的未来。如果三体人真的存在,并且他们的行星真的面临毁灭,他们会怎么做?\n\n答案令人恐惧:他们会寻找新的家园,而地球,就是最近的目标。\n\n这个游戏不是娱乐,而是一个警告。三体人正在向地球进发,人类必须在他们到达之前做好准备。' },
        { title: '第四章:红岸基地', content: '通过游戏,汪淼了解到了一个名为"红岸基地"的秘密军事设施。这个基地在几十年前就开始了与外星文明的联系。\n\n红岸基地的科学家们发现了一个遥远的星系,那里有三颗恒星,以及围绕它们运行的行星。他们开始向那个星系发送信号。\n\n几年后,他们收到了回复。回复的内容令人震惊:三体人收到了地球的信号,并且决定向地球进发。\n\n这个发现被严格保密,但现在已经无法隐瞒。三体舰队正在向地球航行,大约需要四百五十年才能到达。\n\n四百五十年,对人类来说很长,但对宇宙来说只是一瞬间。人类必须在这之前找到应对的方法。\n\n汪淼意识到,他看到的倒计时,可能就是三体人发送的某种信号。' },
        { title: '第五章:智子', content: '三体人向地球发送了一种名为"智子"的超级智能粒子。这些粒子可以干扰人类的科学研究,锁死人类的科技 발전。\n\n智子可以潜入任何实验室,干扰任何实验结果。它们可以让粒子加速器的数据变得毫无意义,让物理学家们无法获得正确的实验结果。\n\n这就是为什么科学家们都看到了倒计时。那是智子在向他们展示自己的力量,告诉他们:不要再挣扎了,你们的科学已经被我们锁死了。\n\n汪淼感到绝望。如果人类的科学被锁死,四百年后的三体舰队到达时,人类将毫无还手之力。\n\n但也有人不服输。他们相信,即使科学被锁死,人类仍然可以找到其他的应对方法。\n\n战争,不仅仅是科技的较量,更是意志的较量。' },
        { title: '第六章:面壁者', content: '联合国启动了"面壁计划"。他们选择了四个人,赋予他们几乎无限的资源,让他们制定对抗三体人的计划。\n\n这四个面壁者中,有一个是汪淼认识的人。他看起来普普通通,但面壁者的身份意味着他必须隐藏自己的真实想法。\n\n三体人可以通过智子监视地球上的一切,但他们无法读取人类的思维。所以面壁者的计划必须是完全保密的,甚至连自己都不能完全理解。\n\n汪淼被卷入了这个计划中。他的纳米材料技术可能是关键。他必须在恐惧和责任之间做出选择。\n\n他知道,无论选择什么,人类的命运都已经改变了。从收到三体信号的那一刻起,人类就再也无法回到从前。' },
        { title: '第七章:黑暗森林', content: '面壁者之一罗辑发现了一个宇宙的秘密:黑暗森林法则。\n\n宇宙就像一座黑暗森林,每个文明都是带枪的猎人。当一个猎人发现另一个猎人时,他只有一个选择:开枪。\n\n因为如果他不开枪,对方可能会先开枪。在宇宙中,善意是奢侈的,猜疑链是永恒的。\n\n这个法则解释了为什么宇宙如此寂静。不是因为没有其他文明,而是因为所有文明都在隐藏自己,害怕被发现。\n\n但三体人已经暴露了地球的位置。其他更高级的文明可能会在到达三体舰队之前,先将地球摧毁。\n\n人类面临的威胁不止一个,而是整个宇宙。' },
        { title: '第八章:威慑', content: '罗辑利用黑暗森林法则,建立了对三体世界的威慑。他向宇宙广播了三体星系的位置,威胁说如果三体人进攻地球,他会让更多的文明知道这个位置。\n\n三体人害怕了。他们停止了舰队的前进,开始与人类谈判。\n\n罗辑成为了执剑人,手持威慑的开关。只要他活着,三体人就不敢轻举妄动。\n\n但这个威慑是脆弱的。它依赖于一个人的意志,依赖于一个人类的决心。\n\n汪淼看着这一切,心中充满 uncertainty。威慑能维持多久?人类真的安全了吗?\n\n他知道,答案是否定的。这只是暴风雨前的宁静。' },
        { title: '第九章:执剑人', content: '罗辑担任执剑人五十四年。在这五十四年里,人类和三体世界维持着脆弱的和平。\n\n但人类开始忘记恐惧。新一代的人类没有经历过末日危机,他们认为三体人不再是威胁。\n\n他们要求更换执剑人。罗辑太老了,太危险了。他们想要一个更温和、更可靠的人。\n\n汪淼看着新执剑人上任,心中充满不安。他知道,三体人正在等待这个机会。\n\n果然,新执剑人上任的几分钟内,三体人就发动了攻击。新执剑人没有按下威慑开关。\n\n威慑失败了。人类的末日,终于来临。' },
        { title: '第十章:最后的希望', content: '在最后的时刻,人类终于明白了罗辑的伟大。他一个人守护了人类五十四年,而人类却抛弃了他。\n\n但即使威慑失败,人类也没有放弃。他们启动了最后的计划:向宇宙广播三体星系的位置。\n\n这个计划成功了。三体星系被其他高级文明发现,并被摧毁。三体人的舰队失去了家园,成为了流浪者。\n\n但地球也暴露了位置。人类的命运,仍然未卜。\n\n汪淼站在海岸上,看着星空。他知道,宇宙中还有其他威胁,还有其他猎人。\n\n但人类已经证明了自己的勇气和决心。无论未来如何,人类都不会轻易屈服。\n\n这就是人类的希望。' }
      ]
    },
    {
      id: 'sample3',
      title: '小王子',
      author: '安托万·德·圣-埃克苏佩里',
      cover: '#4facfe',
      progress: 80,
      lastRead: '2026-05-22',
      chapters: [
        { title: '第一章:画蟒蛇', content: '当我还只有六岁的时候,在一本描写原始森林、名叫《热带丛林中的故事》的书中,看到了一幅精彩的插图,画的是一条蟒蛇正在吞食一只猛兽。这本书中写道:"蟒蛇把猎物囫囵吞下,不嚼碎。它们随后就无法动弹,需要足足六个月来睡觉消化。"\n\n我对此想了很多,于是用彩色铅笔画出了我的第一幅画。我的画一号作品是这样的:我把那条蟒蛇消化大象的情景画了下来。我用了我全部的心思,画了好几个小时。\n\n我把我的杰作拿给大人看,问他们我的画是不是叫他们害怕。他们回答:"一顶帽子有什么可怕的?"\n\n我画的不是帽子,是一条巨蟒在消化一头大象。于是我又画了第二幅画:我把蟒蛇肚子里的情况画了出来,以便让那些大人能看懂。他们总是需要解释。\n\n大人们劝我把这些画着肚皮开或肚皮的巨蟒的画放到一边,把兴趣放在地理、历史、算术和语法上。就这样,在六岁的那年,我就放弃了当画家这一美好的职业。\n\n我的第一幅画和第二幅画都失败了。大人们什么也看不懂,总要为他们解释。这对一个孩子来说,是多么令人沮丧的事情啊。\n\n所以我选择了另一个职业,学会了开飞机。我几乎飞遍了全世界。而地理确实帮了我大忙。我一眼就能分辨出中国和美国亚利桑那州。要是夜里迷失了航向,那是很有用的。\n\n这样,在生活中,我跟很多很多重要的人有过很多的接触。我在大人们中间生活过很长时间。我仔细观察过他们。但这并没有改善我对他们的看法。\n\n要是我碰到一个人,他看起来稍微清爽一点,我就拿出我一直保存着的第一号作品给他看。我想试试看,他是不是真的有理解力。但是,不管是谁,我总是得到这样的回答:"这是一顶帽子。"\n\n于是,我再也不跟他说什么蟒蛇、原始森林、星星了。我只跟他谈他能理解的事情。桥牌、高尔夫球、政治、领带。于是,大人很高兴,他认识了一个通情达理的人。\n\n我孤独地生活着,没有一个真正谈得来的人,直到六年前,我的飞机出了故障,迫降在撒哈拉沙漠。那就是我遇见小王子的地方。' },
        { title: '第二章:沙漠相遇', content: '我开着飞机在撒哈拉沙漠上空飞行,突然引擎出了故障。我被迫降落在沙漠中央,身边没有任何帮助。\n\n我带的水只够喝一个星期了。第一晚,我躺在沙地上睡着了。第二天早上,一个奇怪的小声音把我叫醒。\n\n"请你......给我画一只羊!"\n\n我跳了起来,看见一个非常特别的小人儿站在那儿。他正严肃地打量着我。\n\n这就是小王子,我从没见过这么奇怪又这么可爱的小人儿。他有着一头金色的头发,一双清澈的眼睛。\n\n我拿出纸和笔,给他画了一只羊。他看了很久,说:"这只羊太弱了,给我画另一只。"\n\n就这样,我们成为了朋友。' },
        { title: '第三章:来自B612', content: '慢慢地,我了解到小王子来自一个非常小的星球,编号是B612。\n\n他的星球只有一栋房子那么大,上面有三座火山,两座活的,一座死的。还有一朵他深爱的玫瑰花。\n\n小王子每天都要清理火山,拔除猴面包树的幼苗。如果不及早拔除,猴面包树会长满整个星球,把星球撑破。\n\n"我有一朵花,"小王子告诉我,"我每天给她浇水,给她盖上玻璃罩,给她挡风。"\n\n"她一定很特别。"我说。\n\n"当然,"小王子说,"她是世界上独一无二的玫瑰。"\n\n我从小王子的话中感受到,他已经深深地爱上了那朵玫瑰。' },
        { title: '第四章:旅行开始', content: '小王子告诉我,他是如何离开自己的星球,开始旅行的。\n\n"那天,我和玫瑰吵架了。"小王子说,"她说她很怕冷,需要玻璃罩。她还说有四根刺可以对付老虎。"\n\n"可是你的星球上没有老虎。"我说。\n\n"我知道,"小王子说,"但玫瑰需要被需要。她想要我照顾她。"\n\n小王子决定离开,去看看外面的世界。玫瑰没有挽留他,只是说:"我爱你,你一点也不了解我。这也许是我的错。但这些都不要紧了。"\n\n"祝你幸福。"玫瑰说。\n\n小王子离开时,回头看了玫瑰一眼,但他没有停下脚步。他后来告诉我,那时候他太年轻,还不懂得如何去爱。' },
        { title: '第五章:国王的星球', content: '小王子访问的第一个星球上住着一位国王。国王穿着紫红色的长袍,坐在一个简单却威严的宝座上。\n\n"啊,来了一个臣民!"国王看到小王子时喊道。\n\n小王子很奇怪:"你怎么会认识我呢?我以前从没来过这里。"\n\n"所有的人都是我的臣民。"国王说,"我是宇宙的国王,所有的星星都服从我的命令。"\n\n"那你能命令太阳下山吗?"小王子问。\n\n"当然,"国王说,"但我会等待合适的时机。我命令太阳在傍晚下山,因为那是最合适的时候。"\n\n小王子继续他的旅行。他心想:"大人们真是奇怪。"' },
        { title: '第六章:虚荣的人', content: '第二个星球上住着一个虚荣的人。他戴着帽子,一看到小王子就举起帽子致意。\n\n"这是一个崇拜我的人!"虚荣的人心想。\n\n"你好,"小王子说,"你的帽子很奇怪。"\n\n"这是用来致意的,"虚荣的人说,"当人们向我欢呼时,我就举起帽子致意。可是从来没有人经过这里。"\n\n"那我向你欢呼。"小王子拍起手来。\n\n虚荣的人举起帽子致意,心里很高兴。\n\n"你能一直崇拜我吗?"他问。\n\n"为什么?"小王子问,"这有什么意思呢?"\n\n小王子离开了这个星球,心想:"大人们真是奇怪。"' },
        { title: '第七章:酒鬼的星球', content: '第三个星球上住着一个酒鬼。小王子发现他坐在一堆空瓶子和一堆满瓶子中间。\n\n"你在干什么?"小王子问。\n\n"我在喝酒。"酒鬼悲伤地说。\n\n"你为什么喝酒?"小王子问。\n\n"为了忘记。"酒鬼说。\n\n"忘记什么?"小王子已经有些同情他了。\n\n"忘记我的羞愧。"酒鬼垂下头。\n\n"羞愧什么?"小王子想帮助他。\n\n"羞愧我喝酒!"酒鬼说完就陷入了沉默。\n\n小王子困惑地离开了。他想:"大人们真是非常奇怪。"' },
        { title: '第八章:商人的星球', content: '第四个星球上住着一个商人。他忙得连抬头的时间都没有。\n\n"你好,"小王子说,"你的星球很奇怪。"\n\n"我在数星星,"商人说,"我已经数了五亿一百六十二万二千七百三十一颗星星。"\n\n"你拿这些星星做什么?"小王子问。\n\n"我拥有它们。"商人说。\n\n"你拥有星星做什么?"\n\n"这样我就富有了。"\n\n"你富有做什么?"\n\n"如果有人发现了新的星星,我就可以买下来。"\n\n小王子想:"我有一朵玫瑰,我每天给她浇水。我拥有火山,我每周打扫它们。我对我的拥有物有益处。但你对星星没有益处。"\n\n小王子离开了,心想:"大人们真是奇怪。"' },
        { title: '第九章:点灯人', content: '第五个星球非常小,只能容下一盏路灯和一个点灯人。\n\n"你好,"小王子说,"你为什么把路灯熄灭?"\n\n"这是命令。"点灯人说,"早上好。"然后他又点亮了路灯。\n\n"你为什么又点亮它?"\n\n"这是命令。"\n\n"我不明白。"小王子说。\n\n"不需要明白,"点灯人说,"命令就是命令。早上好。"然后他又熄灭了路灯。\n\n"这个星球一分钟转一圈,"点灯人说,"所以我每分钟都要点亮和熄灭路灯。我没有一秒钟休息。"\n\n小王子觉得这个人至少不那么可笑。因为他关心的是别的事情,而不是自己。\n\n"我想认识一个这样的人,"小王子心想,"他的工作是有意义的。"' },
        { title: '第十章:来到地球', content: '第六个星球是一个巨大的星球,上面住着一个地理学家。\n\n"你好,"小王子说,"你的星球真大。"\n\n"我是一个地理学家,"地理学家说,"我记录永恒的东西。我记录山脉、河流、海洋的位置。"\n\n"那花呢?"小王子问。\n\n"我们不记录花,"地理学家说,"花是短暂的。"\n\n"我的花是短暂的?"小王子心想,"她只有四根刺来保护自己对抗世界!而我却把她独自留在家里!"\n\n地理学家建议小王子去访问地球。\n\n小王子来到了地球,降落在撒哈拉沙漠。他看到了满园的玫瑰,意识到他的玫瑰并不是独一无二的。\n\n"我以为我拥有一朵独一无二的花,"小王子哭泣着,"但那只是一朵普通的玫瑰。"\n\n这时,狐狸出现了,告诉了他一个秘密:"真正重要的东西,用眼睛是看不见的。"' }
      ]
    },
    {
      id: 'sample4',
      title: '活着',
      author: '余华',
      cover: '#f093fb',
      progress: 45,
      lastRead: '2026-05-19',
      chapters: [
        { title: '第一章:少年时代', content: '我比现在年轻十岁的时候,获得了一个游手好闲的职业,去乡间收集民间歌谣。那一年的整个夏天,我如同一只乱飞的麻雀,游荡在知了和阳光充斥的农村。\n\n我喜欢在农舍前的大树下歇脚,和那些干完活回家的农民聊天。他们总是热情地给我端来一碗凉水,然后坐在旁边,给我讲他们年轻时的故事。\n\n有一天,我遇到了一位名叫福贵的老人。他坐在一棵老槐树下,手里拿着一把破旧的蒲扇。他的皮肤黝黑,脸上的皱纹像刀刻一般深。但他的眼睛却异常明亮,透露出一种经历过沧桑后的平静。\n\n"我给你唱首歌吧,"他说,"这是我年轻时候最喜欢唱的。"\n\n他的歌声苍凉而悠远,唱的是一个关于活着的故事。歌声里有苦难,有悲伤,但更多的是一种顽强的生命力。\n\n福贵告诉我,他年轻的时候是个阔少爷,家里有上百亩地,住着大瓦房,吃着白米饭。他整天游手好闲,不务正业,是个十足的败家子。\n\n"那时候我多荒唐啊,"福贵说,"我去赌场赌博,去妓院嫖娼,把家产败得一干二净。我父亲被我气死了,我妻子也被我赶回了娘家。"\n\n我静静地听着,不敢插话。福贵的声音很平静,像是在讲别人的故事。\n\n"后来我改了,"福贵继续说,"我开始种地,开始好好对待我的妻子家珍。我们过上了穷日子,但很踏实。我们有了两个孩子,一个儿子叫有庆,一个女儿叫凤霞。"\n\n"那后来呢?"我问。\n\n福贵沉默了一会儿,然后说:"后来啊,就是活着。活着的滋味,你慢慢就懂了。"\n\n我知道,福贵的故事才刚刚开始。这个老人的一生,经历了太多的苦难和失去,但他依然活着,依然能够平静地唱出那首歌。这就是活着的意义吧。' },
        { title: '第二章:赌徒生活', content: '福贵年轻时是个地主家的少爷。他爹娘都疼他,给他娶了城里米行老板的女儿家珍做媳妇。\n\n家珍是个好女人,温柔贤惠,还给他生了个女儿,取名凤霞。\n\n可是福贵不争气。他迷上了赌博,经常跑到城里的赌馆去,一夜一夜地不回家。\n\n他爹把他绑在柱子上打,他娘跪在地上求他,他都不听。\n\n他觉得自己还年轻,有的是时间。他把家里的地契、房产都拿去抵押,换钱继续赌。\n\n直到有一天,他把所有的家产都输光了。龙二设局骗他,让他欠下了巨额赌债。\n\n福贵一夜之间从地主少爷变成了穷光蛋。他爹气死了,他娘病倒了,家珍带着凤霞回到了娘家。' },
        { title: '第三章:战争岁月', content: '福贵去城里给他娘抓药,却被国民党军队抓了壮丁。\n\n他被押着走了几天几夜,来到了前线。那里到处都是枪炮声和伤兵的惨叫声。\n\n福贵在军队里度过了两年。他看到了死亡的恐怖,也看到了人性的丑陋。\n\n有一天,他和老全被共产党军队俘虏了。共产党给他们吃的,还让他们选择去留。\n\n福贵选择了回家。他走了好几个月,终于回到了家乡。\n\n可是,他娘已经死了,凤霞因为发高烧变成了哑巴。家珍还在等他,带着儿子有庆。\n\n福贵抱着儿子哭了一场,然后发誓要好好活着。' },
        { title: '第四章:土地改革', content: '土地改革开始了,龙二因为当了地主被枪毙了。\n\n福贵听说后,吓得尿了裤子。他想,要是当初没有输掉家产,被枪毙的就是他了。\n\n他觉得因祸得福,开始安心种地。他租了五亩地,每天起早贪黑地干活。\n\n家珍从娘家回来了,带着凤霞和有庆。一家人终于团圆了。\n\n可是好景不长,大跃进开始了。村里办起了大食堂,家家户户都把粮食交出去。\n\n福贵把家里的铁锅都砸了,送去炼钢铁。可是炼出来的都是一堆废铁。\n\n粮食越来越少了,大家都饿得面黄肌瘦。' },
        { title: '第五章:有庆之死', content: '有庆是个好孩子。他每天放学后就跑去割草,喂那只家里养的山羊。\n\n他特别喜欢那只羊,给它取名叫"小羊"。他说等他长大了,要让小羊生很多很多小羊。\n\n可是有一天,有庆出事了。县长夫人生产需要输血,学校组织学生们去献血。\n\n有庆的血型正好匹配。可是医生为了救县长夫人,把有庆的血都抽光了。\n\n有庆死在了医院里。福贵赶到时,只看到了儿子的尸体。\n\n他抱着儿子的尸体哭了一夜。他想不明白,为什么一个好好的孩子就这么没了。\n\n家珍知道后,病得更重了。她整天哭,眼睛都快哭瞎了。' },
        { title: '第六章:凤霞出嫁', content: '凤霞长大了,虽然又聋又哑,但长得很标致。\n\n村里有个叫二喜的青年,是个偏头,但对凤霞很好。\n\n他经常偷偷来看凤霞,给她带好吃的。凤霞也喜欢他,每次都笑得特别开心。\n\n后来二喜托人来提亲,福贵和家珍都同意了。\n\n凤霞出嫁那天,穿着红色的新衣服,笑得像朵花。\n\n福贵看着女儿出嫁,心里既高兴又难过。他想,要是家珍的眼睛没瞎就好了,能看到女儿穿嫁衣的样子。\n\n二喜是个好人,对凤霞照顾得无微不至。他说:"你放心,我会一辈子对凤霞好的。"' },
        { title: '第七章:家珍离世', content: '家珍的病越来越重了。她得了软骨病,连床都起不来了。\n\n福贵每天给她喂饭、擦身子。家珍总是说:"苦了你了。"\n\n福贵说:"不苦,只要你还活着,我就不苦。"\n\n有一天,家珍对福贵说:"福贵,我走后,你要好好活着。别想我,想我也没用。"\n\n福贵握着她的手,一句话也说不出来。\n\n家珍走的那天,天气很好。阳光照在她脸上,她走得很安详。\n\n福贵把她埋在了有庆的坟旁边。他说:"你们母子俩在那边也能有个伴。"' },
        { title: '第八章:二喜遇难', content: '凤霞给二喜生了个儿子,取名苦根。可是凤霞因为大出血死在了产房里。\n\n二喜哭得死去活来。他说:"凤霞,你怎么就这么走了呢?我们不是说好要过一辈子吗?"\n\n福贵也哭了。他想不明白,为什么他身边的人都要一个个离开他。\n\n二喜一个人带着苦根过日子。他在城里当搬运工,每天早出晚归。\n\n苦根很懂事,经常帮二喜干活。他说:"爹,等我长大了,我养你。"\n\n可是,二喜也出事了。他在工地干活时,被两块水泥板夹死了。\n\n福贵赶到时,只看到了二喜的尸体。他的眼睛还睁着,好像还在看着苦根。' },
        { title: '第九章:苦根走了', content: '福贵把苦根接到了身边。他说:"苦根啊,就剩咱们俩了,咱们得好好活着。"\n\n苦根很乖,每天都帮福贵干活。他虽然小,但什么都会做。\n\n有一天,福贵生病了。苦根给他煮豆子吃。\n\n苦根不知道豆子不能多吃,给福贵煮了一大锅。福贵吃撑了,躺在床上起不来。\n\n苦根很害怕,他以为福贵也要死了。他哭着说:"外公,你别死,我怕。"\n\n福贵安慰他说:"外公不会死,外公还要看着苦根长大呢。"\n\n可是,苦根因为吃豆子太多,撑死了。\n\n福贵醒来时,只看到了苦根的尸体。他抱着苦根的尸体,哭了三天三夜。' },
        { title: '第十章:孤独地活着', content: '现在,就剩福贵一个人了。他买了一头老牛,也取名叫福贵。\n\n他每天带着牛下地干活,晚上就坐在老槐树下,回忆过去。\n\n他说:"我爹、我娘、家珍、有庆、凤霞、二喜、苦根,他们都走了,就剩我一个了。"\n\n"有时候我想,要是我也走了就好了。可是我不能走,我得替他们活着。"\n\n"我活着,他们就还在这个世界上。我替他们看着这个世界。"\n\n老人唱起了那首歌谣:"少年去游荡,中年想掘藏,老年做和尚。"\n\n他的歌声在田野里回荡,苍凉而悠远。\n\n这就是福贵的一生。他失去了一切,但还在活着。因为活着本身就是一种力量。' }
      ]
    },
    {
      id: 'sample5',
      title: '时间简史',
      author: '斯蒂芬·霍金',
      cover: '#fa709a',
      progress: 25,
      lastRead: '2026-05-21',
      chapters: [
        { title: '第一章:宇宙图像', content: '一个著名的科学家曾经(有人说是罗素)在一次公开讲演中,描述了地球围绕太阳转,而太阳又围绕银河系中心转的事实。讲演结束时,一位坐在后排的小个子老太太站起来说:"你说的都是废话,地球其实是被一只巨龟驮着的。"\n\n"那么这只巨龟又站在什么上面呢?"科学家微笑着问。\n\n"你很聪明,年轻人,很聪明,"老太太回答,"但是巨龟之下,仍是巨龟!"\n\n今天,大多数人会觉得这个理论很荒谬。但是,我们关于宇宙的观念,又何尝不是在不断变化呢?\n\n古希腊人认为地球是平的,亚里士多德认为地球是圆的,托勒密认为地球是宇宙的中心。哥白尼提出太阳是中心,伽利略用望远镜证实了这一点,牛顿用万有引力定律解释了行星运动。\n\n爱因斯坦的相对论又告诉我们,空间和时间是相互关联的,它们会因为物质和能量而弯曲。这是一个革命性的观念,彻底改变了我们对宇宙的理解。\n\n我们今天认为理所当然的事情,比如地球围绕太阳转,宇宙中有数十亿个星系,在不久之前还是不可想象的。\n\n那么,我们今天关于宇宙的观念,在未来会不会也被认为是荒谬的呢?也许吧。科学就是这样不断进步的。\n\n在这本书中,我将带你了解现代物理学对宇宙的最新理解。从大爆炸到黑洞,从量子力学到相对论,我们会探索这个神秘而美丽的宇宙。\n\n你会看到,人类是如何从一个认为地球是宇宙中心的物种,变成了能够理解宇宙起源和演化的智慧生命。这是一段令人惊叹的旅程。' },
        { title: '第二章:空间和时间', content: '我们的太阳和恒星系都存在于一个叫做银河系的巨大星团中。银河系包含大约一千亿颗恒星。\n\n我们的银河系只是宇宙中数以千亿计的星系之一。每个星系又包含数以千亿计的恒星。\n\n这意味着宇宙中的恒星数量,比地球上所有海滩上的沙子还要多。\n\n当我们仰望星空时,我们看到的不仅是空间,还有时间。\n\n因为光需要时间才能到达我们的眼睛。我们看到的太阳是八分钟前的太阳,我们看到的星星是几百年前、甚至几亿年前的样子。\n\n爱因斯坦的狭义相对论告诉我们,时间和空间不是绝对的,而是相对的。\n\n如果你运动得越快,你的时间就会过得越慢。这叫做"时间膨胀"。\n\n广义相对论进一步告诉我们,物质和能量会使空间和时间弯曲。这就是引力的本质。' },
        { title: '第三章:膨胀的宇宙', content: '1929年,美国天文学家爱德温·哈勃做出了一个划时代的发现。\n\n他发现,无论你往哪个方向看,远处的星系都在离我们远去。而且,星系离我们越远,它远离我们的速度就越快。\n\n这意味着宇宙正在膨胀。就像一个气球,当它被吹胀时,表面上的任何两点都在互相远离。\n\n如果宇宙在膨胀,那么在过去它一定更小。如果我们回溯到足够远的过去,宇宙一定曾经是一个非常小、非常热的点。\n\n这就是大爆炸理论的起源。大约138亿年前,宇宙从一个无限热的点开始膨胀,形成了今天我们看到的宇宙。\n\n大爆炸不是一个发生在空间中的事件,而是空间本身的诞生。在大爆炸之前,没有时间,没有空间。' },
        { title: '第四章:不确定性原理', content: '在经典物理学中,如果我们知道一个粒子在某一时刻的位置和速度,我们就能预测它未来的运动轨迹。\n\n但是,20世纪初的量子力学革命推翻了这个观念。\n\n海森堡提出了"不确定性原理":我们不可能同时精确知道一个粒子的位置和速度。\n\n你测量位置越精确,速度的不确定性就越大,反之亦然。\n\n这不是因为我们测量技术不够好,而是宇宙的基本性质。粒子本质上就不是经典的粒子,而是具有波粒二象性。\n\n这意味着,我们不能预测单个粒子的行为,只能预测它出现在某个位置的概率。\n\n爱因斯坦不喜欢这个观念,他说:"上帝不掷骰子。"但实验证明,量子力学是正确的。' },
        { title: '第五章:基本粒子和力', content: '物质的基本组成是什么?古希腊人认为是水、火、土、气。到了19世纪,科学家认为是原子。\n\n20世纪初,我们发现原子还可以分成原子核和电子。原子核又分成质子和中子。\n\n质子和中子又是由更小的粒子"夸克"组成的。夸克有六种"味":上、下、奇、粲、底、顶。\n\n除了夸克,还有轻子家族,包括电子、μ子、τ子以及它们对应的中微子。\n\n这些粒子通过四种基本力相互作用:引力、电磁力、弱核力、强核力。\n\n物理学家一直在追求"大统一理论",试图用一种力来解释所有现象。但目前还没有成功。' },
        { title: '第六章:黑洞', content: '当一颗大质量恒星耗尽其核燃料时,它会发生剧烈的坍缩。如果质量足够大,坍缩会一直持续下去。\n\n恒星会被压缩到一个无限密度的点,叫做"奇点"。在奇点周围,有一个叫做"事件视界"的边界。\n\n一旦越过这个边界,连光都无法逃脱。这就是黑洞。\n\n黑洞之所以被称为"黑",是因为它不发出任何光。我们无法直接看到黑洞,只能通过它对周围物质的影响来推断它的存在。\n\n1974年,我发现黑洞并不完全是黑的。由于量子效应,黑洞会辐射出粒子,这叫做"霍金辐射"。\n\n这意味着黑洞会慢慢蒸发,最终消失。这引发了一个悖论:掉进黑洞的信息去了哪里?' },
        { title: '第七章:黑洞不是黑的', content: '黑洞的信息悖论是物理学中最大的未解之谜之一。\n\n根据量子力学,信息永远不会丢失。但是根据广义相对论,掉进黑洞的信息会永远消失在奇点中。\n\n这两个理论不能同时正确。我们需要一个量子引力理论来解决这个问题。\n\n弦理论是候选者之一。它认为基本粒子不是点,而是振动的弦。弦的不同振动模式对应不同的粒子。\n\n但弦理论需要额外的维度,而且目前还没有实验证据支持它。\n\n另一个问题是,黑洞的熵是什么?贝肯斯坦提出,黑洞的熵与其事件视界的面积成正比。\n\n这意味着黑洞包含的信息量是有限的。但具体是什么信息,我们还不知道。' },
        { title: '第八章:宇宙的起源和命运', content: '如果大爆炸理论是正确的,那么宇宙一定有一个开端。但是,大爆炸是什么引起的?\n\n暴胀理论提出,宇宙在极早期经历了一段指数级的快速膨胀。\n\n这解释了为什么宇宙在大尺度上如此均匀,也解释了宇宙中的微小密度涨落。\n\n这些涨落后来形成了星系和星系团。如果没有暴胀,宇宙将是一片混沌。\n\n但暴胀理论也有问题。它需要一个"暴胀子"场,但我们还没有发现它。\n\n宇宙的命运取决于它的密度。如果密度超过临界值,宇宙最终会坍缩,形成"大挤压"。\n\n如果密度低于临界值,宇宙会永远膨胀下去,最终变成一片冰冷、黑暗的虚空。' },
        { title: '第九章:时间箭头', content: '为什么我们记得过去,而不记得未来?为什么时间只有一个方向?\n\n热力学第二定律告诉我们,孤立系统的熵(混乱程度)总是增加的。\n\n这就是热力学时间箭头。杯子从桌子上掉下来摔碎,但碎片不会自动重新组合成杯子。\n\n心理学时间箭头是我们感知时间的方向。我们记得过去,而不记得未来。\n\n这其实是由热力学时间箭头决定的。我们的大脑是一个物理系统,遵循热力学定律。\n\n宇宙学时间箭头是宇宙膨胀的方向。有趣的是,这三个箭头都指向同一个方向。\n\n也许这不是巧合。也许正是因为宇宙在膨胀,熵在增加,我们才能感知到时间的流逝。' },
        { title: '第十章:虫洞和时间旅行', content: '广义相对论允许一种叫做"虫洞"的结构。虫洞是连接时空两个不同区域的捷径。\n\n理论上,通过虫洞,你可以在瞬间跨越数百万光年的距离。\n\n更令人兴奋的是,虫洞可能允许时间旅行。如果你以接近光速运动,你的时间会变慢。\n\n这意味着,如果你通过虫洞旅行,你可能回到过去,或者前往未来。\n\n但是,虫洞需要"负能量"来维持其开启状态。而我们不知道是否存在足够的负能量。\n\n即使虫洞存在,时间旅行也会引发悖论。比如"祖父悖论":如果你回到过去杀死你的祖父,你还会存在吗?\n\n也许有某种"时序保护猜想",阻止了时间旅行的发生。或者,时间旅行会导致平行宇宙的产生。\n\n这些问题,目前的物理学还无法回答。也许未来的某一天,我们能找到答案。' }
      ]
    },
    {
      id: 'sample6',
      title: '红楼梦',
      author: '曹雪芹',
      cover: '#fee140',
      progress: 55,
      lastRead: '2026-05-17',
      chapters: [
        { title: '第一章:甄士隐梦幻识通灵', content: '列位看官:你道此书从何而来?说起根由虽近荒唐,细按则深有趣味。待在下将此来历注明,方使阅者了然不惑。\n\n原来女娲氏炼石补天之时,于大荒山无稽崖练成高经十二丈、方经二十四丈顽石三万六千五百零一块。娲皇氏只用了三万六千五百块,只单单剩了一块未用,便弃在此山青埂峰下。\n\n谁知此石自经煅炼之后,灵性已通,因见众石俱得补天,独自己无材不堪入选,遂自怨自叹,日夜悲号惭愧。\n\n一日,正当嗟悼之际,俄见一僧一道远远而来,生得骨格不凡,丰神迥异,说说笑笑来至峰下,坐于石边高谈快论。\n\n先是说些云山雾海神仙玄幻之事,后便说到红尘中荣华富贵。此石听了,不觉打动凡心,也想要到人间去享一享这荣华富贵。\n\n那僧道:"善哉,善哉!那红尘中有却有些乐事,但不能永远依恃;况又有'美中不足,好事多磨'八个字紧相连属,瞬息间则又乐极悲生,人非物换。"\n\n"究竟是到头一梦,万境归空,倒不如不去的好。"但这块凡心已动的石头哪里肯听,只是再三苦求。\n\n那僧道:"也罢,我们把携你到那昌明隆盛之邦,诗礼簪缨之族,花柳繁华地,温柔富贵乡去安身乐业。"石头听了,喜之不尽。\n\n于是那僧便袖了这石,同那道人飘然而去,竟不知投奔何方何舍。\n\n后来,又不知过了几世几劫,因有个空空道人访道求仙,忽从这大荒山无稽崖青埂峰下经过,忽见一大块石上字迹分明,编述历历。\n\n空空道人乃从头一看,原来就是无材补天,幻形入世,蒙茫茫大士渺渺真人携入红尘,历尽离合悲欢炎凉世态的一段故事。\n\n这,就是《红楼梦》的开端。一块被遗弃的石头,一段繁华落尽的梦境,一个家族的兴衰史,一群少男少女的爱恨嗔痴,都从这个神话般的开端展开。' },
        { title: '第二章:贾雨村风尘怀闺秀', content: '贾雨村原是湖州人氏,出身诗书仕宦之族,因祖宗根基已尽,人口衰丧,只剩得他一身一口。\n\n他在京城求取功名,无奈世道艰难,未能如愿。只得暂寄居在葫芦庙中,卖字作文为生。\n\n这日中秋佳节,甄士隐邀请贾雨村到家中饮酒。两人月下畅饮,谈论诗词文章,甚是投机。\n\n贾雨村酒后吟诗:"未卜三生愿,频添一段愁。闷来时敛额,行去几回头。"\n\n甄士隐听罢,知其才华不凡,便赠银五十两、冬衣两套,助他上京赶考。\n\n贾雨村感激不尽,次日便启程赴京。甄士隐站在渡口,望着渐行渐远的船只,心中感慨万千。\n\n谁知这竟是两人最后一次相见。不久之后,甄家遭逢大难,女儿英莲被拐,家产被焚,甄士隐最终出家为僧。' },
        { title: '第三章:林黛玉进贾府', content: '林黛玉的母亲贾敏是贾母的女儿,嫁给林如海为妻。黛玉年幼丧母,贾母怜惜,接到贾府抚养。\n\n黛玉进府那日,步步留心,时时在意,不肯轻易多说一句话,多行一步路,惟恐被人耻笑了他去。\n\n贾母一见黛玉,便搂入怀中,心肝儿肉叫着大哭起来。当下侍立之人,无不掩面涕泣。\n\n黛玉拜见了邢夫人、王夫人、李纨等,又见了迎春、探春、惜春三姐妹。\n\n正说着,只听后院中有笑声传来,说:"我来迟了,不曾迎接远客!"\n\n黛玉纳罕道:"这些人个个皆敛声屏气,恭肃严整如此,这来者系谁,这样放诞无礼?"\n\n只见一群媳妇丫鬟围拥着一个人从后房门进来。这个人打扮与众姑娘不同,彩绣辉煌,恍若神妃仙子。\n\n这便是王熙凤,贾府的实际管理者,精明强干,八面玲珑。' },
        { title: '第四章:宝玉初试云雨情', content: '贾宝玉衔玉而生,出生时嘴里含着一块通灵宝玉,因此得名。\n\n他自幼不爱读书,只爱和姐妹们一起玩耍。他说:"女儿是水作的骨肉,男人是泥作的骨肉。我见了女儿,我便清爽;见了男子,便觉浊臭逼人。"\n\n宝玉房中有个大丫鬟名叫袭人,原名珍珠,是贾母拨给他的。\n\n袭人温柔和顺,克尽职任,宝玉十分依赖她。\n\n一日,宝玉在秦可卿房中午睡,梦中游太虚幻境,与可卿成姻。醒来后,袭人替他整理衣裳,伸手系裤带时,触及他的大腿处。\n\n袭人本是个聪明女子,年纪本又比宝玉大两岁,近来也渐通人事。今见宝玉如此光景,心中便觉察一半了,不觉的羞的红涨了脸面,不敢再问。\n\n此后,袭人与宝玉的关系又亲近了一步。但这只是少年懵懂,并非什么不轨之事。' },
        { title: '第五章:大观园试才题对额', content: '贾政之女元春被封为贵妃,皇帝恩准她回家省亲。贾府为此修建了大观园。\n\n园中亭台楼阁,假山池塘,奇花异草,美不胜收。贾政带着众清客和宝玉入园游玩,题写匾额对联。\n\n宝玉虽然不爱读书,但天资聪颖,才思敏捷,题写的对联深得贾政赞赏。\n\n元春省亲那日,烈火烹油,鲜花着锦,盛况空前。但她却垂泪道:"当日既送我到那不得见人的去处,好容易今日回家娘儿们一会,不说说笑笑,反倒哭起来。"\n\n元春命众姐妹和宝玉入住大观园。宝玉住在怡红院,黛玉住在潇湘馆,宝钗住在蘅芜苑。\n\n从此,大观园成了他们的乐园。他们在园中结社作诗,饮酒赏花,度过了一段无忧无虑的美好时光。' },
        { title: '第六章:共读西厢', content: '三月桃花盛开,宝玉在沁芳闸桥边桃花底下一块石上坐着,展开《会真记》,从头细玩。\n\n正看到"落红成阵",只见一阵风过,把树头上桃花吹下一大半来,落的满身满书满地皆是。\n\n宝玉要抖将下来,恐怕脚步践踏了,只得兜了那花瓣,来至池边,抖在池内。\n\n那花瓣浮在水面,飘飘荡荡,竟流出沁芳闸去了。\n\n回来只见地下还有许多,宝玉正踟蹰间,只听背后有人说道:"你在这里做什么?"\n\n宝玉一回头,却是林黛玉来了,肩上担着花锄,锄上挂着花囊,手内拿着花帚。\n\n黛玉说:"撂在水里不好。你看这里的水干净,只一流出去,有人家的地方脏的臭的混倒,仍旧把花糟蹋了。"\n\n两人一起葬花,共读《西厢记》,情愫暗生。这是他们最美好的时光之一。' },
        { title: '第七章:黛玉葬花', content: '四月二十六日芒种节,闺中兴送花神。大观园中女孩子们都早早起来,或用花瓣柳枝编成轿马,或用绫锦纱罗叠成干旄旌幢,都用彩线系了,每一棵树头每一枝花上,都系了这些物事。\n\n满园里绣带飘飖,花枝招展,更兼这些人打扮的桃羞杏让,燕妒莺惭,一时也道不尽。\n\n黛玉独自扛着花锄,锄上挂着花囊,手内拿着花帚,来到花冢前。\n\n她看到满地落花,想到自己寄人篱下,身世飘零,不禁悲从中来。\n\n她吟诵道:"花谢花飞飞满天,红消香断有谁怜?游丝软系飘春榭,落絮轻沾扑绣帘。"\n\n"闺中女儿惜春暮,愁绪满怀无释处。手把花锄出绣帘,忍踏落花来复去。"\n\n"一朝春尽红颜老,花落人亡两不知!"\n\n恰好宝玉走来,听到此句,不觉痴倒。他深知黛玉的心事,两人相对而泣。' },
        { title: '第八章:金玉良缘', content: '薛宝钗是薛姨妈的女儿,比宝玉大两岁。她生得肌骨莹润,举止娴雅,品格端方。\n\n她有一块金锁,上面刻着"不离不弃,芳龄永继"八个字,与宝玉的通灵宝玉正好是一对。\n\n这就是所谓的"金玉良缘"。贾府上下都认为宝钗是宝玉的最佳配偶。\n\n宝钗为人处世圆滑周到,深得贾母、王夫人等人的喜爱。她经常劝宝玉读书仕进,但宝玉对此十分反感。\n\n黛玉则不同,她从不劝宝玉去考取功名,因此宝玉把她引为知己。\n\n但黛玉体弱多病,性格孤傲,又不会讨好人,因此在贾府中并不受宠。\n\n宝钗和黛玉,一个代表世俗的完美,一个代表灵魂的真实。宝玉夹在中间,难以抉择。' },
        { title: '第九章:抄检大观园', content: '贾府的衰落从内部开始。丫环们之间勾心斗角,主子们之间明争暗斗。\n\n一个绣春囊的出现,引发了抄检大观园的风波。王夫人下令连夜搜查各房。\n\n探春悲愤地说:"你们别忙,自然连你们抄的日子有呢!你们今日早起不曾议论甄家,自己家里好好的抄家,果然今日真抄了。"\n\n这次抄检,晴雯被逐出大观园,不久便病死。芳官、四儿等也被撵了出去。\n\n大观园的清净被打破了,姑娘们的好日子也到头了。\n\n贾府的衰落已不可挽回。外面看着烈火烹油,其实内里已经腐朽。' },
        { title: '第十章:白茫茫大地真干净', content: '贾府被抄家了。曾经显赫一时的荣国府、宁国府,一夜之间化为乌有。\n\n贾母去世了,王熙凤病死了,迎春被丈夫折磨死了,探春远嫁他乡,惜春出家为尼。\n\n宝玉和宝钗成了亲,但宝玉心中只有黛玉。黛玉在宝玉成亲的当晚,含恨而终。\n\n宝玉知道后,痛哭不止。他去黛玉灵前祭奠,却发现人去楼空。\n\n最后,宝玉看破红尘,跟着癞头和尚和跛足道人走了。\n\n"好一似食尽鸟投林,落了片白茫茫大地真干净!"\n\n贾府的繁华如梦,终究是一场空。\n\n这正是:满纸荒唐言,一把辛酸泪。都云作者痴,谁解其中味?' }
      ]
    },
    {
      id: 'sample7',
      title: '哈利·波特与魔法石',
      author: 'J.K.罗琳',
      cover: '#30cfd0',
      progress: 70,
      lastRead: '2026-05-16',
      chapters: [
        { title: '第一章:大难不死的男孩', content: '住在女贞路四号的德思礼夫妇总是骄傲地说他们是非常规矩的人家,拜托,从来没有跟什么可疑的人或奇怪的事打过交道。他们相信保密是成功的首要条件。\n\n弗农·德思礼先生是一家生产钻机的公司格朗宁的经理,他胖得几乎没有脖子,戴着一副很大的眼镜。佩妮·德思礼太太则瘦骨嶙峋,有一头金发,脖子比平常长两倍,这使她偷窥邻居时非常方便。\n\n德思礼一家有一个小儿子,名叫达力,在他们看来,全英国也找不出比他更好的孩子了。然而,德思礼一家有一个秘密,他们最大的恐惧就是被人发现这个秘密。\n\n佩妮·德思礼有一个妹妹叫莉莉,她是个女巫。但佩妮已经多年不承认这个妹妹了。因为莉莉嫁给了一个巫师,生了一个儿子,名叫哈利。\n\n十年前,一个名叫伏地魔的黑巫师杀死了莉莉和她的丈夫詹姆。但在试图杀死哈利时,咒语反弹了,伏地魔失去了身体。\n\n哈利成了"大难不死的男孩",额头上留下一道闪电形的伤疤。整个魔法世界都在庆祝伏地魔的消失,但没有人知道为什么咒语会反弹。\n\n邓布利多------霍格沃茨魔法学校的校长------把婴儿哈利交给了德思礼一家抚养。他希望在麻瓜(非魔法人士)家庭中,哈利能够平安长大。\n\n有一天晚上,弗农·德思礼注意到一只猫在读一张路标,这让他非常不安。但他不知道,这只是个开始。魔法世界正在寻找哈利,而他的命运,即将展开。' },
        { title: '第二章:悄悄消失的玻璃', content: '达力十岁生日那天,德思礼一家去了动物园。哈利被迫同行,因为他不能单独留在家里。\n\n在爬虫馆里,达力和他的朋友皮尔挤在玻璃前看一条蟒蛇。哈利也想看看,但达力总是把他推开。\n\n突然,哈利发现自己能和蟒蛇说话。他用蛇语告诉蟒蛇它来自巴西,蟒蛇兴奋地摆动着尾巴。\n\n达力大喊:"让开!"把哈利推开。就在这时,玻璃消失了。蟒蛇滑了出来,向达力爬去。\n\n达力吓得大叫,皮尔拼命奔跑。等管理员赶来时,玻璃又恢复了原样,蟒蛇也不见了。\n\n弗农·德思礼认为这是哈利的错,把他关在碗橱里,不给他饭吃。\n\n哈利不知道自己是怎么做到的,但他知道,他和其他人不一样。' },
        { title: '第三章:猫头鹰邮递', content: '哈利的十一岁生日快到了。德思礼一家决定去海边的一座小木屋庆祝,以避开那些不断飞来的信件。\n\n那些信都是寄给哈利的,信封上用翠绿色墨水写着:萨里郡小惠金区女贞路4号楼梯下的碗橱,哈利·波特先生收。\n\n弗农·德思礼愤怒地烧掉了每一封信,但更多的信还是源源不断地飞来。\n\n午夜时分,门被猛烈地敲响了。一个巨人站在门口,他几乎有两倍于普通人的高度,五倍于普通人的宽度。\n\n"我叫鲁伯·海格,"他说,"霍格沃茨的猎场看守人。"\n\n海格递给哈利一个被压扁的盒子,里面是一块巧克力蛋糕,上面用绿色糖霜写着:祝哈利生日快乐。\n\n"你是巫师,哈利,"海格说,"而且是一个杰出的巫师,只要你接受过训练。"' },
        { title: '第四章:对角巷', content: '海格带哈利去了对角巷,一个隐藏在伦敦的巫师购物街。\n\n哈利在古灵阁巫师银行看到自己有一大笔财富,这是他的父母留给他的。\n\n他们买了魔杖、长袍、坩埣、天平,还有一只名叫海德薇的雪鸮作为生日礼物。\n\n在奥利凡德魔杖店,哈利试了很多根魔杖都不合适。最后,他得到了一根冬青木魔杖,凤凰羽毛芯。\n\n"奇怪的是,"奥利凡德先生说,"这根魔杖的兄弟给了你那道伤疤。"\n\n哈利的魔杖和伏地魔的魔杖是兄弟,都来自同一只凤凰的羽毛。\n\n离开对角巷时,哈利得知霍格沃茨是一所魔法学校,他将在九月一日去那里上学。' },
        { title: '第五章:九又四分之三站台', content: '九月一日那天,海格告诉哈利要去国王十字车站的九又四分之三站台。\n\n哈利在车站找不到这个站台,正当他焦急时,他看到了一家巫师家庭。\n\n"不知道怎么回事?"一个红头发的胖女人问道。她指着第九和第十站台之间的隔墙说:"笔直朝走,不要停下来。"\n\n哈利推着车朝墙走去,越来越近,他闭上眼睛准备撞上去------但什么也没撞到。\n\n他睁开眼,看到了一辆深红色的蒸汽机车,站台上方写着:霍格沃茨特快列车,上午十一时发车。\n\n在火车上,哈利遇到了罗恩·韦斯莱和赫敏·格兰杰。他们成为了好朋友。\n\n火车穿过苏格兰的高地,驶向一座隐藏在群山中的古老城堡------霍格沃茨魔法学校。' },
        { title: '第六章:分院帽', content: '新生们来到霍格沃茨城堡,麦格教授告诉他们,他们将被分到四个学院:格兰芬多、赫奇帕奇、拉文克劳和斯莱特林。\n\n分院是一顶破旧的帽子,它会读你的思想,决定你最适合哪个学院。\n\n"格兰芬多代表勇气,赫奇帕奇代表忠诚,拉文克劳代表智慧,斯莱特林代表野心。"麦格教授说。\n\n当叫到哈利的名字时,全场安静了。分院帽刚碰到他的头,就小声说:"嗯,有困难。很有勇气,心地不坏。有才华,还有渴望证明自己的强烈愿望。"\n\n"不去斯莱特林?"哈利在心里默念。他听说过斯莱特林培养黑巫师。\n\n"不去斯莱特林?你确定?你能成就伟大的事业。好吧,既然你确定......格兰芬多!"\n\n罗恩和赫敏也被分到了格兰芬多。他们成为了格兰芬多的一年级学生。' },
        { title: '第七章:魔药课', content: '哈利在霍格沃茨学习各种魔法课程:变形术、魔咒课、草药学、魔药课、黑魔法防御术、天文学和历史课。\n\n斯内普教授是魔药课老师,他讨厌哈利,总是找他的茬。\n\n"波特,"斯内普说,"如果我把水仙根粉末加入艾草浸液,会得到什么?"\n\n哈利不知道。赫敏举起手,但斯内普没叫她。\n\n"不知道?我以为你会知道,波特。再来一题。告诉我,如果我要你去给我找一块粪石,你会去哪里找?"\n\n哈利还是不知道。斯内普冷冷地说:"看来名气不能代表一切,波特。"\n\n哈利后来才知道,斯内普和他的父亲詹姆是同学,他们曾是敌人。\n\n但哈利不知道的是,斯内普曾经深爱着他的母亲莉莉。' },
        { title: '第八章:魁地奇比赛', content: '哈利展现了惊人的飞行天赋,麦格教授破例允许他加入格兰芬多魁地奇球队,担任找球手。\n\n魁地奇是巫师世界的足球,球员骑着飞天扫帚在空中比赛。每队七名球员:三名追球手、两名击球手、一名守门员和一名找球手。\n\n找球手的目标是抓住金色飞贼,一个核桃大小的金色球,长着翅膀,飞行速度极快。\n\n抓住飞贼可以为球队赢得150分,并结束比赛。\n\n哈利的第一场比赛是对阵斯莱特林。比赛激烈进行中,哈利的扫帚突然失控,似乎被什么魔法控制了。\n\n赫敏注意到斯内普在看台上盯着哈利的扫帚,嘴里念念有词。\n\n她认为是斯内普在施咒。她偷偷跑到斯内普的看台后面,用火焰点燃了他的斗篷。\n\n斯内普的注意力被打断,哈利重新控制了扫帚,抓住了飞贼,赢得了比赛。' },
        { title: '第九章:厄里斯魔镜', content: '圣诞节那天,哈利收到了一件隐形衣,匿名寄来的。他后来才知道,这是他父亲留下的。\n\n穿着隐形衣,哈利在夜里探索霍格沃茨。他发现了一个隐藏的房间,里面有一面高大的镜子。\n\n镜框顶上刻着:厄里斯·斯特拉·厄赫鲁·阿伊特乌比·卡弗鲁·阿伊特昂·沃赫斯。\n\n哈利在镜子里看到了他的父母,还有他的祖父母,他们都活着,向他微笑。\n\n邓布利多校长发现了他,告诉他:"这面镜子显示的是我们内心最深切的渴望。"\n\n"厄里斯不是镜子,而是desire(渴望)倒过来写的。它不给我们知识或真理,只是展示我们最想要的东西。"\n\n"明天我会把镜子搬到别处,哈利。不要再去寻找它了。沉湎于虚幻的梦想,而忘记现实的生活,这是毫无益处的。"' },
        { title: '第十章:魔法石', content: '哈利、罗恩和赫敏发现,霍格沃茨藏有一块魔法石。魔法石能够制造长生不老药,使人永生不死。\n\n它被藏在四楼禁区,由一只三头犬看守。邓布利多设置了多重保护:魔鬼网、飞天钥匙、巨型棋盘和魔药谜题。\n\n哈利认为斯内普试图偷走魔法石,交给伏地魔。伏地魔失去了身体,需要魔法石来恢复。\n\n三人组决定自己保护魔法石。他们通过了重重障碍:赫敏解开了魔鬼网,哈利抓住了正确的钥匙,罗恩在棋盘上牺牲了自己。\n\n最后,哈利独自面对斯内普------不,是奇洛教授。奇洛的后脑勺上长着伏地魔的脸。\n\n伏地魔试图夺取魔法石,但哈利碰触到奇洛时,奇洛的皮肤开始燃烧。\n\n原来,哈利的母亲用生命在他身上留下了爱的保护。这种爱是伏地魔无法理解的。\n\n哈利阻止了伏地魔,但魔法石被邓布利多销毁了。伏地魔会回来的,但至少现在,哈利是安全的。' }
      ]
    },
    {
      id: 'sample8',
      title: '人类简史',
      author: '尤瓦尔·赫拉利',
      cover: '#ff9a9e',
      progress: 40,
      lastRead: '2026-05-15',
      chapters: [
        { title: '第一章:认知革命', content: '大约7万年前,智人开始做出非常特别的事情。我们开始创造复杂的文化、技术和社会组织。这就是认知革命。\n\n认知革命让智人能够想象不存在的事物,创造共同的虚构故事。宗教、国家、公司、货币------这些都是虚构的概念,但因为所有人都相信,它们就变得真实而有力。\n\n黑猩猩可以告诉同伴"小心,有狮子",但智人可以说"狮子是我们的守护神"。这种能力让智人能够进行大规模合作。\n\n150人是人类社会关系的上限,这是由我们大脑的结构决定的。但通过共同的故事,数百万人可以合作。\n\n想象一下,两个陌生人如果都相信同一个神,或者都使用同一种货币,他们就能够互信并进行交易。这就是虚构故事的力暈。\n\n认知革命让智人走出了非洲,扩散到全世界。我们到达了澳大利亚、美洲,甚至偏远的岛屿。\n\n每到一处,大型动物就会灭绝。在澳大利亚,24种体重超过50公斤的动物中有23种在智人到达后不久就消失了。在美洲,猛犸象、剑齿虎、巨型地懒都在人类到达后灭绝。\n\n智人是历史上最致命的物种。我们不是通过锋利的牙齿或强壮的肌肉来征服世界,而是通过我们的大脑,通过我们的语言能力,通过我们能够创造和相信虚构故事的能力。\n\n这就是人类成为地球主宰的原因。但这并不是一个光荣的故事,而是一个充满破坏和灭绝的故事。了解这一点,才能理解人类的本质。' },
        { title: '第二章:农业革命', content: '大约12000年前,人类开始种植小麦、驯化动物。这被称为农业革命。\n\n通常认为农业革命是进步,但实际上,它让人类的生活变得更辛苦。\n\n采集者每天只需工作3-6小时,农民却要日出而作、日落而息。\n\n农民的饮食单一,容易营养不良;人口密度高,容易传播疾病。\n\n但农业革命让人口爆炸。即使生活更苦,但生的孩子更多,所以农业还是扩散开了。\n\n农业也创造了"家"的概念。人类开始定居,建造房屋,积累财富。\n\n但这带来了新的问题:财产不平等、阶级分化、战争。农业革命是人类历史上最大的骗局吗?' },
        { title: '第三章:人类的融合统一', content: '人类历史的方向是统一。几千年来,小规模的人类社会逐渐融合成更大的文明。\n\n推动统一的力量有三个:经济、政治和宗教。\n\n经济上,货币是最伟大的发明。它让陌生人之间可以互信并进行交易。\n\n政治上,帝国通过征服和同化,将不同的民族融合在一起。\n\n宗教上,普世宗教(如佛教、基督教、伊斯兰教)宣扬全人类平等的理念。\n\n这三种力量相互促进,让人类社会越来越紧密。\n\n今天,全球化让全世界几乎成为一个单一的社会。我们使用相似的货币、遵循相似的法律、相信相似的人权理念。' },
        { title: '第四章:科学革命', content: '500年前,人类承认自己的无知,开始系统地探索世界。这就是科学革命。\n\n科学革命的核心发现是:人类真正重要的无知,是我们不知道自己不知道什么。\n\n古人认为自己什么都知道(通过宗教和传统),科学家承认自己的无知。\n\n科学不是关于已知的知识,而是关于未知的方法。\n\n科学革命与帝国主义和资本主义紧密相连。欧洲人通过科学探索世界,通过帝国统治世界,通过资本主义积累财富。\n\n科学、帝国和资本的结合,创造了前所未有的力量。\n\n过去500年,人类的力量增长了数百倍。我们能够分裂原子、登陆月球、改写基因。' },
        { title: '第五章:资本主义教条', content: '资本主义是现代社会最重要的意识形态。它基于一个简单的理念:利润可以再投资,创造更多利润。\n\n这在人类历史上是革命性的。古代人认为财富是有限的,但资本主义认为财富可以无限增长。\n\n信贷是资本主义的核心。通过信贷,我们可以把未来的财富拿到现在使用。\n\n这依赖于一个信念:未来会比现在更好。\n\n资本主义改变了世界。它推动了工业革命,创造了前所未有的繁荣。\n\n但它也带来了问题:环境破坏、消费主义、不平等。我们越来越富有,但越来越快乐吗?' },
        { title: '第六章:工业的巨轮', content: '工业革命的核心是能源转换。人类学会了将热能转化为机械能。\n\n蒸汽机、内燃机、电力------这些发明让人类的力量呈指数级增长。\n\n工业革命也改变了人类的时间观念。我们以钟表时间生活,而不是日出日落。\n\n城市和乡村的关系颠倒了。以前农村养活城市,现在城市主导农村。\n\n工业革命还改变了家庭和社区。人们离开家乡,到城市找工作。\n\n国家和市场取代了家庭和社区的功能。我们越来越依赖国家和市场,而不是亲人邻居。' },
        { title: '第七章:一场永远的革命', content: '过去200年,人类经历了前所未有的变化。\n\n人口从10亿增长到70亿,预期寿命从30岁增长到70岁,暴力大幅减少。\n\n帝国灭亡了,民主和民族国家成为主流。宗教衰退,科学和人文主义兴起。\n\n我们分裂了原子,登陆了月球,创造了互联网,改写了基因。\n\n我们几乎成为了神。我们能够创造生命(试管婴儿),延长寿命,甚至可能获得永生。\n\n但我们的智慧和力量,是否与我们的幸福感匹配?\n\n现代人拥有古人无法想象的物质享受,但我们是否更快乐?这仍然是一个开放的问题。' },
        { title: '第八章:智人的末日', content: '智人可能正在走向终结。生物工程、人工智能和无机生命正在改变"人类"的定义。\n\n我们可以通过基因工程增强人类的能力,创造"超人类"。\n\n人工智能可能在所有方面超越人类,包括创造力和情感。\n\n无机生命(如硅基生命)可能取代碳基生命,成为地球的主宰。\n\n智人用了7万年走到今天,但可能用不了几个世纪就会被自己创造的新物种取代。\n\n回顾历史,智人的崛起对其他物种是灾难。现在,轮到我们自己了。' },
        { title: '第九章:历史的走向', content: '历史不是必然的。每一个转折点都有无数种可能。\n\n认知革命、农业革命、科学革命------这些都是偶然发生的,不是必然的。\n\n如果历史重来一次,结果可能完全不同。\n\n但历史的趋势是融合和统一。小规模社会融合成大规模文明,这是过去几万年的大方向。\n\n未来会怎样?人类会成为神吗?还是会毁灭自己?\n\n答案取决于我们的选择。历史不会告诉我们该怎么做,但它能帮助我们理解可能的未来。' },
        { title: '第十章:快乐之谜', content: '人类拥有前所未有的力量,但我们快乐吗?\n\n快乐不取决于客观条件,而取决于期望值。如果期望很高,即使条件很好,我们也不快乐。\n\n现代社会提高了我们的期望,所以我们并不比祖先更快乐。\n\n生物学认为快乐由生化机制决定(如血清素、多巴胺),但心理学认为快乐由意义感决定。\n\n如果我们觉得生活有意义,即使痛苦也是快乐的。如果我们觉得生活没有意义,即使富裕也是痛苦的。\n\n也许,人类最大的挑战不是获得力量,而是理解什么让我们真正快乐。\n\n认识你自己------这可能是人类永恒的课题。' }
      ]
    },
    {
      id: 'sample9',
      title: '解忧杂货店',
      author: '东野圭吾',
      cover: '#a8edea',
      progress: 65,
      lastRead: '2026-05-14',
      chapters: [
        { title: '第一章:回答在牛奶箱里', content: '翔太提议:"到那栋废弃的屋子去看看吧!"敦也问:"为什么?"\n\n"因为那里有个传闻,说以前有人在那里收到过来自过去的信。"\n\n敦也和幸平对视了一眼。他们刚抢劫了一家店,正在躲避追捕。三个年轻人,走投无路,不知该去向何方。\n\n"好吧,反正也没地方去。"敦也说。\n\n三人来到那栋废弃的浪矢杂货店。这是一栋和式建筑,已经荒废多年。门牌上写着"浪矢",院子里杂草丛生,屋檐下挂着蜘蛛网。\n\n他们在屋里休息时,突然听到卷帘门外有声音。有人投递了一封信。\n\n翔太打开门,看到一个女人正在离开。地上有一封信,信封上写着:致浪矢杂货店。\n\n他们打开信,发现这是一封咨询烦恼的信。信的开头写着:"我想放弃音乐梦想,但又不甘心。"\n\n更奇怪的是,信上的日期是几十年前。这怎么可能?\n\n敦也翻看着信纸,发现信的末尾写着:"如果您能收到这封信,请给我回信。我会把信放在牛奶箱里。"\n\n牛奶箱?他们在门口找到了一个旧牛奶箱。里面空空如也,但似乎有什么魔法在等待着他们。\n\n"我们回信吧。"敦也突然说。虽然他不知道这封信为什么会跨越时空来到这里,但他知道,有人正在等待着回答。' },
        { title: '第二章:深夜的口琴声', content: '这封信来自一个叫克郎的年轻人。他是鱼店老板的儿子,梦想成为职业音乐人。\n\n但他在东京奋斗了三年,毫无成就。父亲身体不好,希望他回家继承鱼店。\n\n克郎在梦想和责任之间纠结,于是写信给浪矢杂货店求助。\n\n敦也他们讨论后,决定回信。他们不知道这封信是如何穿越时空的,但他们想帮助克郎。\n\n他们写道:"你对音乐的执着是正确的,但你要做好吃苦的准备。"\n\n几天后,他们收到了克郎的回复。他决定坚持音乐梦想。\n\n但故事并没有结束。多年后,他们发现克郎在一场火灾中为了救一个孩子而丧生。\n\n但他创作的歌曲《重生》后来被一个名叫水原芹的女歌手演唱,成为了经典。' },
        { title: '第三章:在思域车上等到天亮', content: '第二个咨询者是一个名叫静子的女孩。她是奥运候选选手,梦想参加奥运会。\n\n但她的男友被诊断出白血病,生命垂危。她想陪伴男友,但又不想放弃奥运梦想。\n\n她写信给浪矢杂货店,寻求建议。\n\n敦也他们回信说:"你应该陪在男友身边。"\n\n但静子似乎不满意这个答案。她又来信,说男友希望她继续训练。\n\n经过几轮通信,他们发现静子的男友其实在说谎。他希望静子追求梦想,所以假装希望她留下。\n\n最终,静子选择了陪伴男友。她放弃了奥运梦想,但无怨无悔。\n\n因为对她来说,爱比梦想更重要。' },
        { title: '第四章:听着披头士默祷', content: '第三个咨询者是一个名叫浩介的男孩。他的父亲准备带他逃亡,因为父亲欠下了巨额债务。\n\n浩介不喜欢这个计划,但他没有选择。他写信给浪矢杂货店求助。\n\n浪矢雄治(杂货店的店主,此时已经去世)的回信是:"你应该和父母在一起。"\n\n但浩介选择了逃亡。他在中途逃离了父母,独自生活。\n\n多年后,他得知父母选择了自杀,以保护他。\n\n他后悔没有听从建议。但如果他听从了,结果会更好吗?\n\n这个问题的答案,只有他自己知道。' },
        { title: '第五章:神圣的助产士', content: '第四个咨询者是一个名叫晴美的女孩。她想辞职创业,但不知道是否应该冒险。\n\n敦也他们回信,给出了详细的商业建议。因为幸平曾经研究过经济。\n\n晴美按照建议创业,取得了成功。她成为了一个成功的女企业家。\n\n多年后,她写信感谢浪矢杂货店。她说:"是你们改变了我的人生。"\n\n但敦也他们不知道的是,晴美正是他们抢劫的那家店的主人。\n\n命运的安排,如此奇妙。' },
        { title: '第六章:祈祷的方式', content: '浪矢雄治是浪矢杂货店的店主。他从1970年开始回答咨询信,持续了十年。\n\n起初,这只是孩子们的恶作剧。但雄治认真对待每一封信,用心回答。\n\n渐渐地,越来越多的大人也开始写信。有家庭的烦恼、事业的困惑、爱情的选择。\n\n雄治总是认真调查,给出诚恳的建议。他说:" map 不是只有一条路。"\n\n但他晚年时,开始怀疑自己的建议是否正确。他会不会误导了别人?\n\n这个疑问折磨着他。他决定测试一下,在报纸上刊登广告,说现在可以收到"空白"的回信。\n\n结果,他收到了一封信。信中说:"您的建议改变了我的人生,谢谢您。"\n\n雄治终于安心了。他知道,自己的建议是有意义的。' },
        { title: '第七章:绿色河的姐妹', content: '第五个咨询者是一对姐妹。她们的母亲去世了,父亲准备再婚。\n\n她们不知道该不该接受新的母亲,于是写信求助。\n\n敦也他们回信说:"你应该试着接受她。"\n\n姐妹俩照做了。新的母亲对她们很好,她们成为了幸福的一家人。\n\n但多年后,她们发现新的母亲曾经是她们的亲生母亲的闺蜜。\n\n她一直在照顾她们,因为这是她对逝去朋友的承诺。\n\n爱,以最意想不到的方式传递。' },
        { title: '第八章:迷宫的出口', content: '敦也他们三个小偷,为什么会来到浪矢杂货店?\n\n因为他们抢劫了晴美的店,但发现晴美并不是他们想象中的坏人。\n\n晴美一直在帮助穷人,回馈社会。他们感到内疚和困惑。\n\n在浪矢杂货店,他们通过回答别人的问题,开始反思自己的人生。\n\n他们意识到,自己不是坏人,只是走错了路。\n\n他们决定回去自首,重新开始人生。\n\n临走前,他们收到了一封信。信是晴美写的,感谢浪矢杂货店的建议。\n\n信的结尾写着:"谢谢你们改变了我的人生。"\n\n他们笑了。原来,他们也改变了他的人生。' },
        { title: '第九章:空白画布', content: '浪矢杂货店的秘密是:它连接了过去和未来。\n\n信可以穿越时空,回答也可以穿越时空。\n\n但为什么会有这样的奇迹?\n\n浪矢雄治说:"因为每个人都有烦恼,每个人都希望被理解。"\n\n"当这种愿望足够强烈时,奇迹就会发生。"\n\n杂货店只是一个媒介。真正重要的是人与人之间的理解和关爱。\n\n每一个咨询者,都在寻找生活的方向。\n\n每一个回答者,都在用自己的经验帮助别人。\n\n这就是浪矢杂货店存在的意义。' },
        { title: '第十章:地图', content: '故事的结尾,敦也他们三个决定自首。\n\n他们给晴美写了一封信,承认了抢劫的事实,并表示愿意赔偿。\n\n他们把信投进了浪矢杂货店的牛奶箱。\n\n然后,他们走出了杂货店,迎接黎明。\n\n他们知道,前方有困难,但他们不再迷茫。\n\n因为他们知道,每个人都有改变的力量。\n\n浪矢雄治在信的结尾写道:" map 是一张白纸,这当然很让人茫然。但如果一切都是白纸,你就可以随心所欲地描绘地图。"\n\n"一切全在你自己。对你来说,一切都是自由的,在你面前是无限的可能。"\n\n"这可是很棒的事啊。我衷心祈祷你可以相信自己,无悔地燃烧自己的人生。"' }
      ]
    },
    {
      id: 'sample10',
      title: '追风筝的人',
      author: '卡勒德·胡赛尼',
      cover: '#ffecd2',
      progress: 50,
      lastRead: '2026-05-13',
      chapters: [
        { title: '第一章:冬天', content: '我成为今天的我,是在1975年某个阴云密布的寒冷冬日,那年我十二岁。我清楚地记得当时自己趴在一堵坍塌的泥墙后面,窥视着那条小巷,旁边是结冰的小溪。\n\n许多年过去了,人们说陈年旧事可以被埋葬,后来我明白他们错了,因为往事会自行爬上来。回首前尘,我意识到在过去二十六年里,自己始终在窥视着那条荒芜的小巷。\n\n我叫阿米尔,出生在阿富汗喀布尔的一个富裕家庭。我的家是一栋宽敞的房子,有一个大花园,里面种着石榴树和玫瑰。\n\n我的父亲是一个成功的商人,他高大强壮,勇敢正义,深受人们尊敬。他修建了喀布尔最美的房子,也修建了孤儿院。他是个了不起的人。\n\n但我总觉得父亲不喜欢我,因为我不像他那样勇敢。我胆小、懦弱,喜欢躲在书堆里。我写的故事他总是看不上,我觉得他对我很失望。\n\n我的母亲在我出生时难产去世了。父亲从未原谅我,虽然他没有说,但我知道。\n\n我们家里有两个仆人,阿里和他的儿子哈桑。阿里是我的父亲的忠实仆人,哈桑则陪伴我长大。\n\n哈桑是哈扎拉人,长着扁平的鼻子和斜斜的眼睛。他和我吃同一母乳长大,我们是最好的朋友,虽然他是我的仆人。\n\n1975年的那个冬天,发生了一件事,彻底改变了我的人生。那件事关于背叛,关于赎罪,关于一个叫做哈桑的男孩。' },
        { title: '第二章:哈桑', content: '哈桑是我的仆人,也是我的朋友。他是哈扎拉人,长着扁平的鼻子和斜斜的眼睛。\n\n哈桑的母亲在生下他不久后就跑了,他的父亲是阿里,我家的仆人。\n\n哈桑和我吃同一母乳长大。他说我说的第一个词是"爸爸",而他说的第一个词是"阿米尔"。\n\n哈桑是我最好的朋友,也是最忠诚的仆人。他总是说:"为你,千千万万遍。"\n\n我们会爬那棵石榴树,用树皮刻下我们的名字:阿米尔和哈桑,喀布尔的苏丹。\n\n我们会在树下读书,我念给他听。哈桑不识字,但他喜欢听故事。\n\n哈桑是个出色的风筝斗士。他能准确地剪断对手的风筝线,让风筝坠落。\n\n每年冬天,喀布尔都会举办风筝大赛。那是我们最期待的日子。' },
        { title: '第三章:风筝大赛', content: '1975年的冬天,风筝大赛如期举行。\n\n我参加了比赛,哈桑是我的风筝线 runner。他负责追踪被我击落的风筝。\n\n我表现出色,一路过关斩将。最后,只剩下一个蓝色的风筝。\n\n我集中注意力,操控我的风筝。终于,我剪断了蓝风筝的线。\n\n我赢了!全场欢呼。哈桑跑过来,眼里满是骄傲:"阿米尔少爷,你赢了!"\n\n"去给我把那个蓝风筝带回来,"我说,"我给你捡回来。"\n\n哈桑点点头,跑进了小巷。那是1975年冬天,他最后一次为我奔跑。\n\n我赢了比赛,但失去了一些更重要的东西。' },
        { title: '第四章:小巷', content: '我跑去找哈桑。我听到了他的声音,在一条小巷里。\n\n我悄悄走过去,看到了让我一生都无法忘记的一幕。\n\n阿塞夫,那个恶霸,正在欺负哈桑。他把哈桑按在墙上,哈桑没有反抗。\n\n我想冲出去,但我害怕。我躲在角落里,看着这一切发生。\n\n哈桑为了保护我的风筝,选择了沉默。\n\n等我终于鼓起勇气时,一切都结束了。哈桑拿着风筝,从巷子里走出来。\n\n他看着我,什么也没说。但我知道,他什么都明白了。\n\n那天晚上,我哭了。不是因为赢了比赛,而是因为我背叛了哈桑。' },
        { title: '第五章:背叛', content: '从那以后,我开始躲避哈桑。我无法面对他,因为我知道自己的懦弱。\n\n哈桑试图修复我们的关系,但我总是冷漠地对待他。\n\n我甚至把新表和钱放在哈桑的床垫下,诬陷他偷东西。\n\n父亲问哈桑:"这是你做的吗?"\n\n哈桑看着我,然后说:"是的。"\n\n父亲原谅了哈桑,但阿里坚持要哈桑离开。\n\n哈桑和阿里搬走了。我站在窗前,看着他们离去。\n\n我知道,我再也见不到哈桑了。我毁掉了我们的友谊。' },
        { title: '第六章:逃亡', content: '1979年,苏联入侵阿富汗。父亲决定逃亡。\n\n我们连夜开车离开喀布尔,经过艰难的旅程,终于到达了巴基斯坦。\n\n后来,我们移民到了美国。我在美国长大,上大学,成为作家,结婚。\n\n父亲在2003年去世了。他走得很安详,但我知道,他心里有一个秘密。\n\n在美国的生活很平静,但我始终无法忘记哈桑。\n\n我试图用写作来逃避,但往事总是找上门来。\n\n2001年,我接到一个电话。拉辛汗,父亲的老朋友,让我去巴基斯坦找他。\n\n他说:"那儿有再次成为好人的路。"' },
        { title: '第七章:真相', content: '我去了巴基斯坦,见到了拉辛汗。他告诉我一个惊人的秘密。\n\n"哈桑是你的同父异母兄弟,"他说,"你的父亲和我保守了这个秘密一辈子。"\n\n我震惊了。父亲对哈桑的好,不是因为他是仆人,而是因为他是他的儿子。\n\n"哈桑死了,"拉辛汗说,"塔利班杀了他和他的妻子。但他们有一个儿子,索拉博。"\n\n"索拉博在喀布尔的孤儿院里。你去把他带回来,这是他应得的。"\n\n"那儿有再次成为好人的路,阿米尔。"\n\n我犹豫了。回到阿富汗意味着面对过去的恐惧。\n\n但我知道,我必须去。为了哈桑,为了索拉博,也为了我自己。' },
        { title: '第八章:喀布尔', content: '我回到了喀布尔。一切都变了,一切又都没变。\n\n城市被炸毁了,街道上满是难民。塔利班的统治让人民生活在恐惧中。\n\n我找到了孤儿院的负责人,但索拉博已经被一个塔利班官员带走了。\n\n通过打听,我得知那个官员就是阿塞夫,我童年时的恶霸。\n\n我来到了阿塞夫的住所。他成了塔利班的高级官员,残酷而冷血。\n\n他认出了我,笑着说:"阿米尔,你终于来了。我等了你很久。"\n\n"你想带走那个男孩?可以,但你必须打赢我。"\n\n阿塞夫开始殴打我。我没有反抗。我笑着,因为我终于得到了解脱。\n\n但索拉博用弹弓射中了阿塞夫的眼睛。我们逃了出来。' },
        { title: '第九章:索拉博', content: '我带着索拉博逃离了喀布尔。他 traumatized,不再说话。\n\n我试图让他开口,但他只是沉默。他的眼神空洞,仿佛灵魂已经死了。\n\n我把他带到了美国。我的妻子索拉雅很喜欢他。\n\n我们试图给他一个温暖的家,但他始终不说话。\n\n一年过去了。有一天,我们去公园。\n\n我看到天空中有一只风筝。我买来一只风筝,问索拉博:"你想放风筝吗?"\n\n他看着我,微微点了点头。\n\n我们开始放风筝。索拉博的脸上露出了微笑。' },
        { title: '第十章:为你,千千万万遍', content: '我剪断了对手的风筝线。索拉博欢呼起来。\n\n"要我帮你捡风筝吗?"我问。\n\n他点了点头。\n\n我跑了起来,去捡那只风筝。我转过身,对索拉博说:"为你,千千万万遍。"\n\n那是哈桑曾经对我说的话。现在,我把它还给了他的儿子。\n\n我知道,过去的错误无法弥补,但我可以创造新的未来。\n\n索拉博的微笑是我收到的最好的礼物。\n\n风筝在天空中飞翔,自由而美丽。\n\n就像哈桑说的那样:为你,千千万万遍。\n\n这不仅仅是一句话,这是爱、忠诚和救赎的承诺。' }
      ]
    }
  ],

  // 渲染书架页面
  render() {
    const container = document.createElement('div');
    container.className = 'bookshelf-page';

    // 顶部导航栏
    const header = this.createHeader();
    container.appendChild(header);

    // 统计信息
    const stats = this.createStats();
    container.appendChild(stats);

    // 书籍列表
    const bookList = this.createBookList();
    container.appendChild(bookList);

    return container;
  },

  // 创建顶部导航栏
  createHeader() {
    const header = document.createElement('div');
    header.className = 'bookshelf-header';

    const title = document.createElement('h1');
    title.className = 'bookshelf-title';
    title.textContent = '📚 我的书架';

    const importBtn = document.createElement('button');
    importBtn.className = 'import-button';
    importBtn.textContent = '📥 导入书籍';
    importBtn.onclick = () => {
      app.router.goTo('/import');
    };

    const settingsBtn = document.createElement('button');
    settingsBtn.className = 'settings-button';
    settingsBtn.textContent = '⚙️';
    settingsBtn.onclick = () => {
      app.router.goTo('/settings');
    };

    header.appendChild(title);
    header.appendChild(importBtn);
    header.appendChild(settingsBtn);

    return header;
  },

  // 创建统计信息
  createStats() {
    const userBooks = storage.loadBooks();
    const totalBooks = this.sampleBooks.length + userBooks.length;

    const stats = document.createElement('div');
    stats.className = 'bookshelf-stats';
    
    if (userBooks.length === 0) {
      stats.textContent = `共 ${this.sampleBooks.length} 本精选书籍`;
    } else {
      stats.textContent = `共 ${totalBooks} 本书(精选 ${this.sampleBooks.length} 本,导入 ${userBooks.length} 本)`;
    }

    return stats;
  },

  // 创建书籍列表
  createBookList() {
    const listContainer = document.createElement('div');
    listContainer.className = 'book-list';

    // 加载用户导入的书籍
    const userBooks = storage.loadBooks();

    // 去重:过滤掉与示例书籍重复的用户书籍
    const sampleBookIds = this.sampleBooks.map(b => b.id);
    const uniqueUserBooks = userBooks.filter(book => !sampleBookIds.includes(book.id));

    // 如果没有书籍,显示提示
    if (uniqueUserBooks.length === 0 && userBooks.length === 0) {
      const emptyTip = document.createElement('div');
      emptyTip.className = 'empty-tip';
      emptyTip.innerHTML = `
        <p style="font-size: 48px; margin-bottom: 15px;">📚</p>
        <p style="font-size: 16px; font-weight: 500; margin-bottom: 8px;">为你精选的10本好书</p>
        <p class="small-tip">涵盖文学、科幻、童话、科普等多种类型</p>
      `;
      listContainer.appendChild(emptyTip);
    }

    // 显示所有示例书籍(10本)
    this.sampleBooks.forEach(book => {
      const bookItem = this.createBookItem(book, true);
      listContainer.appendChild(bookItem);
    });

    // 显示用户导入的书籍(去重后)
    uniqueUserBooks.forEach(book => {
      const bookItem = this.createBookItem(book, false);
      listContainer.appendChild(bookItem);
    });

    return listContainer;
  },

  // 创建书籍卡片
  createBookItem(book, isSample = false) {
    const bookItem = document.createElement('div');
    bookItem.className = 'book-item';

    // 封面
    const cover = document.createElement('div');
    cover.className = 'book-cover';
    cover.style.backgroundColor = book.cover;
    cover.textContent = book.title.substring(0, 2);

    // 书籍信息
    const info = document.createElement('div');
    info.className = 'book-info';

    const title = document.createElement('h3');
    title.className = 'book-title';
    title.textContent = book.title;

    const author = document.createElement('p');
    author.className = 'book-author';
    author.textContent = book.author;

    // 阅读进度
    const progress = document.createElement('div');
    progress.className = 'book-progress';

    const progressText = document.createElement('span');
    progressText.className = 'progress-text';
    progressText.textContent = `阅读进度 ${book.progress}%`;

    const progressBar = document.createElement('div');
    progressBar.className = 'progress-bar';
    const progressFill = document.createElement('div');
    progressFill.className = 'progress-fill';
    progressFill.style.width = `${book.progress}%`;
    progressBar.appendChild(progressFill);

    progress.appendChild(progressText);
    progress.appendChild(progressBar);

    // 上次阅读时间
    const lastRead = document.createElement('p');
    lastRead.className = 'book-last-read';
    lastRead.textContent = `上次阅读:${book.lastRead}`;

    info.appendChild(title);
    info.appendChild(author);
    info.appendChild(progress);
    info.appendChild(lastRead);

    // 箭头
    const arrow = document.createElement('div');
    arrow.className = 'book-arrow';
    arrow.textContent = '›';

    bookItem.appendChild(cover);
    bookItem.appendChild(info);
    bookItem.appendChild(arrow);

    // 点击打开书籍
    bookItem.onclick = () => {
      if (isSample) {
        // 示例书籍:检查是否已存在,不存在才添加
        const existingBook = storage.getBook(book.id);
        if (!existingBook) {
          storage.addBook(book);
        }
      }
      app.router.goTo(`/reader?bookId=${book.id}`);
    };

    return bookItem;
  }
};

为什么需要这个文件?

电子书阅读器的核心功能就是书架管理,如果不实现书架模块,用户无法查看和管理自己的书籍。书架模块让应用具备完整的书籍管理、进度追踪、去重检查等功能,提供流畅的用户体验。

书架实现的关键点

要点 说明
对象字面量模式 使用 const bookshelfPage = {} 而非 class,符合 ArkTS 编码规范
10本示例书籍 每本书包含10章完整内容,涵盖文学、科幻、童话、科普等多种类型
去重逻辑 通过 sampleBookIds 过滤,避免用户导入与示例重复的书籍
DOM 动态创建 使用 document.createElement 而非 innerHTML,提升安全性
路由集成 通过 app.router.goTo() 实现页面跳转,统一路由管理
reader.js ------ 阅读器核心与字体自适应

文件位置:web_engine/src/main/resources/resfile/resources/app/ebook-reader/js/reader.js

最后在阅读页面中实现阅读器核心功能,包括章节内容渲染、字体大小自适应、翻页控制等。关键是通过9档屏幕适配算法动态计算最佳字体大小。

实际项目代码结构

bash 复制代码
// 阅读器核心模块
const readerPage = {
  currentBook: null,
  currentChapterIndex: 0,
  settings: {},

  // 加载书籍
  loadBook(bookId) {
    this.currentBook = storage.getBook(bookId);
    this.settings = storage.loadSettings();
    
    if (!this.currentBook) {
      alert('书籍不存在');
      app.router.goTo('/');
      return;
    }

    // 加载阅读进度
    const progress = storage.loadReadingProgress(bookId);
    this.currentChapterIndex = progress.chapterIndex || 0;
  },

  // 渲染阅读器页面
  render() {
    const container = document.createElement('div');
    container.className = 'reader-page';

    // 顶部导航栏
    const header = this.createHeader();
    container.appendChild(header);

    // 阅读内容区
    const content = this.createContent();
    container.appendChild(content);

    // 底部导航
    const footer = this.createFooter();
    container.appendChild(footer);

    return container;
  },

  // 创建顶部导航栏
  createHeader() {
    const header = document.createElement('div');
    header.className = 'reader-header';

    const backBtn = document.createElement('button');
    backBtn.className = 'back-button';
    backBtn.textContent = '← 返回';
    backBtn.onclick = () => {
      // 保存阅读进度
      this.saveProgress();
      app.router.goTo('/');
    };

    const title = document.createElement('h2');
    title.className = 'reader-title';
    title.textContent = this.currentBook.title;

    header.appendChild(backBtn);
    header.appendChild(title);

    return header;
  },

  // 创建阅读内容区
  createContent() {
    const contentContainer = document.createElement('div');
    contentContainer.className = 'reader-content-container';

    const chapters = this.currentBook.chapters || [{ title: '第一章', content: this.currentBook.content }];
    const chapter = chapters[this.currentChapterIndex];

    if (!chapter) {
      contentContainer.innerHTML = '<p>章节不存在</p>';
      return contentContainer;
    }

    // 章节标题
    const chapterTitle = document.createElement('h3');
    chapterTitle.className = 'chapter-title';
    chapterTitle.textContent = chapter.title;

    // 内容区域
    const content = document.createElement('div');
    content.className = 'reader-content';
    content.id = 'reader-content';

    // 计算自适应字体大小
    const adaptiveFontSize = this.getAdaptiveFontSize();

    // 分割段落并渲染
    const paragraphs = this.splitParagraphs(chapter.content);
    paragraphs.forEach(p => {
      const pElement = document.createElement('p');
      pElement.className = 'paragraph';
      pElement.textContent = p;
      pElement.style.fontSize = `${adaptiveFontSize}px`;
      pElement.style.lineHeight = this.settings.lineHeight;
      pElement.style.marginBottom = `${this.settings.paragraphSpacing}px`;
      content.appendChild(pElement);
    });

    contentContainer.appendChild(chapterTitle);
    contentContainer.appendChild(content);

    // 恢复滚动位置
    setTimeout(() => {
      const progress = storage.loadReadingProgress(this.currentBook.id);
      if (progress.scrollPosition) {
        contentContainer.scrollTop = progress.scrollPosition;
      }
    }, 100);

    // 监听滚动保存进度
    contentContainer.onscroll = () => {
      this.saveProgress();
    };

    // 监听窗口大小变化,重新计算字体大小
    window.addEventListener('resize', () => {
      const newFontSize = this.getAdaptiveFontSize();
      const allParagraphs = contentContainer.querySelectorAll('.paragraph');
      allParagraphs.forEach(p => {
        p.style.fontSize = `${newFontSize}px`;
      });
    });

    return contentContainer;
  },

  // 计算自适应字体大小
  getAdaptiveFontSize() {
    const screenWidth = window.innerWidth;
    const screenHeight = window.innerHeight;
    const baseSize = this.settings.fontSize || 16;
    
    // 更精细的自适应算法
    let adaptiveSize = baseSize;
    
    if (screenWidth < 480) {
      // 小屏手机
      adaptiveSize = Math.max(13, baseSize - 3);
    } else if (screenWidth < 768) {
      // 大屏手机
      adaptiveSize = Math.max(14, baseSize - 1);
    } else if (screenWidth < 1024) {
      // 平板
      adaptiveSize = baseSize;
    } else if (screenWidth < 1280) {
      // 小屏笔记本
      adaptiveSize = baseSize + 2;
    } else if (screenWidth < 1440) {
      // 标准笔记本
      adaptiveSize = baseSize + 3;
    } else if (screenWidth < 1600) {
      // 中等屏幕
      adaptiveSize = baseSize + 4;
    } else if (screenWidth < 1920) {
      // 大屏显示器
      adaptiveSize = baseSize + 5;
    } else if (screenWidth < 2560) {
      // 2K屏幕
      adaptiveSize = baseSize + 6;
    } else {
      // 4K及以上
      adaptiveSize = baseSize + 8;
    }
    
    // 根据屏幕高度微调(竖屏时字体稍小)
    if (screenHeight < screenWidth && screenWidth < 1024) {
      adaptiveSize = Math.max(12, adaptiveSize - 1);
    }
    
    return Math.round(adaptiveSize);
  },

  // 创建底部导航
  createFooter() {
    const footer = document.createElement('div');
    footer.className = 'reader-footer';

    const chapters = this.currentBook.chapters || [{ title: '第一章', content: this.currentBook.content }];
    const totalChapters = chapters.length;

    // 上一章按钮
    const prevBtn = document.createElement('button');
    prevBtn.className = 'nav-button';
    prevBtn.textContent = this.currentChapterIndex > 0 ? '上一章' : '已是第一章';
    prevBtn.disabled = this.currentChapterIndex === 0;
    prevBtn.onclick = () => {
      if (this.currentChapterIndex > 0) {
        this.saveProgress();
        this.currentChapterIndex--;
        this.refreshContent();
      }
    };

    // 章节信息
    const chapterInfo = document.createElement('div');
    chapterInfo.className = 'chapter-info';
    chapterInfo.textContent = `第 ${this.currentChapterIndex + 1} / ${totalChapters} 章`;

    // 下一章按钮
    const nextBtn = document.createElement('button');
    nextBtn.className = 'nav-button';
    nextBtn.textContent = this.currentChapterIndex < totalChapters - 1 ? '下一章' : '已是最后一章';
    nextBtn.disabled = this.currentChapterIndex >= totalChapters - 1;
    nextBtn.onclick = () => {
      if (this.currentChapterIndex < totalChapters - 1) {
        this.saveProgress();
        this.currentChapterIndex++;
        this.refreshContent();
      }
    };

    footer.appendChild(prevBtn);
    footer.appendChild(chapterInfo);
    footer.appendChild(nextBtn);

    return footer;
  },

  // 分割段落
  splitParagraphs(content) {
    return content.split('\n\n').filter(p => p.trim().length > 0);
  },

  // 刷新内容
  refreshContent() {
    const contentContainer = document.querySelector('.reader-content-container');
    if (contentContainer) {
      const newContent = this.createContent();
      contentContainer.parentNode.replaceChild(newContent, contentContainer);
    }
  },

  // 保存阅读进度
  saveProgress() {
    if (!this.currentBook) return;

    const contentContainer = document.querySelector('.reader-content-container');
    const scrollPosition = contentContainer ? contentContainer.scrollTop : 0;

    const chapters = this.currentBook.chapters || [{ title: '第一章', content: this.currentBook.content }];
    const progress = (this.currentChapterIndex + 1) / chapters.length * 100;

    // 更新书籍进度
    storage.updateBook(this.currentBook.id, {
      progress: Math.round(progress),
      lastRead: new Date().toISOString().split('T')[0]
    });

    // 保存详细进度
    storage.saveReadingProgress(this.currentBook.id, {
      chapterIndex: this.currentChapterIndex,
      scrollPosition: scrollPosition
    });
  }
};

阅读器实现的关键点

要点 说明
9档屏幕适配 根据窗口宽度智能计算最佳字体大小,覆盖480px-4K+全场景,基于用户设置的 baseSize 进行调整
resize 监听 实时响应窗口大小变化,动态调整所有段落的字体大小
章节渲染 将章节内容按段落分割,生成 DOM 元素,使用 createElement 而非 innerHTML
进度保存 切换章节、滚动时自动保存阅读进度到 LocalStorage,包括 chapterIndex 和 scrollPosition
对象字面量模式 使用 const readerPage = {} 而非 class,保持代码风格统一
代码修改小结

总共无需修改任何鸿蒙原生代码,只需要确保 Electron 应用本身的代码正确:

文件 作用 技术栈
main.js Electron 主进程入口,创建窗口和系统托盘 Node.js/Electron API
storage.js LocalStorage 数据持久化,管理书籍、设置、进度 浏览器原生 API
bookshelf.js 书架管理与10本示例书籍,去重逻辑 Vanilla JavaScript (对象字面量)
reader.js 阅读器与9档字体自适应算法 Vanilla JavaScript (对象字面量)
settings.js 设置管理与主题切换 Vanilla JavaScript
import.js TXT 文件导入与解析 FileReader API

整体架构流程

bash 复制代码
main.js (Electron 主进程)
  ↓ 创建 BrowserWindow
  ↓ 加载 ebook-reader/index.html
    ↓
index.html (渲染进程入口)
  ↓ 加载 CSS/JS
    ↓
app.js (应用路由与主题管理)
  ↓ 初始化路由
    ↓
bookshelf.js (书架页面)
  ↓ 用户点击书籍
    ↓
reader.js (阅读器页面)
  ↓ 加载章节内容
  ↓ 应用字体自适应
  ↓ 保存阅读进度

桌面级电子书阅读器效果展示:沉浸式阅读与动态主题交互实览

HarmonyOS Electron 适配踩坑实录:编译、签名与权限问题全解析

基于 libelectron 项目在 HarmonyOS 5.0.5(17) SDK 上的实战经验总结

Q1: 为什么编译时会报大量 ArkTS 错误?

A: 项目代码基于更高版本的 SDK 开发,而本地使用的是 SDK 5.0.5(17),部分 API 尚未发布或已变更。

常见错误

  • webNativeMessagingExtensionManager 不存在
  • dataShare 模块不存在
  • startupManager.getAutoStartupStatusForSelf() 方法不存在
  • window.shiftAppWindowTouchEvent() 方法不存在

解决方案

bash 复制代码
// 注释掉不可用的 API,添加降级处理
// TODO: XXX API is not available in current SDK
callback(false); // 返回默认值

Q2: 权限配置错误如何解决?

A: HarmonyOS 权限分为三类:预定义权限、自定义权限、系统权限。

典型错误

bash 复制代码
The ohos.permission.WEB_NATIVE_MESSAGING permission under requestPermissions 
must be a value that is predefined within the SDK or a custom one.

解决方案

bash 复制代码
// module.json5
// 系统权限不能作为自定义权限声明,暂时注释掉
// {
//   "name": "ohos.permission.WEB_NATIVE_MESSAGING"
// },

规则

  • 预定义权限:直接使用
  • 自定义权限:先在 definePermissions 定义,再在 requestPermissions 声明
  • 系统权限:需要华为授权,普通开发者不能使用

Q3: HAP 安装失败,报错 no signature file?

A: 缺少签名配置。

解决方案

  1. DevEco Studio → File → Project Structure → Signing Configs
  2. 勾选 Automatically generate signature
  3. 保存后重新构建

Q4: 安装失败,报错 grant request permissions failed?

A: 声明了无法授予的权限(如系统权限)。

解决方案

  • 注释掉问题权限声明
  • 重新构建安装

Q5: ArkTS 编译报 Unexpected token 或语法错误?

A: ArkTS 对语法要求极其严格。

常见坑

① 枚举不能有尾随逗号

bash 复制代码
// 错误
enum Menu { A = 0, B = 1, }

// 正确
enum Menu { A = 0, B = 1 }

② @Builder 中枚举访问受限

bash 复制代码
// 在 @Builder 中直接访问枚举会报错
.onClick(() => params.onResult(MenuItem.kOpen))

// 使用数字字面量
.onClick(() => params.onResult(0))

③ 禁止使用 any/unknown 类型

bash 复制代码
// 错误
let promise = ... // 推断为 any

// 必须显式声明
let promise: Promise<void> | undefined = ...

Q6: 系统资源找不到怎么办?

A: SDK 版本不同,系统资源名可能变更。

示例

bash 复制代码
// 旧资源名
$r('sys.color.status_button_text_color_focus')

// 新资源名
$r('sys.color.ohos_id_color_text_primary')

Q7: API 方法不存在或改名了?

A: SDK 迭代过程中 API 会调整。

示例

bash 复制代码
// 导入名变更
import { autoStartupManager } ...  // 旧版
import { startupManager } ...       // 新版

// 方法名变更
window.setContentAspectRatio(...)   // 旧版
window.setAspectRatio(...)          // 新版
相关推荐
坚果派·白晓明20 小时前
【鸿蒙PC】SDL3 适配:AtomCode + Skills 快速集成 NAPI 测试工具
c++·华为·ai编程·harmonyos·atomcode
YM52e1 天前
男孩子在外自我保护指南——用鸿蒙 ArkTS 构建交互式安全教育应用
学习·安全·华为·harmonyos·鸿蒙·鸿蒙系统
祭曦念1 天前
古诗小集开发实战:从零开发一款 HarmonyOS 古诗鉴赏应用
pytorch·深度学习·harmonyos
全栈若城1 天前
HarmonyOS AppUtil 应用配置控制:颜色模式/灰度/字体/语言/键盘避让详解
华为·harmonyos·arkts·harmonyos6·键盘避让·字体缩放
FrameNotWork1 天前
HarmonyOS 6.1 Lottie动画集成完全指南:从踩坑到精通
华为·harmonyos
三声三视1 天前
Electron 本地图片在鸿蒙 PC 上白图,我注册了个自定义协议
electron·harmonyos·鸿蒙
李二。1 天前
日历日程管理工具 — 基于 HarmonyOS NEXT (API 23+) 的 ArkTS 纯声明式实现
华为·harmonyos
金启攻1 天前
鸿蒙原生应用实战(三):搜索与详情页 —— 多维度筛选与动态路由
华为·harmonyos
祭曦念1 天前
鸿蒙原生Gauge仪表盘组件深度实践
华为·harmonyos