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
222 lines
6.9 KiB
TypeScript
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 |