From 2196189791a82e8a25c20cf540079d0e35e04636 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 11 Aug 2026 12:43:10 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=A1=A5=E5=85=A8=E6=94=B6=E8=97=8F?= =?UTF-8?q?=E4=B8=8E=E5=88=86=E7=B1=BB=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后台编辑区新增★收藏切换按钮(即时生效) - 后台侧栏新增快速筛选: 全部/★收藏/分类 - 前台快速筛选新增分类chips(按分类过滤) - 两种模式均含暗色样式适配 --- web/admin/index.html | 169 ++++++++++++++++++++++++++++++++++++++++++- web/index.html | 67 +++++++++++++++++ 2 files changed, 235 insertions(+), 1 deletion(-) diff --git a/web/admin/index.html b/web/admin/index.html index cdee1a6..c635df0 100644 --- a/web/admin/index.html +++ b/web/admin/index.html @@ -518,6 +518,41 @@ .backlink-item:hover { background: #f1f3f4; text-decoration: underline; } .backlink-empty { padding: 10px; font-size: 12px; color: #999; } + /* 侧栏筛选(收藏/分类) */ + .side-filter { + border-bottom: 1px solid #e0e0e0; + flex-shrink: 0; + max-height: 45%; + overflow-y: auto; + background: #fff; + } + .side-filter-header { + padding: 8px 14px; + font-size: 13px; + font-weight: 600; + color: #666; + } + .filter-chip-row { + display: flex; + flex-wrap: wrap; + gap: 6px; + padding: 4px 12px 10px; + } + .filter-chip { + padding: 4px 10px; + border: 1px solid #e0e0e0; + border-radius: 14px; + background: #fff; + color: #555; + font-size: 12px; + cursor: pointer; + white-space: nowrap; + } + .filter-chip:hover { background: #f1f3f4; } + .filter-chip.active { background: #1a73e8; color: #fff; border-color: #1a73e8; } + .filter-chip.star { color: #e5a00d; } + .filter-chip.star.active { background: #e5a00d; color: #fff; border-color: #e5a00d; } + /* 预览中的 wiki 链接 */ .preview-content .wiki-link { color: #1a73e8; @@ -583,6 +618,12 @@ body.dark .backlink-item { color: #7aa2f7; } body.dark .backlink-item:hover { background: #1e232d; } body.dark .backlink-empty { color: #9aa0aa; } + body.dark .side-filter { background: #15181e; border-bottom-color: #232733; } + body.dark .side-filter-header { color: #9aa0aa; } + body.dark .filter-chip { background: #1a1e26; border-color: #2a3040; color: #c9ced8; } + body.dark .filter-chip:hover { background: #2a3040; } + body.dark .filter-chip.active { background: #1a73e8; color: #fff; border-color: #1a73e8; } + body.dark .filter-chip.star.active { background: #e5a00d; color: #fff; border-color: #e5a00d; } body.dark .preview-content .wiki-link { color: #7aa2f7; border-bottom-color: #7aa2f7; } body.dark .preview-content .wiki-link:hover { background: #1b2a44; } body.dark .graph-node circle { fill: #6aa1f7; stroke: #0f1115; } @@ -627,6 +668,7 @@ +
加载中...
@@ -668,6 +710,12 @@ 公开 + +
@@ -822,6 +870,8 @@ let searchTimer = null; // 搜索防抖定时器 let searchMode = false; // 是否处于搜索模式(隐藏树形) let autosaveInlineOpen = false; // 草稿已加载标志 + let filterMode = { type: 'all', value: '' }; // 侧栏筛选:all / fav / category:xxx + let categoryList = []; // 分类列表(用于筛选 chips) // 初始化 async function init() { @@ -903,6 +953,7 @@ if (data.code === 0) { treeData = data.data; renderTree(); + renderSideFilter(); } } catch (err) { showToast('加载目录失败', 'error'); @@ -916,12 +967,81 @@ container.innerHTML = '
暂无内容,点击 + 新建
'; return; } - + + // 若处于筛选模式(收藏/分类),渲染扁平结果列表 + if (filterMode.type !== 'all') { + const list = getFilteredList(); + if (list.length === 0) { + container.innerHTML = '
' + + (filterMode.type === 'fav' ? '暂无收藏笔记' : '该分类暂无笔记') + '
'; + } else { + container.innerHTML = list.map(item => { + const isActive = currentItem && currentItem.id === item.id; + return ` +
+ + ${escapeHtml(item.title)} + ${item.is_favorite ? '' : ''} +
+ `; + }).join(''); + } + return; + } + // 构建树形结构 const rootItems = treeData.filter(item => item.parent_id === 0); container.innerHTML = renderTreeItems(rootItems); } + // 根据当前筛选模式返回扁平笔记列表 + function getFilteredList() { + if (filterMode.type === 'fav') { + return treeData.filter(i => !i.is_folder && i.is_favorite); + } + if (filterMode.type === 'category' && filterMode.value) { + return treeData.filter(i => !i.is_folder && i.category === filterMode.value); + } + return treeData.filter(i => !i.is_folder); + } + + // 渲染侧栏筛选条(收藏 + 分类) + function renderSideFilter() { + const box = document.getElementById('sideFilter'); + if (!box) return; + // 汇总分类 + const catSet = new Set(); + treeData.forEach(i => { if (!i.is_folder && i.category) catSet.add(i.category); }); + categoryList = Array.from(catSet); + + let html = '
快速筛选
'; + html += `全部`; + html += `★ 收藏`; + categoryList.forEach(cat => { + const hot = filterMode.type === 'category' && filterMode.value === cat; + html += `${escapeHtml(cat)}`; + }); + // 清理没有笔记的分类(含已删除引用) + if (categoryList.length === 0) { + html += '暂无分类'; + } + html += '
'; + + box.innerHTML = html; + box.style.display = 'block'; + } + + // 设置筛选模式并重绘 + function setFilter(type, value) { + filterMode = { type, value }; + // 筛选时退出目录模式(避免编辑目录) + if (type !== 'all') { + searchMode = false; + } + renderSideFilter(); + renderTree(); + } + function renderTreeItems(items) { return items.map(item => { const children = treeData.filter(c => c.parent_id === item.id); @@ -1022,6 +1142,7 @@ } } + updateFavUI(); renderTree(); } } catch (err) { @@ -1116,9 +1237,54 @@ document.getElementById('noteTags').value = ''; document.getElementById('noteContent').value = ''; updatePreview(); + updateFavUI(); } // 保存笔记 + // 收藏切换按钮(点击直接调接口即时生效) + async function toggleFavorite() { + const btn = document.getElementById('favBtn'); + if (!currentItem || !currentItem.id || currentItem.is_folder) { + showToast('请先打开一篇笔记', 'error'); + return; + } + const newVal = btn.dataset.fav !== '1'; + try { + const res = await fetch(`${ADMIN_API}/notes/${currentItem.id}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ is_favorite: newVal }) + }); + const data = await res.json(); + if (data.code === 0) { + currentItem.is_favorite = newVal; + btn.dataset.fav = newVal ? '1' : '0'; + document.getElementById('favText').textContent = newVal ? '已收藏' : '收藏'; + document.getElementById('favStar').style.fill = newVal ? '#e5a00d' : '#ccc'; + showToast(newVal ? '已收藏' : '已取消收藏', 'success'); + // 若当前正处于收藏筛选,刷新列表 + if (filterMode.type === 'fav' || filterMode.type === 'all') { + await loadTree(); + } + } else { + showToast(data.message || '操作失败', 'error'); + } + } catch (err) { + showToast('操作失败', 'error'); + } + } + + // 根据 currentItem 刷新收藏按钮状态 + function updateFavUI() { + const btn = document.getElementById('favBtn'); + if (!btn) return; + const fav = currentItem && currentItem.id && currentItem.is_favorite; + btn.dataset.fav = fav ? '1' : '0'; + document.getElementById('favText').textContent = fav ? '已收藏' : '收藏'; + document.getElementById('favStar').style.fill = fav ? '#e5a00d' : '#ccc'; + btn.style.display = (!currentItem || currentItem.is_folder) ? 'none' : 'flex'; + } + async function saveNote() { const title = document.getElementById('noteTitle').value.trim(); const content = document.getElementById('noteContent').value; @@ -1144,6 +1310,7 @@ category, tags, is_public: isPublic, + is_favorite: document.getElementById('favBtn').dataset.fav === '1', parent_id: currentItem && currentItem.parent_id ? currentItem.parent_id : 0 }; diff --git a/web/index.html b/web/index.html index d701251..98ebb95 100644 --- a/web/index.html +++ b/web/index.html @@ -72,6 +72,9 @@ body.dark .filter-item:hover { background: #1e232d; } body.dark .filter-item.active { background: #1b2a44; color: #7aa2f7; } body.dark .filter-item.active svg { fill: #7aa2f7; } + body.dark .filter-chip { background: #1a1e26; border-color: #2a3040; color: #c9ced8; } + body.dark .filter-chip:hover { background: #2a3040; } + body.dark .filter-chip.active { background: #1976d2; color: #fff; border-color: #1976d2; } body.dark .note-list { border-top: 1px solid #232733; } body.dark .note-item:hover { background: #1e232d; } @@ -285,6 +288,21 @@ .filter-item:hover { background: #f1f3f4; } .filter-item.active { background: #e3f2fd; color: #1976d2; } .filter-item svg { width: 16px; height: 16px; } + + /* 分类筛选 chips */ + .filter-chip { + padding: 4px 11px; + border: 1px solid #e0e0e0; + border-radius: 14px; + background: #fff; + color: #555; + font-size: 12px; + cursor: pointer; + white-space: nowrap; + transition: all 0.2s; + } + .filter-chip:hover { background: #f1f3f4; } + .filter-chip.active { background: #1976d2; color: #fff; border-color: #1976d2; } .note-list { border-top: 1px solid #e0e0e0; @@ -631,6 +649,7 @@ 收藏笔记
+
@@ -677,6 +696,52 @@ async function init() { await loadTree(); + await loadCategories(); + } + + // 加载分类并在筛选区渲染 chips + async function loadCategories() { + try { + const res = await fetch(`${API}/categories`); + const data = await res.json(); + const cats = data.data || []; + const box = document.getElementById('catFilters'); + if (!box) return; + if (cats.length === 0) { + box.innerHTML = ''; + return; + } + box.innerHTML = cats.map(cat => + `${escapeHtml(cat)}` + ).join(''); + } catch (e) { /* 忽略 */ } + } + + // 按分类筛选 + async function filterCategory(cat) { + currentFilter = { type: 'category', value: cat }; + updateFilterUI(); + renderCatFilters(); + try { + const res = await fetch(`${API}/notes?category=${encodeURIComponent(cat)}&page_size=100`); + const data = await res.json(); + if (data.code === 0) { + flatNotes = data.data || []; + renderFlatNotes(); + } + } catch (err) { + showToast('加载失败', 'error'); + } + } + + function renderCatFilters() { + const box = document.getElementById('catFilters'); + if (!box) return; + const chips = box.querySelectorAll('.filter-chip'); + chips.forEach(chip => { + chip.classList.toggle('active', + currentFilter.type === 'category' && chip.textContent === currentFilter.value); + }); } async function loadTree() { @@ -895,12 +960,14 @@ async function filterAll() { currentFilter = { type: 'all' }; updateFilterUI(); + renderCatFilters(); await loadTree(); } async function filterFavorites() { currentFilter = { type: 'favorites' }; updateFilterUI(); + renderCatFilters(); try { const res = await fetch(`${API}/notes?favorite=true&page_size=100`); const data = await res.json();