feat: 帖子分级置顶(板块/全站)与板块管理员置顶权限、消息管理权限调整
This commit is contained in:
@@ -46,7 +46,7 @@ export default function PostActions({
|
||||
const [user, setUser] = useState<User | null>(null);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [deleting, setDeleting] = useState(false);
|
||||
const [isPinned, setIsPinned] = useState(pinned > 0);
|
||||
const [pinLevel, setPinLevel] = useState(pinned);
|
||||
const [isRecommended, setIsRecommended] = useState(recommended);
|
||||
const [isLocked, setIsLocked] = useState(locked);
|
||||
const [acting, setActing] = useState(false);
|
||||
@@ -89,6 +89,13 @@ export default function PostActions({
|
||||
const showEdit = canManage && !(isLocked && !staffUser) && !editTimeLocked;
|
||||
const canApprove =
|
||||
!!user && status === "pending" && canModerateBoard(user, boardId);
|
||||
// 置顶分级:管理员及以上可设全局置顶(2)与板块置顶(1);板块管理员只在其授权板块做板块置顶。
|
||||
// 已是全站置顶的帖子,板块管理员不可操作,故不展示入口。
|
||||
const canGlobalPin = isAdmin;
|
||||
const canBoardPin = !!user && canModerateBoard(user, boardId);
|
||||
const showGlobalPin = canGlobalPin;
|
||||
const showBoardPin = canBoardPin && (isAdmin || pinLevel < 2);
|
||||
const showPin = showGlobalPin || showBoardPin;
|
||||
// staff 删他人帖:走类型+理由;作者自删:简单确认
|
||||
const staffDeletingOther =
|
||||
!!user && user.id !== authorId && canStaffSoftDelete(user, boardId, authorRole);
|
||||
@@ -126,16 +133,24 @@ export default function PostActions({
|
||||
}
|
||||
};
|
||||
|
||||
const handlePin = async () => {
|
||||
const handlePin = async (level: 1 | 2) => {
|
||||
setActing(true);
|
||||
setOpen(false);
|
||||
try {
|
||||
const res = await apiTogglePin(postId);
|
||||
setIsPinned(res.pinned > 0);
|
||||
toast(res.pinned > 0 ? "已置顶" : "已取消置顶", "ok");
|
||||
// 再次点击当前级别 = 取消置顶
|
||||
const next = pinLevel === level ? 0 : level;
|
||||
const res = await apiTogglePin(postId, next as 0 | 1 | 2);
|
||||
setPinLevel(res.pinned);
|
||||
const okMsg =
|
||||
res.pinned <= 0
|
||||
? "已取消置顶"
|
||||
: res.pinned === 2
|
||||
? "已全局置顶"
|
||||
: "已在本板块置顶";
|
||||
toast(okMsg, "ok");
|
||||
router.refresh();
|
||||
} catch {
|
||||
toast("操作失败,请稍后重试");
|
||||
} catch (e) {
|
||||
toast(e instanceof Error ? e.message : "操作失败,请稍后重试");
|
||||
} finally {
|
||||
setActing(false);
|
||||
}
|
||||
@@ -220,25 +235,44 @@ export default function PostActions({
|
||||
<div style={{ height: 1, background: "var(--line)" }} />
|
||||
</>
|
||||
)}
|
||||
{showPin && (
|
||||
<>
|
||||
{showGlobalPin && (
|
||||
<button
|
||||
type="button"
|
||||
role="menuitem"
|
||||
onClick={() => void handlePin(2)}
|
||||
className={itemCls}
|
||||
style={pinLevel === 2 ? { color: "var(--pin)" } : { color: "var(--ink)" }}
|
||||
>
|
||||
<Pin size={14} className="shrink-0" />
|
||||
<span className="flex-1 text-left">全局置顶</span>
|
||||
{pinLevel === 2 && <Check size={14} className="shrink-0" />}
|
||||
</button>
|
||||
)}
|
||||
{showBoardPin && (
|
||||
<button
|
||||
type="button"
|
||||
role="menuitem"
|
||||
onClick={() => void handlePin(1)}
|
||||
className={itemCls}
|
||||
style={pinLevel === 1 ? { color: "var(--pin)" } : { color: "var(--ink)" }}
|
||||
>
|
||||
<Pin size={14} className="shrink-0" />
|
||||
<span className="flex-1 text-left">板块置顶</span>
|
||||
{pinLevel === 1 && <Check size={14} className="shrink-0" />}
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{isAdmin && (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
role="menuitem"
|
||||
onClick={handlePin}
|
||||
className={itemCls}
|
||||
style={isPinned ? { color: "var(--accent)" } : { color: "var(--ink)" }}
|
||||
>
|
||||
<Pin size={14} className="shrink-0" />
|
||||
<span className="flex-1 text-left">{isPinned ? "取消置顶" : "置顶"}</span>
|
||||
{isPinned && <Check size={14} className="shrink-0" />}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
role="menuitem"
|
||||
onClick={handleRecommend}
|
||||
className={itemCls}
|
||||
style={isRecommended ? { color: "var(--gold)" } : { color: "var(--ink)" }}
|
||||
style={isRecommended ? { color: "var(--rec)" } : { color: "var(--ink)" }}
|
||||
>
|
||||
<Star size={14} className="shrink-0" />
|
||||
<span className="flex-1 text-left">{isRecommended ? "取消推荐" : "推荐"}</span>
|
||||
|
||||
Reference in New Issue
Block a user