Files
kevisual-center/src/app/org/page.tsx
abearxiong 30388533c0 feat: implement configuration management with CRUD operations
- Added configuration management page with table view and modal forms for adding/editing configurations.
- Integrated Zustand for state management of configurations.
- Implemented user management drawer for organizations with user addition/removal functionality.
- Created user management page with CRUD operations for users.
- Introduced admin store for user-related actions including user creation, deletion, and updates.
- Developed reusable drawer component for UI consistency across user management.
- Enhanced error handling and user feedback with toast notifications.
2026-01-26 20:51:35 +08:00

210 lines
6.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { useOrgStore } from './store';
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, Users } from 'lucide-react';
import { LayoutMain } from '@/modules/layout';
import { UserDrawer } from './components/UserDrawer';
const TableList = () => {
const { list, setShowEdit, setFormData, deleteData, setOrgId } = useOrgStore();
const [userDrawerOpen, setUserDrawerOpen] = useState(false);
const handleOpenUserDrawer = (org: any) => {
setOrgId(org.id);
setUserDrawerOpen(true);
};
return (
<div className="rounded-md border">
<Table>
<TableHeader>
<TableRow>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{list.map((org) => (
<TableRow key={org.id}>
<TableCell>{org.username}</TableCell>
<TableCell>{org.description || '-'}</TableCell>
<TableCell>{org.createdAt ? new Date(org.createdAt).toLocaleString() : '-'}</TableCell>
<TableCell>{org.updatedAt ? new Date(org.updatedAt).toLocaleString() : '-'}</TableCell>
<TableCell className="flex gap-2">
<Button
variant="outline"
size="sm"
onClick={() => handleOpenUserDrawer(org)}>
<Users className="w-4 h-4 mr-1" />
</Button>
<Button
variant="outline"
size="sm"
onClick={() => {
setShowEdit(true);
setFormData(org);
}}>
<Pencil className="w-4 h-4 mr-1" />
</Button>
<Popover>
<PopoverTrigger asChild>
<Button
variant="destructive"
size="sm">
<Trash2 className="w-4 h-4 mr-1" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-48 p-2">
<div className="text-sm text-center mb-2"></div>
<div className="flex gap-2 justify-center">
<Button
variant="outline"
size="sm"
onClick={(e) => {
e.stopPropagation();
deleteData(org.id);
}}>
</Button>
<Button
variant="ghost"
size="sm"
onClick={(e) => e.stopPropagation()}>
</Button>
</div>
</PopoverContent>
</Popover>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<UserDrawer open={userDrawerOpen} onOpenChange={setUserDrawerOpen} />
</div>
);
};
const FormModal = () => {
const { showEdit, setShowEdit, formData, setFormData, updateData } = useOrgStore();
const {
handleSubmit,
formState: { errors },
reset,
register,
} = useForm();
useEffect(() => {
if (!showEdit) return;
if (formData?.id) {
reset(formData);
} else {
reset({ username: '', description: '' });
}
}, [formData, showEdit, reset]);
const onSubmit = async (data: any) => {
const res = await updateData(data);
if (res.code === 200) {
setShowEdit(false);
setFormData({});
}
};
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>
<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 } = useOrgStore();
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 () => {
return <LayoutMain><List /></LayoutMain>;
}