diff --git a/frontend/src/api/types.ts b/frontend/src/api/types.ts
index d26eb04..c076eab 100644
--- a/frontend/src/api/types.ts
+++ b/frontend/src/api/types.ts
@@ -147,7 +147,7 @@ export interface AdminDashboard {
export interface ForumLimits {
post_edit_window_hours: number;
- comment_edit_window_hours: number;
+ comment_edit_window_minutes: number;
rate_limit_post: number;
rate_limit_comment: number;
rate_limit_register: number;
@@ -176,7 +176,7 @@ export interface ForumLimitsPublic {
post_tags_max: number;
post_content_max: number;
comment_max: number;
- comment_edit_window_hours: number;
+ comment_edit_window_minutes: number;
search_keyword_min: number;
search_keyword_max: number;
page_size_default: number;
diff --git a/frontend/src/components/CommentThreadList.tsx b/frontend/src/components/CommentThreadList.tsx
index 00d13e7..fb62dd5 100644
--- a/frontend/src/components/CommentThreadList.tsx
+++ b/frontend/src/components/CommentThreadList.tsx
@@ -25,20 +25,21 @@ import {
} from '../utils/comment';
import { isTimeDiffSignificant } from '../utils/content';
import { useForumLimits } from '../hooks/useForumLimits';
+import { Tooltip } from './ui/Tooltip';
import UserLink from './UserLink';
function isCommentAuthor(c: Comment, user?: User | null): boolean {
return !!user && c.user_id > 0 && c.user_id === user.id;
}
-function canEditComment(c: Comment, user: User | null | undefined, windowHours: number): boolean {
+function canEditComment(c: Comment, user: User | null | undefined, windowMinutes: number): boolean {
if (!user) return false;
if (user.role === 'admin') return true;
if (!isCommentAuthor(c, user)) return false;
- if (windowHours <= 0) return true;
+ if (windowMinutes <= 0) return true;
const created = new Date(c.created_at).getTime();
if (Number.isNaN(created)) return false;
- return Date.now() - created <= windowHours * 3600_000;
+ return Date.now() - created <= windowMinutes * 60_000;
}
interface ItemProps {
@@ -84,7 +85,21 @@ function CommentItem({
const isReplying = replyToId === c.id;
const isEditing = editingId === c.id;
const isAdmin = currentUser?.role === 'admin';
- const canEdit = canEditComment(c, currentUser, limits.comment_edit_window_hours ?? 24);
+ const editWindowMinutes = limits.comment_edit_window_minutes ?? 3;
+ // 到期后强制重渲染,使「编辑」按钮自动消失
+ const [, setEditExpireTick] = useState(0);
+ useEffect(() => {
+ if (!currentUser || currentUser.role === 'admin') return;
+ if (!isCommentAuthor(c, currentUser)) return;
+ if (editWindowMinutes <= 0) return;
+ const created = new Date(c.created_at).getTime();
+ if (Number.isNaN(created)) return;
+ const remaining = created + editWindowMinutes * 60_000 - Date.now();
+ if (remaining <= 0) return;
+ const timer = window.setTimeout(() => setEditExpireTick(n => n + 1), remaining + 30);
+ return () => window.clearTimeout(timer);
+ }, [c.created_at, c.id, c.user_id, currentUser, editWindowMinutes]);
+ const canEdit = canEditComment(c, currentUser, editWindowMinutes);
const canDelete = isAdmin;
const canApprove = isAdmin
&& (c.status === 'pending' || c.status === 'rejected')
@@ -153,6 +168,11 @@ function CommentItem({
) : (
{nick}
)}
+ {!c.reply_to && (
+
+ #{c.floor}
+
+ )}
{hidden ? (
@@ -184,9 +204,6 @@ function CommentItem({
) : (
- {c.reply_target && (
- @{commentNick(c.reply_target)}
- )}
)}
@@ -195,10 +212,13 @@ function CommentItem({
{formatCommentDate(c.created_at)}
- {showEdited && · 已编辑}
+ {isAdmin && showEdited && · 已编辑}
{c.status === 'pending' && · 审核中}
{c.status === 'rejected' && · 未通过}
+ {c.reply_target && (
+ @{commentNick(c.reply_target)}
+ )}
{!hidden && !isEditing && canApprove && (