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