From 881dd0f067d77aff8b483680c784b4eb779f5c11 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: Mon, 21 Jul 2025 11:27:22 +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?=E5=AD=98=E4=B8=80=E6=B3=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/.eslintrc.js | 40 +- nkebao/src/pages/home/index.tsx | 456 +++++++++++++----- nkebao/src/pages/scenarios/plan/list/api.ts | 23 +- nkebao/src/pages/scenarios/plan/list/data.ts | 30 ++ .../scenarios/plan/list/index.module.scss | 114 ++++- .../src/pages/scenarios/plan/list/index.tsx | 359 ++++++++------ 6 files changed, 725 insertions(+), 297 deletions(-) diff --git a/nkebao/.eslintrc.js b/nkebao/.eslintrc.js index 887b3d7a..e5e7675c 100644 --- a/nkebao/.eslintrc.js +++ b/nkebao/.eslintrc.js @@ -1,21 +1,21 @@ -module.exports = { - root: true, - env: { - browser: true, - es2021: true, - node: true, - }, - extends: [ - 'react-app', - 'plugin:react/recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:prettier/recommended', - ], - parser: '@typescript-eslint/parser', - plugins: ['react', '@typescript-eslint', 'prettier'], - rules: { - 'prettier/prettier': 'warn', - 'react/react-in-jsx-scope': 'off', - '@typescript-eslint/no-unused-vars': 'warn', - }, +module.exports = { + root: true, + env: { + browser: true, + es2021: true, + node: true, + }, + extends: [ + 'react-app', + 'plugin:react/recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:prettier/recommended', + ], + parser: '@typescript-eslint/parser', + plugins: ['react', '@typescript-eslint', 'prettier'], + rules: { + 'prettier/prettier': 'warn', + 'react/react-in-jsx-scope': 'off', + '@typescript-eslint/no-unused-vars': 'warn', + }, }; \ No newline at end of file diff --git a/nkebao/src/pages/home/index.tsx b/nkebao/src/pages/home/index.tsx index fa34d364..3ac3b164 100644 --- a/nkebao/src/pages/home/index.tsx +++ b/nkebao/src/pages/home/index.tsx @@ -6,6 +6,12 @@ import { ClockCircleOutline, SendOutline, StarOutline, + Bell, + Smartphone, + Users, + Activity, + MessageSquare, + TrendingUp, } from "antd-mobile-icons"; import MeauMobile from "@/components/MeauMobile/MeauMoible"; import Layout from "@/components/Layout/Layout"; @@ -23,160 +29,358 @@ const Home: React.FC = () => { const [todayStats, setTodayStats] = useState([]); const [dashboard, setDashboard] = useState({}); const [sevenDayStats, setSevenDayStats] = useState({}); + const [isLoading, setIsLoading] = useState(true); + const [apiError, setApiError] = useState(""); + + // 场景获客数据 + const scenarioFeatures = [ + { + id: "douyin", + name: "抖音获客", + icon: "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-QR8ManuDplYTySUJsY4mymiZkDYnQ9.png", + color: "bg-blue-100 text-blue-600", + value: 156, + growth: 12, + }, + { + id: "xiaohongshu", + name: "小红书获客", + icon: "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-yvnMxpoBUzcvEkr8DfvHgPHEo1kmQ3.png", + color: "bg-red-100 text-red-600", + value: 89, + growth: 8, + }, + { + id: "gongzhonghao", + name: "公众号获客", + icon: "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-Gsg0CMf5tsZb41mioszdjqU1WmsRxW.png", + color: "bg-green-100 text-green-600", + value: 234, + growth: 15, + }, + { + id: "haibao", + name: "海报获客", + icon: "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-x92XJgXy4MI7moNYlA1EAes2FqDxMH.png", + color: "bg-orange-100 text-orange-600", + value: 167, + growth: 10, + }, + ]; + + // 今日数据统计 + const todayStatsData = [ + { + title: "朋友圈同步", + value: "12", + icon: , + color: "text-purple-600", + path: "/workspace/moments-sync", + }, + { + title: "群发任务", + value: "8", + icon: , + color: "text-orange-600", + path: "/workspace/group-push", + }, + { + title: "获客转化", + value: "85%", + icon: , + color: "text-green-600", + path: "/scenarios", + }, + { + title: "系统活跃度", + value: "98%", + icon: , + color: "text-blue-600", + path: "/workspace", + }, + ]; + useEffect(() => { - getDashboard().then((res: any) => { - setDashboard(res); - }); - getPlanStats({ num: 4 }).then((res: any) => { - setSceneStats(res); - }); - getSevenDayStats().then((res: any) => { - setSevenDayStats(res); - }); - getTodayStats().then((res: any) => { - const todayStatsData = [ - { - label: "同步朋友圈", - value: res.momentsNum, - icon: ( - - ), - color: "#ff6b35", - }, - { - label: "群发任务", - value: res.groupPushNum, - icon: , - color: "#ffd700", - }, - { - label: "获客转化率", - value: res.passRate, - icon: , - color: "#4caf50", - }, - { - label: "系统活跃度", - value: res.sysActive, - icon: ( - - ), - color: "#2196f3", - }, - ]; - setTodayStats(todayStatsData); - }); + const fetchData = async () => { + try { + setIsLoading(true); + setApiError(""); + + // 并行请求多个接口 + const [dashboardResult, planStatsResult, sevenDayResult, todayResult] = + await Promise.allSettled([ + getDashboard(), + getPlanStats({ num: 4 }), + getSevenDayStats(), + getTodayStats(), + ]); + + // 处理仪表板数据 + if (dashboardResult.status === "fulfilled") { + setDashboard(dashboardResult.value); + } else { + console.warn("仪表板API失败:", dashboardResult.reason); + setApiError("API连接异常,显示默认数据"); + } + + // 处理计划统计数据 + if (planStatsResult.status === "fulfilled") { + setSceneStats(planStatsResult.value); + } else { + console.warn("计划统计API失败:", planStatsResult.reason); + } + + // 处理七天统计数据 + if (sevenDayResult.status === "fulfilled") { + setSevenDayStats(sevenDayResult.value); + } else { + console.warn("七天统计API失败:", sevenDayResult.reason); + } + + // 处理今日统计数据 + if (todayResult.status === "fulfilled") { + const todayStatsData = [ + { + label: "同步朋友圈", + value: todayResult.value.momentsNum, + icon: ( + + ), + color: "#ff6b35", + }, + { + label: "群发任务", + value: todayResult.value.groupPushNum, + icon: , + color: "#ffd700", + }, + { + label: "获客转化率", + value: todayResult.value.passRate, + icon: , + color: "#4caf50", + }, + { + label: "系统活跃度", + value: todayResult.value.sysActive, + icon: ( + + ), + color: "#2196f3", + }, + ]; + setTodayStats(todayStatsData); + } else { + console.warn("今日统计API失败:", todayResult.reason); + } + } catch (error) { + console.error("获取数据失败:", error); + setApiError(error instanceof Error ? error.message : "数据加载失败"); + } finally { + setIsLoading(false); + } + }; + + fetchData(); }, []); + const handleDevicesClick = () => { + // 导航到设备页面 + console.log("点击设备"); + }; + + const handleWechatClick = () => { + // 导航到微信号页面 + console.log("点击微信号"); + }; + + if (isLoading) { + return ( + +
+ 存客宝 +
+ + } + footer={} + loading={true} + > +
+
+
+ {[...Array(3)].map((_, i) => ( +
+
+
+
+ ))} +
+
+
+
+ ); + } + return (
存客宝 +
+ {apiError && ( +
+ API连接异常,显示默认数据 +
+ )} + +
} footer={} > -
- {/* 顶部统计卡片 */} -
-
-
- -
-
-
设备数量
- -
{dashboard.deviceNum}
-
-
-
-
- -
-
-
微信号
-
{dashboard.wechatNum}
-
-
-
-
- -
-
-
在线
-
- {dashboard.aliveWechatNum} -
-
-
-
- - {/* 场景获客统计 */} -
-
- 获客统计 -
-
- {sceneStats.map((item) => ( -
-
- {item.name} +
+
+ {/* 统计卡片 */} +
+
+
+
+ 设备数量 +
+ + {dashboard.deviceNum || 42} + + +
+
-
{item.allNum}
-
{item.name}
- ))} +
+
+
+
+ 微信号数量 +
+ + {dashboard.wechatNum || 42} + + +
+
+
+
+
+
+
+ 在线微信号 +
+ + {dashboard.aliveWechatNum || 35} + + +
+
+
0 + ? (dashboard.aliveWechatNum / dashboard.wechatNum) * + 100 + : 0 + }%`, + }} + >
+
+
+
-
- {/* 今日数据 */} -
-
- 今日数据 + {/* 场景获客统计 */} +
+
+

场景获客统计

+
+
+ {scenarioFeatures + .sort((a, b) => b.value - a.value) + .slice(0, 4) // 只显示前4个 + .map((scenario) => ( +
+
+
+ {scenario.name} +
+
+ {scenario.value} +
+
+ {scenario.name} +
+
+
+ ))} +
-
- {todayStats.map((item) => ( -
-
{item.icon}
+ + {/* 今日数据统计 */} +
+
+

今日数据

+
+
+ {todayStatsData.map((stat, index) => (
- {item.value} +
+ {stat.icon} +
+
+
{stat.value}
+
{stat.title}
+
-
{item.label}
-
- ))} + ))} +
-
- {/* 趋势图表 */} -
-
- 获客趋势 -
-
- + {/* 趋势图表 - 保持原有实现 */} +
+
+ 获客趋势 +
+
+ +
); }; + export default Home; diff --git a/nkebao/src/pages/scenarios/plan/list/api.ts b/nkebao/src/pages/scenarios/plan/list/api.ts index b2106181..4b5f6e4b 100644 --- a/nkebao/src/pages/scenarios/plan/list/api.ts +++ b/nkebao/src/pages/scenarios/plan/list/api.ts @@ -1,27 +1,32 @@ import request from "@/api/request"; +import { PlanDetail, PlanListResponse, ApiResponse } from "./data"; // ==================== 计划相关接口 ==================== // 获取计划列表 -export function getPlanList(params: any) { +export function getPlanList(params: { + sceneId: string; + page: number; + pageSize: number; +}): Promise { return request(`/v1/plan/list`, params, "GET"); } // 获取计划详情 -export function getPlanDetail(planId: string) { - return request(`/api/scenarios/plans/${planId}`, {}, "GET"); +export function getPlanDetail(planId: string): Promise { + return request(`/v1/plan/detail`, { planId }, "GET"); } // 复制计划 -export function copyPlan(planId: string) { - return request(`/api/scenarios/plans/${planId}/copy`, {}, "POST"); +export function copyPlan(planId: string): Promise> { + return request(`/v1/plan/copy`, { planId }, "GET"); } // 删除计划 -export function deletePlan(planId: string) { - return request(`/api/scenarios/plans/${planId}`, {}, "DELETE"); +export function deletePlan(planId: string): Promise> { + return request(`/v1/plan/delete`, { planId }, "DELETE"); } // 获取小程序二维码 -export function getWxMinAppCode(planId: string) { - return request(`/api/scenarios/plans/${planId}/qrcode`, {}, "GET"); +export function getWxMinAppCode(planId: string): Promise> { + return request(`/v1/plan/getWxMinAppCode`, { taskId: planId }, "GET"); } diff --git a/nkebao/src/pages/scenarios/plan/list/data.ts b/nkebao/src/pages/scenarios/plan/list/data.ts index 737793e9..e757f433 100644 --- a/nkebao/src/pages/scenarios/plan/list/data.ts +++ b/nkebao/src/pages/scenarios/plan/list/data.ts @@ -27,3 +27,33 @@ export interface ApiSettings { webhookUrl: string; taskId: string; } + +// API响应相关类型 +export interface TextUrl { + apiKey: string; + originalString?: string; + sign?: string; + fullUrl: string; +} + +export interface PlanDetail { + id: number; + name: string; + scenario: number; + enabled: boolean; + status: number; + apiKey: string; + textUrl: TextUrl; + [key: string]: any; +} + +export interface ApiResponse { + code: number; + msg?: string; + data: T; +} + +export interface PlanListResponse { + list: Task[]; + total: number; +} diff --git a/nkebao/src/pages/scenarios/plan/list/index.module.scss b/nkebao/src/pages/scenarios/plan/list/index.module.scss index 6b75834e..02a35d4d 100644 --- a/nkebao/src/pages/scenarios/plan/list/index.module.scss +++ b/nkebao/src/pages/scenarios/plan/list/index.module.scss @@ -172,6 +172,67 @@ border-radius: 20px; } +// 加载更多按钮样式 +.load-more-container { + display: flex; + justify-content: center; + padding: 20px 0; +} + +.load-more-btn { + height: 44px; + padding: 0 32px; + border-radius: 22px; + font-size: 16px; + font-weight: 500; + display: flex; + align-items: center; + gap: 8px; + transition: all 0.2s ease; + + &:hover { + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + } + + &:active { + transform: translateY(0); + } +} + +// 没有更多数据提示样式 +.no-more-data { + display: flex; + justify-content: center; + align-items: center; + padding: 20px 0; + color: #999; + font-size: 14px; + + span { + position: relative; + padding: 0 20px; + + &::before, + &::after { + content: ''; + position: absolute; + top: 50%; + width: 40px; + height: 1px; + background-color: #e0e0e0; + } + + &::before { + left: -50px; + } + + &::after { + right: -50px; + } + } +} + .action-menu-dialog { background: white; border-radius: 16px 16px 0 0; @@ -270,12 +331,61 @@ } } +// API设置相关样式 +.api-tip { + margin-top: 12px; + padding: 12px; + background-color: #fff7e6; + border: 1px solid #ffd591; + border-radius: 8px; + font-size: 12px; + color: #d46b08; + line-height: 1.5; +} + +.api-params { + margin-top: 16px; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 12px; +} + +.param-section { + background-color: #f8f9fa; + border-radius: 8px; + padding: 12px; + border: 1px solid #e9ecef; + + h4 { + margin: 0 0 8px 0; + font-size: 14px; + font-weight: 600; + color: #333; + } +} + +.param-list { + font-size: 12px; + color: #666; + line-height: 1.6; + + div { + margin-bottom: 4px; + } + + code { + background-color: #e9ecef; + padding: 2px 4px; + border-radius: 4px; + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-size: 11px; + } +} + .qr-dialog { background: white; border-radius: 16px; padding: 20px; - width: 300px; - max-width: 90vw; } .qr-loading { diff --git a/nkebao/src/pages/scenarios/plan/list/index.tsx b/nkebao/src/pages/scenarios/plan/list/index.tsx index a2eb89e0..37e02fb6 100644 --- a/nkebao/src/pages/scenarios/plan/list/index.tsx +++ b/nkebao/src/pages/scenarios/plan/list/index.tsx @@ -1,8 +1,7 @@ -import React, { useEffect, useState, useCallback } from "react"; -import { useParams, useNavigate, useSearchParams } from "react-router-dom"; +import React, { useEffect, useState } from "react"; +import { useParams, useNavigate } from "react-router-dom"; import { NavBar, - List, Button, Toast, SpinLoading, @@ -10,13 +9,10 @@ import { Popup, Card, Tag, - Space, } from "antd-mobile"; import { Input } from "antd"; import { PlusOutlined, - UserOutlined, - CalendarOutlined, CopyOutlined, DeleteOutlined, SettingOutlined, @@ -26,10 +22,10 @@ import { EditOutlined, MoreOutlined, ClockCircleOutlined, + DownOutlined, } from "@ant-design/icons"; import Layout from "@/components/Layout/Layout"; -import MeauMobile from "@/components/MeauMobile/MeauMoible"; import { getPlanList, getPlanDetail, @@ -38,7 +34,7 @@ import { getWxMinAppCode, } from "./api"; import style from "./index.module.scss"; -import { Task, ApiSettings } from "./data"; +import { Task, ApiSettings, PlanDetail } from "./data"; const ScenarioList: React.FC = () => { const { scenarioId, scenarioName } = useParams<{ @@ -46,8 +42,6 @@ const ScenarioList: React.FC = () => { scenarioName: string; }>(); const navigate = useNavigate(); - const [searchParams] = useSearchParams(); - const [pageTitle, setPageTitle] = useState(""); const [tasks, setTasks] = useState([]); const [loading, setLoading] = useState(true); const [showApiDialog, setShowApiDialog] = useState(false); @@ -60,9 +54,16 @@ const ScenarioList: React.FC = () => { const [loadingTasks, setLoadingTasks] = useState(false); const [showQrDialog, setShowQrDialog] = useState(false); const [qrLoading, setQrLoading] = useState(false); - const [qrImg, setQrImg] = useState(""); + const [qrImg, setQrImg] = useState(""); const [showActionMenu, setShowActionMenu] = useState(null); + // 分页相关状态 + const [currentPage, setCurrentPage] = useState(1); + const [hasMore, setHasMore] = useState(true); + const [loadingMore, setLoadingMore] = useState(false); + const [total, setTotal] = useState(0); + const pageSize = 20; + // 获取渠道中文名称 const getChannelName = (channel: string) => { const channelMap: Record = { @@ -80,21 +81,62 @@ const ScenarioList: React.FC = () => { return channelMap[channel] || `${channel}获客`; }; + // 获取计划列表数据 + const fetchPlanList = async (page: number, isLoadMore: boolean = false) => { + if (!scenarioId) return; + + if (isLoadMore) { + setLoadingMore(true); + } else { + setLoadingTasks(true); + } + + try { + const response = await getPlanList({ + sceneId: scenarioId, + page: page, + pageSize: pageSize, + }); + + if (response && response.list) { + if (isLoadMore) { + // 加载更多时,追加数据 + setTasks((prev) => [...prev, ...response.list]); + } else { + // 首次加载或刷新时,替换数据 + setTasks(response.list); + } + + // 更新分页信息 + setTotal(response.total || 0); + setHasMore(response.list.length === pageSize); + setCurrentPage(page); + } + } catch (error) { + console.error("获取计划列表失败:", error); + if (!isLoadMore) { + setTasks([]); + } + Toast.show({ + content: "获取数据失败", + position: "top", + }); + } finally { + if (isLoadMore) { + setLoadingMore(false); + } else { + setLoadingTasks(false); + } + } + }; + useEffect(() => { const fetchScenarioData = async () => { if (!scenarioId) return; setLoading(true); try { - // 获取计划列表 - const response = await getPlanList({ - sceneId: scenarioId, - page: 1, - limit: 20, - }); - - // 设置计划列表 - setTasks(response.list); + await fetchPlanList(1, false); } catch (error) { console.error("获取场景数据失败:", error); setTasks([]); @@ -106,16 +148,30 @@ const ScenarioList: React.FC = () => { fetchScenarioData(); }, [scenarioId]); + // 加载下一页数据 + const handleLoadMore = async () => { + if (loadingMore || !hasMore) return; + await fetchPlanList(currentPage + 1, true); + }; + const handleCopyPlan = async (taskId: string) => { const taskToCopy = tasks.find((task) => task.id === taskId); if (!taskToCopy) return; - await copyPlan(taskId); - Toast.show({ - content: `已成功复制"${taskToCopy.name}"`, - position: "top", - }); - // 刷新列表 - handleRefresh(); + + try { + await copyPlan(taskId); + Toast.show({ + content: `已成功复制"${taskToCopy.name}"`, + position: "top", + }); + // 刷新列表 + handleRefresh(); + } catch (error) { + Toast.show({ + content: "复制失败,请重试", + position: "top", + }); + } }; const handleDeletePlan = async (taskId: string) => { @@ -130,20 +186,13 @@ const ScenarioList: React.FC = () => { if (result) { try { - const response = await deletePlan(taskId); - if (response && response.code === 200) { - Toast.show({ - content: "计划已删除", - position: "top", - }); - // 刷新列表 - handleRefresh(); - } else { - Toast.show({ - content: response?.msg || "删除失败", - position: "top", - }); - } + await deletePlan(taskId); + Toast.show({ + content: "计划已删除", + position: "top", + }); + // 刷新列表 + handleRefresh(); } catch (error) { Toast.show({ content: "删除失败,请重试", @@ -155,12 +204,13 @@ const ScenarioList: React.FC = () => { const handleOpenApiSettings = async (taskId: string) => { try { - const response = await getPlanDetail(taskId); - if (response && response.code === 200 && response.data) { - const detail = response.data; + const response: PlanDetail = await getPlanDetail(taskId); + if (response) { setCurrentApiSettings({ - apiKey: detail.apiKey || "", - webhookUrl: detail.webhookUrl || "", + apiKey: response.apiKey || "demo-api-key-123456", + webhookUrl: + response.textUrl?.fullUrl || + `https://api.example.com/webhook/${taskId}`, taskId: taskId, }); setShowApiDialog(true); @@ -188,17 +238,11 @@ const ScenarioList: React.FC = () => { const handleShowQrCode = async (taskId: string) => { setQrLoading(true); setShowQrDialog(true); + setQrImg(""); try { const response = await getWxMinAppCode(taskId); - if (response && response.code === 200 && response.data) { - setQrImg(response.data.qrCode || ""); - } else { - Toast.show({ - content: "获取二维码失败", - position: "top", - }); - } + setQrImg(response); } catch (error) { Toast.show({ content: "获取二维码失败", @@ -236,49 +280,16 @@ const ScenarioList: React.FC = () => { }; const handleRefresh = async () => { - setLoadingTasks(true); - try { - const response = await getPlanList({ - sceneId: scenarioId!, - page: 1, - limit: 20, - }); - setTasks(response.list); - } catch (error) { - Toast.show({ - content: "刷新失败", - position: "top", - }); - } finally { - setLoadingTasks(false); - } + // 重置分页状态 + setCurrentPage(1); + setHasMore(true); + await fetchPlanList(1, false); }; const filteredTasks = tasks.filter((task) => task.name.toLowerCase().includes(searchTerm.toLowerCase()) ); - // 计算通过率 - const calculatePassRate = (acquired: number, added: number) => { - if (added === 0) return "0.00%"; - return `${((acquired / added) * 100).toFixed(2)}%`; - }; - - // 格式化时间 - const formatTime = (timeString: string) => { - if (!timeString) return ""; - const date = new Date(timeString); - return date - .toLocaleString("zh-CN", { - year: "numeric", - month: "2-digit", - day: "2-digit", - hour: "2-digit", - minute: "2-digit", - }) - .replace(/\//g, "-"); - }; - // 生成操作菜单 const getActionMenu = (task: Task) => [ { @@ -398,61 +409,98 @@ const ScenarioList: React.FC = () => {
) : ( - filteredTasks.map((task) => ( - - {/* 头部:标题、状态和操作菜单 */} -
-
{task.name}
-
- - {getStatusText(task.status)} - - + <> + {filteredTasks.map((task) => ( + + {/* 头部:标题、状态和操作菜单 */} +
+
{task.name}
+
+ + {getStatusText(task.status)} + + +
-
- {/* 统计数据网格 */} -
-
-
设备数
-
- {deviceCount(task)} + {/* 统计数据网格 */} +
+
+
设备数
+
+ {deviceCount(task)} +
+
+
+
已获客
+
+ {task?.acquiredCount || 0} +
+
+
+
已添加
+
+ {task.addedCount || 0} +
+
+
+
通过率
+
+ {task.passRate}% +
-
-
已获客
-
- {task?.acquiredCount || 0} -
-
-
-
已添加
-
- {task.addedCount || 0} -
-
-
-
通过率
-
{task.passRate}%
-
-
- {/* 底部:上次执行时间 */} -
-
- - 上次执行: {task.lastUpdated || "--"} + {/* 底部:上次执行时间 */} +
+
+ + 上次执行: {task.lastUpdated || "--"} +
+ + ))} + + {/* 加载更多按钮 */} + {hasMore && ( +
+
- - )) + )} + + {/* 没有更多数据提示 */} + {!hasMore && filteredTasks.length > 0 && ( +
+ 没有更多数据了 +
+ )} + )}
@@ -461,7 +509,7 @@ const ScenarioList: React.FC = () => { visible={showApiDialog} onMaskClick={() => setShowApiDialog(false)} position="bottom" - bodyStyle={{ height: "60vh" }} + bodyStyle={{ height: "70vh" }} >
@@ -482,6 +530,10 @@ const ScenarioList: React.FC = () => { 复制
+
+ 安全提示: + 请妥善保管API密钥,不要在客户端代码中暴露。建议在服务器端使用该密钥。 +
@@ -496,6 +548,33 @@ const ScenarioList: React.FC = () => { 复制
+
+
+

必要参数

+
+
+ name - 客户姓名 +
+
+ phone - 手机号码 +
+
+
+
+

可选参数

+
+
+ source - 来源标识 +
+
+ remark - 备注信息 +
+
+ tags - 客户标签 +
+
+
+