feat: 帖子分级置顶(板块/全站)与板块管理员置顶权限、消息管理权限调整

This commit is contained in:
2026-10-15 23:35:30 +08:00
parent e7b0686947
commit 47f87a011c
40 changed files with 822 additions and 207 deletions

View File

@@ -81,7 +81,7 @@ export interface User {
role: string;
// 板块管理员被授权的板块(仅 /api/me 返回;前端据此渲染审核入口/按钮)
board_ids?: number[];
// 站点消息管理(站长授予;站长/超管固有监管不必依赖此字段)
// 站点消息管理(仅站长可授;超管亦需授予,站长固有监管不必依赖此字段)
can_manage_messages?: boolean;
// 生效权限码(角色默认 ∪ 账号级渠道授予;仅 /api/me 返回,用于后台入口显隐)
perms?: string[];
@@ -2261,8 +2261,8 @@ export async function apiDeletePost(
return res.json();
}
export async function apiTogglePin(postId: string): Promise<{ pinned: number }> {
const res = await fetchWithRefresh(`/api/posts/${postId}/pin`, {
export async function apiTogglePin(postId: string, level: 0 | 1 | 2): Promise<{ pinned: number }> {
const res = await fetchWithRefresh(`/api/posts/${postId}/pin?level=${level}`, {
method: "PUT",
headers: clientHeaders(),
});
@@ -4059,6 +4059,38 @@ export const apiAdminRestoreComment = (id: number) =>
export const apiAdminPurgeComment = (id: number) =>
contentAction(`/api/admin/content/comments/${id}/purge`, "DELETE");
// ===== 置顶顺序配置 =====
export interface AdminPinnedPost {
id: number;
title: string;
pinned: number;
pin_order: number;
board_id: number;
board_name: string;
created_at: string;
}
export async function apiAdminPinnedPosts(
level: 1 | 2,
boardId?: number
): Promise<{ posts: AdminPinnedPost[] }> {
const params = new URLSearchParams({ level: String(level) });
if (boardId) params.set("board_id", String(boardId));
const res = await fetchWithRefresh(`/api/admin/pinned-posts?${params}`, {
headers: clientHeaders(),
});
const data = await res.json().catch(() => ({}));
if (!res.ok) throw new Error(data.error || "获取置顶列表失败");
return data;
}
export const apiAdminSavePinOrder = (level: 1 | 2, boardId: number, ids: number[]) =>
contentAction(`/api/admin/pinned-posts/order`, "PUT", {
level,
board_id: boardId,
ids,
});
export interface CommentEditHistoryItem {
id: number;
editor: {

View File

@@ -76,9 +76,11 @@ export const PERM_META: Record<
[PERMS.ADS]: { label: "广告与赞助", desc: "管理广告位与赞助配置、审批投放" },
};
/** 站长/超管:群聊监管旁路(可见全部群含私聊、撤回任意消息) */
export function canOverseeChat(role?: string | null): boolean {
return role === ROLES.OWNER || role === ROLES.SUPER_ADMIN;
/** 全站消息监管(可见全部群含私聊、撤回任意消息):站长固有;超管需先被授予「消息管理」 */
export function canOverseeChat(user?: RoleUserLike | null): boolean {
if (!user) return false;
if (isOwner(user.role)) return true;
return user.role === ROLES.SUPER_ADMIN && !!user.can_manage_messages;
}
/** 彻底删除帖/评:仅站长 */
@@ -152,10 +154,10 @@ export function canModerateAny(user: RoleUserLike | null | undefined): boolean {
return user.role === ROLES.BOARD_ADMIN && (user.board_ids?.length ?? 0) > 0;
}
/** 站点级消息管理(站长/超管固有,或 can_manage_messages) */
/** 站点级消息管理:站长固有;超管及其他角色需站长授予 can_manage_messages */
export function hasSiteMessagePerm(user: RoleUserLike | null | undefined): boolean {
if (!user) return false;
if (canOverseeChat(user.role)) return true;
if (isOwner(user.role)) return true;
return !!user.can_manage_messages;
}
@@ -201,7 +203,7 @@ export const ROLE_META: Record<RoleKey, RoleMeta> = {
colorToken: "--accent",
softToken: "--accent-soft",
grantDesc:
"用户与角色管理、封禁、外观主题、公告;全站内容软删与置顶/加精;群聊监管(可见全部会话、可撤回任意消息)。不可彻底删除内容,不可动站长账号,亦无站长专属的用户内容档案。",
"用户与角色管理、封禁、外观主题、公告;全站内容软删与置顶/加精;站长授予「消息管理」后方可进入后台消息管理并全站会话监管(可见全部会话、可撤回任意消息)。不可彻底删除内容,不可动站长账号,亦无站长专属的用户内容档案。",
},
[ROLES.ADMIN]: {
key: ROLES.ADMIN,
@@ -219,7 +221,7 @@ export const ROLE_META: Record<RoleKey, RoleMeta> = {
colorToken: "--accent",
softToken: "--accent-soft",
grantDesc:
"仅审核并软删被授权板块内普通用户的帖子与评论;不可处置管理员/站长发布的内容,无消息管理、公告、置顶/加精、用户管理与外观设置;须至少勾选一个板块。",
"仅审核并软删被授权板块内普通用户的帖子与评论;可在被授权板块内单独置顶帖子(板块置顶)。不可处置管理员/站长发布的内容,无消息管理、公告、全站置顶/加精、用户管理与外观设置;须至少勾选一个板块。",
},
[ROLES.USER]: {
key: ROLES.USER,