超管后台 - 删除管理员

This commit is contained in:
柳清爽
2025-04-10 16:34:02 +08:00
parent e81a75b67e
commit 8e5a636893
5 changed files with 173 additions and 7 deletions

View File

@@ -26,6 +26,8 @@ Route::group('', function () {
Route::post('update', 'app\\superadmin\\controller\\Administrator@updateAdmin');
// 添加管理员
Route::post('add', 'app\\superadmin\\controller\\Administrator@addAdmin');
// 删除管理员
Route::post('delete', 'app\\superadmin\\controller\\Administrator@deleteAdmin');
});
// 系统信息相关路由

View File

@@ -264,4 +264,65 @@ class Administrator extends Controller
'data' => null
]);
}
/**
* 删除管理员
* @return \think\response\Json
*/
public function deleteAdmin()
{
if (!$this->request->isPost()) {
return json(['code' => 405, 'msg' => '请求方法不允许']);
}
// 获取当前登录的管理员信息
$currentAdmin = $this->request->adminInfo;
// 获取请求参数
$id = $this->request->post('id/d');
// 参数验证
if (empty($id)) {
return json(['code' => 400, 'msg' => '参数不完整']);
}
// 不能删除自己的账号
if ($currentAdmin->id == $id) {
return json(['code' => 403, 'msg' => '不能删除自己的账号']);
}
// 只有超级管理员(ID为1)可以删除管理员
if ($currentAdmin->id != 1) {
return json(['code' => 403, 'msg' => '您没有权限删除管理员']);
}
// 不能删除超级管理员账号
if ($id == 1) {
return json(['code' => 403, 'msg' => '不能删除超级管理员账号']);
}
// 查询管理员
$admin = AdminModel::where('id', $id)->where('deleteTime', 0)->find();
if (!$admin) {
return json(['code' => 404, 'msg' => '管理员不存在']);
}
// 执行软删除
$admin->deleteTime = time();
$result = $admin->save();
if ($result) {
return json([
'code' => 200,
'msg' => '删除成功',
'data' => null
]);
} else {
return json([
'code' => 500,
'msg' => '删除失败',
'data' => null
]);
}
}
}

View File

@@ -9,7 +9,17 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigge
import { Search, MoreHorizontal, Edit, Trash, UserPlus, Loader2 } from "lucide-react"
import { Badge } from "@/components/ui/badge"
import { useToast } from "@/components/ui/use-toast"
import { getAdministrators, Administrator } from "@/lib/admin-api"
import { getAdministrators, deleteAdministrator, Administrator } from "@/lib/admin-api"
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "@/components/ui/alert-dialog"
// 保留原始示例数据,作为加载失败时的备用数据
const adminsData = [
@@ -54,11 +64,16 @@ const adminsData = [
export default function AdminsPage() {
const [searchTerm, setSearchTerm] = useState("")
const [isLoading, setIsLoading] = useState(true)
const [isDeleting, setIsDeleting] = useState(false)
const [administrators, setAdministrators] = useState<Administrator[]>([])
const [totalCount, setTotalCount] = useState(0)
const [currentPage, setCurrentPage] = useState(1)
const [pageSize] = useState(10)
const { toast } = useToast()
// 删除对话框状态
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false)
const [adminToDelete, setAdminToDelete] = useState<Administrator | null>(null)
// 加载管理员列表
useEffect(() => {
@@ -122,6 +137,50 @@ export default function AdminsPage() {
const isSuperAdmin = (id: number) => {
return id === 1
}
// 打开删除确认对话框
const openDeleteDialog = (admin: Administrator) => {
setAdminToDelete(admin)
setDeleteDialogOpen(true)
}
// 确认删除管理员
const confirmDelete = async () => {
if (!adminToDelete) return
setIsDeleting(true)
try {
const response = await deleteAdministrator(adminToDelete.id)
if (response.code === 200) {
toast({
title: "删除成功",
description: `管理员 ${adminToDelete.name} 已成功删除`,
variant: "success",
})
// 重新获取管理员列表
fetchAdministrators()
} else {
toast({
title: "删除失败",
description: response.msg || "请稍后重试",
variant: "destructive",
})
}
} catch (error) {
console.error("删除管理员出错:", error)
toast({
title: "删除失败",
description: "请检查网络连接后重试",
variant: "destructive",
})
} finally {
setIsDeleting(false)
setDeleteDialogOpen(false)
setAdminToDelete(null)
}
}
return (
<div className="space-y-6">
@@ -205,7 +264,10 @@ export default function AdminsPage() {
</Link>
</DropdownMenuItem>
{!isSuperAdmin(admin.id) && (
<DropdownMenuItem className="text-destructive">
<DropdownMenuItem
className="text-destructive"
onClick={() => openDeleteDialog(admin)}
>
<Trash className="mr-2 h-4 w-4" />
</DropdownMenuItem>
)}
@@ -248,6 +310,35 @@ export default function AdminsPage() {
</Button>
</div>
)}
{/* 删除确认对话框 */}
<AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle></AlertDialogTitle>
<AlertDialogDescription>
"{adminToDelete?.name}"
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel disabled={isDeleting}></AlertDialogCancel>
<AlertDialogAction
onClick={confirmDelete}
disabled={isDeleting}
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
>
{isDeleting ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
...
</>
) : (
"确认删除"
)}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
)
}

View File

@@ -10,7 +10,12 @@ const AlertDialog = AlertDialogPrimitive.Root
const AlertDialogTrigger = AlertDialogPrimitive.Trigger
const AlertDialogPortal = AlertDialogPrimitive.Portal
const AlertDialogPortal = ({
...props
}: AlertDialogPrimitive.AlertDialogPortalProps) => (
<AlertDialogPrimitive.Portal {...props} />
)
AlertDialogPortal.displayName = AlertDialogPrimitive.Portal.displayName
const AlertDialogOverlay = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
@@ -18,7 +23,7 @@ const AlertDialogOverlay = React.forwardRef<
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Overlay
className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
)}
{...props}
@@ -36,7 +41,7 @@ const AlertDialogContent = React.forwardRef<
<AlertDialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full",
className
)}
{...props}
@@ -128,8 +133,6 @@ AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
export {
AlertDialog,
AlertDialogPortal,
AlertDialogOverlay,
AlertDialogTrigger,
AlertDialogContent,
AlertDialogHeader,

View File

@@ -121,4 +121,13 @@ export async function addAdministrator(
}
): Promise<ApiResponse<null>> {
return apiRequest('/administrator/add', 'POST', data);
}
/**
* 删除管理员
* @param id 管理员ID
* @returns 删除结果
*/
export async function deleteAdministrator(id: number | string): Promise<ApiResponse<null>> {
return apiRequest('/administrator/delete', 'POST', { id });
}