Files
kevisual-center/src/pages/users/page.tsx
abearxiong 66ee0d7f60 feat: implement logout on 401 response and update query handling
refactor: replace Button with div for consistent styling in AIEditorLink

refactor: update navigation handling in AppVersionList and remove LayoutMain wrapper

refactor: remove unused LayoutMain imports and components across various pages

fix: ensure user app list is set correctly in useUserAppStore

fix: update login URL format in AuthProvider

fix: adjust layout styles in EnvPage and other pages for better responsiveness

chore: update route definitions and create new routes for apps, config, domain, flowme, org, remote, token, user, and users

style: replace Button with div for delete confirmation in various components

fix: ensure correct handling of user profile image source
2026-02-22 03:24:14 +08:00

222 lines
6.9 KiB
TypeScript

'use client';
import React, { useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { useUserStore } from './store/user';
import { useAdminStore } from './store/admin';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/components/ui/popover';
import { Plus, Pencil, Trash2 } from 'lucide-react';
const TableList = () => {
const { list, setShowEdit, setFormData, getList } = useUserStore();
const { deleteUser } = useAdminStore();
const [openPopover, setOpenPopover] = React.useState<string | null>(null);
const handleDelete = async (id: string) => {
await deleteUser(id);
setOpenPopover(null);
getList();
};
return (
<div className="rounded-md border">
<Table>
<TableHeader>
<TableRow>
<TableHead>ID</TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{list.map((user) => (
<TableRow key={user.id}>
<TableCell>{user.id}</TableCell>
<TableCell>{user.username}</TableCell>
<TableCell>{user.description || '-'}</TableCell>
<TableCell className="flex gap-2">
<Button
variant="outline"
size="sm"
onClick={() => {
setShowEdit(true);
setFormData(user);
}}>
<Pencil className="w-4 h-4 mr-1" />
</Button>
<Popover
open={openPopover === user.id}
onOpenChange={(open) => setOpenPopover(open ? user.id : null)}
>
<PopoverTrigger>
<div
className="flex items-center px-4 py-2 bg-destructive text-white rounded cursor-pointer"
>
<Trash2 className="w-4 h-4 mr-1" />
</div>
</PopoverTrigger>
<PopoverContent className="w-auto p-4" align="start">
<div className="space-y-3">
<p className="text-sm font-medium"> "{user.username}"?</p>
<p className="text-xs text-muted-foreground"></p>
<div className="flex gap-2 justify-end">
<Button
variant="outline"
size="sm"
onClick={() => setOpenPopover(null)}
>
</Button>
<Button
variant="destructive"
size="sm"
onClick={() => handleDelete(user.id)}
>
</Button>
</div>
</div>
</PopoverContent>
</Popover>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
);
};
const FormModal = () => {
const { showEdit, setShowEdit, formData, setFormData, getList } = useUserStore();
const { createNewUser, updateUser } = useAdminStore();
const {
handleSubmit,
formState: { errors },
reset,
register,
} = useForm();
useEffect(() => {
if (!showEdit) return;
if (formData?.id) {
reset({ username: formData.username, description: formData.description });
} else {
reset({ username: '', description: '' });
}
}, [formData, showEdit, reset]);
const onSubmit = async (data: any) => {
let res;
if (formData?.id) {
res = await updateUser(formData.id, data);
} else {
res = await createNewUser(data);
}
if (res?.code === 200) {
setShowEdit(false);
setFormData({});
getList();
}
};
return (
<Dialog open={showEdit} onOpenChange={(open) => {
setShowEdit(open);
if (!open) setFormData({});
}}>
<DialogContent>
<DialogHeader>
<DialogTitle>{formData?.id ? '编辑用户' : '添加用户'}</DialogTitle>
</DialogHeader>
<div className="p-4">
<form className="w-full flex flex-col gap-4" onSubmit={handleSubmit(onSubmit)}>
<div className="flex flex-col gap-2">
<label className="text-sm font-medium"></label>
<Input
{...register('username', { required: '请输入用户名' })}
placeholder="请输入用户名"
className={errors.username ? "border-red-500" : ""}
/>
{errors.username && <span className="text-xs text-red-500">{errors.username.message as string}</span>}
</div>
<div className="flex flex-col gap-2">
<label className="text-sm font-medium"></label>
<Input
{...register('description')}
placeholder="请输入描述"
/>
</div>
{!formData?.id && (
<div className="flex flex-col gap-2">
<label className="text-sm font-medium"></label>
<Input
{...register('password', { required: '请输入密码' })}
type="password"
placeholder="请输入密码"
className={errors.password ? "border-red-500" : ""}
/>
{errors.password && <span className="text-xs text-red-500">{errors.password.message as string}</span>}
</div>
)}
<div className="flex gap-2 justify-end">
<Button type="button" variant="outline" onClick={() => setShowEdit(false)}>
</Button>
<Button type="submit"></Button>
</div>
</form>
</div>
</DialogContent>
</Dialog>
);
};
export const List = () => {
const { getList, setShowEdit, setFormData } = useUserStore();
useEffect(() => {
getList();
}, [getList]);
return (
<div className="p-4 w-full h-full overflow-auto">
<div className="flex mb-4">
<Button
onClick={() => {
setShowEdit(true);
setFormData({});
}}>
<Plus className="w-4 h-4 mr-1" />
</Button>
</div>
<TableList />
<FormModal />
</div>
);
};
export default List