FEAT => 本次更新项目为:

This commit is contained in:
超级老白兔
2025-07-29 19:14:56 +08:00
parent dce6910371
commit c1cc74c2f3
3 changed files with 132 additions and 88 deletions

View File

@@ -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");
}
// 添加用户标签

View File

@@ -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 {
// 前端计算或模拟的数据

View File

@@ -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 />
&nbsp;
</Button>
</div>
)}