新增 文件夹管理功能:实现文件夹选择模式、添加、编辑和删除功能

This commit is contained in:
yuantao
2025-10-23 18:35:23 +08:00
parent ff4adcd949
commit 2eb7eb8618
6 changed files with 1880 additions and 1024 deletions

View File

@@ -0,0 +1,249 @@
<template>
<div v-if="visible" class="pd-mask" @click="handleMaskClick">
<div class="pd-confirm" @click.stop>
<div class="pd-blur"></div>
<div class="pd-plan"></div>
<h2 class="pd-title" v-if="title">{{ title }}</h2>
<p class="pd-message">{{ message }}</p>
<div class="pd-buttons">
<button v-if="showConfirm" class="pd-button pd-confirm-btn" @click="handleConfirm">
{{ confirmText }}
</button>
<button v-if="showCancel" class="pd-button pd-cancel" @click="handleCancel">
{{ cancelText }}
</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, watch, onMounted, onUnmounted } from 'vue'
const props = defineProps({
title: {
type: String,
default: '',
},
message: {
type: String,
default: '',
},
confirmText: {
type: String,
default: '确认',
},
cancelText: {
type: String,
default: '取消',
},
showConfirm: {
type: Boolean,
default: true,
},
showCancel: {
type: Boolean,
default: true,
},
maskClosable: {
type: Boolean,
default: true,
},
})
const emit = defineEmits(['confirm', 'cancel', 'update:visible'])
const visible = defineModel('visible', { type: Boolean, default: false })
// Promise 控制变量
let resolvePromise, rejectPromise
// 返回 Promise 的方法,模拟原生 confirm 行为
const show = (options = {}) => {
// 更新 props 值
Object.assign(props, options)
// 显示对话框
visible.value = true
emit('update:visible', true)
// 返回 Promise
return new Promise((resolve, reject) => {
resolvePromise = resolve
rejectPromise = reject
})
}
const handleConfirm = () => {
emit('confirm')
visible.value = false
emit('update:visible', false)
// 解决 Promise
if (resolvePromise) {
resolvePromise()
resolvePromise = null
rejectPromise = null
}
}
const handleCancel = () => {
emit('cancel')
visible.value = false
emit('update:visible', false)
// 拒绝 Promise
if (rejectPromise) {
rejectPromise()
resolvePromise = null
rejectPromise = null
}
}
const handleMaskClick = () => {
if (props.maskClosable) {
handleCancel() // 点击遮罩相当于取消
}
}
// 添加/移除 body 滚动锁定
const lockBodyScroll = () => {
document.body.style.overflow = 'hidden'
}
const unlockBodyScroll = () => {
document.body.style.overflow = ''
}
onMounted(() => {
if (visible.value) {
lockBodyScroll()
}
})
onUnmounted(() => {
unlockBodyScroll()
})
// 监听 visible 变化
watch(visible, newVal => {
if (newVal) {
lockBodyScroll()
} else {
unlockBodyScroll()
}
})
// 暴露 show 方法给父组件使用
defineExpose({
show,
})
</script>
<style scoped>
.pd-mask {
position: fixed;
inset: 0;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 999999999;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background: inherit;
background: var(--confirmBg, inherit);
}
.pd-confirm {
background: inherit;
background: var(--confirmTheme, #fff);
text-align: center;
color: var(--confirmColor, #636363);
font-size: var(--confirmFontSize, 1rem);
max-width: 75vw;
min-width: 20em;
box-shadow: 0px 35px 35px -10px rgba(0, 0, 0, 0.33);
border-radius: 10px;
position: relative;
white-space: break-spaces;
word-break: break-all;
overflow: hidden;
}
.pd-blur,
.pd-plan {
position: absolute;
inset: 0;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.pd-plan {
background: inherit;
background: rgba(255, 255, 255, 0.66);
filter: blur(10px) saturate(2);
}
.pd-title {
position: relative;
font-size: 1.3em;
min-height: 1em;
background: var(--confirmBtnBg, #fafafa);
color: var(--confirmBtnColor, #636363);
margin: 0;
padding: 0.5em 0;
}
.pd-message {
position: relative;
border-top: 1px solid var(--confirmBtnBorder, #f1f1f1);
width: 100%;
max-height: 70vh;
border-bottom: 1px solid var(--confirmBtnBorder, #f1f1f1);
margin: 0 auto;
box-sizing: border-box;
padding: 2em 10%;
line-height: 1.4;
overflow: auto;
}
.pd-buttons {
display: flex;
}
.pd-button {
position: relative;
width: 100%;
font-size: 1em;
appearance: none;
background: var(--confirmBtnBg, #fafafa);
color: var(--confirmBtnColor, #636363);
border: none;
border-right: 1px solid var(--confirmBtnBorder, #f1f1f1);
padding: 1em 0;
cursor: pointer;
outline: none;
}
/* 当同时显示确认和取消按钮时每个按钮占50%宽度 */
.pd-button:first-child:nth-last-child(2),
.pd-button:first-child:nth-last-child(2) ~ .pd-button {
width: 50%;
}
/* 当只显示一个按钮时该按钮占100%宽度 */
.pd-button:first-child:nth-last-child(1) {
width: 100%;
border-right: none;
}
/* 只有最后一个按钮没有右边框 */
.pd-button:last-child {
border-right: none;
}
</style>

View File

@@ -1,9 +1,21 @@
<template> <template>
<div @click="onPress" class="code-fun-flex-row code-fun-items-center code-fun-relative folder-item"> <div @click="onPress" class="code-fun-flex-row code-fun-items-center code-fun-relative code-fun-justify-between folder-item">
<img class="folder-icon" :src="iconSrc" /> <div class="code-fun-flex-row code-fun-items-center">
<!-- 文件夹图标或复选框 -->
<div v-if="isSelectionMode && showDeleteButton" class="folder-checkbox" @click.stop="onCheckboxClick">
<img :src="isChecked ? '/assets/icons/drawable-xxhdpi/check_box_on.png' : '/assets/icons/drawable-xxhdpi/check_box_off.png'" class="checkbox-icon" />
</div>
<img v-else class="folder-icon" :src="iconSrc" />
<span class="folder-name">{{ name }}</span> <span class="folder-name">{{ name }}</span>
<span class="folder-count">{{ noteCount }}</span> <span class="folder-count">{{ noteCount }}</span>
</div> </div>
<!-- 编辑按钮仅在选择模式下对自定义文件夹显示 -->
<div v-if="showDeleteButton && isSelectionMode" class="folder-actions">
<img class="edit-icon" src="/assets/icons/drawable-xxhdpi/icon_folder_rename.png" @click.stop="onEdit" />
</div>
</div>
</template> </template>
<script setup> <script setup>
@@ -14,41 +26,83 @@ const props = defineProps({
type: String, type: String,
required: true, required: true,
}, },
name: { name: {
type: String, type: String,
required: true, required: true,
}, },
noteCount: { noteCount: {
type: Number, type: Number,
required: true, required: true,
}, },
onPress: { onPress: {
type: Function, type: Function,
required: true, required: true,
}, },
onEdit: {
type: Function,
default: null,
},
onDelete: {
type: Function,
default: null,
},
isSelected: { isSelected: {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
isSelectionMode: {
type: Boolean,
default: false,
},
isChecked: {
type: Boolean,
default: false,
},
onCheckboxClick: {
type: Function,
default: null,
},
}) })
const iconSrc = computed(() => { const iconSrc = computed(() => {
switch (props.id) { switch (props.id) {
case 'all': case 'all':
return 'assets/icons/drawable-xxhdpi/icon_folder_all.png' return 'assets/icons/drawable-xxhdpi/icon_folder_all.png'
case 'starred': case 'starred':
return 'assets/icons/drawable-xxhdpi/icon_folder_favorite.png' return 'assets/icons/drawable-xxhdpi/icon_folder_favorite.png'
case 'trash': case 'trash':
return 'assets/icons/drawable-xxhdpi/icon_folder_trash.png' return 'assets/icons/drawable-xxhdpi/icon_folder_trash.png'
case 'archive': case 'archive':
return 'assets/icons/drawable-xxhdpi/icon_folder_document.png' return 'assets/icons/drawable-xxhdpi/icon_folder_document.png'
default: default:
return 'assets/icons/drawable-xxhdpi/icon_folder_document.png' return 'assets/icons/drawable-xxhdpi/icon_folder_document.png'
} }
}) })
// 仅对自定义文件夹显示删除按钮
const showDeleteButton = computed(() => {
return !['all', 'starred', 'trash', 'archive'].includes(props.id)
})
</script> </script>
<style scoped> <style lang="less" scoped>
.folder-item { .folder-item {
padding: 0.3rem 0; padding: 0.3rem 0;
background-color: #00000000; background-color: #00000000;
@@ -74,4 +128,41 @@ const iconSrc = computed(() => {
margin-top: 0.2rem; margin-top: 0.2rem;
color: #b8b8b8; color: #b8b8b8;
} }
.folder-actions {
display: flex;
margin-left: auto;
margin-right: 0.5rem;
}
.edit-icon,
.delete-icon {
width: 1.5rem;
height: 1.5rem;
margin: 0 0.2rem;
opacity: 0.5;
}
.edit-icon:hover,
.delete-icon:hover {
opacity: 1;
}
.folder-checkbox {
display: flex;
align-items: center;
justify-content: center;
width: 1.8rem;
height: 1.8rem;
flex-shrink: 0;
margin-inline: 0.3rem;
}
.checkbox-icon {
width: 70%;
height: 70%;
}
</style> </style>

View File

@@ -1,23 +1,100 @@
<template> <template>
<div class="code-fun-flex-col page"> <div class="code-fun-flex-col page">
<!-- 全部便签文件夹项 --> <!-- 全部便签文件夹项 -->
<FolderItem id="all" name="全部便签" :noteCount="allCount" :isSelected="selectedFolder === 'all'" :onPress="handleAllClick" /> <FolderItem id="all" name="全部便签" :noteCount="allCount" :isSelected="selectedFolder === 'all'" :onPress="handleAllClick" />
<!-- 加星便签文件夹项 --> <!-- 加星便签文件夹项 -->
<FolderItem id="starred" name="加星便签" :noteCount="starredCount" :isSelected="selectedFolder === 'starred'" :onPress="handleStarredClick" /> <FolderItem id="starred" name="加星便签" :noteCount="starredCount" :isSelected="selectedFolder === 'starred'" :onPress="handleStarredClick" />
<!-- 回收站文件夹项 --> <!-- 回收站文件夹项 -->
<FolderItem id="trash" name="回收站" :noteCount="trashCount" :isSelected="selectedFolder === 'trash'" :onPress="handleTrashClick" /> <FolderItem id="trash" name="回收站" :noteCount="trashCount" :isSelected="selectedFolder === 'trash'" :onPress="handleTrashClick" />
<!-- 同步信息区域 --> <!-- 自定义文件夹项 -->
<FolderItem
v-for="folder in customFolders"
:key="folder.id"
:id="folder.id"
:name="folder.name"
:noteCount="getFolderNoteCount(folder.id)"
:isSelected="selectedFolder === folder.id"
:onPress="() => handleFolderClick(folder.id)"
:onEdit="() => handleEditFolder(folder.id)"
:onDelete="() => handleDeleteFolder(folder.id)"
:isSelectionMode="isSelectionMode"
:isChecked="selectedFolders.includes(folder.id)"
:onCheckboxClick="() => toggleFolderSelection(folder.id)" />
<div class="code-fun-flex-col code-fun-justify-start section_7"> <div class="code-fun-flex-col code-fun-justify-start section_7">
<div class="code-fun-flex-row code-fun-justify-between code-fun-items-center section_8"> <div class="code-fun-flex-row code-fun-justify-between code-fun-items-center section_8">
<div class="code-fun-flex-row code-fun-items-center"> <div class="code-fun-flex-row code-fun-items-center">
<img class="code-fun-shrink-0 image_7" :src="`assets/icons/drawable-xxhdpi/btn_edit_folder.png`" /> <!-- 切换文件夹选择模式按钮 -->
<img class="code-fun-shrink-0 image_7" :src="isSelectionMode ? 'assets/icons/drawable-xxhdpi/btn_back_black.png' : 'assets/icons/drawable-xxhdpi/btn_edit_folder.png'" @click="toggleSelectionMode" />
<!-- 同步信息区域 -->
<span class="text_11 code-fun-ml-10">上次同步:{{ lastSyncTime }}</span> <span class="text_11 code-fun-ml-10">上次同步:{{ lastSyncTime }}</span>
</div> </div>
<div class="code-fun-flex-row code-fun-items-center"> <div class="code-fun-flex-row code-fun-items-center">
<img class="image_8 code-fun-ml-12" :src="`assets/icons/drawable-xxhdpi/btn_add_folder.png`" @click="handleAddFolder" /> <!-- 新建文件夹按钮 / 删除按钮 -->
<img
class="image_8 code-fun-ml-12"
:src="isSelectionMode ? 'assets/icons/drawable-xxhdpi/icon_folder_trash.png' : 'assets/icons/drawable-xxhdpi/btn_add_folder.png'"
@click="isSelectionMode ? handleDeleteSelectedFolders() : handleAddFolder" />
</div>
</div>
</div>
<!-- 添加文件夹的模态框 -->
<div v-if="showAddFolderModal" class="modal-overlay" @click="showAddFolderModal = false">
<div class="modal-content" @click.stop>
<h3>添加文件夹</h3>
<input v-model="newFolderName" placeholder="请输入文件夹名称" class="folder-input" @keyup.enter="confirmAddFolder" ref="folderInput" />
<div class="modal-actions">
<button @click="showAddFolderModal = false" class="cancel-btn">取消</button>
<button @click="confirmAddFolder" class="confirm-btn">确定</button>
</div>
</div>
</div>
<!-- 重命名文件夹的模态框 -->
<div v-if="showRenameFolderModal" class="modal-overlay" @click="showRenameFolderModal = false">
<div class="modal-content" @click.stop>
<h3>重命名文件夹</h3>
<input v-model="renameFolderName" placeholder="请输入文件夹名称" class="folder-input" @keyup.enter="confirmRenameFolder" ref="folderInput" />
<div class="modal-actions">
<button @click="showRenameFolderModal = false" class="cancel-btn">取消</button>
<button @click="confirmRenameFolder" class="confirm-btn">确定</button>
</div>
</div>
</div>
<!-- 确认删除文件夹的模态框 -->
<div v-if="showDeleteConfirmModal" class="modal-overlay" @click="showDeleteConfirmModal = false">
<div class="modal-content" @click.stop>
<h3>删除文件夹</h3>
<p>确定要删除选中的 {{ selectedFolders.length }} 个文件夹吗文件夹中的便签将移至"全部便签"</p>
<div class="modal-actions">
<button @click="showDeleteConfirmModal = false" class="cancel-btn">取消</button>
<button @click="confirmDeleteSelectedFolders" class="confirm-btn">确定</button>
</div> </div>
</div> </div>
</div> </div>
@@ -25,85 +102,347 @@
</template> </template>
<script setup> <script setup>
import { computed } from 'vue' import { ref, computed, nextTick } from 'vue'
import { useAppStore } from '../stores/useAppStore'
import FolderItem from './FolderItem.vue' import FolderItem from './FolderItem.vue'
const store = useAppStore()
const props = defineProps({ const props = defineProps({
allCount: { allCount: {
type: Number, type: Number,
default: 0, default: 0,
}, },
starredCount: { starredCount: {
type: Number, type: Number,
default: 0, default: 0,
}, },
trashCount: { trashCount: {
type: Number, type: Number,
default: 0, default: 0,
}, },
archiveCount: { archiveCount: {
type: Number, type: Number,
default: 0, default: 0,
}, },
selectedFolder: { selectedFolder: {
type: String, type: String,
default: '', default: '',
}, },
lastSyncTime: { lastSyncTime: {
type: String, type: String,
default: '10/10上午9:28', default: '10/10上午9:28',
}, },
onAllClick: { onAllClick: {
type: Function, type: Function,
default: null, default: null,
}, },
onStarredClick: { onStarredClick: {
type: Function, type: Function,
default: null, default: null,
}, },
onTrashClick: { onTrashClick: {
type: Function, type: Function,
default: null, default: null,
}, },
onArchiveClick: { onArchiveClick: {
type: Function, type: Function,
default: null, default: null,
}, },
onAddFolder: { onAddFolder: {
type: Function, type: Function,
default: null,
},
onFolderClick: {
type: Function,
default: null, default: null,
}, },
}) })
// 添加文件夹相关状态
const showAddFolderModal = ref(false)
const newFolderName = ref('')
const folderInput = ref(null)
// 重命名文件夹相关状态
const showRenameFolderModal = ref(false)
const renameFolderId = ref(null)
const renameFolderName = ref('')
// 选择模式相关状态
const isSelectionMode = ref(false)
const selectedFolders = ref([])
const showDeleteConfirmModal = ref(false)
// 计算自定义文件夹(排除系统文件夹)
const customFolders = computed(() => {
return store.folders.filter(folder => !['all', 'starred', 'trash', 'archive'].includes(folder.id))
})
// 获取文件夹中的便签数量
const getFolderNoteCount = folderId => {
return store.notes.filter(note => note.folderId === folderId && !note.isDeleted).length
}
// 切换选择模式
const toggleSelectionMode = () => {
isSelectionMode.value = !isSelectionMode.value
// 退出选择模式时清空选中项
if (!isSelectionMode.value) {
selectedFolders.value = []
}
}
// 切换文件夹选中状态
const toggleFolderSelection = folderId => {
if (!isSelectionMode.value) return
const index = selectedFolders.value.indexOf(folderId)
if (index > -1) {
// 已选中,取消选中
selectedFolders.value.splice(index, 1)
} else {
// 未选中,添加选中
selectedFolders.value.push(folderId)
}
}
// 处理文件夹点击
const handleFolderClick = folderId => {
if (isSelectionMode.value) {
toggleFolderSelection(folderId)
} else {
if (props.onFolderClick) {
props.onFolderClick(folderId)
}
}
}
// 处理删除选中的文件夹
const handleDeleteSelectedFolders = () => {
if (selectedFolders.value.length === 0) return
showDeleteConfirmModal.value = true
}
// 确认删除选中的文件夹
const confirmDeleteSelectedFolders = async () => {
try {
// 删除选中的文件夹
for (const folderId of selectedFolders.value) {
// 跳过系统文件夹
if (['all', 'starred', 'trash', 'archive'].includes(folderId)) continue
await store.deleteFolder(folderId)
}
// 清空选中项并退出选择模式
selectedFolders.value = []
isSelectionMode.value = false
showDeleteConfirmModal.value = false
} catch (error) {
console.error('删除文件夹失败:', error)
}
}
// 处理删除文件夹
const handleDeleteFolder = folderId => {
// 阻止事件冒泡到父元素
event.stopPropagation()
// 确认删除
if (confirm(`确定要删除文件夹 "${getFolderName(folderId)}" 吗?文件夹中的便签将移至"全部便签"。`)) {
try {
store.deleteFolder(folderId)
} catch (error) {
console.error('删除文件夹失败:', error)
}
}
}
// 处理编辑文件夹
const handleEditFolder = folderId => {
// 阻止事件冒泡到父元素
event.stopPropagation()
const folder = store.folders.find(f => f.id === folderId)
if (folder) {
renameFolderId.value = folderId
renameFolderName.value = folder.name
showRenameFolderModal.value = true
// 在下次DOM更新后聚焦输入框
nextTick(() => {
if (folderInput.value) {
folderInput.value.focus()
}
})
}
}
// 确认重命名文件夹
const confirmRenameFolder = async () => {
if (renameFolderName.value.trim() && renameFolderId.value) {
try {
await store.updateFolder(renameFolderId.value, { name: renameFolderName.value.trim() })
showRenameFolderModal.value = false
renameFolderId.value = null
renameFolderName.value = ''
} catch (error) {
console.error('重命名文件夹失败:', error)
}
}
}
// 获取文件夹名称
const getFolderName = folderId => {
const folder = store.folders.find(f => f.id === folderId)
return folder ? folder.name : ''
}
const handleAllClick = () => { const handleAllClick = () => {
if (isSelectionMode.value) {
toggleFolderSelection('all')
} else {
if (props.onAllClick) { if (props.onAllClick) {
props.onAllClick() props.onAllClick()
} }
} }
}
const handleStarredClick = () => { const handleStarredClick = () => {
if (isSelectionMode.value) {
toggleFolderSelection('starred')
} else {
if (props.onStarredClick) { if (props.onStarredClick) {
props.onStarredClick() props.onStarredClick()
} }
} }
}
const handleTrashClick = () => { const handleTrashClick = () => {
if (isSelectionMode.value) {
toggleFolderSelection('trash')
} else {
if (props.onTrashClick) { if (props.onTrashClick) {
props.onTrashClick() props.onTrashClick()
} }
} }
}
const handleArchiveClick = () => { const handleArchiveClick = () => {
if (isSelectionMode.value) {
toggleFolderSelection('archive')
} else {
if (props.onArchiveClick) { if (props.onArchiveClick) {
props.onArchiveClick() props.onArchiveClick()
} }
} }
}
const handleAddFolder = event => { const handleAddFolder = event => {
// 阻止事件冒泡到父元素 // 阻止事件冒泡到父元素
event.stopPropagation() event.stopPropagation()
if (props.onAddFolder) {
props.onAddFolder() showAddFolderModal.value = true
newFolderName.value = ''
// 在下次DOM更新后聚焦输入框
nextTick(() => {
if (folderInput.value) {
folderInput.value.focus()
}
})
}
const confirmAddFolder = async () => {
if (newFolderName.value.trim()) {
try {
const newFolder = {
name: newFolderName.value.trim(),
id: `folder_${Date.now()}`, // 生成唯一ID
createdAt: new Date().toISOString(),
}
await store.addFolder(newFolder)
showAddFolderModal.value = false
newFolderName.value = ''
} catch (error) {
console.error('添加文件夹失败:', error)
}
} }
} }
</script> </script>
@@ -120,9 +459,10 @@ const handleAddFolder = event => {
.image_7, .image_7,
.image_8 { .image_8 {
border-radius: 0.63rem; border-radius: 0.63rem;
width: 2rem; width: 2.2rem;
height: 2rem; height: 1.75rem;
object-fit: contain; object-fit: contain;
background: url(/assets/icons/drawable-xxhdpi/folder_bottom_button_normal.9.png), #fdfbfb;
} }
.text_11 { .text_11 {
color: #cacaca; color: #cacaca;
@@ -132,4 +472,83 @@ const handleAddFolder = event => {
} }
} }
} }
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1001;
}
.modal-content {
background: white;
border-radius: 0.5rem;
padding: 1.5rem;
text-align: center;
width: 80%;
max-width: 300px;
h3 {
margin-top: 0;
margin-bottom: 1rem;
color: #333;
}
.folder-input {
width: 100%;
padding: 0.8rem;
border: 1px solid #ddd;
border-radius: 0.3rem;
font-size: 1rem;
margin-bottom: 1.5rem;
box-sizing: border-box;
}
.modal-actions {
display: flex;
justify-content: space-between;
button {
flex: 1;
padding: 0.8rem;
border: none;
border-radius: 0.3rem;
font-size: 1rem;
cursor: pointer;
margin: 0 0.5rem;
&:first-child {
margin-left: 0;
}
&:last-child {
margin-right: 0;
}
}
.cancel-btn {
background-color: #f0f0f0;
color: #333;
&:hover {
background-color: #e0e0e0;
}
}
.confirm-btn {
background-color: #4a90e2;
color: white;
&:hover {
background-color: #357ae8;
}
}
}
}
</style> </style>

View File

@@ -24,7 +24,9 @@
:selectedFolder="currentFolder" :selectedFolder="currentFolder"
:onAllClick="handleAllNotesClick" :onAllClick="handleAllNotesClick"
:onStarredClick="handleStarredNotesClick" :onStarredClick="handleStarredNotesClick"
:onTrashClick="handleTrashNotesClick" /> :onTrashClick="handleTrashNotesClick"
:onFolderClick="handleFolderClick"
:onAddFolder="handleAddFolder" />
</div> </div>
</transition> </transition>
<!-- 点击外部区域收起文件夹列表的覆盖层 --> <!-- 点击外部区域收起文件夹列表的覆盖层 -->
@@ -168,7 +170,9 @@ const headerTitle = computed(() => {
case 'trash': case 'trash':
return '回收站' return '回收站'
default: default:
return '全部便签' // 查找自定义文件夹的名称
const folder = store.folders.find(f => f.id === currentFolder.value)
return folder ? folder.name : '全部便签'
} }
}) })
@@ -279,6 +283,17 @@ const handleTrashNotesClick = () => {
setIsFolderExpanded(false) setIsFolderExpanded(false)
} }
const handleFolderClick = (folderId) => {
setCurrentFolder(folderId)
setIsFolderExpanded(false)
}
const handleAddFolder = () => {
// 文件夹添加功能已在FolderManage组件中实现
// 这里只需关闭文件夹列表
setIsFolderExpanded(false)
}
const handleFolderPress = () => { const handleFolderPress = () => {
// 使用vue-router导航到文件夹页面 // 使用vue-router导航到文件夹页面
router.push('/folders') router.push('/folders')

View File

@@ -295,6 +295,53 @@ export const useAppStore = defineStore('app', {
} }
}, },
/**
* 更新文件夹
* @param {string} id - 文件夹ID
* @param {Object} updates - 要更新的属性对象
* @returns {Promise<Object>} 更新后的文件夹对象
*/
async updateFolder(id, updates) {
try {
const updatedFolder = await storage.updateFolder(id, updates)
if (updatedFolder) {
const index = this.folders.findIndex(folder => folder.id === id)
if (index !== -1) {
this.folders[index] = updatedFolder
}
}
return updatedFolder
} catch (error) {
console.error('Error updating folder:', error)
throw error
}
},
/**
* 删除文件夹
* @param {string} id - 要删除的文件夹ID
* @returns {Promise<boolean>} 删除成功返回true失败返回false
*/
async deleteFolder(id) {
try {
const result = await storage.deleteFolder(id)
if (result) {
// 将文件夹中的便签移回"全部便签"
const notesInFolder = this.notes.filter(note => note.folderId === id)
for (const note of notesInFolder) {
await this.updateNote(note.id, { folderId: null })
}
// 从文件夹列表中移除文件夹
this.folders = this.folders.filter(folder => folder.id !== id)
}
return result
} catch (error) {
console.error('Error deleting folder:', error)
throw error
}
},
/** /**
* 设置操作函数 * 设置操作函数
*/ */

View File

@@ -371,6 +371,41 @@ export const addFolder = async (folder) => {
} }
} }
/**
* 更新文件夹
* 根据ID查找并更新文件夹信息
* @param {string} id - 文件夹ID
* @param {Object} updates - 要更新的属性对象
* @returns {Promise<Object|null>} 更新后的文件夹对象如果未找到则返回null
*/
export const updateFolder = async (id, updates) => {
try {
// 更新文件夹并保存
const updatedFolder = await updateInStore(FOLDERS_STORE, id, updates)
return updatedFolder
} catch (error) {
console.error('Error updating folder:', error)
throw error
}
}
/**
* 删除文件夹
* 根据ID从文件夹列表中移除文件夹
* @param {string} id - 要删除的文件夹ID
* @returns {Promise<boolean>} 删除成功返回true未找到文件夹返回false
*/
export const deleteFolder = async (id) => {
try {
// 从存储中删除
const result = await deleteFromStore(FOLDERS_STORE, id)
return result
} catch (error) {
console.error('Error deleting folder:', error)
return false
}
}
// 设置操作函数 // 设置操作函数
// 提供应用设置的读取和保存功能 // 提供应用设置的读取和保存功能