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