feat: 本次提交更新内容如下

流量分发列表页面完成
This commit is contained in:
笔记本里的永平
2025-07-10 12:00:07 +08:00
parent 2c1e0ab7ac
commit 8baf4169ce

View File

@@ -11,6 +11,7 @@ import {
Pause,
Play,
Users,
Filter,
} from 'lucide-react';
import { Card } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
@@ -83,31 +84,6 @@ export default function TrafficDistribution() {
navigate(`/workspace/traffic-distribution/${ruleId}`);
};
// 注释掉未使用的函数
/*
const handleCopy = async (ruleId: string) => {
const ruleToCopy = tasks.find((rule) => rule.id === ruleId);
if (ruleToCopy) {
try {
// 这里可以添加复制API调用
toast({
title: '复制成功',
description: '已成功复制分发规则',
});
// 重新加载列表
fetchData();
} catch (error) {
console.error('复制流量分发规则失败:', error);
toast({
title: '复制失败',
description: '操作失败,请稍后重试',
variant: 'destructive',
});
}
}
};
*/
const toggleRuleStatus = async (ruleId: string) => {
const rule = tasks.find((r) => r.id === ruleId);
if (!rule) return;
@@ -217,21 +193,6 @@ export default function TrafficDistribution() {
rule.name.toLowerCase().includes(searchTerm.toLowerCase()),
);
const getStatusColor = (status: number) => {
switch (status) {
case WorkbenchTaskStatus.RUNNING:
return 'bg-green-100 text-green-800';
case WorkbenchTaskStatus.PAUSED:
return 'bg-gray-100 text-gray-800';
case WorkbenchTaskStatus.COMPLETED:
return 'bg-blue-100 text-blue-800';
case WorkbenchTaskStatus.FAILED:
return 'bg-red-100 text-red-800';
default:
return 'bg-gray-100 text-gray-800';
}
};
const getStatusText = (status: number) => {
switch (status) {
case WorkbenchTaskStatus.RUNNING:
@@ -285,7 +246,7 @@ export default function TrafficDistribution() {
// 初始加载和搜索
useEffect(() => {
fetchData(1, searchTerm);
}, [searchTerm]); // 添加依赖项
}, []); // 初始加载时只执行一次
// 处理搜索
const handleSearch = () => {
@@ -297,136 +258,155 @@ export default function TrafficDistribution() {
fetchData();
};
// 页面头部右侧内容
const headerRightContent = (
<Button onClick={handleCreateNew}>
<Plus className="h-4 w-4 mr-1" />
</Button>
);
return (
<Layout
header={<PageHeader title="流量分发" defaultBackPath="/workspace" />}
header={
<PageHeader
title="流量分发"
defaultBackPath="/workspace"
rightContent={headerRightContent}
/>
}
footer={<BottomNav activeTab="workspace" />}
>
<div className="p-4 space-y-4">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<div className="relative">
<div className="bg-gray-50 min-h-screen pb-16">
<div className="p-4 space-y-4">
<div className="flex items-center space-x-2 bg-white p-3 rounded-lg shadow-sm">
<div className="relative flex-1">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400" size={16} />
<Input
placeholder="搜索规则名称"
placeholder="搜索计划名称"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="pl-9 h-10 w-48"
className="pl-9 h-10"
/>
</div>
<Button variant="outline" size="sm" onClick={handleSearch} className="h-10">
<Filter size={16} className="mr-1" />
</Button>
</div>
<div className="flex items-center space-x-2">
<Button variant="outline" size="icon" onClick={handleRefresh} className="h-10 w-10">
<Button variant="outline" size="sm" onClick={handleRefresh} className="h-10">
<RefreshCw size={16} />
</Button>
<Button onClick={handleCreateNew} className="h-10">
<Plus size={16} className="mr-1" />
</Button>
</div>
</div>
{isLoading ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{[1, 2, 3].map((i) => (
<Card key={i} className="p-4 animate-pulse">
<div className="h-6 bg-gray-200 rounded w-3/4 mb-2"></div>
<div className="h-4 bg-gray-200 rounded w-1/2 mb-4"></div>
<div className="flex justify-between items-center">
<div className="h-5 bg-gray-200 rounded w-1/4"></div>
<div className="h-5 bg-gray-200 rounded w-1/4"></div>
</div>
</Card>
))}
</div>
) : filteredRules.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{filteredRules.map((rule) => (
<Card key={rule.id} className="overflow-hidden">
<div className="p-4">
<div className="flex justify-between items-start">
<h3
className="font-medium text-lg cursor-pointer hover:text-blue-600 truncate max-w-[200px]"
onClick={() => handleView(rule.id)}
>
{rule.name}
</h3>
<CardMenu
rule={rule}
onEdit={() => handleEdit(rule.id)}
onToggleStatus={() => toggleRuleStatus(rule.id)}
onDelete={() => handleDelete(rule.id)}
/>
{isLoading ? (
<div className="space-y-4">
{[1, 2, 3].map((i) => (
<Card key={i} className="p-4 animate-pulse">
<div className="h-6 bg-gray-200 rounded w-3/4 mb-2"></div>
<div className="h-4 bg-gray-200 rounded w-1/2 mb-4"></div>
<div className="flex justify-between items-center">
<div className="h-5 bg-gray-200 rounded w-1/4"></div>
<div className="h-5 bg-gray-200 rounded w-1/4"></div>
</div>
<div className="mt-1 text-sm text-gray-500 flex items-center">
<Clock className="h-3.5 w-3.5 mr-1" />
{rule.createTime?.substring(0, 16) || '未知时间'}
</div>
<div className="mt-4 flex justify-between items-center">
<Badge className={getStatusColor(rule.status)}>
{getStatusText(rule.status)}
</Badge>
<div className="text-sm text-gray-500">
<span className="font-medium">{rule.distributedTraffic || 0}</span>
<span className="mx-1">/</span>
<span>{rule.totalTraffic || 0}</span>
</Card>
))}
</div>
) : filteredRules.length > 0 ? (
<div className="space-y-4">
{filteredRules.map((rule) => (
<Card key={rule.id} className="overflow-hidden">
<div className="p-4">
<div className="flex justify-between items-center">
<h3 className="font-medium text-lg"></h3>
<div className="flex items-center space-x-2">
<Badge className={rule.status === WorkbenchTaskStatus.RUNNING ? "bg-blue-100 text-blue-800" : "bg-gray-100 text-gray-800"}>
{getStatusText(rule.status)}
</Badge>
<Switch
checked={rule.status === WorkbenchTaskStatus.RUNNING}
onCheckedChange={() => toggleRuleStatus(rule.id)}
/>
<CardMenu
rule={rule}
onEdit={() => handleEdit(rule.id)}
onToggleStatus={() => toggleRuleStatus(rule.id)}
onDelete={() => handleDelete(rule.id)}
/>
</div>
</div>
<div className="grid grid-cols-3 gap-4 mt-4 border-b pb-4">
<div className="text-center">
<div className="text-2xl font-semibold">{rule.deviceCount || 2}</div>
<div className="text-xs text-gray-500"></div>
</div>
<div className="text-center">
<div className="text-2xl font-semibold">{rule.totalTraffic || 7}</div>
<div className="text-xs text-gray-500"></div>
</div>
<div className="text-center">
<div className="text-2xl font-semibold">ALL</div>
<div className="text-xs text-gray-500"></div>
</div>
</div>
<div className="grid grid-cols-2 gap-4 mt-4">
<div className="text-center">
<div className="text-2xl font-semibold">{rule.distributedTraffic || 119}</div>
<div className="text-xs text-gray-500"></div>
</div>
<div className="text-center">
<div className="text-2xl font-semibold">{rule.totalTraffic || 2}</div>
<div className="text-xs text-gray-500"></div>
</div>
</div>
<div className="mt-4 text-sm text-gray-500 flex items-center justify-between">
<div className="flex items-center">
<Clock className="h-4 w-4 mr-1" />
: {rule.lastDistributionTime?.substring(0, 16) || '2025-07-02 09:00'}
</div>
<div>: {rule.creator || '售前'}</div>
</div>
</div>
</div>
<div className="bg-gray-50 px-4 py-2 flex justify-between items-center">
<div className="text-sm text-gray-500 flex items-center">
<Users className="h-3.5 w-3.5 mr-1" />
{rule.deviceCount || 0}
</div>
<div className="flex items-center">
<Switch
checked={rule.status === WorkbenchTaskStatus.RUNNING}
onCheckedChange={() => toggleRuleStatus(rule.id)}
className="data-[state=checked]:bg-blue-600"
/>
</div>
</div>
</Card>
))}
</div>
) : (
<div className="text-center py-20">
<div className="text-gray-400 mb-2"></div>
<Button variant="outline" onClick={handleCreateNew}>
<Plus size={16} className="mr-1" />
</Button>
</div>
)}
{totalItems > 10 && (
<div className="flex justify-center mt-6">
<div className="flex space-x-1">
<Button
variant="outline"
size="sm"
disabled={currentPage === 1}
onClick={() => fetchData(currentPage - 1)}
>
</Button>
<div className="flex items-center px-3 text-sm">
{currentPage} {Math.ceil(totalItems / 10)}
</div>
<Button
variant="outline"
size="sm"
disabled={currentPage >= Math.ceil(totalItems / 10)}
onClick={() => fetchData(currentPage + 1)}
>
</Card>
))}
</div>
) : (
<div className="text-center py-20 bg-white rounded-lg shadow-sm">
<div className="text-gray-400 mb-2"></div>
<Button variant="outline" onClick={handleCreateNew}>
<Plus size={16} className="mr-1" />
</Button>
</div>
</div>
)}
)}
{totalItems > 10 && (
<div className="flex justify-center mt-6">
<div className="flex space-x-1">
<Button
variant="outline"
size="sm"
disabled={currentPage === 1}
onClick={() => fetchData(currentPage - 1)}
>
</Button>
<div className="flex items-center px-3 text-sm">
{currentPage} {Math.ceil(totalItems / 10)}
</div>
<Button
variant="outline"
size="sm"
disabled={currentPage >= Math.ceil(totalItems / 10)}
onClick={() => fetchData(currentPage + 1)}
>
</Button>
</div>
</div>
)}
</div>
</div>
</Layout>
);