Update comment pagination and ignore data files

This commit is contained in:
LeonspaceX
2026-01-27 12:13:19 +08:00
parent 463c793e89
commit 72204c74b0
7 changed files with 78 additions and 22 deletions

6
.gitignore vendored
View File

@@ -1,2 +1,4 @@
back/data back/data/
back/__pycache__ back/__pycache__/
**/__pycache__/
**/*.pyc

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 KiB

View File

@@ -340,14 +340,23 @@ def get_comments():
if not submission or submission.status != "Pass": if not submission or submission.status != "Pass":
return jsonify({"code": 2002, "data": "投稿不存在"}) return jsonify({"code": 2002, "data": "投稿不存在"})
page = request.args.get("page", 1, type=int)
if page < 1:
page = 1
per_page = 5
pagination = Comment.query.filter_by(submission_id=submission_id)\
.order_by(Comment.id.asc())\
.paginate(page=page, per_page=per_page, error_out=False)
data = [{ data = [{
"id": c.id, "id": c.id,
"nickname": c.nickname, "nickname": c.nickname,
"content": c.content, "content": c.content,
"parent_comment_id": c.parent_comment_id if c.parent_comment_id is not None else 0 "parent_comment_id": c.parent_comment_id if c.parent_comment_id is not None else 0
} for c in submission.comments] } for c in pagination.items]
return jsonify({"code": 1000, "data": data}) return jsonify({"code": 1000, "data": {"comments": data, "total_pages": pagination.pages}})
except Exception as e: except Exception as e:
return jsonify({"code": 2003, "data": str(e)}) return jsonify({"code": 2003, "data": str(e)})
@@ -469,8 +478,8 @@ def get_statics():
except Exception as e: except Exception as e:
return jsonify({"code": 2003, "data": str(e)}) return jsonify({"code": 2003, "data": str(e)})
@app.route('/api/10_info', methods=['GET']) @app.route('/api/posts_info', methods=['GET'])
def get_10_info(): def get_posts_info():
try: try:
page = request.args.get("page", 1, type=int) page = request.args.get("page", 1, type=int)
if page < 1: if page < 1:
@@ -503,8 +512,8 @@ def get_10_info():
except Exception as e: except Exception as e:
return jsonify({"code": 2003, "data": str(e)}) return jsonify({"code": 2003, "data": str(e)})
# --- 菜单 --- # --- 彩蛋 ---
@app.route('/get/teapot', methods=['GET']) @app.route('/api/teapot', methods=['GET'])
def return_418(): def return_418():
abort(418) abort(418)

View File

@@ -175,7 +175,7 @@ export interface Article {
export const fetchArticles = async (page: number, signal?: AbortSignal): Promise<Article[]> => { export const fetchArticles = async (page: number, signal?: AbortSignal): Promise<Article[]> => {
try { try {
const response = await fetch(`/api/10_info?page=${page}`, { signal }); const response = await fetch(`/api/posts_info?page=${page}`, { signal });
if (!response.ok) { if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
} }
@@ -219,15 +219,23 @@ export interface Comment {
parent_comment_id: number; parent_comment_id: number;
} }
export const getComments = async (id: string | number): Promise<Comment[]> => { export interface CommentPage {
comments: Comment[];
total_pages: number;
}
export const getComments = async (id: string | number, page: number = 1): Promise<CommentPage> => {
try { try {
const response = await fetch(`/api/get/comment?id=${id}`); const response = await fetch(`/api/get/comment?id=${id}&page=${page}`);
if (!response.ok) { if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
} }
const json = await response.json(); const json = await response.json();
if (json.code === 1000 && Array.isArray(json.data)) { if (json.code === 1000 && json.data && Array.isArray(json.data.comments)) {
return json.data as Comment[]; return {
comments: json.data.comments as Comment[],
total_pages: Number(json.data.total_pages) || 0,
};
} }
throw new Error('Invalid response code or missing data'); throw new Error('Invalid response code or missing data');
} catch (error) { } catch (error) {

View File

@@ -12,7 +12,7 @@ import {
Toast, Toast,
ToastTitle, ToastTitle,
} from '@fluentui/react-components'; } from '@fluentui/react-components';
import { Dismiss24Regular, ArrowReply24Regular } from '@fluentui/react-icons'; import { Dismiss24Regular, ArrowReply24Regular, ArrowClockwise24Regular } from '@fluentui/react-icons';
import { getComments, postComment } from '../api'; import { getComments, postComment } from '../api';
import type { Comment as CommentType } from '../api'; import type { Comment as CommentType } from '../api';
import { useLayout } from '../context/LayoutContext'; import { useLayout } from '../context/LayoutContext';
@@ -91,6 +91,15 @@ const useStyles = makeStyles({
color: tokens.colorBrandForeground1, color: tokens.colorBrandForeground1,
textDecoration: 'none', textDecoration: 'none',
}, },
loadMore: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: tokens.spacingHorizontalXS,
color: tokens.colorBrandForeground1,
cursor: 'pointer',
marginTop: tokens.spacingVerticalS,
},
}); });
// 自定义 remark 插件,用于高亮 #tag // 自定义 remark 插件,用于高亮 #tag
@@ -137,17 +146,29 @@ const CommentSection: React.FC<CommentSectionProps> = ({ postId }) => {
const [nickname, setNickname] = useState(''); const [nickname, setNickname] = useState('');
const [replyTo, setReplyTo] = useState<CommentType | null>(null); const [replyTo, setReplyTo] = useState<CommentType | null>(null);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [loadingMore, setLoadingMore] = useState(false);
const [page, setPage] = useState(1);
const [totalPages, setTotalPages] = useState(0);
const commentCardRefs = useRef<Map<number, HTMLDivElement>>(new Map()); const commentCardRefs = useRef<Map<number, HTMLDivElement>>(new Map());
useEffect(() => { useEffect(() => {
fetchComments(); setComments([]);
setPage(1);
setTotalPages(0);
fetchComments(1, false);
}, [postId]); }, [postId]);
const fetchComments = async () => { const fetchComments = async (targetPage: number, append: boolean) => {
try { try {
setLoading(true); if (append) {
const data = await getComments(postId); setLoadingMore(true);
setComments(data as CommentType[]); } else {
setLoading(true);
}
const data = await getComments(postId, targetPage);
setComments(prev => (append ? [...prev, ...data.comments] : data.comments));
setPage(targetPage);
setTotalPages(data.total_pages || 0);
} catch (error) { } catch (error) {
dispatchToast( dispatchToast(
<Toast> <Toast>
@@ -157,7 +178,11 @@ const CommentSection: React.FC<CommentSectionProps> = ({ postId }) => {
); );
console.error('Error fetching comments:', error); console.error('Error fetching comments:', error);
} finally { } finally {
setLoading(false); if (append) {
setLoadingMore(false);
} else {
setLoading(false);
}
} }
}; };
@@ -188,7 +213,7 @@ const CommentSection: React.FC<CommentSectionProps> = ({ postId }) => {
); );
setContent(''); setContent('');
if (replyTo) setReplyTo(null); if (replyTo) setReplyTo(null);
fetchComments(); fetchComments(1, false);
} catch (error: any) { } catch (error: any) {
dispatchToast( dispatchToast(
<Toast> <Toast>
@@ -300,6 +325,18 @@ const CommentSection: React.FC<CommentSectionProps> = ({ postId }) => {
{loading && <Text>..</Text>} {loading && <Text>..</Text>}
{!loading && comments.length === 0 && <Text></Text>} {!loading && comments.length === 0 && <Text></Text>}
{renderComments()} {renderComments()}
{totalPages > 1 && page < totalPages && (
<div
className={styles.loadMore}
onClick={() => {
if (loading || loadingMore) return;
fetchComments(page + 1, true);
}}
>
<ArrowClockwise24Regular />
<Text size={200}></Text>
</div>
)}
</div> </div>
</div> </div>
); );