diff --git a/frontend/src/views/Feed.vue b/frontend/src/views/Feed.vue index e26c0d3..4ae9c84 100644 --- a/frontend/src/views/Feed.vue +++ b/frontend/src/views/Feed.vue @@ -210,11 +210,23 @@ async function toggleRead(a: ArticleListItem) { } } - // hide_read 模式:卡片**保留在列表里**(视觉降级为 opacity 0.7) - // 不再 splice 移除文章 — 否则会触发 NCard 的 leave 动画, - // 进而影响 wrapper 显示,导致分类提示条看不见 - // 用户主动刷新 / 切页时 hideRead=true 的过滤会自然把已读过滤掉 - // (hide_read 模式的"splice"逻辑废弃,保留 is_read 状态以驱动 article-card-read 样式) + // hide_read 模式:卡片从列表里滑出(用户预期行为) + // wrapper 现在嵌在 NCard 内部,NCard leave 动画会带 wrapper 一起走 + // 有提示条:让用户能看完 8s;无提示条:照旧 360ms 滑出 + if (!wasRead && hideRead.value) { + const hasPrompt = (categoryPromptsByArticle.value.get(a.id)?.length ?? 0) > 0 + const delay = hasPrompt ? 8200 : 360 // 有提示:等提示条走完;无提示:照旧 + const idx = items.value.findIndex((x) => x.id === a.id) + if (idx >= 0) { + pendingRemoval.value.add(a.id) + setTimeout(() => { + const i = items.value.findIndex((x) => x.id === a.id) + if (i >= 0) items.value.splice(i, 1) + pendingRemoval.value.delete(a.id) + if (total.value > 0) total.value -= 1 + }, delay) + } + } } catch (e: any) { // 失败回滚 a.is_read = wasRead @@ -276,9 +288,22 @@ async function confirmMarkCategory(articleId: number, category: string) { for (const item of items.value) { if (ids.has(item.id)) item.is_read = true } - // 走 hide_read 模式下的滑出 — 已废弃,改成已读卡片视觉降级 - // 原因:批量 splice 触发的 NCard leave 动画会带歪提示条 wrapper - // 用户主动刷新 / 切页时,hideRead=true 的过滤会自然把已读过滤掉 + // 走 hide_read 模式下的滑出(累计 delay,逐个错开,避免 30 个 setTimeout 同时触发视觉抖) + // wrapper 嵌在 NCard 内部,NCard leave 动画会带 wrapper 一起走 + let i = 0 + for (const id of resp.article_ids) { + const idx = items.value.findIndex((x) => x.id === id) + if (idx < 0) continue + pendingRemoval.value.add(id) + const delay = 360 + i * 20 + i++ + setTimeout(() => { + const k = items.value.findIndex((x) => x.id === id) + if (k >= 0) items.value.splice(k, 1) + pendingRemoval.value.delete(id) + if (total.value > 0) total.value -= 1 + }, delay) + } // 关闭该 article 的整组提示(不只关 category — 既然"全部已读"了,这一片都不需要了) clearPromptsForArticle(articleId) message.success(`已将 ${resp.marked} 条「${category}」标记为已读`)