FEAT => 本次更新项目为:
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
import request from "@/api/request";
|
||||
import type { TrafficPoolUserDetail, UserJourneyResponse } from "./data";
|
||||
import type {
|
||||
TrafficPoolUserDetail,
|
||||
UserJourneyResponse,
|
||||
UserTagsResponse,
|
||||
} from "./data";
|
||||
|
||||
export function getTrafficPoolDetail(
|
||||
wechatId: string
|
||||
@@ -17,8 +21,8 @@ export function getUserJourney(params: {
|
||||
}
|
||||
|
||||
// 获取用户标签
|
||||
export function getUserTags(userId: string): Promise<any> {
|
||||
return request("/v1/user/tags", { userId }, "GET");
|
||||
export function getUserTags(userId: string): Promise<UserTagsResponse> {
|
||||
return request("/v1/traffic/pool/getUserTags", { userId }, "GET");
|
||||
}
|
||||
|
||||
// 添加用户标签
|
||||
|
||||
@@ -62,6 +62,20 @@ export interface UserJourneyResponse {
|
||||
total: number;
|
||||
}
|
||||
|
||||
// 用户标签类型
|
||||
export interface UserTagItem {
|
||||
id: string;
|
||||
name: string;
|
||||
color?: string;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
// 用户标签响应类型
|
||||
export interface UserTagsResponse {
|
||||
wechat: UserTagItem[];
|
||||
siteLabels: UserTagItem[];
|
||||
}
|
||||
|
||||
// 扩展的用户详情类型 - 用于前端展示
|
||||
export interface ExtendedUserDetail extends TrafficPoolUserDetail {
|
||||
// 前端计算或模拟的数据
|
||||
|
||||
@@ -24,12 +24,14 @@ import {
|
||||
} from "@ant-design/icons";
|
||||
import Layout from "@/components/Layout/Layout";
|
||||
import NavCommon from "@/components/NavCommon";
|
||||
import { getTrafficPoolDetail, getUserJourney } from "./api";
|
||||
import { getTrafficPoolDetail, getUserJourney, getUserTags } from "./api";
|
||||
import type {
|
||||
TrafficPoolUserDetail,
|
||||
ExtendedUserDetail,
|
||||
InteractionRecord,
|
||||
UserJourneyRecord,
|
||||
UserTagsResponse,
|
||||
UserTagItem,
|
||||
} from "./data";
|
||||
import styles from "./index.module.scss";
|
||||
|
||||
@@ -47,6 +49,10 @@ const TrafficPoolDetail: React.FC = () => {
|
||||
const [journeyTotal, setJourneyTotal] = useState(0);
|
||||
const pageSize = 10;
|
||||
|
||||
// 用户标签相关状态
|
||||
const [tagsLoading, setTagsLoading] = useState(false);
|
||||
const [userTagsList, setUserTagsList] = useState<UserTagItem[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!wxid) return;
|
||||
setLoading(true);
|
||||
@@ -117,12 +123,30 @@ const TrafficPoolDetail: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 获取用户标签数据
|
||||
const fetchUserTags = async () => {
|
||||
if (!userId) return;
|
||||
|
||||
setTagsLoading(true);
|
||||
try {
|
||||
const response: UserTagsResponse = await getUserTags(userId);
|
||||
setUserTagsList(response.siteLabels || []);
|
||||
} catch (error) {
|
||||
console.error("获取用户标签失败:", error);
|
||||
} finally {
|
||||
setTagsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 标签切换处理
|
||||
const handleTabChange = (tab: string) => {
|
||||
setActiveTab(tab);
|
||||
if (tab === "journey" && journeyList.length === 0) {
|
||||
fetchUserJourney(1);
|
||||
}
|
||||
if (tab === "tags" && userTagsList.length === 0) {
|
||||
fetchUserTags();
|
||||
}
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
@@ -245,6 +269,12 @@ const TrafficPoolDetail: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 获取标签颜色
|
||||
const getTagColor = (index: number): string => {
|
||||
const colors = ["primary", "success", "warning", "danger", "default"];
|
||||
return colors[index % colors.length];
|
||||
};
|
||||
|
||||
if (!user) {
|
||||
return (
|
||||
<Layout header={<NavCommon title="用户详情" />} loading={loading}>
|
||||
@@ -256,73 +286,69 @@ const TrafficPoolDetail: React.FC = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Layout loading={loading}>
|
||||
<div className={styles.container}>
|
||||
{/* 头部 */}
|
||||
<div className={styles.header}>
|
||||
<div className={styles.title}>用户详情</div>
|
||||
<Button
|
||||
className={styles.closeBtn}
|
||||
onClick={handleClose}
|
||||
fill="none"
|
||||
size="small"
|
||||
>
|
||||
<CloseOutlined />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 用户基本信息 */}
|
||||
<Card className={styles.userCard}>
|
||||
<div className={styles.userInfo}>
|
||||
<Avatar
|
||||
src={user.userInfo.avatar}
|
||||
className={styles.avatar}
|
||||
fallback={<UserOutlined />}
|
||||
/>
|
||||
<div className={styles.userDetails}>
|
||||
<div className={styles.nickname}>{user.userInfo.nickname}</div>
|
||||
<div className={styles.wechatId}>{user.userInfo.wechatId}</div>
|
||||
<div className={styles.tags}>
|
||||
<Tag color="warning" fill="outline" className={styles.userTag}>
|
||||
<CrownOutlined />
|
||||
重要价值客户
|
||||
</Tag>
|
||||
<Tag color="danger" fill="outline" className={styles.userTag}>
|
||||
优先添加
|
||||
</Tag>
|
||||
<Layout
|
||||
loading={loading}
|
||||
header={
|
||||
<>
|
||||
<NavCommon title="用户详情" />
|
||||
{/* 用户基本信息 */}
|
||||
<Card className={styles.userCard}>
|
||||
<div className={styles.userInfo}>
|
||||
<Avatar
|
||||
src={user.userInfo.avatar}
|
||||
className={styles.avatar}
|
||||
fallback={<UserOutlined />}
|
||||
/>
|
||||
<div className={styles.userDetails}>
|
||||
<div className={styles.nickname}>{user.userInfo.nickname}</div>
|
||||
<div className={styles.wechatId}>{user.userInfo.wechatId}</div>
|
||||
<div className={styles.tags}>
|
||||
<Tag
|
||||
color="warning"
|
||||
fill="outline"
|
||||
className={styles.userTag}
|
||||
>
|
||||
<CrownOutlined />
|
||||
重要价值客户
|
||||
</Tag>
|
||||
<Tag color="danger" fill="outline" className={styles.userTag}>
|
||||
优先添加
|
||||
</Tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
{/* 导航标签 */}
|
||||
<div className={styles.tabNav}>
|
||||
<div
|
||||
className={`${styles.tabItem} ${
|
||||
activeTab === "basic" ? styles.active : ""
|
||||
}`}
|
||||
onClick={() => handleTabChange("basic")}
|
||||
>
|
||||
基本信息
|
||||
</div>
|
||||
<div
|
||||
className={`${styles.tabItem} ${
|
||||
activeTab === "journey" ? styles.active : ""
|
||||
}`}
|
||||
onClick={() => handleTabChange("journey")}
|
||||
>
|
||||
用户旅程
|
||||
</div>
|
||||
<div
|
||||
className={`${styles.tabItem} ${
|
||||
activeTab === "tags" ? styles.active : ""
|
||||
}`}
|
||||
onClick={() => handleTabChange("tags")}
|
||||
>
|
||||
用户标签
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* 导航标签 */}
|
||||
<div className={styles.tabNav}>
|
||||
<div
|
||||
className={`${styles.tabItem} ${
|
||||
activeTab === "basic" ? styles.active : ""
|
||||
}`}
|
||||
onClick={() => handleTabChange("basic")}
|
||||
>
|
||||
基本信息
|
||||
</div>
|
||||
<div
|
||||
className={`${styles.tabItem} ${
|
||||
activeTab === "journey" ? styles.active : ""
|
||||
}`}
|
||||
onClick={() => handleTabChange("journey")}
|
||||
>
|
||||
用户旅程
|
||||
</div>
|
||||
<div
|
||||
className={`${styles.tabItem} ${
|
||||
activeTab === "tags" ? styles.active : ""
|
||||
}`}
|
||||
onClick={() => handleTabChange("tags")}
|
||||
>
|
||||
用户标签
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div className={styles.container}>
|
||||
{/* 内容区域 */}
|
||||
<div className={styles.content}>
|
||||
{activeTab === "basic" && (
|
||||
@@ -593,20 +619,12 @@ const TrafficPoolDetail: React.FC = () => {
|
||||
<div className={styles.tabContent}>
|
||||
{/* 用户标签 */}
|
||||
<Card title="用户标签" className={styles.infoCard}>
|
||||
{user.userTags && user.userTags.length > 0 ? (
|
||||
<div className={styles.tagsSection}>
|
||||
{user.userTags.map(tag => (
|
||||
<Tag
|
||||
key={tag.id}
|
||||
color={tag.color}
|
||||
fill="outline"
|
||||
className={styles.tagItem}
|
||||
>
|
||||
{tag.name}
|
||||
</Tag>
|
||||
))}
|
||||
{tagsLoading && userTagsList.length === 0 ? (
|
||||
<div className={styles.loadingContainer}>
|
||||
<SpinLoading color="primary" style={{ fontSize: 24 }} />
|
||||
<div className={styles.loadingText}>加载中...</div>
|
||||
</div>
|
||||
) : (
|
||||
) : userTagsList.length === 0 ? (
|
||||
<div className={styles.emptyState}>
|
||||
<div className={styles.emptyIcon}>
|
||||
<TagOutlined style={{ fontSize: 48, color: "#ccc" }} />
|
||||
@@ -614,6 +632,19 @@ const TrafficPoolDetail: React.FC = () => {
|
||||
<div className={styles.emptyText}>暂无用户标签</div>
|
||||
<div className={styles.emptyDesc}>该用户还没有任何标签</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.tagsSection}>
|
||||
{userTagsList.map((tag, index) => (
|
||||
<Tag
|
||||
key={tag.id}
|
||||
color={getTagColor(index)}
|
||||
fill="outline"
|
||||
className={styles.tagItem}
|
||||
>
|
||||
{tag.name}
|
||||
</Tag>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
@@ -661,14 +692,9 @@ const TrafficPoolDetail: React.FC = () => {
|
||||
</Card>
|
||||
|
||||
{/* 添加新标签按钮 */}
|
||||
<Button
|
||||
block
|
||||
color="primary"
|
||||
size="large"
|
||||
className={styles.addTagBtn}
|
||||
>
|
||||
<Button block color="primary" className={styles.addTagBtn}>
|
||||
<TagOutlined />
|
||||
添加新标签
|
||||
添加新标签
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user