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

流量分发的功能构建完成
This commit is contained in:
笔记本里的永平
2025-07-10 17:05:13 +08:00
parent c87378fc66
commit cb253a568c
3 changed files with 86 additions and 71 deletions

View File

@@ -204,7 +204,7 @@ export const updateDistributionRule = async (id: string, params: any): Promise<A
* @returns 删除结果
*/
export const deleteDistributionRule = async (id: string): Promise<ApiResponse<any>> => {
return del<ApiResponse<any>>(`/v1/workbench/delete/${id}`);
return del<ApiResponse<any>>(`/v1/workbench/delete?id=${id}`);
};
/**

View File

@@ -1,13 +1,11 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useCallback } from 'react';
import { Search, Database } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { ScrollArea } from '@/components/ui/scroll-area';
import { Checkbox } from '@/components/ui/checkbox';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog';
import { useToast } from '@/components/ui/toast';
import { fetchDeviceLabels, type TrafficPool, type TrafficPoolListResponse } from '@/api/trafficDistribution';
import type { ApiResponse } from '@/types/common';
import { fetchDeviceLabels, type TrafficPool } from '@/api/trafficDistribution';
// 组件属性接口
interface TrafficPoolSelectionProps {
@@ -34,34 +32,8 @@ export default function TrafficPoolSelection({
const [loading, setLoading] = useState(false);
const { toast } = useToast();
// 当弹窗打开时获取流量池列表
useEffect(() => {
if (dialogOpen && deviceIds.length > 0) {
// 弹窗打开时重置搜索和页码,然后立即请求第一页数据
setSearchQuery('');
setCurrentPage(1);
fetchPools(1, '');
}
}, [dialogOpen, deviceIds]);
// 监听页码变化,重新请求数据
useEffect(() => {
if (dialogOpen && deviceIds.length > 0 && currentPage > 1) {
fetchPools(currentPage, searchQuery);
}
}, [currentPage]);
// 当设备ID变化时清空已选择的流量池如果需要的话
useEffect(() => {
if (deviceIds.length === 0) {
setPools([]);
setTotalPools(0);
setTotalPages(1);
}
}, [deviceIds]);
// 获取流量池列表API
const fetchPools = async (page: number, keyword: string = '') => {
const fetchPools = useCallback(async (page: number, keyword: string = '') => {
if (deviceIds.length === 0) return;
setLoading(true);
@@ -136,7 +108,33 @@ export default function TrafficPoolSelection({
} finally {
setLoading(false);
}
};
}, [deviceIds, toast]);
// 当弹窗打开时获取流量池列表
useEffect(() => {
if (dialogOpen && deviceIds.length > 0) {
// 弹窗打开时重置搜索和页码,然后立即请求第一页数据
setSearchQuery('');
setCurrentPage(1);
fetchPools(1, '');
}
}, [dialogOpen, deviceIds, fetchPools]);
// 监听页码变化,重新请求数据
useEffect(() => {
if (dialogOpen && deviceIds.length > 0 && currentPage > 1) {
fetchPools(currentPage, searchQuery);
}
}, [currentPage, dialogOpen, deviceIds.length, fetchPools, searchQuery]);
// 当设备ID变化时清空已选择的流量池如果需要的话
useEffect(() => {
if (deviceIds.length === 0) {
setPools([]);
setTotalPools(0);
setTotalPages(1);
}
}, [deviceIds]);
// 处理搜索
const handleSearch = (keyword: string) => {
@@ -167,16 +165,6 @@ export default function TrafficPoolSelection({
setDialogOpen(false);
};
// 处理弹窗关闭
const handleDialogClose = (open: boolean) => {
setDialogOpen(open);
if (!open) {
// 弹窗关闭时可以选择性清理状态,这里保留搜索状态以便下次打开时使用
// setSearchQuery('');
// setCurrentPage(1);
}
};
// 处理输入框点击
const handleInputClick = () => {
if (deviceIds.length === 0) {
@@ -207,9 +195,9 @@ export default function TrafficPoolSelection({
</div>
{/* 流量池选择弹窗 */}
<Dialog open={dialogOpen} onOpenChange={handleDialogClose}>
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
<DialogContent className="max-w-sm w-[90vw] max-h-[90vh] flex flex-col p-0 gap-0 overflow-hidden">
<div className="p-6">
<div>
<DialogTitle className="text-center text-xl font-medium mb-6"></DialogTitle>
<div className="relative mb-4">

