diff --git a/index.html b/index.html index aed3f33..26d14dc 100644 --- a/index.html +++ b/index.html @@ -88,6 +88,10 @@ --white-60: #ffffff99; /* 60% white */ --white-80: #ffffffcc; /* 80% white */ --white-90: #ffffffe6; /* 90% white */ + + --confirmFontSize: 0.8rem; + --confirmBg: rgba(0, 0, 0, 0.15); + --confirmBtnColor: #000000cc; } body { diff --git a/package.json b/package.json index fbca4af..241562c 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,6 @@ "moment": "^2.30.1", "pinia": "^3.0.3", "vue": "^3.5.22", - "vue-draggable-plus": "^0.6.0", "vue-router": "^4.5.1" }, "devDependencies": { diff --git a/src/App.vue b/src/App.vue index 41b645b..c735b4f 100644 --- a/src/App.vue +++ b/src/App.vue @@ -13,19 +13,17 @@ - + + + + + + diff --git a/src/components/FolderManage.vue b/src/components/FolderManage.vue index 025b4b7..d3e9ae7 100644 --- a/src/components/FolderManage.vue +++ b/src/components/FolderManage.vue @@ -1,135 +1,335 @@ - - - - - + + + + + diff --git a/src/components/Modal.vue b/src/components/Modal.vue index bd6d6e5..13fcb10 100644 --- a/src/components/Modal.vue +++ b/src/components/Modal.vue @@ -1,206 +1,347 @@ - - - - - + + + + + diff --git a/src/pages/NoteListPage.vue b/src/pages/NoteListPage.vue index 58d75a6..c3fe96a 100644 --- a/src/pages/NoteListPage.vue +++ b/src/pages/NoteListPage.vue @@ -1,430 +1,473 @@ - - - - + + + + diff --git a/src/stores/useAppStore.js b/src/stores/useAppStore.js index d92427b..dc7ea11 100644 --- a/src/stores/useAppStore.js +++ b/src/stores/useAppStore.js @@ -1,336 +1,383 @@ -import { defineStore } from 'pinia' -import * as storage from '../utils/indexedDBStorage' -import { getCurrentDateTime, getPastDate } from '../utils/dateUtils' - -/** - * 应用状态管理Store - * 使用Pinia进行状态管理,包含便签、文件夹和设置数据 - */ -export const useAppStore = defineStore('app', { - /** - * 状态定义 - * 包含应用的核心数据:便签列表、文件夹列表和设置 - */ - state: () => ({ - notes: [], // 便签列表 - folders: [], // 文件夹列表 - settings: { cloudSync: false, darkMode: false }, // 应用设置 - }), - - /** - * 计算属性 - * 基于状态派生的计算值 - */ - getters: { - /** - * 计算加星便签数量 - * @param {Object} state - 当前状态对象 - * @returns {number} 加星便签的数量 - */ - starredNotesCount: state => { - return state.notes.filter(note => note.isStarred).length - }, - - /** - * 计算所有便签数量 - * @param {Object} state - 当前状态对象 - * @returns {number} 所有便签的数量 - */ - allNotesCount: state => { - return state.notes.length - }, - }, - - /** - * 状态变更操作 - * 包含所有修改状态的方法 - */ - actions: { - /** - * 初始化数据 - * 从Storage加载便签、文件夹和设置数据 - * 如果没有数据则加载预设的mock数据 - * @returns {Promise} - */ - async loadData() { - try { - // 从Storage加载数据 - const loadedNotes = await storage.getNotes() - const loadedFolders = await storage.getFolders() - const loadedSettings = await storage.getSettings() - - // 如果没有数据,则加载mock数据 - if (loadedNotes.length === 0 && loadedFolders.length === 0) { - this.loadMockData() - } else { - // 否则使用加载的数据 - this.notes = loadedNotes - this.folders = loadedFolders - this.settings = loadedSettings - } - } catch (error) { - console.error('Error loading data:', error) - } - }, - - /** - * 加载预设的mock数据 - * 用于开发和演示目的,提供示例便签、文件夹和设置 - * @returns {Promise} - */ - async loadMockData() { - // Mock notes - 使用固定的日期值以避免每次运行时变化 - const fixedCurrentDate = '2025-10-12T10:00:00.000Z'; - - // 预设的便签示例数据 - 仅保留一条关于应用功能介绍和示例的便签 - const mockNotes = [ - { - id: '1', - title: '欢迎使用锤子便签', - content: '

这是一个功能强大的便签应用,您可以在这里记录您的想法、待办事项等。


功能介绍

1. 创建和编辑便签
2. 为便签加星和置顶
3. 将便签分类到文件夹
4. 搜索便签内容
5. 回收站功能


编辑器功能演示


标题格式

点击标题按钮可创建居中的标题


待办事项

这是一个待办事项
这是一个已完成的待办事项

列表格式

  • 无序列表项1
  • 无序列表项2

文本格式

加粗文本


引用格式

这是一段引用文本
可以用来引用他人的话语

图片

点击图片按钮可以插入图片,长按图片可以拖拽排序

