fix(feed): 把 wrapper 嵌入 NCard 内部底部,真正紧贴对应卡片

This commit is contained in:
xiaji
2026-06-16 10:15:36 +08:00
parent 4e90253047
commit b468c57139

View File

@@ -44,17 +44,12 @@ let categoryPromptTimer: number | null = null
// - items 没这条 → 不渲染(孤儿)
// - items 有 + Map 也有 → 渲染
// - items 有 + Map 没 → 不渲染(用户已 dismiss/确认/超时)
const articlesWithPrompts = computed(() => {
const r = items.value.filter((a) => (categoryPromptsByArticle.value.get(a.id)?.length ?? 0) > 0)
// eslint-disable-next-line no-console
console.log('[AWP] resultLen=', r.length, 'itemsLen=', items.value.length, 'mapSize=', categoryPromptsByArticle.value.size)
return r
})
const articlesWithPrompts = computed(() =>
items.value.filter((a) => (categoryPromptsByArticle.value.get(a.id)?.length ?? 0) > 0)
)
// 移除某篇文章的所有提示条(8 秒超时 / 确认后 / 过滤变化)
function clearPromptsForArticle(articleId: number) {
// eslint-disable-next-line no-console
console.log('[CLEAR_ONE]', articleId, 'stack:', new Error().stack?.split('\n').slice(2, 5).join(' | '))
if (categoryPromptsByArticle.value.has(articleId)) {
const next = new Map(categoryPromptsByArticle.value)
next.delete(articleId)
@@ -64,8 +59,6 @@ function clearPromptsForArticle(articleId: number) {
// 全清
function clearAllPrompts() {
// eslint-disable-next-line no-console
console.log('[CLEAR_ALL] size=', categoryPromptsByArticle.value.size, 'stack:', new Error().stack?.split('\n').slice(2, 5).join(' | '))
if (categoryPromptsByArticle.value.size > 0) {
categoryPromptsByArticle.value = new Map()
}
@@ -693,58 +686,57 @@ onMounted(async () => {
{{ a.is_read ? '已读' : '标为已读' }}
</NButton>
</div>
<!--
分类提示条(per-article,紧贴卡片底部):
- 嵌在 NCard 内部,确保 DOM 位置紧贴对应卡片
- 只有当该 article 在 categoryPromptsByArticle Map 里有值时才渲染
- 8s 后 Map 删掉 → wrapper 走 leave 动画
- 内部 NAlert v-for 用 ${a.id}-${category} 作为 key,确保多个 category 时各自独立
-->
<div
v-if="(categoryPromptsByArticle.get(a.id)?.length ?? 0) > 0"
: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}`"
type="success"
:show-icon="false"
closable
@close="dismissPrompt(a.id, p.category)"
class="feed-category-prompt"
>
<template #header>
<NSpace align="center" :size="8" :wrap="true">
<NText>「{{ p.category }}」分类下还有 {{ p.unreadCount }} 条 24 小时未读</NText>
</NSpace>
</template>
<NSpace :size="8" style="margin-top: 8px">
<NButton
type="primary"
size="small"
round
:loading="pendingCategory === p.category"
:disabled="pendingCategory !== null"
@click="confirmMarkCategory(a.id, p.category)"
>
全部已读
</NButton>
<NButton size="small" round @click="dismissPrompt(a.id, p.category)">
稍后再说
</NButton>
</NSpace>
</NAlert>
</div>
</NSpace>
</NCard>
<!--
该文章对应的分类提示条(per-article 挂载,卡片**下方**):
- 用 articlesWithPrompts(过滤后只有有提示条的 article)做 v-for
- 跟 NCard 在同一个 TransitionGroup,但 hide_read 模式不再 splice 移除文章,
所以 NCard 不会触发 leave 动画,wrapper 不会被卷走
- 8s 后 Map 删掉 → wrapper 走自己的 leave 动画(只影响 wrapper,不影响 NCard)
- 内部 NAlert v-for 用 ${a.id}-${category} 作为 key,确保多个 category 时各自独立
-->
<div
v-for="a in articlesWithPrompts"
: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}`"
type="success"
:show-icon="false"
closable
@close="dismissPrompt(a.id, p.category)"
class="feed-category-prompt"
>
<template #header>
<NSpace align="center" :size="8" :wrap="true">
<NText>「{{ p.category }}」分类下还有 {{ p.unreadCount }} 条 24 小时未读</NText>
</NSpace>
</template>
<NSpace :size="8" style="margin-top: 8px">
<NButton
type="primary"
size="small"
round
:loading="pendingCategory === p.category"
:disabled="pendingCategory !== null"
@click="confirmMarkCategory(a.id, p.category)"
>
全部已读
</NButton>
<NButton size="small" round @click="dismissPrompt(a.id, p.category)">
稍后再说
</NButton>
</NSpace>
</NAlert>
</div>
</TransitionGroup>
<!-- 页码分页 -->