From 289947e68f99246f8b5c3387d343ad8f9d9d31cf 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, 7 Jul 2025 16:13:04 +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=9B=B4=E4=BA=86=E5=85=A8=E5=B1=80layout=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/src/components/Layout.css | 10 + nkebao/src/components/Layout.tsx | 35 +++ nkebao/src/pages/Home.tsx | 30 +-- nkebao/src/pages/devices/Devices.tsx | 41 ++-- nkebao/src/pages/profile/Profile.tsx | 148 ++++++------ nkebao/src/pages/scenarios/Scenarios.tsx | 166 +++++++------ .../pages/scenarios/douyin/DouyinScenario.tsx | 219 +++++++++--------- .../gongzhonghao/GongzhonghaoScenario.tsx | 102 ++++---- .../pages/scenarios/haibao/HaibaoScenario.tsx | 219 +++++++++--------- 9 files changed, 529 insertions(+), 441 deletions(-) create mode 100644 nkebao/src/components/Layout.css create mode 100644 nkebao/src/components/Layout.tsx diff --git a/nkebao/src/components/Layout.css b/nkebao/src/components/Layout.css new file mode 100644 index 00000000..b1af68a6 --- /dev/null +++ b/nkebao/src/components/Layout.css @@ -0,0 +1,10 @@ +.container { + display: flex; + height: 100vh; + flex-direction: column; +} + +.container main { + flex: 1; + overflow: auto; +} \ No newline at end of file diff --git a/nkebao/src/components/Layout.tsx b/nkebao/src/components/Layout.tsx new file mode 100644 index 00000000..5ea387ef --- /dev/null +++ b/nkebao/src/components/Layout.tsx @@ -0,0 +1,35 @@ +import React from 'react'; + +interface LayoutProps { + loading?: boolean; + children?: React.ReactNode; + header?: React.ReactNode; + footer?: React.ReactNode; +} + +const Layout: React.FC = ({ + loading, + children, + header, + footer +}) => { + return ( +
+ {header && ( +
+ {header} +
+ )} +
+ {children} +
+ {footer && ( +
+ {footer} +
+ )} +
+ ); +}; + +export default Layout; \ No newline at end of file diff --git a/nkebao/src/pages/Home.tsx b/nkebao/src/pages/Home.tsx index 9795732f..0c9cada7 100644 --- a/nkebao/src/pages/Home.tsx +++ b/nkebao/src/pages/Home.tsx @@ -2,6 +2,9 @@ import React, { useEffect, useRef } from 'react'; import { useNavigate } from 'react-router-dom'; import { Bell, Smartphone, Users, Activity } from 'lucide-react'; import Chart from 'chart.js/auto'; +import Layout from '@/components/Layout'; +import BottomNav from '@/components/BottomNav'; +import '@/components/Layout.css'; // 模拟数据 const stats = { @@ -140,19 +143,20 @@ export default function Home() { }, []); return ( -
- {/* 固定header */} -
-
-

存客宝

- + +
+

存客宝

