234 lines
8.0 KiB
TypeScript
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>
|
|
)
|
|
}
|