You've already forked SmartisanNote.Remake
优化 头部便签管理点击区域;
补充注释;
This commit is contained in:
@@ -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()))
|
||||
})
|
||||
|
||||
@@ -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.')
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user