From 9facfe2ff8fe564643df5e6b33f6bc4c928239ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Mon, 28 Jul 2025 14:43:49 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90=E4=BA=A4?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B=20?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=AF=B9=E6=8E=A5=E6=88=90=E5=8A=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile/mine/traffic-pool/detail/api.ts | 4 +- .../traffic-pool/detail/index.module.scss | 309 +++++++++++ .../mobile/mine/traffic-pool/detail/index.tsx | 488 +++++++++--------- .../mobile/mine/traffic-pool/list/index.tsx | 4 +- .../src/pages/mobile/workspace/main/index.tsx | 15 +- 5 files changed, 553 insertions(+), 267 deletions(-) diff --git a/nkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts b/nkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts index 13e3d535..dc8b6e92 100644 --- a/nkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts +++ b/nkebao/src/pages/mobile/mine/traffic-pool/detail/api.ts @@ -1,5 +1,5 @@ import request from "@/api/request"; -export function getTrafficPoolDetail(id: string): Promise { - return request("/v1/workbench/detail", { id }, "GET"); +export function getTrafficPoolDetail(wechatId: string): Promise { + return request("/v1/wechats/getWechatInfo", { wechatId }, "GET"); } diff --git a/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.module.scss b/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.module.scss index e69de29b..aff534bb 100644 --- a/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.module.scss +++ b/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.module.scss @@ -0,0 +1,309 @@ +.container { + padding: 12px; + background: #f5f5f5; + min-height: 100vh; +} + +.userCard { + margin-bottom: 12px; + border-radius: 12px; + overflow: hidden; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); +} + +.userInfo { + display: flex; + align-items: center; + gap: 16px; + padding: 16px; +} + +.avatar { + width: 64px; + height: 64px; + border-radius: 50%; + flex-shrink: 0; +} + +.userDetails { + flex: 1; + min-width: 0; +} + +.nickname { + font-size: 18px; + font-weight: 600; + color: #333; + margin-bottom: 4px; + line-height: 1.2; +} + +.wechatId { + font-size: 14px; + color: #1677ff; + margin-bottom: 8px; + line-height: 1.2; +} + +.tags { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.packageTag { + font-size: 12px; + padding: 2px 8px; + border-radius: 12px; +} + +.tabs { + background: transparent; + + :global(.adm-tabs-header) { + background: white; + border-radius: 12px 12px 0 0; + margin-bottom: 0; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + } + + :global(.adm-tabs-tab) { + font-size: 14px; + font-weight: 500; + } + + :global(.adm-tabs-tab-active) { + color: #1677ff; + } + + :global(.adm-tabs-tab-line) { + background: #1677ff; + } + + :global(.adm-tabs-content) { + background: white; + border-radius: 0 0 12px 12px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + } +} + +.tabContent { + padding: 16px; +} + +.infoCard { + margin-bottom: 12px; + border-radius: 8px; + overflow: hidden; + + &:last-child { + margin-bottom: 0; + } + + :global(.adm-card-header) { + padding: 12px 16px; + border-bottom: 1px solid #f0f0f0; + font-size: 14px; + font-weight: 600; + color: #333; + } + + :global(.adm-card-body) { + padding: 0; + } +} + +.rfmGrid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 16px; + padding: 16px; +} + +.rfmItem { + text-align: center; +} + +.rfmValue { + font-size: 20px; + font-weight: 700; + line-height: 1.2; + margin-bottom: 4px; +} + +.rfmLabel { + font-size: 12px; + color: #666; + line-height: 1.2; +} + +.statsGrid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 16px; + padding: 16px; +} + +.statItem { + text-align: center; +} + +.statValue { + font-size: 18px; + font-weight: 700; + line-height: 1.2; + margin-bottom: 4px; +} + +.statLabel { + font-size: 12px; + color: #666; + line-height: 1.2; +} + +.interactionContent { + display: flex; + align-items: center; + gap: 8px; + font-size: 13px; + color: #666; + line-height: 1.4; +} + +.purchaseValue { + color: #22c55e; + font-weight: 600; +} + +.tagsContainer { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-bottom: 16px; + padding: 16px; +} + +.userTag { + font-size: 12px; + padding: 4px 12px; + border-radius: 16px; +} + +.addTagButton { + margin: 0 16px 16px; + height: 40px; + border-radius: 8px; + font-size: 14px; + + :global(.antd-mobile-icon) { + margin-right: 4px; + } +} + +.emptyState { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 48px 16px; + text-align: center; +} + +.emptyText { + color: #999; + font-size: 14px; + line-height: 1.4; +} + +// 响应式设计 +@media (max-width: 375px) { + .container { + padding: 8px; + } + + .userInfo { + padding: 12px; + gap: 12px; + } + + .avatar { + width: 56px; + height: 56px; + } + + .nickname { + font-size: 16px; + } + + .wechatId { + font-size: 13px; + } + + .tabContent { + padding: 12px; + } + + .rfmGrid { + gap: 12px; + padding: 12px; + } + + .rfmValue { + font-size: 18px; + } + + .statsGrid { + gap: 12px; + padding: 12px; + } + + .statValue { + font-size: 16px; + } + + .tagsContainer { + padding: 12px; + } + + .addTagButton { + margin: 0 12px 12px; + height: 36px; + font-size: 13px; + } +} + +// 暗色模式支持 +@media (prefers-color-scheme: dark) { + .container { + background: #1a1a1a; + } + + .userCard, + .tabs :global(.adm-tabs-header), + .tabs :global(.adm-tabs-content) { + background: #2a2a2a; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); + } + + .nickname { + color: #fff; + } + + .wechatId { + color: #4a9eff; + } + + .infoCard :global(.adm-card-header) { + color: #fff; + border-bottom-color: #3a3a3a; + } + + .rfmLabel, + .statLabel { + color: #999; + } + + .emptyText { + color: #666; + } +} diff --git a/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx b/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx index 6a3b983c..25bd986d 100644 --- a/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx +++ b/nkebao/src/pages/mobile/mine/traffic-pool/detail/index.tsx @@ -1,24 +1,24 @@ import React, { useEffect, useState } from "react"; import { useParams, useNavigate } from "react-router-dom"; +import { Card, Button, Avatar, Tag, Tabs, List, Badge } from "antd-mobile"; +import { + UserOutlined, + MessageOutlined, + ShoppingOutlined, + EyeOutlined, + PlusOutlined, +} from "@ant-design/icons"; import Layout from "@/components/Layout/Layout"; +import NavCommon from "@/components/NavCommon"; 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: "用户标签" }, -]; +import styles from "./index.module.scss"; const TrafficPoolDetail: React.FC = () => { const { id } = useParams(); const navigate = useNavigate(); const [loading, setLoading] = useState(true); const [user, setUser] = useState(null); - const [activeTab, setActiveTab] = useState<"base" | "journey" | "tags">( - "base" - ); useEffect(() => { if (!id) return; @@ -28,270 +28,256 @@ const TrafficPoolDetail: React.FC = () => { .finally(() => setLoading(false)); }, [id]); - if (loading) { - return ( - -
- -
-
- ); - } + const getInteractionIcon = (type: string) => { + switch (type) { + case "click": + return ; + case "message": + return ; + case "purchase": + return ; + case "view": + return ; + default: + return ; + } + }; + + const getInteractionTitle = (type: string) => { + switch (type) { + case "click": + return "点击行为"; + case "message": + return "消息互动"; + case "purchase": + return "购买行为"; + case "view": + return "页面浏览"; + default: + return "未知行为"; + } + }; + + const getStatusText = (status: string | number) => { + if (status === "failed" || status === 0) return "添加失败"; + if (status === "added" || status === 1) return "添加成功"; + return "未添加"; + }; + + const getStatusColor = (status: string | number) => { + if (status === "failed" || status === 0) return "danger"; + if (status === "added" || status === 1) return "success"; + return "default"; + }; + if (!user) { return ( - -
- 未找到该用户 + } loading={loading}> +
+
未找到该用户
); } return ( - - -
用户详情
-
- } - > -
- {/* 顶部信息 */} -
- -
-
{user.nickname}
-
- {user.wechatId} + } loading={loading}> +
+ {/* 用户基本信息 */} + +
+ } + /> +
+
{user.nickname}
+
{user.wechatId}
+
+ {user.packages?.map((pkg) => ( + + {pkg} + + ))} +
- {user.packages && - user.packages.length > 0 && - user.packages.map((pkg) => ( - - {pkg} - - ))}
-
- {/* Tab栏 */} -
- {tabList.map((tab) => ( -
setActiveTab(tab.key as any)} - > - {tab.label} -
- ))} -
+ + {/* Tab内容 */} - {activeTab === "base" && ( - <> - -
-
设备:{user.deviceName || "--"}
-
微信号:{user.wechatAccountName || "--"}
-
客服:{user.customerServiceName || "--"}
-
添加时间:{user.addTime || "--"}
-
最近互动:{user.lastInteraction || "--"}
-
-
- -
-
-
- {user.rfmScore?.recency ?? "-"} + + +
+ {/* 关键信息 */} + + + 设备 + + 微信号 + + + 客服 + + 添加时间 + + 最近互动 + + + + + {/* RFM评分 */} + +
+
+
+ {user.rfmScore?.recency ?? "-"} +
+
最近性(R)
-
最近性(R)
-
-
-
- {user.rfmScore?.frequency ?? "-"} +
+
+ {user.rfmScore?.frequency ?? "-"} +
+
频率(F)
-
频率(F)
-
-
-
- {user.rfmScore?.monetary ?? "-"} +
+
+ {user.rfmScore?.monetary ?? "-"} +
+
金额(M)
-
金额(M)
-
- - -
-
-
- ¥{user.totalSpent ?? "-"} + + + {/* 统计数据 */} + +
+
+
+ ¥{user.totalSpent ?? "-"} +
+
总消费
-
总消费
-
-
-
- {user.interactionCount ?? "-"} +
+
+ {user.interactionCount ?? "-"} +
+
互动次数
-
互动次数
-
-
-
- {user.conversionRate ?? "-"} +
+
+ {user.conversionRate ?? "-"} +
+
转化率
-
转化率
-
-
-
- {user.status === "failed" - ? "添加失败" - : user.status === "added" - ? "添加成功" - : "未添加"} +
+
+ {getStatusText(user.status)} +
+
添加状态
-
添加状态
-
- - - )} - {activeTab === "journey" && ( - - {user.interactions && user.interactions.length > 0 ? ( - user.interactions.slice(0, 4).map((it) => ( -
+
+ + + +
+ + {user.interactions && user.interactions.length > 0 ? ( + + {user.interactions.slice(0, 4).map((interaction) => ( + + {interaction.content} + {interaction.type === "purchase" && + interaction.value && ( + + ¥{interaction.value} + + )} +
+ } + extra={interaction.timestamp} + /> + ))} + + ) : ( +
+
暂无互动记录
+
+ )} +
+
+ + + +
+ +
+ {user.tags && user.tags.length > 0 ? ( + user.tags.map((tag) => ( + + {tag} + + )) + ) : ( +
暂无标签
+ )} +
+
- )) - ) : ( -
- 暂无互动记录 -
- )} - - )} - {activeTab === "tags" && ( - -
- {user.tags && user.tags.length > 0 ? ( - user.tags.map((tag) => ( - - {tag} - - )) - ) : ( - 暂无标签 - )} + + 添加新标签 + +
- -
- )} +
+
); diff --git a/nkebao/src/pages/mobile/mine/traffic-pool/list/index.tsx b/nkebao/src/pages/mobile/mine/traffic-pool/list/index.tsx index afa631e4..a0c96393 100644 --- a/nkebao/src/pages/mobile/mine/traffic-pool/list/index.tsx +++ b/nkebao/src/pages/mobile/mine/traffic-pool/list/index.tsx @@ -191,7 +191,9 @@ const TrafficPoolList: React.FC = () => {
navigate(`/traffic-pool/detail/${item.id}`)} + onClick={() => + navigate(`/traffic-pool/detail/${item.sourceId}`) + } >
{ path: "/workspace/traffic-distribution", bgColor: "#e6f7ff", }, - { - id: "ai-assistant", - name: "AI对话助手", - description: "智能回复,提高互动质量", - icon: ( - - ), - path: "/workspace/ai-assistant", - bgColor: "#e6f7ff", - isNew: true, - }, ]; // AI智能助手 @@ -176,7 +165,7 @@ const Workspace: React.FC = () => {
{/* AI智能助手 */} -
+ {/*

AI 智能助手

{aiFeatures.map((feature) => ( @@ -205,7 +194,7 @@ const Workspace: React.FC = () => { ))}
-
+
*/}
);