feat(counts): 浏览/评论徽标接入文章与瞬间页,修同载双计(5h T4)

用户要「加一个访客数量、评论数量,采用类似于开源页面从 GitHub 移植过来的那个
眼睛图标」——落地为 ViewMeta 本地 SVG 徽标,无第三方图片依赖。

接入(op-artalk 车道):
- 文章详情页 reading/tech 两分支各挂一枚 mode="page"(两分支互斥,同页仅一处计数)
- ArtalkComments 顶部旧的纯文本 pv 行与自成一套的 bumpPv 脚本整段删除,改为
  readonly chip:评论区只读展示,不再各计一次
- 瞬间页顶部整页 pv 行同样改 chip;每条瞬间卡片底部为 readonly 徽标

组件修复(本人,op-artalk 实测揪出):
- **同载双计**:脚本既注册 astro:page-load 又立即调用 run(),首屏两者都会触发
  → 每元素 POST 两次、浏览量翻倍(实测 15→17→19 每载 +2)。加每元素一次性
  守卫后 1/2/3 每载 +1;软导航新元素仍照常计数
- **同页两枚数字差 1**:readonly 实例独立 GET 到的 pv 可能比计过数的少 1;
  改为同键存在 page 实例时 readonly 让位,由 page 实例回填同一份结果
- page 实例请求失败时释放兄弟自读,避免评论区永远停在「–」

验证: 构建 exit 0;E2E 37/37;Playwright 拦截对账——
      每载 POST=1、同页两枚徽标文本一致(101/101)、刷新后累计 2、
      接口全挂时两枚均显示占位「–」不报错

Spec: 2026-11-02_blog-5h-content-social (T4)

Co-authored-by: GLM-5.3
This commit is contained in:
wait4xandGLM-5.3 committed 2026-11-02 20:29:44 +08:00
1 parent 8e74bead62
commit 63c9c8ad4a
6 files changed
+52 -67

No files matched your search

+5 -31
View File
@@ -3,6 +3,7 @@ import { Image } from 'astro:assets';
import { getCollection } from 'astro:content';
import BaseLayout from '../../layouts/BaseLayout.astro';
import MomentComments from '../MomentComments.astro';
import ViewMeta from '../geek/ViewMeta.astro';
import { t } from '../../i18n/ui';
import { renderRich } from '../../lib/site-content';
import type { Locale } from '../../lib/locale';
@@ -32,10 +33,9 @@ const iso = (d: Date) => d.toISOString();
<BaseLayout title={`${t('moments_title', locale)} · wait4x`}>
<h1 class="page-title">{t('moments_title', locale)}</h1>
<p class="page-note">{t('moments_note', locale)}</p>
<p class="pv-line" aria-label={t('pv_viewed', locale)}>
<span data-artalk-pv data-page-key={locale === 'en' ? '/en/moments/' : '/moments/'}>–</span>
{' '}{t('pv_viewed', locale)}
</p>
<!-- 整页计数(Phase 5h T4):这是本页唯一的计数点(page 模式);
每条瞬间卡片底部的徽标是 readonly,读者开评论不会虚增浏览数 -->
<ViewMeta class="mom-page-meta" pageKey={locale === 'en' ? '/en/moments/' : '/moments/'} {locale} variant="chip" />
<ol class="tl">
{moments.map((m) => (
<li class="tl-item">
@@ -80,25 +80,6 @@ const iso = (d: Date) => d.toISOString();
</BaseLayout>
<script>
// 访问量:与 ArtalkComments 同款独立 POST(双轨 pageKey 分开计数;POST 绕 ESA 缓存)
const bumpPv = async () => {
const el = document.querySelector('[data-artalk-pv]');
if (!el) return;
const key = el.dataset.pageKey ?? '';
if (!key) return;
try {
const res = await fetch('/artalk/api/v2/pages/pv', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ page_key: key, site_name: 'wait4x.online' }),
});
const d = await res.json();
el.textContent = String(d.pv ?? '–');
} catch {
el.textContent = '–';
}
};
// 时间线渐显:html[data-mom-anim] 由 JS 前置标记——无 JS / reduced-motion 时
// 标记缺席,CSS 初始隐藏规则整体不生效(渐进降级,元素永远可见)
let io: IntersectionObserver | null = null;
@@ -122,11 +103,9 @@ const iso = (d: Date) => d.toISOString();
};
document.addEventListener('astro:page-load', () => {
void bumpPv();
armAnim();
refreshInstallCta();
});
void bumpPv();
armAnim();
// —— PWA 安装引导:beforeinstallprompt 早在页面加载初期触发,
@@ -318,12 +297,7 @@ const iso = (d: Date) => d.toISOString();
transition: border-color var(--dur) var(--ease);
}
.install-btn:hover { border-color: var(--accent); }
.pv-line {
font-family: var(--font-mono);
font-size: 12px;
color: var(--ink-4);
margin: 0 0 var(--space-4);
}
.mom-page-meta { margin: 0 0 var(--space-4); }
/* 初始隐藏仅在 JS 标记存在时生效:宁轻勿闹——透明度+8px 上移,进入视口一次性点亮 */
[data-mom-anim] .tl-item {
opacity: 0;