425 lines
15 KiB
TypeScript
425 lines
15 KiB
TypeScript
"use client"
|
||
|
||
import type React from "react"
|
||
|
||
import { useState, useEffect } from "react"
|
||
import { Button } from "@/components/ui/button"
|
||
import {
|
||
Dialog,
|
||
DialogContent,
|
||
DialogDescription,
|
||
DialogFooter,
|
||
DialogHeader,
|
||
DialogTitle,
|
||
DialogTrigger,
|
||
} from "@/components/ui/dialog"
|
||
import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"
|
||
import { Input } from "@/components/ui/input"
|
||
import { Textarea } from "@/components/ui/textarea"
|
||
import { Checkbox } from "@/components/ui/checkbox"
|
||
import { Loader2, Shield } from "lucide-react"
|
||
import { useForm } from "react-hook-form"
|
||
import { zodResolver } from "@hookform/resolvers/zod"
|
||
import * as z from "zod"
|
||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||
|
||
// 定义角色类型
|
||
export type Role = {
|
||
id: string
|
||
name: string
|
||
description: string
|
||
userCount: number
|
||
createdAt: string
|
||
status: "系统角色" | "自定义角色"
|
||
permissions: Record<string, boolean>
|
||
}
|
||
|
||
// 定义权限组
|
||
const permissionGroups = [
|
||
{
|
||
name: "仪表盘",
|
||
permissions: [
|
||
{ id: "dashboard.view", label: "仪表盘查看" },
|
||
{ id: "dashboard.edit", label: "仪表盘编辑" },
|
||
],
|
||
},
|
||
{
|
||
name: "用户管理",
|
||
permissions: [
|
||
{ id: "users.view", label: "查看用户" },
|
||
{ id: "users.create", label: "创建用户" },
|
||
{ id: "users.edit", label: "编辑用户" },
|
||
{ id: "users.delete", label: "删除用户" },
|
||
],
|
||
},
|
||
{
|
||
name: "角色权限",
|
||
permissions: [
|
||
{ id: "roles.view", label: "查看角色" },
|
||
{ id: "roles.create", label: "创建角色" },
|
||
{ id: "roles.edit", label: "编辑角色" },
|
||
{ id: "roles.delete", label: "删除角色" },
|
||
],
|
||
},
|
||
{
|
||
name: "AI模型",
|
||
permissions: [
|
||
{ id: "ai.view", label: "查看AI模型" },
|
||
{ id: "ai.create", label: "创建AI模型" },
|
||
{ id: "ai.edit", label: "编辑AI模型" },
|
||
{ id: "ai.delete", label: "删除AI模型" },
|
||
],
|
||
},
|
||
{
|
||
name: "内容管理",
|
||
permissions: [
|
||
{ id: "outfits.view", label: "查看服装" },
|
||
{ id: "outfits.create", label: "创建服装" },
|
||
{ id: "outfits.edit", label: "编辑服装" },
|
||
{ id: "outfits.delete", label: "删除服装" },
|
||
{ id: "props.view", label: "查看道具" },
|
||
{ id: "props.create", label: "创建道具" },
|
||
{ id: "props.edit", label: "编辑道具" },
|
||
{ id: "props.delete", label: "删除道具" },
|
||
{ id: "homeDecor.view", label: "查看家居装饰" },
|
||
{ id: "homeDecor.create", label: "创建家居装饰" },
|
||
{ id: "homeDecor.edit", label: "编辑家居装饰" },
|
||
{ id: "homeDecor.delete", label: "删除家居装饰" },
|
||
{ id: "food.view", label: "查看食物" },
|
||
{ id: "food.create", label: "创建食物" },
|
||
{ id: "food.edit", label: "编辑食物" },
|
||
{ id: "food.delete", label: "删除食物" },
|
||
{ id: "songs.view", label: "查看歌曲" },
|
||
{ id: "songs.create", label: "创建歌曲" },
|
||
{ id: "songs.edit", label: "编辑歌曲" },
|
||
{ id: "songs.delete", label: "删除歌曲" },
|
||
],
|
||
},
|
||
{
|
||
name: "系统设置",
|
||
permissions: [
|
||
{ id: "settings.view", label: "查看设置" },
|
||
{ id: "settings.edit", label: "编辑设置" },
|
||
],
|
||
},
|
||
{
|
||
name: "好感度系统",
|
||
permissions: [
|
||
{ id: "affinity.view", label: "查看好感度系统" },
|
||
{ id: "affinity.create", label: "创建好感度规则" },
|
||
{ id: "affinity.edit", label: "编辑好感度规则" },
|
||
{ id: "affinity.delete", label: "删除好感度规则" },
|
||
],
|
||
},
|
||
]
|
||
|
||
// 表单验证模式
|
||
const formSchema = z.object({
|
||
name: z
|
||
.string()
|
||
.min(2, {
|
||
message: "角色名称至少需要2个字符",
|
||
})
|
||
.max(30, {
|
||
message: "角色名称不能超过30个字符",
|
||
}),
|
||
description: z
|
||
.string()
|
||
.min(5, {
|
||
message: "描述至少需要5个字符",
|
||
})
|
||
.max(200, {
|
||
message: "描述不能超过200个字符",
|
||
}),
|
||
status: z.enum(["系统角色", "自定义角色"]),
|
||
permissions: z.record(z.boolean()).refine(
|
||
(data) => {
|
||
// 至少选择一个权限
|
||
return Object.values(data).some((value) => value === true)
|
||
},
|
||
{
|
||
message: "至少需要选择一个权限",
|
||
},
|
||
),
|
||
})
|
||
|
||
type RoleDialogProps = {
|
||
open?: boolean
|
||
onOpenChange?: (open: boolean) => void
|
||
onSubmit: (data: z.infer<typeof formSchema>) => Promise<void>
|
||
defaultValues?: Partial<z.infer<typeof formSchema>>
|
||
mode: "add" | "edit"
|
||
trigger?: React.ReactNode
|
||
}
|
||
|
||
export function RoleDialog({ open, onOpenChange, onSubmit, defaultValues, mode, trigger }: RoleDialogProps) {
|
||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||
const [dialogOpen, setDialogOpen] = useState(open || false)
|
||
|
||
// 初始化所有权限为false
|
||
const initialPermissions: Record<string, boolean> = {}
|
||
permissionGroups.forEach((group) => {
|
||
group.permissions.forEach((permission) => {
|
||
initialPermissions[permission.id] = false
|
||
})
|
||
})
|
||
|
||
const form = useForm<z.infer<typeof formSchema>>({
|
||
resolver: zodResolver(formSchema),
|
||
defaultValues: {
|
||
name: "",
|
||
description: "",
|
||
status: "自定义角色",
|
||
permissions: initialPermissions,
|
||
...defaultValues,
|
||
},
|
||
})
|
||
|
||
// 当defaultValues改变时重置表单
|
||
useEffect(() => {
|
||
if (defaultValues) {
|
||
form.reset({
|
||
name: defaultValues.name || "",
|
||
description: defaultValues.description || "",
|
||
status: defaultValues.status || "自定义角色",
|
||
permissions: {
|
||
...initialPermissions,
|
||
...defaultValues.permissions,
|
||
},
|
||
})
|
||
}
|
||
}, [defaultValues, form])
|
||
|
||
// 同步内部状态和外部状态
|
||
useEffect(() => {
|
||
if (open !== undefined) {
|
||
setDialogOpen(open)
|
||
}
|
||
}, [open])
|
||
|
||
const handleOpenChange = (newOpen: boolean) => {
|
||
setDialogOpen(newOpen)
|
||
if (onOpenChange) {
|
||
onOpenChange(newOpen)
|
||
}
|
||
|
||
// 如果对话框关闭,重置表单
|
||
if (!newOpen) {
|
||
form.reset()
|
||
}
|
||
}
|
||
|
||
const handleSubmit = async (data: z.infer<typeof formSchema>) => {
|
||
setIsSubmitting(true)
|
||
try {
|
||
await onSubmit(data)
|
||
handleOpenChange(false)
|
||
} catch (error) {
|
||
console.error("提交失败:", error)
|
||
} finally {
|
||
setIsSubmitting(false)
|
||
}
|
||
}
|
||
|
||
// 全选/取消全选某个组的所有权限
|
||
const toggleGroupPermissions = (groupName: string, value: boolean) => {
|
||
const group = permissionGroups.find((g) => g.name === groupName)
|
||
if (!group) return
|
||
|
||
const updatedPermissions = { ...form.getValues().permissions }
|
||
group.permissions.forEach((permission) => {
|
||
updatedPermissions[permission.id] = value
|
||
})
|
||
|
||
form.setValue("permissions", updatedPermissions, { shouldValidate: true })
|
||
}
|
||
|
||
// 检查一个组是否所有权限都被选中
|
||
const isGroupAllChecked = (groupName: string) => {
|
||
const group = permissionGroups.find((g) => g.name === groupName)
|
||
if (!group) return false
|
||
|
||
const permissions = form.getValues().permissions
|
||
return group.permissions.every((permission) => permissions[permission.id])
|
||
}
|
||
|
||
// 检查一个组是否有部分权限被选中
|
||
const isGroupPartiallyChecked = (groupName: string) => {
|
||
const group = permissionGroups.find((g) => g.name === groupName)
|
||
if (!group) return false
|
||
|
||
const permissions = form.getValues().permissions
|
||
const checkedCount = group.permissions.filter((permission) => permissions[permission.id]).length
|
||
|
||
return checkedCount > 0 && checkedCount < group.permissions.length
|
||
}
|
||
|
||
return (
|
||
<Dialog open={dialogOpen} onOpenChange={handleOpenChange}>
|
||
<DialogTrigger asChild>
|
||
{trigger || (
|
||
<Button className="bg-gradient-to-r from-pink-500 to-purple-600 hover:from-pink-600 hover:to-purple-700 transition-all duration-300 shadow-md hover:shadow-lg">
|
||
<Shield className="mr-2 h-4 w-4" />
|
||
添加角色
|
||
</Button>
|
||
)}
|
||
</DialogTrigger>
|
||
<DialogContent className="sm:max-w-[700px] max-h-[90vh] overflow-y-auto">
|
||
<DialogHeader>
|
||
<DialogTitle className="text-xl font-bold flex items-center">
|
||
<Shield className="mr-2 h-5 w-5 text-purple-600" />
|
||
<span className="bg-clip-text text-transparent bg-gradient-to-r from-purple-600 to-pink-600">
|
||
{mode === "add" ? "添加角色" : "编辑角色"}
|
||
</span>
|
||
</DialogTitle>
|
||
<DialogDescription>
|
||
{mode === "add" ? "创建新的系统角色并分配权限" : "修改角色信息和权限设置"}
|
||
</DialogDescription>
|
||
</DialogHeader>
|
||
<Form {...form}>
|
||
<form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-6">
|
||
<Tabs defaultValue="basic" className="w-full">
|
||
<TabsList className="grid w-full grid-cols-2">
|
||
<TabsTrigger value="basic">基本信息</TabsTrigger>
|
||
<TabsTrigger value="permissions">权限设置</TabsTrigger>
|
||
</TabsList>
|
||
<TabsContent value="basic" className="space-y-4 pt-4">
|
||
<FormField
|
||
control={form.control}
|
||
name="name"
|
||
render={({ field }) => (
|
||
<FormItem>
|
||
<FormLabel>角色名称</FormLabel>
|
||
<FormControl>
|
||
<Input placeholder="输入角色名称" {...field} />
|
||
</FormControl>
|
||
<FormDescription>角色名称将显示在系统中,请使用简洁明了的名称</FormDescription>
|
||
<FormMessage />
|
||
</FormItem>
|
||
)}
|
||
/>
|
||
<FormField
|
||
control={form.control}
|
||
name="description"
|
||
render={({ field }) => (
|
||
<FormItem>
|
||
<FormLabel>角色描述</FormLabel>
|
||
<FormControl>
|
||
<Textarea placeholder="输入角色描述" className="resize-none" {...field} />
|
||
</FormControl>
|
||
<FormDescription>详细描述该角色的职责和权限范围</FormDescription>
|
||
<FormMessage />
|
||
</FormItem>
|
||
)}
|
||
/>
|
||
<FormField
|
||
control={form.control}
|
||
name="status"
|
||
render={({ field }) => (
|
||
<FormItem>
|
||
<FormLabel>角色类型</FormLabel>
|
||
<div className="flex items-center space-x-4">
|
||
<FormControl>
|
||
<div className="flex items-center space-x-2">
|
||
<input
|
||
type="radio"
|
||
id="system-role"
|
||
value="系统角色"
|
||
checked={field.value === "系统角色"}
|
||
onChange={() => field.onChange("系统角色")}
|
||
className="h-4 w-4 text-purple-600 focus:ring-purple-500"
|
||
/>
|
||
<label htmlFor="system-role" className="text-sm font-medium">
|
||
系统角色
|
||
</label>
|
||
</div>
|
||
</FormControl>
|
||
<FormControl>
|
||
<div className="flex items-center space-x-2">
|
||
<input
|
||
type="radio"
|
||
id="custom-role"
|
||
value="自定义角色"
|
||
checked={field.value === "自定义角色"}
|
||
onChange={() => field.onChange("自定义角色")}
|
||
className="h-4 w-4 text-purple-600 focus:ring-purple-500"
|
||
/>
|
||
<label htmlFor="custom-role" className="text-sm font-medium">
|
||
自定义角色
|
||
</label>
|
||
</div>
|
||
</FormControl>
|
||
</div>
|
||
<FormDescription>系统角色不可删除,自定义角色可以随时修改或删除</FormDescription>
|
||
<FormMessage />
|
||
</FormItem>
|
||
)}
|
||
/>
|
||
</TabsContent>
|
||
<TabsContent value="permissions" className="pt-4">
|
||
<div className="space-y-6">
|
||
{permissionGroups.map((group) => (
|
||
<div key={group.name} className="border rounded-lg p-4">
|
||
<div className="flex items-center mb-3">
|
||
<Checkbox
|
||
id={`group-${group.name}`}
|
||
checked={isGroupAllChecked(group.name)}
|
||
onCheckedChange={(checked) => {
|
||
toggleGroupPermissions(group.name, checked === true)
|
||
}}
|
||
className={isGroupPartiallyChecked(group.name) ? "bg-purple-300" : ""}
|
||
/>
|
||
<label htmlFor={`group-${group.name}`} className="ml-2 text-sm font-medium text-purple-800">
|
||
{group.name}
|
||
</label>
|
||
</div>
|
||
<div className="grid grid-cols-2 gap-3 ml-6">
|
||
{group.permissions.map((permission) => (
|
||
<FormField
|
||
key={permission.id}
|
||
control={form.control}
|
||
name={`permissions.${permission.id}`}
|
||
render={({ field }) => (
|
||
<FormItem className="flex flex-row items-start space-x-3 space-y-0">
|
||
<FormControl>
|
||
<Checkbox checked={field.value} onCheckedChange={field.onChange} />
|
||
</FormControl>
|
||
<div className="space-y-1 leading-none">
|
||
<FormLabel className="text-sm font-normal">{permission.label}</FormLabel>
|
||
</div>
|
||
</FormItem>
|
||
)}
|
||
/>
|
||
))}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</TabsContent>
|
||
</Tabs>
|
||
<DialogFooter>
|
||
<Button type="button" variant="outline" onClick={() => handleOpenChange(false)} disabled={isSubmitting}>
|
||
取消
|
||
</Button>
|
||
<Button
|
||
type="submit"
|
||
disabled={isSubmitting}
|
||
className="bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700"
|
||
>
|
||
{isSubmitting ? (
|
||
<>
|
||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||
提交中...
|
||
</>
|
||
) : (
|
||
<>{mode === "add" ? "创建角色" : "保存修改"}</>
|
||
)}
|
||
</Button>
|
||
</DialogFooter>
|
||
</form>
|
||
</Form>
|
||
</DialogContent>
|
||
</Dialog>
|
||
)
|
||
}
|