From 3f43b6d38814d421a907b5643d5442bbd8741873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Thu, 10 Jul 2025 18:00:33 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90=E4=BA=A4?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B=20?= =?UTF-8?q?=E6=B5=81=E9=87=8F=E5=88=86=E5=8F=91=E6=9E=84=E5=BB=BA=E5=AE=8C?= =?UTF-8?q?=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/src/App.tsx | 1 + nkebao/src/api/trafficDistribution.ts | 51 +- .../traffic-distribution/NewDistribution.tsx | 489 +++++++++++------- .../TrafficDistribution.tsx | 19 +- 4 files changed, 349 insertions(+), 211 deletions(-) diff --git a/nkebao/src/App.tsx b/nkebao/src/App.tsx index e6a66d30..f1e3347f 100644 --- a/nkebao/src/App.tsx +++ b/nkebao/src/App.tsx @@ -65,6 +65,7 @@ function App() { } /> } /> } /> + } /> } /> } /> } /> diff --git a/nkebao/src/api/trafficDistribution.ts b/nkebao/src/api/trafficDistribution.ts index 9e05fe02..b082f44b 100644 --- a/nkebao/src/api/trafficDistribution.ts +++ b/nkebao/src/api/trafficDistribution.ts @@ -57,31 +57,39 @@ export interface TrafficPoolListResponse { // 流量分发规则类型 export interface DistributionRule { - id: string; + id: number; name: string; + type: number; status: number; - deviceCount: number; - totalTraffic: number; - distributedTraffic: number; - lastDistributionTime: string; + autoStart: number; createTime: string; - creator: string; - distributionInterval: number; - maxDistributionPerDay: number; - timeRange: { start: string; end: string }; - targetChannels?: string[]; - distributionRatio?: Record; - priority?: 'high' | 'medium' | 'low'; - filterConditions?: string[]; + updateTime: string; + companyId: number; config?: { - total?: { + id: number; + workbenchId: number; + distributeType: number; // 1-均分配, 2-优先级分配, 3-比例分配 + maxPerDay: number; // 每日最大分配量 + timeType: number; // 1-全天, 2-自定义时间段 + startTime: string; // 开始时间 + endTime: string; // 结束时间 + account: string[]; // 账号列表 + devices: string[]; // 设备列表 + pools: string[]; // 流量池列表 + createTime: string; + updateTime: string; + lastUpdated: string; + total: { dailyAverage: number; // 日均分发量 - deviceCount: number; // 分发设备 - poolCount: string; // 流量池 - totalAccounts: number; // 分发账户 + totalAccounts: number; // 分发账户总数 + deviceCount: number; // 分发设备数量 + poolCount: number; // 流量池数量 totalUsers: number; // 总用户数 - } + }; }; + auto_like?: any; + moments_sync?: any; + group_push?: any; } // 流量分发列表响应类型 @@ -170,7 +178,7 @@ export const fetchDistributionRules = async (params: { * @returns 流量分发规则详情 */ export const fetchDistributionRuleDetail = async (id: string): Promise> => { - return get>(`/v1/workbench/detail/${id}`); + return get>(`/v1/workbench/detail?id=${id}`); }; /** @@ -191,8 +199,9 @@ export const createDistributionRule = async (params: any): Promise> => { - return put>(`/v1/workbench/update/${id}`, { +export const updateDistributionRule = async (id : string, params: any): Promise> => { + return post>(`/v1/workbench/update`, { + id: id, ...params, type: WorkbenchTaskType.TRAFFIC_DISTRIBUTION }); diff --git a/nkebao/src/pages/workspace/traffic-distribution/NewDistribution.tsx b/nkebao/src/pages/workspace/traffic-distribution/NewDistribution.tsx index 4c09dccc..4b664227 100644 --- a/nkebao/src/pages/workspace/traffic-distribution/NewDistribution.tsx +++ b/nkebao/src/pages/workspace/traffic-distribution/NewDistribution.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect, useCallback } from 'react'; -import { useNavigate } from 'react-router-dom'; +import { useNavigate, useParams } from 'react-router-dom'; import { Steps, StepItem } from 'tdesign-mobile-react'; import { Users, @@ -16,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, createDistributionRule } from '@/api/trafficDistribution'; +import { fetchAccountList, Account, createDistributionRule, updateDistributionRule, fetchDistributionRuleDetail } from '@/api/trafficDistribution'; import '@/components/Layout.css'; import TrafficPoolSelection from '@/components/TrafficPoolSelection'; @@ -248,7 +248,7 @@ const AccountSelectionDialog = ({ > 确认 - + ); @@ -256,8 +256,11 @@ const AccountSelectionDialog = ({ export default function NewDistribution() { const navigate = useNavigate(); + const { id: ruleId } = useParams<{ id: string }>(); const { toast } = useToast(); const [currentStep, setCurrentStep] = useState(0); + const [isEditMode, setIsEditMode] = useState(false); + const [loading, setLoading] = useState(false); const [formData, setFormData] = useState({ basicInfo: {}, targetSettings: {}, @@ -270,6 +273,100 @@ export default function NewDistribution() { { title: "流量池选择", content: "step3" }, ]; + // 加载规则数据 + const loadRuleData = useCallback(async (id: string) => { + console.log('开始加载规则数据,ID:', id); + setLoading(true); + try { + console.log('调用API: /v1/workbench/detail?id=' + id); + const response = await fetchDistributionRuleDetail(id); + + if (response.code === 200 && response.data) { + const rule = response.data; + const config = rule.config as any || {}; + + console.log('接收到的详情数据:', rule); + console.log('config数据:', config); + + // 转换分配类型:1-均分配, 2-优先级分配, 3-比例分配 + const getDistributionMethod = (distributeType: number) => { + switch (distributeType) { + case 2: return 'priority'; + case 3: return 'ratio'; + default: return 'equal'; + } + }; + + // 转换时间限制类型:1-全天, 2-自定义时间段 + const timeRestriction = config?.timeType === 1 ? 'allDay' : 'custom'; + + // 转换后端数据为前端表单格式 + setFormData({ + basicInfo: { + name: rule.name || '', + distributionMethod: getDistributionMethod(config?.distributeType), + dailyLimit: config?.maxPerDay || 50, + timeRestriction: timeRestriction, + startTime: config?.startTime || '09:00', + endTime: config?.endTime || '18:00', + selectedAccounts: config?.account || [], + }, + targetSettings: { + selectedDevices: config?.devices || [], + }, + trafficPool: { + selectedPools: config?.pools || [], + }, + }); + + console.log('转换后的表单数据:', { + basicInfo: { + name: rule.name || '', + distributionMethod: getDistributionMethod(config?.distributeType), + dailyLimit: config?.maxPerDay || 50, + timeRestriction: timeRestriction, + startTime: config?.startTime || '09:00', + endTime: config?.endTime || '18:00', + selectedAccounts: config?.account || [], + }, + targetSettings: { + selectedDevices: config?.devices || [], + }, + trafficPool: { + selectedPools: config?.pools || [], + }, + }); + } else { + toast({ + title: "加载失败", + description: response.msg || "无法加载规则数据", + variant: "destructive" + }); + navigate('/workspace/traffic-distribution'); + } + } catch (error) { + console.error('加载规则数据失败:', error); + toast({ + title: "加载失败", + description: "网络错误,请稍后重试", + variant: "destructive" + }); + navigate('/workspace/traffic-distribution'); + } finally { + setLoading(false); + } + }, [toast, navigate]); + + // 检查是否为编辑模式并加载数据 + useEffect(() => { + console.log('检查编辑模式 - ruleId:', ruleId); + if (ruleId) { + console.log('进入编辑模式,加载数据...'); + setIsEditMode(true); + loadRuleData(ruleId); + } + }, [ruleId, loadRuleData]); + // 生成默认计划名称 const generateDefaultName = () => { const now = new Date(); @@ -341,18 +438,25 @@ export default function NewDistribution() { console.log('提交的数据:', apiParams); - const response = await createDistributionRule(apiParams); + let response; + if (isEditMode && ruleId) { + // 编辑模式 - 使用更新接口 + response = await updateDistributionRule(ruleId, apiParams); + } else { + // 创建模式 - 使用创建接口 + response = await createDistributionRule(apiParams); + } if (response.code === 200) { toast({ - title: "创建成功", - description: "流量分发规则已成功创建" + title: isEditMode ? "更新成功" : "创建成功", + description: isEditMode ? "流量分发规则已成功更新" : "流量分发规则已成功创建" }); navigate('/workspace/traffic-distribution'); } else { toast({ - title: "创建失败", + title: isEditMode ? "更新失败" : "创建失败", description: response.msg || "请稍后重试", variant: "destructive" }); @@ -360,27 +464,43 @@ export default function NewDistribution() { } catch (error) { console.error('提交失败:', error); toast({ - title: "创建失败", + title: isEditMode ? "更新失败" : "创建失败", description: "网络错误,请稍后重试", variant: "destructive" }); } }; - // 基本信息步骤组件 +// 基本信息步骤组件 const BasicInfoStep = ({ onNext, initialData = {} }: { onNext: (data: BasicInfoData) => void; initialData?: Partial }) => { - const [formData, setFormData] = useState({ - name: initialData.name || generateDefaultName(), - distributionMethod: initialData.distributionMethod || "equal", - dailyLimit: initialData.dailyLimit || 50, - timeRestriction: initialData.timeRestriction || "custom", - startTime: initialData.startTime || "09:00", - endTime: initialData.endTime || "18:00", + const [formData, setFormData] = useState({ + name: initialData.name || (isEditMode ? '' : generateDefaultName()), + distributionMethod: initialData.distributionMethod || "equal", + dailyLimit: initialData.dailyLimit || 50, + timeRestriction: initialData.timeRestriction || "custom", + startTime: initialData.startTime || "09:00", + endTime: initialData.endTime || "18:00", selectedAccounts: initialData.selectedAccounts || [], }); const [accountDialogOpen, setAccountDialogOpen] = useState(false); + // 当编辑模式下initialData变化时,更新表单数据 + useEffect(() => { + if (initialData.name) { + setFormData(prev => ({ + ...prev, + name: initialData.name || '', + distributionMethod: initialData.distributionMethod || prev.distributionMethod, + dailyLimit: initialData.dailyLimit || prev.dailyLimit, + timeRestriction: initialData.timeRestriction || prev.timeRestriction, + startTime: initialData.startTime || prev.startTime, + endTime: initialData.endTime || prev.endTime, + selectedAccounts: initialData.selectedAccounts || prev.selectedAccounts, + })); + } + }, [initialData]); + const handleChange = (field: keyof BasicInfoData, value: string | number | string[]) => { setFormData(prev => ({ ...prev, [field]: value })); }; @@ -394,9 +514,9 @@ export default function NewDistribution() { return "请选择账号"; } return `已选择 ${formData.selectedAccounts.length} 个账号`; - }; + }; - const handleSubmit = () => { + const handleSubmit = () => { if (!formData.name.trim()) { toast({ title: "请填写计划名称", @@ -411,26 +531,26 @@ export default function NewDistribution() { }); return; } - onNext(formData); - }; + onNext(formData); + }; - return ( + return (
-

基本信息

+

基本信息

-
-
- - handleChange("name", e.target.value)} - placeholder="请输入计划名称" +
+
+ + handleChange("name", e.target.value)} + placeholder="请输入计划名称" className="h-12" - /> -
+ /> +
-
+
+ +
+ + handleChange("distributionMethod", value as 'equal' | 'priority' | 'ratio')} + className="space-y-2" + > +
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
- - handleChange("distributionMethod", value as 'equal' | 'priority' | 'ratio')} - className="space-y-2" - > -
- - -
-
- - -
-
- - -
-
-
- -
- - -
-
- 每日最大分配量 - {formData.dailyLimit} 人/天 -
- handleChange("dailyLimit", parseInt(e.target.value))} - className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer" +
+ 每日最大分配量 + {formData.dailyLimit} 人/天 +
+ handleChange("dailyLimit", parseInt(e.target.value))} + className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer" style={{ background: `linear-gradient(to right, #3b82f6 0%, #3b82f6 ${(formData.dailyLimit / 200) * 100}%, #e5e7eb ${(formData.dailyLimit / 200) * 100}%, #e5e7eb 100%)` }} - /> -

