fix: 封面图不再被图床拒绝,作品栏补上封面
- NoteCover: 新增共用封面组件,核心是 referrerPolicy="no-referrer"。小红书图床对带外部 Referer 的请求一律 403,而浏览器对跨域 <img> 默认就会带上本站源作为 Referer —— 于是封面全显示成破图,而 URL 本身完全正常。实测同一张图:无 Referer 200 / 67968B,Referer 为本站 403 / 0B - CommentsFeed: 改用 NoteCover,修掉评论栏封面全部加载失败 - NotesTable: 作品栏此前完全没有渲染封面,补上缩略图;封面缺失时用占位块,避免行高随封面陆续到达而跳动 放在一个组件里而不是就地加属性,是为了让下一个显示封面的页面不会漏掉。
This commit is contained in:
@@ -27,6 +27,8 @@ import { monitorApi } from '@/lib/api'
|
||||
import { formatRelative } from '@/lib/monitorFormat'
|
||||
import type { CommentBucket, MonitorComment } from '@/types/monitor'
|
||||
|
||||
import { NoteCover } from './NoteCover'
|
||||
|
||||
interface CommentsFeedProps {
|
||||
taskId: number | null
|
||||
}
|
||||
@@ -49,22 +51,7 @@ function NoteBadge({
|
||||
}) {
|
||||
return (
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
{cover ? (
|
||||
<img
|
||||
src={cover}
|
||||
alt=""
|
||||
loading="lazy"
|
||||
className={`rounded object-cover bg-cyber-bg-tertiary flex-shrink-0 ${
|
||||
compact ? 'w-6 h-8' : 'w-8 h-10'
|
||||
}`}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
className={`rounded bg-cyber-bg-tertiary flex-shrink-0 ${
|
||||
compact ? 'w-6 h-8' : 'w-8 h-10'
|
||||
}`}
|
||||
/>
|
||||
)}
|
||||
<NoteCover src={cover} size={compact ? 'sm' : 'md'} />
|
||||
<span className="truncate text-[10px] font-mono text-cyber-text-secondary" title={title || noteId}>
|
||||
{title || noteId}
|
||||
</span>
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 作品封面缩略图。
|
||||
*
|
||||
* `referrerPolicy="no-referrer"` 是功能性的,不是装饰。小红书的图床对带**外部
|
||||
* Referer** 的请求一律返回 403,而浏览器对跨域 `<img>` 默认就会带上本站的源作为
|
||||
* Referer —— 于是封面全都显示成破图,而 URL 本身完全正常。实测同一张图:
|
||||
*
|
||||
* 无 Referer -> 200, 67968 B, image/webp
|
||||
* Referer 为本站 -> 403, 0 B
|
||||
*
|
||||
* 放在一个组件里,是为了让下一个要显示封面的页面不会漏掉这个属性。
|
||||
*/
|
||||
|
||||
const SIZES = {
|
||||
sm: 'w-6 h-8',
|
||||
md: 'w-8 h-10',
|
||||
lg: 'w-12 h-16',
|
||||
} as const
|
||||
|
||||
export function NoteCover({
|
||||
src,
|
||||
size = 'md',
|
||||
className = '',
|
||||
}: {
|
||||
src?: string
|
||||
size?: keyof typeof SIZES
|
||||
className?: string
|
||||
}) {
|
||||
const base = `rounded object-cover bg-cyber-bg-tertiary flex-shrink-0 ${SIZES[size]} ${className}`
|
||||
|
||||
// 占位块是必要的:作品在首轮采集前没有封面,若此时不占位,行高会随着封面陆续
|
||||
// 到达而跳动。
|
||||
if (!src) return <div className={base} aria-hidden />
|
||||
|
||||
return (
|
||||
<img src={src} alt="" loading="lazy" referrerPolicy="no-referrer" className={base} />
|
||||
)
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { Badge } from '@/components/ui/badge'
|
||||
import { useMonitorNotes } from '@/hooks/useMonitor'
|
||||
import { formatCount, formatDelta, formatRelative } from '@/lib/monitorFormat'
|
||||
import type { MonitorNote, NoteMetrics } from '@/types/monitor'
|
||||
import { NoteCover } from './NoteCover'
|
||||
import { NoteTrendChart } from './NoteTrendChart'
|
||||
|
||||
interface NotesTableProps {
|
||||
@@ -89,18 +90,24 @@ export function NotesTable({ taskId, onlyNew }: NotesTableProps) {
|
||||
{open ? <ChevronDown className="w-3 h-3" /> : <ChevronRight className="w-3 h-3" />}
|
||||
</td>
|
||||
<td className="py-2 px-2 max-w-[320px]">
|
||||
<div className="flex items-center gap-1.5">
|
||||
{note.is_new && (
|
||||
<Badge variant="success" className="text-[9px] px-1 py-0 flex-shrink-0">
|
||||
NEW
|
||||
</Badge>
|
||||
)}
|
||||
<span className="truncate text-cyber-text-primary" title={note.title}>
|
||||
{note.title || note.note_id}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-[9px] text-cyber-text-muted truncate">
|
||||
{note.note_id} · {note.snapshot_count} 次快照
|
||||
<div className="flex items-center gap-2">
|
||||
<NoteCover src={note.cover} size="md" />
|
||||
{/* min-w-0 so the title can actually truncate inside the flex row */}
|
||||
<div className="min-w-0">
|
||||
<div className="flex items-center gap-1.5">
|
||||
{note.is_new && (
|
||||
<Badge variant="success" className="text-[9px] px-1 py-0 flex-shrink-0">
|
||||
NEW
|
||||
</Badge>
|
||||
)}
|
||||
<span className="truncate text-cyber-text-primary" title={note.title}>
|
||||
{note.title || note.note_id}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-[9px] text-cyber-text-muted truncate">
|
||||
{note.note_id} · {note.snapshot_count} 次快照
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
{METRIC_COLUMNS.map((column) => (
|
||||
|
||||
Reference in New Issue
Block a user