lty/qy-lty-admin/components/users/user-detail-dialog.tsx
2026-03-17 13:17:02 +08:00

234 lines
8.0 KiB
TypeScript

"use client"
import { useState } from "react"
import { Button } from "@/components/ui/button"
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import { Badge } from "@/components/ui/badge"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Eye, Edit, Mail, Phone, Calendar, Shield, MapPin } from "lucide-react"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
export type User = {
id: string
name: string
email: string
role: string
status: "活跃" | "未激活" | "已禁用"
registeredAt: string
lastLogin?: string
phone?: string
address?: string
avatar?: string
permissions?: string[]
loginHistory?: Array<{
date: string
ip: string
device: string
}>
}
type UserDetailDialogProps = {
user: User
onEdit?: () => void
}
export function UserDetailDialog({ user, onEdit }: UserDetailDialogProps) {
const [open, setOpen] = useState(false)
const getStatusColor = (status: string) => {
switch (status) {
case "活跃":
return "bg-green-500"
case "未激活":
return "bg-gray-500"
case "已禁用":
return "bg-red-500"
default:
return "bg-gray-500"
}
}
const getInitials = (name: string) => {
return name
.split(" ")
.map((part) => part[0])
.join("")
.toUpperCase()
.substring(0, 2)
}
const getAvatarColor = (name: string) => {
const colors = [
"from-pink-500 to-purple-500",
"from-blue-500 to-teal-500",
"from-red-500 to-orange-500",
"from-green-500 to-emerald-500",
"from-purple-500 to-indigo-500",
]
// Simple hash function to pick a color based on name
const hash = name.split("").reduce((acc, char) => acc + char.charCodeAt(0), 0)
return colors[hash % colors.length]
}
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button variant="ghost" size="icon" className="hover:bg-blue-50 hover:text-blue-600">
<Eye className="h-4 w-4" />
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[600px]">
<DialogHeader>
<DialogTitle className="text-xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-purple-600 to-pink-600">
</DialogTitle>
<DialogDescription></DialogDescription>
</DialogHeader>
<div className="py-4">
<div className="flex items-center gap-4 mb-6">
<Avatar className="h-16 w-16 border-2 border-white shadow-md">
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback className={`bg-gradient-to-br ${getAvatarColor(user.name)} text-white text-lg`}>
{getInitials(user.name)}
</AvatarFallback>
</Avatar>
<div>
<h3 className="text-xl font-bold">{user.name}</h3>
<div className="flex items-center gap-2 mt-1">
<Badge className={getStatusColor(user.status)}>{user.status}</Badge>
<Badge className="bg-purple-500">{user.role}</Badge>
</div>
</div>
</div>
<Tabs defaultValue="info" className="w-full">
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="info"></TabsTrigger>
<TabsTrigger value="permissions"></TabsTrigger>
<TabsTrigger value="activity"></TabsTrigger>
</TabsList>
<TabsContent value="info" className="space-y-4 pt-4">
<div className="grid grid-cols-2 gap-4">
<div className="space-y-1">
<div className="flex items-center text-sm text-gray-500">
<Mail className="h-4 w-4 mr-2" />
</div>
<p className="font-medium">{user.email}</p>
</div>
<div className="space-y-1">
<div className="flex items-center text-sm text-gray-500">
<Phone className="h-4 w-4 mr-2" />
</div>
<p className="font-medium">{user.phone || "未设置"}</p>
</div>
<div className="space-y-1">
<div className="flex items-center text-sm text-gray-500">
<Calendar className="h-4 w-4 mr-2" />
</div>
<p className="font-medium">{user.registeredAt}</p>
</div>
<div className="space-y-1">
<div className="flex items-center text-sm text-gray-500">
<Calendar className="h-4 w-4 mr-2" />
</div>
<p className="font-medium">{user.lastLogin || "从未登录"}</p>
</div>
{user.address && (
<div className="space-y-1 col-span-2">
<div className="flex items-center text-sm text-gray-500">
<MapPin className="h-4 w-4 mr-2" />
</div>
<p className="font-medium">{user.address}</p>
</div>
)}
</div>
</TabsContent>
<TabsContent value="permissions" className="pt-4">
<div className="space-y-4">
<div className="flex items-center gap-2">
<Shield className="h-5 w-5 text-purple-500" />
<h3 className="font-medium">: {user.role}</h3>
</div>
<div className="grid grid-cols-2 gap-2">
{user.permissions ? (
user.permissions.map((permission, index) => (
<div key={index} className="flex items-center p-2 bg-gray-50 rounded-md">
<div className="h-2 w-2 rounded-full bg-purple-500 mr-2"></div>
<span className="text-sm">{permission}</span>
</div>
))
) : (
<p className="text-gray-500 col-span-2"></p>
)}
</div>
</div>
</TabsContent>
<TabsContent value="activity" className="pt-4">
<div className="space-y-4">
<h3 className="font-medium"></h3>
{user.loginHistory && user.loginHistory.length > 0 ? (
<div className="space-y-2">
{user.loginHistory.map((login, index) => (
<div key={index} className="flex justify-between items-center p-3 bg-gray-50 rounded-md">
<div>
<p className="font-medium">{login.date}</p>
<p className="text-sm text-gray-500">{login.device}</p>
</div>
<div className="text-sm text-gray-500">IP: {login.ip}</div>
</div>
))}
</div>
) : (
<p className="text-gray-500"></p>
)}
</div>
</TabsContent>
</Tabs>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setOpen(false)}>
</Button>
{onEdit && (
<Button
className="bg-gradient-to-r from-pink-500 to-purple-600 hover:from-pink-600 hover:to-purple-700"
onClick={() => {
setOpen(false)
onEdit()
}}
>
<Edit className="mr-2 h-4 w-4" />
</Button>
)}
</DialogFooter>
</DialogContent>
</Dialog>
)
}