"use client" import { useState, useEffect, useRef, useCallback } from "react" import { useParams } from "next/navigation" import { useRouter } from "next/navigation" import { api } from "@/lib/api" import { fetchWechatAccountSummary, fetchWechatFriendDetail, WechatFriendDetail } from "@/api/wechat-accounts" import { Card } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Badge } from "@/components/ui/badge" import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { Progress } from "@/components/ui/progress" import { Input } from "@/components/ui/input" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { toast } from "@/components/ui/use-toast" import { ChevronLeft, Smartphone, Users, Star, Clock, MessageSquare, Shield, Info, UserPlus, Search, Filter, Tag, ChevronRight, Loader2, } from "lucide-react" import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogFooter, } from "@/components/ui/dialog" import { ScrollArea } from "@/components/ui/scroll-area" import { Tooltip as UITooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip" import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, } from "@/components/ui/pagination" interface ApiResponse { code: number; msg: string; data: T; } interface FriendsResponse { list: Array<{ id: number; nickname: string; avatar: string; wechatId: string; memo: string; tags: string[]; }>; total: number; } interface RestrictionRecord { id: string date: string reason: string recoveryTime: string type: "friend_limit" | "marketing" | "spam" | "other" } interface FriendTag { id: string name: string color: 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 WechatAccountDetail { id: string avatar: string nickname: string wechatId: string wechatAccount: string deviceId: string deviceName: string friendCount: number todayAdded: number status: "normal" | "abnormal" lastActive: string messageCount: number activeRate: number accountAge: { years: number months: number } totalChats: number chatFrequency: number restrictionRecords: RestrictionRecord[] isVerified: boolean firstMomentDate: string accountWeight: number weightFactors: { restrictionFactor: number verificationFactor: number ageFactor: number activityFactor: number } weeklyStats: { date: string friends: number messages: number }[] friends: Friend[] } 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 PageProps { params: { id: string } } export default function WechatAccountDetailPage() { const router = useRouter() const params = useParams() const id = params?.id as string const [account, setAccount] = useState(null) const [accountSummary, setAccountSummary] = 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 [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 friendsObserver = useRef(null) const friendsLoadingRef = useRef(null) const friendsContainerRef = useRef(null) const [initialData, setInitialData] = useState<{ avatar: string; nickname: string; status: "normal" | "abnormal"; wechatId: string; wechatAccount: string; deviceName: string; deviceId?: string | number; } | null>(null) useEffect(() => { // 从 URL 参数中获取初始数据 const searchParams = new URLSearchParams(window.location.search); const dataParam = searchParams.get('data'); if (dataParam) { try { const decodedData = JSON.parse(decodeURIComponent(dataParam)); setInitialData(decodedData); // 使用初始数据设置account const mockData = generateMockAccountData(id); if (decodedData) { mockData.avatar = decodedData.avatar; mockData.nickname = decodedData.nickname; mockData.status = decodedData.status; mockData.wechatId = decodedData.wechatId; mockData.deviceName = decodedData.deviceName; mockData.wechatAccount = decodedData.wechatAccount; } setAccount(mockData); setFriendsTotal(mockData.friendCount); setIsLoading(false); } catch (error) { console.error('解析初始数据失败:', error); setIsLoading(false); } } else { // 如果没有初始数据,使用模拟数据 const mockData = generateMockAccountData(id); setAccount(mockData); setFriendsTotal(mockData.friendCount); setIsLoading(false); } }, [id]); // 计算好友列表容器高度 const getFriendsContainerHeight = () => { // 最少显示一条记录的高度,最多显示十条记录的高度 const minHeight = 80; // 单条记录高度 const maxHeight = 800; // 十条记录高度 if (friends.length === 0) return minHeight; return Math.min(Math.max(friends.length * 80, minHeight), maxHeight); }; // 生成模拟账号数据(作为备用,服务器请求失败时使用) const generateMockAccountData = (accountId: string): WechatAccountDetail => { // 生成随机标签 const generateRandomTags = (count: number): FriendTag[] => { const tagPool = [ { name: "潜在客户", color: "bg-blue-100 text-blue-800" }, { name: "高意向", color: "bg-green-100 text-green-800" }, { name: "已成交", color: "bg-purple-100 text-purple-800" }, { name: "需跟进", color: "bg-yellow-100 text-yellow-800" }, { name: "活跃用户", color: "bg-indigo-100 text-indigo-800" }, { name: "沉默用户", color: "bg-gray-100 text-gray-800" }, { name: "企业客户", color: "bg-red-100 text-red-800" }, { name: "个人用户", color: "bg-pink-100 text-pink-800" }, { name: "新增好友", color: "bg-emerald-100 text-emerald-800" }, { name: "老客户", color: "bg-amber-100 text-amber-800" }, ]; return Array.from({ length: Math.floor(Math.random() * count) + 1 }, () => { const randomTag = tagPool[Math.floor(Math.random() * tagPool.length)]; return { id: `tag-${Math.random().toString(36).substring(2, 9)}`, name: randomTag.name, color: randomTag.color, }; }); }; // 生成随机好友 const friendCount = Math.floor(Math.random() * (300 - 150)) + 150; const generateFriends = (count: number): Friend[] => { return Array.from({ length: count }, (_, i) => { const firstName = ["张", "王", "李", "赵", "陈", "刘", "杨", "黄", "周", "吴"][Math.floor(Math.random() * 10)]; const secondName = ["小", "大", "明", "华", "强", "伟", "芳", "娜", "秀", "英"][ Math.floor(Math.random() * 10) ]; const lastName = ["明", "华", "强", "伟", "芳", "娜", "秀", "英", "军", "杰"][Math.floor(Math.random() * 10)]; const nickname = firstName + secondName + lastName; // 生成随机的添加时间(过去1年内) const addDate = new Date(); addDate.setDate(addDate.getDate() - Math.floor(Math.random() * 365)); // 生成随机的最后互动时间(过去30天内) const lastDate = new Date(); lastDate.setDate(lastDate.getDate() - Math.floor(Math.random() * 30)); return { id: `friend-${i}`, avatar: `/placeholder.svg?height=40&width=40&text=${nickname[0]}`, nickname, wechatId: `wxid_${Math.random().toString(36).substring(2, 9)}`, remark: Math.random() > 0.5 ? `${nickname}(${["同事", "客户", "朋友", "同学"][Math.floor(Math.random() * 4)]})` : "", addTime: addDate.toISOString().split("T")[0], lastInteraction: lastDate.toISOString().split("T")[0], tags: generateRandomTags(3), region: ["广东", "北京", "上海", "浙江", "江苏", "四川", "湖北", "福建", "山东", "河南"][ Math.floor(Math.random() * 10) ], source: ["抖音", "小红书", "朋友介绍", "搜索添加", "群聊", "附近的人", "名片分享"][ Math.floor(Math.random() * 7) ], notes: Math.random() > 0.7 ? ["对产品很感兴趣", "需要进一步跟进", "已购买过产品", "价格敏感", "需要更多信息"][ Math.floor(Math.random() * 5) ] : "", }; }); }; const friends = generateFriends(friendCount); const mockAccount: WechatAccountDetail = { id: accountId, avatar: "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/img_v3_02jn_e7fcc2a4-3560-478d-911a-4ccd69c6392g.jpg-a8zVtwxMuSrPWN9dfWH93EBY0yM3Dh.jpeg", nickname: "卡若-25vig", wechatId: `wxid_${Math.random().toString(36).substr(2, 8)}`, wechatAccount: initialData?.wechatAccount || "wxid_default", deviceId: "device-1", deviceName: "设备1", friendCount: friends.length, todayAdded: 12, status: "normal", lastActive: new Date().toLocaleString(), messageCount: 1234, activeRate: 87, accountAge: { years: 2, months: 8, }, totalChats: 15234, chatFrequency: 42, restrictionRecords: [ { id: "1", date: "2024-02-25", reason: "添加好友过于频繁", recoveryTime: "2024-02-26", type: "friend_limit", }, { id: "2", date: "2024-01-15", reason: "营销内容违规", recoveryTime: "2024-01-16", type: "marketing", }, ], isVerified: true, firstMomentDate: "2021-06-15", accountWeight: 85, weightFactors: { restrictionFactor: 0.8, verificationFactor: 1.0, ageFactor: 0.9, activityFactor: 0.85, }, weeklyStats: Array.from({ length: 7 }, (_, i) => ({ date: `Day ${i + 1}`, friends: Math.floor(Math.random() * 50) + 50, messages: Math.floor(Math.random() * 100) + 100, })), friends: friends, }; return mockAccount; }; // 随机生成标签颜色 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)]; }; // 修改fetchFriends函数 const fetchFriends = useCallback(async (page: number = 1, isNewSearch: boolean = false) => { if (!account || isFetchingFriends) return; try { setIsFetchingFriends(true); setHasFriendLoadError(false); const data = await api.get>(`/v1/wechats/${account?.wechatId}/friends?page=${page}&limit=30`, true); if (data && data.code === 200) { // 更新总数计数 if (isNewSearch || friendsTotal === 0) { setFriendsTotal(data.data.total || 0); } const newFriends = data.data.list.map((friend) => ({ id: friend.id.toString(), avatar: friend.avatar, nickname: friend.nickname, wechatId: friend.wechatId, remark: friend.memo || '', addTime: '2024-01-01', // 接口未返回,使用默认值 lastInteraction: '2024-01-01', // 接口未返回,使用默认值 tags: (friend.tags || []).map((label: string, index: number) => ({ id: `tag-${index}`, name: label, color: getRandomTagColor(), })), region: '未知地区', source: '未记录', notes: '', })); // 更新状态 if (isNewSearch) { setFriends(newFriends); } else { setFriends(prev => [...prev, ...newFriends]); } setFriendsPage(page); // 判断是否还有更多数据 setHasMoreFriends(page * 30 < data.data.total); } else { setHasFriendLoadError(true); toast({ title: "获取好友列表失败", description: data?.msg || "请稍后再试", variant: "destructive" }); } } catch (error) { setHasFriendLoadError(true); console.error("获取好友列表失败:", error); toast({ title: "获取好友列表失败", description: "请检查网络连接或稍后再试", variant: "destructive" }); } finally { setIsFetchingFriends(false); } }, [account, id, friendsTotal]); // 处理搜索 const handleSearch = useCallback(() => { setFriends([]); setFriendsPage(1); setHasMoreFriends(true); fetchFriends(1, true); }, [fetchFriends]); // 处理标签切换 const handleTabChange = (value: string) => { setActiveTab(value); // 只在首次切换到 friends 标签时加载好友列表 if (value === "friends" && friends.length === 0) { fetchFriends(1, true); } }; // 设置IntersectionObserver用于懒加载 useEffect(() => { friendsObserver.current = new IntersectionObserver((entries) => { if (entries[0].isIntersecting && hasMoreFriends && !isFetchingFriends) { fetchFriends(friendsPage + 1); } }, { threshold: 0.5 }); return () => { if (friendsObserver.current) { friendsObserver.current.disconnect(); } }; }, [fetchFriends, friendsPage, hasMoreFriends, isFetchingFriends]); // 观察加载指示器 useEffect(() => { if (friendsLoadingRef.current && friendsObserver.current) { friendsObserver.current.observe(friendsLoadingRef.current); } return () => { if (friendsLoadingRef.current && friendsObserver.current) { friendsObserver.current.unobserve(friendsLoadingRef.current); } }; }, [friendsLoadingRef.current, friendsObserver.current]); // 计算账号年龄 const calculateAccountAge = (registerTime: string) => { const register = new Date(registerTime); const now = new Date(); const years = now.getFullYear() - register.getFullYear(); const months = now.getMonth() - register.getMonth(); if (months < 0) { return { years: years - 1, months: months + 12 }; } return { years, months }; }; // 获取账号概览数据 const fetchSummaryData = useCallback(async () => { if (!account?.wechatId || isLoading) return; try { setIsLoading(true); const response = await fetchWechatAccountSummary(account.wechatId); if (response.code === 200) { setAccountSummary(response.data); } else { toast({ title: "获取账号概览失败", description: response.msg || "请稍后再试", variant: "destructive" }); } } catch (error) { console.error("获取账号概览失败:", error); toast({ title: "获取账号概览失败", description: "请检查网络连接或稍后再试", variant: "destructive" }); } finally { setIsLoading(false); } }, [account?.wechatId]); // 统一在账号数据加载完成后获取概览数据 useEffect(() => { if (account?.wechatId) { fetchSummaryData(); } }, [account?.wechatId, fetchSummaryData]); if (!account) { return
加载中...
} 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 calculateMaxDailyAdds = (weight: number) => { const baseLimit = 20 return Math.floor(baseLimit * (weight / 100)) } const getRestrictionTypeColor = (type: string) => { switch (type) { case "friend_limit": return "text-yellow-600" case "marketing": return "text-red-600" case "spam": return "text-orange-600" default: return "text-gray-600" } } const formatAccountAge = (age: { years: number; months: number }) => { if (age.years > 0) { return `${age.years}年${age.months}个月`; } return `${age.months}个月`; }; const handleTransferFriends = () => { setShowTransferConfirm(true) } const confirmTransferFriends = () => { // 模拟API调用 toast({ title: "好友转移成功", description: `已成功转移 ${account?.friends.length} 个好友`, }); setShowTransferConfirm(false) } const handleFriendClick = async (friend: Friend) => { setSelectedFriend(friend) setShowFriendDetail(true) setIsLoadingFriendDetail(true) setFriendDetailError(null) try { const response = await fetchWechatFriendDetail(friend.wechatId) if (response.code === 200) { setFriendDetail(response.data) } else { setFriendDetailError(response.msg || "获取好友详情失败") } } catch (error) { console.error("获取好友详情失败:", error) setFriendDetailError("获取好友详情失败,请稍后再试") } finally { setIsLoadingFriendDetail(false) } } // 修改获取限制等级颜色的函数 const getRestrictionLevelColor = (level: string) => { const colorMap = { "1": "text-gray-600", "2": "text-yellow-600", "3": "text-red-600" }; return colorMap[level as keyof typeof colorMap] || "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, '-'); } return ( {isLoading ? (
) : account ? (

账号详情

{account.nickname[0]} {account.isVerified && ( 已认证 )}

{account.nickname}

{account.status === "normal" ? "正常" : "异常"}

微信号:{account.wechatAccount}

账号概览 好友列表{activeTab === "friends" && friendsTotal > 0 ? ` (${friendsTotal.toLocaleString()})` : ''} {/* 账号基础信息 */}
账号年龄
{accountSummary && ( <>
{formatAccountAge(calculateAccountAge(accountSummary.accountAge))}
注册时间:{new Date(accountSummary.accountAge).toLocaleDateString()}
)}
活跃程度
{accountSummary && ( <>
{accountSummary.activityLevel.dayTimes.toLocaleString()}次/天
总聊天数:{accountSummary.activityLevel.allTimes.toLocaleString()}
)}
{/* 账号权重评估 */}
账号权重评估
{accountSummary && (
{accountSummary.accountWeight.scope}
)}
{accountSummary && ( <>

{getWeightDescription(accountSummary.accountWeight.scope)}

账号年龄
{accountSummary.accountWeight.ageWeight}%
活跃度
{accountSummary.accountWeight.activityWeigth}%
限制影响
{accountSummary.accountWeight.restrictWeight}%
实名认证
{accountSummary.accountWeight.realNameWeight}%
)}
{/* 添加好友统计 */}
添加好友统计

根据账号权重计算每日可添加好友数量

{accountSummary && (
今日已添加 {accountSummary.statistics.todayAdded}
添加进度 {accountSummary.statistics.todayAdded}/{accountSummary.statistics.addLimit}
根据当前账号权重({accountSummary.accountWeight.scope}分),每日最多可添加{" "} {accountSummary.statistics.addLimit.toLocaleString()}{" "} 个好友
)}
{/* 限制记录 */}
限制记录
{accountSummary && ( setShowRestrictions(true)}> 共 {accountSummary.restrictions.length} 次 )}
{accountSummary && (
{accountSummary.restrictions.slice(0, 2).map((record) => (
{record.reason} {formatDateTime(record.date)}
))}
)}
{/* 搜索栏 */}
setSearchQuery(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleSearch()} className="pl-9 bg-white border-gray-200 focus:border-blue-500" />
{/* 好友列表 */}
{isFetchingFriends && friends.length === 0 ? (
) : friends.length === 0 ? (
未找到匹配的好友
) : ( <> {friends.map((friend) => (
handleFriendClick(friend)} > {friend.nickname?.[0] || 'U'}
{friend.nickname} {friend.remark && ({friend.remark})}
{friend.wechatId}
{friend.tags?.map((tag, index) => ( {typeof tag === 'string' ? tag : tag.name} ))}
))} {hasMoreFriends && (
)} )}
{/* 限制记录详情弹窗 */} 限制记录详情 每次限制恢复时间为24小时
{(accountSummary?.restrictions && accountSummary.restrictions.length > 0) ? ( accountSummary.restrictions.map((record) => (
{record.reason}
{formatDateTime(record.date)}
恢复时间:{formatDateTime(record.date)}
)) ) : (
暂无风险记录,请继续保持
)}
{/* 好友转移确认弹窗 */} 好友转移确认 即将导出该微信号的好友列表,用于创建新的获客计划
{account.nickname[0]}
{account.nickname}
{account.wechatId}

