实现用户注销和删除系统

This commit is contained in:
2025-05-18 22:25:05 +08:00
parent 4d9245aabb
commit 1baff0712a
9 changed files with 87 additions and 30 deletions

View File

@@ -75,7 +75,7 @@ export function CreateUserEditor({ children, onRefresh }: CreateUserEditorProps)
<Label htmlFor="password"></Label>
<Input id="password" name="password" />
</div>
<Button type="submit"></Button>
<Button type="submit"></Button>
</form>
<DrawerFooter className="pt-2">

View File

@@ -27,17 +27,17 @@ import {
} from "@/components/ui/alert"
import { AlertCircle } from "lucide-react";
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog";
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
export function UserInfoEditor({
onClose,
onUserUpdate,
onUserDelete,
onUserSoftDelete,
userId,
}: {
onClose: () => void,
onUserUpdate: (user: User) => void,
onUserDelete: (userId: string) => void,
onUserSoftDelete: (userId: string) => void,
userId: string
}) {
const { user, isLoading, error } = useUser(userId);
@@ -65,12 +65,12 @@ export function UserInfoEditor({
const handleRemove = async (userId: string) => {
try {
setRemoveLoading(true);
await AdminApi.user.remove(userId);
toast.success("删除成功");
onUserDelete(userId);
await AdminApi.user.remove(userId, true);
toast.success("注销成功");
onUserSoftDelete(userId);
onClose();
} catch (error) {
toast.error((error as Error).message || "删除失败");
toast.error((error as Error).message || "注销失败");
} finally {
setRemoveLoading(false);
}
@@ -208,18 +208,18 @@ function ProfileForm({ className, user, onSetPassword, onRemove, passwordDialogO
<AlertDialog>
<AlertDialogTrigger asChild>
<Button type="button" variant="destructive" className="flex-1"></Button>
<Button type="button" variant="destructive" className="flex-1"></Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>?</AlertDialogTitle>
<AlertDialogTitle>?</AlertDialogTitle>
<AlertDialogDescription>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel></AlertDialogCancel>
<AlertDialogAction onClick={() => onRemove(user.userId)}></AlertDialogAction>
<AlertDialogAction onClick={() => onRemove(user.userId)}></AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>

View File

@@ -8,13 +8,17 @@ import { useState } from "react";
import { UserInfoEditor } from "./components/user-info-editor";
import { User } from "@/lib/types/user";
import { CreateUserEditor } from "./components/create-user-editor";
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog";
import { AdminApi } from "@/lib/api";
import { toast } from "sonner";
import { ApiError } from "next/dist/server/api-utils";
export default function Page() {
const { users, isLoading, error, total, page, pageSize, mutate, refresh } = useUserList();
const [editorUserId, setEditorUserId] = useState("");
const handleUserUpdate = async (newUser: User) => {
const handleUserUpdateLocal = async (newUser: User) => {
await mutate(
(data) => {
if (!data) return data;
@@ -31,11 +35,14 @@ export default function Page() {
)
}
const handleUserDelete = async (userId: string) => {
const handleUserDeleteLocal = async (userId: string, soft: boolean) => {
await mutate(
(data) => {
if (!data) return data;
return {
return soft ? {
...data,
items: data.items.map(u => u.userId === userId ? { ...u, deletedAt: new Date().toLocaleString() } : u)
} : {
...data,
items: data.items.filter((user) => user.userId !== userId),
};
@@ -46,6 +53,18 @@ export default function Page() {
)
}
const [deletedUserId, setDeletedUserId] = useState('');
const handleUserDelete = async (userId: string) => {
try {
await AdminApi.user.remove(userId, false);
toast.success('删除成功');
handleUserDeleteLocal(userId, false);
setDeletedUserId('');
} catch (error) {
toast.error((error as ApiError).message || '删除失败');
}
}
return (
<>
<div>
@@ -86,7 +105,12 @@ export default function Page() {
<TableCell>{user.email}</TableCell>
<TableCell>{user.phone}</TableCell>
<TableCell>
<Button className="cursor-pointer" variant='outline' size='sm' onClick={() => setEditorUserId(user.userId)}></Button>
{user.deletedAt
? <Button className="cursor-pointer" variant='destructive' size='sm'
onClick={() => setDeletedUserId(user.userId)}></Button>
: <Button className="cursor-pointer" variant='outline' size='sm'
onClick={() => setEditorUserId(user.userId)}></Button>
}
</TableCell>
</TableRow>
))
@@ -104,14 +128,29 @@ export default function Page() {
</TableRow>
)}
</TableBody>
</Table>
</Table >
<UserInfoEditor
onClose={() => setEditorUserId('')}
userId={editorUserId}
onUserUpdate={handleUserUpdate}
onUserDelete={handleUserDelete}
onUserUpdate={handleUserUpdateLocal}
onUserSoftDelete={userId => handleUserDeleteLocal(userId, true)}
/>
<AlertDialog open={!!deletedUserId} onOpenChange={o => !o && setDeletedUserId('')}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>?</AlertDialogTitle>
<AlertDialogDescription>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel></AlertDialogCancel>
<AlertDialogAction onClick={() => handleUserDelete(deletedUserId)}></AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</>
)
}