Files
cunkebao_v3/Cunkebao/app/traffic-pool/components/pool-management.tsx
2025-07-07 17:08:27 +08:00

421 lines
14 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 { useState } from "react"
import { Card } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Textarea } from "@/components/ui/textarea"
import { Badge } from "@/components/ui/badge"
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { useToast } from "@/components/ui/use-toast"
import { Plus, Edit, Trash2, Users, Calendar, Tag } from "lucide-react"
interface TrafficPool {
id: string
name: string
description: string
userCount: number
tags: string[]
createdAt: string
status: "active" | "inactive"
priority: "high" | "medium" | "low"
}
interface PoolManagementProps {
pools: TrafficPool[]
onPoolCreate: (pool: Omit<TrafficPool, "id" | "createdAt" | "userCount">) => void
onPoolUpdate: (id: string, pool: Partial<TrafficPool>) => void
onPoolDelete: (id: string) => void
}
export function PoolManagement({ pools, onPoolCreate, onPoolUpdate, onPoolDelete }: PoolManagementProps) {
const { toast } = useToast()
const [showCreateDialog, setShowCreateDialog] = useState(false)
const [showEditDialog, setShowEditDialog] = useState(false)
const [editingPool, setEditingPool] = useState<TrafficPool | null>(null)
const [formData, setFormData] = useState({
name: "",
description: "",
tags: "",
status: "active" as "active" | "inactive",
priority: "medium" as "high" | "medium" | "low",
})
// 重置表单
const resetForm = () => {
setFormData({
name: "",
description: "",
tags: "",
status: "active",
priority: "medium",
})
}
// 处理创建流量池
const handleCreate = () => {
if (!formData.name.trim()) {
toast({
title: "请输入流量池名称",
variant: "destructive",
})
return
}
const newPool = {
name: formData.name.trim(),
description: formData.description.trim(),
tags: formData.tags
.split(",")
.map((tag) => tag.trim())
.filter((tag) => tag.length > 0),
status: formData.status,
priority: formData.priority,
}
onPoolCreate(newPool)
setShowCreateDialog(false)
resetForm()
toast({
title: "创建成功",
description: `流量池 "${newPool.name}" 已创建`,
})
}
// 处理编辑流量池
const handleEdit = (pool: TrafficPool) => {
setEditingPool(pool)
setFormData({
name: pool.name,
description: pool.description,
tags: pool.tags.join(", "),
status: pool.status,
priority: pool.priority,
})
setShowEditDialog(true)
}
// 处理更新流量池
const handleUpdate = () => {
if (!editingPool || !formData.name.trim()) {
toast({
title: "请输入流量池名称",
variant: "destructive",
})
return
}
const updatedPool = {
name: formData.name.trim(),
description: formData.description.trim(),
tags: formData.tags
.split(",")
.map((tag) => tag.trim())
.filter((tag) => tag.length > 0),
status: formData.status,
priority: formData.priority,
}
onPoolUpdate(editingPool.id, updatedPool)
setShowEditDialog(false)
setEditingPool(null)
resetForm()
toast({
title: "更新成功",
description: `流量池 "${updatedPool.name}" 已更新`,
})
}
// 处理删除流量池
const handleDelete = (pool: TrafficPool) => {
if (pool.userCount > 0) {
toast({
title: "无法删除",
description: "该流量池中还有用户,请先清空用户后再删除",
variant: "destructive",
})
return
}
if (confirm(`确定要删除流量池 "${pool.name}" 吗?此操作不可恢复。`)) {
onPoolDelete(pool.id)
toast({
title: "删除成功",
description: `流量池 "${pool.name}" 已删除`,
})
}
}
// 获取优先级颜色
const getPriorityColor = (priority: string) => {
switch (priority) {
case "high":
return "bg-red-100 text-red-800"
case "medium":
return "bg-blue-100 text-blue-800"
case "low":
return "bg-gray-100 text-gray-800"
default:
return "bg-gray-100 text-gray-800"
}
}
// 获取状态颜色
const getStatusColor = (status: string) => {
switch (status) {
case "active":
return "bg-green-100 text-green-800"
case "inactive":
return "bg-gray-100 text-gray-800"
default:
return "bg-gray-100 text-gray-800"
}
}
// 格式化日期
const formatDate = (dateString: string) => {
const date = new Date(dateString)
return new Intl.DateTimeFormat("zh-CN", {
year: "numeric",
month: "2-digit",
day: "2-digit",
}).format(date)
}
return (
<div className="space-y-4">
{/* 头部操作栏 */}
<div className="flex justify-between items-center">
<h3 className="text-lg font-medium"></h3>
<Button onClick={() => setShowCreateDialog(true)} size="sm">
<Plus className="h-4 w-4 mr-1" />
</Button>
</div>
{/* 流量池列表 */}
<div className="grid gap-4">
{pools.map((pool) => (
<Card key={pool.id} className="p-4">
<div className="flex justify-between items-start mb-3">
<div className="flex-1">
<div className="flex items-center space-x-2 mb-1">
<h4 className="font-medium text-lg">{pool.name}</h4>
<Badge className={getStatusColor(pool.status)}>{pool.status === "active" ? "活跃" : "停用"}</Badge>
<Badge className={getPriorityColor(pool.priority)}>
{pool.priority === "high" ? "高优先级" : pool.priority === "medium" ? "中优先级" : "低优先级"}
</Badge>
</div>
<p className="text-sm text-gray-600 mb-2">{pool.description}</p>
<div className="flex flex-wrap gap-1 mb-2">
{pool.tags.map((tag, index) => (
<Badge key={index} variant="outline" className="text-xs">
<Tag className="h-3 w-3 mr-1" />
{tag}
</Badge>
))}
</div>
</div>
<div className="flex items-center space-x-2">
<Button variant="outline" size="sm" onClick={() => handleEdit(pool)}>
<Edit className="h-4 w-4" />
</Button>
<Button variant="outline" size="sm" onClick={() => handleDelete(pool)}>
<Trash2 className="h-4 w-4" />
</Button>
</div>
</div>
{/* 统计信息 */}
<div className="flex items-center justify-between text-sm text-gray-500 pt-3 border-t">
<div className="flex items-center space-x-4">
<div className="flex items-center">
<Users className="h-4 w-4 mr-1" />
<span>{pool.userCount} </span>
</div>
<div className="flex items-center">
<Calendar className="h-4 w-4 mr-1" />
<span> {formatDate(pool.createdAt)}</span>
</div>
</div>
</div>
</Card>
))}
{pools.length === 0 && (
<Card className="p-8 text-center">
<div className="text-gray-500 mb-4"></div>
<Button onClick={() => setShowCreateDialog(true)} variant="outline">
<Plus className="h-4 w-4 mr-1" />
</Button>
</Card>
)}
</div>
{/* 创建流量池对话框 */}
<Dialog open={showCreateDialog} onOpenChange={setShowCreateDialog}>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle></DialogTitle>
</DialogHeader>
<div className="space-y-4 py-2">
<div className="space-y-2">
<label className="text-sm font-medium"> *</label>
<Input
placeholder="请输入流量池名称"
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium"></label>
<Textarea
placeholder="请输入流量池描述"
value={formData.description}
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
rows={3}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium"></label>
<Input
placeholder="请输入标签,用逗号分隔"
value={formData.tags}
onChange={(e) => setFormData({ ...formData, tags: e.target.value })}
/>
<div className="text-xs text-gray-500">,,</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<label className="text-sm font-medium"></label>
<Select
value={formData.status}
onValueChange={(value: "active" | "inactive") => setFormData({ ...formData, status: value })}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="active"></SelectItem>
<SelectItem value="inactive"></SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<label className="text-sm font-medium"></label>
<Select
value={formData.priority}
onValueChange={(value: "high" | "medium" | "low") => setFormData({ ...formData, priority: value })}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="high"></SelectItem>
<SelectItem value="medium"></SelectItem>
<SelectItem value="low"></SelectItem>
</SelectContent>
</Select>
</div>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setShowCreateDialog(false)}>
</Button>
<Button onClick={handleCreate}></Button>
</DialogFooter>
</DialogContent>
</Dialog>
{/* 编辑流量池对话框 */}
<Dialog open={showEditDialog} onOpenChange={setShowEditDialog}>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle></DialogTitle>
</DialogHeader>
<div className="space-y-4 py-2">
<div className="space-y-2">
<label className="text-sm font-medium"> *</label>
<Input
placeholder="请输入流量池名称"
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium"></label>
<Textarea
placeholder="请输入流量池描述"
value={formData.description}
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
rows={3}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium"></label>
<Input
placeholder="请输入标签,用逗号分隔"
value={formData.tags}
onChange={(e) => setFormData({ ...formData, tags: e.target.value })}
/>
<div className="text-xs text-gray-500">,,</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<label className="text-sm font-medium"></label>
<Select
value={formData.status}
onValueChange={(value: "active" | "inactive") => setFormData({ ...formData, status: value })}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="active"></SelectItem>
<SelectItem value="inactive"></SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<label className="text-sm font-medium"></label>
<Select
value={formData.priority}
onValueChange={(value: "high" | "medium" | "low") => setFormData({ ...formData, priority: value })}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="high"></SelectItem>
<SelectItem value="medium"></SelectItem>
<SelectItem value="low"></SelectItem>
</SelectContent>
</Select>
</div>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setShowEditDialog(false)}>
</Button>
<Button onClick={handleUpdate}></Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
)
}