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 删除结果 * @returns 删除结果
*/ */
export const deleteDistributionRule = async (id: string): Promise<ApiResponse<any>> => { 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 { Search, Database } from 'lucide-react';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { ScrollArea } from '@/components/ui/scroll-area'; import { ScrollArea } from '@/components/ui/scroll-area';
import { Checkbox } from '@/components/ui/checkbox'; import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { useToast } from '@/components/ui/toast'; import { useToast } from '@/components/ui/toast';
import { fetchDeviceLabels, type TrafficPool, type TrafficPoolListResponse } from '@/api/trafficDistribution'; import { fetchDeviceLabels, type TrafficPool } from '@/api/trafficDistribution';
import type { ApiResponse } from '@/types/common';
// 组件属性接口 // 组件属性接口
interface TrafficPoolSelectionProps { interface TrafficPoolSelectionProps {
@@ -34,34 +32,8 @@ export default function TrafficPoolSelection({
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const { toast } = useToast(); 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 // 获取流量池列表API
const fetchPools = async (page: number, keyword: string = '') => { const fetchPools = useCallback(async (page: number, keyword: string = '') => {
if (deviceIds.length === 0) return; if (deviceIds.length === 0) return;
setLoading(true); setLoading(true);
@@ -136,7 +108,33 @@ export default function TrafficPoolSelection({
} finally { } finally {
setLoading(false); 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) => { const handleSearch = (keyword: string) => {
@@ -167,16 +165,6 @@ export default function TrafficPoolSelection({
setDialogOpen(false); setDialogOpen(false);
}; };
// 处理弹窗关闭
const handleDialogClose = (open: boolean) => {
setDialogOpen(open);
if (!open) {
// 弹窗关闭时可以选择性清理状态,这里保留搜索状态以便下次打开时使用
// setSearchQuery('');
// setCurrentPage(1);
}
};
// 处理输入框点击 // 处理输入框点击
const handleInputClick = () => { const handleInputClick = () => {
if (deviceIds.length === 0) { if (deviceIds.length === 0) {
@@ -207,9 +195,9 @@ export default function TrafficPoolSelection({
</div> </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"> <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> <DialogTitle className="text-center text-xl font-medium mb-6"></DialogTitle>
<div className="relative mb-4"> <div className="relative mb-4">

View File

@@ -4,7 +4,6 @@ import { Steps, StepItem } from 'tdesign-mobile-react';
import { import {
Users, Users,
Search, Search,
Database,
X, X,
} from 'lucide-react'; } from 'lucide-react';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
@@ -17,7 +16,7 @@ import Layout from '@/components/Layout';
import PageHeader from '@/components/PageHeader'; import PageHeader from '@/components/PageHeader';
import DeviceSelection from '@/components/DeviceSelection'; import DeviceSelection from '@/components/DeviceSelection';
import { useToast } from '@/components/ui/toast'; import { useToast } from '@/components/ui/toast';
import { fetchAccountList, Account } from '@/api/trafficDistribution'; import { fetchAccountList, Account, createDistributionRule } from '@/api/trafficDistribution';
import '@/components/Layout.css'; import '@/components/Layout.css';
import TrafficPoolSelection from '@/components/TrafficPoolSelection'; import TrafficPoolSelection from '@/components/TrafficPoolSelection';
@@ -45,13 +44,6 @@ interface FormData {
trafficPool: Partial<TrafficPoolData>; trafficPool: Partial<TrafficPoolData>;
} }
interface TrafficPool {
id: string;
name: string;
count: number;
description: string;
}
// 账号选择对话框组件 // 账号选择对话框组件
const AccountSelectionDialog = ({ const AccountSelectionDialog = ({
open, open,
@@ -289,6 +281,20 @@ export default function NewDistribution() {
return `流量分发 ${year}${month}${day} ${hour}${minute}`; 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) => { const handleBasicInfoNext = (data: BasicInfoData) => {
setFormData(prev => ({ ...prev, basicInfo: data })); setFormData(prev => ({ ...prev, basicInfo: data }));
setCurrentStep(1); setCurrentStep(1);
@@ -314,19 +320,48 @@ export default function NewDistribution() {
}; };
try { 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({ const response = await createDistributionRule(apiParams);
title: "创建成功",
description: "流量分发规则已成功创建"
});
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) { } catch (error) {
console.error('提交失败:', error); console.error('提交失败:', error);
toast({ toast({
title: "创建失败", title: "创建失败",
description: "请稍后重试", description: "网络错误,请稍后重试",
variant: "destructive" variant: "destructive"
}); });
} }
@@ -589,14 +624,6 @@ export default function NewDistribution() {
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = async () => { const handleSubmit = async () => {
if (selectedPools.length === 0) {
toast({
title: "请选择至少一个流量池",
variant: "destructive"
});
return;
}
setIsSubmitting(true); setIsSubmitting(true);
try { try {
@@ -634,7 +661,7 @@ export default function NewDistribution() {
<Button variant="outline" onClick={onBack}> <Button variant="outline" onClick={onBack}>
</Button> </Button>
<Button onClick={handleSubmit} disabled={isSubmitting || selectedPools.length === 0}> <Button onClick={handleSubmit} disabled={isSubmitting}>
{isSubmitting ? "提交中..." : "完成"} {isSubmitting ? "提交中..." : "完成"}
</Button> </Button>
</div> </div>