优化帖子详情与列表展示,并新增问答帖类型与已解决状态。
统一标题锚点定位、编辑底栏固定与操作栏分层,列表徽章前置并区分置顶/问答配色。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -8,7 +8,7 @@ interface Props {
|
||||
boards: Board[];
|
||||
stats: ForumStats | null;
|
||||
postTotal: number;
|
||||
/** 首页「全部帖子」用 h2,板块/搜索页用 h1 */
|
||||
/** 搜索页用 h1;首页/板块页中间栏不再展示标题 */
|
||||
titleAs?: 'h1' | 'h2';
|
||||
}
|
||||
|
||||
@@ -16,18 +16,15 @@ export default function FeedHeader({ boardId, keyword, boards, stats, postTotal,
|
||||
const nav = useNavigate();
|
||||
const board = boards.find(b => b.id === boardId);
|
||||
|
||||
const title = keyword
|
||||
? `搜索:${keyword}`
|
||||
: (boardId && board ? board.name : '全部帖子');
|
||||
|
||||
const boardHint = boardId && board ? (board.description || '') : '';
|
||||
const TitleTag = titleAs;
|
||||
const inBoard = !keyword && boardId > 0 && !!board;
|
||||
/** 侧栏已有「全部帖子 / 板块名」,中间栏不再重复;仅搜索保留标题 */
|
||||
const title = keyword ? `搜索:${keyword}` : '';
|
||||
const TitleTag = titleAs;
|
||||
|
||||
return (
|
||||
<div className={`feed-head${keyword ? ' feed-head--solo' : ''}`}>
|
||||
<div className={`feed-head${keyword ? ' feed-head--solo' : ' feed-head--stats-only'}`}>
|
||||
<div className="feed-head__title">
|
||||
<TitleTag title={boardHint || undefined}>{title}</TitleTag>
|
||||
{title ? <TitleTag>{title}</TitleTag> : null}
|
||||
{!keyword && inBoard && (
|
||||
<div className="feed-head__stats">
|
||||
<span className="feed-stat-chip">
|
||||
|
||||
@@ -8,9 +8,8 @@ export default function FeedPageSkeleton() {
|
||||
<div className="feed-panel">
|
||||
<div className="feed-top">
|
||||
<div className="feed-top__bar">
|
||||
<div className="feed-head">
|
||||
<div className="feed-head feed-head--stats-only">
|
||||
<div className="feed-head__title">
|
||||
<Skeleton className="skeleton--feed-title" />
|
||||
<div className="feed-head__stats">
|
||||
<Skeleton className="skeleton--stat-chip" />
|
||||
<Skeleton className="skeleton--stat-chip" />
|
||||
|
||||
@@ -66,6 +66,20 @@ export default function PostContent({
|
||||
openLightbox(zoomImg);
|
||||
return;
|
||||
}
|
||||
const headingCopy = target.closest<HTMLElement>('[data-heading-copy]');
|
||||
if (headingCopy) {
|
||||
e.preventDefault();
|
||||
const id = headingCopy.getAttribute('data-heading-copy') || '';
|
||||
if (!id) return;
|
||||
const url = `${window.location.origin}${window.location.pathname}${window.location.search}#${id}`;
|
||||
try {
|
||||
await navigator.clipboard.writeText(url);
|
||||
notify.success('已复制本节链接');
|
||||
} catch {
|
||||
notify.error('复制失败');
|
||||
}
|
||||
return;
|
||||
}
|
||||
const copyBtn = target.closest<HTMLElement>('[data-code-copy]');
|
||||
if (copyBtn) {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -75,33 +75,38 @@ function PostListItem({ post, sort = 'latest', onSelect }: Props) {
|
||||
<span className="post-head-dot" aria-hidden>·</span>
|
||||
<span className="post-time">{timeLabel}</span>
|
||||
</div>
|
||||
{(post.featured || post.pinned || post.status === 'pending' || post.status === 'rejected') && (
|
||||
<div className="post-head-badges">
|
||||
{post.status === 'pending' && (
|
||||
<span className="post-status-badge post-status-badge--pending" title="审核中">审核中</span>
|
||||
)}
|
||||
{post.status === 'rejected' && (
|
||||
<span className="post-status-badge post-status-badge--rejected" title="未通过">未通过</span>
|
||||
)}
|
||||
{post.featured && (
|
||||
<span className="post-feature-badge" title="精华">
|
||||
<FeaturedIcon size={12} />
|
||||
精华
|
||||
</span>
|
||||
)}
|
||||
{post.pinned && (
|
||||
<span className="post-pin-badge" title="置顶">
|
||||
<PinnedIcon size={12} />
|
||||
置顶
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<a href={href} className="post-title" onClick={onTitleClick}>
|
||||
{post.title}
|
||||
</a>
|
||||
<div className="post-title-row">
|
||||
{post.pinned && (
|
||||
<span className="post-pin-badge post-pin-badge--icon" title="置顶">
|
||||
<PinnedIcon size={13} />
|
||||
</span>
|
||||
)}
|
||||
{post.featured && (
|
||||
<span className="post-feature-badge" title="精华">
|
||||
<FeaturedIcon size={12} />
|
||||
精华
|
||||
</span>
|
||||
)}
|
||||
{post.status === 'pending' && (
|
||||
<span className="post-status-badge post-status-badge--pending" title="审核中">审核中</span>
|
||||
)}
|
||||
{post.status === 'rejected' && (
|
||||
<span className="post-status-badge post-status-badge--rejected" title="未通过">未通过</span>
|
||||
)}
|
||||
{post.post_type === 'question' && (
|
||||
<span
|
||||
className={`post-qa-badge${post.question_resolved ? ' post-qa-badge--resolved' : ' post-qa-badge--open'}`}
|
||||
title={post.question_resolved ? '已解决' : '未解决'}
|
||||
>
|
||||
{post.question_resolved ? '已解决' : '未解决'}
|
||||
</span>
|
||||
)}
|
||||
<a href={href} className="post-title" onClick={onTitleClick}>
|
||||
{post.title}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{excerpt && <p className="post-excerpt">{excerpt}</p>}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user