diff --git a/nkebao/src/pages/devices/DeviceDetail.tsx b/nkebao/src/pages/mine/devices/DeviceDetail.tsx similarity index 100% rename from nkebao/src/pages/devices/DeviceDetail.tsx rename to nkebao/src/pages/mine/devices/DeviceDetail.tsx diff --git a/nkebao/src/pages/devices/Devices.tsx b/nkebao/src/pages/mine/devices/index.tsx similarity index 100% rename from nkebao/src/pages/devices/Devices.tsx rename to nkebao/src/pages/mine/devices/index.tsx diff --git a/nkebao/src/pages/mine/api.ts b/nkebao/src/pages/mine/main/api.ts similarity index 100% rename from nkebao/src/pages/mine/api.ts rename to nkebao/src/pages/mine/main/api.ts diff --git a/nkebao/src/pages/mine/index.module.scss b/nkebao/src/pages/mine/main/index.module.scss similarity index 100% rename from nkebao/src/pages/mine/index.module.scss rename to nkebao/src/pages/mine/main/index.module.scss diff --git a/nkebao/src/pages/mine/index.tsx b/nkebao/src/pages/mine/main/index.tsx similarity index 100% rename from nkebao/src/pages/mine/index.tsx rename to nkebao/src/pages/mine/main/index.tsx diff --git a/nkebao/src/pages/mine/wechat-accounts/detail/api.ts b/nkebao/src/pages/mine/wechat-accounts/detail/api.ts new file mode 100644 index 00000000..0f3d7f2c --- /dev/null +++ b/nkebao/src/pages/mine/wechat-accounts/detail/api.ts @@ -0,0 +1,29 @@ +import request from "@/api/request"; + +// 获取微信号详情 +export function getWechatAccountDetail(id: string) { + return request("/v1/wechats/getWechatInfo", { wechatId: id }, "GET"); +} + +// 获取微信号好友列表 +export function getWechatFriends(params: { + wechatAccount: string; + page: number; + limit: number; + keyword?: string; +}) { + return request( + `/v1/wechats/${params.wechatAccount}/friends`, + { + page: params.page, + limit: params.limit, + keyword: params.keyword, + }, + "GET" + ); +} + +// 获取微信好友详情 +export function getWechatFriendDetail(id: string) { + return request("/v1/WechatFriend/detail", { id }, "GET"); +} diff --git a/nkebao/src/pages/mine/wechat-accounts/detail/data.ts b/nkebao/src/pages/mine/wechat-accounts/detail/data.ts new file mode 100644 index 00000000..dbccfc5d --- /dev/null +++ b/nkebao/src/pages/mine/wechat-accounts/detail/data.ts @@ -0,0 +1,54 @@ +export interface WechatAccountSummary { + accountAge: string; + activityLevel: { + allTimes: number; + dayTimes: number; + }; + accountWeight: { + scope: number; + ageWeight: number; + activityWeigth: number; + restrictWeight: number; + realNameWeight: number; + }; + statistics: { + todayAdded: number; + addLimit: number; + }; + restrictions: { + id: number; + level: string; + reason: string; + date: string; + }[]; +} + +export interface Friend { + id: string; + avatar: string; + nickname: string; + wechatId: string; + remark: string; + addTime: string; + lastInteraction: string; + tags: Array<{ + id: string; + name: string; + color: string; + }>; + region: string; + source: string; + notes: string; +} + +export interface WechatFriendDetail { + id: number; + avatar: string; + nickname: string; + region: string; + wechatId: string; + addDate: string; + tags: string[]; + memo: string; + source: string; +} diff --git a/nkebao/src/pages/wechat-accounts/detail/detail.module.scss b/nkebao/src/pages/mine/wechat-accounts/detail/detail.module.scss similarity index 76% rename from nkebao/src/pages/wechat-accounts/detail/detail.module.scss rename to nkebao/src/pages/mine/wechat-accounts/detail/detail.module.scss index bd0ca84c..a63980a9 100644 --- a/nkebao/src/pages/wechat-accounts/detail/detail.module.scss +++ b/nkebao/src/pages/mine/wechat-accounts/detail/detail.module.scss @@ -1,7 +1,5 @@ .wechat-account-detail-page { padding: 16px; - background: linear-gradient(to bottom, #f0f8ff, #ffffff); - min-height: 100vh; .loading { display: flex; @@ -435,6 +433,20 @@ padding: 8px 12px; border-radius: 8px; } + + .refresh-btn { + padding: 8px 12px; + border-radius: 8px; + border: 1px solid #d9d9d9; + background: #fff; + color: #666; + transition: all 0.2s; + + &:hover { + background: #f5f5f5; + border-color: #bfbfbf; + } + } } .friends-list { @@ -463,80 +475,83 @@ border: 1px solid #e8e8e8; border-radius: 8px; margin-bottom: 8px; - cursor: pointer; - transition: all 0.2s; + } - &:hover { - background: #f5f5f5; - border-color: #d9d9d9; - } + .friend-item-static { + display: flex; + align-items: center; + padding: 12px; + background: #fff; + border: 1px solid #e8e8e8; + border-radius: 8px; + margin-bottom: 8px; + } - .friend-avatar { - width: 40px; - height: 40px; - border-radius: 50%; - margin-right: 12px; - } + .friend-avatar { + width: 40px; + height: 40px; + border-radius: 50%; + margin-right: 12px; + } - .friend-info { - flex: 1; - min-width: 0; + .friend-info { + flex: 1; + min-width: 0; - .friend-header { - display: flex; - align-items: center; - justify-content: space-between; - margin-bottom: 4px; + .friend-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 4px; - .friend-name { - font-size: 14px; - font-weight: 500; - color: #333; - max-width: 180px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - - .friend-remark { - color: #666; - margin-left: 4px; - } - } - - .friend-arrow { - font-size: 12px; - color: #ccc; - } - } - - .friend-wechat-id { - font-size: 12px; - color: #666; - margin-bottom: 4px; + .friend-name { + font-size: 14px; + font-weight: 500; + color: #333; + max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + + .friend-remark { + color: #666; + margin-left: 4px; + } } - .friend-tags { - display: flex; - flex-wrap: wrap; - gap: 4px; + .friend-arrow { + font-size: 12px; + color: #ccc; + } + } - .friend-tag { - font-size: 10px; - padding: 2px 6px; - border-radius: 6px; - } + .friend-wechat-id { + font-size: 12px; + color: #666; + margin-bottom: 4px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .friend-tags { + display: flex; + flex-wrap: wrap; + gap: 4px; + + .friend-tag { + font-size: 10px; + padding: 2px 6px; + border-radius: 6px; } } } + } - .loading-more { - display: flex; - justify-content: center; - padding: 16px 0; - } + .loading-more { + display: flex; + justify-content: center; + padding: 16px 0; } } } @@ -624,97 +639,102 @@ } } } - - .loading-detail { - display: flex; - justify-content: center; - align-items: center; - padding: 40px 0; - } - - .error-detail { - text-align: center; - color: #ff4d4f; - padding: 40px 0; - - p { - margin-bottom: 12px; - } - } - - .friend-detail-content { - .friend-detail-header { - display: flex; - align-items: center; - gap: 12px; - margin-bottom: 20px; - padding-bottom: 16px; - border-bottom: 1px solid #f0f0f0; - - .friend-detail-avatar { - width: 48px; - height: 48px; - border-radius: 50%; - } - - .friend-detail-info { - .friend-detail-name { - font-size: 16px; - font-weight: 600; - color: #333; - margin: 0 0 4px 0; - } - - .friend-detail-wechat-id { - font-size: 12px; - color: #666; - margin: 0; - } - } - } - - .friend-detail-items { - .detail-item { - display: flex; - justify-content: space-between; - align-items: flex-start; - padding: 12px 0; - border-bottom: 1px solid #f0f0f0; - - &:last-child { - border-bottom: none; - } - - .detail-label { - font-size: 14px; - color: #666; - flex-shrink: 0; - width: 80px; - } - - .detail-value { - font-size: 14px; - color: #333; - text-align: right; - flex: 1; - margin-left: 16px; - } - - .detail-tags { - display: flex; - flex-wrap: wrap; - gap: 4px; - justify-content: flex-end; - flex: 1; - margin-left: 16px; - - .detail-tag { - font-size: 10px; - padding: 2px 6px; - border-radius: 6px; - } - } - } - } - } +} + +.pagination-wrapper { + display: flex; + justify-content: center; + align-items: center; + padding: 20px 0; + margin-top: 16px; + border-top: 1px solid #f0f0f0; +} + +.summary-grid { + display: flex; + gap: 16px; + margin-bottom: 12px; +} +.summary-item { + flex: 1; + background: #fafbfc; + border-radius: 10px; + padding: 16px 0 8px 0; + text-align: center; + box-shadow: 0 1px 2px rgba(0,0,0,0.03); +} +.summary-value { + font-size: 24px; + font-weight: 600; + color: #222; + margin-bottom: 2px; +} +.summary-value-green { + font-size: 24px; + font-weight: 600; + color: #27ae60; + margin-bottom: 2px; +} +.summary-value-blue { + font-size: 24px; + font-weight: 600; + color: #3498db; + margin-bottom: 2px; +} +.summary-label { + font-size: 13px; + color: #888; +} +.summary-progress-row { + display: flex; + align-items: center; + font-size: 14px; + color: #666; + margin-bottom: 4px; + gap: 8px; +} +.summary-progress-text { + font-weight: 500; + color: #222; +} +.summary-progress-bar { + width: 100%; + margin-bottom: 16px; +} +.progress-bg { + width: 100%; + height: 8px; + background: #f0f0f0; + border-radius: 6px; + overflow: hidden; +} +.progress-fill { + height: 8px; + background: #3498db; + border-radius: 6px 0 0 6px; + transition: width 0.3s; +} +.device-card { + background: #fafbfc; + border-radius: 10px; + padding: 16px; + margin-top: 12px; + box-shadow: 0 1px 2px rgba(0,0,0,0.03); +} +.device-title { + font-size: 15px; + font-weight: 600; + margin-bottom: 8px; + color: #222; +} +.device-row { + display: flex; + align-items: center; + font-size: 14px; + margin-bottom: 4px; +} +.device-label { + color: #888; + min-width: 70px; + margin-right: 8px; } diff --git a/nkebao/src/pages/mine/wechat-accounts/detail/index.tsx b/nkebao/src/pages/mine/wechat-accounts/detail/index.tsx new file mode 100644 index 00000000..ad0f1686 --- /dev/null +++ b/nkebao/src/pages/mine/wechat-accounts/detail/index.tsx @@ -0,0 +1,557 @@ +import React, { useState, useEffect, useRef, useCallback } from "react"; +import { useParams, useNavigate } from "react-router-dom"; +import { + NavBar, + Card, + Tabs, + Button, + SpinLoading, + Popup, + Toast, + Avatar, + Tag, +} from "antd-mobile"; +import { Input, Pagination } from "antd"; +import NavCommon from "@/components/NavCommon"; +import { + SearchOutlined, + ReloadOutlined, + UserOutlined, + ClockCircleOutlined, + MessageOutlined, + StarOutlined, + ExclamationCircleOutlined, +} from "@ant-design/icons"; +import Layout from "@/components/Layout/Layout"; +import style from "./detail.module.scss"; +import { getWechatAccountDetail, getWechatFriends } from "./api"; + +import { WechatAccountSummary, Friend } from "./data"; + +const WechatAccountDetail: React.FC = () => { + const { id } = useParams<{ id: string }>(); + const navigate = useNavigate(); + + const [accountSummary, setAccountSummary] = + useState(null); + const [accountInfo, setAccountInfo] = useState(null); + const [showRestrictions, setShowRestrictions] = useState(false); + const [showTransferConfirm, setShowTransferConfirm] = useState(false); + const [searchQuery, setSearchQuery] = useState(""); + const [activeTab, setActiveTab] = useState("overview"); + const [isLoading, setIsLoading] = useState(false); + const [loadingInfo, setLoadingInfo] = useState(true); + + // 好友列表相关状态 + const [friends, setFriends] = useState([]); + const [friendsPage, setFriendsPage] = useState(1); + const [friendsTotal, setFriendsTotal] = useState(0); + const [isFetchingFriends, setIsFetchingFriends] = useState(false); + const [hasFriendLoadError, setHasFriendLoadError] = useState(false); + const [isFriendsEmpty, setIsFriendsEmpty] = useState(false); + + // 获取基础信息 + const fetchAccountInfo = useCallback(async () => { + if (!id) return; + setLoadingInfo(true); + try { + const response = await getWechatAccountDetail(id); + if (response && response.userInfo) { + setAccountInfo(response.userInfo); + // 构造 summary 数据结构 + setAccountSummary({ + accountAge: response.accountAge, + activityLevel: response.activityLevel, + accountWeight: response.accountWeight, + statistics: response.statistics, + restrictions: response.restrictions || [], + }); + } else { + Toast.show({ + content: "获取账号信息失败", + position: "top", + }); + } + } catch (e) { + Toast.show({ content: "获取账号信息失败", position: "top" }); + } finally { + setLoadingInfo(false); + } + }, [id]); + + // 获取好友列表 - 封装为独立函数 + const fetchFriendsList = useCallback( + async (page: number = 1, keyword: string = "") => { + if (!id) return; + + setIsFetchingFriends(true); + setHasFriendLoadError(false); + + try { + const response = await getWechatFriends({ + wechatAccount: id, + page: page, + limit: 20, + keyword: keyword, + }); + + const newFriends = response.list.map((friend: any) => ({ + id: friend.id.toString(), + avatar: friend.avatar || "/placeholder.svg", + nickname: friend.nickname || "未知用户", + wechatId: friend.wechatId || "", + remark: friend.memo || "", + addTime: friend.createTime || new Date().toISOString().split("T")[0], + lastInteraction: + friend.lastInteraction || new Date().toISOString().split("T")[0], + tags: friend.tags + ? friend.tags.map((tag: string, index: number) => ({ + id: `tag-${index}`, + name: tag, + color: getRandomTagColor(), + })) + : [], + region: friend.region || "未知", + source: friend.source || "未知", + notes: friend.notes || "", + })); + + setFriends(newFriends); + setFriendsTotal(response.total); + setFriendsPage(page); + setIsFriendsEmpty(newFriends.length === 0); + } catch (error) { + console.error("获取好友列表失败:", error); + setHasFriendLoadError(true); + setFriends([]); + setIsFriendsEmpty(true); + Toast.show({ + content: "获取好友列表失败,请检查网络连接", + position: "top", + }); + } finally { + setIsFetchingFriends(false); + } + }, + [id] + ); + + // 搜索好友 + const handleSearch = useCallback(() => { + setFriendsPage(1); + fetchFriendsList(1, searchQuery); + }, [searchQuery, fetchFriendsList]); + + // 刷新好友列表 + const handleRefreshFriends = useCallback(() => { + fetchFriendsList(friendsPage, searchQuery); + }, [friendsPage, searchQuery, fetchFriendsList]); + + // 分页切换 + const handlePageChange = useCallback( + (page: number) => { + setFriendsPage(page); + fetchFriendsList(page, searchQuery); + }, + [searchQuery, fetchFriendsList] + ); + + // 初始化数据 + useEffect(() => { + if (id) { + fetchAccountInfo(); + } + }, [id, fetchAccountInfo]); + + // 监听标签切换 - 只在切换到好友列表时请求一次 + useEffect(() => { + if (activeTab === "friends" && id) { + setIsFriendsEmpty(false); + setHasFriendLoadError(false); + fetchFriendsList(1, searchQuery); + } + }, [activeTab, id, fetchFriendsList, searchQuery]); + + // 工具函数 + const getRandomTagColor = (): string => { + const colors = [ + "bg-blue-100 text-blue-800", + "bg-green-100 text-green-800", + "bg-red-100 text-red-800", + "bg-pink-100 text-pink-800", + "bg-emerald-100 text-emerald-800", + "bg-amber-100 text-amber-800", + ]; + return colors[Math.floor(Math.random() * colors.length)]; + }; + + const calculateAccountAge = (registerTime: string) => { + const registerDate = new Date(registerTime); + const now = new Date(); + const diffTime = Math.abs(now.getTime() - registerDate.getTime()); + const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); + const years = Math.floor(diffDays / 365); + const months = Math.floor((diffDays % 365) / 30); + return { years, months }; + }; + + const formatAccountAge = (age: { years: number; months: number }) => { + if (age.years > 0) { + return `${age.years}年${age.months}个月`; + } + return `${age.months}个月`; + }; + + const getWeightColor = (weight: number) => { + if (weight >= 80) return "text-green-600"; + if (weight >= 60) return "text-yellow-600"; + return "text-red-600"; + }; + + const getWeightDescription = (weight: number) => { + if (weight >= 80) return "账号质量优秀,可以正常使用"; + if (weight >= 60) return "账号质量良好,需要注意使用频率"; + return "账号质量较差,建议谨慎使用"; + }; + + const handleTransferFriends = () => { + setShowTransferConfirm(true); + }; + + const confirmTransferFriends = () => { + Toast.show({ + content: "好友转移计划已创建,请在场景获客中查看详情", + position: "top", + }); + setShowTransferConfirm(false); + navigate("/scenarios"); + }; + + const getRestrictionLevelColor = (level: string) => { + switch (level) { + case "high": + return "text-red-600"; + case "medium": + return "text-yellow-600"; + default: + return "text-gray-600"; + } + }; + + const formatDateTime = (dateString: string) => { + const date = new Date(dateString); + return date + .toLocaleString("zh-CN", { + year: "numeric", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + hour12: false, + }) + .replace(/\//g, "-"); + }; + + const handleTabChange = (value: string) => { + setActiveTab(value); + }; + + return ( + } loading={loadingInfo}> +
+ {/* 账号基本信息卡片 */} + +
+
+ +
+
+
+

