feat: 本次提交更新内容如下
流量分发的功能构建完成
This commit is contained in:
@@ -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}`);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user