feat: 本次提交更新内容如下

流量池功能缺失
This commit is contained in:
笔记本里的永平
2025-07-24 20:57:18 +08:00
parent d46ff6e777
commit fd2cf3149e
4 changed files with 303 additions and 8 deletions

View File

@@ -1,5 +1,5 @@
import request from "@/api/request";
export function getTrafficPoolDetail(id: string): Promise<any> {
return request("/v1/traffic/pool/detail", { id }, "GET");
return request("/v1/workbench/detail", { id }, "GET");
}

View File

@@ -1,3 +1,300 @@
export default function TrafficPoolDetail() {
return <div>TrafficPoolDetail</div>;
}
import React, { useEffect, useState } from "react";
import { useParams, useNavigate } from "react-router-dom";
import Layout from "@/components/Layout/Layout";
import { getTrafficPoolDetail } from "./api";
import type { TrafficPoolUserDetail } from "./data";
import { Card, Button, Avatar, Tag, Spin } from "antd";
const tabList = [
{ key: "base", label: "基本信息" },
{ key: "journey", label: "用户旅程" },
{ key: "tags", label: "用户标签" },
];
const TrafficPoolDetail: React.FC = () => {
const { id } = useParams();
const navigate = useNavigate();
const [loading, setLoading] = useState(true);
const [user, setUser] = useState<TrafficPoolUserDetail | null>(null);
const [activeTab, setActiveTab] = useState<"base" | "journey" | "tags">(
"base"
);
useEffect(() => {
if (!id) return;
setLoading(true);
getTrafficPoolDetail(id as string)
.then((res) => setUser(res))
.finally(() => setLoading(false));
}, [id]);
if (loading) {
return (
<Layout>
<div style={{ textAlign: "center", padding: "64px 0" }}>
<Spin size="large" />
</div>
</Layout>
);
}
if (!user) {
return (
<Layout>
<div style={{ textAlign: "center", color: "#aaa", padding: "64px 0" }}>
</div>
</Layout>
);
}
return (
<Layout
header={
<div
style={{
display: "flex",
alignItems: "center",
height: 48,
borderBottom: "1px solid #eee",
background: "#fff",
}}
>
<Button
type="link"
onClick={() => navigate(-1)}
style={{ marginRight: 8 }}
>
&lt;
</Button>
<div style={{ fontWeight: 600, fontSize: 18 }}></div>
</div>
}
>
<div style={{ padding: 16 }}>
{/* 顶部信息 */}
<div
style={{
display: "flex",
alignItems: "center",
gap: 16,
marginBottom: 16,
}}
>
<Avatar src={user.avatar} size={64} />
<div>
<div style={{ fontSize: 20, fontWeight: 600 }}>{user.nickname}</div>
<div style={{ color: "#1677ff", fontSize: 14, margin: "4px 0" }}>
{user.wechatId}
</div>
{user.packages &&
user.packages.length > 0 &&
user.packages.map((pkg) => (
<Tag color="purple" key={pkg} style={{ marginRight: 4 }}>
{pkg}
</Tag>
))}
</div>
</div>
{/* Tab栏 */}
<div
style={{
display: "flex",
gap: 24,
borderBottom: "1px solid #eee",
marginBottom: 16,
}}
>
{tabList.map((tab) => (
<div
key={tab.key}
style={{
padding: "8px 0",
fontWeight: 500,
color: activeTab === tab.key ? "#1677ff" : "#888",
borderBottom:
activeTab === tab.key ? "2px solid #1677ff" : "none",
cursor: "pointer",
fontSize: 16,
}}
onClick={() => setActiveTab(tab.key as any)}
>
{tab.label}
</div>
))}
</div>
{/* Tab内容 */}
{activeTab === "base" && (
<>
<Card style={{ marginBottom: 16 }} title="关键信息">
<div style={{ display: "flex", flexWrap: "wrap", gap: 24 }}>
<div>{user.deviceName || "--"}</div>
<div>{user.wechatAccountName || "--"}</div>
<div>{user.customerServiceName || "--"}</div>
<div>{user.addTime || "--"}</div>
<div>{user.lastInteraction || "--"}</div>
</div>
</Card>
<Card style={{ marginBottom: 16 }} title="RFM评分">
<div style={{ display: "flex", gap: 32 }}>
<div>
<div
style={{ fontSize: 20, fontWeight: 600, color: "#1677ff" }}
>
{user.rfmScore?.recency ?? "-"}
</div>
<div style={{ fontSize: 12, color: "#888" }}>(R)</div>
</div>
<div>
<div
style={{ fontSize: 20, fontWeight: 600, color: "#52c41a" }}
>
{user.rfmScore?.frequency ?? "-"}
</div>
<div style={{ fontSize: 12, color: "#888" }}>(F)</div>
</div>
<div>
<div
style={{ fontSize: 20, fontWeight: 600, color: "#eb2f96" }}
>
{user.rfmScore?.monetary ?? "-"}
</div>
<div style={{ fontSize: 12, color: "#888" }}>(M)</div>
</div>
</div>
</Card>
<Card style={{ marginBottom: 16 }} title="统计数据">
<div style={{ display: "flex", gap: 32 }}>
<div>
<div
style={{ fontSize: 18, fontWeight: 600, color: "#52c41a" }}
>
¥{user.totalSpent ?? "-"}
</div>
<div style={{ fontSize: 12, color: "#888" }}></div>
</div>
<div>
<div
style={{ fontSize: 18, fontWeight: 600, color: "#1677ff" }}
>
{user.interactionCount ?? "-"}
</div>
<div style={{ fontSize: 12, color: "#888" }}></div>
</div>
<div>
<div
style={{ fontSize: 18, fontWeight: 600, color: "#faad14" }}
>
{user.conversionRate ?? "-"}
</div>
<div style={{ fontSize: 12, color: "#888" }}></div>
</div>
<div>
<div
style={{ fontSize: 18, fontWeight: 600, color: "#ff4d4f" }}
>
{user.status === "failed"
? "添加失败"
: user.status === "added"
? "添加成功"
: "未添加"}
</div>
<div style={{ fontSize: 12, color: "#888" }}></div>
</div>
</div>
</Card>
</>
)}
{activeTab === "journey" && (
<Card title="互动记录">
{user.interactions && user.interactions.length > 0 ? (
user.interactions.slice(0, 4).map((it) => (
<div
key={it.id}
style={{
display: "flex",
alignItems: "center",
gap: 12,
borderBottom: "1px solid #f0f0f0",
padding: "12px 0",
}}
>
<div style={{ fontSize: 22 }}>
{it.type === "click" && "📱"}
{it.type === "message" && "💬"}
{it.type === "purchase" && "💲"}
{it.type === "view" && "👁️"}
</div>
<div style={{ flex: 1 }}>
<div style={{ fontWeight: 500 }}>
{it.type === "click" && "点击行为"}
{it.type === "message" && "消息互动"}
{it.type === "purchase" && "购买行为"}
{it.type === "view" && "页面浏览"}
</div>
<div style={{ color: "#888", fontSize: 13 }}>
{it.content}
{it.type === "purchase" && it.value && (
<span
style={{
color: "#52c41a",
fontWeight: 600,
marginLeft: 4,
}}
>
¥{it.value}
</span>
)}
</div>
</div>
<div
style={{
fontSize: 12,
color: "#aaa",
whiteSpace: "nowrap",
}}
>
{it.timestamp}
</div>
</div>
))
) : (
<div
style={{
color: "#aaa",
textAlign: "center",
padding: "24px 0",
}}
>
</div>
)}
</Card>
)}
{activeTab === "tags" && (
<Card title="用户标签">
<div style={{ marginBottom: 12 }}>
{user.tags && user.tags.length > 0 ? (
user.tags.map((tag) => (
<Tag
key={tag}
color="blue"
style={{ marginRight: 8, marginBottom: 8 }}
>
{tag}
</Tag>
))
) : (
<span style={{ color: "#aaa" }}></span>
)}
</div>
<Button type="dashed" block>
</Button>
</Card>
)}
</div>
</Layout>
);
};
export default TrafficPoolDetail;

View File

@@ -191,9 +191,7 @@ const TrafficPoolList: React.FC = () => {
<div
className={styles.card}
style={{ cursor: "pointer" }}
onClick={() =>
navigate(`/mine/traffic-pool/detail/${item.id}`)
}
onClick={() => navigate(`/traffic-pool/detail/${item.id}`)}
>
<div className={styles.cardContent}>
<Checkbox

View File

@@ -29,7 +29,7 @@ const routes = [
auth: true,
},
{
path: "/traffic-pool/:id",
path: "/traffic-pool/detail/:id",
element: <TrafficPoolDetail />,
auth: true,
},