+ {accountInfo?.nickname || "未知昵称"} +

+
+

+ 微信号:{accountInfo?.wechatId || "未知"} +

+
+ +
+
+
+
+ + {/* 标签页 */} + + + +
+
+
+
+ {accountInfo?.friendShip?.totalFriend ?? "-"} +
+
好友数量
+
+
+
+ +{accountSummary?.statistics.todayAdded ?? "-"} +
+
今日新增
+
+
+
+ 今日可添加: + + {accountSummary?.statistics.todayAdded ?? 0}/ + {accountSummary?.statistics.addLimit ?? 0} + +
+
+
+
+
+
+
+
+
+ {accountInfo?.friendShip?.groupNumber ?? "-"} +
+
群聊数量
+
+
+
+ {accountInfo?.activity?.yesterdayMsgCount ?? "-"} +
+
今日消息
+
+
+
+
设备信息
+
+ 设备名称: + {accountInfo?.deviceName ?? "-"} +
+
+ 系统类型: + {accountInfo?.deviceType ?? "-"} +
+
+ 系统版本: + {accountInfo?.deviceVersion ?? "-"} +
+
+
+ + + 0 ? ` (${friendsTotal.toLocaleString()})` : ""}`} + key="friends" + > +
+ {/* 搜索栏 */} +
+
+ setSearchQuery(e.target.value)} + prefix={} + allowClear + size="large" + onPressEnter={handleSearch} + /> +
+ +
+ + {/* 好友列表 */} +
+ {isFetchingFriends && friends.length === 0 ? ( +
+ +
+ ) : isFriendsEmpty ? ( +
暂无好友数据
+ ) : hasFriendLoadError ? ( +
+

加载失败,请重试

+ +
+ ) : ( + <> + {friends.map((friend) => ( +
+ +
+
+
+ {friend.nickname} + {friend.remark && ( + + ({friend.remark}) + + )} +
+
+
+ {friend.wechatId} +
+
+ {friend.tags?.map((tag, index) => ( + + {typeof tag === "string" ? tag : tag.name} + + ))} +
+
+
+ ))} + + )} +
+ + {/* 分页组件 */} + {friendsTotal > 20 && + !isFriendsEmpty && + !hasFriendLoadError && ( +
+ +
+ )} +
+
+ + +
+ + {/* 限制记录详情弹窗 */} + setShowRestrictions(false)} + bodyStyle={{ borderRadius: "16px 16px 0 0" }} + > +
+
+

限制记录详情

+ +
+

每次限制恢复时间为24小时

+ {accountSummary && accountSummary.restrictions && ( +
+ {accountSummary.restrictions.map((restriction) => ( +
+
+
+ {restriction.reason} +
+
+ {formatDateTime(restriction.date)} +
+
+ + {restriction.level === "high" + ? "高风险" + : restriction.level === "medium" + ? "中风险" + : "低风险"} + +
+ ))} +
+ )} +
+
+ + {/* 好友转移确认弹窗 */} + setShowTransferConfirm(false)} + bodyStyle={{ borderRadius: "16px 16px 0 0" }} + > +
+
+

确认好友转移

+
+

+ 确定要将该微信号的好友转移到其他账号吗?此操作将创建一个好友转移计划。 +

+
+ + +
+
+
+ + {/* 好友详情弹窗 */} + {/* Removed */} + + ); +}; + +export default WechatAccountDetail; diff --git a/nkebao/src/pages/wechat-accounts/list/api.ts b/nkebao/src/pages/mine/wechat-accounts/list/api.ts similarity index 100% rename from nkebao/src/pages/wechat-accounts/list/api.ts rename to nkebao/src/pages/mine/wechat-accounts/list/api.ts diff --git a/nkebao/src/pages/wechat-accounts/list/index.module.scss b/nkebao/src/pages/mine/wechat-accounts/list/index.module.scss similarity index 100% rename from nkebao/src/pages/wechat-accounts/list/index.module.scss rename to nkebao/src/pages/mine/wechat-accounts/list/index.module.scss diff --git a/nkebao/src/pages/wechat-accounts/list/index.tsx b/nkebao/src/pages/mine/wechat-accounts/list/index.tsx similarity index 77% rename from nkebao/src/pages/wechat-accounts/list/index.tsx rename to nkebao/src/pages/mine/wechat-accounts/list/index.tsx index e1a306a6..7b097b9d 100644 --- a/nkebao/src/pages/wechat-accounts/list/index.tsx +++ b/nkebao/src/pages/mine/wechat-accounts/list/index.tsx @@ -1,30 +1,18 @@ import React, { useState, useEffect } from "react"; -import { - NavBar, - List, - Card, - Button, - SpinLoading, - Popup, - Toast, -} from "antd-mobile"; +import { Button, SpinLoading, Toast } from "antd-mobile"; import { Pagination, Input, Tooltip } from "antd"; -import { - ArrowLeftOutlined, - SearchOutlined, - ReloadOutlined, -} from "@ant-design/icons"; +import { SearchOutlined, ReloadOutlined } from "@ant-design/icons"; import { useNavigate } from "react-router-dom"; import Layout from "@/components/Layout/Layout"; import style from "./index.module.scss"; import { getWechatAccounts } from "./api"; +import NavCommon from "@/components/NavCommon"; interface WechatAccount { id: number; nickname: string; avatar: string; wechatId: string; - wechatAccount: string; deviceId: number; times: number; // 今日可添加 addedCount: number; // 今日新增 @@ -44,10 +32,6 @@ const WechatAccounts: React.FC = () => { const [totalAccounts, setTotalAccounts] = useState(0); const [isLoading, setIsLoading] = useState(true); const [isRefreshing, setIsRefreshing] = useState(false); - const [popupVisible, setPopupVisible] = useState(false); - const [selectedAccount, setSelectedAccount] = useState( - null - ); const fetchAccounts = async (page = 1, keyword = "") => { setIsLoading(true); @@ -91,8 +75,7 @@ const WechatAccounts: React.FC = () => { }; const handleAccountClick = (account: WechatAccount) => { - setSelectedAccount(account); - setPopupVisible(true); + navigate(`/wechat-accounts/detail/${account.wechatId}`); }; const handleTransferFriends = (account: WechatAccount) => { @@ -104,20 +87,7 @@ const WechatAccounts: React.FC = () => { - - navigate(-1)} - /> -
- } - > - 微信号管理 - +
{
- 微信号:{account.wechatAccount} + 微信号:{account.wechatId}
@@ -259,48 +229,6 @@ const WechatAccounts: React.FC = () => { /> )} - setPopupVisible(false)} - bodyStyle={{ borderRadius: "16px 16px 0 0" }} - > - {selectedAccount && ( -
-
- avatar -
- {selectedAccount.nickname} -
-
- 微信号:{selectedAccount.wechatAccount} -
-
-
- -
- -
- )} -
); diff --git a/nkebao/src/pages/wechat-accounts/detail/api.ts b/nkebao/src/pages/wechat-accounts/detail/api.ts deleted file mode 100644 index 40757d27..00000000 --- a/nkebao/src/pages/wechat-accounts/detail/api.ts +++ /dev/null @@ -1,26 +0,0 @@ -import request from "@/api/request"; - -// 获取微信号详情 -export function getWechatAccountDetail(id: string) { - return request("/WechatAccount/detail", { id }, "GET"); -} - -// 获取微信号summary -export function getWechatAccountSummary(id: string) { - return request(`/v1/wechats/${id}/summary`, {}, "GET"); -} - -// 获取微信号好友列表 -export function getWechatFriends(params: { - wechatAccountKeyword: string; - pageIndex: number; - pageSize: number; - friendKeyword?: string; -}) { - return request("/WechatFriend/friendlistData", params, "POST"); -} - -// 获取微信好友详情 -export function getWechatFriendDetail(id: string) { - return request("/v1/WechatFriend/detail", { id }, "GET"); -} diff --git a/nkebao/src/pages/wechat-accounts/detail/index.tsx b/nkebao/src/pages/wechat-accounts/detail/index.tsx deleted file mode 100644 index fe8dd32e..00000000 --- a/nkebao/src/pages/wechat-accounts/detail/index.tsx +++ /dev/null @@ -1,940 +0,0 @@ -import React, { useState, useEffect, useRef, useCallback } from "react"; -import { useParams, useNavigate } from "react-router-dom"; -import { - NavBar, - Card, - Tabs, - Button, - SpinLoading, - Popup, - Toast, - Input, - Avatar, - Tag, -} from "antd-mobile"; -import NavCommon from "@/components/NavCommon"; -import { - SearchOutlined, - ReloadOutlined, - UserOutlined, - ClockCircleOutlined, - MessageOutlined, - StarOutlined, - ExclamationCircleOutlined, - RightOutlined, -} from "@ant-design/icons"; -import Layout from "@/components/Layout/Layout"; -import style from "./detail.module.scss"; -import { - getWechatAccountDetail, - getWechatAccountSummary, - getWechatFriends, - getWechatFriendDetail, -} from "./api"; - -interface WechatAccountSummary { - accountAge: string; - activityLevel: { - allTimes: number; - dayTimes: number; - }; - accountWeight: { - scope: number; - ageWeight: number; - activityWeigth: number; - restrictWeight: number; - realNameWeight: number; - }; - statistics: { - todayAdded: number; - addLimit: number; - }; - restrictions: { - id: number; - level: string; - reason: string; - date: string; - }[]; -} - -interface Friend { - id: string; - avatar: string; - nickname: string; - wechatId: string; - remark: string; - addTime: string; - lastInteraction: string; - tags: Array<{ - id: string; - name: string; - color: string; - }>; - region: string; - source: string; - notes: string; -} - -interface WechatFriendDetail { - id: number; - avatar: string; - nickname: string; - region: string; - wechatId: string; - addDate: string; - tags: string[]; - memo: string; - source: string; -} - -const WechatAccountDetail: React.FC = () => { - const { id } = useParams<{ id: string }>(); - const navigate = useNavigate(); - - const [accountSummary, setAccountSummary] = - useState(null); - const [accountInfo, setAccountInfo] = useState(null); - const [showRestrictions, setShowRestrictions] = useState(false); - const [showTransferConfirm, setShowTransferConfirm] = useState(false); - const [showFriendDetail, setShowFriendDetail] = useState(false); - const [selectedFriend, setSelectedFriend] = useState(null); - const [friendDetail, setFriendDetail] = useState( - null - ); - const [isLoadingFriendDetail, setIsLoadingFriendDetail] = useState(false); - const [friendDetailError, setFriendDetailError] = useState( - null - ); - const [searchQuery, setSearchQuery] = useState(""); - const [activeTab, setActiveTab] = useState("overview"); - const [isLoading, setIsLoading] = useState(false); - const [loadingInfo, setLoadingInfo] = useState(true); - const [loadingSummary, setLoadingSummary] = useState(true); - - // 好友列表相关状态 - const [friends, setFriends] = useState([]); - const [friendsPage, setFriendsPage] = useState(1); - const [friendsTotal, setFriendsTotal] = useState(0); - const [hasMoreFriends, setHasMoreFriends] = useState(true); - const [isFetchingFriends, setIsFetchingFriends] = useState(false); - const [hasFriendLoadError, setHasFriendLoadError] = useState(false); - const [isFriendsEmpty, setIsFriendsEmpty] = useState(false); - const friendsObserver = useRef(null); - const friendsLoadingRef = useRef(null); - - // 获取基础信息 - const fetchAccountInfo = useCallback(async () => { - if (!id) return; - setLoadingInfo(true); - try { - const response = await getWechatAccountDetail(id); - if (response && response.data) { - setAccountInfo(response.data); - } else { - Toast.show({ - content: response?.msg || "获取账号信息失败", - position: "top", - }); - } - } catch (e) { - Toast.show({ content: "获取账号信息失败", position: "top" }); - } finally { - setLoadingInfo(false); - } - }, [id]); - - // 获取summary - const fetchAccountSummary = useCallback(async () => { - if (!id) return; - setLoadingSummary(true); - try { - const response = await getWechatAccountSummary(id); - if (response && response.data) { - setAccountSummary(response.data); - } else { - Toast.show({ - content: response?.msg || "获取账号概览失败", - position: "top", - }); - } - } catch (e) { - Toast.show({ content: "获取账号概览失败", position: "top" }); - } finally { - setLoadingSummary(false); - } - }, [id]); - - // 获取好友列表 - const fetchFriends = useCallback( - async (page: number = 1, isNewSearch: boolean = false) => { - if (!id || isFetchingFriends) return; - - try { - setIsFetchingFriends(true); - setHasFriendLoadError(false); - const response = await getWechatFriends({ - wechatAccountKeyword: id, - pageIndex: page, - pageSize: 20, - friendKeyword: searchQuery, - }); - - if (response && response.data) { - const newFriends = response.data.list.map((friend: any) => ({ - id: friend.id.toString(), - avatar: friend.avatar || "/placeholder.svg", - nickname: friend.nickname || "未知用户", - wechatId: friend.wechatId || "", - remark: friend.memo || "", - addTime: - friend.createTime || new Date().toISOString().split("T")[0], - lastInteraction: - friend.lastInteraction || new Date().toISOString().split("T")[0], - tags: friend.tags - ? friend.tags.map((tag: string, index: number) => ({ - id: `tag-${index}`, - name: tag, - color: getRandomTagColor(), - })) - : [], - region: friend.region || "未知", - source: friend.source || "未知", - notes: friend.notes || "", - })); - - if (isNewSearch) { - setFriends(newFriends); - if (newFriends.length === 0) { - setIsFriendsEmpty(true); - setHasMoreFriends(false); - } else { - setIsFriendsEmpty(false); - setHasMoreFriends(newFriends.length === 20); - } - } else { - setFriends((prev) => [...prev, ...newFriends]); - setHasMoreFriends(newFriends.length === 20); - } - - setFriendsTotal(response.data.total); - setFriendsPage(page); - } else { - setHasFriendLoadError(true); - if (isNewSearch) { - setFriends([]); - setIsFriendsEmpty(true); - setHasMoreFriends(false); - } - Toast.show({ - content: response?.msg || "获取好友列表失败", - position: "top", - }); - } - } catch (error) { - console.error("获取好友列表失败:", error); - setHasFriendLoadError(true); - if (isNewSearch) { - setFriends([]); - setIsFriendsEmpty(true); - setHasMoreFriends(false); - } - Toast.show({ - content: "获取好友列表失败,请检查网络连接", - position: "top", - }); - } finally { - setIsFetchingFriends(false); - } - }, - [id, searchQuery, isFetchingFriends] - ); - - // 初始化数据 - useEffect(() => { - if (id) { - fetchAccountInfo(); - fetchAccountSummary(); - if (activeTab === "friends") { - fetchFriends(1, true); - } - } - // eslint-disable-next-line - }, [id]); - - // 监听标签切换 - useEffect(() => { - if (activeTab === "friends" && id) { - setIsFriendsEmpty(false); - setHasFriendLoadError(false); - fetchFriends(1, true); - } - }, [activeTab, id, fetchFriends]); - - // 无限滚动加载好友 - useEffect(() => { - if ( - !friendsLoadingRef.current || - !hasMoreFriends || - isFetchingFriends || - isFriendsEmpty - ) - return; - - friendsObserver.current = new IntersectionObserver( - (entries) => { - if ( - entries[0].isIntersecting && - hasMoreFriends && - !isFetchingFriends && - !isFriendsEmpty - ) { - fetchFriends(friendsPage + 1, false); - } - }, - { threshold: 0.1 } - ); - - friendsObserver.current.observe(friendsLoadingRef.current); - - return () => { - if (friendsObserver.current) { - friendsObserver.current.disconnect(); - } - }; - }, [ - hasMoreFriends, - isFetchingFriends, - friendsPage, - fetchFriends, - isFriendsEmpty, - ]); - - // 工具函数 - const getRandomTagColor = (): string => { - const colors = [ - "bg-blue-100 text-blue-800", - "bg-green-100 text-green-800", - "bg-red-100 text-red-800", - "bg-pink-100 text-pink-800", - "bg-emerald-100 text-emerald-800", - "bg-amber-100 text-amber-800", - ]; - return colors[Math.floor(Math.random() * colors.length)]; - }; - - const calculateAccountAge = (registerTime: string) => { - const registerDate = new Date(registerTime); - const now = new Date(); - const diffTime = Math.abs(now.getTime() - registerDate.getTime()); - const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); - const years = Math.floor(diffDays / 365); - const months = Math.floor((diffDays % 365) / 30); - return { years, months }; - }; - - const formatAccountAge = (age: { years: number; months: number }) => { - if (age.years > 0) { - return `${age.years}年${age.months}个月`; - } - return `${age.months}个月`; - }; - - const getWeightColor = (weight: number) => { - if (weight >= 80) return "text-green-600"; - if (weight >= 60) return "text-yellow-600"; - return "text-red-600"; - }; - - const getWeightDescription = (weight: number) => { - if (weight >= 80) return "账号质量优秀,可以正常使用"; - if (weight >= 60) return "账号质量良好,需要注意使用频率"; - return "账号质量较差,建议谨慎使用"; - }; - - const handleTransferFriends = () => { - setShowTransferConfirm(true); - }; - - const confirmTransferFriends = () => { - Toast.show({ - content: "好友转移计划已创建,请在场景获客中查看详情", - position: "top", - }); - setShowTransferConfirm(false); - navigate("/scenarios"); - }; - - const handleFriendClick = async (friend: Friend) => { - setSelectedFriend(friend); - setShowFriendDetail(true); - setIsLoadingFriendDetail(true); - setFriendDetailError(null); - - try { - const response = await getWechatFriendDetail(friend.id); - if (response && response.data) { - setFriendDetail(response.data); - } else { - setFriendDetailError(response?.msg || "获取好友详情失败"); - } - } catch (error) { - console.error("获取好友详情失败:", error); - setFriendDetailError("网络错误,请稍后重试"); - } finally { - setIsLoadingFriendDetail(false); - } - }; - - const getRestrictionLevelColor = (level: string) => { - switch (level) { - case "high": - return "text-red-600"; - case "medium": - return "text-yellow-600"; - default: - return "text-gray-600"; - } - }; - - const formatDateTime = (dateString: string) => { - const date = new Date(dateString); - return date - .toLocaleString("zh-CN", { - year: "numeric", - month: "2-digit", - day: "2-digit", - hour: "2-digit", - minute: "2-digit", - second: "2-digit", - hour12: false, - }) - .replace(/\//g, "-"); - }; - - const handleSearch = () => { - setIsFriendsEmpty(false); - setHasFriendLoadError(false); - fetchFriends(1, true); - }; - - const handleTabChange = (value: string) => { - setActiveTab(value); - }; - - if (loadingInfo || loadingSummary) { - return ( - - 微信号详情 - - } - > -
- -
-
- ); - } - - return ( - }> -
- {/* 账号基本信息卡片 */} - -
-
- -
-
-
-
-

- {accountInfo?.nickname || "未知昵称"} -

- - {accountInfo?.wechatStatus === 1 ? "正常" : "异常"} - -
-

- 微信号:{accountInfo?.wechatAccount || "未知"} -

-
- - -
-
-
- - - {/* 标签页 */} - - - -
- {/* 账号基础信息 */} -
-
-
- -
-
账号年龄
- {accountSummary && ( -
- 注册于{" "} - {new Date( - accountSummary.accountAge - ).toLocaleDateString()} -
- )} -
-
- {accountSummary && ( -
- {formatAccountAge( - calculateAccountAge(accountSummary.accountAge) - )} -
- )} -
- -
-
- -
-
活跃程度
- {accountSummary && ( -
- 总聊天{" "} - {accountSummary.activityLevel.allTimes.toLocaleString()}{" "} - 次 -
- )} -
-
- {accountSummary && ( -
- {accountSummary.activityLevel.dayTimes.toLocaleString()} - 次/天 -
- )} -
-
- - {/* 账号权重评估 */} - {accountSummary && ( -
-
- - - 账号权重评估 - -
- - {accountSummary.accountWeight.scope} - - -
-
-

- {getWeightDescription(accountSummary.accountWeight.scope)} -

-
-
- 账号年龄 -
-
-
- - {accountSummary.accountWeight.ageWeight}% - -
-
- 活跃度 -
-
-
- - {accountSummary.accountWeight.activityWeigth}% - -
-
-
- )} - - {/* 限制记录 */} - {accountSummary && - accountSummary.restrictions && - accountSummary.restrictions.length > 0 && ( -
-
- - - 限制记录 - - -
-
- {accountSummary.restrictions - .slice(0, 3) - .map((restriction) => ( -
-
- - {restriction.reason} - - - {formatDateTime(restriction.date)} - -
- - {restriction.level === "high" - ? "高风险" - : restriction.level === "medium" - ? "中风险" - : "低风险"} - -
- ))} -
-
- )} -
- - - 0 ? ` (${friendsTotal.toLocaleString()})` : ""}`} - key="friends" - > -
- {/* 搜索栏 */} -
-
- setSearchQuery(e.target.value)} - prefix={} - allowClear - size="large" - onPressEnter={handleSearch} - /> -
- -
- - {/* 好友列表 */} -
- {isFriendsEmpty ? ( -
暂无好友数据
- ) : hasFriendLoadError ? ( -
-

加载失败,请重试

- -
- ) : ( - <> - {friends.map((friend) => ( -
handleFriendClick(friend)} - > - -
-
-
- {friend.nickname} - {friend.remark && ( - - ({friend.remark}) - - )} -
- -
-
- {friend.wechatId} -
-
- {friend.tags?.map((tag, index) => ( - - {typeof tag === "string" ? tag : tag.name} - - ))} -
-
-
- ))} - {hasMoreFriends && !isFriendsEmpty && ( -
- -
- )} - - )} -
-
-
- - -
- - {/* 限制记录详情弹窗 */} - setShowRestrictions(false)} - bodyStyle={{ borderRadius: "16px 16px 0 0" }} - > -
-
-