限制每天最多分配的流量数量

-
+ /> +

限制每天最多分配的流量数量

+
-
- - + + handleChange("timeRestriction", value as 'allDay' | 'custom')} - className="space-y-4" - > -
- - -
-
- - -
-
+ className="space-y-4" + > +
+ + +
+
+ + +
+
- {formData.timeRestriction === "custom" && ( -
-
- + {formData.timeRestriction === "custom" && ( +
+
+ handleChange("startTime", e.target.value)} className="h-12" /> -
-
- +
+
+ handleChange("endTime", e.target.value)} className="h-12" /> -
- )} -
+
+ )}
+
-
- -
+
+ +
- + ); }; @@ -580,7 +700,7 @@ export default function NewDistribution() { const TargetSettingsStep = ({ onNext, onBack, initialData = {} }: { onNext: (data: TargetSettingsData) => void; onBack: () => void; initialData?: Partial }) => { const [selectedDevices, setSelectedDevices] = useState(initialData.selectedDevices || []); - const handleSubmit = () => { + const handleSubmit = () => { if (selectedDevices.length === 0) { toast({ title: "请选择至少一个设备", @@ -589,15 +709,15 @@ export default function NewDistribution() { return; } - onNext({ - selectedDevices, - }); - }; + onNext({ + selectedDevices, + }); + }; - return ( + return (
-

目标设置

- +

目标设置

+
-
- +
+ -
+ 下一步 → +
- ); +
+ ); }; - // 流量池选择步骤组件 +// 流量池选择步骤组件 const TrafficPoolStep = ({ onSubmit, onBack, initialData = {} }: { onSubmit: (data: TrafficPoolData) => void; onBack: () => void; initialData?: Partial }) => { - const [selectedPools, setSelectedPools] = useState(initialData.selectedPools || []); - const [isSubmitting, setIsSubmitting] = useState(false); + const [selectedPools, setSelectedPools] = useState(initialData.selectedPools || []); + const [isSubmitting, setIsSubmitting] = useState(false); - const handleSubmit = async () => { - setIsSubmitting(true); + const handleSubmit = async () => { + setIsSubmitting(true); - try { + try { await new Promise(resolve => setTimeout(resolve, 1000)); onSubmit({ selectedPools }); - } catch (error) { - console.error("提交失败:", error); + } catch (error) { + console.error("提交失败:", error); toast({ title: "创建失败", description: "请稍后重试", variant: "destructive" }); - } finally { - setIsSubmitting(false); - } - }; + } finally { + setIsSubmitting(false); + } + }; // 从formData中获取选中的设备ID const deviceIds = formData.targetSettings?.selectedDevices || []; - return ( + return (
-

流量池选择

- +

流量池选择

+
-
- -
- - -
- ); + +
+ + +
+ + ); }; const headerRightContent = ( @@ -684,22 +804,30 @@ export default function NewDistribution() { } >
-
-
- - {steps.map((step, index) => ( - - ))} - + {loading ? ( +
+
+
+
加载中...
+
- + ) : ( +
+
+ + {steps.map((step, index) => ( + + ))} + +
+ {currentStep === 0 && ( )} - {currentStep === 1 && ( - - )} + {currentStep === 1 && ( + + )} - {currentStep === 2 && ( + {currentStep === 2 && ( )} -
+
+ )}
); diff --git a/nkebao/src/pages/workspace/traffic-distribution/TrafficDistribution.tsx b/nkebao/src/pages/workspace/traffic-distribution/TrafficDistribution.tsx index e4dcee76..65cda799 100644 --- a/nkebao/src/pages/workspace/traffic-distribution/TrafficDistribution.tsx +++ b/nkebao/src/pages/workspace/traffic-distribution/TrafficDistribution.tsx @@ -10,7 +10,6 @@ import { Trash2, Pause, Play, - Users, Filter, } from 'lucide-react'; import { Card } from '@/components/ui/card'; @@ -41,7 +40,7 @@ export default function TrafficDistribution() { const [tasks, setTasks] = useState([]); // 处理删除 - const handleDelete = async (ruleId: string) => { + const handleDelete = async (ruleId: number) => { const ruleToDelete = tasks.find((rule) => rule.id === ruleId); if (!ruleToDelete) return; @@ -49,7 +48,7 @@ export default function TrafficDistribution() { try { setIsLoading(true); - const response = await deleteDistributionRule(ruleId); + const response = await deleteDistributionRule(ruleId.toString()); if (response.code === 200) { setTasks(tasks.filter((rule) => rule.id !== ruleId)); @@ -76,15 +75,15 @@ export default function TrafficDistribution() { } }; - const handleEdit = (ruleId: string) => { - navigate(`/workspace/traffic-distribution/${ruleId}/edit`); + const handleEdit = (ruleId: number) => { + navigate(`/workspace/traffic-distribution/edit/${ruleId}`); }; - const handleView = (ruleId: string) => { + const handleView = (ruleId: number) => { navigate(`/workspace/traffic-distribution/${ruleId}`); }; - const toggleRuleStatus = async (ruleId: string) => { + const toggleRuleStatus = async (ruleId: number) => { const rule = tasks.find((r) => r.id === ruleId); if (!rule) return; @@ -93,7 +92,7 @@ export default function TrafficDistribution() { // 根据当前状态决定新状态:1表示开启,0表示关闭 const newStatus = rule.status === WorkbenchTaskStatus.RUNNING ? 0 : 1; - const response = await toggleDistributionRuleStatus(ruleId, newStatus as 0 | 1); + const response = await toggleDistributionRuleStatus(ruleId.toString(), newStatus as 0 | 1); if (response.code === 200) { // 更新本地状态:1对应RUNNING,0对应PAUSED @@ -363,9 +362,9 @@ export default function TrafficDistribution() {
- 上次执行: {rule.lastDistributionTime?.substring(0, 16) || '2025-07-02 09:00'} + 上次执行: {rule.config?.lastUpdated?.substring(0, 16) || '2025-07-02 09:00'}
-
创建人: {rule.creator || '售前'}
+
创建时间: {rule.createTime || '2025-07-02'}