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

补充注释;
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

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)
@@ -101,13 +111,11 @@ const trashNotesCount = computed(() => {
const filteredNotes = computed(() => {
// 预处理搜索查询,提高性能
const lowerCaseQuery = searchQuery.value.toLowerCase().trim()
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 = () => {
@@ -278,7 +290,7 @@ const handleFolderToggle = () => {
const handleSearch = query => {
// 搜索功能已在computed属性filteredAndSortedNotes中实现
console.log('Search for:', query)
// 可以在这里添加搜索统计或其它功能
if (query && query.length > 0) {
console.log(`Found ${filteredAndSortedNotes.value.length} matching notes`)
@@ -288,7 +300,7 @@ const handleSearch = query => {
const handleClearSearch = () => {
// 清除搜索已在v-model中处理
console.log('Search cleared')
// 清除搜索后可以重置一些状态
setSearchQuery('')
}
@@ -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)
@@ -382,4 +395,4 @@ const notes = computed(() => store.notes)
.note-item {
margin: 0.4rem 0;
}
</style>
</style>

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);