You've already forked SmartisanNote.Remake
优化: 将本地存储从localStorage迁移至IndexedDB以支持更大数据量存储
This commit is contained in:
8
IFLOW.md
8
IFLOW.md
@@ -12,7 +12,7 @@
|
||||
* **路由**: Vue Router
|
||||
* **UI 组件库**: Ionic Vue (部分使用)
|
||||
* **PWA 支持**: vite-plugin-pwa
|
||||
* **本地存储**: localStorage (通过 `src/utils/storage.js` 封装)
|
||||
* **本地存储**: IndexedDB (通过 `src/utils/indexedDBStorage.js` 封装)
|
||||
* **CSS 预处理器**: Less
|
||||
|
||||
## 项目结构
|
||||
@@ -45,7 +45,7 @@
|
||||
* **回收站**: 临时存储已删除的便签,支持彻底删除。
|
||||
* **多种排序方式**: 按更新时间、标题、星标状态排序。
|
||||
* **PWA 支持**: 可安装为独立应用,支持离线使用。
|
||||
* **本地存储**: 所有数据存储在浏览器的 `localStorage` 中。
|
||||
* **本地存储**: 所有数据存储在浏览器的 `IndexedDB` 中。
|
||||
* **深色模式**: (计划中) 支持切换深色/浅色主题。
|
||||
* **云同步**: (计划中) 支持多设备间数据同步。
|
||||
|
||||
@@ -118,7 +118,7 @@ npm run android
|
||||
* **路由**: Vue Router
|
||||
* **UI 组件库**: Ionic Vue (部分使用)
|
||||
* **PWA 支持**: vite-plugin-pwa
|
||||
* **本地存储**: localStorage (通过 `src/utils/storage.js` 封装)
|
||||
* **本地存储**: IndexedDB (通过 `src/utils/indexedDBStorage.js` 封装)
|
||||
* **CSS 预处理器**: Less
|
||||
* **CSS 命名**: 使用 BEM 命名规范,部分使用原子化 CSS 类名(以 `code-fun-` 开头)
|
||||
* **响应式设计**: 使用 viewport 单位 (vw/vh) 和 CSS 变量实现响应式布局
|
||||
@@ -149,7 +149,7 @@ npm run android
|
||||
* Store 文件中按照 state, getters, actions 的顺序组织代码
|
||||
* 异步操作使用 `async/await` 语法
|
||||
* **工具函数**:
|
||||
* 工具函数按功能模块划分文件,如 `storage.js`, `dateUtils.js`
|
||||
* 工具函数按功能模块划分文件,如 `indexedDBStorage.js`, `dateUtils.js`
|
||||
* 工具函数使用 `export const` 导出
|
||||
|
||||
### 注释规范
|
||||
|
||||
310
console.txt
310
console.txt
@@ -1,310 +0,0 @@
|
||||
prepare.js:1 🍍 "app" store installed 🆕
|
||||
NoteListPage.vue:291 搜索栏获得焦点
|
||||
NoteListPage.vue:296 搜索栏失去焦点
|
||||
NoteListPage.vue:291 搜索栏获得焦点
|
||||
NoteListPage.vue:104 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'toLowerCase')
|
||||
at NoteListPage.vue:104:57
|
||||
at wrappedFn (reactivity.esm-bundler.js:878:19)
|
||||
at Array.filter (<anonymous>)
|
||||
at apply (reactivity.esm-bundler.js:886:27)
|
||||
at Proxy.filter (reactivity.esm-bundler.js:778:12)
|
||||
at ComputedRefImpl.fn (NoteListPage.vue:102:22)
|
||||
at refreshComputed (reactivity.esm-bundler.js:391:28)
|
||||
at isDirty (reactivity.esm-bundler.js:362:68)
|
||||
at refreshComputed (reactivity.esm-bundler.js:380:90)
|
||||
at get value (reactivity.esm-bundler.js:1655:5)
|
||||
(匿名) @ NoteListPage.vue:104
|
||||
wrappedFn @ reactivity.esm-bundler.js:878
|
||||
apply @ reactivity.esm-bundler.js:886
|
||||
filter @ reactivity.esm-bundler.js:778
|
||||
(匿名) @ NoteListPage.vue:102
|
||||
refreshComputed @ reactivity.esm-bundler.js:391
|
||||
isDirty @ reactivity.esm-bundler.js:362
|
||||
refreshComputed @ reactivity.esm-bundler.js:380
|
||||
get value @ reactivity.esm-bundler.js:1655
|
||||
unref @ reactivity.esm-bundler.js:1500
|
||||
get @ reactivity.esm-bundler.js:1506
|
||||
(匿名) @ NoteListPage.vue:35
|
||||
renderFnWithContext @ runtime-core.esm-bundler.js:695
|
||||
(匿名) @ runtime.js:286
|
||||
renderComponentRoot @ runtime-core.esm-bundler.js:6590
|
||||
componentUpdateFn @ runtime-core.esm-bundler.js:5468
|
||||
run @ reactivity.esm-bundler.js:237
|
||||
runIfDirty @ reactivity.esm-bundler.js:275
|
||||
callWithErrorHandling @ runtime-core.esm-bundler.js:199
|
||||
flushJobs @ runtime-core.esm-bundler.js:408
|
||||
Promise.then
|
||||
queueFlush @ runtime-core.esm-bundler.js:322
|
||||
queueJob @ runtime-core.esm-bundler.js:317
|
||||
effect2.scheduler @ runtime-core.esm-bundler.js:5519
|
||||
trigger @ reactivity.esm-bundler.js:265
|
||||
endBatch @ reactivity.esm-bundler.js:323
|
||||
notify @ reactivity.esm-bundler.js:614
|
||||
trigger @ reactivity.esm-bundler.js:588
|
||||
set value @ reactivity.esm-bundler.js:1472
|
||||
set @ reactivity.esm-bundler.js:1510
|
||||
_createVNode.onUpdate:modelValue._cache.<computed>._cache.<computed> @ NoteListPage.vue:31
|
||||
callWithErrorHandling @ runtime-core.esm-bundler.js:199
|
||||
callWithAsyncErrorHandling @ runtime-core.esm-bundler.js:206
|
||||
emit @ runtime-core.esm-bundler.js:6473
|
||||
(匿名) @ runtime-core.esm-bundler.js:8188
|
||||
handleInput @ Search.vue:31
|
||||
callWithErrorHandling @ runtime-core.esm-bundler.js:199
|
||||
callWithAsyncErrorHandling @ runtime-core.esm-bundler.js:206
|
||||
invoker @ runtime-dom.esm-bundler.js:730
|
||||
NoteListPage.vue:104 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'toLowerCase')
|
||||
at NoteListPage.vue:104:57
|
||||
at wrappedFn (reactivity.esm-bundler.js:878:19)
|
||||
at Array.filter (<anonymous>)
|
||||
at apply (reactivity.esm-bundler.js:886:27)
|
||||
at Proxy.filter (reactivity.esm-bundler.js:778:12)
|
||||
at ComputedRefImpl.fn (NoteListPage.vue:102:22)
|
||||
at refreshComputed (reactivity.esm-bundler.js:391:28)
|
||||
at get value (reactivity.esm-bundler.js:1655:5)
|
||||
at ComputedRefImpl.fn (NoteListPage.vue:130:28)
|
||||
at refreshComputed (reactivity.esm-bundler.js:391:28)
|
||||
(匿名) @ NoteListPage.vue:104
|
||||
wrappedFn @ reactivity.esm-bundler.js:878
|
||||
apply @ reactivity.esm-bundler.js:886
|
||||
filter @ reactivity.esm-bundler.js:778
|
||||
(匿名) @ NoteListPage.vue:102
|
||||
refreshComputed @ reactivity.esm-bundler.js:391
|
||||
get value @ reactivity.esm-bundler.js:1655
|
||||
(匿名) @ NoteListPage.vue:130
|
||||
refreshComputed @ reactivity.esm-bundler.js:391
|
||||
get value @ reactivity.esm-bundler.js:1655
|
||||
unref @ reactivity.esm-bundler.js:1500
|
||||
get @ reactivity.esm-bundler.js:1506
|
||||
(匿名) @ NoteListPage.vue:35
|
||||
renderFnWithContext @ runtime-core.esm-bundler.js:695
|
||||
(匿名) @ runtime.js:286
|
||||
renderComponentRoot @ runtime-core.esm-bundler.js:6590
|
||||
componentUpdateFn @ runtime-core.esm-bundler.js:5468
|
||||
run @ reactivity.esm-bundler.js:237
|
||||
runIfDirty @ reactivity.esm-bundler.js:275
|
||||
callWithErrorHandling @ runtime-core.esm-bundler.js:199
|
||||
flushJobs @ runtime-core.esm-bundler.js:408
|
||||
Promise.then
|
||||
queueFlush @ runtime-core.esm-bundler.js:322
|
||||
queueJob @ runtime-core.esm-bundler.js:317
|
||||
effect2.scheduler @ runtime-core.esm-bundler.js:5519
|
||||
trigger @ reactivity.esm-bundler.js:265
|
||||
endBatch @ reactivity.esm-bundler.js:323
|
||||
notify @ reactivity.esm-bundler.js:614
|
||||
trigger @ reactivity.esm-bundler.js:588
|
||||
set value @ reactivity.esm-bundler.js:1472
|
||||
set @ reactivity.esm-bundler.js:1510
|
||||
_createVNode.onUpdate:modelValue._cache.<computed>._cache.<computed> @ NoteListPage.vue:31
|
||||
callWithErrorHandling @ runtime-core.esm-bundler.js:199
|
||||
callWithAsyncErrorHandling @ runtime-core.esm-bundler.js:206
|
||||
emit @ runtime-core.esm-bundler.js:6473
|
||||
(匿名) @ runtime-core.esm-bundler.js:8188
|
||||
handleInput @ Search.vue:31
|
||||
callWithErrorHandling @ runtime-core.esm-bundler.js:199
|
||||
callWithAsyncErrorHandling @ runtime-core.esm-bundler.js:206
|
||||
invoker @ runtime-dom.esm-bundler.js:730
|
||||
NoteListPage.vue:104 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'toLowerCase')
|
||||
at NoteListPage.vue:104:57
|
||||
at wrappedFn (reactivity.esm-bundler.js:878:19)
|
||||
at Array.filter (<anonymous>)
|
||||
at apply (reactivity.esm-bundler.js:886:27)
|
||||
at Proxy.filter (reactivity.esm-bundler.js:778:12)
|
||||
at ComputedRefImpl.fn (NoteListPage.vue:102:22)
|
||||
at refreshComputed (reactivity.esm-bundler.js:391:28)
|
||||
at get value (reactivity.esm-bundler.js:1655:5)
|
||||
at ComputedRefImpl.fn (NoteListPage.vue:130:28)
|
||||
at refreshComputed (reactivity.esm-bundler.js:391:28)
|
||||
(匿名) @ NoteListPage.vue:104
|
||||
wrappedFn @ reactivity.esm-bundler.js:878
|
||||
apply @ reactivity.esm-bundler.js:886
|
||||
filter @ reactivity.esm-bundler.js:778
|
||||
(匿名) @ NoteListPage.vue:102
|
||||
refreshComputed @ reactivity.esm-bundler.js:391
|
||||
get value @ reactivity.esm-bundler.js:1655
|
||||
(匿名) @ NoteListPage.vue:130
|
||||
refreshComputed @ reactivity.esm-bundler.js:391
|
||||
get value @ reactivity.esm-bundler.js:1655
|
||||
unref @ reactivity.esm-bundler.js:1500
|
||||
get @ reactivity.esm-bundler.js:1506
|
||||
(匿名) @ NoteListPage.vue:35
|
||||
renderFnWithContext @ runtime-core.esm-bundler.js:695
|
||||
(匿名) @ runtime.js:286
|
||||
renderComponentRoot @ runtime-core.esm-bundler.js:6590
|
||||
componentUpdateFn @ runtime-core.esm-bundler.js:5468
|
||||
run @ reactivity.esm-bundler.js:237
|
||||
runIfDirty @ reactivity.esm-bundler.js:275
|
||||
callWithErrorHandling @ runtime-core.esm-bundler.js:199
|
||||
flushJobs @ runtime-core.esm-bundler.js:408
|
||||
Promise.then
|
||||
queueFlush @ runtime-core.esm-bundler.js:322
|
||||
queueJob @ runtime-core.esm-bundler.js:317
|
||||
effect2.scheduler @ runtime-core.esm-bundler.js:5519
|
||||
trigger @ reactivity.esm-bundler.js:265
|
||||
endBatch @ reactivity.esm-bundler.js:323
|
||||
notify @ reactivity.esm-bundler.js:614
|
||||
trigger @ reactivity.esm-bundler.js:588
|
||||
set value @ reactivity.esm-bundler.js:1472
|
||||
set @ reactivity.esm-bundler.js:1510
|
||||
_createVNode.onUpdate:modelValue._cache.<computed>._cache.<computed> @ NoteListPage.vue:31
|
||||
callWithErrorHandling @ runtime-core.esm-bundler.js:199
|
||||
callWithAsyncErrorHandling @ runtime-core.esm-bundler.js:206
|
||||
emit @ runtime-core.esm-bundler.js:6473
|
||||
(匿名) @ runtime-core.esm-bundler.js:8188
|
||||
handleInput @ Search.vue:31
|
||||
callWithErrorHandling @ runtime-core.esm-bundler.js:199
|
||||
callWithAsyncErrorHandling @ runtime-core.esm-bundler.js:206
|
||||
invoker @ runtime-dom.esm-bundler.js:730
|
||||
NoteListPage.vue:104 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'toLowerCase')
|
||||
at NoteListPage.vue:104:57
|
||||
at wrappedFn (reactivity.esm-bundler.js:878:19)
|
||||
at Array.filter (<anonymous>)
|
||||
at apply (reactivity.esm-bundler.js:886:27)
|
||||
at Proxy.filter (reactivity.esm-bundler.js:778:12)
|
||||
at ComputedRefImpl.fn (NoteListPage.vue:102:22)
|
||||
at refreshComputed (reactivity.esm-bundler.js:391:28)
|
||||
at get value (reactivity.esm-bundler.js:1655:5)
|
||||
at ComputedRefImpl.fn (NoteListPage.vue:130:28)
|
||||
at refreshComputed (reactivity.esm-bundler.js:391:28)
|
||||
(匿名) @ NoteListPage.vue:104
|
||||
wrappedFn @ reactivity.esm-bundler.js:878
|
||||
apply @ reactivity.esm-bundler.js:886
|
||||
filter @ reactivity.esm-bundler.js:778
|
||||
(匿名) @ NoteListPage.vue:102
|
||||
refreshComputed @ reactivity.esm-bundler.js:391
|
||||
get value @ reactivity.esm-bundler.js:1655
|
||||
(匿名) @ NoteListPage.vue:130
|
||||
refreshComputed @ reactivity.esm-bundler.js:391
|
||||
get value @ reactivity.esm-bundler.js:1655
|
||||
unref @ reactivity.esm-bundler.js:1500
|
||||
get @ reactivity.esm-bundler.js:1506
|
||||
(匿名) @ NoteListPage.vue:35
|
||||
renderFnWithContext @ runtime-core.esm-bundler.js:695
|
||||
(匿名) @ runtime.js:286
|
||||
renderComponentRoot @ runtime-core.esm-bundler.js:6590
|
||||
componentUpdateFn @ runtime-core.esm-bundler.js:5468
|
||||
run @ reactivity.esm-bundler.js:237
|
||||
runIfDirty @ reactivity.esm-bundler.js:275
|
||||
callWithErrorHandling @ runtime-core.esm-bundler.js:199
|
||||
flushJobs @ runtime-core.esm-bundler.js:408
|
||||
Promise.then
|
||||
queueFlush @ runtime-core.esm-bundler.js:322
|
||||
queueJob @ runtime-core.esm-bundler.js:317
|
||||
effect2.scheduler @ runtime-core.esm-bundler.js:5519
|
||||
trigger @ reactivity.esm-bundler.js:265
|
||||
endBatch @ reactivity.esm-bundler.js:323
|
||||
notify @ reactivity.esm-bundler.js:614
|
||||
trigger @ reactivity.esm-bundler.js:588
|
||||
set value @ reactivity.esm-bundler.js:1472
|
||||
set @ reactivity.esm-bundler.js:1510
|
||||
_createVNode.onUpdate:modelValue._cache.<computed>._cache.<computed> @ NoteListPage.vue:31
|
||||
callWithErrorHandling @ runtime-core.esm-bundler.js:199
|
||||
callWithAsyncErrorHandling @ runtime-core.esm-bundler.js:206
|
||||
emit @ runtime-core.esm-bundler.js:6473
|
||||
(匿名) @ runtime-core.esm-bundler.js:8188
|
||||
handleInput @ Search.vue:31
|
||||
callWithErrorHandling @ runtime-core.esm-bundler.js:199
|
||||
callWithAsyncErrorHandling @ runtime-core.esm-bundler.js:206
|
||||
invoker @ runtime-dom.esm-bundler.js:730
|
||||
NoteListPage.vue:104 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'toLowerCase')
|
||||
at NoteListPage.vue:104:57
|
||||
at wrappedFn (reactivity.esm-bundler.js:878:19)
|
||||
at Array.filter (<anonymous>)
|
||||
at apply (reactivity.esm-bundler.js:886:27)
|
||||
at Proxy.filter (reactivity.esm-bundler.js:778:12)
|
||||
at ComputedRefImpl.fn (NoteListPage.vue:102:22)
|
||||
at refreshComputed (reactivity.esm-bundler.js:391:28)
|
||||
at get value (reactivity.esm-bundler.js:1655:5)
|
||||
at ComputedRefImpl.fn (NoteListPage.vue:130:28)
|
||||
at refreshComputed (reactivity.esm-bundler.js:391:28)
|
||||
(匿名) @ NoteListPage.vue:104
|
||||
wrappedFn @ reactivity.esm-bundler.js:878
|
||||
apply @ reactivity.esm-bundler.js:886
|
||||
filter @ reactivity.esm-bundler.js:778
|
||||
(匿名) @ NoteListPage.vue:102
|
||||
refreshComputed @ reactivity.esm-bundler.js:391
|
||||
get value @ reactivity.esm-bundler.js:1655
|
||||
(匿名) @ NoteListPage.vue:130
|
||||
refreshComputed @ reactivity.esm-bundler.js:391
|
||||
get value @ reactivity.esm-bundler.js:1655
|
||||
unref @ reactivity.esm-bundler.js:1500
|
||||
get @ reactivity.esm-bundler.js:1506
|
||||
(匿名) @ NoteListPage.vue:35
|
||||
renderFnWithContext @ runtime-core.esm-bundler.js:695
|
||||
(匿名) @ runtime.js:286
|
||||
renderComponentRoot @ runtime-core.esm-bundler.js:6590
|
||||
componentUpdateFn @ runtime-core.esm-bundler.js:5468
|
||||
run @ reactivity.esm-bundler.js:237
|
||||
runIfDirty @ reactivity.esm-bundler.js:275
|
||||
callWithErrorHandling @ runtime-core.esm-bundler.js:199
|
||||
flushJobs @ runtime-core.esm-bundler.js:408
|
||||
Promise.then
|
||||
queueFlush @ runtime-core.esm-bundler.js:322
|
||||
queueJob @ runtime-core.esm-bundler.js:317
|
||||
effect2.scheduler @ runtime-core.esm-bundler.js:5519
|
||||
trigger @ reactivity.esm-bundler.js:265
|
||||
endBatch @ reactivity.esm-bundler.js:323
|
||||
notify @ reactivity.esm-bundler.js:614
|
||||
trigger @ reactivity.esm-bundler.js:588
|
||||
set value @ reactivity.esm-bundler.js:1472
|
||||
set @ reactivity.esm-bundler.js:1510
|
||||
_createVNode.onUpdate:modelValue._cache.<computed>._cache.<computed> @ NoteListPage.vue:31
|
||||
callWithErrorHandling @ runtime-core.esm-bundler.js:199
|
||||
callWithAsyncErrorHandling @ runtime-core.esm-bundler.js:206
|
||||
emit @ runtime-core.esm-bundler.js:6473
|
||||
(匿名) @ runtime-core.esm-bundler.js:8188
|
||||
handleInput @ Search.vue:31
|
||||
callWithErrorHandling @ runtime-core.esm-bundler.js:199
|
||||
callWithAsyncErrorHandling @ runtime-core.esm-bundler.js:206
|
||||
invoker @ runtime-dom.esm-bundler.js:730
|
||||
NoteListPage.vue:104 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'toLowerCase')
|
||||
at NoteListPage.vue:104:57
|
||||
at wrappedFn (reactivity.esm-bundler.js:878:19)
|
||||
at Array.filter (<anonymous>)
|
||||
at apply (reactivity.esm-bundler.js:886:27)
|
||||
at Proxy.filter (reactivity.esm-bundler.js:778:12)
|
||||
at ComputedRefImpl.fn (NoteListPage.vue:102:22)
|
||||
at refreshComputed (reactivity.esm-bundler.js:391:28)
|
||||
at get value (reactivity.esm-bundler.js:1655:5)
|
||||
at ComputedRefImpl.fn (NoteListPage.vue:130:28)
|
||||
at refreshComputed (reactivity.esm-bundler.js:391:28)
|
||||
(匿名) @ NoteListPage.vue:104
|
||||
wrappedFn @ reactivity.esm-bundler.js:878
|
||||
apply @ reactivity.esm-bundler.js:886
|
||||
filter @ reactivity.esm-bundler.js:778
|
||||
(匿名) @ NoteListPage.vue:102
|
||||
refreshComputed @ reactivity.esm-bundler.js:391
|
||||
get value @ reactivity.esm-bundler.js:1655
|
||||
(匿名) @ NoteListPage.vue:130
|
||||
refreshComputed @ reactivity.esm-bundler.js:391
|
||||
get value @ reactivity.esm-bundler.js:1655
|
||||
unref @ reactivity.esm-bundler.js:1500
|
||||
get @ reactivity.esm-bundler.js:1506
|
||||
(匿名) @ NoteListPage.vue:35
|
||||
renderFnWithContext @ runtime-core.esm-bundler.js:695
|
||||
(匿名) @ runtime.js:286
|
||||
renderComponentRoot @ runtime-core.esm-bundler.js:6590
|
||||
componentUpdateFn @ runtime-core.esm-bundler.js:5468
|
||||
run @ reactivity.esm-bundler.js:237
|
||||
runIfDirty @ reactivity.esm-bundler.js:275
|
||||
callWithErrorHandling @ runtime-core.esm-bundler.js:199
|
||||
flushJobs @ runtime-core.esm-bundler.js:408
|
||||
Promise.then
|
||||
queueFlush @ runtime-core.esm-bundler.js:322
|
||||
queueJob @ runtime-core.esm-bundler.js:317
|
||||
effect2.scheduler @ runtime-core.esm-bundler.js:5519
|
||||
trigger @ reactivity.esm-bundler.js:265
|
||||
endBatch @ reactivity.esm-bundler.js:323
|
||||
notify @ reactivity.esm-bundler.js:614
|
||||
trigger @ reactivity.esm-bundler.js:588
|
||||
set value @ reactivity.esm-bundler.js:1472
|
||||
set @ reactivity.esm-bundler.js:1510
|
||||
_createVNode.onUpdate:modelValue._cache.<computed>._cache.<computed> @ NoteListPage.vue:31
|
||||
callWithErrorHandling @ runtime-core.esm-bundler.js:199
|
||||
callWithAsyncErrorHandling @ runtime-core.esm-bundler.js:206
|
||||
emit @ runtime-core.esm-bundler.js:6473
|
||||
(匿名) @ runtime-core.esm-bundler.js:8188
|
||||
handleInput @ Search.vue:31
|
||||
callWithErrorHandling @ runtime-core.esm-bundler.js:199
|
||||
callWithAsyncErrorHandling @ runtime-core.esm-bundler.js:206
|
||||
invoker @ runtime-dom.esm-bundler.js:730
|
||||
NoteListPage.vue:296 搜索栏失去焦点
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "smartisannote.vue",
|
||||
"name": "smartisannote.re",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "smartisannote.vue",
|
||||
"name": "smartisannote.re",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
|
||||
13
src/main.js
13
src/main.js
@@ -13,6 +13,9 @@ import FolderPage from './pages/FolderPage.vue'
|
||||
// 设置页面
|
||||
import SettingsPage from './pages/SettingsPage.vue'
|
||||
|
||||
// 导入数据库初始化函数
|
||||
import { initDB } from './utils/indexedDBStorage'
|
||||
|
||||
// 配置路由规则
|
||||
// 定义应用的所有路由路径和对应的组件
|
||||
const routes = [
|
||||
@@ -42,6 +45,16 @@ const router = createRouter({
|
||||
// 创建并挂载Vue应用实例
|
||||
// 配置Pinia状态管理和Vue Router路由
|
||||
const app = createApp(App)
|
||||
|
||||
// 初始化数据库
|
||||
initDB().then(() => {
|
||||
console.log('数据库初始化成功')
|
||||
}).catch(error => {
|
||||
console.error('数据库初始化失败:', error)
|
||||
// 即使数据库初始化失败,也继续启动应用
|
||||
// 这样可以确保应用在没有IndexedDB支持的环境中仍然可以运行
|
||||
})
|
||||
|
||||
// 使用Pinia进行状态管理
|
||||
app.use(createPinia())
|
||||
// 使用Vue Router进行路由管理
|
||||
|
||||
@@ -73,7 +73,7 @@ onMounted(() => {
|
||||
// 加载预设的模拟数据
|
||||
store.loadMockData()
|
||||
} else {
|
||||
// 从localStorage加载用户数据
|
||||
// 从Storage加载用户数据
|
||||
store.loadData()
|
||||
}
|
||||
})
|
||||
@@ -102,8 +102,8 @@ const filteredNotes = computed(() => {
|
||||
return store.notes.filter(note => {
|
||||
// 先检查搜索条件
|
||||
const matchesSearch = !lowerCaseQuery ||
|
||||
(note.title && note.title.toLowerCase().includes(lowerCaseQuery)) ||
|
||||
(note.content && note.content.toLowerCase().includes(lowerCaseQuery))
|
||||
(note.title && typeof note.title === 'string' && note.title.toLowerCase().includes(lowerCaseQuery)) ||
|
||||
(note.content && typeof note.content === 'string' && note.content.toLowerCase().includes(lowerCaseQuery))
|
||||
|
||||
if (!matchesSearch) return false
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ const store = useAppStore()
|
||||
const router = useRouter()
|
||||
|
||||
// 页面挂载时加载初始数据
|
||||
// 从localStorage加载用户设置和便签数据
|
||||
// 从Storage加载用户设置和便签数据
|
||||
onMounted(() => {
|
||||
store.loadData()
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import * as storage from '../utils/storage'
|
||||
import * as storage from '../utils/indexedDBStorage'
|
||||
import { getCurrentDateTime, getPastDate } from '../utils/dateUtils'
|
||||
|
||||
/**
|
||||
@@ -48,13 +48,13 @@ export const useAppStore = defineStore('app', {
|
||||
actions: {
|
||||
/**
|
||||
* 初始化数据
|
||||
* 从localStorage加载便签、文件夹和设置数据
|
||||
* 从Storage加载便签、文件夹和设置数据
|
||||
* 如果没有数据则加载预设的mock数据
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async loadData() {
|
||||
try {
|
||||
// 从localStorage加载数据
|
||||
// 从Storage加载数据
|
||||
const loadedNotes = await storage.getNotes()
|
||||
const loadedFolders = await storage.getFolders()
|
||||
const loadedSettings = await storage.getSettings()
|
||||
@@ -201,14 +201,14 @@ export const useAppStore = defineStore('app', {
|
||||
this.folders = mockFolders
|
||||
this.settings = mockSettings
|
||||
|
||||
// 保存到localStorage
|
||||
// 保存到Storage
|
||||
await storage.saveNotes(mockNotes)
|
||||
await storage.saveFolders(mockFolders)
|
||||
await storage.saveSettings(mockSettings)
|
||||
},
|
||||
|
||||
/**
|
||||
* 保存便签数据到localStorage
|
||||
* 保存便签数据到Storage
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async saveNotes() {
|
||||
@@ -220,7 +220,7 @@ export const useAppStore = defineStore('app', {
|
||||
},
|
||||
|
||||
/**
|
||||
* 保存文件夹数据到localStorage
|
||||
* 保存文件夹数据到Storage
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async saveFolders() {
|
||||
@@ -232,7 +232,7 @@ export const useAppStore = defineStore('app', {
|
||||
},
|
||||
|
||||
/**
|
||||
* 保存设置数据到localStorage
|
||||
* 保存设置数据到Storage
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async saveSettings() {
|
||||
|
||||
475
src/utils/indexedDBStorage.js
Normal file
475
src/utils/indexedDBStorage.js
Normal file
@@ -0,0 +1,475 @@
|
||||
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<IDBDatabase>} 数据库实例
|
||||
*/
|
||||
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<Array>} 数据数组
|
||||
*/
|
||||
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<void>}
|
||||
*/
|
||||
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<Object|null>} 项对象或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<Object>} 添加的项
|
||||
*/
|
||||
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<Object|null>} 更新后的项或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<boolean>} 删除成功返回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<Array>} 便签数组
|
||||
*/
|
||||
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<void>}
|
||||
*/
|
||||
export const saveNotes = async (notes) => {
|
||||
try {
|
||||
await saveToStore(NOTES_STORE, notes)
|
||||
} catch (error) {
|
||||
console.error('Error saving notes:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加新便签
|
||||
* 创建一个新的便签对象并添加到便签列表中
|
||||
* @param {Object} note - 便签对象,包含便签内容和其他属性
|
||||
* @returns {Promise<Object>} 新创建的便签对象
|
||||
*/
|
||||
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<Object|null>} 更新后的便签对象,如果未找到则返回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<boolean>} 删除成功返回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<Array>} 文件夹数组
|
||||
*/
|
||||
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<void>}
|
||||
*/
|
||||
export const saveFolders = async (folders) => {
|
||||
try {
|
||||
await saveToStore(FOLDERS_STORE, folders)
|
||||
} catch (error) {
|
||||
console.error('Error saving folders:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加新文件夹
|
||||
* 创建一个新的文件夹对象并添加到文件夹列表中
|
||||
* @param {Object} folder - 文件夹对象,包含文件夹名称等属性
|
||||
* @returns {Promise<Object>} 新创建的文件夹对象
|
||||
*/
|
||||
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<Object>} 设置对象,如果读取失败则返回默认设置
|
||||
*/
|
||||
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<void>}
|
||||
*/
|
||||
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<void>}
|
||||
*/
|
||||
export const initDB = async () => {
|
||||
try {
|
||||
await openDB()
|
||||
} catch (error) {
|
||||
console.error('Error initializing database:', error)
|
||||
}
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
import { getCurrentDateTime, getTimestamp } from './dateUtils'
|
||||
|
||||
// 本地存储键名常量
|
||||
// 用于在localStorage中标识不同类型的数据
|
||||
const NOTES_KEY = 'notes'; // 便签数据键名
|
||||
const FOLDERS_KEY = 'folders'; // 文件夹数据键名
|
||||
const SETTINGS_KEY = 'settings'; // 设置数据键名
|
||||
|
||||
// 便签操作函数
|
||||
// 提供便签的增删改查功能
|
||||
|
||||
/**
|
||||
* 获取所有便签数据
|
||||
* 从localStorage中读取便签数据并解析为JavaScript对象
|
||||
* @returns {Promise<Array>} 便签数组,如果读取失败则返回空数组
|
||||
*/
|
||||
export const getNotes = async () => {
|
||||
try {
|
||||
const notesJson = localStorage.getItem(NOTES_KEY);
|
||||
return notesJson ? JSON.parse(notesJson) : [];
|
||||
} catch (error) {
|
||||
console.error('Error getting notes:', error);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存便签数据
|
||||
* 将便签数组转换为JSON字符串并保存到localStorage
|
||||
* @param {Array} notes - 便签数组
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export const saveNotes = async (notes) => {
|
||||
try {
|
||||
localStorage.setItem(NOTES_KEY, JSON.stringify(notes));
|
||||
} catch (error) {
|
||||
console.error('Error saving notes:', error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 添加新便签
|
||||
* 创建一个新的便签对象并添加到便签列表中
|
||||
* @param {Object} note - 便签对象,包含便签内容和其他属性
|
||||
* @returns {Promise<Object>} 新创建的便签对象
|
||||
*/
|
||||
export const addNote = async (note) => {
|
||||
// 创建新的便签对象,添加必要的属性
|
||||
const newNote = {
|
||||
...note,
|
||||
id: getTimestamp().toString(), // 使用时间戳生成唯一ID
|
||||
createdAt: getCurrentDateTime(), // 创建时间
|
||||
updatedAt: getCurrentDateTime(), // 更新时间
|
||||
isStarred: note.isStarred || false, // 是否加星
|
||||
isTop: note.isTop || false, // 是否置顶
|
||||
hasImage: note.hasImage || false, // 是否包含图片
|
||||
isDeleted: note.isDeleted || false, // 是否已删除
|
||||
deletedAt: note.deletedAt || null // 删除时间
|
||||
};
|
||||
|
||||
// 获取现有便签列表,添加新便签并保存
|
||||
const notes = await getNotes();
|
||||
notes.push(newNote);
|
||||
await saveNotes(notes);
|
||||
|
||||
return newNote;
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新便签
|
||||
* 根据ID查找并更新便签信息
|
||||
* @param {string} id - 便签ID
|
||||
* @param {Object} updates - 要更新的属性对象
|
||||
* @returns {Promise<Object|null>} 更新后的便签对象,如果未找到则返回null
|
||||
*/
|
||||
export const updateNote = async (id, updates) => {
|
||||
// 获取所有便签并查找要更新的便签
|
||||
const notes = await getNotes();
|
||||
const index = notes.findIndex(note => note.id === id);
|
||||
|
||||
// 如果未找到指定ID的便签,返回null
|
||||
if (index === -1) return null;
|
||||
|
||||
// 创建更新后的便签对象
|
||||
const updatedNote = {
|
||||
...notes[index],
|
||||
...updates,
|
||||
updatedAt: getCurrentDateTime(), // 更新最后修改时间
|
||||
};
|
||||
|
||||
// 更新便签列表并保存
|
||||
notes[index] = updatedNote;
|
||||
await saveNotes(notes);
|
||||
|
||||
return updatedNote;
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除便签
|
||||
* 根据ID从便签列表中移除便签
|
||||
* @param {string} id - 要删除的便签ID
|
||||
* @returns {Promise<boolean>} 删除成功返回true,未找到便签返回false
|
||||
*/
|
||||
export const deleteNote = async (id) => {
|
||||
// 获取所有便签并过滤掉要删除的便签
|
||||
const notes = await getNotes();
|
||||
const filteredNotes = notes.filter(note => note.id !== id);
|
||||
|
||||
// 如果便签数量没有变化,说明未找到要删除的便签
|
||||
if (notes.length === filteredNotes.length) return false;
|
||||
|
||||
// 保存更新后的便签列表
|
||||
await saveNotes(filteredNotes);
|
||||
return true;
|
||||
};
|
||||
|
||||
// 文件夹操作函数
|
||||
// 提供文件夹的增删改查功能
|
||||
|
||||
/**
|
||||
* 获取所有文件夹数据
|
||||
* 从localStorage中读取文件夹数据并解析为JavaScript对象
|
||||
* @returns {Promise<Array>} 文件夹数组,如果读取失败则返回空数组
|
||||
*/
|
||||
export const getFolders = async () => {
|
||||
try {
|
||||
const foldersJson = localStorage.getItem(FOLDERS_KEY);
|
||||
return foldersJson ? JSON.parse(foldersJson) : [];
|
||||
} catch (error) {
|
||||
console.error('Error getting folders:', error);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存文件夹数据
|
||||
* 将文件夹数组转换为JSON字符串并保存到localStorage
|
||||
* @param {Array} folders - 文件夹数组
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export const saveFolders = async (folders) => {
|
||||
try {
|
||||
localStorage.setItem(FOLDERS_KEY, JSON.stringify(folders));
|
||||
} catch (error) {
|
||||
console.error('Error saving folders:', error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 添加新文件夹
|
||||
* 创建一个新的文件夹对象并添加到文件夹列表中
|
||||
* @param {Object} folder - 文件夹对象,包含文件夹名称等属性
|
||||
* @returns {Promise<Object>} 新创建的文件夹对象
|
||||
*/
|
||||
export const addFolder = async (folder) => {
|
||||
// 创建新的文件夹对象,添加必要的属性
|
||||
const newFolder = {
|
||||
...folder,
|
||||
id: getTimestamp().toString(), // 使用时间戳生成唯一ID
|
||||
createdAt: getCurrentDateTime(), // 创建时间
|
||||
};
|
||||
|
||||
// 获取现有文件夹列表,添加新文件夹并保存
|
||||
const folders = await getFolders();
|
||||
folders.push(newFolder);
|
||||
await saveFolders(folders);
|
||||
|
||||
return newFolder;
|
||||
};
|
||||
|
||||
// 设置操作函数
|
||||
// 提供应用设置的读取和保存功能
|
||||
|
||||
/**
|
||||
* 获取应用设置
|
||||
* 从localStorage中读取设置数据并解析为JavaScript对象
|
||||
* @returns {Promise<Object>} 设置对象,如果读取失败则返回默认设置
|
||||
*/
|
||||
export const getSettings = async () => {
|
||||
try {
|
||||
const settingsJson = localStorage.getItem(SETTINGS_KEY);
|
||||
// 如果没有保存的设置,返回默认设置
|
||||
return settingsJson ? JSON.parse(settingsJson) : { cloudSync: false, darkMode: false };
|
||||
} catch (error) {
|
||||
console.error('Error getting settings:', error);
|
||||
// 出错时返回默认设置
|
||||
return { cloudSync: false, darkMode: false };
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存应用设置
|
||||
* 将设置对象转换为JSON字符串并保存到localStorage
|
||||
* @param {Object} settings - 设置对象
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export const saveSettings = async (settings) => {
|
||||
try {
|
||||
localStorage.setItem(SETTINGS_KEY, JSON.stringify(settings));
|
||||
} catch (error) {
|
||||
console.error('Error saving settings:', error);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user