📚 從零構建桌面寫作軟件的書籍管理系統:Electron + Vue 3 實戰指南
💡 本文深入探討了基于 Electron + Vue 3 技術棧的桌面寫作軟件中書籍管理系統的設計與實現,涵蓋了書籍的創建、編輯、刪除等核心功能的完整技術方案,為開發者提供一套完整的書籍管理解決方案。
📋 目錄
- 項目背景
- 技術架構概覽
- 書籍管理核心功能實現
- 核心功能實現細節
- 用戶體驗優化
- 技術亮點總結
- 擴展性考慮
- 總結與展望
🎯 項目背景
51mazi 是一款專為小說創作者設計的桌面寫作軟件,其核心功能之一就是完善的書籍管理系統。作者需要一個直觀、高效的書籍管理界面來組織和管理自己的創作項目,包括書籍的創建、編輯、刪除以及元數據管理等功能。
📖 書籍管理界面
直觀的書籍管理界面 - 支持創建、編輯、刪除等操作
? 功能特性
- 📝 書籍創建: 支持多種類型書籍創建
- ?? 書籍編輯: 實時編輯書籍信息和元數據
- 🗑? 書籍刪除: 安全刪除確認機制
- 📊 數據統計: 字數統計和更新記錄
- 🎨 界面美觀: 書籍卡片式展示
- 🔄 實時同步: 狀態管理和數據同步
🏗? 技術架構概覽
核心技術棧
- Electron 35.0.3: 跨平臺桌面應用框架
- Vue 3.5.13: 漸進式 JavaScript 框架
- Element Plus 2.10.1: 企業級 UI 組件庫
- Pinia 3.0.1: Vue 3 官方推薦的狀態管理庫
系統架構設計
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 渲染進程 │ │ 主進程 │ │ 文件系統 │
│ (Vue 3) │?──?│ (Node.js) │?──?│ (本地存儲) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
📁 項目目錄結構
51mazi/
├── src/
│ ├── main/ # Electron 主進程
│ │ └── index.js # 主進程入口文件
│ ├── preload/ # 預加載腳本
│ │ └── index.js # IPC 通信接口
│ └── renderer/ # 渲染進程 (Vue 應用)
│ ├── src/
│ │ ├── components/ # 組件庫
│ │ │ ├── Bookshelf.vue # 書籍列表組件
│ │ │ └── Book.vue # 書籍卡片組件
│ │ ├── views/ # 頁面視圖
│ │ ├── stores/ # 狀態管理
│ │ │ └── index.js # Pinia 狀態管理
│ │ ├── service/ # 服務層
│ │ │ └── books.js # 書籍相關 API
│ │ └── utils/ # 工具函數
│ └── assets/ # 靜態資源
🔧 書籍管理核心功能實現
1. 📊 書籍數據結構設計
每本書籍包含以下核心信息:
const bookData = {id: 'unique_id', // 唯一標識name: '書籍名稱', // 書名type: 'novel', // 類型typeName: '小說', // 類型名稱targetCount: 100000, // 目標字數intro: '書籍簡介', // 簡介createdAt: '2024-01-01', // 創建時間updatedAt: '2024-01-01', // 更新時間totalWords: 50000 // 當前字數
}
💡 完整數據結構請查看: src/renderer/src/components/Bookshelf.vue
2. 🗂? 主進程文件操作層
在主進程中實現文件系統操作,確保數據持久化:
// src/main/index.js
import { ipcMain } from 'electron'
import fs from 'fs'
import { join } from 'path'// 創建書籍
ipcMain.handle('create-book', async (event, bookInfo) => {const safeName = bookInfo.name.replace(/[\\/:*?"<>|]/g, '_')const booksDir = store.get('booksDir')const bookPath = join(booksDir, safeName)// 創建書籍目錄結構if (!fs.existsSync(bookPath)) {fs.mkdirSync(bookPath)}// 寫入元數據文件const meta = {...bookInfo,createdAt: new Date().toLocaleString(),updatedAt: new Date().toLocaleString()}fs.writeFileSync(join(bookPath, 'mazi.json'), JSON.stringify(meta, null, 2))// 創建默認目錄結構const textPath = join(bookPath, '正文')const notesPath = join(bookPath, '筆記')fs.mkdirSync(textPath, { recursive: true })fs.mkdirSync(notesPath, { recursive: true })return true
})// 刪除書籍
ipcMain.handle('delete-book', async (event, { name }) => {const booksDir = store.get('booksDir')const bookPath = join(booksDir, name)if (fs.existsSync(bookPath)) {fs.rmSync(bookPath, { recursive: true })return true}return false
})// 編輯書籍
ipcMain.handle('edit-book', async (event, bookInfo) => {const booksDir = store.get('booksDir')const bookPath = join(booksDir, bookInfo.name)if (fs.existsSync(bookPath)) {const metaPath = join(bookPath, 'mazi.json')const existingMeta = JSON.parse(fs.readFileSync(metaPath, 'utf-8'))const mergedMeta = { ...existingMeta, ...bookInfo }fs.writeFileSync(metaPath, JSON.stringify(mergedMeta, null, 2))return true}return false
})
💡 完整主進程代碼請查看: src/main/index.js
3. 🔌 渲染進程服務層
在渲染進程中封裝 API 調用,提供統一的接口:
// src/renderer/src/service/books.js
export function createBook(bookInfo) {return window.electron.createBook(bookInfo)
}export function updateBook(bookInfo) {return window.electron.editBook(bookInfo)
}export async function deleteBook(name) {const dir = await getBookDir()return window.electron.deleteBook(dir, name)
}export async function readBooksDir() {const mainStore = useMainStore()const dir = await getBookDir()if (!dir) return []const books = await window.electron.readBooksDir(dir)mainStore.setBooks(books)return books
}
💡 完整服務層代碼請查看: src/renderer/src/service/books.js
4. 🎨 用戶界面組件設計
4.1 📚 書籍列表組件 (Bookshelf.vue)
<template><div class="bookshelf"><!-- 頂部操作欄 --><div class="top-bar"><el-button type="primary" @click="handleNewBook"><el-icon><Plus /></el-icon>新建書籍</el-button></div><!-- 書籍列表 --><div class="books-box"><Bookv-for="book in books":key="book.id":name="book.name":type="book.type":type-name="book.typeName":total-words="book.totalWords":updated-at="book.updatedAt"@on-open="onOpen(book)"@on-edit="onEdit(book)"@on-delete="onDelete(book)"/></div></div>
</template>
💡 完整書籍列表組件代碼請查看: src/renderer/src/components/Bookshelf.vue
4.2 📖 書籍卡片組件 (Book.vue)
<template><div class="book" @click="emit('onOpen')" @contextmenu.prevent="showMenu($event)"><div class="spine"></div><div class="cover-bg"><div class="title-block"><div class="vertical-title">{{ name }}</div></div></div><div class="info"><div class="type">{{ typeName }}</div><div class="stats"><div class="word-count">字數:{{ totalWords }}</div><div class="update-time">更新:{{ updatedAt }}</div></div></div></div>
</template>
💡 完整書籍卡片組件代碼請查看: src/renderer/src/components/Book.vue
5. 🗃? 狀態管理設計
使用 Pinia 進行全局狀態管理:
// src/renderer/src/stores/index.js
import { defineStore } from 'pinia'
import { ref } from 'vue'export const useMainStore = defineStore('main', () => {const books = ref([])function setBooks(newBooks) {books.value = newBooks}function addBook(book) {books.value.push(book)}function removeBook(bookId) {const index = books.value.findIndex(book => book.id === bookId)if (index > -1) {books.value.splice(index, 1)}}return {books,setBooks,addBook,removeBook}
})
💡 完整狀態管理代碼請查看: src/renderer/src/stores/index.js
?? 核心功能實現細節
1. 📝 書籍創建流程
async function handleConfirm() {formRef.value.validate(async (valid) => {if (valid) {// 校驗同名書籍const exists = books.value.some((b) => b.name === form.value.name && (!isEdit.value || b.id !== editBookId.value))if (exists) {ElMessage.error('已存在同名書籍,不能重復創建!')return}const randomId = Date.now().toString() + Math.floor(Math.random() * 10000).toString()const bookData = {id: randomId,name: form.value.name,type: form.value.type,typeName: BOOK_TYPES.find((item) => item.value === form.value.type)?.label,targetCount: form.value.targetCount,intro: form.value.intro}await createBook(bookData)dialogVisible.value = falseawait readBooksDir()}})
}
💡 完整創建流程代碼請查看: src/renderer/src/components/Bookshelf.vue
2. ?? 書籍編輯功能
function onEdit(book) {isEdit.value = trueeditBookId.value = book.iddialogVisible.value = trueform.value.name = book.nameform.value.type = book.typeform.value.targetCount = book.targetCountform.value.intro = book.intro
}
💡 完整編輯功能代碼請查看: src/renderer/src/components/Bookshelf.vue
3. 🗑? 書籍刪除確認
async function onDelete(book) {try {await ElMessageBox.confirm(`確定要刪除《${book.name}》嗎?此操作不可恢復!`, '刪除確認', {confirmButtonText: '刪除',cancelButtonText: '取消',type: 'warning'})await deleteBook(book.name)ElMessage.success('刪除成功')await readBooksDir()} catch (e) {// 用戶取消刪除console.log(e)}
}
💡 完整刪除功能代碼請查看: src/renderer/src/components/Bookshelf.vue
🎨 用戶體驗優化
1. 🖱? 右鍵菜單支持
function showMenu(e) {menuX.value = e.clientXmenuY.value = e.clientYmenuVisible.value = truedocument.addEventListener('click', hideMenu)
}function hideMenu() {menuVisible.value = falsedocument.removeEventListener('click', hideMenu)
}
💡 完整右鍵菜單代碼請查看: src/renderer/src/components/Book.vue
2. ? 表單驗證
const rules = ref({name: [{ required: true, message: '請輸入書籍名稱', trigger: 'blur' }],type: [{ required: true, message: '請選擇類型', trigger: 'blur' }],targetCount: [{ required: true, message: '請輸入目標字數', trigger: 'blur' }],intro: [{ required: true, message: '請輸入簡介', trigger: 'blur' }]
})
💡 完整表單驗證代碼請查看: src/renderer/src/components/Bookshelf.vue
3. 💬 錯誤處理與用戶反饋
// 創建成功提示
ElMessage.success('創建成功')// 刪除確認
await ElMessageBox.confirm(`確定要刪除《${book.name}》嗎?此操作不可恢復!`, '刪除確認', {confirmButtonText: '刪除',cancelButtonText: '取消',type: 'warning'
})
💡 完整錯誤處理代碼請查看: src/renderer/src/components/Bookshelf.vue
? 技術亮點總結
1. 🔄 跨進程通信設計
- 使用 Electron 的 IPC 機制實現主進程與渲染進程的安全通信
- 通過 contextBridge 暴露安全的 API 接口
2. 🗂? 文件系統管理
- 自動創建標準化的書籍目錄結構
- 元數據 JSON 文件存儲,便于擴展和維護
- 文件名安全處理,避免特殊字符沖突
3. 🗃? 狀態管理優化
- 使用 Pinia 實現響應式狀態管理
- 統一的數據流,確保 UI 與數據同步
4. 🎨 用戶體驗設計
- 直觀的書籍卡片展示
- 右鍵菜單快速操作
- 完善的表單驗證和錯誤提示
🔮 擴展性考慮
1. 📚 書籍類型擴展
const BOOK_TYPES = [{ value: 'novel', label: '小說' },{ value: 'essay', label: '散文' },{ value: 'poetry', label: '詩歌' },{ value: 'script', label: '劇本' }
]
💡 完整書籍類型配置請查看: src/renderer/src/constants/config.js
2. 📊 元數據擴展
const bookMeta = {// 基礎信息id: 'unique_id',name: '書籍名稱',type: 'novel',// 擴展信息tags: ['標簽1', '標簽2'],status: 'writing', // writing, completed, pausedcoverImage: 'cover.jpg',wordCountGoal: 100000,// 統計信息currentWordCount: 50000,chaptersCount: 10,lastModified: '2024-01-01'
}
💡 完整元數據結構請查看: src/renderer/src/components/Bookshelf.vue
📝 總結與展望
通過 Electron + Vue 3 技術棧,我們成功構建了一個功能完善、用戶體驗優秀的書籍管理系統。該系統不僅滿足了基本的 CRUD 操作需求,還在用戶體驗、數據安全、擴展性等方面進行了深度優化。
🎯 關鍵成功因素
- 🏗? 架構清晰: 主進程負責文件操作,渲染進程負責 UI 交互
- 🔒 數據安全: 通過 IPC 機制確保跨進程通信的安全性
- 🎨 用戶體驗: 直觀的界面設計和流暢的操作體驗
- 🔧 可維護性: 模塊化的代碼結構和統一的狀態管理
🚀 技術價值
- 跨平臺支持: 基于 Electron 實現 Windows、macOS、Linux 全平臺支持
- 高性能: 使用 Vue 3 的 Composition API 和 Pinia 狀態管理
- 可擴展: 模塊化的組件設計和清晰的代碼結構
- 用戶友好: 完善的錯誤處理和用戶反饋機制
這個書籍管理系統為整個寫作軟件奠定了堅實的基礎,為后續的功能擴展提供了良好的架構支持。
📚 相關鏈接
- 項目地址: GitHub - 51mazi,給個 Star 哦~
- Electron 官方文檔: Electron Documentation
- Vue 3 官方文檔: Vue 3 Documentation
- Pinia 狀態管理: Pinia Documentation
🏷? 標簽
#Electron
#Vue3
#書籍管理
#桌面應用
#前端開發
#狀態管理
#用戶體驗
💡 如果這篇文章對你有幫助,請給個 ?? 支持一下!