feat: 本次提交更新内容如下
存一波
This commit is contained in:
6402
nkebao/package-lock.json
generated
Normal file
6402
nkebao/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,7 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@ant-design/icons": "^5.6.1",
|
||||||
"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",
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import { NavBar } from "antd-mobile";
|
|||||||
import {
|
import {
|
||||||
AppOutline,
|
AppOutline,
|
||||||
UserOutline,
|
UserOutline,
|
||||||
LoopOutline,
|
|
||||||
TravelOutline,
|
|
||||||
ClockCircleOutline,
|
ClockCircleOutline,
|
||||||
|
SendOutline,
|
||||||
|
StarOutline,
|
||||||
} from "antd-mobile-icons";
|
} from "antd-mobile-icons";
|
||||||
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
||||||
import Layout from "@/components/Layout/Layout";
|
import Layout from "@/components/Layout/Layout";
|
||||||
@@ -38,19 +38,21 @@ const Home: React.FC = () => {
|
|||||||
{
|
{
|
||||||
label: "同步朋友圈",
|
label: "同步朋友圈",
|
||||||
value: res.momentsNum,
|
value: res.momentsNum,
|
||||||
icon: <LoopOutline style={{ fontSize: 16, color: "#ff6b35" }} />,
|
icon: (
|
||||||
|
<ClockCircleOutline style={{ fontSize: 16, color: "#ff6b35" }} />
|
||||||
|
),
|
||||||
color: "#ff6b35",
|
color: "#ff6b35",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "群发任务",
|
label: "群发任务",
|
||||||
value: res.groupPushNum,
|
value: res.groupPushNum,
|
||||||
icon: <TravelOutline style={{ fontSize: 16, color: "#ffd700" }} />,
|
icon: <SendOutline style={{ fontSize: 16, color: "#ffd700" }} />,
|
||||||
color: "#ffd700",
|
color: "#ffd700",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "获客转化率",
|
label: "获客转化率",
|
||||||
value: res.passRate,
|
value: res.passRate,
|
||||||
icon: <LoopOutline style={{ fontSize: 16, color: "#4caf50" }} />,
|
icon: <StarOutline style={{ fontSize: 16, color: "#4caf50" }} />,
|
||||||
color: "#4caf50",
|
color: "#4caf50",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,71 +1,150 @@
|
|||||||
.home-page {
|
.mine-page {
|
||||||
padding: 12px;
|
padding: 16px;
|
||||||
background: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-cards {
|
.user-card {
|
||||||
display: flex;
|
margin-bottom: 16px;
|
||||||
gap: 12px;
|
border-radius: 12px;
|
||||||
margin-bottom: 12px;
|
overflow: hidden;
|
||||||
> :global(.adm-card) {
|
|
||||||
flex: 1;
|
:global(.adm-card-body) {
|
||||||
|
padding: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-section {
|
.user-info {
|
||||||
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;
|
display: flex;
|
||||||
justify-content: space-between;
|
align-items: center;
|
||||||
margin: 0 8px 8px 8px;
|
gap: 16px;
|
||||||
}
|
}
|
||||||
.home-scene-item {
|
|
||||||
|
.user-avatar {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 50%;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 2px solid var(--primary-color);
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-details {
|
||||||
flex: 1;
|
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 {
|
.user-name {
|
||||||
text-align: center;
|
font-size: 18px;
|
||||||
padding: 8px 0;
|
font-weight: 600;
|
||||||
background: #f7f8fa;
|
color: #333;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-level {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--primary-color);
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-points {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-card {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
:global(.adm-card-body) {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.adm-list-item) {
|
||||||
|
padding: 16px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.adm-list-item-content) {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.adm-list-item-content-prefix) {
|
||||||
|
margin-right: 12px;
|
||||||
|
color: var(--primary-color);
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.adm-list-item-content-main) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.adm-list-item-title) {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.adm-list-item-description) {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.adm-list-item-content-arrow) {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-section {
|
||||||
|
padding: 0 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-btn {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
height: 48px;
|
||||||
.home-today-value {
|
font-size: 16px;
|
||||||
font-size: 18px;
|
font-weight: 500;
|
||||||
font-weight: bold;
|
|
||||||
color: #1677ff;
|
|
||||||
}
|
|
||||||
.home-today-label {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #888;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-chart {
|
// 响应式设计
|
||||||
margin-top: 8px;
|
@media (max-width: 375px) {
|
||||||
width: 100%;
|
.mine-page {
|
||||||
min-height: 100px;
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-avatar {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-name {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-card {
|
||||||
|
:global(.adm-list-item) {
|
||||||
|
padding: 12px;
|
||||||
|
|
||||||
|
:global(.adm-list-item-content-prefix) {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.adm-list-item-title) {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,132 +1,143 @@
|
|||||||
import React, { useState } from "react";
|
import React from "react";
|
||||||
import { Card, NavBar, TabBar, Grid } from "antd-mobile";
|
import { Card, NavBar, List, Button } from "antd-mobile";
|
||||||
import {
|
import {
|
||||||
AppOutline,
|
|
||||||
UserOutline,
|
UserOutline,
|
||||||
PieOutline, // 替换 BarChartOutline
|
AppOutline,
|
||||||
ShopbagOutline,
|
|
||||||
BellOutline,
|
BellOutline,
|
||||||
|
HeartOutline,
|
||||||
|
StarOutline,
|
||||||
|
MessageOutline,
|
||||||
|
SendOutline,
|
||||||
|
MailOutline,
|
||||||
} from "antd-mobile-icons";
|
} from "antd-mobile-icons";
|
||||||
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
||||||
import Layout from "@/components/Layout/Layout";
|
import Layout from "@/components/Layout/Layout";
|
||||||
import style from "./index.module.scss";
|
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 = [
|
const Mine: React.FC = () => {
|
||||||
{ label: "朋友圈同步", value: 12 },
|
const userInfo = {
|
||||||
{ label: "群发任务", value: 8 },
|
name: "张三",
|
||||||
{ label: "获客转化", value: "85%" },
|
avatar: "https://via.placeholder.com/60",
|
||||||
{ label: "系统活跃度", value: "98%" },
|
level: "VIP会员",
|
||||||
];
|
points: 1280,
|
||||||
|
};
|
||||||
|
|
||||||
|
const menuItems = [
|
||||||
|
{
|
||||||
|
icon: <UserOutline />,
|
||||||
|
title: "个人资料",
|
||||||
|
subtitle: "修改个人信息",
|
||||||
|
path: "/profile",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <AppOutline />,
|
||||||
|
title: "系统设置",
|
||||||
|
subtitle: "应用设置与偏好",
|
||||||
|
path: "/settings",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <BellOutline />,
|
||||||
|
title: "消息通知",
|
||||||
|
subtitle: "通知设置",
|
||||||
|
path: "/notifications",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <HeartOutline />,
|
||||||
|
title: "我的收藏",
|
||||||
|
subtitle: "收藏的内容",
|
||||||
|
path: "/favorites",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <StarOutline />,
|
||||||
|
title: "我的评价",
|
||||||
|
subtitle: "查看评价记录",
|
||||||
|
path: "/reviews",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <MessageOutline />,
|
||||||
|
title: "意见反馈",
|
||||||
|
subtitle: "问题反馈与建议",
|
||||||
|
path: "/feedback",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <SendOutline />,
|
||||||
|
title: "联系客服",
|
||||||
|
subtitle: "在线客服",
|
||||||
|
path: "/customer-service",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <MailOutline />,
|
||||||
|
title: "关于我们",
|
||||||
|
subtitle: "版本信息",
|
||||||
|
path: "/about",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const Home: React.FC = () => {
|
|
||||||
return (
|
return (
|
||||||
<Layout
|
<Layout
|
||||||
header={
|
header={
|
||||||
<NavBar back={null} style={{ background: "#fff" }}>
|
<NavBar back={null} style={{ background: "#fff" }}>
|
||||||
<span style={{ color: "#1677ff", fontWeight: 700 }}>存客宝</span>
|
<div style={{ color: "var(--primary-color)", fontWeight: 600 }}>
|
||||||
|
我的
|
||||||
|
</div>
|
||||||
</NavBar>
|
</NavBar>
|
||||||
}
|
}
|
||||||
footer={<MeauMobile />}
|
footer={<MeauMobile />}
|
||||||
>
|
>
|
||||||
<div className={style["home-page"]}>
|
<div className={style["mine-page"]}>
|
||||||
{/* 统计卡片 */}
|
{/* 用户信息卡片 */}
|
||||||
<div className={style["home-cards"]}>
|
<Card className={style["user-card"]}>
|
||||||
<Card className={style["home-card"]}>
|
<div className={style["user-info"]}>
|
||||||
<div className={style["home-card-title"]}>设备数量</div>
|
<div className={style["user-avatar"]}>
|
||||||
<div className={style["home-card-value"]}>0</div>
|
<img src={userInfo.avatar} alt="头像" />
|
||||||
</Card>
|
</div>
|
||||||
<Card className={style["home-card"]}>
|
<div className={style["user-details"]}>
|
||||||
<div className={style["home-card-title"]}>微信号数量</div>
|
<div className={style["user-name"]}>{userInfo.name}</div>
|
||||||
<div className={style["home-card-value"]}>0</div>
|
<div className={style["user-level"]}>{userInfo.level}</div>
|
||||||
</Card>
|
<div className={style["user-points"]}>
|
||||||
<Card className={style["home-card"]}>
|
积分: {userInfo.points}
|
||||||
<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>
|
||||||
))}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* 今日数据 */}
|
{/* 菜单列表 */}
|
||||||
<Card className={style["home-section"]}>
|
<Card className={style["menu-card"]}>
|
||||||
<div className={style["home-section-title"]}>今日数据</div>
|
<List>
|
||||||
<Grid columns={2} gap={12}>
|
{menuItems.map((item, index) => (
|
||||||
{todayStats.map((item) => (
|
<List.Item
|
||||||
<Grid.Item key={item.label}>
|
key={index}
|
||||||
<div className={style["home-today-item"]}>
|
prefix={item.icon}
|
||||||
<div className={style["home-today-value"]}>{item.value}</div>
|
title={item.title}
|
||||||
<div className={style["home-today-label"]}>{item.label}</div>
|
description={item.subtitle}
|
||||||
</div>
|
arrow
|
||||||
</Grid.Item>
|
onClick={() => {
|
||||||
))}
|
// 这里可以添加导航逻辑
|
||||||
</Grid>
|
console.log(`点击了: ${item.title}`);
|
||||||
</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(
|
</List>
|
||||||
(d, i) => (
|
|
||||||
<text
|
|
||||||
key={d}
|
|
||||||
x={10 + i * 30}
|
|
||||||
y={115}
|
|
||||||
fontSize="12"
|
|
||||||
textAnchor="middle"
|
|
||||||
>
|
|
||||||
{d}
|
|
||||||
</text>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
{/* 退出登录按钮 */}
|
||||||
|
<div className={style["logout-section"]}>
|
||||||
|
<Button
|
||||||
|
block
|
||||||
|
color="danger"
|
||||||
|
fill="outline"
|
||||||
|
className={style["logout-btn"]}
|
||||||
|
onClick={() => {
|
||||||
|
// 这里可以添加退出登录逻辑
|
||||||
|
console.log("退出登录");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
退出登录
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Home;
|
export default Mine;
|
||||||
|
|||||||
@@ -31,9 +31,9 @@
|
|||||||
|
|
||||||
// 页面容器
|
// 页面容器
|
||||||
.scene-page {
|
.scene-page {
|
||||||
padding: 12px;
|
background: #f5f6fa;
|
||||||
background: #f5f5f5;
|
min-height: 100vh;
|
||||||
min-height: calc(100vh - 100px);
|
padding: 0 0 60px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 错误提示
|
// 错误提示
|
||||||
@@ -142,109 +142,89 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.scenarios-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
.scenario-card {
|
.scenario-card {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 12px;
|
border-radius: 16px;
|
||||||
padding: 16px 20px;
|
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
transition: box-shadow 0.2s, transform 0.2s;
|
||||||
border: 1px solid #f0f0f0;
|
cursor: pointer;
|
||||||
transition: all 0.2s ease;
|
overflow: hidden;
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
min-height: 78px;
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
box-shadow: 0 4px 16px rgba(24, 142, 238, 0.12);
|
box-shadow: 0 6px 16px rgba(0,0,0,0.12);
|
||||||
border-color: var(--primary-color-light);
|
transform: translateY(-2px) scale(1.02);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.scenario-left {
|
.card-inner {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 16px;
|
padding: 18px 10px 14px 10px;
|
||||||
flex: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.scenario-icon {
|
.card-img-wrap {
|
||||||
width: 52px;
|
margin-bottom: 8px;
|
||||||
height: 52px;
|
}
|
||||||
background: linear-gradient(135deg, #f0f8ff 0%, #e6f7ff 100%);
|
.card-img-bg {
|
||||||
border-radius: 12px;
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
background: #f0f2f5;
|
||||||
|
border-radius: 50%;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border: 1px solid rgba(24, 142, 238, 0.1);
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
}
|
||||||
|
.card-img {
|
||||||
.scenario-image {
|
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
border-radius: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.scenario-info {
|
.card-title {
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 6px;
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scenario-name {
|
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #1a1a1a;
|
color: #1677ff;
|
||||||
line-height: 1.3;
|
text-align: center;
|
||||||
white-space: nowrap;
|
margin-bottom: 2px;
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.scenario-stats {
|
.card-desc {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #888;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
line-height: 1.4;
|
||||||
|
min-height: 32px;
|
||||||
|
max-height: 32px;
|
||||||
|
overflow: hidden;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-stats {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
|
.card-count {
|
||||||
.stat-text {
|
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: #666;
|
color: #666;
|
||||||
font-weight: 500;
|
|
||||||
}
|
}
|
||||||
|
.card-growth {
|
||||||
.scenario-right {
|
font-size: 12px;
|
||||||
display: flex;
|
color: #52c41a;
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-end;
|
|
||||||
gap: 8px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scenario-status {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-tag {
|
|
||||||
font-size: 11px;
|
|
||||||
padding: 3px 8px;
|
|
||||||
border-radius: 10px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scenario-growth {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 3px;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.growth-icon {
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 空状态
|
// 空状态
|
||||||
@@ -317,3 +297,35 @@
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
.scenarios-grid {
|
||||||
|
gap: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.card-inner {
|
||||||
|
padding: 12px 4px 10px 4px;
|
||||||
|
}
|
||||||
|
.card-img-bg {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
.card-img {
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
}
|
||||||
|
.card-title {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
.card-desc {
|
||||||
|
font-size: 11px;
|
||||||
|
min-height: 24px;
|
||||||
|
max-height: 24px;
|
||||||
|
}
|
||||||
|
.card-count {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.card-growth {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { NavBar, Button, SpinLoading, Toast, Tag } from "antd-mobile";
|
import { NavBar, Button, Toast, SpinLoading } from "antd-mobile";
|
||||||
import { AddOutline, UserAddOutline } from "antd-mobile-icons";
|
import { AddOutline } from "antd-mobile-icons";
|
||||||
|
import { RiseOutlined } from "@ant-design/icons";
|
||||||
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
||||||
import Layout from "@/components/Layout/Layout";
|
import Layout from "@/components/Layout/Layout";
|
||||||
import { getScenarios } from "./api";
|
import { getScenarios } from "./api";
|
||||||
@@ -11,11 +12,23 @@ interface Scenario {
|
|||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
image: string;
|
image: string;
|
||||||
|
description?: string;
|
||||||
count: number;
|
count: number;
|
||||||
growth: string;
|
growth: string;
|
||||||
status: number;
|
status: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const scenarioDescriptions: Record<string, string> = {
|
||||||
|
douyin: "通过抖音平台进行精准获客",
|
||||||
|
xiaohongshu: "利用小红书平台进行内容营销获客",
|
||||||
|
gongzhonghao: "通过微信公众号进行获客",
|
||||||
|
haibao: "通过海报分享进行获客",
|
||||||
|
phone: "通过电话营销进行获客",
|
||||||
|
weixinqun: "通过微信群进行获客",
|
||||||
|
payment: "通过付款码进行获客",
|
||||||
|
api: "通过API接口进行获客",
|
||||||
|
};
|
||||||
|
|
||||||
const Scene: React.FC = () => {
|
const Scene: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [scenarios, setScenarios] = useState<Scenario[]>([]);
|
const [scenarios, setScenarios] = useState<Scenario[]>([]);
|
||||||
@@ -26,25 +39,23 @@ const Scene: React.FC = () => {
|
|||||||
const fetchScenarios = async () => {
|
const fetchScenarios = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError("");
|
setError("");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await getScenarios({ page: 1, limit: 20 });
|
const response = await getScenarios({ page: 1, limit: 20 });
|
||||||
|
|
||||||
// 转换API数据为前端需要的格式
|
|
||||||
const transformedScenarios: Scenario[] = response.map((item: any) => ({
|
const transformedScenarios: Scenario[] = response.map((item: any) => ({
|
||||||
id: item.id.toString(),
|
id: item.id.toString(),
|
||||||
name: item.name,
|
name: item.name,
|
||||||
image:
|
image:
|
||||||
item.image ||
|
item.image ||
|
||||||
"https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-api.png",
|
"https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-api.png",
|
||||||
count: item.count, // 模拟今日数据
|
description:
|
||||||
growth: item.growth, // 模拟增长率
|
scenarioDescriptions[item.name?.toLowerCase()] ||
|
||||||
|
"通过该平台进行获客",
|
||||||
|
count: item.count,
|
||||||
|
growth: item.growth,
|
||||||
status: item.status,
|
status: item.status,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
setScenarios(transformedScenarios);
|
setScenarios(transformedScenarios);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("获取场景数据失败:", error);
|
|
||||||
setError("获取场景数据失败,请稍后重试");
|
setError("获取场景数据失败,请稍后重试");
|
||||||
Toast.show({
|
Toast.show({
|
||||||
content: "获取场景数据失败,请稍后重试",
|
content: "获取场景数据失败,请稍后重试",
|
||||||
@@ -54,7 +65,6 @@ const Scene: React.FC = () => {
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchScenarios();
|
fetchScenarios();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -68,12 +78,61 @@ const Scene: React.FC = () => {
|
|||||||
navigate("/scenarios/new");
|
navigate("/scenarios/new");
|
||||||
};
|
};
|
||||||
|
|
||||||
const getGrowthColor = (growth: string) => {
|
if (loading) {
|
||||||
const value = parseFloat(growth.replace(/[+%]/g, ""));
|
return (
|
||||||
if (value > 10) return "#52c41a";
|
<Layout
|
||||||
if (value > 5) return "#faad14";
|
header={
|
||||||
return "#ff4d4f";
|
<NavBar back={null} style={{ background: "#fff" }}>
|
||||||
};
|
<div className={style["nav-title"]}>场景获客</div>
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
color="primary"
|
||||||
|
onClick={handleNewPlan}
|
||||||
|
className={style["new-plan-btn"]}
|
||||||
|
style={{ marginLeft: "auto" }}
|
||||||
|
>
|
||||||
|
<AddOutline /> 新建计划
|
||||||
|
</Button>
|
||||||
|
</NavBar>
|
||||||
|
}
|
||||||
|
footer={<MeauMobile />}
|
||||||
|
>
|
||||||
|
<div className={style["loading"]}>
|
||||||
|
<SpinLoading color="primary" style={{ fontSize: 32 }} />
|
||||||
|
<div className={style["loading-text"]}>加载场景数据中...</div>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error && scenarios.length === 0) {
|
||||||
|
return (
|
||||||
|
<Layout
|
||||||
|
header={
|
||||||
|
<NavBar back={null} style={{ background: "#fff" }}>
|
||||||
|
<div className={style["nav-title"]}>场景获客</div>
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
color="primary"
|
||||||
|
onClick={handleNewPlan}
|
||||||
|
className={style["new-plan-btn"]}
|
||||||
|
style={{ marginLeft: "auto" }}
|
||||||
|
>
|
||||||
|
<AddOutline /> 新建计划
|
||||||
|
</Button>
|
||||||
|
</NavBar>
|
||||||
|
}
|
||||||
|
footer={<MeauMobile />}
|
||||||
|
>
|
||||||
|
<div className={style["error"]}>
|
||||||
|
<div className={style["error-text"]}>{error}</div>
|
||||||
|
<Button color="primary" onClick={() => window.location.reload()}>
|
||||||
|
重新加载
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout
|
<Layout
|
||||||
@@ -88,94 +147,58 @@ const Scene: React.FC = () => {
|
|||||||
color="primary"
|
color="primary"
|
||||||
onClick={handleNewPlan}
|
onClick={handleNewPlan}
|
||||||
className={style["new-plan-btn"]}
|
className={style["new-plan-btn"]}
|
||||||
|
style={{ marginLeft: "auto" }}
|
||||||
>
|
>
|
||||||
<AddOutline />
|
<AddOutline /> 新建计划
|
||||||
<span className={style["nav-right"]}>新建计划</span>
|
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
/>
|
></NavBar>
|
||||||
}
|
}
|
||||||
footer={<MeauMobile />}
|
footer={<MeauMobile />}
|
||||||
>
|
>
|
||||||
<div className={style["scene-page"]}>
|
<div className={style["scene-page"]}>
|
||||||
<div className={style["scene-header"]}>
|
<div className={style["scenarios-grid"]}>
|
||||||
<div className={style["header-title"]}>
|
|
||||||
<UserAddOutline className={style["header-icon"]} />
|
|
||||||
<span>热门获客场景</span>
|
|
||||||
</div>
|
|
||||||
<div className={style["header-subtitle"]}>
|
|
||||||
选择适合的获客场景,提升转化效果
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={style["scenarios-list"]}>
|
|
||||||
{scenarios.map((scenario) => (
|
{scenarios.map((scenario) => (
|
||||||
<div
|
<div
|
||||||
key={scenario.id}
|
key={scenario.id}
|
||||||
className={style["scenario-item"]}
|
className={style["scenario-card"]}
|
||||||
onClick={() => handleScenarioClick(scenario.id, scenario.name)}
|
onClick={() => handleScenarioClick(scenario.id, scenario.name)}
|
||||||
>
|
>
|
||||||
<div className={style["scenario-card"]}>
|
<div className={style["card-inner"]}>
|
||||||
<div className={style["scenario-left"]}>
|
<div className={style["card-img-wrap"]}>
|
||||||
<div className={style["scenario-icon"]}>
|
<div className={style["card-img-bg"]}>
|
||||||
<img
|
<img
|
||||||
src={scenario.image}
|
src={scenario.image}
|
||||||
alt={scenario.name}
|
alt={scenario.name}
|
||||||
className={style["scenario-image"]}
|
className={style["card-img"]}
|
||||||
onError={(e) => {
|
onError={(e) => {
|
||||||
e.currentTarget.src =
|
e.currentTarget.src =
|
||||||
"https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-api.png";
|
"https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-api.png";
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={style["scenario-info"]}>
|
|
||||||
<div className={style["scenario-name"]}>
|
|
||||||
{scenario.name}
|
|
||||||
</div>
|
|
||||||
<div className={style["scenario-stats"]}>
|
|
||||||
<span className={style["stat-text"]}>
|
|
||||||
今日获客: {scenario.count}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className={style["card-title"]}>{scenario.name}</div>
|
||||||
<div className={style["scenario-right"]}>
|
{scenario.description && (
|
||||||
<div className={style["scenario-status"]}>
|
<div className={style["card-desc"]}>
|
||||||
<Tag
|
{scenario.description}
|
||||||
color={scenario.status === 1 ? "success" : "default"}
|
|
||||||
fill="outline"
|
|
||||||
className={style["status-tag"]}
|
|
||||||
>
|
|
||||||
{scenario.status === 1 ? "活跃" : "暂停"}
|
|
||||||
</Tag>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
)}
|
||||||
className={style["scenario-growth"]}
|
<div className={style["card-stats"]}>
|
||||||
style={{ color: getGrowthColor(scenario.growth) }}
|
<span className={style["card-count"]}>
|
||||||
>
|
今日: {scenario.count}
|
||||||
|
</span>
|
||||||
|
<span className={style["card-growth"]}>
|
||||||
|
<RiseOutlined
|
||||||
|
style={{ fontSize: 14, color: "#52c41a", marginRight: 2 }}
|
||||||
|
/>
|
||||||
{scenario.growth}
|
{scenario.growth}
|
||||||
</div>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{scenarios.length === 0 && !loading && (
|
|
||||||
<div className={style["empty-state"]}>
|
|
||||||
<div className={style["empty-icon"]}>📊</div>
|
|
||||||
<div className={style["empty-text"]}>暂无场景数据</div>
|
|
||||||
<Button
|
|
||||||
color="primary"
|
|
||||||
size="small"
|
|
||||||
onClick={handleNewPlan}
|
|
||||||
className={style["empty-action"]}
|
|
||||||
>
|
|
||||||
创建第一个场景
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { Card, NavBar, Badge, Progress } from "antd-mobile";
|
import { Card, NavBar, Badge } from "antd-mobile";
|
||||||
import {
|
import {
|
||||||
ThumbsUpOutline,
|
LikeOutline,
|
||||||
MessageOutline,
|
MessageOutline,
|
||||||
SendOutline,
|
SendOutline,
|
||||||
TeamOutline,
|
TeamOutline,
|
||||||
ShareOutline,
|
LinkOutline,
|
||||||
AppOutline,
|
AppOutline,
|
||||||
BarChartOutline,
|
PieOutline,
|
||||||
LineChartOutline,
|
|
||||||
ClockCircleOutline,
|
ClockCircleOutline,
|
||||||
} from "antd-mobile-icons";
|
} from "antd-mobile-icons";
|
||||||
import Layout from "@/components/Layout/Layout";
|
import Layout from "@/components/Layout/Layout";
|
||||||
@@ -33,7 +32,7 @@ const Workspace: React.FC = () => {
|
|||||||
name: "自动点赞",
|
name: "自动点赞",
|
||||||
description: "智能自动点赞互动",
|
description: "智能自动点赞互动",
|
||||||
icon: (
|
icon: (
|
||||||
<ThumbsUpOutline className={styles.icon} style={{ color: "#ff4d4f" }} />
|
<LikeOutline className={styles.icon} style={{ color: "#ff4d4f" }} />
|
||||||
),
|
),
|
||||||
path: "/workspace/auto-like",
|
path: "/workspace/auto-like",
|
||||||
bgColor: "#fff2f0",
|
bgColor: "#fff2f0",
|
||||||
@@ -77,7 +76,7 @@ const Workspace: React.FC = () => {
|
|||||||
name: "流量分发",
|
name: "流量分发",
|
||||||
description: "管理流量分发和分配",
|
description: "管理流量分发和分配",
|
||||||
icon: (
|
icon: (
|
||||||
<ShareOutline className={styles.icon} style={{ color: "#1890ff" }} />
|
<LinkOutline className={styles.icon} style={{ color: "#1890ff" }} />
|
||||||
),
|
),
|
||||||
path: "/workspace/traffic-distribution",
|
path: "/workspace/traffic-distribution",
|
||||||
bgColor: "#e6f7ff",
|
bgColor: "#e6f7ff",
|
||||||
@@ -101,9 +100,7 @@ const Workspace: React.FC = () => {
|
|||||||
id: "ai-analyzer",
|
id: "ai-analyzer",
|
||||||
name: "AI数据分析",
|
name: "AI数据分析",
|
||||||
description: "智能分析客户行为特征",
|
description: "智能分析客户行为特征",
|
||||||
icon: (
|
icon: <PieOutline className={styles.icon} style={{ color: "#531dab" }} />,
|
||||||
<BarChartOutline className={styles.icon} style={{ color: "#531dab" }} />
|
|
||||||
),
|
|
||||||
path: "/workspace/ai-analyzer",
|
path: "/workspace/ai-analyzer",
|
||||||
bgColor: "#f0f0ff",
|
bgColor: "#f0f0ff",
|
||||||
isNew: true,
|
isNew: true,
|
||||||
@@ -121,17 +118,17 @@ const Workspace: React.FC = () => {
|
|||||||
id: "ai-forecast",
|
id: "ai-forecast",
|
||||||
name: "AI销售预测",
|
name: "AI销售预测",
|
||||||
description: "智能预测销售趋势",
|
description: "智能预测销售趋势",
|
||||||
icon: (
|
icon: <PieOutline className={styles.icon} style={{ color: "#d48806" }} />,
|
||||||
<LineChartOutline
|
|
||||||
className={styles.icon}
|
|
||||||
style={{ color: "#d48806" }}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
path: "/workspace/ai-forecast",
|
path: "/workspace/ai-forecast",
|
||||||
bgColor: "#fffbe6",
|
bgColor: "#fffbe6",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// 进度条宽度
|
||||||
|
const progressPercent = Math.round(
|
||||||
|
(taskStats.inProgress / taskStats.total) * 100
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout
|
<Layout
|
||||||
header={
|
header={
|
||||||
@@ -149,10 +146,26 @@ const Workspace: React.FC = () => {
|
|||||||
<Card className={styles.statsCard}>
|
<Card className={styles.statsCard}>
|
||||||
<div className={styles.statsTitle}>总任务数</div>
|
<div className={styles.statsTitle}>总任务数</div>
|
||||||
<div className={styles.statsValue}>{taskStats.total}</div>
|
<div className={styles.statsValue}>{taskStats.total}</div>
|
||||||
<Progress
|
<div className={styles.progress}>
|
||||||
percent={(taskStats.inProgress / taskStats.total) * 100}
|
<div
|
||||||
className={styles.progress}
|
style={{
|
||||||
/>
|
width: progressPercent + "%",
|
||||||
|
height: 6,
|
||||||
|
background: "var(--primary-color)",
|
||||||
|
borderRadius: 4,
|
||||||
|
transition: "width 0.3s",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 100 - progressPercent + "%",
|
||||||
|
height: 6,
|
||||||
|
background: "#f0f0f0",
|
||||||
|
borderRadius: 4,
|
||||||
|
display: "inline-block",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className={styles.statsSubtitle}>
|
<div className={styles.statsSubtitle}>
|
||||||
进行中: {taskStats.inProgress} / 已完成: {taskStats.completed}
|
进行中: {taskStats.inProgress} / 已完成: {taskStats.completed}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user