View File

@@ -4,7 +4,6 @@ import { Steps, StepItem } from 'tdesign-mobile-react';
import {
Users,
Search,
Database,
X,
} from 'lucide-react';
import { Button } from '@/components/ui/button';
@@ -17,7 +16,7 @@ import Layout from '@/components/Layout';
import PageHeader from '@/components/PageHeader';
import DeviceSelection from '@/components/DeviceSelection';
import { useToast } from '@/components/ui/toast';
import { fetchAccountList, Account } from '@/api/trafficDistribution';
import { fetchAccountList, Account, createDistributionRule } from '@/api/trafficDistribution';
import '@/components/Layout.css';
import TrafficPoolSelection from '@/components/TrafficPoolSelection';
@@ -45,13 +44,6 @@ interface FormData {
trafficPool: Partial<TrafficPoolData>;
}
interface TrafficPool {
id: string;
name: string;
count: number;
description: string;
}
// 账号选择对话框组件
const AccountSelectionDialog = ({
open,
@@ -289,6 +281,20 @@ export default function NewDistribution() {
return `流量分发 ${year}${month}${day} ${hour}${minute}`;
};
// 转换分配方式为后端需要的格式
const getDistributeType = (distributionMethod: string | undefined) => {
switch (distributionMethod) {
case 'equal':
return 1; // 均分配
case 'priority':
return 2; // 优先级分配
case 'ratio':
return 3; // 比例分配
default:
return 1; // 默认均分配
}
};
const handleBasicInfoNext = (data: BasicInfoData) => {
setFormData(prev => ({ ...prev, basicInfo: data }));
setCurrentStep(1);
@@ -314,19 +320,48 @@ export default function NewDistribution() {
};
try {
console.log('提交的数据:', finalData);
// 构造API请求参数 - 按照后端要求的格式
const apiParams = {
type: 5, // 流量分发类型
name: finalData.basicInfo.name || '',
distributeType: getDistributeType(finalData.basicInfo.distributionMethod),
maxPerDay: finalData.basicInfo.dailyLimit || 50,
timeType: finalData.basicInfo.timeRestriction === 'allDay' ? 1 : 2,
startTime: finalData.basicInfo.timeRestriction === 'custom'
? (finalData.basicInfo.startTime || "09:00")
: "00:00",
endTime: finalData.basicInfo.timeRestriction === 'custom'
? (finalData.basicInfo.endTime || "18:00")
: "23:59",
devices: finalData.targetSettings?.selectedDevices || [],
account: finalData.basicInfo?.selectedAccounts || [],
pools: finalData.trafficPool?.selectedPools || [],
enabled: true // 默认启用
};
console.log('提交的数据:', apiParams);
toast({
title: "创建成功",
description: "流量分发规则已成功创建"
});
const response = await createDistributionRule(apiParams);
navigate('/workspace/traffic-distribution');
if (response.code === 200) {
toast({
title: "创建成功",
description: "流量分发规则已成功创建"
});
navigate('/workspace/traffic-distribution');
} else {
toast({
title: "创建失败",
description: response.msg || "请稍后重试",
variant: "destructive"
});
}
} catch (error) {
console.error('提交失败:', error);
toast({
title: "创建失败",
description: "请稍后重试",
description: "网络错误,请稍后重试",
variant: "destructive"
});
}
@@ -589,14 +624,6 @@ export default function NewDistribution() {
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = async () => {
if (selectedPools.length === 0) {
toast({
title: "请选择至少一个流量池",
variant: "destructive"
});
return;
}
setIsSubmitting(true);
try {
@@ -634,7 +661,7 @@ export default function NewDistribution() {
<Button variant="outline" onClick={onBack}>
</Button>
<Button onClick={handleSubmit} disabled={isSubmitting || selectedPools.length === 0}>
<Button onClick={handleSubmit} disabled={isSubmitting}>
{isSubmitting ? "提交中..." : "完成"}
</Button>
</div>