feat: 本次提交更新内容如下
搞登录拦截模块
This commit is contained in:
@@ -6,6 +6,8 @@
|
|||||||
"antd": "^5.13.1",
|
"antd": "^5.13.1",
|
||||||
"antd-mobile": "^5.39.1",
|
"antd-mobile": "^5.39.1",
|
||||||
"axios": "^1.6.7",
|
"axios": "^1.6.7",
|
||||||
|
"echarts": "^5.6.0",
|
||||||
|
"echarts-for-react": "^3.0.2",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-router-dom": "^6.20.0",
|
"react-router-dom": "^6.20.0",
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import AppRouter from "@/router";
|
import AppRouter from "@/router";
|
||||||
import { Button } from "antd";
|
|
||||||
import { Button as MobileButton } from "antd-mobile";
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import "layout.module.scss";
|
import styles from "./layout.module.scss";
|
||||||
interface LayoutProps {
|
interface LayoutProps {
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
@@ -9,9 +9,9 @@ interface LayoutProps {
|
|||||||
|
|
||||||
const Layout: React.FC<LayoutProps> = ({ children, header, footer }) => {
|
const Layout: React.FC<LayoutProps> = ({ children, header, footer }) => {
|
||||||
return (
|
return (
|
||||||
<div className="container">
|
<div className={styles.container}>
|
||||||
{header && <header>{header}</header>}
|
{header && <header>{header}</header>}
|
||||||
<main className="bg-gray-50">{children}</main>
|
<main>{children}</main>
|
||||||
{footer && <footer>{footer}</footer>}
|
{footer && <footer>{footer}</footer>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
53
nkebao/src/components/LineChart.tsx
Normal file
53
nkebao/src/components/LineChart.tsx
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import React from "react";
|
||||||
|
import ReactECharts from "echarts-for-react";
|
||||||
|
|
||||||
|
interface LineChartProps {
|
||||||
|
title?: string;
|
||||||
|
xData: string[];
|
||||||
|
yData: number[];
|
||||||
|
height?: number | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LineChart: React.FC<LineChartProps> = ({
|
||||||
|
title = "",
|
||||||
|
xData,
|
||||||
|
yData,
|
||||||
|
height = 200,
|
||||||
|
}) => {
|
||||||
|
const option = {
|
||||||
|
title: {
|
||||||
|
text: title,
|
||||||
|
left: "center",
|
||||||
|
textStyle: { fontSize: 16 },
|
||||||
|
},
|
||||||
|
tooltip: { trigger: "axis" },
|
||||||
|
xAxis: {
|
||||||
|
type: "category",
|
||||||
|
data: xData,
|
||||||
|
boundaryGap: false,
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: "value",
|
||||||
|
boundaryGap: ["10%", "10%"], // 上下留白
|
||||||
|
min: (value: any) => value.min - 10, // 下方多留一点空间
|
||||||
|
max: (value: any) => value.max + 10, // 上方多留一点空间
|
||||||
|
minInterval: 1,
|
||||||
|
axisLabel: { margin: 12 },
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: yData,
|
||||||
|
type: "line",
|
||||||
|
smooth: true,
|
||||||
|
symbol: "circle",
|
||||||
|
lineStyle: { color: "#1677ff" },
|
||||||
|
itemStyle: { color: "#1677ff" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
grid: { left: 40, right: 24, top: 40, bottom: 32 },
|
||||||
|
};
|
||||||
|
|
||||||
|
return <ReactECharts option={option} style={{ height, width: "100%" }} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LineChart;
|
||||||
81
nkebao/src/components/MeauMobile/MeauMoible.tsx
Normal file
81
nkebao/src/components/MeauMobile/MeauMoible.tsx
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import { TabBar } from "antd-mobile";
|
||||||
|
import {
|
||||||
|
AppOutline,
|
||||||
|
ShopbagOutline,
|
||||||
|
PieOutline,
|
||||||
|
UserOutline,
|
||||||
|
} from "antd-mobile-icons";
|
||||||
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
|
const tabs = [
|
||||||
|
{
|
||||||
|
key: "home",
|
||||||
|
title: "首页",
|
||||||
|
icon: <AppOutline />,
|
||||||
|
path: "/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "scene",
|
||||||
|
title: "场景获客",
|
||||||
|
icon: <ShopbagOutline />,
|
||||||
|
path: "/scene",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "work",
|
||||||
|
title: "工作台",
|
||||||
|
icon: <PieOutline />,
|
||||||
|
path: "/work",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "mine",
|
||||||
|
title: "我的",
|
||||||
|
icon: <UserOutline />,
|
||||||
|
path: "/mine",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// 需要展示菜单的路由白名单(可根据实际业务调整)
|
||||||
|
const menuPaths = ["/", "/scene", "/work", "/mine"];
|
||||||
|
|
||||||
|
const MeauMobile: React.FC = () => {
|
||||||
|
const location = useLocation();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [activeKey, setActiveKey] = useState("home");
|
||||||
|
|
||||||
|
// 根据当前路由自动设置 activeKey,支持嵌套路由
|
||||||
|
useEffect(() => {
|
||||||
|
const found = tabs.find((tab) =>
|
||||||
|
tab.path === "/"
|
||||||
|
? location.pathname === "/"
|
||||||
|
: location.pathname.startsWith(tab.path)
|
||||||
|
);
|
||||||
|
if (found) setActiveKey(found.key);
|
||||||
|
}, [location.pathname]);
|
||||||
|
|
||||||
|
// 判断当前路由是否需要展示菜单
|
||||||
|
const showMenu = menuPaths.some((path) =>
|
||||||
|
path === "/"
|
||||||
|
? location.pathname === "/"
|
||||||
|
: location.pathname.startsWith(path)
|
||||||
|
);
|
||||||
|
if (!showMenu) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TabBar
|
||||||
|
style={{ background: "#fff" }}
|
||||||
|
activeKey={activeKey}
|
||||||
|
onChange={(key) => {
|
||||||
|
setActiveKey(key);
|
||||||
|
const tab = tabs.find((t) => t.key === key);
|
||||||
|
if (tab && tab.path) navigate(tab.path);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tabs.map((item) => (
|
||||||
|
<TabBar.Item key={item.key} icon={item.icon} title={item.title} />
|
||||||
|
))}
|
||||||
|
</TabBar>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MeauMobile;
|
||||||
@@ -10,5 +10,22 @@ export function getWechatStats() {
|
|||||||
return request('/v1/dashboard/wechat-stats', {}, 'GET');
|
return request('/v1/dashboard/wechat-stats', {}, 'GET');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 你可以根据需要继续添加其他接口
|
// 今日数据统计
|
||||||
// 例如:场景获客统计、今日数据统计等
|
export function getTodayStats() {
|
||||||
|
return request('/v1/dashboard/today-stats', {}, 'GET');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 首页仪表盘总览
|
||||||
|
export function getDashboard() {
|
||||||
|
return request('/v1/dashboard', {}, 'GET');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获客场景统计
|
||||||
|
export function getPlanStats(params:any) {
|
||||||
|
return request('/v1/dashboard/plan-stats', params, 'GET');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 近七天统计
|
||||||
|
export function getSevenDayStats() {
|
||||||
|
return request('/v1/dashboard/sevenDay-stats', {}, 'GET');
|
||||||
|
}
|
||||||
|
|||||||
70
nkebao/src/pages/home/index.module.scss
Normal file
70
nkebao/src/pages/home/index.module.scss
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
.home-page {
|
||||||
|
padding: 12px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-cards {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
> :global(.adm-card) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-section {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-section-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-scene-stats {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 0 8px 8px 8px;
|
||||||
|
}
|
||||||
|
.home-scene-item {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.home-scene-icon {
|
||||||
|
margin: 0 auto 4px auto;
|
||||||
|
}
|
||||||
|
.home-scene-value {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #1677ff;
|
||||||
|
}
|
||||||
|
.home-scene-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-today-item {
|
||||||
|
text-align: center;
|
||||||
|
padding: 8px 0;
|
||||||
|
background: #f7f8fa;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
.home-today-value {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #1677ff;
|
||||||
|
}
|
||||||
|
.home-today-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-chart {
|
||||||
|
margin-top: 8px;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100px;
|
||||||
|
}
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
.home-page {
|
|
||||||
padding: 16px;
|
|
||||||
background: #f5f5f5;
|
|
||||||
min-height: 100vh;
|
|
||||||
.home-title {
|
|
||||||
font-size: 22px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
color: #1677ff;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.home-cards {
|
|
||||||
display: flex;
|
|
||||||
gap: 16px;
|
|
||||||
flex-direction: column;
|
|
||||||
.home-card {
|
|
||||||
.home-card-value {
|
|
||||||
font-size: 28px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #1677ff;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
.home-card-desc {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #888;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.home-loading {
|
|
||||||
margin-top: 24px;
|
|
||||||
text-align: center;
|
|
||||||
color: #999;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +1,121 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Card, Toast } from "antd-mobile";
|
import { Card, NavBar, TabBar, Grid } from "antd-mobile";
|
||||||
import { getDeviceStats, getWechatStats } from "./api";
|
import {
|
||||||
import Layout from "@/com/Layout/Layout";
|
AppOutline,
|
||||||
import "./index.scss";
|
UserOutline,
|
||||||
|
PieOutline, // 替换 BarChartOutline
|
||||||
|
ShopbagOutline,
|
||||||
|
} from "antd-mobile-icons";
|
||||||
|
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
||||||
|
import Layout from "@/components/Layout/Layout";
|
||||||
|
import style from "./index.module.scss";
|
||||||
|
import LineChart from "@/components/LineChart";
|
||||||
|
import { getPlanStats } from "./api";
|
||||||
|
|
||||||
|
const sceneStats = [
|
||||||
|
{
|
||||||
|
label: "公众号获客",
|
||||||
|
value: 234,
|
||||||
|
icon: <ShopbagOutline style={{ fontSize: 28, color: "#4caf50" }} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "海报获客",
|
||||||
|
value: 167,
|
||||||
|
icon: <AppOutline style={{ fontSize: 28, color: "#ff9800" }} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "抖音获客",
|
||||||
|
value: 156,
|
||||||
|
icon: <PieOutline style={{ fontSize: 28, color: "#2196f3" }} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "小红书获客",
|
||||||
|
value: 89,
|
||||||
|
icon: <UserOutline style={{ fontSize: 28, color: "#e91e63" }} />,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const todayStats = [
|
||||||
|
{ label: "朋友圈同步", value: 12 },
|
||||||
|
{ label: "群发任务", value: 8 },
|
||||||
|
{ label: "获客转化", value: "85%" },
|
||||||
|
{ label: "系统活跃度", value: "98%" },
|
||||||
|
];
|
||||||
|
|
||||||
const Home: React.FC = () => {
|
const Home: React.FC = () => {
|
||||||
const [stats, setStats] = useState({
|
|
||||||
totalDevices: 0,
|
|
||||||
onlineDevices: 0,
|
|
||||||
totalWechatAccounts: 0,
|
|
||||||
onlineWechatAccounts: 0,
|
|
||||||
});
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchStats = async () => {
|
getPlanStats({ num: 4 }).then((res: any) => {
|
||||||
setLoading(true);
|
console.log(res);
|
||||||
try {
|
});
|
||||||
const [device, wechat] = await Promise.all([
|
|
||||||
getDeviceStats(),
|
|
||||||
getWechatStats(),
|
|
||||||
]);
|
|
||||||
setStats({
|
|
||||||
totalDevices: device.total || 0,
|
|
||||||
onlineDevices: device.online || 0,
|
|
||||||
totalWechatAccounts: wechat.total || 0,
|
|
||||||
onlineWechatAccounts: wechat.active || 0,
|
|
||||||
});
|
|
||||||
} catch (err: any) {
|
|
||||||
Toast.show({
|
|
||||||
content: err?.message || "数据加载失败",
|
|
||||||
position: "top",
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
fetchStats();
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout
|
<Layout
|
||||||
loading={loading}
|
header={
|
||||||
header={<h1 className="home-title">首页 Home</h1>}
|
<NavBar back={null} style={{ background: "#fff" }}>
|
||||||
footer={<div>底部内容</div>} // 如有底部内容可加
|
<span style={{ color: "#1677ff", fontWeight: 700 }}>存客宝</span>
|
||||||
|
</NavBar>
|
||||||
|
}
|
||||||
|
footer={<MeauMobile />}
|
||||||
>
|
>
|
||||||
<div className="home-cards">
|
<div className={style["home-page"]}>
|
||||||
<Card title="设备数量" className="home-card">
|
{/* 统计卡片 */}
|
||||||
<div className="home-card-value">{stats.totalDevices}</div>
|
<div className={style["home-cards"]}>
|
||||||
<div className="home-card-desc">在线设备:{stats.onlineDevices}</div>
|
<Card className={style["home-card"]}>
|
||||||
|
<div className={style["home-card-title"]}>设备数量</div>
|
||||||
|
<div className={style["home-card-value"]}>0</div>
|
||||||
|
</Card>
|
||||||
|
<Card className={style["home-card"]}>
|
||||||
|
<div className={style["home-card-title"]}>微信号数量</div>
|
||||||
|
<div className={style["home-card-value"]}>0</div>
|
||||||
|
</Card>
|
||||||
|
<Card className={style["home-card"]}>
|
||||||
|
<div className={style["home-card-title"]}>在线微信号</div>
|
||||||
|
<div className={style["home-card-value"]}>0</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 场景获客统计 */}
|
||||||
|
<Card className={style["home-section"]}>
|
||||||
|
<div className={style["home-section-title"]}>场景获客统计</div>
|
||||||
|
<div className={style["home-scene-stats"]}>
|
||||||
|
{sceneStats.map((item) => (
|
||||||
|
<div key={item.label} className={style["home-scene-item"]}>
|
||||||
|
<div className={style["home-scene-icon"]}>{item.icon}</div>
|
||||||
|
<div className={style["home-scene-value"]}>{item.value}</div>
|
||||||
|
<div className={style["home-scene-label"]}>{item.label}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<Card title="微信号数量" className="home-card">
|
|
||||||
<div className="home-card-value">{stats.totalWechatAccounts}</div>
|
{/* 今日数据 */}
|
||||||
<div className="home-card-desc">
|
<Card className={style["home-section"]}>
|
||||||
在线微信号:{stats.onlineWechatAccounts}
|
<div className={style["home-section-title"]}>今日数据</div>
|
||||||
|
<Grid columns={2} gap={12}>
|
||||||
|
{todayStats.map((item) => (
|
||||||
|
<Grid.Item key={item.label}>
|
||||||
|
<div className={style["home-today-item"]}>
|
||||||
|
<div className={style["home-today-value"]}>{item.value}</div>
|
||||||
|
<div className={style["home-today-label"]}>{item.label}</div>
|
||||||
|
</div>
|
||||||
|
</Grid.Item>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* 每日获客趋势(静态图表占位) */}
|
||||||
|
<Card className={style["home-section"]}>
|
||||||
|
<div className={style["home-section-title"]}>每日获客趋势</div>
|
||||||
|
<div className={style["home-chart"]}>
|
||||||
|
<LineChart
|
||||||
|
xData={["周一", "周二", "周三", "周四", "周五", "周六", "周日"]}
|
||||||
|
yData={[120, 150, 180, 200, 230, 210, 190]}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
{loading && <div className="home-loading">加载中...</div>}
|
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Home;
|
export default Home;
|
||||||
|
|||||||
0
nkebao/src/pages/login/login.module.scss
Normal file
0
nkebao/src/pages/login/login.module.scss
Normal file
74
nkebao/src/pages/login/login.tsx
Normal file
74
nkebao/src/pages/login/login.tsx
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import { Form, Input, Button, Toast } from "antd-mobile";
|
||||||
|
import { EyeInvisibleOutline, EyeOutline } from "antd-mobile-icons";
|
||||||
|
import request from "@/api/request";
|
||||||
|
import style from "./login.module.scss";
|
||||||
|
|
||||||
|
const Login: React.FC = () => {
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
|
||||||
|
const onFinish = async (values: any) => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
// 假设接口为 /api/login,实际请根据后端接口调整
|
||||||
|
const res = await request("/api/login", values, "POST");
|
||||||
|
// 登录成功后保存 token
|
||||||
|
localStorage.setItem("token", res.token);
|
||||||
|
Toast.show({ content: "登录成功", position: "top" });
|
||||||
|
// 跳转首页或其他页面
|
||||||
|
window.location.href = "/";
|
||||||
|
} catch (err: any) {
|
||||||
|
Toast.show({ content: err?.message || "登录失败", position: "top" });
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={style["login-page"]}>
|
||||||
|
<div className={style["login-title"]}>账号登录</div>
|
||||||
|
<Form
|
||||||
|
layout="horizontal"
|
||||||
|
onFinish={onFinish}
|
||||||
|
footer={
|
||||||
|
<Button
|
||||||
|
block
|
||||||
|
type="submit"
|
||||||
|
color="primary"
|
||||||
|
loading={loading}
|
||||||
|
size="large"
|
||||||
|
>
|
||||||
|
登录
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
name="username"
|
||||||
|
label="账号"
|
||||||
|
rules={[{ required: true, message: "请输入账号" }]}
|
||||||
|
>
|
||||||
|
<Input placeholder="请输入账号" clearable />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name="password"
|
||||||
|
label="密码"
|
||||||
|
rules={[{ required: true, message: "请输入密码" }]}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
placeholder="请输入密码"
|
||||||
|
clearable
|
||||||
|
type={visible ? "text" : "password"}
|
||||||
|
extra={
|
||||||
|
<div onClick={() => setVisible((v) => !v)}>
|
||||||
|
{visible ? <EyeOutline /> : <EyeInvisibleOutline />}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Login;
|
||||||
14
nkebao/src/pages/mine/api.ts
Normal file
14
nkebao/src/pages/mine/api.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import request from '@/api/request';
|
||||||
|
|
||||||
|
// 设备统计
|
||||||
|
export function getDeviceStats() {
|
||||||
|
return request('/v1/dashboard/device-stats', {}, 'GET');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 微信号统计
|
||||||
|
export function getWechatStats() {
|
||||||
|
return request('/v1/dashboard/wechat-stats', {}, 'GET');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 你可以根据需要继续添加其他接口
|
||||||
|
// 例如:场景获客统计、今日数据统计等
|
||||||
71
nkebao/src/pages/mine/index.module.scss
Normal file
71
nkebao/src/pages/mine/index.module.scss
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
.home-page {
|
||||||
|
padding: 12px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-cards {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
> :global(.adm-card) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-section {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-section-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
padding-left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-scene-stats {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 0 8px 8px 8px;
|
||||||
|
}
|
||||||
|
.home-scene-item {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.home-scene-icon {
|
||||||
|
margin: 0 auto 4px auto;
|
||||||
|
}
|
||||||
|
.home-scene-value {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #1677ff;
|
||||||
|
}
|
||||||
|
.home-scene-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-today-item {
|
||||||
|
text-align: center;
|
||||||
|
padding: 8px 0;
|
||||||
|
background: #f7f8fa;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
.home-today-value {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #1677ff;
|
||||||
|
}
|
||||||
|
.home-today-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-chart {
|
||||||
|
margin-top: 8px;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100px;
|
||||||
|
}
|
||||||
132
nkebao/src/pages/mine/index.tsx
Normal file
132
nkebao/src/pages/mine/index.tsx
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import { Card, NavBar, TabBar, Grid } from "antd-mobile";
|
||||||
|
import {
|
||||||
|
AppOutline,
|
||||||
|
UserOutline,
|
||||||
|
PieOutline, // 替换 BarChartOutline
|
||||||
|
ShopbagOutline,
|
||||||
|
BellOutline,
|
||||||
|
} from "antd-mobile-icons";
|
||||||
|
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
||||||
|
import Layout from "@/components/Layout/Layout";
|
||||||
|
import style from "./index.module.scss";
|
||||||
|
const sceneStats = [
|
||||||
|
{
|
||||||
|
label: "公众号获客",
|
||||||
|
value: 234,
|
||||||
|
icon: <ShopbagOutline style={{ fontSize: 28, color: "#4caf50" }} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "海报获客",
|
||||||
|
value: 167,
|
||||||
|
icon: <AppOutline style={{ fontSize: 28, color: "#ff9800" }} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "抖音获客",
|
||||||
|
value: 156,
|
||||||
|
icon: <PieOutline style={{ fontSize: 28, color: "#2196f3" }} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "小红书获客",
|
||||||
|
value: 89,
|
||||||
|
icon: <UserOutline style={{ fontSize: 28, color: "#e91e63" }} />,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const todayStats = [
|
||||||
|
{ label: "朋友圈同步", value: 12 },
|
||||||
|
{ label: "群发任务", value: 8 },
|
||||||
|
{ label: "获客转化", value: "85%" },
|
||||||
|
{ label: "系统活跃度", value: "98%" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const Home: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<Layout
|
||||||
|
header={
|
||||||
|
<NavBar back={null} style={{ background: "#fff" }}>
|
||||||
|
<span style={{ color: "#1677ff", fontWeight: 700 }}>存客宝</span>
|
||||||
|
</NavBar>
|
||||||
|
}
|
||||||
|
footer={<MeauMobile />}
|
||||||
|
>
|
||||||
|
<div className={style["home-page"]}>
|
||||||
|
{/* 统计卡片 */}
|
||||||
|
<div className={style["home-cards"]}>
|
||||||
|
<Card className={style["home-card"]}>
|
||||||
|
<div className={style["home-card-title"]}>设备数量</div>
|
||||||
|
<div className={style["home-card-value"]}>0</div>
|
||||||
|
</Card>
|
||||||
|
<Card className={style["home-card"]}>
|
||||||
|
<div className={style["home-card-title"]}>微信号数量</div>
|
||||||
|
<div className={style["home-card-value"]}>0</div>
|
||||||
|
</Card>
|
||||||
|
<Card className={style["home-card"]}>
|
||||||
|
<div className={style["home-card-title"]}>在线微信号</div>
|
||||||
|
<div className={style["home-card-value"]}>0</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 场景获客统计 */}
|
||||||
|
<Card className={style["home-section"]}>
|
||||||
|
<div className={style["home-section-title"]}>场景获客统计</div>
|
||||||
|
<div className={style["home-scene-stats"]}>
|
||||||
|
{sceneStats.map((item) => (
|
||||||
|
<div key={item.label} className={style["home-scene-item"]}>
|
||||||
|
<div className={style["home-scene-icon"]}>{item.icon}</div>
|
||||||
|
<div className={style["home-scene-value"]}>{item.value}</div>
|
||||||
|
<div className={style["home-scene-label"]}>{item.label}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* 今日数据 */}
|
||||||
|
<Card className={style["home-section"]}>
|
||||||
|
<div className={style["home-section-title"]}>今日数据</div>
|
||||||
|
<Grid columns={2} gap={12}>
|
||||||
|
{todayStats.map((item) => (
|
||||||
|
<Grid.Item key={item.label}>
|
||||||
|
<div className={style["home-today-item"]}>
|
||||||
|
<div className={style["home-today-value"]}>{item.value}</div>
|
||||||
|
<div className={style["home-today-label"]}>{item.label}</div>
|
||||||
|
</div>
|
||||||
|
</Grid.Item>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* 每日获客趋势(静态图表占位) */}
|
||||||
|
<Card className={style["home-section"]}>
|
||||||
|
<div className={style["home-section-title"]}>每日获客趋势</div>
|
||||||
|
<div className={style["home-chart"]}>
|
||||||
|
<svg width="100%" height="120">
|
||||||
|
<polyline
|
||||||
|
fill="none"
|
||||||
|
stroke="#1677ff"
|
||||||
|
strokeWidth="3"
|
||||||
|
points="10,100 40,80 70,60 100,50 130,40 160,60 190,80"
|
||||||
|
/>
|
||||||
|
{/* x轴文字 */}
|
||||||
|
{["周一", "周二", "周三", "周四", "周五", "周六", "周日"].map(
|
||||||
|
(d, i) => (
|
||||||
|
<text
|
||||||
|
key={d}
|
||||||
|
x={10 + i * 30}
|
||||||
|
y={115}
|
||||||
|
fontSize="12"
|
||||||
|
textAnchor="middle"
|
||||||
|
>
|
||||||
|
{d}
|
||||||
|
</text>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Home;
|
||||||
14
nkebao/src/pages/scene/api.ts
Normal file
14
nkebao/src/pages/scene/api.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import request from '@/api/request';
|
||||||
|
|
||||||
|
// 设备统计
|
||||||
|
export function getDeviceStats() {
|
||||||
|
return request('/v1/dashboard/device-stats', {}, 'GET');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 微信号统计
|
||||||
|
export function getWechatStats() {
|
||||||
|
return request('/v1/dashboard/wechat-stats', {}, 'GET');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 你可以根据需要继续添加其他接口
|
||||||
|
// 例如:场景获客统计、今日数据统计等
|
||||||
71
nkebao/src/pages/scene/index.module.scss
Normal file
71
nkebao/src/pages/scene/index.module.scss
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
.home-page {
|
||||||
|
padding: 12px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-cards {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
> :global(.adm-card) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-section {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-section-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
padding-left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-scene-stats {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 0 8px 8px 8px;
|
||||||
|
}
|
||||||
|
.home-scene-item {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.home-scene-icon {
|
||||||
|
margin: 0 auto 4px auto;
|
||||||
|
}
|
||||||
|
.home-scene-value {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #1677ff;
|
||||||
|
}
|
||||||
|
.home-scene-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-today-item {
|
||||||
|
text-align: center;
|
||||||
|
padding: 8px 0;
|
||||||
|
background: #f7f8fa;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
.home-today-value {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #1677ff;
|
||||||
|
}
|
||||||
|
.home-today-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-chart {
|
||||||
|
margin-top: 8px;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100px;
|
||||||
|
}
|
||||||
132
nkebao/src/pages/scene/index.tsx
Normal file
132
nkebao/src/pages/scene/index.tsx
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import { Card, NavBar, TabBar, Grid } from "antd-mobile";
|
||||||
|
import {
|
||||||
|
AppOutline,
|
||||||
|
UserOutline,
|
||||||
|
PieOutline, // 替换 BarChartOutline
|
||||||
|
ShopbagOutline,
|
||||||
|
BellOutline,
|
||||||
|
} from "antd-mobile-icons";
|
||||||
|
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
||||||
|
import Layout from "@/components/Layout/Layout";
|
||||||
|
import style from "./index.module.scss";
|
||||||
|
const sceneStats = [
|
||||||
|
{
|
||||||
|
label: "公众号获客",
|
||||||
|
value: 234,
|
||||||
|
icon: <ShopbagOutline style={{ fontSize: 28, color: "#4caf50" }} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "海报获客",
|
||||||
|
value: 167,
|
||||||
|
icon: <AppOutline style={{ fontSize: 28, color: "#ff9800" }} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "抖音获客",
|
||||||
|
value: 156,
|
||||||
|
icon: <PieOutline style={{ fontSize: 28, color: "#2196f3" }} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "小红书获客",
|
||||||
|
value: 89,
|
||||||
|
icon: <UserOutline style={{ fontSize: 28, color: "#e91e63" }} />,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const todayStats = [
|
||||||
|
{ label: "朋友圈同步", value: 12 },
|
||||||
|
{ label: "群发任务", value: 8 },
|
||||||
|
{ label: "获客转化", value: "85%" },
|
||||||
|
{ label: "系统活跃度", value: "98%" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const Home: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<Layout
|
||||||
|
header={
|
||||||
|
<NavBar back={null} style={{ background: "#fff" }}>
|
||||||
|
<span style={{ color: "#1677ff", fontWeight: 700 }}>存客宝</span>
|
||||||
|
</NavBar>
|
||||||
|
}
|
||||||
|
footer={<MeauMobile />}
|
||||||
|
>
|
||||||
|
<div className={style["home-page"]}>
|
||||||
|
{/* 统计卡片 */}
|
||||||
|
<div className={style["home-cards"]}>
|
||||||
|
<Card className={style["home-card"]}>
|
||||||
|
<div className={style["home-card-title"]}>设备数量</div>
|
||||||
|
<div className={style["home-card-value"]}>0</div>
|
||||||
|
</Card>
|
||||||
|
<Card className={style["home-card"]}>
|
||||||
|
<div className={style["home-card-title"]}>微信号数量</div>
|
||||||
|
<div className={style["home-card-value"]}>0</div>
|
||||||
|
</Card>
|
||||||
|
<Card className={style["home-card"]}>
|
||||||
|
<div className={style["home-card-title"]}>在线微信号</div>
|
||||||
|
<div className={style["home-card-value"]}>0</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 场景获客统计 */}
|
||||||
|
<Card className={style["home-section"]}>
|
||||||
|
<div className={style["home-section-title"]}>场景获客统计</div>
|
||||||
|
<div className={style["home-scene-stats"]}>
|
||||||
|
{sceneStats.map((item) => (
|
||||||
|
<div key={item.label} className={style["home-scene-item"]}>
|
||||||
|
<div className={style["home-scene-icon"]}>{item.icon}</div>
|
||||||
|
<div className={style["home-scene-value"]}>{item.value}</div>
|
||||||
|
<div className={style["home-scene-label"]}>{item.label}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* 今日数据 */}
|
||||||
|
<Card className={style["home-section"]}>
|
||||||
|
<div className={style["home-section-title"]}>今日数据</div>
|
||||||
|
<Grid columns={2} gap={12}>
|
||||||
|
{todayStats.map((item) => (
|
||||||
|
<Grid.Item key={item.label}>
|
||||||
|
<div className={style["home-today-item"]}>
|
||||||
|
<div className={style["home-today-value"]}>{item.value}</div>
|
||||||
|
<div className={style["home-today-label"]}>{item.label}</div>
|
||||||
|
</div>
|
||||||
|
</Grid.Item>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* 每日获客趋势(静态图表占位) */}
|
||||||
|
<Card className={style["home-section"]}>
|
||||||
|
<div className={style["home-section-title"]}>每日获客趋势</div>
|
||||||
|
<div className={style["home-chart"]}>
|
||||||
|
<svg width="100%" height="120">
|
||||||
|
<polyline
|
||||||
|
fill="none"
|
||||||
|
stroke="#1677ff"
|
||||||
|
strokeWidth="3"
|
||||||
|
points="10,100 40,80 70,60 100,50 130,40 160,60 190,80"
|
||||||
|
/>
|
||||||
|
{/* x轴文字 */}
|
||||||
|
{["周一", "周二", "周三", "周四", "周五", "周六", "周日"].map(
|
||||||
|
(d, i) => (
|
||||||
|
<text
|
||||||
|
key={d}
|
||||||
|
x={10 + i * 30}
|
||||||
|
y={115}
|
||||||
|
fontSize="12"
|
||||||
|
textAnchor="middle"
|
||||||
|
>
|
||||||
|
{d}
|
||||||
|
</text>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Home;
|
||||||
14
nkebao/src/pages/work/api.ts
Normal file
14
nkebao/src/pages/work/api.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import request from '@/api/request';
|
||||||
|
|
||||||
|
// 设备统计
|
||||||
|
export function getDeviceStats() {
|
||||||
|
return request('/v1/dashboard/device-stats', {}, 'GET');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 微信号统计
|
||||||
|
export function getWechatStats() {
|
||||||
|
return request('/v1/dashboard/wechat-stats', {}, 'GET');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 你可以根据需要继续添加其他接口
|
||||||
|
// 例如:场景获客统计、今日数据统计等
|
||||||
71
nkebao/src/pages/work/index.module.scss
Normal file
71
nkebao/src/pages/work/index.module.scss
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
.home-page {
|
||||||
|
padding: 12px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-cards {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
> :global(.adm-card) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-section {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-section-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
padding-left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-scene-stats {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 0 8px 8px 8px;
|
||||||
|
}
|
||||||
|
.home-scene-item {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.home-scene-icon {
|
||||||
|
margin: 0 auto 4px auto;
|
||||||
|
}
|
||||||
|
.home-scene-value {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #1677ff;
|
||||||
|
}
|
||||||
|
.home-scene-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-today-item {
|
||||||
|
text-align: center;
|
||||||
|
padding: 8px 0;
|
||||||
|
background: #f7f8fa;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
.home-today-value {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #1677ff;
|
||||||
|
}
|
||||||
|
.home-today-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-chart {
|
||||||
|
margin-top: 8px;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100px;
|
||||||
|
}
|
||||||
132
nkebao/src/pages/work/index.tsx
Normal file
132
nkebao/src/pages/work/index.tsx
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import { Card, NavBar, TabBar, Grid } from "antd-mobile";
|
||||||
|
import {
|
||||||
|
AppOutline,
|
||||||
|
UserOutline,
|
||||||
|
PieOutline, // 替换 BarChartOutline
|
||||||
|
ShopbagOutline,
|
||||||
|
BellOutline,
|
||||||
|
} from "antd-mobile-icons";
|
||||||
|
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
||||||
|
import Layout from "@/components/Layout/Layout";
|
||||||
|
import style from "./index.module.scss";
|
||||||
|
const sceneStats = [
|
||||||
|
{
|
||||||
|
label: "公众号获客",
|
||||||
|
value: 234,
|
||||||
|
icon: <ShopbagOutline style={{ fontSize: 28, color: "#4caf50" }} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "海报获客",
|
||||||
|
value: 167,
|
||||||
|
icon: <AppOutline style={{ fontSize: 28, color: "#ff9800" }} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "抖音获客",
|
||||||
|
value: 156,
|
||||||
|
icon: <PieOutline style={{ fontSize: 28, color: "#2196f3" }} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "小红书获客",
|
||||||
|
value: 89,
|
||||||
|
icon: <UserOutline style={{ fontSize: 28, color: "#e91e63" }} />,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const todayStats = [
|
||||||
|
{ label: "朋友圈同步", value: 12 },
|
||||||
|
{ label: "群发任务", value: 8 },
|
||||||
|
{ label: "获客转化", value: "85%" },
|
||||||
|
{ label: "系统活跃度", value: "98%" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const Home: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<Layout
|
||||||
|
header={
|
||||||
|
<NavBar back={null} style={{ background: "#fff" }}>
|
||||||
|
<span style={{ color: "#1677ff", fontWeight: 700 }}>存客宝</span>
|
||||||
|
</NavBar>
|
||||||
|
}
|
||||||
|
footer={<MeauMobile />}
|
||||||
|
>
|
||||||
|
<div className={style["home-page"]}>
|
||||||
|
{/* 统计卡片 */}
|
||||||
|
<div className={style["home-cards"]}>
|
||||||
|
<Card className={style["home-card"]}>
|
||||||
|
<div className={style["home-card-title"]}>设备数量</div>
|
||||||
|
<div className={style["home-card-value"]}>0</div>
|
||||||
|
</Card>
|
||||||
|
<Card className={style["home-card"]}>
|
||||||
|
<div className={style["home-card-title"]}>微信号数量</div>
|
||||||
|
<div className={style["home-card-value"]}>0</div>
|
||||||
|
</Card>
|
||||||
|
<Card className={style["home-card"]}>
|
||||||
|
<div className={style["home-card-title"]}>在线微信号</div>
|
||||||
|
<div className={style["home-card-value"]}>0</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 场景获客统计 */}
|
||||||
|
<Card className={style["home-section"]}>
|
||||||
|
<div className={style["home-section-title"]}>场景获客统计</div>
|
||||||
|
<div className={style["home-scene-stats"]}>
|
||||||
|
{sceneStats.map((item) => (
|
||||||
|
<div key={item.label} className={style["home-scene-item"]}>
|
||||||
|
<div className={style["home-scene-icon"]}>{item.icon}</div>
|
||||||
|
<div className={style["home-scene-value"]}>{item.value}</div>
|
||||||
|
<div className={style["home-scene-label"]}>{item.label}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* 今日数据 */}
|
||||||
|
<Card className={style["home-section"]}>
|
||||||
|
<div className={style["home-section-title"]}>今日数据</div>
|
||||||
|
<Grid columns={2} gap={12}>
|
||||||
|
{todayStats.map((item) => (
|
||||||
|
<Grid.Item key={item.label}>
|
||||||
|
<div className={style["home-today-item"]}>
|
||||||
|
<div className={style["home-today-value"]}>{item.value}</div>
|
||||||
|
<div className={style["home-today-label"]}>{item.label}</div>
|
||||||
|
</div>
|
||||||
|
</Grid.Item>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* 每日获客趋势(静态图表占位) */}
|
||||||
|
<Card className={style["home-section"]}>
|
||||||
|
<div className={style["home-section-title"]}>每日获客趋势</div>
|
||||||
|
<div className={style["home-chart"]}>
|
||||||
|
<svg width="100%" height="120">
|
||||||
|
<polyline
|
||||||
|
fill="none"
|
||||||
|
stroke="#1677ff"
|
||||||
|
strokeWidth="3"
|
||||||
|
points="10,100 40,80 70,60 100,50 130,40 160,60 190,80"
|
||||||
|
/>
|
||||||
|
{/* x轴文字 */}
|
||||||
|
{["周一", "周二", "周三", "周四", "周五", "周六", "周日"].map(
|
||||||
|
(d, i) => (
|
||||||
|
<text
|
||||||
|
key={d}
|
||||||
|
x={10 + i * 30}
|
||||||
|
y={115}
|
||||||
|
fontSize="12"
|
||||||
|
textAnchor="middle"
|
||||||
|
>
|
||||||
|
{d}
|
||||||
|
</text>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Home;
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import Home from "@/pages/home/index";
|
import Home from "@/pages/home/index";
|
||||||
|
import Login from "@/pages/login/login";
|
||||||
|
|
||||||
const homeRoutes = [
|
const homeRoutes = [
|
||||||
{
|
{
|
||||||
@@ -6,6 +7,11 @@ const homeRoutes = [
|
|||||||
element: <Home />,
|
element: <Home />,
|
||||||
auth: false, // 不需要权限
|
auth: false, // 不需要权限
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/login",
|
||||||
|
element: <Login />,
|
||||||
|
auth: false, // 不需要权限
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export default homeRoutes;
|
export default homeRoutes;
|
||||||
|
|||||||
5
package.json
Normal file
5
package.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"echarts": "^5.6.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
23
yarn.lock
Normal file
23
yarn.lock
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
echarts@^5.6.0:
|
||||||
|
version "5.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/echarts/-/echarts-5.6.0.tgz#2377874dca9fb50f104051c3553544752da3c9d6"
|
||||||
|
integrity sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==
|
||||||
|
dependencies:
|
||||||
|
tslib "2.3.0"
|
||||||
|
zrender "5.6.1"
|
||||||
|
|
||||||
|
tslib@2.3.0:
|
||||||
|
version "2.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
|
||||||
|
integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
|
||||||
|
|
||||||
|
zrender@5.6.1:
|
||||||
|
version "5.6.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/zrender/-/zrender-5.6.1.tgz#e08d57ecf4acac708c4fcb7481eb201df7f10a6b"
|
||||||
|
integrity sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==
|
||||||
|
dependencies:
|
||||||
|
tslib "2.3.0"
|
||||||
Reference in New Issue
Block a user