From 77a2247cc3b0f8c6149ce67da938120b954616e2 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Wed, 11 Jun 2025 15:30:37 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9C=BA=E6=99=AF=E8=8E=B7=E5=AE=A2=E7=95=8C?= =?UTF-8?q?=E9=9D=A2=E4=BC=98=E5=8C=96=E5=8F=8A=E8=AE=BE=E5=A4=87=E9=80=89?= =?UTF-8?q?=E6=8B=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/device-selection-dialog.tsx | 391 ++++++------------ .../app/scenarios/new/steps/BasicSettings.tsx | 4 +- .../new/steps/FriendRequestSettings.tsx | 62 +-- .../new/components/traffic-pool-step.tsx | 2 +- 4 files changed, 147 insertions(+), 312 deletions(-) diff --git a/Cunkebao/app/components/device-selection-dialog.tsx b/Cunkebao/app/components/device-selection-dialog.tsx index d6c4e31e..05625768 100644 --- a/Cunkebao/app/components/device-selection-dialog.tsx +++ b/Cunkebao/app/components/device-selection-dialog.tsx @@ -1,34 +1,26 @@ "use client" import { useState, useEffect } from "react" -import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog" -import { Button } from "@/components/ui/button" +import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog" import { Input } from "@/components/ui/input" -import { Search, Filter, RefreshCw } from "lucide-react" +import { Button } from "@/components/ui/button" +import { Search, RefreshCw, Filter } from "lucide-react" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" -import { Checkbox } from "@/components/ui/checkbox" -import { Card } from "@/components/ui/card" -import { Badge } from "@/components/ui/badge" import { ScrollArea } from "@/components/ui/scroll-area" -import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs" -import { ImeiDisplay } from "@/components/ImeiDisplay" - -interface WechatAccount { - wechatId: string - nickname: string - remainingAdds: number - maxDailyAdds: number - todayAdded: number -} +import { Checkbox } from "@/components/ui/checkbox" +import { api } from "@/lib/api" interface Device { id: string - imei: string name: string + imei: string status: "online" | "offline" - wechatAccounts: WechatAccount[] - usedInPlans: number - tags?: string[] + wechatAccounts: { + wechatId: string + nickname: string + remainingAdds: number + maxDailyAdds: number + }[] } interface DeviceSelectionDialogProps { @@ -36,117 +28,61 @@ interface DeviceSelectionDialogProps { onOpenChange: (open: boolean) => void selectedDevices: string[] onSelect: (deviceIds: string[]) => void - excludeUsedDevices?: boolean } -export function DeviceSelectionDialog({ - open, - onOpenChange, - selectedDevices, - onSelect, - excludeUsedDevices = false, -}: DeviceSelectionDialogProps) { +export function DeviceSelectionDialog({ open, onOpenChange, selectedDevices, onSelect }: DeviceSelectionDialogProps) { const [devices, setDevices] = useState([]) const [loading, setLoading] = useState(false) const [searchQuery, setSearchQuery] = useState("") const [statusFilter, setStatusFilter] = useState("all") - const [tagFilter, setTagFilter] = useState("all") const [selectedDeviceIds, setSelectedDeviceIds] = useState([]) - const [activeTab, setActiveTab] = useState("all") - // 初始化已选设备 useEffect(() => { - if (open) { - setSelectedDeviceIds(selectedDevices) - } + if (open) setSelectedDeviceIds(selectedDevices) }, [open, selectedDevices]) - // 模拟获取设备数据 useEffect(() => { if (!open) return - const fetchDevices = async () => { setLoading(true) try { - // 模拟API请求 - await new Promise((resolve) => setTimeout(resolve, 800)) - - // 生成模拟数据 - const deviceTags = ["高性能", "稳定", "新设备", "已配置", "测试中", "备用"] - - const mockDevices: Device[] = Array.from({ length: 30 }, (_, i) => { - // 随机生成1-3个标签 - const tags = Array.from( - { length: Math.floor(Math.random() * 3) + 1 }, - () => deviceTags[Math.floor(Math.random() * deviceTags.length)], - ) - - // 确保标签唯一 - const uniqueTags = Array.from(new Set(tags)) - - return { - id: `device-${i + 1}`, - imei: `IMEI-${Math.random().toString(36).substr(2, 9)}`, - name: `设备 ${i + 1}`, - status: Math.random() > 0.3 ? "online" : "offline", - wechatAccounts: Array.from({ length: Math.floor(Math.random() * 2) + 1 }, (_, j) => ({ - wechatId: `wxid_${Math.random().toString(36).substr(2, 8)}`, - nickname: `微信号 ${j + 1}`, - remainingAdds: Math.floor(Math.random() * 10) + 5, - maxDailyAdds: 20, - todayAdded: Math.floor(Math.random() * 15), - })), - usedInPlans: Math.floor(Math.random() * 3), - tags: uniqueTags, - } - }) - - setDevices(mockDevices) - } catch (error) { - console.error("Failed to fetch devices:", error) + const params = [] + if (searchQuery) params.push(`keyword=${encodeURIComponent(searchQuery)}`) + if (statusFilter !== "all") params.push(`status=${statusFilter}`) + params.push("page=1", "limit=100") + const url = `/v1/devices?${params.join("&")}` + const response = await api.get(url) + const list = response.data?.list || response.data?.items || [] + const devices = list.map((device: any) => ({ + id: device.id?.toString() || device.id, + imei: device.imei || "", + name: device.memo || device.name || `设备_${device.id}`, + status: device.alive === 1 || device.status === "online" ? "online" : "offline", + wechatAccounts: [ + { + wechatId: device.wechatId || device.wxid || "", + nickname: device.nickname || "", + remainingAdds: device.remainingAdds || 0, + maxDailyAdds: device.maxDailyAdds || 0, + }, + ], + })) + setDevices(devices) + } catch { + setDevices([]) } finally { setLoading(false) } } - fetchDevices() - }, [open]) + }, [open, searchQuery, statusFilter]) - // 过滤设备 - const filteredDevices = devices.filter((device) => { - const matchesSearch = - searchQuery === "" || - device.name.toLowerCase().includes(searchQuery.toLowerCase()) || - device.imei.toLowerCase().includes(searchQuery.toLowerCase()) || - device.wechatAccounts.some( - (account) => - account.wechatId.toLowerCase().includes(searchQuery.toLowerCase()) || - account.nickname.toLowerCase().includes(searchQuery.toLowerCase()), - ) - - const matchesStatus = statusFilter === "all" || device.status === statusFilter - - const matchesUsage = !excludeUsedDevices || device.usedInPlans === 0 - - const matchesTag = tagFilter === "all" || (device.tags && device.tags.includes(tagFilter)) - - const matchesTab = - activeTab === "all" || - (activeTab === "online" && device.status === "online") || - (activeTab === "offline" && device.status === "offline") || - (activeTab === "unused" && device.usedInPlans === 0) - - return matchesSearch && matchesStatus && matchesUsage && matchesTag && matchesTab - }) - - // 处理选择设备 const handleSelectDevice = (deviceId: string) => { setSelectedDeviceIds((prev) => - prev.includes(deviceId) ? prev.filter((id) => id !== deviceId) : [...prev, deviceId], + prev.includes(deviceId) ? prev.filter((id) => id !== deviceId) : [...prev, deviceId] ) } - // 处理全选 const handleSelectAll = () => { if (selectedDeviceIds.length === filteredDevices.length) { setSelectedDeviceIds([]) @@ -155,189 +91,114 @@ export function DeviceSelectionDialog({ } } - // 处理确认选择 const handleConfirm = () => { onSelect(selectedDeviceIds) onOpenChange(false) } - // 获取所有标签选项 - const allTags = Array.from(new Set(devices.flatMap((device) => device.tags || []))) + const handleCancel = () => { + setSelectedDeviceIds(selectedDevices) + onOpenChange(false) + } + + const filteredDevices = devices.filter((device) => { + const searchLower = searchQuery.toLowerCase() + const matchesSearch = + (device.name || '').toLowerCase().includes(searchLower) || + (device.imei || '').toLowerCase().includes(searchLower) || + (device.wechatAccounts[0]?.wechatId || '').toLowerCase().includes(searchLower) + const matchesStatus = + statusFilter === "all" || + (statusFilter === "online" && device.status === "online") || + (statusFilter === "offline" && device.status === "offline") + return matchesSearch && matchesStatus + }) return ( - + - 选择设备 + 选择设备 - -
- {/* 搜索和筛选区域 */} -
-
-
- - setSearchQuery(e.target.value)} - className="pl-9" - /> -
- - -
- - {/* 分类标签页 */} - - - 全部 - 在线 - 离线 - 未使用 - - - - {/* 筛选器 */} -
- - - {allTags.length > 0 && ( - - )} - - -
+
+ {/* 搜索和筛选 */} +
+ setSearchQuery(e.target.value)} + className="flex-1 rounded-lg border-gray-200 focus:border-blue-500 focus:ring-2 focus:ring-blue-100" + /> +
- {/* 设备列表 */} - +
{loading ? ( -
-
-
+
加载中...
) : filteredDevices.length === 0 ? ( -
- {searchQuery || statusFilter !== "all" || tagFilter !== "all" || activeTab !== "all" - ? "没有符合条件的设备" - : "暂无设备数据"} -
+
暂无设备
) : ( -
- {filteredDevices.map((device) => ( - { + const checked = selectedDeviceIds.includes(device.id) + const wx = device.wechatAccounts[0] || {} + return ( + + ) + }) )} - -
- - -
- 已选择 {selectedDeviceIds.length} 个设备
-
- -
-
+
) diff --git a/Cunkebao/app/scenarios/new/steps/BasicSettings.tsx b/Cunkebao/app/scenarios/new/steps/BasicSettings.tsx index dd900af8..8c191553 100644 --- a/Cunkebao/app/scenarios/new/steps/BasicSettings.tsx +++ b/Cunkebao/app/scenarios/new/steps/BasicSettings.tsx @@ -113,8 +113,8 @@ const tagColorPoolDark = [ "bg-purple-500 text-white", "bg-red-500 text-white", "bg-orange-500 text-white", - "bg-yellow-400 text-white", - "bg-gray-700 text-white", + "bg-yellow-500 text-white", + "bg-gray-500 text-white", "bg-pink-500 text-white", ]; function getTagColorIdx(tag: string) { diff --git a/Cunkebao/app/scenarios/new/steps/FriendRequestSettings.tsx b/Cunkebao/app/scenarios/new/steps/FriendRequestSettings.tsx index 4525a2b5..10fb75f3 100644 --- a/Cunkebao/app/scenarios/new/steps/FriendRequestSettings.tsx +++ b/Cunkebao/app/scenarios/new/steps/FriendRequestSettings.tsx @@ -12,6 +12,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/u import { Alert, AlertDescription } from "@/components/ui/alert" import { ChevronsUpDown } from "lucide-react" import { Checkbox } from "@/components/ui/checkbox" +import { DeviceSelectionDialog } from "../../../components/device-selection-dialog" interface FriendRequestSettingsProps { formData: any @@ -49,7 +50,9 @@ export function FriendRequestSettings({ formData, onChange, onNext, onPrev }: Fr const [isTemplateDialogOpen, setIsTemplateDialogOpen] = useState(false) const [hasWarnings, setHasWarnings] = useState(false) const [isDeviceSelectorOpen, setIsDeviceSelectorOpen] = useState(false) - const [selectedDevices, setSelectedDevices] = useState(formData.selectedDevices || []) + const [selectedDeviceIds, setSelectedDeviceIds] = useState( + (formData.selectedDevices || []).map((d: any) => d.id || d) + ) // 获取场景标题 const getScenarioTitle = () => { @@ -96,18 +99,11 @@ export function FriendRequestSettings({ formData, onChange, onNext, onPrev }: Fr onNext() } - const toggleDeviceSelection = (device: any) => { - const isSelected = selectedDevices.some((d) => d.id === device.id) - let newSelectedDevices - - if (isSelected) { - newSelectedDevices = selectedDevices.filter((d) => d.id !== device.id) - } else { - newSelectedDevices = [...selectedDevices, device] - } - - setSelectedDevices(newSelectedDevices) - onChange({ ...formData, selectedDevices: newSelectedDevices }) + // 设备选择回填 + const handleDeviceSelect = (deviceIds: string[]) => { + setSelectedDeviceIds(deviceIds) + // 只存id,或如需完整对象可自行扩展 + onChange({ ...formData, selectedDevices: deviceIds }) } return ( @@ -119,40 +115,18 @@ export function FriendRequestSettings({ formData, onChange, onNext, onPrev }: Fr - - {isDeviceSelectorOpen && ( -
-
- -
- {mockDevices.map((device) => ( -
toggleDeviceSelection(device)} - > -
- d.id === device.id)} - onCheckedChange={() => toggleDeviceSelection(device)} - /> - {device.name} -
- - {device.status === "online" ? "在线" : "离线"} - -
- ))} -
-
-
- )} +
@@ -239,7 +213,7 @@ export function FriendRequestSettings({ formData, onChange, onNext, onPrev }: Fr
{hasWarnings && ( - + 您有未完成的设置项,建议完善后再进入下一步。 diff --git a/Cunkebao/app/workspace/traffic-distribution/new/components/traffic-pool-step.tsx b/Cunkebao/app/workspace/traffic-distribution/new/components/traffic-pool-step.tsx index 2d25c7d0..bfaaaaf2 100644 --- a/Cunkebao/app/workspace/traffic-distribution/new/components/traffic-pool-step.tsx +++ b/Cunkebao/app/workspace/traffic-distribution/new/components/traffic-pool-step.tsx @@ -206,7 +206,7 @@ export default function TrafficPoolStep({ onSubmit, onBack, initialData = {}, de -