优化 头部便签管理点击区域;

补充注释;
This commit is contained in:
User
2025-10-13 10:48:18 +08:00
parent 27133aa107
commit 2e933ece94
13 changed files with 523 additions and 175 deletions

63
package-lock.json generated
View File

@@ -13,6 +13,7 @@
"@capacitor/cli": "^5.7.2",
"@capacitor/core": "^5.7.2",
"@capacitor/ios": "^5.7.2",
"@ionic/vue": "^8.7.6",
"@vue/cli-service": "^5.0.9",
"@vue/compiler-sfc": "^3.5.22",
"basic-ftp": "^5.0.5",
@@ -2150,6 +2151,26 @@
"node": ">=16.0.0"
}
},
"node_modules/@ionic/core": {
"version": "8.7.6",
"resolved": "https://registry.npmmirror.com/@ionic/core/-/core-8.7.6.tgz",
"integrity": "sha512-ufV64Pl0BYSoNla+DaTRXTS3hX6MQZZJPhAR3fJQ4N5Fg/vwMcHADQffstKZeoPqk6mbJoLqoTBjcWvaLRdO0g==",
"license": "MIT",
"dependencies": {
"@stencil/core": "4.38.0",
"ionicons": "^8.0.13",
"tslib": "^2.1.0"
}
},
"node_modules/@ionic/core/node_modules/ionicons": {
"version": "8.0.13",
"resolved": "https://registry.npmmirror.com/ionicons/-/ionicons-8.0.13.tgz",
"integrity": "sha512-2QQVyG2P4wszne79jemMjWYLp0DBbDhr4/yFroPCxvPP1wtMxgdIV3l5n+XZ5E9mgoXU79w7yTWpm2XzJsISxQ==",
"license": "MIT",
"dependencies": {
"@stencil/core": "^4.35.3"
}
},
"node_modules/@ionic/utils-array": {
"version": "2.1.6",
"resolved": "https://registry.npmmirror.com/@ionic/utils-array/-/utils-array-2.1.6.tgz",
@@ -2359,6 +2380,26 @@
"node": ">=16.0.0"
}
},
"node_modules/@ionic/vue": {
"version": "8.7.6",
"resolved": "https://registry.npmmirror.com/@ionic/vue/-/vue-8.7.6.tgz",
"integrity": "sha512-gK5x5Y0ZpZAW12gjvyBO9oUfwDZxMS7y0xcO0P9qzo++h3ZLcFcSGjHs8D4isUY/mF6mRagt1Y/5b0xDhgUBBw==",
"license": "MIT",
"dependencies": {
"@ionic/core": "8.7.6",
"@stencil/vue-output-target": "0.10.7",
"ionicons": "^8.0.13"
}
},
"node_modules/@ionic/vue/node_modules/ionicons": {
"version": "8.0.13",
"resolved": "https://registry.npmmirror.com/ionicons/-/ionicons-8.0.13.tgz",
"integrity": "sha512-2QQVyG2P4wszne79jemMjWYLp0DBbDhr4/yFroPCxvPP1wtMxgdIV3l5n+XZ5E9mgoXU79w7yTWpm2XzJsISxQ==",
"license": "MIT",
"dependencies": {
"@stencil/core": "^4.35.3"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
@@ -2936,6 +2977,28 @@
"@rollup/rollup-win32-x64-msvc": "4.34.9"
}
},
"node_modules/@stencil/vue-output-target": {
"version": "0.10.7",
"resolved": "https://registry.npmmirror.com/@stencil/vue-output-target/-/vue-output-target-0.10.7.tgz",
"integrity": "sha512-IYxDe+SLCkwhwsWRdynE31rTK1zN3hVwwojQ/V9lrN8Gnx4PTvrUQHiRno9jFo1dk+EaBZWX9gZSmXta0ZaZew==",
"license": "MIT",
"peerDependencies": {
"@stencil/core": ">=2.0.0 || >=3 || >= 4.0.0-beta.0 || >= 4.0.0",
"vue": "^3.4.38",
"vue-router": "^4.5.0"
},
"peerDependenciesMeta": {
"@stencil/core": {
"optional": true
},
"vue": {
"optional": false
},
"vue-router": {
"optional": true
}
}
},
"node_modules/@surma/rollup-plugin-off-main-thread": {
"version": "2.2.3",
"resolved": "https://registry.npmmirror.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",

View File

@@ -18,6 +18,7 @@
"@capacitor/cli": "^5.7.2",
"@capacitor/core": "^5.7.2",
"@capacitor/ios": "^5.7.2",
"@ionic/vue": "^8.7.6",
"@vue/cli-service": "^5.0.9",
"@vue/compiler-sfc": "^3.5.22",
"basic-ftp": "^5.0.5",

View File

@@ -6,5 +6,4 @@
<script setup>
import '@/common/base.css'
// App根组件不需要额外的逻辑
</script>

View File

@@ -5,8 +5,8 @@
<img class="left-icon" :src="leftIconSource" @click="handleLeftAction" />
<!-- 标题区域 -->
<div class="title-container" @click="handleTitlePress">
<span class="text">{{ title }}</span>
<div class="title-container">
<span class="text" @click="handleTitlePress">{{ title }}</span>
<!-- 文件夹展开图标 -->
<img v-if="showFolderIcon" class="folder-icon" :src="folderExpanded ? '/assets/icons/drawable-xxhdpi/folder_title_arrow_pressed.png' : '/assets/icons/drawable-xxhdpi/folder_title_arrow_normal.png'" @click.stop="handleFolderToggle" />
</div>
@@ -118,7 +118,7 @@ const handleLeftAction = () => {
}
}
const handleAction = (actionType) => {
const handleAction = actionType => {
// 处理右侧操作按钮点击事件
if (props.onAction) {
props.onAction(actionType)
@@ -156,7 +156,7 @@ const handleTitlePress = () => {
cursor: pointer;
}
.right-group {
gap: .6rem;
gap: 0.6rem;
}
.image_4-placeholder {

View File

@@ -79,13 +79,15 @@ const isSlided = ref(false) // 是否已经滑动到阈值
const formattedDate = computed(() => {
// 直接返回已经格式化的日期字符串
// 日期格式化已在父组件中完成
return props.date
})
// 处理显示内容过滤HTML标签并只显示第一行
// 用于在便签列表中显示便签的预览内容
const displayContent = computed(() => {
console.log('NoteItem content:', props.content)
// 过滤HTML标签
// 过滤HTML标签,只保留纯文本内容
let text = props.content.replace(/<[^>]*>/g, '')
console.log('NoteItem text without HTML:', text)
@@ -100,8 +102,11 @@ const displayContent = computed(() => {
})
// 滑动阈值(删除按钮宽度)
// 当滑动距离超过此值时,显示删除按钮
const SLIDE_THRESHOLD = 64 // 4rem 转换为 px
// 处理便签点击事件
// 只有在未滑动状态下才触发点击事件,避免与滑动操作冲突
const handlePress = () => {
// 只有在未滑动状态下才触发点击事件
if (slideOffset.value === 0 && props.onPress) {
@@ -109,31 +114,39 @@ const handlePress = () => {
}
}
// 处理星标切换事件
// 点击星标图标时调用父组件传递的回调函数
const handleStarToggle = () => {
if (props.onStarToggle) {
props.onStarToggle()
}
}
// 处理置顶切换事件
// 点击置顶图标时调用父组件传递的回调函数
const handleTopToggle = () => {
if (props.onTopToggle) {
props.onTopToggle()
}
}
// 处理删除事件
// 点击删除按钮时调用父组件传递的回调函数
const handleDelete = () => {
if (props.onDelete) {
props.onDelete()
}
}
// 触摸开始
// 触摸开始事件处理函数
// 记录触摸开始时的X坐标用于计算滑动距离
const handleTouchStart = e => {
// 重置滑动状态
startX.value = e.touches[0].clientX
}
// 触摸移动
// 触摸移动事件处理函数
// 根据手指移动距离计算便签条的水平偏移量
const handleTouchMove = e => {
if (!startX.value) return
@@ -147,15 +160,15 @@ const handleTouchMove = e => {
// 设置滑动状态
isSliding.value = true
// 应用阻尼效果
// 应用阻尼效果,使超过阈值后的滑动更加困难
let offset = 0
if (diffX <= SLIDE_THRESHOLD) {
// 线性滑动
// 线性滑动,在阈值内正常滑动
offset = diffX
} else {
// 超过阈值后应用阻尼效果
// 超过阈值后应用阻尼效果,增加滑动阻力
const excess = diffX - SLIDE_THRESHOLD
offset = SLIDE_THRESHOLD + excess * 0.03 // 0.3 为阻尼系数
offset = SLIDE_THRESHOLD + excess * 0.03 // 0.03 为阻尼系数
}
slideOffset.value = offset
@@ -171,12 +184,14 @@ const handleTouchMove = e => {
}
}
// 触摸结束
// 触摸结束事件处理函数
// 根据滑动距离决定便签条的最终位置
const handleTouchEnd = () => {
if (!startX.value) return
// 如果滑动超过阈值,保持滑出状态;否则回弹
if (slideOffset.value >= SLIDE_THRESHOLD) {
// 保持滑出状态,显示删除按钮
slideOffset.value = SLIDE_THRESHOLD
isSlided.value = true
} else {

View File

@@ -50,42 +50,43 @@ onMounted(() => {
}
})
// 工具配置
// 工具配置
// 定义富文本编辑器的所有工具按钮及其功能
const tools = ref([
{
name: 'bold',
name: 'bold', // 加粗工具
icon: '/assets/icons/drawable-xxhdpi/rtf_bold_normal.9.png',
action: () => formatText('bold'),
active: false,
action: () => formatText('bold'), // 执行加粗格式化
active: false, // 工具是否处于激活状态
},
{
name: 'center',
name: 'center', // 居中对齐工具
icon: '/assets/icons/drawable-xxhdpi/rtf_center_normal.9.png',
action: () => formatText('justifyCenter'),
action: () => formatText('justifyCenter'), // 执行居中对齐格式化
active: false,
},
{
name: 'todo',
name: 'todo', // 待办事项工具
icon: '/assets/icons/drawable-xxhdpi/rtf_gtasks_normal.9.png',
action: () => formatText('insertTodoList'),
action: () => formatText('insertTodoList'), // 插入待办事项列表
active: false,
},
{
name: 'list',
name: 'list', // 无序列表工具
icon: '/assets/icons/drawable-xxhdpi/rtf_list_normal.9.png',
action: () => formatText('insertUnorderedList'),
action: () => formatText('insertUnorderedList'), // 插入无序列表
active: false,
},
{
name: 'header',
name: 'header', // 标题工具
icon: '/assets/icons/drawable-xxhdpi/rtf_header_normal.9.png',
action: () => formatText('formatBlock', 'h2'),
action: () => formatText('formatBlock', 'h2'), // 格式化为二级标题
active: false,
},
{
name: 'quote',
name: 'quote', // 引用工具
icon: '/assets/icons/drawable-xxhdpi/rtf_quot_normal.9.png',
action: () => insertQuote(),
action: () => insertQuote(), // 插入引用格式
active: false,
},
])
@@ -112,6 +113,7 @@ const handleInput = () => {
}
// 检查当前选区是否已经在某种格式中
// 用于防止重复应用相同的格式,例如重复加粗
const isAlreadyInFormat = formatType => {
const selection = window.getSelection()
if (selection.rangeCount > 0) {
@@ -121,10 +123,15 @@ const isAlreadyInFormat = formatType => {
// 向上查找父元素,检查是否已经在指定格式中
let current = container.nodeType === Node.TEXT_NODE ? container.parentElement : container
while (current && current !== editorRef.value) {
// 检查加粗格式
if (formatType === 'bold' && current.tagName === 'B') return true
// 检查居中对齐格式
if (formatType === 'center' && current.style.textAlign === 'center') return true
// 检查标题格式
if (formatType === 'header' && current.tagName === 'H2') return true
// 检查引用格式
if (formatType === 'quote' && (current.tagName === 'BLOCKQUOTE' || current.classList.contains('quote-content'))) return true
// 检查列表格式
if (formatType === 'list' && (current.tagName === 'UL' || current.tagName === 'OL' || current.tagName === 'LI')) return true
current = current.parentElement
}
@@ -133,6 +140,7 @@ const isAlreadyInFormat = formatType => {
}
// 检查是否在列表、引用或待办事项中(用于嵌套限制)
// 防止在已有的列表、引用或待办事项中再次插入相同类型的元素
const isInListOrQuote = () => {
const selection = window.getSelection()
if (selection.rangeCount > 0) {
@@ -142,7 +150,10 @@ const isInListOrQuote = () => {
// 向上查找父元素,检查是否在列表、引用或待办事项中
let current = container.nodeType === Node.TEXT_NODE ? container.parentElement : container
while (current && current !== editorRef.value) {
if (current.tagName === 'UL' || current.tagName === 'OL' || current.tagName === 'LI' || current.tagName === 'BLOCKQUOTE' || current.classList.contains('quote-content') || current.classList.contains('todo-container')) {
// 检查是否在列表、引用或待办事项中
if (current.tagName === 'UL' || current.tagName === 'OL' || current.tagName === 'LI' ||
current.tagName === 'BLOCKQUOTE' || current.classList.contains('quote-content') ||
current.classList.contains('todo-container')) {
return true
}
current = current.parentElement
@@ -152,8 +163,10 @@ const isInListOrQuote = () => {
}
// 格式化文本
// 根据指定的命令和值对选中文本应用格式
const formatText = (command, value = null) => {
// 检查是否已经应用了相同的格式,如果已应用则取消格式
// 例如,如果文本已经是加粗的,再次点击加粗按钮会取消加粗
if (command === 'bold' && isAlreadyInFormat('bold')) {
document.execCommand('bold', false, null)
updateToolbarState()
@@ -161,6 +174,7 @@ const formatText = (command, value = null) => {
return
}
// 处理居中对齐切换:如果已居中则取消居中
if (command === 'justifyCenter' && isAlreadyInFormat('center')) {
document.execCommand('justifyLeft', false, null)
updateToolbarState()
@@ -168,6 +182,7 @@ const formatText = (command, value = null) => {
return
}
// 处理标题格式切换:如果已是标题则转为普通段落
if (command === 'formatBlock' && value === 'h2' && isAlreadyInFormat('header')) {
document.execCommand('formatBlock', false, '<p>')
updateToolbarState()
@@ -175,6 +190,7 @@ const formatText = (command, value = null) => {
return
}
// 处理列表格式切换:如果已是列表则取消列表
if (command === 'insertUnorderedList' && isAlreadyInFormat('list')) {
document.execCommand('insertUnorderedList', false, null)
updateToolbarState()
@@ -189,13 +205,16 @@ const formatText = (command, value = null) => {
return
}
// 检查嵌套限制
// 检查嵌套限制,防止在列表、引用中再次插入列表或引用
if ((command === 'insertUnorderedList' || (command === 'formatBlock' && value === 'blockquote')) && isInListOrQuote()) {
return
}
// 执行格式化命令
document.execCommand(command, false, value)
// 更新工具栏状态以反映当前格式
updateToolbarState()
// 触发输入事件以更新内容
handleInput()
}
@@ -283,39 +302,40 @@ const insertQuote = () => {
}
// 插入待办事项列表
// 创建一个可交互的待办事项元素,包含复选框图标和可编辑内容区域
const insertTodoList = () => {
const selection = window.getSelection()
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0)
// 检查嵌套限制
// 检查嵌套限制,防止在列表或引用中插入待办事项
if (isInListOrQuote()) return
// 创建待办事项容器
const todoContainer = document.createElement('div')
todoContainer.contentEditable = false
todoContainer.contentEditable = false // 容器本身不可编辑
todoContainer.className = 'todo-container'
// 创建图标元素
// 创建图标元素(复选框)
const icon = document.createElement('img')
icon.className = 'todo-icon'
icon.src = '/assets/icons/drawable-xxhdpi/rtf_icon_gtasks.png'
icon.src = '/assets/icons/drawable-xxhdpi/rtf_icon_gtasks.png' // 未完成状态图标
icon.alt = '待办事项'
// 创建内容容器
// 创建内容容器(可编辑区域)
const contentSpan = document.createElement('div')
contentSpan.contentEditable = true
contentSpan.contentEditable = true // 内容区域可编辑
contentSpan.className = 'todo-content'
contentSpan.textContent = '待办事项'
contentSpan.textContent = '待办事项' // 默认文本
// 组装元素
// 组装元素:将图标和内容区域添加到容器中
todoContainer.appendChild(icon)
todoContainer.appendChild(contentSpan)
// 插入到当前光标位置
range.insertNode(todoContainer)
// 添加换行
// 添加换行,确保待办事项下方有空白行
const br = document.createElement('br')
todoContainer.parentNode.insertBefore(br, todoContainer.nextSibling)
@@ -338,19 +358,21 @@ const insertTodoList = () => {
}
}, 0)
// 添加事件监听器到图标
// 添加事件监听器到图标,实现待办事项完成状态切换
icon.addEventListener('click', function () {
// 根据当前状态切换图标
// 根据当前状态切换图标和样式
if (this.src.includes('rtf_icon_gtasks.png')) {
this.src = '/assets/icons/drawable-xxhdpi/rtf_icon_gtasks_light.png'
contentSpan.style.color = 'var(--text-tertiary)'
contentSpan.style.textDecoration = 'line-through'
// 切换到完成状态
this.src = '/assets/icons/drawable-xxhdpi/rtf_icon_gtasks_light.png' // 完成状态图标
contentSpan.style.color = 'var(--text-tertiary)' // 灰色文字
contentSpan.style.textDecoration = 'line-through' // 添加删除线
} else {
this.src = '/assets/icons/drawable-xxhdpi/rtf_icon_gtasks.png'
contentSpan.style.color = 'var(--note-content)'
contentSpan.style.textDecoration = 'none'
// 切换到未完成状态
this.src = '/assets/icons/drawable-xxhdpi/rtf_icon_gtasks.png' // 未完成状态图标
contentSpan.style.color = 'var(--note-content)' // 正常文字颜色
contentSpan.style.textDecoration = 'none' // 移除删除线
}
handleInput()
handleInput() // 触发内容更新
})
// 添加事件监听器到内容区域,监听内容变化和按键事件
@@ -365,8 +387,8 @@ const insertTodoList = () => {
}, 0)
}
contentSpan.addEventListener('input', checkContent)
contentSpan.addEventListener('blur', checkContent)
contentSpan.addEventListener('input', checkContent) // 内容输入时检查
contentSpan.addEventListener('blur', checkContent) // 失去焦点时检查
// 添加焦点事件监听器,确保工具栏在待办事项获得焦点时保持可见
contentSpan.addEventListener('focus', () => {

View File

@@ -3,30 +3,48 @@ import { createRouter, createWebHashHistory } from 'vue-router'
import { createPinia } from 'pinia'
import App from './App.vue'
// Pages
// 导入页面组件
// 便签列表页面
import NoteListPage from './pages/NoteListPage.vue'
// 便签编辑页面(用于新建和编辑便签)
import NoteEditorPage from './pages/NoteEditorPage.vue'
// 文件夹管理页面
import FolderPage from './pages/FolderPage.vue'
// 设置页面
import SettingsPage from './pages/SettingsPage.vue'
// Router
// 配置路由规则
// 定义应用的所有路由路径和对应的组件
const routes = [
// 根路径重定向到便签列表页面
{ path: '/', redirect: '/notes' },
// 便签列表页面路由
{ path: '/notes', component: NoteListPage },
// 编辑便签页面路由带便签ID参数
{ path: '/notes/:id', component: NoteEditorPage, props: true },
// 新建便签页面路由
{ path: '/editor', component: NoteEditorPage },
// 编辑便签页面路由带便签ID参数
{ path: '/editor/:id', component: NoteEditorPage, props: true },
// 文件夹管理页面路由
{ path: '/folders', component: FolderPage },
// 设置页面路由
{ path: '/settings', component: SettingsPage }
]
// 创建路由实例
// 使用Hash模式以支持静态文件部署
const router = createRouter({
history: createWebHashHistory(),
routes
})
// App
// 创建并挂载Vue应用实例
// 配置Pinia状态管理和Vue Router路由
const app = createApp(App)
// 使用Pinia进行状态管理
app.use(createPinia())
// 使用Vue Router进行路由管理
app.use(router)
// 挂载应用到DOM元素
app.mount('#app')

View File

@@ -29,12 +29,14 @@
<script setup>
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useAppStore } from '../stores/useAppStore'
import { search, closeCircle } from 'ionicons/icons'
import FolderManage from '../components/FolderManage.vue'
import Header from '../components/Header.vue'
const store = useAppStore()
const router = useRouter()
// 加载初始数据
onMounted(() => {
@@ -44,7 +46,8 @@ onMounted(() => {
const searchQuery = ref('')
const selectedFolder = ref('all')
// Calculate note count for each folder
// 计算每个文件夹中的便签数量
// 遍历所有自定义文件夹,统计每个文件夹中的便签数量
const foldersWithCount = computed(() => {
return store.folders.map(folder => {
const noteCount = store.notes.filter(note => note.folderId === folder.id).length
@@ -55,13 +58,17 @@ const foldersWithCount = computed(() => {
})
})
// Add default folders at the beginning
// 添加默认文件夹(全部便签、加星便签、回收站)到列表开头
// 计算全部便签数量
const allNotesCount = computed(() => store.notes.length)
// 计算加星便签数量
const starredNotesCount = computed(() => store.notes.filter(note => note.isStarred).length)
// Assuming we have a way to track deleted notes in the future
// 回收站便签数量(暂未实现完整功能)
const trashNotesCount = 0
// 归档便签数量(暂未实现完整功能)
const archiveCount = 0
// 合并默认文件夹和自定义文件夹
const foldersWithAllNotes = computed(() => {
return [
{ id: 'all', name: '全部便签', noteCount: allNotesCount.value, createdAt: new Date() },
@@ -74,26 +81,28 @@ const foldersWithAllNotes = computed(() => {
const handleFolderPress = folderId => {
// 更新选中的文件夹状态
selectedFolder.value = folderId
// 在实际应用中这里会将选中的文件夹传递回NoteListScreen
// 通过导航参数传递选中的文件夹ID
window.location.hash = `#/notes?folder=${folderId}`
// 使用vue-router导航回便签列表页面并传递文件夹参数
router.push(`/notes?folder=${folderId}`)
}
// 处理添加文件夹按钮点击事件
// 在完整实现中,这里会打开文件夹创建对话框
const handleAddFolder = () => {
// In a full implementation, this would open a folder creation dialog
console.log('Add folder pressed')
}
// 处理搜索功能
// 在完整实现中,这里会根据搜索关键词过滤文件夹
const handleSearch = () => {
// In a full implementation, this would filter folders based on searchQuery
console.log('Search for:', searchQuery.value)
}
const handleBackPress = () => {
window.history.back()
router.back()
}
// Filter folders based on search query
// 根据搜索关键词过滤文件夹
// 将文件夹名称转换为小写进行模糊匹配
const filteredFolders = computed(() => {
return foldersWithAllNotes.value.filter(folder => folder.name.toLowerCase().includes(searchQuery.value.toLowerCase()))
})

View File

@@ -40,6 +40,7 @@
<script setup>
import { ref, computed, onMounted, nextTick, watch } from 'vue'
import { useRouter } from 'vue-router'
import { useAppStore } from '../stores/useAppStore'
import Header from '../components/Header.vue'
import RichTextEditor from '../components/RichTextEditor.vue'
@@ -53,31 +54,35 @@ const props = defineProps({
})
// 为了保持向后兼容性我们也支持noteId属性
// 通过计算属性确保无论使用id还是noteId都能正确获取便签ID
const noteId = computed(() => props.id || props.noteId)
const store = useAppStore()
const router = useRouter()
const editorRef = ref(null)
// 设置便签内容的函数
// 用于在编辑器中加载指定便签的内容
const setNoteContent = async (noteId) => {
// 确保store数据已加载
// 确保store数据已加载,如果便签列表为空则先加载数据
if (store.notes.length === 0) {
await store.loadData()
console.log('Store loaded, notes count:', store.notes.length)
}
// 从store中查找指定ID的便签
const note = store.notes.find(n => n.id === noteId)
console.log('Found note:', note)
// 确保编辑器已经初始化
// 确保编辑器已经初始化完成
await nextTick()
console.log('Editor ref:', editorRef.value)
if (note) {
console.log('Setting content:', note.content)
// 无论editorRef是否可用都先设置content的值
// 无论editorRef是否可用都先设置content的值作为备份
content.value = note.content || ''
// 如果editorRef可用直接设置内容
// 如果editorRef可用直接设置编辑器内容
if (editorRef.value) {
editorRef.value.setContent(note.content || '')
}
@@ -113,11 +118,12 @@ watch(() => store.notes, async (newNotes) => {
}
}, { immediate: true })
// Check if we're editing an existing note
// 检查是否正在编辑现有便签
// 如果noteId存在则表示是编辑模式否则是新建模式
const isEditing = !!noteId.value
const existingNote = isEditing ? store.notes.find(n => n.id === noteId.value) : null
// Initialize state with existing note data or empty strings
// 初始化内容状态,如果是编辑现有便签则使用便签内容,否则为空字符串
const content = ref(existingNote?.content || '')
// 当组件挂载时,确保编辑器初始化为空内容(针对新建便签)
@@ -137,6 +143,7 @@ watch(() => store.notes, async (newNotes) => {
const showAlert = ref(false)
// 防抖函数
// 用于避免函数在短时间内被频繁调用,提高性能
const debounce = (func, delay) => {
let timeoutId
return function (...args) {
@@ -146,18 +153,21 @@ const debounce = (func, delay) => {
}
// 防抖处理内容变化
// 延迟300ms更新内容避免用户输入时频繁触发更新
const debouncedHandleContentChange = debounce((newContent) => {
content.value = newContent
console.log('Content updated:', newContent)
}, 300)
// 监听编辑器内容变化
// 当编辑器内容发生变化时调用此函数
const handleContentChange = (newContent) => {
console.log('Editor content changed:', newContent)
debouncedHandleContentChange(newContent)
}
// 计算属性
// 计算属性 - 格式化时间显示
// 如果是编辑现有便签则显示便签的更新时间,否则显示当前时间
const formattedTime = computed(() => {
if (existingNote?.updatedAt) {
return formatNoteEditorDate(existingNote.updatedAt)
@@ -165,8 +175,10 @@ const formattedTime = computed(() => {
return formatNoteEditorDate(new Date())
})
// 计算属性 - 计算字数
// 移除HTML标签后计算纯文本字数
const wordCount = computed(() => {
// 移除HTML标签计算字数
// 使用正则表达式移除HTML标签,只保留纯文本内容
const textContent = content.value.replace(/<[^>]*>/g, '')
return textContent.length || 0
})
@@ -191,7 +203,7 @@ const handleSave = async () => {
}
// Navigate back to the previous screen
window.location.hash = '#/notes'
router.push('/notes')
} catch (error) {
// In a full implementation, show an alert or toast
console.log('Save error: Failed to save note. Please try again.')
@@ -206,7 +218,7 @@ const handleCancel = () => {
if (hasUnsavedChanges) {
showAlert.value = true
} else {
window.location.hash = '#/notes'
router.push('/notes')
}
}
@@ -223,7 +235,7 @@ const handleCreate = async () => {
})
// Navigate back to the previous screen
window.location.hash = '#/notes'
router.push('/notes')
} catch (error) {
// In a full implementation, show an alert or toast
console.log('Create error: Failed to create note. Please try again.')

View File

@@ -1,12 +1,18 @@
<template>
<ion-app>
<div class="container">
<Header :title="headerTitle" :onAction="handleHeaderAction" actionIcon="create" leftType="settings" :onLeftAction="handleSettingsPress" :onFolderToggle="handleFolderToggle" :isFolderExpanded="isFolderExpanded" :onTitlePress="handleFolderToggle" />
<Header
:title="headerTitle"
:onAction="handleHeaderAction"
actionIcon="create"
leftType="settings"
:onLeftAction="handleSettingsPress"
:onFolderToggle="handleFolderToggle"
:isFolderExpanded="isFolderExpanded"
:onTitlePress="handleFolderToggle" />
<!-- 悬浮文件夹列表 - 使用绝对定位实现 -->
<div
v-if="isFolderExpanded"
class="folder-list">
<div v-if="isFolderExpanded" class="folder-list">
<FolderManage
:allCount="allNotesCount"
:starredCount="starredNotesCount"
@@ -60,6 +66,7 @@
<script setup>
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useAppStore } from '../stores/useAppStore'
import NoteItem from '../components/NoteItem.vue'
import Header from '../components/Header.vue'
@@ -68,22 +75,25 @@ import SearchBar from '../components/SearchBar.vue'
import { formatNoteListDate } from '../utils/dateUtils'
const store = useAppStore()
const router = useRouter()
// 加载初始数据
// 页面挂载时加载初始数据
onMounted(() => {
// 检查URL参数是否包含mock数据加载指令
// 检查URL参数是否包含mock数据加载指令,用于开发和演示
const urlParams = new URLSearchParams(window.location.search)
if (urlParams.get('mock') === 'true') {
// 加载预设的模拟数据
store.loadMockData()
} else {
// 从localStorage加载用户数据
store.loadData()
}
})
const searchQuery = ref('')
const sortBy = ref('date') // 'date', 'title', 'starred'
const isFolderExpanded = ref(false)
const currentFolder = ref('all') // 默认文件夹是"全部便签"
const sortBy = ref('date') // 排序方式:'date'(按日期)、'title'(按标题)、'starred'(按星标)
const isFolderExpanded = ref(false) // 文件夹列表是否展开
const currentFolder = ref('all') // 当前选中的文件夹,默认是"全部便签"
const showAlert = ref(false)
const noteToDelete = ref(null)
@@ -104,9 +114,7 @@ const filteredNotes = computed(() => {
return store.notes.filter(note => {
// 先检查搜索条件
const matchesSearch = !lowerCaseQuery ||
note.title.toLowerCase().includes(lowerCaseQuery) ||
note.content.toLowerCase().includes(lowerCaseQuery)
const matchesSearch = !lowerCaseQuery || note.title.toLowerCase().includes(lowerCaseQuery) || note.content.toLowerCase().includes(lowerCaseQuery)
if (!matchesSearch) return false
@@ -128,7 +136,9 @@ const filteredNotes = computed(() => {
})
})
// Filter and sort notes
// 过滤并排序便签列表
// 首先按置顶状态排序,置顶的便签排在前面
// 然后根据sortBy的值进行二次排序
const filteredAndSortedNotes = computed(() => {
return [...filteredNotes.value].sort((a, b) => {
// 置顶的便签排在前面
@@ -138,9 +148,10 @@ const filteredAndSortedNotes = computed(() => {
// 根据排序方式排序
switch (sortBy.value) {
case 'title':
// 按标题字母顺序排序
return a.title.localeCompare(b.title)
case 'starred':
// 加星的便签排在前面
// 按星标状态排序,加星的便签排在前面
return (b.isStarred ? 1 : 0) - (a.isStarred ? 1 : 0)
case 'date':
default:
@@ -170,17 +181,17 @@ const allNotesCount = computed(() => {
})
const handleNotePress = noteId => {
// 导航到编辑页面的逻辑将在路由中处理
window.location.hash = `#/editor/${noteId}`
// 使用vue-router导航到编辑页面
router.push(`/editor/${noteId}`)
}
const handleAddNote = () => {
// 导航到编辑页面的逻辑将在路由中处理
window.location.hash = '#/editor'
// 使用vue-router导航到新建便签页面
router.push('/editor')
}
// 处理Header组件的操作按钮点击事件
const handleHeaderAction = (actionType) => {
const handleHeaderAction = actionType => {
if (actionType === 'create') {
handleAddNote()
}
@@ -236,13 +247,14 @@ const confirmDeleteNote = async () => {
showAlert.value = false
}
// 处理排序方式切换
// 循环切换排序选项:按日期 -> 按标题 -> 按星标 -> 按日期...
const handleSort = () => {
// In a full implementation, this would cycle through sort options
const sortOptions = ['date', 'title', 'starred']
const currentIndex = sortOptions.indexOf(sortBy.value)
const nextIndex = (currentIndex + 1) % sortOptions.length
sortBy.value = sortOptions[nextIndex]
console.log('Sort by:', sortOptions[nextIndex])
console.log('当前排序方式:', sortOptions[nextIndex])
}
const handleAllNotesClick = () => {
@@ -261,13 +273,13 @@ const handleTrashNotesClick = () => {
}
const handleFolderPress = () => {
// 导航到文件夹页面的逻辑将在路由中处理
window.location.hash = '#/folders'
// 使用vue-router导航到文件夹页面
router.push('/folders')
}
const handleSettingsPress = () => {
// 导航到设置页面的逻辑将在路由中处理
window.location.hash = '#/settings'
// 使用vue-router导航到设置页面
router.push('/settings')
}
const handleFolderToggle = () => {
@@ -303,7 +315,8 @@ const handleSearchBlur = () => {
// 可以在这里添加失去焦点时的特殊处理
}
// 防抖搜索函数,避免频繁触发搜索
// 防抖函数,用于避免频繁触发搜索
// 通过延迟执行函数,只在最后一次调用后执行
const debounceSearch = (func, delay) => {
let timeoutId
return function (...args) {
@@ -312,8 +325,8 @@ const debounceSearch = (func, delay) => {
}
}
// 防抖搜索处理
const debouncedHandleSearch = debounceSearch((query) => {
// 防抖搜索处理函数延迟300ms执行搜索
const debouncedHandleSearch = debounceSearch(query => {
handleSearch(query)
}, 300)

View File

@@ -81,61 +81,75 @@
<script setup>
import { computed, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { useAppStore } from '../stores/useAppStore';
import Header from '../components/Header.vue';
const store = useAppStore();
const router = useRouter();
// 加载初始数据
// 页面挂载时加载初始数据
// 从localStorage加载用户设置和便签数据
onMounted(() => {
store.loadData();
});
// 切换云同步设置
// 调用store中的方法更新云同步状态
const toggleCloudSync = () => {
store.toggleCloudSync();
};
// 切换深色模式设置
// 调用store中的方法更新深色模式状态
const toggleDarkMode = () => {
store.toggleDarkMode();
};
// 处理登录云同步按钮点击事件
// 在完整实现中,这里会打开登录界面
const handleLogin = () => {
// In a full implementation, this would open a login screen
console.log('Login to cloud');
};
// 处理隐私政策按钮点击事件
// 在完整实现中,这里会显示隐私政策内容
const handlePrivacyPolicy = () => {
// In a full implementation, this would show the privacy policy
console.log('Privacy policy');
};
// 处理服务条款按钮点击事件
// 在完整实现中,这里会显示服务条款内容
const handleTermsOfService = () => {
// In a full implementation, this would show the terms of service
console.log('Terms of service');
};
// 处理备份便签按钮点击事件
// 在完整实现中,这里会执行便签备份操作
const handleBackup = () => {
// In a full implementation, this would backup notes
console.log('Backup notes');
};
// 处理恢复便签按钮点击事件
// 在完整实现中,这里会执行便签恢复操作
const handleRestore = () => {
// In a full implementation, this would restore notes
console.log('Restore notes');
};
// 处理导出便签按钮点击事件
// 在完整实现中,这里会执行便签导出操作
const handleExport = () => {
// In a full implementation, this would export notes
console.log('Export notes');
};
// 处理导入便签按钮点击事件
// 在完整实现中,这里会执行便签导入操作
const handleImport = () => {
// In a full implementation, this would import notes
console.log('Import notes');
};
const handleBackPress = () => {
window.history.back();
router.back();
};
const settings = computed(() => store.settings);

View File

@@ -2,27 +2,59 @@ import { defineStore } from 'pinia'
import * as storage from '../utils/storage'
import { getCurrentDateTime, getPastDate } from '../utils/dateUtils'
/**
* 应用状态管理Store
* 使用Pinia进行状态管理包含便签、文件夹和设置数据
*/
export const useAppStore = defineStore('app', {
/**
* 状态定义
* 包含应用的核心数据:便签列表、文件夹列表和设置
*/
state: () => ({
notes: [],
folders: [],
settings: { cloudSync: false, darkMode: false },
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: {
// 初始化数据
/**
* 初始化数据
* 从localStorage加载便签、文件夹和设置数据
* 如果没有数据则加载预设的mock数据
* @returns {Promise<void>}
*/
async loadData() {
try {
// 从localStorage加载数据
const loadedNotes = await storage.getNotes()
const loadedFolders = await storage.getFolders()
const loadedSettings = await storage.getSettings()
@@ -31,6 +63,7 @@ export const useAppStore = defineStore('app', {
if (loadedNotes.length === 0 && loadedFolders.length === 0) {
this.loadMockData()
} else {
// 否则使用加载的数据
this.notes = loadedNotes
this.folders = loadedFolders
this.settings = loadedSettings
@@ -40,7 +73,11 @@ export const useAppStore = defineStore('app', {
}
},
// 加载mock数据
/**
* 加载预设的mock数据
* 用于开发和演示目的,提供示例便签、文件夹和设置
* @returns {Promise<void>}
*/
async loadMockData() {
// Mock notes - 使用固定的日期值以避免每次运行时变化
const fixedCurrentDate = '2025-10-12T10:00:00.000Z';
@@ -50,6 +87,7 @@ export const useAppStore = defineStore('app', {
const fixedFourDaysAgo = '2025-10-08T10:00:00.000Z';
const fixedFiveDaysAgo = '2025-10-07T10:00:00.000Z';
// 预设的便签示例数据
const mockNotes = [
{
id: '1',
@@ -58,10 +96,10 @@ export const useAppStore = defineStore('app', {
createdAt: fixedCurrentDate,
updatedAt: fixedCurrentDate,
folderId: null,
isStarred: true,
isTop: true,
hasImage: false,
isDeleted: false,
isStarred: true, // 加星便签
isTop: true, // 置顶便签
hasImage: false, // 不包含图片
isDeleted: false, // 未删除
deletedAt: null,
},
{
@@ -71,10 +109,10 @@ export const useAppStore = defineStore('app', {
createdAt: fixedYesterday,
updatedAt: fixedYesterday,
folderId: null,
isStarred: true,
isTop: false,
hasImage: true,
isDeleted: false,
isStarred: true, // 加星便签
isTop: false, // 非置顶
hasImage: true, // 包含图片
isDeleted: false, // 未删除
deletedAt: null,
},
{
@@ -84,10 +122,10 @@ export const useAppStore = defineStore('app', {
createdAt: fixedTwoDaysAgo,
updatedAt: fixedTwoDaysAgo,
folderId: null,
isStarred: false,
isTop: false,
hasImage: false,
isDeleted: false,
isStarred: false, // 非加星
isTop: false, // 非置顶
hasImage: false, // 不包含图片
isDeleted: false, // 未删除
deletedAt: null,
},
{
@@ -97,10 +135,10 @@ export const useAppStore = defineStore('app', {
createdAt: fixedThreeDaysAgo,
updatedAt: fixedThreeDaysAgo,
folderId: null,
isStarred: false,
isTop: false,
hasImage: false,
isDeleted: false,
isStarred: false, // 非加星
isTop: false, // 非置顶
hasImage: false, // 不包含图片
isDeleted: false, // 未删除
deletedAt: null,
},
{
@@ -110,10 +148,10 @@ export const useAppStore = defineStore('app', {
createdAt: fixedFourDaysAgo,
updatedAt: fixedFourDaysAgo,
folderId: null,
isStarred: false,
isTop: false,
hasImage: false,
isDeleted: false,
isStarred: false, // 非加星
isTop: false, // 非置顶
hasImage: false, // 不包含图片
isDeleted: false, // 未删除
deletedAt: null,
},
{
@@ -123,15 +161,16 @@ export const useAppStore = defineStore('app', {
createdAt: fixedFiveDaysAgo,
updatedAt: fixedFiveDaysAgo,
folderId: null,
isStarred: false,
isTop: false,
hasImage: false,
isDeleted: true,
isStarred: false, // 非加星
isTop: false, // 非置顶
hasImage: false, // 不包含图片
isDeleted: true, // 已删除
deletedAt: fixedYesterday,
},
]
// Mock folders - 使用固定的日期值
// 预设的文件夹示例数据
const mockFolders = [
{
id: 'folder1',
@@ -151,11 +190,13 @@ export const useAppStore = defineStore('app', {
]
// Mock settings
// 预设的设置示例数据
const mockSettings = {
cloudSync: false,
darkMode: false,
cloudSync: false, // 云同步关闭
darkMode: false, // 深色模式关闭
}
// 更新store状态
this.notes = mockNotes
this.folders = mockFolders
this.settings = mockSettings
@@ -166,7 +207,10 @@ export const useAppStore = defineStore('app', {
await storage.saveSettings(mockSettings)
},
// 保存notes到localStorage
/**
* 保存便签数据到localStorage
* @returns {Promise<void>}
*/
async saveNotes() {
try {
await storage.saveNotes(this.notes)
@@ -175,7 +219,10 @@ export const useAppStore = defineStore('app', {
}
},
// 保存folders到localStorage
/**
* 保存文件夹数据到localStorage
* @returns {Promise<void>}
*/
async saveFolders() {
try {
await storage.saveFolders(this.folders)
@@ -184,7 +231,10 @@ export const useAppStore = defineStore('app', {
}
},
// 保存settings到localStorage
/**
* 保存设置数据到localStorage
* @returns {Promise<void>}
*/
async saveSettings() {
try {
await storage.saveSettings(this.settings)
@@ -193,11 +243,20 @@ export const useAppStore = defineStore('app', {
}
},
// Note functions
/**
* 便签操作函数
*/
/**
* 添加新便签
* @param {Object} note - 便签对象
* @returns {Promise<Object>} 新创建的便签对象
*/
async addNote(note) {
try {
const newNote = await storage.addNote(note)
this.notes.push(newNote)
return newNote
} catch (error) {
console.error('Error adding note:', error)
@@ -205,6 +264,12 @@ export const useAppStore = defineStore('app', {
}
},
/**
* 更新便签
* @param {string} id - 便签ID
* @param {Object} updates - 要更新的属性对象
* @returns {Promise<Object>} 更新后的便签对象
*/
async updateNote(id, updates) {
try {
const updatedNote = await storage.updateNote(id, updates)
@@ -221,6 +286,11 @@ export const useAppStore = defineStore('app', {
}
},
/**
* 删除便签
* @param {string} id - 要删除的便签ID
* @returns {Promise<boolean>} 删除成功返回true失败返回false
*/
async deleteNote(id) {
try {
const result = await storage.deleteNote(id)
@@ -234,7 +304,12 @@ export const useAppStore = defineStore('app', {
}
},
// 将便签移至回收站
/**
* 将便签移至回收站
* 将便签标记为已删除状态,并记录删除时间
* @param {string} id - 便签ID
* @returns {Promise<Object>} 更新后的便签对象
*/
async moveToTrash(id) {
try {
const updatedNote = await storage.updateNote(id, { isDeleted: true, deletedAt: getCurrentDateTime() })
@@ -251,7 +326,12 @@ export const useAppStore = defineStore('app', {
}
},
// 永久删除便签
/**
* 永久删除便签
* 从便签列表中彻底移除便签
* @param {string} id - 便签ID
* @returns {Promise<boolean>} 删除成功返回true失败返回false
*/
async permanentlyDeleteNote(id) {
try {
const result = await storage.deleteNote(id)
@@ -265,7 +345,15 @@ export const useAppStore = defineStore('app', {
}
},
// Folder functions
/**
* 文件夹操作函数
*/
/**
* 添加新文件夹
* @param {Object} folder - 文件夹对象
* @returns {Promise<Object>} 新创建的文件夹对象
*/
async addFolder(folder) {
try {
const newFolder = await storage.addFolder(folder)
@@ -277,7 +365,15 @@ export const useAppStore = defineStore('app', {
}
},
// Settings functions
/**
* 设置操作函数
*/
/**
* 更新设置
* @param {Object} newSettings - 新的设置对象
* @returns {Promise<void>}
*/
async updateSettings(newSettings) {
try {
const updatedSettings = { ...this.settings, ...newSettings }
@@ -289,12 +385,20 @@ export const useAppStore = defineStore('app', {
}
},
// 切换云同步设置
/**
* 切换云同步设置
* 开启或关闭云同步功能
* @returns {Promise<void>}
*/
async toggleCloudSync() {
await this.updateSettings({ cloudSync: !this.settings.cloudSync })
},
// 切换深色模式设置
/**
* 切换深色模式设置
* 开启或关闭深色模式
* @returns {Promise<void>}
*/
async toggleDarkMode() {
await this.updateSettings({ darkMode: !this.settings.darkMode })
},

View File

@@ -1,11 +1,19 @@
import { getCurrentDateTime, getTimestamp } from './dateUtils'
// Storage keys
const NOTES_KEY = 'notes';
const FOLDERS_KEY = 'folders';
const SETTINGS_KEY = 'settings';
// 本地存储键名常量
// 用于在localStorage中标识不同类型的数据
const NOTES_KEY = 'notes'; // 便签数据键名
const FOLDERS_KEY = 'folders'; // 文件夹数据键名
const SETTINGS_KEY = 'settings'; // 设置数据键名
// Notes functions
// 便签操作函数
// 提供便签的增删改查功能
/**
* 获取所有便签数据
* 从localStorage中读取便签数据并解析为JavaScript对象
* @returns {Promise<Array>} 便签数组,如果读取失败则返回空数组
*/
export const getNotes = async () => {
try {
const notesJson = localStorage.getItem(NOTES_KEY);
@@ -16,6 +24,12 @@ export const getNotes = async () => {
}
};
/**
* 保存便签数据
* 将便签数组转换为JSON字符串并保存到localStorage
* @param {Array} notes - 便签数组
* @returns {Promise<void>}
*/
export const saveNotes = async (notes) => {
try {
localStorage.setItem(NOTES_KEY, JSON.stringify(notes));
@@ -24,19 +38,27 @@ export const saveNotes = async (notes) => {
}
};
/**
* 添加新便签
* 创建一个新的便签对象并添加到便签列表中
* @param {Object} note - 便签对象,包含便签内容和其他属性
* @returns {Promise<Object>} 新创建的便签对象
*/
export const addNote = async (note) => {
// 创建新的便签对象,添加必要的属性
const newNote = {
...note,
id: getTimestamp().toString(),
createdAt: getCurrentDateTime(),
updatedAt: getCurrentDateTime(),
isStarred: note.isStarred || false,
isTop: note.isTop || false,
hasImage: note.hasImage || false,
isDeleted: note.isDeleted || false,
deletedAt: note.deletedAt || null
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);
@@ -44,35 +66,62 @@ export const addNote = async (note) => {
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(),
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;
};
// Folders functions
// 文件夹操作函数
// 提供文件夹的增删改查功能
/**
* 获取所有文件夹数据
* 从localStorage中读取文件夹数据并解析为JavaScript对象
* @returns {Promise<Array>} 文件夹数组,如果读取失败则返回空数组
*/
export const getFolders = async () => {
try {
const foldersJson = localStorage.getItem(FOLDERS_KEY);
@@ -83,6 +132,12 @@ export const getFolders = async () => {
}
};
/**
* 保存文件夹数据
* 将文件夹数组转换为JSON字符串并保存到localStorage
* @param {Array} folders - 文件夹数组
* @returns {Promise<void>}
*/
export const saveFolders = async (folders) => {
try {
localStorage.setItem(FOLDERS_KEY, JSON.stringify(folders));
@@ -91,13 +146,21 @@ export const saveFolders = async (folders) => {
}
};
/**
* 添加新文件夹
* 创建一个新的文件夹对象并添加到文件夹列表中
* @param {Object} folder - 文件夹对象,包含文件夹名称等属性
* @returns {Promise<Object>} 新创建的文件夹对象
*/
export const addFolder = async (folder) => {
// 创建新的文件夹对象,添加必要的属性
const newFolder = {
...folder,
id: getTimestamp().toString(),
createdAt: getCurrentDateTime(),
id: getTimestamp().toString(), // 使用时间戳生成唯一ID
createdAt: getCurrentDateTime(), // 创建时间
};
// 获取现有文件夹列表,添加新文件夹并保存
const folders = await getFolders();
folders.push(newFolder);
await saveFolders(folders);
@@ -105,17 +168,32 @@ export const addFolder = async (folder) => {
return newFolder;
};
// Settings functions
// 设置操作函数
// 提供应用设置的读取和保存功能
/**
* 获取应用设置
* 从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));