', - createdAt: fixedCurrentDate, - updatedAt: fixedCurrentDate, - folderId: null, - isStarred: true, // 加星便签 - isTop: true, // 置顶便签 - hasImage: true, // 包含图片 - isDeleted: false, // 未删除 - deletedAt: null, - } - ] - - // Mock folders - 使用固定的日期值 - // 预设的文件夹示例数据 - const mockFolders = [ - { - id: 'folder1', - name: '工作', - createdAt: '2025-10-12T10:00:00.000Z', - }, - { - id: 'folder2', - name: '个人', - createdAt: '2025-10-12T10:00:00.000Z', - }, - { - id: 'folder3', - name: '学习', - createdAt: '2025-10-12T10:00:00.000Z', - }, - ] - - // Mock settings - // 预设的设置示例数据 - const mockSettings = { - cloudSync: false, // 云同步关闭 - darkMode: false, // 深色模式关闭 - } - - // 更新store状态 - this.notes = mockNotes - this.folders = mockFolders - this.settings = mockSettings - - // 保存到Storage - await storage.saveNotes(mockNotes) - await storage.saveFolders(mockFolders) - await storage.saveSettings(mockSettings) - }, - - /** - * 保存便签数据到Storage - * @returns {Promise} - */ - async saveNotes() { - try { - await storage.saveNotes(this.notes) - } catch (error) { - console.error('Error saving notes:', error) - } - }, - - /** - * 保存文件夹数据到Storage - * @returns {Promise} - */ - async saveFolders() { - try { - await storage.saveFolders(this.folders) - } catch (error) { - console.error('Error saving folders:', error) - } - }, - - /** - * 保存设置数据到Storage - * @returns {Promise} - */ - async saveSettings() { - try { - await storage.saveSettings(this.settings) - } catch (error) { - console.error('Error saving settings:', error) - } - }, - - /** - * 便签操作函数 - */ - - /** - * 添加新便签 - * @param {Object} note - 便签对象 - * @returns {Promise} 新创建的便签对象 - */ - async addNote(note) { - try { - const newNote = await storage.addNote(note) - this.notes.push(newNote) - - return newNote - } catch (error) { - console.error('Error adding note:', error) - throw error - } - }, - - /** - * 更新便签 - * @param {string} id - 便签ID - * @param {Object} updates - 要更新的属性对象 - * @returns {Promise} 更新后的便签对象 - */ - async updateNote(id, updates) { - try { - const updatedNote = await storage.updateNote(id, updates) - if (updatedNote) { - const index = this.notes.findIndex(note => note.id === id) - if (index !== -1) { - this.notes[index] = updatedNote - } - } - return updatedNote - } catch (error) { - console.error('Error updating note:', error) - throw error - } - }, - - /** - * 删除便签 - * @param {string} id - 要删除的便签ID - * @returns {Promise} 删除成功返回true,失败返回false - */ - async deleteNote(id) { - try { - const result = await storage.deleteNote(id) - if (result) { - this.notes = this.notes.filter(note => note.id !== id) - } - return result - } catch (error) { - console.error('Error deleting note:', error) - throw error - } - }, - - /** - * 将便签移至回收站 - * 将便签标记为已删除状态,并记录删除时间 - * @param {string} id - 便签ID - * @returns {Promise} 更新后的便签对象 - */ - async moveToTrash(id) { - try { - const updatedNote = await storage.updateNote(id, { isDeleted: true, deletedAt: getCurrentDateTime() }) - if (updatedNote) { - const index = this.notes.findIndex(note => note.id === id) - if (index !== -1) { - this.notes[index] = updatedNote - } - } - return updatedNote - } catch (error) { - console.error('Error moving note to trash:', error) - throw error - } - }, - - /** - * 永久删除便签 - * 从便签列表中彻底移除便签 - * @param {string} id - 便签ID - * @returns {Promise} 删除成功返回true,失败返回false - */ - async permanentlyDeleteNote(id) { - try { - const result = await storage.deleteNote(id) - if (result) { - this.notes = this.notes.filter(note => note.id !== id) - } - return result - } catch (error) { - console.error('Error permanently deleting note:', error) - throw error - } - }, - - /** - * 文件夹操作函数 - */ - - /** - * 添加新文件夹 - * @param {Object} folder - 文件夹对象 - * @returns {Promise} 新创建的文件夹对象 - */ - async addFolder(folder) { - try { - const newFolder = await storage.addFolder(folder) - this.folders.push(newFolder) - return newFolder - } catch (error) { - console.error('Error adding folder:', error) - throw error - } - }, - - /** - * 设置操作函数 - */ - - /** - * 更新设置 - * @param {Object} newSettings - 新的设置对象 - * @returns {Promise} - */ - async updateSettings(newSettings) { - try { - const updatedSettings = { ...this.settings, ...newSettings } - this.settings = updatedSettings - await storage.saveSettings(updatedSettings) - } catch (error) { - console.error('Error updating settings:', error) - throw error - } - }, - - /** - * 切换云同步设置 - * 开启或关闭云同步功能 - * @returns {Promise} - */ - async toggleCloudSync() { - await this.updateSettings({ cloudSync: !this.settings.cloudSync }) - }, - - /** - * 切换深色模式设置 - * 开启或关闭深色模式 - * @returns {Promise} - */ - async toggleDarkMode() { - await this.updateSettings({ darkMode: !this.settings.darkMode }) - }, - }, -}) +import { defineStore } from 'pinia' +import * as storage from '../utils/indexedDBStorage' +import { getCurrentDateTime, getPastDate } from '../utils/dateUtils' + +/** + * 应用状态管理Store + * 使用Pinia进行状态管理,包含便签、文件夹和设置数据 + */ +export const useAppStore = defineStore('app', { + /** + * 状态定义 + * 包含应用的核心数据:便签列表、文件夹列表和设置 + */ + state: () => ({ + notes: [], // 便签列表 + folders: [], // 文件夹列表 + settings: { cloudSync: false, darkMode: false }, // 应用设置 + }), + + /** + * 计算属性 + * 基于状态派生的计算值 + */ + getters: { + /** + * 计算加星便签数量 + * @param {Object} state - 当前状态对象 + * @returns {number} 加星便签的数量 + */ + starredNotesCount: state => { + return state.notes.filter(note => note.isStarred).length + }, + + /** + * 计算所有便签数量 + * @param {Object} state - 当前状态对象 + * @returns {number} 所有便签的数量 + */ + allNotesCount: state => { + return state.notes.length + }, + }, + + /** + * 状态变更操作 + * 包含所有修改状态的方法 + */ + actions: { + /** + * 初始化数据 + * 从Storage加载便签、文件夹和设置数据 + * 如果没有数据则加载预设的mock数据 + * @returns {Promise} + */ + async loadData() { + try { + // 从Storage加载数据 + const loadedNotes = await storage.getNotes() + const loadedFolders = await storage.getFolders() + const loadedSettings = await storage.getSettings() + + // 如果没有数据,则加载mock数据 + if (loadedNotes.length === 0 && loadedFolders.length === 0) { + this.loadMockData() + } else { + // 否则使用加载的数据 + this.notes = loadedNotes + this.folders = loadedFolders + this.settings = loadedSettings + } + } catch (error) { + console.error('Error loading data:', error) + } + }, + + /** + * 加载预设的mock数据 + * 用于开发和演示目的,提供示例便签、文件夹和设置 + * @returns {Promise} + */ + async loadMockData() { + // Mock notes - 使用固定的日期值以避免每次运行时变化 + const fixedCurrentDate = '2025-10-12T10:00:00.000Z'; + + // 预设的便签示例数据 - 仅保留一条关于应用功能介绍和示例的便签 + const mockNotes = [ + { + id: '1', + title: '欢迎使用锤子便签', + content: '

