diff --git a/nkebao/src/components/InfiniteList/InfiniteList.module.scss b/nkebao/src/components/InfiniteList/InfiniteList.module.scss new file mode 100644 index 00000000..c443db9d --- /dev/null +++ b/nkebao/src/components/InfiniteList/InfiniteList.module.scss @@ -0,0 +1,87 @@ +.listContainer { + display: flex; + flex-direction: column; + overflow: hidden; + position: relative; +} + +.listItem { + flex-shrink: 0; + width: 100%; +} + +.loadMoreButtonContainer { + display: flex; + justify-content: center; + align-items: center; + padding: 16px; + flex-shrink: 0; +} + +.noMoreText { + text-align: center; + color: #999; + font-size: 14px; + padding: 16px; + flex-shrink: 0; +} + +.emptyState { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 60px 20px; + color: #999; + flex: 1; + min-height: 200px; +} + +.emptyIcon { + font-size: 48px; + margin-bottom: 16px; + opacity: 0.5; +} + +.emptyText { + font-size: 14px; + color: #999; +} + +.pullToRefresh { + height: 100%; + overflow: auto; +} + +// 自定义滚动条样式 +.listContainer::-webkit-scrollbar { + width: 4px; +} + +.listContainer::-webkit-scrollbar-track { + background: transparent; +} + +.listContainer::-webkit-scrollbar-thumb { + background: rgba(0, 0, 0, 0.1); + border-radius: 2px; +} + +.listContainer::-webkit-scrollbar-thumb:hover { + background: rgba(0, 0, 0, 0.2); +} + +// 响应式设计 +@media (max-width: 768px) { + .listContainer { + padding: 0 8px; + } + + .loadMoreButtonContainer { + padding: 12px; + } + + .noMoreText { + padding: 12px; + } +} \ No newline at end of file diff --git a/nkebao/src/components/InfiniteList/InfiniteList.tsx b/nkebao/src/components/InfiniteList/InfiniteList.tsx new file mode 100644 index 00000000..37a325e5 --- /dev/null +++ b/nkebao/src/components/InfiniteList/InfiniteList.tsx @@ -0,0 +1,195 @@ +import React, { useState, useEffect, useRef, useCallback } from "react"; +import { + PullToRefresh, + InfiniteScroll, + Button, + SpinLoading, +} from "antd-mobile"; +import styles from "./InfiniteList.module.scss"; + +interface InfiniteListProps { + // 数据相关 + data: T[]; + loading?: boolean; + hasMore?: boolean; + loadingText?: string; + noMoreText?: string; + + // 渲染相关 + renderItem: (item: T, index: number) => React.ReactNode; + keyExtractor?: (item: T, index: number) => string | number; + + // 事件回调 + onLoadMore?: () => Promise | void; + onRefresh?: () => Promise | void; + + // 样式相关 + className?: string; + itemClassName?: string; + containerStyle?: React.CSSProperties; + + // 功能开关 + enablePullToRefresh?: boolean; + enableInfiniteScroll?: boolean; + enableLoadMoreButton?: boolean; + + // 自定义高度 + height?: string | number; + minHeight?: string | number; +} + +const InfiniteList = ({ + data, + loading = false, + hasMore = true, + loadingText = "加载中...", + noMoreText = "没有更多了", + + renderItem, + keyExtractor = (_, index) => index, + + onLoadMore, + onRefresh, + + className = "", + itemClassName = "", + containerStyle = {}, + + enablePullToRefresh = true, + enableInfiniteScroll = true, + enableLoadMoreButton = false, + + height = "100%", + minHeight = "200px", +}: InfiniteListProps) => { + const [refreshing, setRefreshing] = useState(false); + const [loadingMore, setLoadingMore] = useState(false); + const containerRef = useRef(null); + + // 处理下拉刷新 + const handleRefresh = useCallback(async () => { + if (!onRefresh) return; + + setRefreshing(true); + try { + await onRefresh(); + } catch (error) { + console.error("Refresh failed:", error); + } finally { + setRefreshing(false); + } + }, [onRefresh]); + + // 处理加载更多 + const handleLoadMore = useCallback(async () => { + if (!onLoadMore || loadingMore || !hasMore) return; + + setLoadingMore(true); + try { + await onLoadMore(); + } catch (error) { + console.error("Load more failed:", error); + } finally { + setLoadingMore(false); + } + }, [onLoadMore, loadingMore, hasMore]); + + // 点击加载更多按钮 + const handleLoadMoreClick = useCallback(() => { + handleLoadMore(); + }, [handleLoadMore]); + + // 容器样式 + const containerStyles: React.CSSProperties = { + height, + minHeight, + ...containerStyle, + }; + + // 渲染列表项 + const renderListItems = () => { + return data.map((item, index) => ( +
+ {renderItem(item, index)} +
+ )); + }; + + // 渲染加载更多按钮 + const renderLoadMoreButton = () => { + if (!enableLoadMoreButton || !hasMore) return null; + + return ( +
+ +
+ ); + }; + + // 渲染无更多数据提示 + const renderNoMoreText = () => { + if (hasMore || data.length === 0) return null; + + return
{noMoreText}
; + }; + + // 渲染空状态 + const renderEmptyState = () => { + if (data.length > 0 || loading) return null; + + return ( +
+
📝
+
暂无数据
+
+ ); + }; + + const content = ( +
+ {renderListItems()} + {renderLoadMoreButton()} + {renderNoMoreText()} + {renderEmptyState()} + + {/* 无限滚动组件 */} + {enableInfiniteScroll && ( + + )} +
+ ); + + // 如果启用下拉刷新,包装PullToRefresh + if (enablePullToRefresh && onRefresh) { + return ( + + {content} + + ); + } + + return content; +}; + +export default InfiniteList; diff --git a/nkebao/src/components/MeauMobile/MeauMoible.tsx b/nkebao/src/components/MeauMobile/MeauMoible.tsx index 5f6f8086..20b90b62 100644 --- a/nkebao/src/components/MeauMobile/MeauMoible.tsx +++ b/nkebao/src/components/MeauMobile/MeauMoible.tsx @@ -19,7 +19,7 @@ const tabs = [ key: "scene", title: "场景获客", icon: , - path: "/scene", + path: "/scenarios", }, { key: "work", @@ -36,7 +36,7 @@ const tabs = [ ]; // 需要展示菜单的路由白名单(可根据实际业务调整) -const menuPaths = ["/", "/scene", "/workspace", "/mine"]; +const menuPaths = ["/", "/scenarios", "/workspace", "/mine"]; const MeauMobile: React.FC = () => { const location = useLocation(); diff --git a/nkebao/src/pages/scenarios/api.ts b/nkebao/src/pages/scenarios/api.ts deleted file mode 100644 index e7ae1867..00000000 --- a/nkebao/src/pages/scenarios/api.ts +++ /dev/null @@ -1,51 +0,0 @@ -import request from "@/api/request"; - -// 获取计划列表 -export const getPlanList = async (scenarioId: string, page: number = 1, limit: number = 20) => { - return request(`/api/scenarios/${scenarioId}/plans`, { page, limit }, 'GET'); -}; - -// 获取计划详情 -export const getPlanDetail = async (planId: string) => { - return request(`/api/scenarios/plans/${planId}`, undefined, 'GET'); -}; - -// 复制计划 -export const copyPlan = async (planId: string) => { - return request(`/api/scenarios/plans/${planId}/copy`, undefined, 'POST'); -}; - -// 删除计划 -export const deletePlan = async (planId: string) => { - return request(`/api/scenarios/plans/${planId}`, undefined, 'DELETE'); -}; - -// 创建计划 -export const createPlan = async (data: any) => { - return request('/api/scenarios/plans', data, 'POST'); -}; - -// 更新计划 -export const updatePlan = async (planId: string, data: any) => { - return request(`/api/scenarios/plans/${planId}`, data, 'PUT'); -}; - -// 获取小程序二维码 -export const getWxMinAppCode = async (planId: string) => { - return request(`/api/scenarios/plans/${planId}/qrcode`, undefined, 'GET'); -}; - -// 获取场景类型列表 -export const getScenarioTypes = async () => { - return request('/api/scenarios/types', undefined, 'GET'); -}; - -// 获取设备列表 -export const getDevices = async () => { - return request('/api/devices', undefined, 'GET'); -}; - -// 获取海报列表 -export const getPosters = async () => { - return request('/api/posters', undefined, 'GET'); -}; \ No newline at end of file diff --git a/nkebao/src/pages/scene/api.ts b/nkebao/src/pages/scenarios/list/api.ts similarity index 100% rename from nkebao/src/pages/scene/api.ts rename to nkebao/src/pages/scenarios/list/api.ts diff --git a/nkebao/src/pages/scene/index.module.scss b/nkebao/src/pages/scenarios/list/index.module.scss similarity index 100% rename from nkebao/src/pages/scene/index.module.scss rename to nkebao/src/pages/scenarios/list/index.module.scss diff --git a/nkebao/src/pages/scene/index.tsx b/nkebao/src/pages/scenarios/list/index.tsx similarity index 86% rename from nkebao/src/pages/scene/index.tsx rename to nkebao/src/pages/scenarios/list/index.tsx index 23948fa2..48785bec 100644 --- a/nkebao/src/pages/scene/index.tsx +++ b/nkebao/src/pages/scenarios/list/index.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; -import { NavBar, Button, Toast, SpinLoading } from "antd-mobile"; +import { NavBar, Button, Toast } from "antd-mobile"; import { PlusOutlined, UpOutlined } from "@ant-design/icons"; import MeauMobile from "@/components/MeauMobile/MeauMoible"; import Layout from "@/components/Layout/Layout"; @@ -77,33 +77,6 @@ const Scene: React.FC = () => { navigate("/scenarios/new"); }; - if (loading) { - return ( - -
场景获客
- - - } - footer={} - > -
- -
加载场景数据中...
-
-
- ); - } - if (error && scenarios.length === 0) { return ( { return ( = ({ const loadData = async () => { setLoading(true); try { - // 获取设备列表 - const devicesRes = await getDevices(); - if (devicesRes?.data) { - setDevices(devicesRes.data); - } - - // 获取海报列表 - const postersRes = await getPosters(); - if (postersRes?.data) { - setPosters(postersRes.data); - } + // // 获取设备列表 + // const devicesRes = await getDevices(); + // if (devicesRes?.data) { + // setDevices(devicesRes.data); + // } + // // 获取海报列表 + // const postersRes = await getPosters(); + // if (postersRes?.data) { + // setPosters(postersRes.data); + // } } catch (error) { Toast.show({ content: "加载数据失败", diff --git a/nkebao/src/pages/scenarios/new/steps/step.api.ts b/nkebao/src/pages/scenarios/new/steps/step.api.ts new file mode 100644 index 00000000..0e1b2c34 --- /dev/null +++ b/nkebao/src/pages/scenarios/new/steps/step.api.ts @@ -0,0 +1,363 @@ +import request from '@/api/request'; + +// ==================== 场景相关接口 ==================== + +// 获取场景列表 +export function getScenarios(params: any) { + return request('/v1/plan/scenes', params, 'GET'); +} + +// 获取场景详情 +export function getScenarioDetail(id: string) { + return request(`/v1/scenarios/${id}`, {}, 'GET'); +} + +// 创建场景 +export function createScenario(data: any) { + return request('/v1/scenarios', data, 'POST'); +} + +// 更新场景 +export function updateScenario(id: string, data: any) { + return request(`/v1/scenarios/${id}`, data, 'PUT'); +} + +// 删除场景 +export function deleteScenario(id: string) { + return request(`/v1/scenarios/${id}`, {}, 'DELETE'); +} + +// ==================== 计划相关接口 ==================== + +// 获取计划列表 +export function getPlanList(scenarioId: string, page: number = 1, limit: number = 20) { + return request(`/api/scenarios/${scenarioId}/plans`, { page, limit }, 'GET'); +} + // 复制计划 + export function copyPlan(planId: string) { + return request(`/api/scenarios/plans/${planId}/copy`, undefined, 'POST'); +} + +// 删除计划 +export function deletePlan(planId: string) { + return request(`/api/scenarios/plans/${planId}`, undefined, 'DELETE'); +} + + + +// 获取小程序二维码 +export function getWxMinAppCode(planId: string) { + return request(`/api/scenarios/plans/${planId}/qrcode`, undefined, 'GET'); +} + + +// ==================== 设备相关接口 ==================== + +// 获取设备列表 +export function getDevices() { + return request('/api/devices', undefined, 'GET'); +} + +// 获取设备详情 +export function getDeviceDetail(deviceId: string) { + return request(`/api/devices/${deviceId}`, undefined, 'GET'); +} + +// 创建设备 +export function createDevice(data: any) { + return request('/api/devices', data, 'POST'); +} + +// 更新设备 +export function updateDevice(deviceId: string, data: any) { + return request(`/api/devices/${deviceId}`, data, 'PUT'); +} + +// 删除设备 +export function deleteDevice(deviceId: string) { + return request(`/api/devices/${deviceId}`, undefined, 'DELETE'); +} + +// ==================== 微信号相关接口 ==================== + +// 获取微信号列表 +export function getWechatAccounts() { + return request('/api/wechat-accounts', undefined, 'GET'); +} + +// 获取微信号详情 +export function getWechatAccountDetail(accountId: string) { + return request(`/api/wechat-accounts/${accountId}`, undefined, 'GET'); +} + +// 创建微信号 +export function createWechatAccount(data: any) { + return request('/api/wechat-accounts', data, 'POST'); +} + +// 更新微信号 +export function updateWechatAccount(accountId: string, data: any) { + return request(`/api/wechat-accounts/${accountId}`, data, 'PUT'); +} + +// 删除微信号 +export function deleteWechatAccount(accountId: string) { + return request(`/api/wechat-accounts/${accountId}`, undefined, 'DELETE'); +} + +// ==================== 海报相关接口 ==================== + +// 获取海报列表 +export function getPosters() { + return request('/api/posters', undefined, 'GET'); +} + +// 获取海报详情 +export function getPosterDetail(posterId: string) { + return request(`/api/posters/${posterId}`, undefined, 'GET'); +} + +// 创建海报 +export function createPoster(data: any) { + return request('/api/posters', data, 'POST'); +} + +// 更新海报 +export function updatePoster(posterId: string, data: any) { + return request(`/api/posters/${posterId}`, data, 'PUT'); +} + +// 删除海报 +export function deletePoster(posterId: string) { + return request(`/api/posters/${posterId}`, undefined, 'DELETE'); +} + +// ==================== 内容相关接口 ==================== + +// 获取内容列表 +export function getContents(params: any) { + return request('/api/contents', params, 'GET'); +} + +// 获取内容详情 +export function getContentDetail(contentId: string) { + return request(`/api/contents/${contentId}`, undefined, 'GET'); +} + +// 创建内容 +export function createContent(data: any) { + return request('/api/contents', data, 'POST'); +} + +// 更新内容 +export function updateContent(contentId: string, data: any) { + return request(`/api/contents/${contentId}`, data, 'PUT'); +} + +// 删除内容 +export function deleteContent(contentId: string) { + return request(`/api/contents/${contentId}`, undefined, 'DELETE'); +} + +// ==================== 流量池相关接口 ==================== + +// 获取流量池列表 +export function getTrafficPools() { + return request('/api/traffic-pools', undefined, 'GET'); +} + +// 获取流量池详情 +export function getTrafficPoolDetail(poolId: string) { + return request(`/api/traffic-pools/${poolId}`, undefined, 'GET'); +} + +// 创建流量池 +export function createTrafficPool(data: any) { + return request('/api/traffic-pools', data, 'POST'); +} + +// 更新流量池 +export function updateTrafficPool(poolId: string, data: any) { + return request(`/api/traffic-pools/${poolId}`, data, 'PUT'); +} + +// 删除流量池 +export function deleteTrafficPool(poolId: string) { + return request(`/api/traffic-pools/${poolId}`, undefined, 'DELETE'); +} + +// ==================== 工作台相关接口 ==================== + +// 获取工作台统计数据 +export function getWorkspaceStats() { + return request('/api/workspace/stats', undefined, 'GET'); +} + +// 获取自动点赞任务列表 +export function getAutoLikeTasks() { + return request('/api/workspace/auto-like/tasks', undefined, 'GET'); +} + +// 创建自动点赞任务 +export function createAutoLikeTask(data: any) { + return request('/api/workspace/auto-like/tasks', data, 'POST'); +} + +// 更新自动点赞任务 +export function updateAutoLikeTask(taskId: string, data: any) { + return request(`/api/workspace/auto-like/tasks/${taskId}`, data, 'PUT'); +} + +// 删除自动点赞任务 +export function deleteAutoLikeTask(taskId: string) { + return request(`/api/workspace/auto-like/tasks/${taskId}`, undefined, 'DELETE'); +} + +// ==================== 群发相关接口 ==================== + +// 获取群发任务列表 +export function getGroupPushTasks() { + return request('/api/workspace/group-push/tasks', undefined, 'GET'); +} + +// 创建群发任务 +export function createGroupPushTask(data: any) { + return request('/api/workspace/group-push/tasks', data, 'POST'); +} + +// 更新群发任务 +export function updateGroupPushTask(taskId: string, data: any) { + return request(`/api/workspace/group-push/tasks/${taskId}`, data, 'PUT'); +} + +// 删除群发任务 +export function deleteGroupPushTask(taskId: string) { + return request(`/api/workspace/group-push/tasks/${taskId}`, undefined, 'DELETE'); +} + +// ==================== 自动建群相关接口 ==================== + +// 获取自动建群任务列表 +export function getAutoGroupTasks() { + return request('/api/workspace/auto-group/tasks', undefined, 'GET'); +} + +// 创建自动建群任务 +export function createAutoGroupTask(data: any) { + return request('/api/workspace/auto-group/tasks', data, 'POST'); +} + +// 更新自动建群任务 +export function updateAutoGroupTask(taskId: string, data: any) { + return request(`/api/workspace/auto-group/tasks/${taskId}`, data, 'PUT'); +} + +// 删除自动建群任务 +export function deleteAutoGroupTask(taskId: string) { + return request(`/api/workspace/auto-group/tasks/${taskId}`, undefined, 'DELETE'); +} + +// ==================== AI助手相关接口 ==================== + +// 获取AI对话历史 +export function getAIChatHistory() { + return request('/api/workspace/ai-assistant/chat-history', undefined, 'GET'); +} + +// 发送AI消息 +export function sendAIMessage(data: any) { + return request('/api/workspace/ai-assistant/send-message', data, 'POST'); +} + +// 获取AI分析报告 +export function getAIAnalysisReport() { + return request('/api/workspace/ai-assistant/analysis-report', undefined, 'GET'); +} + +// ==================== 订单相关接口 ==================== + +// 获取订单列表 +export function getOrders(params: any) { + return request('/api/orders', params, 'GET'); +} + +// 获取订单详情 +export function getOrderDetail(orderId: string) { + return request(`/api/orders/${orderId}`, undefined, 'GET'); +} + +// 创建订单 +export function createOrder(data: any) { + return request('/api/orders', data, 'POST'); +} + +// 更新订单 +export function updateOrder(orderId: string, data: any) { + return request(`/api/orders/${orderId}`, data, 'PUT'); +} + +// 删除订单 +export function deleteOrder(orderId: string) { + return request(`/api/orders/${orderId}`, undefined, 'DELETE'); +} + +// ==================== 用户相关接口 ==================== + +// 获取用户信息 +export function getUserInfo() { + return request('/api/user/info', undefined, 'GET'); +} + +// 更新用户信息 +export function updateUserInfo(data: any) { + return request('/api/user/info', data, 'PUT'); +} + +// 修改密码 +export function changePassword(data: any) { + return request('/api/user/change-password', data, 'POST'); +} + +// 上传头像 +export function uploadAvatar(data: any) { + return request('/api/user/upload-avatar', data, 'POST'); +} + +// ==================== 文件上传相关接口 ==================== + +// 上传文件 +export function uploadFile(data: any) { + return request('/api/upload/file', data, 'POST'); +} + +// 上传图片 +export function uploadImage(data: any) { + return request('/api/upload/image', data, 'POST'); +} + +// 删除文件 +export function deleteFile(fileId: string) { + return request(`/api/upload/files/${fileId}`, undefined, 'DELETE'); +} + +// ==================== 系统配置相关接口 ==================== + +// 获取系统配置 +export function getSystemConfig() { + return request('/api/system/config', undefined, 'GET'); +} + +// 更新系统配置 +export function updateSystemConfig(data: any) { + return request('/api/system/config', data, 'PUT'); +} + +// 获取系统通知 +export function getSystemNotifications() { + return request('/api/system/notifications', undefined, 'GET'); +} + +// 标记通知为已读 +export function markNotificationAsRead(notificationId: string) { + return request(`/api/system/notifications/${notificationId}/read`, undefined, 'PUT'); +} diff --git a/nkebao/src/router/module/index.tsx b/nkebao/src/router/module/index.tsx index 60033084..44eb2faf 100644 --- a/nkebao/src/router/module/index.tsx +++ b/nkebao/src/router/module/index.tsx @@ -1,5 +1,4 @@ import Home from "@/pages/home/index"; -import Scene from "@/pages/scene/index"; import Mine from "@/pages/mine/index"; import Workspace from "@/pages/workspace/main"; @@ -10,11 +9,6 @@ const routes = [ element: , auth: true, // 需要登录 }, - { - path: "/scene", - element: , - auth: true, - }, { path: "/work", element: , diff --git a/nkebao/src/router/module/scenarios.tsx b/nkebao/src/router/module/scenarios.tsx index a1b9ff5b..a7480bfa 100644 --- a/nkebao/src/router/module/scenarios.tsx +++ b/nkebao/src/router/module/scenarios.tsx @@ -1,17 +1,11 @@ -import Scene from "@/pages/scene"; -import Scenarios from "@/pages/scenarios/Scenarios"; +import ScenariosList from "@/pages/scenarios/list"; import NewPlan from "@/pages/scenarios/new/page"; import ScenarioList from "@/pages/scenarios/ScenarioList"; const scenarioRoutes = [ - { - path: "/scene", - element: , - auth: true, - }, { path: "/scenarios", - element: , + element: , auth: true, }, {