From 3d1050db3dc827b1c382b78cdc522718c5914483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=B0=B8=E5=B9=B3?= Date: Mon, 7 Jul 2025 11:00:58 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E6=8F=90=E4=BA=A4=E5=90=8C?= =?UTF-8?q?=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/src/api/scenarios.ts | 167 ++++++++++++-- .../gongzhonghao/GongzhonghaoScenario.tsx | 210 ++++++++++-------- 2 files changed, 262 insertions(+), 115 deletions(-) diff --git a/nkebao/src/api/scenarios.ts b/nkebao/src/api/scenarios.ts index c6a6cc9a5..bcb26db33 100644 --- a/nkebao/src/api/scenarios.ts +++ b/nkebao/src/api/scenarios.ts @@ -1,4 +1,4 @@ -import { get } from './request'; +import { get, del } from './request'; import type { ApiResponse } from '@/types/common'; // 服务器返回的场景数据类型 @@ -34,6 +34,30 @@ export interface Plan { acquisitionCount: number; } +// 任务类型 +export interface Task { + id: string; + name: string; + status: "running" | "paused" | "completed"; + stats: { + devices: number; + acquired: number; + added: number; + }; + lastUpdated: string; + executionTime: string; + nextExecutionTime: string; + trend: { date: string; customers: number }[]; +} + +// 计划详情类型 +export interface PlanDetail { + apiKey: string; + textUrl: { + fullUrl: string; + }; +} + /** * 获取获客场景列表 * @@ -68,6 +92,128 @@ export const fetchScenes = async (params: { } }; +/** + * 获取场景详情 + * + * @param id 场景ID + * @returns 场景详情 + */ +export const fetchSceneDetail = async (id: string | number): Promise> => { + try { + return await get>(`/v1/plan/scenes/${id}`); + } catch (error) { + console.error("Error fetching scene detail:", error); + return { + code: 500, + message: "获取场景详情失败", + data: null + }; + } +}; + +/** + * 获取场景名称 + * + * @param channel 场景标识 + * @returns 场景名称 + */ +export const fetchSceneName = async (channel: string): Promise> => { + try { + return await get>(`/v1/plan/scenes-detail?id=${channel}`); + } catch (error) { + console.error("Error fetching scene name:", error); + return { + code: 500, + message: "获取场景名称失败", + data: { name: channel } + }; + } +}; + +/** + * 获取计划列表 + * + * @param channel 场景标识 + * @param page 页码 + * @param pageSize 每页数量 + * @returns 计划列表 + */ +export const fetchPlanList = async ( + channel: string, + page: number = 1, + pageSize: number = 10 +): Promise> => { + try { + return await get>( + `/v1/plan/list?sceneId=${channel}&page=${page}&pageSize=${pageSize}` + ); + } catch (error) { + console.error("Error fetching plan list:", error); + return { + code: 500, + message: "获取计划列表失败", + data: { list: [], total: 0 } + }; + } +}; + +/** + * 复制计划 + * + * @param planId 计划ID + * @returns 复制结果 + */ +export const copyPlan = async (planId: string): Promise> => { + try { + return await get>(`/v1/plan/copy?planId=${planId}`); + } catch (error) { + console.error("Error copying plan:", error); + return { + code: 500, + message: "复制计划失败", + data: null + }; + } +}; + +/** + * 删除计划 + * + * @param planId 计划ID + * @returns 删除结果 + */ +export const deletePlan = async (planId: string): Promise> => { + try { + return await del>(`/v1/plan/delete?planId=${planId}`); + } catch (error) { + console.error("Error deleting plan:", error); + return { + code: 500, + message: "删除计划失败", + data: null + }; + } +}; + +/** + * 获取计划详情 + * + * @param planId 计划ID + * @returns 计划详情 + */ +export const fetchPlanDetail = async (planId: string): Promise> => { + try { + return await get>(`/v1/plan/detail?planId=${planId}`); + } catch (error) { + console.error("Error fetching plan detail:", error); + return { + code: 500, + message: "获取计划详情失败", + data: null + }; + } +}; + /** * 将服务器返回的场景数据转换为前端展示需要的格式 * @@ -91,23 +237,4 @@ export const transformSceneItem = (item: SceneItem): Channel => { growth: growthPercent } }; -}; - -/** - * 获取场景详情 - * - * @param id 场景ID - * @returns 场景详情 - */ -export const fetchSceneDetail = async (id: string | number): Promise> => { - try { - return await get>(`/v1/plan/scenes/${id}`); - } catch (error) { - console.error("Error fetching scene detail:", error); - return { - code: 500, - message: "获取场景详情失败", - data: null - }; - } }; \ No newline at end of file diff --git a/nkebao/src/pages/scenarios/gongzhonghao/GongzhonghaoScenario.tsx b/nkebao/src/pages/scenarios/gongzhonghao/GongzhonghaoScenario.tsx index e11a19043..230c6d02d 100644 --- a/nkebao/src/pages/scenarios/gongzhonghao/GongzhonghaoScenario.tsx +++ b/nkebao/src/pages/scenarios/gongzhonghao/GongzhonghaoScenario.tsx @@ -16,33 +16,21 @@ import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import PageHeader from '@/components/PageHeader'; - -interface Task { - id: string; - name: string; - status: "running" | "paused" | "completed"; - stats: { - devices: number; - acquired: number; - added: number; - }; - lastUpdated: string; - executionTime: string; - nextExecutionTime: string; - trend: { date: string; customers: number }[]; - reqConf?: { - device?: string[]; - selectedDevices?: string[]; - }; - acquiredCount?: number; - addedCount?: number; - passRate?: number; -} +import { + fetchSceneName, + fetchPlanList, + copyPlan, + deletePlan, + fetchPlanDetail, + type Task +} from '@/api/scenarios'; interface DeviceStats { active: number; } +const API_BASE_URL = process.env.REACT_APP_API_BASE_URL || 'http://yishi.com'; + // API文档提示组件 function ApiDocumentationTooltip() { return ( @@ -65,7 +53,7 @@ export default function GongzhonghaoScenario() { const navigate = useNavigate(); const { toast } = useToast(); const channel = "gongzhonghao"; - const [channelName, setChannelName] = useState("公众号获客"); + const [channelName, setChannelName] = useState(""); // 1. tasks 初始值设为 [] const [tasks, setTasks] = useState([]); @@ -76,45 +64,32 @@ export default function GongzhonghaoScenario() { const [total, setTotal] = useState(0); useEffect(() => { - // 模拟API调用获取场景名称 - setChannelName("公众号获客"); + fetchSceneName(channel) + .then((res) => { + if (res.code === 200 && res.data?.name) { + setChannelName(res.data.name); + } else { + setChannelName(channel); + } + }) + .catch(() => setChannelName(channel)); }, [channel]); // 抽出请求列表的函数 const fetchTasks = () => { setLoading(true); setError(""); - // 模拟API调用 - setTimeout(() => { - const mockTasks: Task[] = [ - { - id: "1", - name: "公众号私域引流计划", - status: "running", - stats: { - devices: 4, - acquired: 67, - added: 45, - }, - lastUpdated: "2024-03-18 16:15", - executionTime: "2024-03-18 16:15", - nextExecutionTime: "预计20分钟后", - trend: Array.from({ length: 7 }, (_, i) => ({ - date: `3月${String(i + 12)}日`, - customers: Math.floor(Math.random() * 15) + 8, - })), - acquiredCount: 67, - addedCount: 45, - passRate: 67, - reqConf: { - selectedDevices: ["device1", "device2", "device3", "device4"] - } - }, - ]; - setTasks(mockTasks); - setTotal(1); - setLoading(false); - }, 1000); + fetchPlanList(channel, page, pageSize) + .then((res) => { + if (res.code === 200 && Array.isArray(res.data?.list)) { + setTasks(res.data.list); + setTotal(res.data.total || 0); + } else { + setError(res.message || "接口返回异常"); + } + }) + .catch((err: any) => setError(err?.message || "接口请求失败")) + .finally(() => setLoading(false)); }; useEffect(() => { @@ -140,35 +115,60 @@ export default function GongzhonghaoScenario() { const handleCopyPlan = (taskId: string) => { const taskToCopy = tasks.find((task) => task.id === taskId); if (!taskToCopy) return; - - // 模拟API调用 - setTimeout(() => { - const newTask = { - ...taskToCopy, - id: `${Date.now()}`, - name: `${taskToCopy.name} (副本)`, - status: "paused" as const, - }; - setTasks([...tasks, newTask]); - toast({ - title: "计划已复制", - description: `已成功复制"${taskToCopy.name}"`, + copyPlan(taskId) + .then((res) => { + if (res.code === 200) { + toast({ + title: "计划已复制", + description: `已成功复制"${taskToCopy.name}"`, + variant: "default", + }); + setPage(1); + fetchTasks(); + } else { + toast({ + title: "复制失败", + description: res.message || "复制计划失败,请重试", + variant: "destructive", + }); + } + }) + .catch((err: any) => { + toast({ + title: "复制失败", + description: err?.message || "复制计划失败,请重试", + variant: "destructive", + }); }); - }, 500); }; const handleDeletePlan = (taskId: string) => { const taskToDelete = tasks.find((t) => t.id === taskId); if (!taskToDelete) return; - - // 模拟API调用 - setTimeout(() => { - setTasks(tasks.filter((t) => t.id !== taskId)); - toast({ - title: "计划已删除", - description: `已成功删除"${taskToDelete.name}"`, + deletePlan(taskId) + .then((res) => { + if (res.code === 200) { + setTasks(tasks.filter((t) => t.id !== taskId)); + toast({ + title: "计划已删除", + description: `已成功删除"${taskToDelete.name}"`, + variant: "default", + }); + } else { + toast({ + title: "删除失败", + description: res.message || "删除计划失败,请重试", + variant: "destructive", + }); + } + }) + .catch((err: any) => { + toast({ + title: "删除失败", + description: err?.message || "删除计划失败,请重试", + variant: "destructive", + }); }); - }, 500); }; const handleStatusChange = (taskId: string, newStatus: "running" | "paused") => { @@ -177,31 +177,50 @@ export default function GongzhonghaoScenario() { toast({ title: newStatus === "running" ? "计划已启动" : "计划已暂停", description: `已${newStatus === "running" ? "启动" : "暂停"}获客计划`, + variant: "default", }); }; const handleOpenApiSettings = async (taskId: string) => { const task = tasks.find((t) => t.id === taskId); - if (!task) return; - - // 模拟API调用获取接口设置 - const mockApiSettings = { - apiKey: `api_key_${taskId}_${Date.now()}`, - webhookUrl: `https://api.example.com/webhook/${taskId}`, - taskId: taskId, - fullUrl: `name=测试用户&phone=13800138000&source=公众号&remark=测试备注` - }; - - setCurrentApiSettings(mockApiSettings); - setShowApiDialog(true); + if (task) { + try { + const res = await fetchPlanDetail(taskId); + if (res.code === 200 && res.data) { + setCurrentApiSettings({ + apiKey: res.data.apiKey || '', // 使用接口返回的 API 密钥 + webhookUrl: `${API_BASE_URL}/v1/api/scenarios`, + fullUrl: res.data.textUrl.fullUrl || '', + taskId, + }); + setShowApiDialog(true); + } else { + toast({ + title: "获取 API 密钥失败", + description: res.message || "请重试", + variant: "destructive", + }); + } + } catch (err: any) { + toast({ + title: "获取 API 密钥失败", + description: err?.message || "请重试", + variant: "destructive", + }); + } + } }; const handleCopyApiUrl = (url: string, withParams = false) => { - const textToCopy = withParams ? `${url}?${currentApiSettings.fullUrl}` : url; - navigator.clipboard.writeText(textToCopy); + let copyUrl = url; + if (withParams) { + copyUrl = `${url}?name=张三&phone=13800138000&source=外部系统&remark=测试数据`; + } + navigator.clipboard.writeText(copyUrl); toast({ title: "已复制", - description: "接口地址已复制到剪贴板", + description: withParams ? "接口地址(含示例参数)已复制到剪贴板" : "接口地址已复制到剪贴板", + variant: "default", }); }; @@ -235,7 +254,7 @@ export default function GongzhonghaoScenario() { handleEditPlan(task.id)} onCopy={handleCopyPlan} onDelete={handleDeletePlan} onStatusChange={handleStatusChange} @@ -308,6 +327,7 @@ export default function GongzhonghaoScenario() { toast({ title: "已复制", description: "API密钥已复制到剪贴板", + variant: "default", }); }} > @@ -430,4 +450,4 @@ export default function GongzhonghaoScenario() { ); -} \ No newline at end of file +}