这是一个功能强大的便签应用,您可以在这里记录您的想法、待办事项等。


功能介绍

1. 创建和编辑便签
2. 为便签加星和置顶
3. 将便签分类到文件夹
4. 搜索便签内容
5. 回收站功能


编辑器功能演示


标题格式

点击标题按钮可创建居中的标题


待办事项

这是一个待办事项
这是一个已完成的待办事项

列表格式

  • 无序列表项1
  • 无序列表项2

文本格式

加粗文本


引用格式

这是一段引用文本
可以用来引用他人的话语

图片

点击图片按钮可以插入图片,长按图片可以拖拽排序

', + createdAt: fixedCurrentDate, + updatedAt: fixedCurrentDate, + folderId: null, + isStarred: true, // 加星便签 + isTop: true, // 置顶便签 + hasImage: true, // 包含图片 + isDeleted: false, // 未删除 + deletedAt: null, + } + ] + + // Mock folders - 使用固定的日期值 + // 预设的文件夹示例数据 + const mockFolders = [ + { + id: 'folder1', + name: '工作', + createdAt: '2025-10-12T10:00:00.000Z', + }, + { + id: 'folder2', + name: '个人', + createdAt: '2025-10-12T10:00:00.000Z', + }, + { + id: 'folder3', + name: '学习', + createdAt: '2025-10-12T10:00:00.000Z', + }, + ] + + // Mock settings + // 预设的设置示例数据 + const mockSettings = { + cloudSync: false, // 云同步关闭 + darkMode: false, // 深色模式关闭 + } + + // 更新store状态 + this.notes = mockNotes + this.folders = mockFolders + this.settings = mockSettings + + // 保存到Storage + await storage.saveNotes(mockNotes) + await storage.saveFolders(mockFolders) + await storage.saveSettings(mockSettings) + }, + + /** + * 保存便签数据到Storage + * @returns {Promise} + */ + async saveNotes() { + try { + await storage.saveNotes(this.notes) + } catch (error) { + console.error('Error saving notes:', error) + } + }, + + /** + * 保存文件夹数据到Storage + * @returns {Promise} + */ + async saveFolders() { + try { + await storage.saveFolders(this.folders) + } catch (error) { + console.error('Error saving folders:', error) + } + }, + + /** + * 保存设置数据到Storage + * @returns {Promise} + */ + async saveSettings() { + try { + await storage.saveSettings(this.settings) + } catch (error) { + console.error('Error saving settings:', error) + } + }, + + /** + * 便签操作函数 + */ + + /** + * 添加新便签 + * @param {Object} note - 便签对象 + * @returns {Promise} 新创建的便签对象 + */ + async addNote(note) { + try { + const newNote = await storage.addNote(note) + this.notes.push(newNote) + + return newNote + } catch (error) { + console.error('Error adding note:', error) + throw error + } + }, + + /** + * 更新便签 + * @param {string} id - 便签ID + * @param {Object} updates - 要更新的属性对象 + * @returns {Promise} 更新后的便签对象 + */ + async updateNote(id, updates) { + try { + const updatedNote = await storage.updateNote(id, updates) + if (updatedNote) { + const index = this.notes.findIndex(note => note.id === id) + if (index !== -1) { + this.notes[index] = updatedNote + } + } + return updatedNote + } catch (error) { + console.error('Error updating note:', error) + throw error + } + }, + + /** + * 删除便签 + * @param {string} id - 要删除的便签ID + * @returns {Promise} 删除成功返回true,失败返回false + */ + async deleteNote(id) { + try { + const result = await storage.deleteNote(id) + if (result) { + this.notes = this.notes.filter(note => note.id !== id) + } + return result + } catch (error) { + console.error('Error deleting note:', error) + throw error + } + }, + + /** + * 将便签移至回收站 + * 将便签标记为已删除状态,并记录删除时间 + * @param {string} id - 便签ID + * @returns {Promise} 更新后的便签对象 + */ + async moveToTrash(id) { + try { + const updatedNote = await storage.updateNote(id, { isDeleted: true, deletedAt: getCurrentDateTime() }) + if (updatedNote) { + const index = this.notes.findIndex(note => note.id === id) + if (index !== -1) { + this.notes[index] = updatedNote + } + } + return updatedNote + } catch (error) { + console.error('Error moving note to trash:', error) + throw error + } + }, + + /** + * 永久删除便签 + * 从便签列表中彻底移除便签 + * @param {string} id - 便签ID + * @returns {Promise} 删除成功返回true,失败返回false + */ + async permanentlyDeleteNote(id) { + try { + const result = await storage.deleteNote(id) + if (result) { + this.notes = this.notes.filter(note => note.id !== id) + } + return result + } catch (error) { + console.error('Error permanently deleting note:', error) + throw error + } + }, + + /** + * 文件夹操作函数 + */ + + /** + * 添加新文件夹 + * @param {Object} folder - 文件夹对象 + * @returns {Promise} 新创建的文件夹对象 + */ + async addFolder(folder) { + try { + const newFolder = await storage.addFolder(folder) + this.folders.push(newFolder) + return newFolder + } catch (error) { + console.error('Error adding folder:', error) + throw error + } + }, + + /** + * 更新文件夹 + * @param {string} id - 文件夹ID + * @param {Object} updates - 要更新的属性对象 + * @returns {Promise} 更新后的文件夹对象 + */ + async updateFolder(id, updates) { + try { + const updatedFolder = await storage.updateFolder(id, updates) + if (updatedFolder) { + const index = this.folders.findIndex(folder => folder.id === id) + if (index !== -1) { + this.folders[index] = updatedFolder + } + } + return updatedFolder + } catch (error) { + console.error('Error updating folder:', error) + throw error + } + }, + + /** + * 删除文件夹 + * @param {string} id - 要删除的文件夹ID + * @returns {Promise} 删除成功返回true,失败返回false + */ + async deleteFolder(id) { + try { + const result = await storage.deleteFolder(id) + if (result) { + // 将文件夹中的便签移回"全部便签" + const notesInFolder = this.notes.filter(note => note.folderId === id) + for (const note of notesInFolder) { + await this.updateNote(note.id, { folderId: null }) + } + + // 从文件夹列表中移除文件夹 + this.folders = this.folders.filter(folder => folder.id !== id) + } + return result + } catch (error) { + console.error('Error deleting folder:', error) + throw error + } + }, + + /** + * 设置操作函数 + */ + + /** + * 更新设置 + * @param {Object} newSettings - 新的设置对象 + * @returns {Promise} + */ + async updateSettings(newSettings) { + try { + const updatedSettings = { ...this.settings, ...newSettings } + this.settings = updatedSettings + await storage.saveSettings(updatedSettings) + } catch (error) { + console.error('Error updating settings:', error) + throw error + } + }, + + /** + * 切换云同步设置 + * 开启或关闭云同步功能 + * @returns {Promise} + */ + async toggleCloudSync() { + await this.updateSettings({ cloudSync: !this.settings.cloudSync }) + }, + + /** + * 切换深色模式设置 + * 开启或关闭深色模式 + * @returns {Promise} + */ + async toggleDarkMode() { + await this.updateSettings({ darkMode: !this.settings.darkMode }) + }, + }, +}) diff --git a/src/utils/indexedDBStorage.js b/src/utils/indexedDBStorage.js index 2c6179c..954c82d 100644 --- a/src/utils/indexedDBStorage.js +++ b/src/utils/indexedDBStorage.js @@ -1,475 +1,510 @@ -import { getCurrentDateTime, getTimestamp } from './dateUtils' - -// 数据库配置 -const DB_NAME = 'SmartisanNoteDB' -const DB_VERSION = 2 // 更新版本号以确保数据库重新创建 -const NOTES_STORE = 'notes' -const FOLDERS_STORE = 'folders' -const SETTINGS_STORE = 'settings' - -let db = null - -/** - * 打开数据库连接 - * @returns {Promise} 数据库实例 - */ -const openDB = () => { - return new Promise((resolve, reject) => { - if (db) { - return resolve(db) - } - - const request = indexedDB.open(DB_NAME, DB_VERSION) - - request.onerror = () => { - reject(new Error('无法打开数据库')) - } - - request.onsuccess = () => { - db = request.result - resolve(db) - } - - request.onupgradeneeded = (event) => { - const database = event.target.result - - // 删除现有的对象存储(如果版本已更改) - if (event.oldVersion > 0) { - if (database.objectStoreNames.contains(NOTES_STORE)) { - database.deleteObjectStore(NOTES_STORE) - } - if (database.objectStoreNames.contains(FOLDERS_STORE)) { - database.deleteObjectStore(FOLDERS_STORE) - } - if (database.objectStoreNames.contains(SETTINGS_STORE)) { - database.deleteObjectStore(SETTINGS_STORE) - } - } - - // 创建便签存储对象 - const notesStore = database.createObjectStore(NOTES_STORE, { keyPath: 'id' }) - notesStore.createIndex('folderId', 'folderId', { unique: false }) - notesStore.createIndex('isStarred', 'isStarred', { unique: false }) - notesStore.createIndex('isDeleted', 'isDeleted', { unique: false }) - notesStore.createIndex('createdAt', 'createdAt', { unique: false }) - notesStore.createIndex('updatedAt', 'updatedAt', { unique: false }) - - // 创建文件夹存储对象 - database.createObjectStore(FOLDERS_STORE, { keyPath: 'id' }) - - // 创建设置存储对象 - database.createObjectStore(SETTINGS_STORE) - } - }) -} - -/** - * 从存储中获取数据 - * @param {string} storeName - 存储名称 - * @returns {Promise} 数据数组 - */ -const getAllFromStore = async (storeName) => { - const database = await openDB() - const transaction = database.transaction([storeName], 'readonly') - const store = transaction.objectStore(storeName) - const request = store.getAll() - - return new Promise((resolve, reject) => { - request.onsuccess = () => { - resolve(request.result || []) - } - - request.onerror = () => { - reject(new Error(`获取 ${storeName} 数据失败`)) - } - }) -} - -/** - * 保存数据到存储 - * @param {string} storeName - 存储名称 - * @param {Array} data - 要保存的数据数组 - * @returns {Promise} - */ -const saveToStore = async (storeName, data) => { - const database = await openDB() - const transaction = database.transaction([storeName], 'readwrite') - const store = transaction.objectStore(storeName) - - // 清除现有数据 - await new Promise((resolve, reject) => { - const clearRequest = store.clear() - clearRequest.onsuccess = () => resolve() - clearRequest.onerror = () => reject(new Error(`清除 ${storeName} 数据失败`)) - }) - - // 添加新数据 - for (const item of data) { - await new Promise((resolve, reject) => { - const addRequest = store.add(item) - addRequest.onsuccess = () => resolve() - addRequest.onerror = () => reject(new Error(`保存 ${storeName} 数据失败`)) - }) - } -} - -/** - * 从存储中获取单个项 - * @param {string} storeName - 存储名称 - * @param {string} id - 项的ID - * @returns {Promise} 项对象或null - */ -const getFromStore = async (storeName, id) => { - const database = await openDB() - const transaction = database.transaction([storeName], 'readonly') - const store = transaction.objectStore(storeName) - const request = store.get(id) - - return new Promise((resolve, reject) => { - request.onsuccess = () => { - resolve(request.result || null) - } - - request.onerror = () => { - reject(new Error(`获取 ${storeName} 项失败`)) - } - }) -} - -/** - * 向存储中添加项 - * @param {string} storeName - 存储名称 - * @param {Object} item - 要添加的项 - * @returns {Promise} 添加的项 - */ -const addToStore = async (storeName, item) => { - const database = await openDB() - const transaction = database.transaction([storeName], 'readwrite') - const store = transaction.objectStore(storeName) - const request = store.add(item) - - return new Promise((resolve, reject) => { - request.onsuccess = () => { - resolve(item) - } - - request.onerror = () => { - reject(new Error(`添加 ${storeName} 项失败`)) - } - }) -} - -/** - * 更新存储中的项 - * @param {string} storeName - 存储名称 - * @param {string} id - 项的ID - * @param {Object} updates - 要更新的属性对象 - * @returns {Promise} 更新后的项或null - */ -const updateInStore = async (storeName, id, updates) => { - const item = await getFromStore(storeName, id) - if (!item) return null - - const updatedItem = { ...item, ...updates } - const database = await openDB() - const transaction = database.transaction([storeName], 'readwrite') - const store = transaction.objectStore(storeName) - const request = store.put(updatedItem) - - return new Promise((resolve, reject) => { - request.onsuccess = () => { - resolve(updatedItem) - } - - request.onerror = () => { - reject(new Error(`更新 ${storeName} 项失败`)) - } - }) -} - -/** - * 从存储中删除项 - * @param {string} storeName - 存储名称 - * @param {string} id - 要删除的项的ID - * @returns {Promise} 删除成功返回true,失败返回false - */ -const deleteFromStore = async (storeName, id) => { - const database = await openDB() - const transaction = database.transaction([storeName], 'readwrite') - const store = transaction.objectStore(storeName) - const request = store.delete(id) - - return new Promise((resolve, reject) => { - request.onsuccess = () => { - resolve(true) - } - - request.onerror = () => { - reject(new Error(`删除 ${storeName} 项失败`)) - } - }) -} - -// 便签操作函数 -// 提供便签的增删改查功能 - -/** - * 获取所有便签数据 - * 从IndexedDB中读取便签数据 - * @returns {Promise} 便签数组 - */ -export const getNotes = async () => { - try { - const notes = await getAllFromStore(NOTES_STORE) - return ensureNotesDefaults(notes) - } catch (error) { - console.error('Error getting notes:', error) - return [] - } -} - -/** - * 保存便签数据 - * 将便签数组保存到IndexedDB - * @param {Array} notes - 便签数组 - * @returns {Promise} - */ -export const saveNotes = async (notes) => { - try { - await saveToStore(NOTES_STORE, notes) - } catch (error) { - console.error('Error saving notes:', error) - } -} - -/** - * 添加新便签 - * 创建一个新的便签对象并添加到便签列表中 - * @param {Object} note - 便签对象,包含便签内容和其他属性 - * @returns {Promise} 新创建的便签对象 - */ -export const addNote = async (note) => { - try { - // 创建新的便签对象,添加必要的属性 - const newNote = { - title: note.title || '', - content: note.content || '', - id: note.id || getTimestamp().toString(), // 使用时间戳生成唯一ID - createdAt: note.createdAt || getCurrentDateTime(), // 创建时间 - updatedAt: note.updatedAt || getCurrentDateTime(), // 更新时间 - isStarred: note.isStarred || false, // 是否加星 - isTop: note.isTop || false, // 是否置顶 - hasImage: note.hasImage || false, // 是否包含图片 - isDeleted: note.isDeleted || false, // 是否已删除 - deletedAt: note.deletedAt || null, // 删除时间 - folderId: note.folderId || null, // 文件夹ID - ...note - } - - // 添加到存储 - await addToStore(NOTES_STORE, newNote) - return newNote - } catch (error) { - console.error('Error adding note:', error) - throw error - } -} - -/** - * 更新便签 - * 根据ID查找并更新便签信息 - * @param {string} id - 便签ID - * @param {Object} updates - 要更新的属性对象 - * @returns {Promise} 更新后的便签对象,如果未找到则返回null - */ -export const updateNote = async (id, updates) => { - try { - // 更新便签并保存 - const updatedNote = await updateInStore(NOTES_STORE, id, { - ...updates, - updatedAt: getCurrentDateTime() // 更新最后修改时间 - }) - - return updatedNote - } catch (error) { - console.error('Error updating note:', error) - throw error - } -} - -/** - * 删除便签 - * 根据ID从便签列表中移除便签 - * @param {string} id - 要删除的便签ID - * @returns {Promise} 删除成功返回true,未找到便签返回false - */ -export const deleteNote = async (id) => { - try { - // 从存储中删除 - const result = await deleteFromStore(NOTES_STORE, id) - return result - } catch (error) { - console.error('Error deleting note:', error) - return false - } -} - -// 文件夹操作函数 -// 提供文件夹的增删改查功能 - -/** - * 获取所有文件夹数据 - * 从IndexedDB中读取文件夹数据 - * @returns {Promise} 文件夹数组 - */ -export const getFolders = async () => { - try { - const folders = await getAllFromStore(FOLDERS_STORE) - return ensureFoldersDefaults(folders) - } catch (error) { - console.error('Error getting folders:', error) - return [] - } -} - -/** - * 保存文件夹数据 - * 将文件夹数组保存到IndexedDB - * @param {Array} folders - 文件夹数组 - * @returns {Promise} - */ -export const saveFolders = async (folders) => { - try { - await saveToStore(FOLDERS_STORE, folders) - } catch (error) { - console.error('Error saving folders:', error) - } -} - -/** - * 添加新文件夹 - * 创建一个新的文件夹对象并添加到文件夹列表中 - * @param {Object} folder - 文件夹对象,包含文件夹名称等属性 - * @returns {Promise} 新创建的文件夹对象 - */ -export const addFolder = async (folder) => { - try { - // 创建新的文件夹对象,添加必要的属性 - const newFolder = { - name: folder.name || '', - id: folder.id || getTimestamp().toString(), // 使用时间戳生成唯一ID - createdAt: folder.createdAt || getCurrentDateTime(), // 创建时间 - ...folder - } - - // 添加到存储 - await addToStore(FOLDERS_STORE, newFolder) - return newFolder - } catch (error) { - console.error('Error adding folder:', error) - throw error - } -} - -// 设置操作函数 -// 提供应用设置的读取和保存功能 - -/** - * 获取应用设置 - * 从IndexedDB中读取设置数据 - * @returns {Promise} 设置对象,如果读取失败则返回默认设置 - */ -export const getSettings = async () => { - try { - const database = await openDB() - const transaction = database.transaction([SETTINGS_STORE], 'readonly') - const store = transaction.objectStore(SETTINGS_STORE) - const request = store.get('settings') - - const settings = await new Promise((resolve, reject) => { - request.onsuccess = () => { - resolve(request.result || { cloudSync: false, darkMode: false }) - } - - request.onerror = () => { - reject(new Error('获取设置失败')) - } - }) - - return settings - } catch (error) { - console.error('Error getting settings:', error) - // 出错时返回默认设置 - return { cloudSync: false, darkMode: false } - } -} - -/** - * 保存应用设置 - * 将设置对象保存到IndexedDB - * @param {Object} settings - 设置对象 - * @returns {Promise} - */ -export const saveSettings = async (settings) => { - try { - const database = await openDB() - const transaction = database.transaction([SETTINGS_STORE], 'readwrite') - const store = transaction.objectStore(SETTINGS_STORE) - const request = store.put(settings, 'settings') - - await new Promise((resolve, reject) => { - request.onsuccess = () => resolve() - request.onerror = () => reject(new Error('保存设置失败')) - }) - } catch (error) { - console.error('Error saving settings:', error) - } -} - -/** - * 确保数据有默认值 - * @param {Array} notes - 便签数组 - * @returns {Array} 处理后的便签数组 - */ -const ensureNotesDefaults = (notes) => { - return notes.map(note => ({ - title: note.title || '', - content: note.content || '', - id: note.id, - createdAt: note.createdAt, - updatedAt: note.updatedAt, - isStarred: note.isStarred || false, - isTop: note.isTop || false, - hasImage: note.hasImage || false, - isDeleted: note.isDeleted || false, - deletedAt: note.deletedAt || null, - folderId: note.folderId || null, - ...note - })) -} - -/** - * 确保文件夹数据有默认值 - * @param {Array} folders - 文件夹数组 - * @returns {Array} 处理后的文件夹数组 - */ -const ensureFoldersDefaults = (folders) => { - return folders.map(folder => ({ - name: folder.name || '', - id: folder.id, - createdAt: folder.createdAt, - ...folder - })) -} - -/** - * 初始化数据库 - * @returns {Promise} - */ -export const initDB = async () => { - try { - await openDB() - } catch (error) { - console.error('Error initializing database:', error) - } +import { getCurrentDateTime, getTimestamp } from './dateUtils' + +// 数据库配置 +const DB_NAME = 'SmartisanNoteDB' +const DB_VERSION = 2 // 更新版本号以确保数据库重新创建 +const NOTES_STORE = 'notes' +const FOLDERS_STORE = 'folders' +const SETTINGS_STORE = 'settings' + +let db = null + +/** + * 打开数据库连接 + * @returns {Promise} 数据库实例 + */ +const openDB = () => { + return new Promise((resolve, reject) => { + if (db) { + return resolve(db) + } + + const request = indexedDB.open(DB_NAME, DB_VERSION) + + request.onerror = () => { + reject(new Error('无法打开数据库')) + } + + request.onsuccess = () => { + db = request.result + resolve(db) + } + + request.onupgradeneeded = (event) => { + const database = event.target.result + + // 删除现有的对象存储(如果版本已更改) + if (event.oldVersion > 0) { + if (database.objectStoreNames.contains(NOTES_STORE)) { + database.deleteObjectStore(NOTES_STORE) + } + if (database.objectStoreNames.contains(FOLDERS_STORE)) { + database.deleteObjectStore(FOLDERS_STORE) + } + if (database.objectStoreNames.contains(SETTINGS_STORE)) { + database.deleteObjectStore(SETTINGS_STORE) + } + } + + // 创建便签存储对象 + const notesStore = database.createObjectStore(NOTES_STORE, { keyPath: 'id' }) + notesStore.createIndex('folderId', 'folderId', { unique: false }) + notesStore.createIndex('isStarred', 'isStarred', { unique: false }) + notesStore.createIndex('isDeleted', 'isDeleted', { unique: false }) + notesStore.createIndex('createdAt', 'createdAt', { unique: false }) + notesStore.createIndex('updatedAt', 'updatedAt', { unique: false }) + + // 创建文件夹存储对象 + database.createObjectStore(FOLDERS_STORE, { keyPath: 'id' }) + + // 创建设置存储对象 + database.createObjectStore(SETTINGS_STORE) + } + }) +} + +/** + * 从存储中获取数据 + * @param {string} storeName - 存储名称 + * @returns {Promise} 数据数组 + */ +const getAllFromStore = async (storeName) => { + const database = await openDB() + const transaction = database.transaction([storeName], 'readonly') + const store = transaction.objectStore(storeName) + const request = store.getAll() + + return new Promise((resolve, reject) => { + request.onsuccess = () => { + resolve(request.result || []) + } + + request.onerror = () => { + reject(new Error(`获取 ${storeName} 数据失败`)) + } + }) +} + +/** + * 保存数据到存储 + * @param {string} storeName - 存储名称 + * @param {Array} data - 要保存的数据数组 + * @returns {Promise} + */ +const saveToStore = async (storeName, data) => { + const database = await openDB() + const transaction = database.transaction([storeName], 'readwrite') + const store = transaction.objectStore(storeName) + + // 清除现有数据 + await new Promise((resolve, reject) => { + const clearRequest = store.clear() + clearRequest.onsuccess = () => resolve() + clearRequest.onerror = () => reject(new Error(`清除 ${storeName} 数据失败`)) + }) + + // 添加新数据 + for (const item of data) { + await new Promise((resolve, reject) => { + const addRequest = store.add(item) + addRequest.onsuccess = () => resolve() + addRequest.onerror = () => reject(new Error(`保存 ${storeName} 数据失败`)) + }) + } +} + +/** + * 从存储中获取单个项 + * @param {string} storeName - 存储名称 + * @param {string} id - 项的ID + * @returns {Promise} 项对象或null + */ +const getFromStore = async (storeName, id) => { + const database = await openDB() + const transaction = database.transaction([storeName], 'readonly') + const store = transaction.objectStore(storeName) + const request = store.get(id) + + return new Promise((resolve, reject) => { + request.onsuccess = () => { + resolve(request.result || null) + } + + request.onerror = () => { + reject(new Error(`获取 ${storeName} 项失败`)) + } + }) +} + +/** + * 向存储中添加项 + * @param {string} storeName - 存储名称 + * @param {Object} item - 要添加的项 + * @returns {Promise} 添加的项 + */ +const addToStore = async (storeName, item) => { + const database = await openDB() + const transaction = database.transaction([storeName], 'readwrite') + const store = transaction.objectStore(storeName) + const request = store.add(item) + + return new Promise((resolve, reject) => { + request.onsuccess = () => { + resolve(item) + } + + request.onerror = () => { + reject(new Error(`添加 ${storeName} 项失败`)) + } + }) +} + +/** + * 更新存储中的项 + * @param {string} storeName - 存储名称 + * @param {string} id - 项的ID + * @param {Object} updates - 要更新的属性对象 + * @returns {Promise} 更新后的项或null + */ +const updateInStore = async (storeName, id, updates) => { + const item = await getFromStore(storeName, id) + if (!item) return null + + const updatedItem = { ...item, ...updates } + const database = await openDB() + const transaction = database.transaction([storeName], 'readwrite') + const store = transaction.objectStore(storeName) + const request = store.put(updatedItem) + + return new Promise((resolve, reject) => { + request.onsuccess = () => { + resolve(updatedItem) + } + + request.onerror = () => { + reject(new Error(`更新 ${storeName} 项失败`)) + } + }) +} + +/** + * 从存储中删除项 + * @param {string} storeName - 存储名称 + * @param {string} id - 要删除的项的ID + * @returns {Promise} 删除成功返回true,失败返回false + */ +const deleteFromStore = async (storeName, id) => { + const database = await openDB() + const transaction = database.transaction([storeName], 'readwrite') + const store = transaction.objectStore(storeName) + const request = store.delete(id) + + return new Promise((resolve, reject) => { + request.onsuccess = () => { + resolve(true) + } + + request.onerror = () => { + reject(new Error(`删除 ${storeName} 项失败`)) + } + }) +} + +// 便签操作函数 +// 提供便签的增删改查功能 + +/** + * 获取所有便签数据 + * 从IndexedDB中读取便签数据 + * @returns {Promise} 便签数组 + */ +export const getNotes = async () => { + try { + const notes = await getAllFromStore(NOTES_STORE) + return ensureNotesDefaults(notes) + } catch (error) { + console.error('Error getting notes:', error) + return [] + } +} + +/** + * 保存便签数据 + * 将便签数组保存到IndexedDB + * @param {Array} notes - 便签数组 + * @returns {Promise} + */ +export const saveNotes = async (notes) => { + try { + await saveToStore(NOTES_STORE, notes) + } catch (error) { + console.error('Error saving notes:', error) + } +} + +/** + * 添加新便签 + * 创建一个新的便签对象并添加到便签列表中 + * @param {Object} note - 便签对象,包含便签内容和其他属性 + * @returns {Promise} 新创建的便签对象 + */ +export const addNote = async (note) => { + try { + // 创建新的便签对象,添加必要的属性 + const newNote = { + title: note.title || '', + content: note.content || '', + id: note.id || getTimestamp().toString(), // 使用时间戳生成唯一ID + createdAt: note.createdAt || getCurrentDateTime(), // 创建时间 + updatedAt: note.updatedAt || getCurrentDateTime(), // 更新时间 + isStarred: note.isStarred || false, // 是否加星 + isTop: note.isTop || false, // 是否置顶 + hasImage: note.hasImage || false, // 是否包含图片 + isDeleted: note.isDeleted || false, // 是否已删除 + deletedAt: note.deletedAt || null, // 删除时间 + folderId: note.folderId || null, // 文件夹ID + ...note + } + + // 添加到存储 + await addToStore(NOTES_STORE, newNote) + return newNote + } catch (error) { + console.error('Error adding note:', error) + throw error + } +} + +/** + * 更新便签 + * 根据ID查找并更新便签信息 + * @param {string} id - 便签ID + * @param {Object} updates - 要更新的属性对象 + * @returns {Promise} 更新后的便签对象,如果未找到则返回null + */ +export const updateNote = async (id, updates) => { + try { + // 更新便签并保存 + const updatedNote = await updateInStore(NOTES_STORE, id, { + ...updates, + updatedAt: getCurrentDateTime() // 更新最后修改时间 + }) + + return updatedNote + } catch (error) { + console.error('Error updating note:', error) + throw error + } +} + +/** + * 删除便签 + * 根据ID从便签列表中移除便签 + * @param {string} id - 要删除的便签ID + * @returns {Promise} 删除成功返回true,未找到便签返回false + */ +export const deleteNote = async (id) => { + try { + // 从存储中删除 + const result = await deleteFromStore(NOTES_STORE, id) + return result + } catch (error) { + console.error('Error deleting note:', error) + return false + } +} + +// 文件夹操作函数 +// 提供文件夹的增删改查功能 + +/** + * 获取所有文件夹数据 + * 从IndexedDB中读取文件夹数据 + * @returns {Promise} 文件夹数组 + */ +export const getFolders = async () => { + try { + const folders = await getAllFromStore(FOLDERS_STORE) + return ensureFoldersDefaults(folders) + } catch (error) { + console.error('Error getting folders:', error) + return [] + } +} + +/** + * 保存文件夹数据 + * 将文件夹数组保存到IndexedDB + * @param {Array} folders - 文件夹数组 + * @returns {Promise} + */ +export const saveFolders = async (folders) => { + try { + await saveToStore(FOLDERS_STORE, folders) + } catch (error) { + console.error('Error saving folders:', error) + } +} + +/** + * 添加新文件夹 + * 创建一个新的文件夹对象并添加到文件夹列表中 + * @param {Object} folder - 文件夹对象,包含文件夹名称等属性 + * @returns {Promise} 新创建的文件夹对象 + */ +export const addFolder = async (folder) => { + try { + // 创建新的文件夹对象,添加必要的属性 + const newFolder = { + name: folder.name || '', + id: folder.id || getTimestamp().toString(), // 使用时间戳生成唯一ID + createdAt: folder.createdAt || getCurrentDateTime(), // 创建时间 + ...folder + } + + // 添加到存储 + await addToStore(FOLDERS_STORE, newFolder) + return newFolder + } catch (error) { + console.error('Error adding folder:', error) + throw error + } +} + +/** + * 更新文件夹 + * 根据ID查找并更新文件夹信息 + * @param {string} id - 文件夹ID + * @param {Object} updates - 要更新的属性对象 + * @returns {Promise} 更新后的文件夹对象,如果未找到则返回null + */ +export const updateFolder = async (id, updates) => { + try { + // 更新文件夹并保存 + const updatedFolder = await updateInStore(FOLDERS_STORE, id, updates) + return updatedFolder + } catch (error) { + console.error('Error updating folder:', error) + throw error + } +} + +/** + * 删除文件夹 + * 根据ID从文件夹列表中移除文件夹 + * @param {string} id - 要删除的文件夹ID + * @returns {Promise} 删除成功返回true,未找到文件夹返回false + */ +export const deleteFolder = async (id) => { + try { + // 从存储中删除 + const result = await deleteFromStore(FOLDERS_STORE, id) + return result + } catch (error) { + console.error('Error deleting folder:', error) + return false + } +} + +// 设置操作函数 +// 提供应用设置的读取和保存功能 + +/** + * 获取应用设置 + * 从IndexedDB中读取设置数据 + * @returns {Promise} 设置对象,如果读取失败则返回默认设置 + */ +export const getSettings = async () => { + try { + const database = await openDB() + const transaction = database.transaction([SETTINGS_STORE], 'readonly') + const store = transaction.objectStore(SETTINGS_STORE) + const request = store.get('settings') + + const settings = await new Promise((resolve, reject) => { + request.onsuccess = () => { + resolve(request.result || { cloudSync: false, darkMode: false }) + } + + request.onerror = () => { + reject(new Error('获取设置失败')) + } + }) + + return settings + } catch (error) { + console.error('Error getting settings:', error) + // 出错时返回默认设置 + return { cloudSync: false, darkMode: false } + } +} + +/** + * 保存应用设置 + * 将设置对象保存到IndexedDB + * @param {Object} settings - 设置对象 + * @returns {Promise} + */ +export const saveSettings = async (settings) => { + try { + const database = await openDB() + const transaction = database.transaction([SETTINGS_STORE], 'readwrite') + const store = transaction.objectStore(SETTINGS_STORE) + const request = store.put(settings, 'settings') + + await new Promise((resolve, reject) => { + request.onsuccess = () => resolve() + request.onerror = () => reject(new Error('保存设置失败')) + }) + } catch (error) { + console.error('Error saving settings:', error) + } +} + +/** + * 确保数据有默认值 + * @param {Array} notes - 便签数组 + * @returns {Array} 处理后的便签数组 + */ +const ensureNotesDefaults = (notes) => { + return notes.map(note => ({ + title: note.title || '', + content: note.content || '', + id: note.id, + createdAt: note.createdAt, + updatedAt: note.updatedAt, + isStarred: note.isStarred || false, + isTop: note.isTop || false, + hasImage: note.hasImage || false, + isDeleted: note.isDeleted || false, + deletedAt: note.deletedAt || null, + folderId: note.folderId || null, + ...note + })) +} + +/** + * 确保文件夹数据有默认值 + * @param {Array} folders - 文件夹数组 + * @returns {Array} 处理后的文件夹数组 + */ +const ensureFoldersDefaults = (folders) => { + return folders.map(folder => ({ + name: folder.name || '', + id: folder.id, + createdAt: folder.createdAt, + ...folder + })) +} + +/** + * 初始化数据库 + * @returns {Promise} + */ +export const initDB = async () => { + try { + await openDB() + } catch (error) { + console.error('Error initializing database:', error) + } } \ No newline at end of file diff --git a/src/utils/modalService.js b/src/utils/modalService.js new file mode 100644 index 0000000..19b2d53 --- /dev/null +++ b/src/utils/modalService.js @@ -0,0 +1,113 @@ +import { createApp } from 'vue' +import Modal from '@/components/Modal.vue' + +/** + * 全局弹框服务 + * 提供统一的弹框调用接口,使用项目中已有的 Modal 组件 + */ + +// 创建一个全局的 Modal 实例容器 +let modalInstance = null +let modalContainer = null + +/** + * 初始化 Modal 实例 + * 在应用启动时调用一次 + */ +export const initModalService = () => { + if (!modalContainer) { + // 创建一个隐藏的 div 作为 Modal 的挂载点 + modalContainer = document.createElement('div') + document.body.appendChild(modalContainer) + + // 创建 Modal 实例 + const app = createApp(Modal) + modalInstance = app.mount(modalContainer) + } + + return modalInstance +} + +/** + * 显示确认弹框 + * @param {string} message - 弹框消息 + * @param {string} title - 弹框标题 + * @param {Object} options - 弹框选项 + * @returns {Promise} 用户确认返回 true,取消返回 false + */ +export const showConfirm = (message, title = '确认', options = {}) => { + // 确保 Modal 实例已初始化 + if (!modalInstance) { + initModalService() + } + + // 显示弹框 + return modalInstance.show({ + title, + message, + showInput: false, + showConfirm: true, + showCancel: true, + ...options + }) +} + +/** + * 显示提示弹框 + * @param {string} message - 弹框消息 + * @param {string} title - 弹框标题 + * @param {Object} options - 弹框选项 + * @returns {Promise} + */ +export const showAlert = (message, title = '提示', options = {}) => { + // 确保 Modal 实例已初始化 + if (!modalInstance) { + initModalService() + } + + // 显示弹框 + return modalInstance.show({ + title, + message, + showInput: false, + showConfirm: true, + showCancel: false, + ...options + }) +} + +/** + * 显示输入弹框 + * @param {string} message - 弹框消息 + * @param {string} title - 弹框标题 + * @param {string} placeholder - 输入框占位符 + * @param {string} defaultValue - 输入框默认值 + * @param {Object} options - 弹框选项 + * @returns {Promise} 用户输入的值,取消返回 null + */ +export const showPrompt = (message, title = '输入', placeholder = '请输入文字', defaultValue = '', options = {}) => { + // 确保 Modal 实例已初始化 + if (!modalInstance) { + initModalService() + } + + // 显示弹框 + return modalInstance.show({ + title, + message, + showInput: true, + showConfirm: true, + showCancel: true, + inputPlaceholder: placeholder, + inputValue: defaultValue, + ...options + }) +} + +// 默认导出所有方法 +export default { + initModalService, + showConfirm, + showAlert, + showPrompt +} \ No newline at end of file