151 lines
5.2 KiB
TypeScript
151 lines
5.2 KiB
TypeScript
import { Button } from '@/components/ui/button';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Textarea } from '@/components/ui/textarea';
|
|
import { Label } from '@/components/ui/label';
|
|
import { Checkbox } from '@/components/ui/checkbox';
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
} from '@/components/ui/dialog';
|
|
import { Shield } from 'lucide-react';
|
|
|
|
export interface RoleFormData {
|
|
name: string;
|
|
code: string;
|
|
description: string;
|
|
permission_ids: number[];
|
|
}
|
|
|
|
export interface PermissionOption {
|
|
id: number;
|
|
name: string;
|
|
code: string;
|
|
type?: string;
|
|
}
|
|
|
|
interface RoleFormDialogProps {
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
editingRole: any | null;
|
|
form: RoleFormData;
|
|
setForm: React.Dispatch<React.SetStateAction<RoleFormData>>;
|
|
saving: boolean;
|
|
onSubmit: () => void;
|
|
permissions: PermissionOption[];
|
|
}
|
|
|
|
export function RoleFormDialog({
|
|
open,
|
|
onOpenChange,
|
|
editingRole,
|
|
form,
|
|
setForm,
|
|
saving,
|
|
onSubmit,
|
|
permissions,
|
|
}: RoleFormDialogProps) {
|
|
const togglePermission = (id: number, checked: boolean) => {
|
|
setForm((prev) => {
|
|
const set = new Set(prev.permission_ids);
|
|
if (checked) {
|
|
set.add(id);
|
|
} else {
|
|
set.delete(id);
|
|
}
|
|
return { ...prev, permission_ids: Array.from(set) };
|
|
});
|
|
};
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
<DialogContent className="bg-slate-900 border-slate-800">
|
|
<DialogHeader>
|
|
<DialogTitle className="text-slate-100 flex items-center gap-2">
|
|
<Shield className="h-5 w-5 text-purple-400" />
|
|
{editingRole ? '编辑角色' : '新建角色'}
|
|
</DialogTitle>
|
|
<DialogDescription className="text-slate-500">
|
|
{editingRole ? '修改角色信息与权限' : '创建新的系统角色并分配权限'}
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<div className="space-y-4">
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div className="space-y-2">
|
|
<Label className="text-xs text-slate-500">角色名称</Label>
|
|
<Input
|
|
value={form.name}
|
|
onChange={(e) => setForm({ ...form, name: e.target.value })}
|
|
className="bg-slate-800/50 border-slate-700 text-slate-200 placeholder:text-slate-500"
|
|
placeholder="如:运维工程师"
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label className="text-xs text-slate-500">角色编码</Label>
|
|
<Input
|
|
value={form.code}
|
|
onChange={(e) => setForm({ ...form, code: e.target.value })}
|
|
disabled={!!editingRole}
|
|
className="bg-slate-800/50 border-slate-700 text-slate-200 placeholder:text-slate-500 disabled:opacity-60"
|
|
placeholder="如:ops_engineer"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label className="text-xs text-slate-500">描述</Label>
|
|
<Textarea
|
|
value={form.description}
|
|
onChange={(e) => setForm({ ...form, description: e.target.value })}
|
|
className="bg-slate-800/50 border-slate-700 text-slate-200 placeholder:text-slate-500 resize-none"
|
|
placeholder="角色职责描述(可选)"
|
|
rows={2}
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label className="text-xs text-slate-500">
|
|
权限分配
|
|
{form.permission_ids.length > 0 && (
|
|
<span className="ml-2 text-slate-400">已选 {form.permission_ids.length} 项</span>
|
|
)}
|
|
</Label>
|
|
<div className="max-h-48 overflow-y-auto rounded-lg border border-slate-800 bg-slate-800/30 p-3">
|
|
{permissions.length === 0 ? (
|
|
<p className="text-xs text-slate-500 text-center py-4">暂无可分配权限</p>
|
|
) : (
|
|
<div className="grid grid-cols-2 gap-2">
|
|
{permissions.map((perm) => (
|
|
<Label
|
|
key={perm.id}
|
|
className="flex items-center gap-2 text-xs text-slate-300 font-normal cursor-pointer hover:text-slate-100"
|
|
>
|
|
<Checkbox
|
|
checked={form.permission_ids.includes(perm.id)}
|
|
onCheckedChange={(checked) => togglePermission(perm.id, checked === true)}
|
|
className="border-slate-600 data-[state=checked]:bg-blue-600 data-[state=checked]:border-blue-600"
|
|
/>
|
|
<span className="truncate">{perm.name}</span>
|
|
</Label>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<DialogFooter>
|
|
<Button variant="outline" className="border-slate-700 text-slate-300" onClick={() => onOpenChange(false)}>
|
|
取消
|
|
</Button>
|
|
<Button className="bg-blue-600 hover:bg-blue-700 text-white" onClick={onSubmit} disabled={saving}>
|
|
{saving ? '保存中...' : '保存'}
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|