feat(feed): 分类批量已读提示条加标题行
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -55,6 +55,9 @@ alembic/versions/__pycache__/
|
||||
# 临时调试脚本(下划线开头,不进仓库)
|
||||
scripts/_*.py
|
||||
scripts/_*/
|
||||
# docs/ 下的临时调试产物(截图 + 临时 js 测试脚本)
|
||||
docs/*.js
|
||||
docs/*.png
|
||||
|
||||
# 敏感
|
||||
secrets/
|
||||
|
||||
@@ -192,13 +192,26 @@ async function toggleRead(a: ArticleListItem) {
|
||||
} else {
|
||||
await readsApi.mark(a.id)
|
||||
}
|
||||
// 标记为已读后,如果当前在 hide_read 模式,卡片要从列表里消失
|
||||
|
||||
// === 关键:先 await 提示条 fetch,再 splice ===
|
||||
// 原因:hide_read 模式下,350ms 后 items.splice 会把 a 移除,wrapper 跟着走。
|
||||
// 如果 category-count 比 350ms 慢,wrapper 还没渲染就被切走 → 用户看不到。
|
||||
if (!wasRead && a.category) {
|
||||
try {
|
||||
const fetchPromise = maybePromptCategoryRead(a)
|
||||
const timeout = new Promise<void>((resolve) => setTimeout(resolve, 1500))
|
||||
await Promise.race([fetchPromise, timeout])
|
||||
// eslint-disable-next-line no-console
|
||||
console.debug('[category-prompt] mounted for article', a.id, 'cats=', categoryPromptsByArticle.value.get(a.id))
|
||||
} catch {
|
||||
// 静默失败
|
||||
}
|
||||
}
|
||||
|
||||
// hide_read 模式:卡片从列表里消失(用户预期行为)
|
||||
if (!wasRead && hideRead.value) {
|
||||
// 等 leave 动画跑完再从 items 数组里移除(TransitionGroup 才能触发动画)
|
||||
const idx = items.value.findIndex((x) => x.id === a.id)
|
||||
if (idx >= 0) {
|
||||
// 触发 leave 动画:Vue 会保留 DOM 元素直到 transition 结束
|
||||
// 但 splice(items, idx, 1) 会立即从 v-for 移除 → 用 markPending 标记 → 350ms 后再真正移除
|
||||
pendingRemoval.value.add(a.id)
|
||||
setTimeout(() => {
|
||||
const i = items.value.findIndex((x) => x.id === a.id)
|
||||
@@ -208,13 +221,6 @@ async function toggleRead(a: ArticleListItem) {
|
||||
}, 360)
|
||||
}
|
||||
}
|
||||
|
||||
// === 新增:刚标为已读 → 查该文章前 2 个 category 的 24h 未读数 ===
|
||||
// unmark 路径不触发(用户反悔了,不该再骚扰);hide_read 模式仍可触发
|
||||
// (滑出动画只影响当前这一条,提示条展示的是"这个分类下还有别的未读")
|
||||
if (!wasRead && a.category) {
|
||||
await maybePromptCategoryRead(a)
|
||||
}
|
||||
} catch (e: any) {
|
||||
// 失败回滚
|
||||
a.is_read = wasRead
|
||||
@@ -713,6 +719,12 @@ onMounted(async () => {
|
||||
:key="`prompt-wrapper-${a.id}`"
|
||||
class="feed-category-prompt-wrapper"
|
||||
>
|
||||
<!-- 标题行:标记已读的确认 + 1~2 条分类提示的容器 -->
|
||||
<div class="feed-category-prompt-header">
|
||||
<NSpace align="center" :size="8" :wrap="true">
|
||||
<NTag type="success" size="small" round :bordered="false">✓ 已将《{{ a.title_zh || a.title }}》标记为已读</NTag>
|
||||
</NSpace>
|
||||
</div>
|
||||
<NAlert
|
||||
v-for="p in (categoryPromptsByArticle.get(a.id) || [])"
|
||||
:key="`${a.id}-${p.category}`"
|
||||
@@ -724,7 +736,6 @@ onMounted(async () => {
|
||||
>
|
||||
<template #header>
|
||||
<NSpace align="center" :size="8" :wrap="true">
|
||||
<NTag type="success" size="small" round :bordered="false">✓ 已读</NTag>
|
||||
<NText>「{{ p.category }}」分类下还有 {{ p.unreadCount }} 条 24 小时未读</NText>
|
||||
</NSpace>
|
||||
</template>
|
||||
@@ -1102,6 +1113,12 @@ onMounted(async () => {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
/* 标题行(标已读的确认)— 跟下面的 NAlert 视觉分层 */
|
||||
.feed-category-prompt-header {
|
||||
padding: 6px 4px 0;
|
||||
font-size: 13px;
|
||||
color: var(--color-text-faint);
|
||||
}
|
||||
.feed-category-prompt {
|
||||
background: linear-gradient(135deg, #f0fdf4 0%, #ecfdf5 100%);
|
||||
border-left: 3px solid var(--color-primary, #5b86e5);
|
||||
|
||||
Reference in New Issue
Block a user