+ +
-
- - {/* 可滚动的内容区域 */} -
+ } + footer={} + > +
{/* 统计卡片 */}
@@ -234,6 +238,6 @@ export default function Home() {
-
+ ); } \ No newline at end of file diff --git a/nkebao/src/pages/devices/Devices.tsx b/nkebao/src/pages/devices/Devices.tsx index d6300f29..3d821439 100644 --- a/nkebao/src/pages/devices/Devices.tsx +++ b/nkebao/src/pages/devices/Devices.tsx @@ -4,6 +4,8 @@ import { Plus, Search, RefreshCw, QrCode, Loader2, AlertTriangle, X } from 'luci import { devicesApi } from '@/api'; import { useToast } from '@/components/ui/toast'; import PageHeader from '@/components/PageHeader'; +import Layout from '@/components/Layout'; +import '@/components/Layout.css'; // 设备接口 interface Device { @@ -376,24 +378,24 @@ export default function Devices() { }); return ( -
- {/* 页面Header */} - - - 添加设备 - - } - /> - - {/* 内容区域 */} -
+ + + 添加设备 + + } + /> + } + > +
{/* 统计卡片 */}
@@ -500,7 +502,6 @@ export default function Devices() {
- {/* 添加设备弹窗 */} {isAddDeviceOpen && (
@@ -713,6 +714,6 @@ export default function Devices() {
)} -
+ ); } \ No newline at end of file diff --git a/nkebao/src/pages/profile/Profile.tsx b/nkebao/src/pages/profile/Profile.tsx index 5dbe2ce5..5ef667d0 100644 --- a/nkebao/src/pages/profile/Profile.tsx +++ b/nkebao/src/pages/profile/Profile.tsx @@ -7,6 +7,9 @@ import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { useAuth } from '@/contexts/AuthContext'; import { useToast } from '@/components/ui/toast'; +import Layout from '@/components/Layout'; +import BottomNav from '@/components/BottomNav'; +import '@/components/Layout.css'; const menuItems = [ { href: '/devices', label: '设备管理' }, @@ -52,85 +55,88 @@ export default function Profile() {
); } - return ( -
-
-
-

我的

-
- - + +
+

我的

+
+ + +
-
- -
- {/* 用户信息卡片 */} - -
- - - - {(userInfo?.username || user?.username || '用户').slice(0, 2).toUpperCase()} - - -
-

- {userInfo?.username || user?.username || '用户'} -

-

- 账号: {userInfo?.account || user?.account || Math.floor(10000000 + Math.random() * 90000000).toString()} -

-
- + } + footer={} + > +
+
+ {/* 用户信息卡片 */} + +
+ + + + {(userInfo?.username || user?.username || '用户').slice(0, 2).toUpperCase()} + + +
+

+ {userInfo?.username || user?.username || '用户'} +

+

+ 账号: {userInfo?.account || user?.account || Math.floor(10000000 + Math.random() * 90000000).toString()} +

