diff --git a/package-lock.json b/package-lock.json
index d2e330f..5470a46 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,6 +17,7 @@
"electron": "^42.3.0",
"electron-vite": "^5.0.0",
"gray-matter": "^4.0.3",
+ "lucide-vue-next": "^1.0.0",
"nanoid": "^5.1.11",
"pinia": "^3.0.4",
"primeicons": "^7.0.0",
@@ -5478,6 +5479,16 @@
"yallist": "^3.0.2"
}
},
+ "node_modules/lucide-vue-next": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/lucide-vue-next/-/lucide-vue-next-1.0.0.tgz",
+ "integrity": "sha512-V6SPvx1IHTj/UY+FrIYWV5faISsPSb8BnWSFDxAtezWKvWc9ZZ40PDrdu1/Qb5vg4lHWr1hs1BAMGVGm6V1Xdg==",
+ "deprecated": "Package deprecated. Please use @lucide/vue instead.",
+ "license": "ISC",
+ "peerDependencies": {
+ "vue": ">=3.0.1"
+ }
+ },
"node_modules/magic-string": {
"version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
diff --git a/package.json b/package.json
index aa59034..1441f03 100644
--- a/package.json
+++ b/package.json
@@ -26,6 +26,7 @@
"electron": "^42.3.0",
"electron-vite": "^5.0.0",
"gray-matter": "^4.0.3",
+ "lucide-vue-next": "^1.0.0",
"nanoid": "^5.1.11",
"pinia": "^3.0.4",
"primeicons": "^7.0.0",
diff --git a/src/renderer/src/components/LucideIcon.vue b/src/renderer/src/components/LucideIcon.vue
new file mode 100644
index 0000000..1097940
--- /dev/null
+++ b/src/renderer/src/components/LucideIcon.vue
@@ -0,0 +1,42 @@
+
+
+
+
+
+
diff --git a/src/renderer/src/components/SideBar.vue b/src/renderer/src/components/SideBar.vue
index 907b2ad..e0af2f2 100644
--- a/src/renderer/src/components/SideBar.vue
+++ b/src/renderer/src/components/SideBar.vue
@@ -3,22 +3,15 @@ import {ref, computed, onMounted, nextTick} from 'vue'
import Button from 'primevue/button'
import InputText from 'primevue/inputtext'
import Dialog from 'primevue/dialog'
-import Menu from 'primevue/menu'
import IconField from 'primevue/iconfield'
import InputIcon from 'primevue/inputicon'
-import ScrollPanel from 'primevue/scrollpanel'
-import Tree from 'primevue/tree'
-import {
- Search, Plus, File, Star, Trash2,
- Settings, Sun, Moon, Monitor,
- BookOpen, Tag, ChevronDown, ChevronRight, Hash,
- MoreHorizontal
-} from '@lucide/vue'
+import {Plus, File, Sun, Moon, Monitor, Settings} from '@lucide/vue'
import {useThemeStore} from '../stores/theme'
import {useNotesStore} from '../stores/notes'
import {useNotebooksStore} from '../stores/notebooks'
-import {useSidebarTree} from '../composables/useSidebarTree'
-import ButtonIcon from "./ButtonIcon.vue";
+
+import Menu from 'primevue/menu'
+import LucideIcon from "./LucideIcon.vue";
const themeStore = useThemeStore()
const notesStore = useNotesStore()
@@ -29,7 +22,22 @@ const props = defineProps({
tags: {type: Array, default: () => []}
})
-const {sidebarTree, flatNotebooks, expandedKeys, toggleNodeExpanded, loadNotebooks, loadNotes} = useSidebarTree(computed(() => props.tags))
+async function loadNotebooks() {
+ await notebooksStore.loadNotebooks()
+}
+
+async function loadNotes() {
+ await notesStore.loadNotes()
+}
+
+
+const items11 = ref([
+
+ {label: 'Documents', icon: 'search' },
+ {label: '收藏夹', icon: 'search'},
+ {label: '标签', icon: 'search'},
+ {label: '笔记本', icon: 'search'},
+]);
const theme = computed(() => themeStore.theme)
const toggleTheme = themeStore.toggleTheme
@@ -42,22 +50,11 @@ const newNotebookParentName = computed(() => {
const emit = defineEmits(['navigate', 'create-note', 'select-notebook', 'select-tag', 'select-note'])
-// Selected tree node
-const selectedTreeKey = ref(null)
-
-// Load notebooks and notes on mount
onMounted(async () => {
await loadNotebooks()
await loadNotes()
})
-// Collapse state
-const collapsed = ref(false)
-
-function toggleCollapsed() {
- collapsed.value = !collapsed.value
-}
-
// Navigation state
const activeNav = ref('all')
@@ -78,7 +75,6 @@ async function onSearchInput() {
}, 200)
}
-// Methods
function handleNav(id) {
activeNav.value = id
emit('navigate', id)
@@ -89,72 +85,122 @@ function selectNotebook(notebook) {
emit('select-notebook', notebook)
}
-/**
- * Handle Tree node selection
- * PrimeVue v4 passes node directly (not wrapped in event object)
- */
-function onTreeSelect(node) {
- const { type, id } = node.data
-
- if (type === 'nav') {
- selectedTreeKey.value = node.key
- handleNav(id)
- } else if (type === 'notebook') {
- selectedTreeKey.value = node.key
- activeNav.value = `notebook-${id}`
- emit('select-notebook', node.data)
- } else if (type === 'note') {
- selectedTreeKey.value = node.key
- activeNav.value = `note-${id}`
- emit('select-note', node.data)
- } else if (type === 'tag') {
- selectedTreeKey.value = node.key
- activeNav.value = `tag-${id}`
- emit('select-tag', node.data)
- }
-}
-
function selectTag(tag) {
activeNav.value = `tag-${tag.id}`
emit('select-tag', tag)
}
+function selectSearchNote(note) {
+ emit('select-note', note)
+ searchQuery.value = ''
+ searchResults.value = []
+}
+
+function getNotebookName(note) {
+ if (note.notebook_name) return note.notebook_name
+ const nb = props.notebooks.find(n => n.id === note.notebook_id)
+ return nb ? nb.name : ''
+}
+
function createNote(notebookId) {
- closeMenu()
emit('create-note', notebookId)
}
-// Notebook actions menu (PrimeVue Menu)
-const notebookMenuRef = ref(null)
-const activeMenuNotebookId = ref(null)
+// Notebook tree for PanelMenu
+const notebookTreeItems = computed(() => {
+ const notebookList = notebooksStore.notebooks
+ const noteList = notesStore.notes
-const notebookMenuItems = computed(() => [
+ function buildNotebookChildren(parentId) {
+ const children = []
+
+ const childNotebooks = notebookList.filter(nb => (nb.parent_id || null) === parentId)
+ childNotebooks.sort((a, b) => a.name.localeCompare(b.name))
+ childNotebooks.forEach(nb => {
+ const nbChildren = buildNotebookChildren(nb.id)
+ children.push({
+ key: `notebook-${nb.id}`,
+ label: nb.name,
+ icon: 'pi pi-folder',
+ command: () => selectNotebook(nb),
+ items: nbChildren.length > 0 ? nbChildren : undefined
+ })
+ })
+
+ const notebookNotes = noteList.filter(n => n.notebook_id === parentId && !n.is_trashed)
+ notebookNotes.sort((a, b) => (a.title || '').localeCompare(b.title || ''))
+ notebookNotes.forEach(note => {
+ children.push({
+ key: `note-${note.id}`,
+ label: note.title || '无标题',
+ icon: 'pi pi-file',
+ command: () => emit('select-note', note)
+ })
+ })
+
+ return children
+ }
+
+ const rootNotebooks = notebookList.filter(nb => !nb.parent_id)
+ rootNotebooks.sort((a, b) => a.name.localeCompare(b.name))
+
+ return rootNotebooks.map(nb => {
+ const children = buildNotebookChildren(nb.id)
+ return {
+ key: `notebook-${nb.id}`,
+ label: nb.name,
+ icon: 'pi pi-folder',
+ command: () => selectNotebook(nb),
+ items: children.length > 0 ? children : undefined
+ }
+ })
+})
+
+// PanelMenu items
+const items = computed(() => [
{
- label: '新建子笔记本',
- icon: 'pi pi-folder',
- command: () => startNewNotebook(activeMenuNotebookId.value)
+ label: '导航',
+ icon: 'pi pi-compass',
+ items: [
+ {
+ label: '全部笔记',
+ icon: 'pi pi-home',
+ command: () => handleNav('all')
+ },
+ {
+ label: '收藏',
+ icon: 'pi pi-star',
+ command: () => handleNav('favorites')
+ },
+ {
+ label: '最近编辑',
+ icon: 'pi pi-clock',
+ command: () => handleNav('recent')
+ },
+ {
+ label: '回收站',
+ icon: 'pi pi-trash',
+ command: () => handleNav('trash')
+ }
+ ]
},
{
- label: '新建笔记',
- icon: 'pi pi-file',
- command: () => {
- closeMenu();
- emit('create-note', activeMenuNotebookId.value)
- }
- }
+ label: '笔记本',
+ icon: 'pi pi-book',
+ items: notebookTreeItems.value
+ },
+ ...(props.tags && props.tags.length > 0 ? [{
+ label: '标签',
+ icon: 'pi pi-tag',
+ items: props.tags.map(tag => ({
+ label: tag.name,
+ icon: 'pi pi-tag',
+ command: () => selectTag(tag)
+ }))
+ }] : [])
])
-function toggleNotebookMenu(notebookId, event) {
- event.stopPropagation()
- activeMenuNotebookId.value = notebookId
- notebookMenuRef.value.toggle(event)
-}
-
-function closeMenu() {
- activeMenuNotebookId.value = null
-}
-
-// New notebook creation (supports parent_id for sub-notebooks)
+// New notebook dialog
const showNewNotebookDialog = ref(false)
const newNotebookName = ref('')
const newNotebookInputRef = ref(null)
@@ -164,7 +210,6 @@ function startNewNotebook(parentId = null) {
newNotebookName.value = ''
newNotebookParentId.value = parentId
showNewNotebookDialog.value = true
- closeMenu()
nextTick(() => {
newNotebookInputRef.value?.$el?.focus()
})
@@ -192,74 +237,36 @@ function cancelNewNotebook() {
newNotebookName.value = ''
newNotebookParentId.value = null
}
-
-function selectSearchNote(note) {
- emit('select-note', note)
- searchQuery.value = ''
- searchResults.value = []
-}
-
-// Get notebook display name for a note
-function getNotebookName(note) {
- if (note.notebook_name) return note.notebook_name
- const nb = props.notebooks.find(n => n.id === note.notebook_id)
- return nb ? nb.name : ''
-}
-
-/**
- * Get the active style class based on node type
- */
-function getNodeActiveClass(node) {
- const { type, id } = node.data
- if (type === 'nav') {
- return activeNav.value === id
- ? 'bg-surface-200/70 text-primary-700 font-medium shadow-sm'
- : 'text-surface-600 hover:bg-surface-100'
- }
- if (type === 'notebook') {
- return activeNav.value === `notebook-${id}`
- ? 'bg-emerald-50 text-emerald-700 font-medium shadow-sm'
- : 'text-surface-600 hover:bg-surface-100'
- }
- if (type === 'tag') {
- return activeNav.value === `tag-${id}`
- ? 'bg-amber-50 text-amber-700 font-medium shadow-sm'
- : 'text-surface-600 hover:bg-surface-100'
- }
- return ''
-}
-