feat: 评论/帖子编辑历史与编辑标记,内容锁定

- 新增 smartDiff 差异计算与 EditHistoryModal 编辑历史弹窗,替换原 CommentEditHistoryModal
- 新增 PostEditedMark 编辑标记展示
- 新增内容锁定机制(content_lock)防止并发编辑冲突
- 审核与通知服务适配
This commit is contained in:
2026-09-25 23:41:45 +08:00
parent cab8230803
commit 5270fee2b7
40 changed files with 2521 additions and 350 deletions

View File

@@ -3,7 +3,7 @@
import { useState, useEffect, useRef } from "react";
import { useRouter } from "next/navigation";
import Link from "next/link";
import { Bell, CheckCheck, Heart, MessageCircle, CheckCircle2, XCircle, AtSign, ClipboardList, Award } from "lucide-react";
import { Bell, CheckCheck, Heart, MessageCircle, CheckCircle2, XCircle, AtSign, ClipboardList, Award, AlertTriangle } from "lucide-react";
import {
apiFetchNotifications,
apiUnreadCount,
@@ -137,17 +137,22 @@ export default function NotificationBell({ initialUnread = 0 }: { initialUnread?
if (n.type === "pending_review") return "有新内容待审核";
if (n.type === "deleted") return "删除了你的帖子";
if (n.type === "badge") return "向你颁布了徽章";
if (n.type === "necro_reply") return "旧帖回复提醒";
return "点赞了你的帖子";
};
// 审核结果类通知:无互动对象语义,使用专用图标与语义色
const isApproval = (n: NotificationItem) =>
n.type === "approved" || n.type === "rejected" || n.type === "deleted";
// 系统提醒(ActorID=0):同样不渲染操作者链接
const isSystemNotice = (n: NotificationItem) => n.type === "necro_reply";
const iconStyle = (n: NotificationItem): { color: string; bg: string; node: React.ReactNode } => {
if (n.type === "approved")
return { color: "var(--ok)", bg: "var(--ok-soft)", node: <CheckCircle2 size={15} /> };
if (n.type === "rejected" || n.type === "deleted")
return { color: "var(--danger)", bg: "var(--danger-soft)", node: <XCircle size={15} /> };
if (n.type === "necro_reply")
return { color: "var(--danger)", bg: "var(--danger-soft)", node: <AlertTriangle size={15} /> };
if (n.type === "like")
return { color: "var(--clay)", bg: "var(--clay-soft)", node: <Heart size={15} /> };
if (n.type === "mention")
@@ -223,7 +228,7 @@ export default function NotificationBell({ initialUnread = 0 }: { initialUnread?
) : (
notifications.map((n) => {
const ic = iconStyle(n);
const approval = isApproval(n);
const approval = isApproval(n) || isSystemNotice(n);
return (
<button
key={n.id}