+
+ +
-
- + - {/* 功能菜单 */} - - {menuItems.map((item) => ( -
(item.href ? navigate(item.href) : null)} - > -
- {item.label} + {/* 功能菜单 */} + + {menuItems.map((item) => ( +
(item.href ? navigate(item.href) : null)} + > +
+ {item.label} +
+
- -
- ))} - + ))} + - {/* 退出登录按钮 */} - + {/* 退出登录按钮 */} + +
- {/* 退出登录确认对话框 */} @@ -150,6 +156,6 @@ export default function Profile() {
-
+ ); } \ No newline at end of file diff --git a/nkebao/src/pages/scenarios/Scenarios.tsx b/nkebao/src/pages/scenarios/Scenarios.tsx index 38c04143..a0c24c06 100644 --- a/nkebao/src/pages/scenarios/Scenarios.tsx +++ b/nkebao/src/pages/scenarios/Scenarios.tsx @@ -2,6 +2,9 @@ import React, { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { Plus, TrendingUp } from 'lucide-react'; import PageHeader from '@/components/PageHeader'; +import Layout from '@/components/Layout'; +import BottomNav from '@/components/BottomNav'; +import '@/components/Layout.css'; interface Scenario { id: string; @@ -107,94 +110,74 @@ export default function Scenarios() { if (loading) { return ( -
- -
-
加载中...
+ + } + footer={} + > +
+
+
加载中...
+
-
+ ); } - if (error) { return ( -
+ + } + footer={} + > +
+
{error}
+
+
+ ); + } + return ( + -
{error}
-
- ); - } - - return ( -
- navigate('/scenarios/new')} - className="flex items-center px-3 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors text-sm" - > - - 新建计划 - - } - /> - -
-
- {scenarios.map((scenario) => ( -
navigate(`/scenarios/${scenario.id}/detail`)} + rightContent={ +
- ))} -
- - {/* AI智能获客部分(暂时注释掉,可以后续启用) */} - {/* -
-
-

AI智能获客

- - Beta - -
- + + 新建计划 + + } + /> + } + footer={} + > +
+
- {aiScenarios.map((scenario) => ( + {scenarios.map((scenario) => (
navigate(scenario.path)} + onClick={() => navigate(`/scenarios/${scenario.id}/detail`)} >
-
{scenario.icon}
+ {scenario.name}

{scenario.name}

-

{scenario.description}

+ {scenario.description && ( +

{scenario.description}

+ )}
今日: {scenario.count} @@ -207,9 +190,44 @@ export default function Scenarios() {
))}
+ + {/* AI智能获客部分(暂时注释掉,可以后续启用) */} + {/* +
+
+

AI智能获客

+ + Beta + +
+ +
+ {aiScenarios.map((scenario) => ( +
navigate(scenario.path)} + > +
+
{scenario.icon}
+

{scenario.name}

+

{scenario.description}

+
+ 今日: + {scenario.count} +
+
+ + {scenario.growth} +
+
+
+ ))} +
+
+ */}
- */}
-
+ ); } \ No newline at end of file diff --git a/nkebao/src/pages/scenarios/douyin/DouyinScenario.tsx b/nkebao/src/pages/scenarios/douyin/DouyinScenario.tsx index da79a968..9f17b092 100644 --- a/nkebao/src/pages/scenarios/douyin/DouyinScenario.tsx +++ b/nkebao/src/pages/scenarios/douyin/DouyinScenario.tsx @@ -3,6 +3,8 @@ import { useNavigate } from 'react-router-dom'; import { Plus, Copy, Trash2, Play, Pause, TrendingUp, Users, Clock } from 'lucide-react'; import PageHeader from '@/components/PageHeader'; import { useToast } from '@/components/ui/toast'; +import Layout from '@/components/Layout'; +import '@/components/Layout.css'; interface Task { id: string; @@ -122,123 +124,126 @@ export default function DouyinScenario() { }; return ( -
- navigate('/scenarios/new?scenario=douyin')} - className="flex items-center px-3 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors text-sm" - > - - 新建计划 - - } - /> - - {/* 添加pt-16来避免被固定导航栏遮挡 */} -
- {tasks.map((task) => ( -
- {/* 任务头部 */} -
-
-
-
- {getStatusIcon(task.status)} -
-
-

{task.name}

-
- - {getStatusText(task.status)} - - 最后更新: {task.lastUpdated} + navigate('/scenarios/new?scenario=douyin')} + className="flex items-center px-3 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors text-sm" + > + + 新建计划 + + } + /> + } + > +
+
+ {tasks.map((task) => ( +
+ {/* 任务头部 */} +
+
+
+
+ {getStatusIcon(task.status)} +
+
+

{task.name}

+
+ + {getStatusText(task.status)} + + 最后更新: {task.lastUpdated} +
+
+ + +
-
- - +
+ + {/* 统计信息 */} +
+
+
+
+ + 设备数 +
+
{task.stats.devices}
+
+
+
+ + 已获客 +
+
{task.stats.acquired}
+
+
+
+ + 已添加 +
+
{task.stats.added}
+
+
+ + {/* 趋势图表 */} +
+

7天趋势

+
+ {task.trend.map((item, index) => ( +
+
+ {item.date} +
+ ))} +
+
+ + {/* 执行信息 */} +
+
+ 执行时间: {task.executionTime} + {task.nextExecutionTime} +
+ ))} - {/* 统计信息 */} -
-
-
-
- - 设备数 -
-
{task.stats.devices}
-
-
-
- - 已获客 -
-
{task.stats.acquired}
-
-
-
- - 已添加 -
-
{task.stats.added}
-
-
- - {/* 趋势图表 */} -
-

7天趋势

-
- {task.trend.map((item, index) => ( -
-
- {item.date} -
- ))} -
-
- - {/* 执行信息 */} -
-
- 执行时间: {task.executionTime} - {task.nextExecutionTime} -
-
+ {/* 设备树图表 */} +
+

设备分布

+
+ +

设备树图表功能开发中...

- ))} - - {/* 设备树图表 */} -
-

设备分布

-
- -

设备树图表功能开发中...

