Compare commits

...

3 Commits

Author SHA1 Message Date
c8aecaeb03 fix: 修复文件夹过滤时分页总数计算错误 2026-04-22 11:18:51 +08:00
bf63610510 fix: 点击类别同时展开文件夹并过滤数据 2026-04-22 10:52:30 +08:00
7af3a7f21d feat: 侧边栏文件夹UI优化 - 折叠式设计
- 新建文件夹按钮移到类别右侧(文件夹+图标)
- 默认折叠状态,点击类别名称展开/折叠
- 投影箭头指示展开状态(▶折叠,▼展开)
- 按钮hover时显示绿色背景,更明显可见
2026-04-22 10:44:53 +08:00
2 changed files with 115 additions and 17 deletions

View File

@@ -55,7 +55,8 @@ def list_items():
status=request.args.get('status'),
tag=request.args.get('tag'),
keyword=request.args.get('keyword'),
starred=starred
starred=starred,
folder_id=folder_id
)
return jsonify({'success': True, 'data': items, 'total': total})
@@ -929,12 +930,49 @@ INDEX_TEMPLATE = '''
opacity: 0.8;
}
/* 文件夹列表样式 */
.sidebar-section .section-header { font-weight: 500; }
/* 文件夹列表样式 - 折叠式 */
.sidebar-section { margin-bottom: 2px; }
.sidebar-section .section-header {
font-weight: 500;
display: flex;
justify-content: space-between;
align-items: center;
padding-right: 10px;
}
.sidebar-section .section-header .header-left {
display: flex;
align-items: center;
flex: 1;
}
.sidebar-section .section-header .toggle-arrow {
font-size: 10px;
margin-right: 8px;
transition: transform 0.2s;
}
.sidebar-section.expanded .section-header .toggle-arrow {
transform: rotate(90deg);
}
.sidebar-section .section-header .new-folder-btn {
font-size: 14px;
color: #adb5bd;
cursor: pointer;
padding: 2px 6px;
border-radius: 4px;
transition: all 0.2s;
background: rgba(255,255,255,0.1);
}
.sidebar-section .section-header .new-folder-btn:hover {
color: #fff;
background: #28a745;
}
.folder-list {
padding-left: 10px;
max-height: 150px;
max-height: 200px;
overflow-y: auto;
display: none; /* 默认隐藏 */
}
.sidebar-section.expanded .folder-list {
display: block; /* 展开时显示 */
}
.folder-list a {
padding: 6px 20px;
@@ -1041,31 +1079,59 @@ INDEX_TEMPLATE = '''
<a href="#" data-filter="starred"><i class="bi bi-star-fill" style="color:#ffc107;"></i> 重点关注</a>
<!-- 文本类别 -->
<div class="sidebar-section">
<a href="#" data-filter="text" class="section-header"><i class="bi bi-file-text"></i> 文本</a>
<div class="sidebar-section" id="section-text">
<a href="#" class="section-header" onclick="toggleSection('text'); return false;">
<span class="header-left">
<span class="toggle-arrow"><i class="bi bi-chevron-right"></i></span>
<i class="bi bi-file-text"></i> 文本
</span>
<span class="new-folder-btn" onclick="event.stopPropagation(); showNewFolderModal('text'); return false;">
<i class="bi bi-folder-plus"></i>
</span>
</a>
<div class="folder-list" id="folderList-text"></div>
<a href="#" class="folder-action" onclick="showNewFolderModal('text'); return false;"><i class="bi bi-folder-plus"></i> 新建文件夹</a>
</div>
<!-- 链接类别 -->
<div class="sidebar-section">
<a href="#" data-filter="link" class="section-header"><i class="bi bi-link-45deg"></i> 链接</a>
<div class="sidebar-section" id="section-link">
<a href="#" class="section-header" onclick="toggleSection('link'); return false;">
<span class="header-left">
<span class="toggle-arrow"><i class="bi bi-chevron-right"></i></span>
<i class="bi bi-link-45deg"></i> 链接
</span>
<span class="new-folder-btn" onclick="event.stopPropagation(); showNewFolderModal('link'); return false;">
<i class="bi bi-folder-plus"></i>
</span>
</a>
<div class="folder-list" id="folderList-link"></div>
<a href="#" class="folder-action" onclick="showNewFolderModal('link'); return false;"><i class="bi bi-folder-plus"></i> 新建文件夹</a>
</div>
<!-- 专栏类别 -->
<div class="sidebar-section">
<a href="#" data-filter="column" class="section-header"><i class="bi bi-newspaper"></i> 专栏</a>
<div class="sidebar-section" id="section-column">
<a href="#" class="section-header" onclick="toggleSection('column'); return false;">
<span class="header-left">
<span class="toggle-arrow"><i class="bi bi-chevron-right"></i></span>
<i class="bi bi-newspaper"></i> 专栏
</span>
<span class="new-folder-btn" onclick="event.stopPropagation(); showNewFolderModal('column'); return false;">
<i class="bi bi-folder-plus"></i>
</span>
</a>
<div class="folder-list" id="folderList-column"></div>
<a href="#" class="folder-action" onclick="showNewFolderModal('column'); return false;"><i class="bi bi-folder-plus"></i> 新建文件夹</a>
</div>
<!-- 待办类别 -->
<div class="sidebar-section">
<a href="#" data-filter="todo" class="section-header"><i class="bi bi-check2-square"></i> 待办</a>
<div class="sidebar-section" id="section-todo">
<a href="#" class="section-header" onclick="toggleSection('todo'); return false;">
<span class="header-left">
<span class="toggle-arrow"><i class="bi bi-chevron-right"></i></span>
<i class="bi bi-check2-square"></i> 待办
</span>
<span class="new-folder-btn" onclick="event.stopPropagation(); showNewFolderModal('todo'); return false;">
<i class="bi bi-folder-plus"></i>
</span>
</a>
<div class="folder-list" id="folderList-todo"></div>
<a href="#" class="folder-action" onclick="showNewFolderModal('todo'); return false;"><i class="bi bi-folder-plus"></i> 新建文件夹</a>
</div>
<hr class="border-secondary">
@@ -3375,15 +3441,20 @@ async function loadFolders() {
function renderFolderList(type) {
const container = document.getElementById(`folderList-${type}`);
const section = document.getElementById(`section-${type}`);
if (!container) return;
const folders = allFolders[type] || [];
if (folders.length === 0) {
container.innerHTML = '';
if (section) section.classList.remove('expanded');
return;
}
// 默认不展开,保持折叠状态
// if (section) section.classList.add('expanded'); // 已移除
container.innerHTML = folders.map(f => `
<a href="#" data-folder="${f.id}" onclick="filterByFolder('${type}', ${f.id}); return false;">
<i class="bi bi-folder"></i> ${f.name} <small class="text-muted">(${f.item_count || 0})</small>
@@ -3391,6 +3462,24 @@ function renderFolderList(type) {
`).join('');
}
// 切换文件夹区域展开/折叠
// 切换文件夹区域展开/折叠,并过滤显示该类别数据
function toggleSection(type) {
const section = document.getElementById(`section-${type}`);
if (!section) return;
// 切换展开状态
section.classList.toggle('expanded');
// 更新侧边栏选中状态
document.querySelectorAll('.sidebar a').forEach(a => a.classList.remove('active'));
section.querySelector('.section-header').classList.add('active');
// 设置过滤条件:只过滤类型,不限制文件夹
currentFilter = { type, status: '', starred: null, folder_id: null };
loadItems(1);
}
async function showNewFolderModal(type) {
// 离线检查
if (!checkOnlineBeforeAction('新建文件夹')) return;

View File

@@ -326,7 +326,7 @@ class Database:
return items
def count_items(self, type: str = None, status: str = None, tag: str = None,
keyword: str = None, starred: bool = None) -> int:
keyword: str = None, starred: bool = None, folder_id: int = None) -> int:
"""计算符合条件的条目总数"""
with self.get_conn() as conn:
cursor = conn.cursor()
@@ -358,6 +358,15 @@ class Database:
keyword_pattern = f"%{keyword}%"
params.extend([keyword_pattern, keyword_pattern, keyword_pattern])
# 文件夹过滤
if folder_id is not None:
if folder_id == -1:
# 未分类folder_id为NULL
conditions.append("i.folder_id IS NULL")
else:
conditions.append("i.folder_id = ?")
params.append(folder_id)
if conditions:
query += " WHERE " + " AND ".join(conditions)