• 将导出该账号下的所有好友信息

• 好友信息将用于创建新的订单获客计划

• 导出过程中请勿关闭页面

{/* 好友详情弹窗 */} 好友详情 {isLoadingFriendDetail ? (
) : friendDetailError ? (

{friendDetailError}

) : friendDetail ? (
{friendDetail.nickname[0]}
{friendDetail.nickname}
{friendDetail.wechatId}
{friendDetail.memo && (
备注: {friendDetail.memo}
)}
添加时间
{friendDetail.addDate}
最近互动
{friendDetail.playDate}
{friendDetail.region && (
地区
{friendDetail.region || '未知地区'}
)}
来源
{friendDetail.source || '未记录'}
标签
{friendDetail.tags.map((tag, index) => ( {tag} ))} {friendDetail.tags.length === 0 && 暂无标签}
{/* */}
) : selectedFriend && (
{selectedFriend.nickname[0]}
{selectedFriend.nickname}
{selectedFriend.wechatId}
{selectedFriend.remark && (
备注: {selectedFriend.remark}
)}
添加时间
{selectedFriend.addTime}
最近互动
{selectedFriend.lastInteraction}
地区
{selectedFriend.region}
来源
{selectedFriend.source}
标签
{selectedFriend.tags.map((tag: FriendTag) => ( {tag.name} ))} {selectedFriend.tags.length === 0 && 暂无标签}
{selectedFriend.notes && (
备注信息
{selectedFriend.notes}
)}
)}
) : (

未找到账号信息

)}
) }