-
-
+ ); } \ 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 89c0cba8..dbc7cb38 100644 --- a/nkebao/src/pages/scenarios/gongzhonghao/GongzhonghaoScenario.tsx +++ b/nkebao/src/pages/scenarios/gongzhonghao/GongzhonghaoScenario.tsx @@ -16,6 +16,8 @@ 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'; +import Layout from '@/components/Layout'; +import '@/components/Layout.css'; import { fetchSceneName, fetchPlanList, @@ -212,58 +214,60 @@ export default function GongzhonghaoScenario() { }; return ( -
- - - 新建计划 - - } - /> - - {/* 添加pt-16来避免被固定导航栏遮挡 */} -
-
- {loading ? ( -
加载中...
- ) : error ? ( -
{error}
- ) : tasks.length > 0 ? ( - <> - {tasks.map((task) => ( -
- handleEditPlan(task.id)} - onCopy={handleCopyPlan} - onDelete={handleDeletePlan} - onStatusChange={handleStatusChange} - onOpenSettings={handleOpenApiSettings} - /> + + + 新建计划 + + } + /> + } + > +
+
+
+ {loading ? ( +
加载中...
+ ) : error ? ( +
{error}
+ ) : tasks.length > 0 ? ( + <> + {tasks.map((task) => ( +
+ handleEditPlan(task.id)} + onCopy={handleCopyPlan} + onDelete={handleDeletePlan} + onStatusChange={handleStatusChange} + onOpenSettings={handleOpenApiSettings} + /> +
+ ))} +
+ + 第 {page} 页 / 共 {Math.max(1, Math.ceil(total / pageSize))} 页 +
- ))} -
- - 第 {page} 页 / 共 {Math.max(1, Math.ceil(total / pageSize))} 页 - + + ) : ( +
+
暂无获客计划
+
- - ) : ( -
-
暂无获客计划
- -
- )} + )} +
- {/* API接口设置对话框 */} @@ -432,6 +436,6 @@ export default function GongzhonghaoScenario() { -
+
); } diff --git a/nkebao/src/pages/scenarios/haibao/HaibaoScenario.tsx b/nkebao/src/pages/scenarios/haibao/HaibaoScenario.tsx index 6b6d3d44..a4f271e8 100644 --- a/nkebao/src/pages/scenarios/haibao/HaibaoScenario.tsx +++ b/nkebao/src/pages/scenarios/haibao/HaibaoScenario.tsx @@ -3,6 +3,8 @@ import { useNavigate } from 'react-router-dom'; import { Plus, Copy, Trash2, Play, Pause, TrendingUp, Users, Clock, Image } from 'lucide-react'; import PageHeader from '@/components/PageHeader'; import { useToast } from '@/components/ui/toast'; +import Layout from '@/components/Layout'; +import '@/components/Layout.css'; interface Task { id: string; @@ -122,123 +124,126 @@ export default function HaibaoScenario() { }; return ( -
- navigate('/scenarios/new?scenario=haibao')} - className="flex items-center px-3 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors text-sm" - > - - 新建计划 - - } - /> - - {/* 添加pt-16来避免被固定导航栏遮挡 */} -
- {tasks.map((task) => ( -
- {/* 任务头部 */} -
-
-
-
- {getStatusIcon(task.status)} -
-
-

{task.name}

-
- - {getStatusText(task.status)} - - 最后更新: {task.lastUpdated} + navigate('/scenarios/new?scenario=haibao')} + className="flex items-center px-3 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors text-sm" + > + + 新建计划 + + } + /> + } + > +
+
+ {tasks.map((task) => ( +
+ {/* 任务头部 */} +
+
+
+
+ {getStatusIcon(task.status)} +
+
+

{task.name}

+
+ + {getStatusText(task.status)} + + 最后更新: {task.lastUpdated} +
+
+ + +
-
- - +
+ + {/* 统计信息 */} +
+
+
+
+ + 设备数 +
+
{task.stats.devices}
+
+
+
+ + 已获客 +
+
{task.stats.acquired}
+
+
+
+ + 已添加 +
+
{task.stats.added}
+
+
+ + {/* 趋势图表 */} +
+

7天趋势

+
+ {task.trend.map((item, index) => ( +
+
+ {item.date} +
+ ))} +
+
+ + {/* 执行信息 */} +
+
+ 执行时间: {task.executionTime} + {task.nextExecutionTime} +
+ ))} - {/* 统计信息 */} -
-
-
-
- - 设备数 -
-
{task.stats.devices}
-
-
-
- - 已获客 -
-
{task.stats.acquired}
-
-
-
- - 已添加 -
-
{task.stats.added}
-
-
- - {/* 趋势图表 */} -
-

7天趋势

-
- {task.trend.map((item, index) => ( -
-
- {item.date} -
- ))} -
-
- - {/* 执行信息 */} -
-
- 执行时间: {task.executionTime} - {task.nextExecutionTime} -
-
+ {/* 海报管理 */} +
+

海报管理

+
+ +

海报管理功能开发中...

- ))} - - {/* 海报管理 */} -
-

海报管理

-
- -

海报管理功能开发中...

-
-
+ ); } \ No newline at end of file