From 87067011dfb74a46bde1800e2a320ce3a96a7111 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=B0=B8=E5=B9=B3?= Date: Sat, 5 Jul 2025 14:12:47 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=AE=BE=E5=A4=87=E8=AF=A6=E6=83=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/src/api/devices.ts | 26 + nkebao/src/pages/devices/DeviceDetail.tsx | 850 +++++++++++++++++++++- 2 files changed, 872 insertions(+), 4 deletions(-) diff --git a/nkebao/src/api/devices.ts b/nkebao/src/api/devices.ts index f90dc1a6..da84ee8f 100644 --- a/nkebao/src/api/devices.ts +++ b/nkebao/src/api/devices.ts @@ -171,4 +171,30 @@ export const devicesApi = { async checkStatus(ids: string[]): Promise>> { return post>>(`${API_BASE}/status`, { deviceIds: ids }); }, + + // 获取设备关联的微信账号 + async getRelatedAccounts(id: string | number): Promise> { + return get>(`/v1/wechats/related-device/${id}`); + }, + + // 获取设备操作记录 + async getHandleLogs(id: string | number, page: number = 1, limit: number = 10): Promise> { + return get>(`/v1/devices/${id}/handle-logs?page=${page}&limit=${limit}`); + }, + + // 更新设备任务配置 + async updateTaskConfig(config: { + deviceId: string | number; + autoAddFriend?: boolean; + autoReply?: boolean; + momentsSync?: boolean; + aiChat?: boolean; + }): Promise> { + return post>(`/v1/devices/task-config`, config); + }, + + // 获取设备任务配置 + async getTaskConfig(id: string | number): Promise> { + return get>(`/v1/devices/${id}/task-config`); + }, }; \ No newline at end of file diff --git a/nkebao/src/pages/devices/DeviceDetail.tsx b/nkebao/src/pages/devices/DeviceDetail.tsx index 2b814edf..1911f9ff 100644 --- a/nkebao/src/pages/devices/DeviceDetail.tsx +++ b/nkebao/src/pages/devices/DeviceDetail.tsx @@ -1,7 +1,849 @@ -import React from 'react'; -import { useParams } from 'react-router-dom'; +import React, { useState, useEffect, useRef } from 'react'; +import { useParams, useNavigate } from 'react-router-dom'; +import { ChevronLeft, Smartphone, Battery, Wifi, MessageCircle, Users, Settings, History, RefreshCw, Loader2 } from 'lucide-react'; +import { devicesApi, fetchDeviceDetail, fetchDeviceRelatedAccounts, fetchDeviceHandleLogs, updateDeviceTaskConfig } from '../../api/devices'; +import { useToast } from '../../components/ui/toast'; + +interface WechatAccount { + id: string; + avatar: string; + nickname: string; + wechatId: string; + gender: number; + status: number; + statusText: string; + wechatAlive: number; + wechatAliveText: string; + addFriendStatus: number; + totalFriend: number; + lastActive: string; +} + +interface Device { + id: string; + imei: string; + name: string; + status: "online" | "offline"; + battery: number; + lastActive: string; + historicalIds: string[]; + wechatAccounts: WechatAccount[]; + features: { + autoAddFriend: boolean; + autoReply: boolean; + momentsSync: boolean; + aiChat: boolean; + }; + history: { + time: string; + action: string; + operator: string; + }[]; + totalFriend: number; + thirtyDayMsgCount: number; +} + +interface HandleLog { + id: string | number; + content: string; + username: string; + createTime: string; +} export default function DeviceDetail() { - const { id } = useParams(); - return
设备详情页,当前ID: {id}
; + const { id } = useParams<{ id: string }>(); + const navigate = useNavigate(); + const { toast } = useToast(); + const [device, setDevice] = useState(null); + const [activeTab, setActiveTab] = useState("info"); + const [loading, setLoading] = useState(true); + const [accountsLoading, setAccountsLoading] = useState(false); + const [logsLoading, setLogsLoading] = useState(false); + const [handleLogs, setHandleLogs] = useState([]); + const [logPage, setLogPage] = useState(1); + const [hasMoreLogs, setHasMoreLogs] = useState(true); + const logsPerPage = 10; + const logsEndRef = useRef(null); + const [savingFeatures, setSavingFeatures] = useState({ + autoAddFriend: false, + autoReply: false, + momentsSync: false, + aiChat: false + }); + const [tabChangeLoading, setTabChangeLoading] = useState(false); + const [accountPage, setAccountPage] = useState(1); + const [hasMoreAccounts, setHasMoreAccounts] = useState(true); + const accountsPerPage = 10; + const accountsEndRef = useRef(null); + + // 获取设备详情 + useEffect(() => { + if (!id) return; + + const fetchDevice = async () => { + try { + setLoading(true); + const response = await fetchDeviceDetail(id); + + if (response && response.code === 200 && response.data) { + const serverData = response.data; + + // 构建符合前端期望格式的设备对象 + const formattedDevice: Device = { + id: serverData.id?.toString() || "", + imei: serverData.imei || "", + name: serverData.memo || "未命名设备", + status: serverData.alive === 1 ? "online" : "offline", + battery: serverData.battery || 0, + lastActive: serverData.lastUpdateTime || new Date().toISOString(), + historicalIds: [], + wechatAccounts: [], + history: [], + features: { + autoAddFriend: false, + autoReply: false, + momentsSync: false, + aiChat: false + }, + totalFriend: serverData.totalFriend || 0, + thirtyDayMsgCount: serverData.thirtyDayMsgCount || 0 + }; + + // 解析features + if (serverData.features) { + formattedDevice.features = { + autoAddFriend: Boolean(serverData.features.autoAddFriend), + autoReply: Boolean(serverData.features.autoReply), + momentsSync: Boolean(serverData.features.momentsSync || serverData.features.contentSync), + aiChat: Boolean(serverData.features.aiChat) + }; + } else if (serverData.taskConfig) { + try { + const taskConfig = JSON.parse(serverData.taskConfig || '{}'); + + if (taskConfig) { + formattedDevice.features = { + autoAddFriend: Boolean(taskConfig.autoAddFriend), + autoReply: Boolean(taskConfig.autoReply), + momentsSync: Boolean(taskConfig.momentsSync), + aiChat: Boolean(taskConfig.aiChat) + }; + } + } catch (err) { + console.error('解析taskConfig失败:', err); + } + } + + setDevice(formattedDevice); + + // 如果当前激活标签是"accounts",则立即加载关联微信账号 + if (activeTab === "accounts") { + fetchRelatedAccounts(); + } + } else { + toast({ + title: "获取设备信息失败", + description: response.message || "未知错误", + variant: "destructive", + }); + } + } catch (error) { + console.error("获取设备信息失败:", error); + toast({ + title: "获取设备信息失败", + description: "请稍后重试", + variant: "destructive", + }); + } finally { + setLoading(false); + } + }; + + fetchDevice(); + }, [id, toast]); + + // 获取设备关联微信账号 + const fetchRelatedAccounts = async (page = 1) => { + if (!id || accountsLoading) return; + + try { + setAccountsLoading(true); + const response = await fetchDeviceRelatedAccounts(id); + + if (response && response.code === 200 && response.data) { + const accounts = response.data.accounts || []; + + if (page === 1) { + setDevice(prev => prev ? { + ...prev, + wechatAccounts: accounts + } : null); + } else { + setDevice(prev => prev ? { + ...prev, + wechatAccounts: [...prev.wechatAccounts, ...accounts] + } : null); + } + + setHasMoreAccounts(accounts.length === accountsPerPage); + setAccountPage(page); + } else { + toast({ + title: "获取关联账号失败", + description: response.message || "请稍后重试", + variant: "destructive", + }); + } + } catch (error) { + console.error("获取关联账号失败:", error); + toast({ + title: "获取关联账号失败", + description: "请稍后重试", + variant: "destructive", + }); + } finally { + setAccountsLoading(false); + } + }; + + // 获取操作记录 + const fetchHandleLogs = async () => { + if (!id || logsLoading) return; + + try { + setLogsLoading(true); + const response = await fetchDeviceHandleLogs(id, logPage, logsPerPage); + + if (response && response.code === 200 && response.data) { + const logs = response.data.list || []; + + if (logPage === 1) { + setHandleLogs(logs); + } else { + setHandleLogs(prev => [...prev, ...logs]); + } + + setHasMoreLogs(logs.length === logsPerPage); + } else { + toast({ + title: "获取操作记录失败", + description: response.message || "请稍后重试", + variant: "destructive", + }); + } + } catch (error) { + console.error("获取操作记录失败:", error); + toast({ + title: "获取操作记录失败", + description: "请稍后重试", + variant: "destructive", + }); + } finally { + setLogsLoading(false); + } + }; + + // 加载更多操作记录 + const loadMoreLogs = () => { + if (logsLoading || !hasMoreLogs) return; + setLogPage(prev => prev + 1); + fetchHandleLogs(); + }; + + // 无限滚动加载操作记录 + useEffect(() => { + if (!hasMoreLogs || logsLoading) return; + + const observer = new IntersectionObserver( + entries => { + if (entries[0].isIntersecting && hasMoreLogs && !logsLoading) { + loadMoreLogs(); + } + }, + { threshold: 0.5 } + ); + + if (logsEndRef.current) { + observer.observe(logsEndRef.current); + } + + return () => { + observer.disconnect(); + }; + }, [hasMoreLogs, logsLoading]); + + // 获取任务配置 + const fetchTaskConfig = async () => { + if (!id) return; + + try { + const response = await devicesApi.getTaskConfig(id); + if (response && response.code === 200 && response.data) { + const config = response.data; + setDevice(prev => prev ? { + ...prev, + features: { + autoAddFriend: Boolean(config.autoAddFriend), + autoReply: Boolean(config.autoReply), + momentsSync: Boolean(config.momentsSync), + aiChat: Boolean(config.aiChat) + } + } : null); + } + } catch (error) { + console.error("获取任务配置失败:", error); + } + }; + + // 标签页切换处理 + const handleTabChange = (value: string) => { + setActiveTab(value); + setTabChangeLoading(true); + + setTimeout(() => { + if (value === "accounts" && device && (!device.wechatAccounts || device.wechatAccounts.length === 0)) { + fetchRelatedAccounts(1); + } else if (value === "history" && handleLogs.length === 0) { + setLogPage(1); + setHasMoreLogs(true); + fetchHandleLogs(); + } + setTabChangeLoading(false); + }, 100); + }; + + // 功能开关处理 + const handleFeatureChange = async (feature: keyof Device['features'], checked: boolean) => { + if (!id) return; + + setSavingFeatures(prev => ({ ...prev, [feature]: true })); + + try { + const response = await updateDeviceTaskConfig({ + deviceId: id, + [feature]: checked + }); + + if (response && response.code === 200) { + setDevice(prev => prev ? { + ...prev, + features: { + ...prev.features, + [feature]: checked + } + } : null); + + toast({ + title: "设置成功", + description: `${getFeatureName(feature)}已${checked ? '启用' : '禁用'}`, + }); + } else { + toast({ + title: "设置失败", + description: response.message || "请稍后重试", + variant: "destructive", + }); + } + } catch (error) { + console.error("设置功能失败:", error); + toast({ + title: "设置失败", + description: "请稍后重试", + variant: "destructive", + }); + } finally { + setSavingFeatures(prev => ({ ...prev, [feature]: false })); + } + }; + + // 获取功能名称 + const getFeatureName = (feature: string): string => { + const names: Record = { + autoAddFriend: "自动加好友", + autoReply: "自动回复", + momentsSync: "朋友圈同步", + aiChat: "AI会话" + }; + return names[feature] || feature; + }; + + // 加载更多账号 + const loadMoreAccounts = () => { + if (accountsLoading || !hasMoreAccounts) return; + fetchRelatedAccounts(accountPage + 1); + }; + + // 无限滚动加载账号 + useEffect(() => { + if (!hasMoreAccounts || accountsLoading) return; + + const observer = new IntersectionObserver( + entries => { + if (entries[0].isIntersecting && hasMoreAccounts && !accountsLoading) { + loadMoreAccounts(); + } + }, + { threshold: 0.5 } + ); + + if (accountsEndRef.current) { + observer.observe(accountsEndRef.current); + } + + return () => { + observer.disconnect(); + }; + }, [hasMoreAccounts, accountsLoading]); + + if (loading) { + return ( +
+
+ +

加载设备信息中...

+
+
+ ); + } + + if (!device) { + return ( +
+
+
+ +
+
设备不存在或已被删除
+
+ 无法加载ID为 "{id}" 的设备信息,请检查设备是否存在。 +
+ +
+
+ ); + } + + return ( +
+ {/* 固定header */} +
+
+
+ +

设备详情

+
+ +
+
+ + {/* 内容区域 */} +
+
+ {/* 设备基本信息卡片 */} +
+
+
+ +
+
+
+

{device.name}

+ + {device.status === "online" ? "在线" : "离线"} + +
+
+ IMEI: + {device.imei} +
+ {device.historicalIds && device.historicalIds.length > 0 && ( +
历史ID: {device.historicalIds.join(", ")}
+ )} +
+
+
+
+ + {device.battery}% +
+
+ + {device.status === "online" ? "已连接" : "未连接"} +
+
+
最后活跃:{device.lastActive}
+
+ + {/* 标签页 */} +
+
+ + + +
+ + {/* 基本信息标签页 */} + {activeTab === "info" && ( +
+ {/* 功能配置 */} +
+
+
+
自动加好友
+
自动通过好友验证
+
+
+ {savingFeatures.autoAddFriend && ( + + )} + +
+
+
+
+
自动回复
+
自动回复好友消息
+
+
+ {savingFeatures.autoReply && ( + + )} + +
+
+
+
+
朋友圈同步
+
自动同步朋友圈内容
+
+
+ {savingFeatures.momentsSync && ( + + )} + +
+
+
+
+
AI会话
+
启用AI智能对话
+
+
+ {savingFeatures.aiChat && ( + + )} + +
+
+
+ + {/* 统计卡片 */} +
+
+
+ + 好友总数 +
+
+ {(device.totalFriend || 0).toLocaleString()} +
+
+
+
+ + 消息数量 +
+
+ {(device.thirtyDayMsgCount || 0).toLocaleString()} +
+
+
+
+ )} + + {/* 关联账号标签页 */} + {activeTab === "accounts" && ( +
+
+

微信账号列表

+ +
+ +
+ {accountsLoading && !device?.wechatAccounts?.length ? ( +
+ + 加载微信账号中... +
+ ) : device?.wechatAccounts && device.wechatAccounts.length > 0 ? ( +
+ {device.wechatAccounts.map((account) => ( +
+ {account.nickname} +
+
+
{account.nickname}
+ + {account.wechatAliveText} + +
+
微信号: {account.wechatId}
+
性别: {account.gender === 1 ? "男" : "女"}
+
+ 好友数: {account.totalFriend} + + {account.statusText} + +
+
最后活跃: {account.lastActive}
+
+
+ ))} + + {/* 加载更多区域 */} +
+ {accountsLoading && hasMoreAccounts ? ( +
+ + 加载更多... +
+ ) : hasMoreAccounts ? ( + + ) : device.wechatAccounts.length > 0 && ( + - 已加载全部记录 - + )} +
+
+ ) : ( +
+

此设备暂无关联的微信账号

+ +
+ )} +
+
+ )} + + {/* 操作记录标签页 */} + {activeTab === "history" && ( +
+
+

操作记录

+ +
+ +
+ {logsLoading && handleLogs.length === 0 ? ( +
+ + 加载操作记录中... +
+ ) : handleLogs.length > 0 ? ( +
+ {handleLogs.map((log) => ( +
+
+ +
+
+
{log.content}
+
+ 操作人: {log.username} · {log.createTime} +
+
+
+ ))} + + {/* 加载更多区域 */} +
+ {logsLoading && hasMoreLogs ? ( +
+ + 加载更多... +
+ ) : hasMoreLogs ? ( + + ) : ( + - 已加载全部记录 - + )} +
+
+ ) : ( +
+

暂无操作记录

+ +
+ )} +
+
+ )} +
+
+
+
+ ); } \ No newline at end of file