限制记录详情

- -
-

每次限制恢复时间为24小时

- {accountSummary && accountSummary.restrictions && ( -
- {accountSummary.restrictions.map((restriction) => ( -
-
-
- {restriction.reason} -
-
- {formatDateTime(restriction.date)} -
-
- - {restriction.level === "high" - ? "高风险" - : restriction.level === "medium" - ? "中风险" - : "低风险"} - -
- ))} -
- )} -
-
- - {/* 好友转移确认弹窗 */} - setShowTransferConfirm(false)} - bodyStyle={{ borderRadius: "16px 16px 0 0" }} - > -
-
-

确认好友转移

-
-

- 确定要将该微信号的好友转移到其他账号吗?此操作将创建一个好友转移计划。 -

-
- - -
-
-
- - {/* 好友详情弹窗 */} - setShowFriendDetail(false)} - bodyStyle={{ borderRadius: "16px 16px 0 0" }} - > -
-
-

好友详情

- -
- - {isLoadingFriendDetail ? ( -
- -
- ) : friendDetailError ? ( -
-

{friendDetailError}

- -
- ) : friendDetail && selectedFriend ? ( -
-
- -
-

- {selectedFriend.nickname} -

-

- 微信号:{selectedFriend.wechatId} -

-
-
- -
-
- 地区 - - {friendDetail.region || "未知"} - -
-
- 添加时间 - - {friendDetail.addDate} - -
-
- 来源 - - {friendDetail.source || "未知"} - -
- {friendDetail.memo && ( -
- 备注 - - {friendDetail.memo} - -
- )} - {friendDetail.tags && friendDetail.tags.length > 0 && ( -
- 标签 -
- {friendDetail.tags.map((tag, index) => ( - - {tag} - - ))} -
-
- )} -
-
- ) : null} -
-
- - ); -}; - -export default WechatAccountDetail; diff --git a/nkebao/src/pages/workspace/auto-group/list/index.tsx b/nkebao/src/pages/workspace/auto-group/list/index.tsx index 10d66484..dac222d8 100644 --- a/nkebao/src/pages/workspace/auto-group/list/index.tsx +++ b/nkebao/src/pages/workspace/auto-group/list/index.tsx @@ -296,6 +296,9 @@ const AutoGroupList: React.FC = () => { 更新时间:{task.lastCreateTime}
+
+ 创建时间:{task.createTime} +
)) diff --git a/nkebao/src/router/module/devices.tsx b/nkebao/src/router/module/devices.tsx deleted file mode 100644 index d31db945..00000000 --- a/nkebao/src/router/module/devices.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import Devices from "@/pages/devices/Devices"; -import DeviceDetail from "@/pages/devices/DeviceDetail"; - -const deviceRoutes = [ - { - path: "/devices", - element: , - auth: true, - }, - { - path: "/devices/:id", - element: , - auth: true, - }, -]; - -export default deviceRoutes; diff --git a/nkebao/src/router/module/index.tsx b/nkebao/src/router/module/index.tsx index c7233d03..807dbf86 100644 --- a/nkebao/src/router/module/index.tsx +++ b/nkebao/src/router/module/index.tsx @@ -1,7 +1,6 @@ import Home from "@/pages/home/index"; -import Mine from "@/pages/mine/index"; -import WechatAccounts from "@/pages/wechat-accounts/list/index"; -import WechatAccountDetail from "@/pages/wechat-accounts/detail/index"; +import WechatAccounts from "@/pages/mine/wechat-accounts/list/index"; +import WechatAccountDetail from "@/pages/mine/wechat-accounts/detail/index"; import Recharge from "@/pages/mine/recharge/index"; import UserSetting from "@/pages/mine/userSet/index"; @@ -12,11 +11,6 @@ const routes = [ element: , auth: true, // 需要登录 }, - { - path: "/mine", - element: , - auth: true, - }, // 微信号管理路由 { path: "/wechat-accounts", diff --git a/nkebao/src/router/module/other.tsx b/nkebao/src/router/module/other.tsx index f222072c..46c284fb 100644 --- a/nkebao/src/router/module/other.tsx +++ b/nkebao/src/router/module/other.tsx @@ -1,4 +1,3 @@ -import Profile from "@/pages/profile/Profile"; import Plans from "@/pages/plans/Plans"; import PlanDetail from "@/pages/plans/PlanDetail"; import Orders from "@/pages/orders/Orders"; @@ -6,16 +5,6 @@ import ContactImport from "@/pages/contact-import/ContactImport"; import SelectionTest from "@/components/SelectionTest"; const otherRoutes = [ - { - path: "/mine", - element: , - auth: true, - }, - { - path: "/profile", - element: , - auth: true, - }, { path: "/plans", element: , diff --git a/nkebao/src/router/module/users.tsx b/nkebao/src/router/module/users.tsx new file mode 100644 index 00000000..72d8ea13 --- /dev/null +++ b/nkebao/src/router/module/users.tsx @@ -0,0 +1,23 @@ +import Mine from "@/pages/mine/main/index"; +import Devices from "@/pages/mine/devices/index"; +import DeviceDetail from "@/pages/mine/devices/DeviceDetail"; + +const routes = [ + { + path: "/mine", + element: , + auth: true, + }, + { + path: "/devices", + element: , + auth: true, + }, + { + path: "/devices/:id", + element: , + auth: true, + }, +]; + +export default routes; diff --git a/nkebao/src/router/module/wechat-accounts.tsx b/nkebao/src/router/module/wechat-accounts.tsx index 378e49db..92137594 100644 --- a/nkebao/src/router/module/wechat-accounts.tsx +++ b/nkebao/src/router/module/wechat-accounts.tsx @@ -1,5 +1,5 @@ -import WechatAccounts from "@/pages/wechat-accounts/list"; -import WechatAccountDetail from "@/pages/wechat-accounts/detail"; +import WechatAccounts from "@/pages/mine/wechat-accounts/list"; +import WechatAccountDetail from "@/pages/mine/wechat-accounts/detail"; const wechatAccountRoutes = [ {