feat: 管理后台目录树支持折叠/展开

- 有子节点的目录前显示折叠箭头, 点击折叠/展开子目录
- 折叠状态持久化到 localStorage, 刷新后保留
- 与前台目录树折叠行为一致
This commit is contained in:
Your Name
2026-08-11 16:19:53 +08:00
parent 13c53fea0a
commit 6a2ffb5d4b
+50 -1
View File
@@ -120,6 +120,27 @@
}
.tree-item .actions button:hover { background: #e8eaed; }
.tree-children { padding-left: 20px; }
.tree-item .toggle {
width: 18px;
height: 18px;
margin-right: 2px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: none;
background: transparent;
padding: 0;
color: #888;
flex-shrink: 0;
transition: transform 0.15s ease;
}
.tree-item .toggle svg { width: 14px; height: 14px; fill: currentColor; }
.tree-item .toggle.collapsed { transform: rotate(-90deg); }
.tree-item .toggle:hover { color: #1a73e8; }
.tree-children { display: block; }
.tree-children.hidden { display: none; }
/* 右侧内容区 - 左右分栏 */
.content {
@@ -900,6 +921,15 @@
const API = '/api';
const ADMIN_API = '/admin/api';
let treeData = [];
// 折叠的目录 id 集合(持久化到 localStorage,按用户隔离)
let collapsedFolders = new Set();
try {
const saved = JSON.parse(localStorage.getItem('noteManagerCollapsedFolders') || '[]');
collapsedFolders = new Set(Array.isArray(saved) ? saved : []);
} catch (e) { collapsedFolders = new Set(); }
function saveCollapsedFolders() {
try { localStorage.setItem('noteManagerCollapsedFolders', JSON.stringify([...collapsedFolders])); } catch (e) {}
}
let currentItem = null;
let isFolderMode = false;
let editId = null; // 当前打开的笔记 id
@@ -1137,8 +1167,15 @@
const isActive = currentItem && currentItem.id === item.id;
if (item.is_folder) {
// 目录折叠状态:默认展开;在 collapsedFolders 中则折叠
const isCollapsed = collapsedFolders.has(item.id);
return `
<div class="tree-item folder ${isActive ? 'active' : ''}" onclick="selectItem(${item.id})">
${hasChildren ? `
<button class="toggle ${isCollapsed ? 'collapsed' : ''}" title="${isCollapsed ? '展开' : '折叠'}" onclick="event.stopPropagation(); toggleFolder(${item.id})">
<svg viewBox="0 0 24 24"><path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6z"/></svg>
</button>` : `
<span class="toggle" style="visibility:hidden;"><svg viewBox="0 0 24 24"><path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6z"/></svg></span>`}
<span class="icon">
<svg width="18" height="18" viewBox="0 0 24 24" fill="${isActive ? '#1a73e8' : '#fbbc04'}">
<path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/>
@@ -1150,11 +1187,12 @@
<button onclick="event.stopPropagation(); showNewNoteModal(${item.id})">+笔记</button>
</div>
</div>
${hasChildren ? `<div class="tree-children">${renderTreeItems(children)}</div>` : ''}
${hasChildren ? `<div class="tree-children ${isCollapsed ? 'hidden' : ''}">${renderTreeItems(children)}</div>` : ''}
`;
} else {
return `
<div class="tree-item ${isActive ? 'active' : ''}" onclick="selectItem(${item.id})">
<span class="toggle" style="visibility:hidden;"><svg viewBox="0 0 24 24"><path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6z"/></svg></span>
<span class="icon">
<svg width="16" height="16" viewBox="0 0 24 24" fill="#666">
<path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z"/>
@@ -1167,6 +1205,17 @@
}).join('');
}
// 折叠/展开目录
function toggleFolder(id) {
if (collapsedFolders.has(id)) {
collapsedFolders.delete(id);
} else {
collapsedFolders.add(id);
}
saveCollapsedFolders();
renderTree();
}
// 选择项目
async function selectItem(id) {
try {