优化帖子详情与列表展示,并新增问答帖类型与已解决状态。

统一标题锚点定位、编辑底栏固定与操作栏分层,列表徽章前置并区分置顶/问答配色。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-04 00:19:32 +08:00
parent 3aca5c42c5
commit b24f6c23ea
16 changed files with 696 additions and 239 deletions

View File

@@ -23,6 +23,7 @@ interface ComposeBaseline {
tags: string;
content: string;
boardId: string;
postType: 'normal' | 'question';
}
function resolveBoards(ctxBoards?: Board[]): Board[] {
@@ -63,6 +64,7 @@ export default function ComposePage() {
const [title, setTitle] = useState('');
const [tags, setTags] = useState('');
const [content, setContent] = useState('');
const [postType, setPostType] = useState<'normal' | 'question'>('normal');
const [publishing, setPublishing] = useState(false);
const [loading, setLoading] = useState(isEdit);
/** 新建帖:板块列表是否已就绪(避免请求中误显空态) */
@@ -102,17 +104,20 @@ export default function ComposePage() {
return;
}
const loadedBoardId = String(post.board_id);
const loadedType = post.post_type === 'question' ? 'question' : 'normal';
const serverBaseline: ComposeBaseline = {
title: post.title,
tags: post.tags ?? '',
content: post.content ?? '',
boardId: loadedBoardId,
postType: loadedType,
};
setBoardId(loadedBoardId);
setBaseline(serverBaseline);
setTitle(serverBaseline.title);
setTags(serverBaseline.tags);
setContent(serverBaseline.content);
setPostType(loadedType);
const windowHours = postData.post_edit_window_hours ?? 0;
if (user.role !== 'admin' && windowHours > 0) {
@@ -139,6 +144,7 @@ export default function ComposePage() {
tags: '',
content: '',
boardId: boardForBaseline,
postType: 'normal',
});
};
@@ -169,8 +175,9 @@ export default function ComposePage() {
|| serializeTags(parseTags(tags)) !== serializeTags(parseTags(baseline.tags))
|| content !== baseline.content
|| boardId !== baseline.boardId
|| postType !== baseline.postType
);
}, [baseline, title, tags, content, boardId, isEdit, boards.length]);
}, [baseline, title, tags, content, boardId, postType, isEdit, boards.length]);
const {
dialogOpen,
@@ -247,6 +254,7 @@ export default function ComposePage() {
content: content.trim(),
tags: serializeTags(parseTags(tags)),
board_id: boardId,
post_type: postType,
};
if (isEdit) {
await api.updatePost(editId!, payload);
@@ -305,6 +313,32 @@ export default function ComposePage() {
<div className="compose-shell-body">
<section className="compose-context" aria-label="发布设置">
<div className="compose-context-row">
<span className="compose-context-label"></span>
<div className="compose-type-pills" role="radiogroup" aria-label="帖子类型">
<button
type="button"
role="radio"
aria-checked={postType === 'normal'}
className={`compose-type-pill${postType === 'normal' ? ' active' : ''}`}
onClick={() => setPostType('normal')}
>
</button>
<button
type="button"
role="radio"
aria-checked={postType === 'question'}
className={`compose-type-pill${postType === 'question' ? ' active' : ''}`}
onClick={() => setPostType('question')}
>
</button>
</div>
{postType === 'question' && (
<span className="compose-type-hint"> / </span>
)}
</div>
<div className="compose-context-row">
<span className="compose-context-label"></span>
<div className="compose-board-pills" role="listbox" aria-label={isEdit ? '修改板块' : '选择板块'}>
@@ -337,7 +371,7 @@ export default function ComposePage() {
<input
className="compose-title"
type="text"
placeholder="输入文章标题…"
placeholder={postType === 'question' ? '用一句话描述你的问题…' : '输入文章标题…'}
value={title}
onChange={e => setTitle(e.target.value)}
maxLength={limits.post_title_max > 0 ? limits.post_title_max : undefined}