diff --git a/nkebao/src/components/DeviceSelection.tsx b/nkebao/src/components/DeviceSelection.tsx new file mode 100644 index 00000000..7f341d76 --- /dev/null +++ b/nkebao/src/components/DeviceSelection.tsx @@ -0,0 +1,194 @@ +import React, { useState, useEffect } from 'react'; +import { Search } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { ScrollArea } from '@/components/ui/scroll-area'; +import { Checkbox } from '@/components/ui/checkbox'; +import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; +import { fetchDeviceList } from '@/api/devices'; + +// 设备选择项接口 +interface DeviceSelectionItem { + id: string; + name: string; + imei: string; + wechatId: string; + status: 'online' | 'offline'; +} + +// 组件属性接口 +interface DeviceSelectionProps { + selectedDevices: string[]; + onSelect: (devices: string[]) => void; + placeholder?: string; + className?: string; +} + +export default function DeviceSelection({ + selectedDevices, + onSelect, + placeholder = "选择设备", + className = "" +}: DeviceSelectionProps) { + const [dialogOpen, setDialogOpen] = useState(false); + const [devices, setDevices] = useState([]); + const [searchQuery, setSearchQuery] = useState(''); + const [statusFilter, setStatusFilter] = useState('all'); + const [loading, setLoading] = useState(false); + + // 当弹窗打开时获取设备列表 + useEffect(() => { + if (dialogOpen) { + fetchDevices(); + } + }, [dialogOpen]); + + // 获取设备列表 + const fetchDevices = async () => { + setLoading(true); + try { + const res = await fetchDeviceList(1, 100); + if (res && res.data && Array.isArray(res.data.list)) { + setDevices(res.data.list.map(d => ({ + id: d.id?.toString() || '', + name: d.memo || d.imei || '', + imei: d.imei || '', + wechatId: d.wechatId || '', + status: d.alive === 1 ? 'online' : 'offline', + }))); + } + } catch (error) { + console.error('获取设备列表失败:', error); + } finally { + setLoading(false); + } + }; + + // 过滤设备 + const filteredDevices = devices.filter((device) => { + const matchesSearch = + device.name.toLowerCase().includes(searchQuery.toLowerCase()) || + device.imei.toLowerCase().includes(searchQuery.toLowerCase()) || + device.wechatId.toLowerCase().includes(searchQuery.toLowerCase()); + + const matchesStatus = + statusFilter === 'all' || + (statusFilter === 'online' && device.status === 'online') || + (statusFilter === 'offline' && device.status === 'offline'); + + return matchesSearch && matchesStatus; + }); + + // 处理设备选择 + const handleDeviceToggle = (deviceId: string) => { + if (selectedDevices.includes(deviceId)) { + onSelect(selectedDevices.filter(id => id !== deviceId)); + } else { + onSelect([...selectedDevices, deviceId]); + } + }; + + // 获取显示文本 + const getDisplayText = () => { + if (selectedDevices.length === 0) return ''; + return `已选择 ${selectedDevices.length} 个设备`; + }; + + return ( + <> + {/* 输入框 */} +
+ + setDialogOpen(true)} + value={getDisplayText()} + /> +
+ + + {/* 设备选择弹窗 */} + + + + 选择设备 + + +
+
+ + setSearchQuery(e.target.value)} + className="pl-9" + /> +
+ +
+ + + {loading ? ( +
+
加载中...
+
+ ) : ( +
+ {filteredDevices.map((device) => ( + + ))} +
+ )} +
+ +
+
+ 已选择 {selectedDevices.length} 个设备 +
+
+ + +
+
+
+
+ + ); +} \ No newline at end of file diff --git a/nkebao/src/components/FriendSelection.tsx b/nkebao/src/components/FriendSelection.tsx new file mode 100644 index 00000000..f18ec377 --- /dev/null +++ b/nkebao/src/components/FriendSelection.tsx @@ -0,0 +1,275 @@ +import React, { useState, useEffect } from 'react'; +import { Search, X } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { ScrollArea } from '@/components/ui/scroll-area'; +import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; +import { get } from '@/api/request'; + +// 微信好友接口类型 +interface WechatFriend { + id: string; + nickname: string; + wechatId: string; + avatar: string; + customer: string; +} + +// 好友列表API响应类型 +interface FriendsResponse { + code: number; + msg: string; + data: { + list: Array<{ + id: number; + nickname: string; + wechatId: string; + avatar?: string; + customer?: string; + }>; + total: number; + page: number; + limit: number; + }; +} + +// 获取好友列表API函数 +const fetchFriendsList = async (page: number = 1, limit: number = 20, deviceIds: string[]): Promise => { + if (deviceIds.length === 0) { + return { + code: 200, + msg: 'success', + data: { + list: [], + total: 0, + page, + limit + } + }; + } + + const deviceIdsParam = deviceIds.join(','); + return get(`/v1/friend?page=${page}&limit=${limit}&deviceIds=${deviceIdsParam}`); +}; + +// 组件属性接口 +interface FriendSelectionProps { + selectedFriends: string[]; + onSelect: (friends: string[]) => void; + deviceIds: string[]; + placeholder?: string; + className?: string; +} + +export default function FriendSelection({ + selectedFriends, + onSelect, + deviceIds, + placeholder = "选择微信好友", + className = "" +}: FriendSelectionProps) { + const [dialogOpen, setDialogOpen] = useState(false); + const [friends, setFriends] = useState([]); + const [searchQuery, setSearchQuery] = useState(''); + const [currentPage, setCurrentPage] = useState(1); + const [totalPages, setTotalPages] = useState(1); + const [totalFriends, setTotalFriends] = useState(0); + const [loading, setLoading] = useState(false); + + // 当弹窗打开时获取好友列表 + useEffect(() => { + if (dialogOpen && deviceIds.length > 0) { + fetchFriends(currentPage); + } + }, [dialogOpen, currentPage, deviceIds]); + + // 当设备ID变化时,重置页码 + useEffect(() => { + if (deviceIds.length > 0) { + setCurrentPage(1); + } + }, [deviceIds]); + + // 获取好友列表API + const fetchFriends = async (page: number) => { + if (deviceIds.length === 0) return; + + setLoading(true); + try { + const res = await fetchFriendsList(page, 20, deviceIds); + + if (res && res.code === 200 && res.data) { + setFriends(res.data.list.map((friend) => ({ + id: friend.id?.toString() || '', + nickname: friend.nickname || '', + wechatId: friend.wechatId || '', + avatar: friend.avatar || '', + customer: friend.customer || '', + }))); + + setTotalFriends(res.data.total || 0); + setTotalPages(Math.ceil((res.data.total || 0) / 20)); + } + } catch (error) { + console.error('获取好友列表失败:', error); + } finally { + setLoading(false); + } + }; + + // 过滤好友 + const filteredFriends = friends.filter(friend => + friend.nickname.toLowerCase().includes(searchQuery.toLowerCase()) || + friend.wechatId.toLowerCase().includes(searchQuery.toLowerCase()) + ); + + // 处理好友选择 + const handleFriendToggle = (friendId: string) => { + if (selectedFriends.includes(friendId)) { + onSelect(selectedFriends.filter(id => id !== friendId)); + } else { + onSelect([...selectedFriends, friendId]); + } + }; + + // 获取显示文本 + const getDisplayText = () => { + if (selectedFriends.length === 0) return ''; + return `已选择 ${selectedFriends.length} 个好友`; + }; + + const handleConfirm = () => { + setDialogOpen(false); + }; + + return ( + <> + {/* 输入框 */} +
+ + + + + + setDialogOpen(true)} + value={getDisplayText()} + /> +
+ + {/* 微信好友选择弹窗 */} + + +
+ 选择微信好友 + +
+ setSearchQuery(e.target.value)} + className="pl-10 py-2 rounded-full border-gray-200" + /> + + +
+
+ + + {loading ? ( +
+
加载中...
+
+ ) : filteredFriends.length > 0 ? ( +
+ {filteredFriends.map((friend) => ( + + ))} +
+ ) : ( +
+
+ {deviceIds.length === 0 ? '请先选择设备' : '没有找到好友'} +
+
+ )} +
+ +
+
+ 总计 {totalFriends} 个好友 +
+
+ + {currentPage} / {totalPages} + +
+
+ +
+ + +
+
+
+ + ); +} \ No newline at end of file diff --git a/nkebao/src/pages/workspace/auto-like/NewAutoLike.tsx b/nkebao/src/pages/workspace/auto-like/NewAutoLike.tsx index 59f0be50..7c52c583 100644 --- a/nkebao/src/pages/workspace/auto-like/NewAutoLike.tsx +++ b/nkebao/src/pages/workspace/auto-like/NewAutoLike.tsx @@ -1,82 +1,22 @@ import React, { useState, useEffect } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; -import { ChevronLeft, Search, Plus, Minus, Check, X, Tag as TagIcon } from 'lucide-react'; +import { ChevronLeft,Plus, Minus, Check, X, Tag as TagIcon } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Switch } from '@/components/ui/switch'; -import { Badge } from '@/components/ui/badge'; -import { Card, CardContent } from '@/components/ui/card'; -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; -import { ScrollArea } from '@/components/ui/scroll-area'; -import { Checkbox } from '@/components/ui/checkbox'; -import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; -import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; - import { createAutoLikeTask, updateAutoLikeTask, fetchAutoLikeTaskDetail } from '@/api/autoLike'; import { ContentType } from '@/types/auto-like'; import { useToast } from '@/components/ui/toast'; import Layout from '@/components/Layout'; -import { fetchDeviceList } from '@/api/devices'; - -import { get } from '@/api/request'; +import DeviceSelection from '@/components/DeviceSelection'; +import FriendSelection from '@/components/FriendSelection'; -// 用于设备选择弹窗的简化设备类型 -interface DeviceSelectionItem { - id: string; - name: string; - imei: string; - wechatId: string; - status: 'online' | 'offline'; -} -// 微信好友接口类型 -interface WechatFriend { - id: string; - nickname: string; - wechatId: string; - avatar: string; - customer: string; -} -// 好友列表API响应类型 -interface FriendsResponse { - code: number; - msg: string; - data: { - list: Array<{ - id: number; - nickname: string; - wechatId: string; - avatar?: string; - customer?: string; - }>; - total: number; - page: number; - limit: number; - }; -} -// 获取好友列表API函数 -const fetchFriendsList = async (page: number = 1, limit: number = 20, deviceIds: string[]): Promise => { - if (deviceIds.length === 0) { - return { - code: 200, - msg: 'success', - data: { - list: [], - total: 0, - page, - limit - } - }; - } - - const deviceIdsParam = deviceIds.join(','); - return get(`/v1/friend?page=${page}&limit=${limit}&deviceIds=${deviceIdsParam}`); -}; // 修改CreateLikeTaskData接口,确保friends字段不是可选的 interface CreateLikeTaskDataLocal { @@ -100,8 +40,6 @@ export default function NewAutoLike() { const isEditMode = !!id; const { toast } = useToast(); const [currentStep, setCurrentStep] = useState(1); - const [deviceDialogOpen, setDeviceDialogOpen] = useState(false); - const [friendDialogOpen, setFriendDialogOpen] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); const [isLoading, setIsLoading] = useState(isEditMode); const [formData, setFormData] = useState({ @@ -120,22 +58,6 @@ export default function NewAutoLike() { }); // 新增自动开启的独立状态 const [autoEnabled, setAutoEnabled] = useState(false); - const [devices, setDevices] = useState([]); - - // 获取设备列表 - useEffect(() => { - fetchDeviceList(1, 100).then(res => { - if (res && res.data && Array.isArray(res.data.list)) { - setDevices(res.data.list.map(d => ({ - id: d.id?.toString() || '', - name: d.memo || d.imei || '', - imei: d.imei || '', - wechatId: d.wechatId || '', - status: d.alive === 1 ? 'online' : 'offline', - }))); - } - }); - }, []); // 如果是编辑模式,获取任务详情 useEffect(() => { @@ -308,42 +230,11 @@ export default function NewAutoLike() { {currentStep === 2 && (
-
- - setDeviceDialogOpen(true)} - value={formData.devices.length > 0 ? `已选择 ${formData.devices.length} 个设备` : ''} - /> -
- - {formData.devices.length > 0 && ( -
-

已选择的设备

-
- {formData.devices.map(deviceId => { - const device = devices.find(d => d.id === deviceId); - if (!device) return null; - return ( -
-
-
{device.name}
-
- {device.status === 'online' ? '在线' : '离线'} -
-
-
-
IMEI: {device.imei}
-
微信号: {device.wechatId}
-
-
- ); - })} -
-
- )} + handleUpdateFormData({ devices })} + placeholder="选择设备" + />
- - handleUpdateFormData({ devices })} - devices={devices} - />
)} {currentStep === 3 && ( -
-
-
- - - - setFriendDialogOpen(true)} - value={formData.friends && formData.friends.length > 0 ? `已选择 ${formData.friends.length} 个好友` : ''} - /> -
-
+
+ handleUpdateFormData({ friends })} + deviceIds={formData.devices} + placeholder="选择微信好友" + /> +
- handleUpdateFormData({ friends })} - deviceIds={formData.devices} - /> -
+
)} @@ -676,304 +545,8 @@ function BasicSettings({ formData, onChange, onNext, autoEnabled, setAutoEnabled ); } -// 设备选择对话框组件 -interface DeviceSelectionDialogProps { - open: boolean; - onOpenChange: (open: boolean) => void; - selectedDevices: string[]; - onSelect: (devices: string[]) => void; - devices: DeviceSelectionItem[]; -} -function DeviceSelectionDialog({ open, onOpenChange, selectedDevices, onSelect, devices }: DeviceSelectionDialogProps) { - const [searchQuery, setSearchQuery] = useState(''); - const [statusFilter, setStatusFilter] = useState('all'); - - const filteredDevices = devices.filter((device) => { - const matchesSearch = - device.name.toLowerCase().includes(searchQuery.toLowerCase()) || - device.imei.toLowerCase().includes(searchQuery.toLowerCase()) || - device.wechatId.toLowerCase().includes(searchQuery.toLowerCase()); - - const matchesStatus = - statusFilter === 'all' || - (statusFilter === 'online' && device.status === 'online') || - (statusFilter === 'offline' && device.status === 'offline'); - - return matchesSearch && matchesStatus; - }); - - // 处理设备选择 - const handleDeviceToggle = (deviceId: string) => { - if (selectedDevices.includes(deviceId)) { - onSelect(selectedDevices.filter(id => id !== deviceId)); - } else { - onSelect([...selectedDevices, deviceId]); - } - }; - - return ( - - - - 选择设备 - - -
-
- - setSearchQuery(e.target.value)} - className="pl-9" - /> -
- -
- - -
- {filteredDevices.map((device) => ( - - ))} -
-
-
-
- 已选择 {selectedDevices.length} 个设备 -
-
- - -
-
-
-
- ); -} -// 微信好友选择弹窗组件 -interface FriendSelectionDialogProps { - open: boolean; - onOpenChange: (open: boolean) => void; - selectedFriends: string[]; - onSelect: (friends: string[]) => void; - deviceIds: string[]; -} - -function FriendSelectionDialog({ open, onOpenChange, selectedFriends = [], onSelect, deviceIds }: FriendSelectionDialogProps) { - const [searchQuery, setSearchQuery] = useState(''); - const [currentPage, setCurrentPage] = useState(1); - const [totalPages, setTotalPages] = useState(1); - const [totalFriends, setTotalFriends] = useState(0); - const [friends, setFriends] = useState([]); - const [loading, setLoading] = useState(false); - - // 获取好友列表 - useEffect(() => { - if (open && deviceIds.length > 0) { - fetchFriends(currentPage); - } - }, [open, currentPage, deviceIds]); - - // 当设备ID变化时,重置页码 - useEffect(() => { - if (deviceIds.length > 0) { - setCurrentPage(1); - } - }, [deviceIds]); - - // 获取好友列表API - const fetchFriends = async (page: number) => { - if (deviceIds.length === 0) return; - - setLoading(true); - try { - const res = await fetchFriendsList(page, 20, deviceIds); - - if (res && res.code === 200 && res.data) { - setFriends(res.data.list.map((friend) => ({ - id: friend.id?.toString() || '', - nickname: friend.nickname || '', - wechatId: friend.wechatId || '', - avatar: friend.avatar || '', - customer: friend.customer || '', - }))); - - setTotalFriends(res.data.total || 0); - setTotalPages(Math.ceil((res.data.total || 0) / 20)); - } - } catch (error) { - console.error('获取好友列表失败:', error); - } finally { - setLoading(false); - } - }; - - const filteredFriends = friends.filter(friend => - friend.nickname.toLowerCase().includes(searchQuery.toLowerCase()) || - friend.wechatId.toLowerCase().includes(searchQuery.toLowerCase()) - ); - - const handleFriendToggle = (friendId: string) => { - if (selectedFriends.includes(friendId)) { - onSelect(selectedFriends.filter(id => id !== friendId)); - } else { - onSelect([...selectedFriends, friendId]); - } - }; - - - - const handleConfirm = () => { - onOpenChange(false); - }; - - return ( - - -
- 选择微信好友 - -
- setSearchQuery(e.target.value)} - className="pl-10 py-2 rounded-full border-gray-200" - /> - - -
-
- - - {loading ? ( -
-
加载中...
-
- ) : filteredFriends.length > 0 ? ( -
- {filteredFriends.map((friend) => ( - - ))} -
- ) : ( -
-
- {deviceIds.length === 0 ? '请先选择设备' : '没有找到好友'} -
-
- )} -
- -
-
- 总计 {totalFriends} 个好友 -
-
- - {currentPage} / {totalPages} - -
-
- -
- - -
-
-
- ); -} \ No newline at end of file + \ No newline at end of file diff --git a/nkebao/src/pages/workspace/traffic-distribution/NewDistribution.tsx b/nkebao/src/pages/workspace/traffic-distribution/NewDistribution.tsx index d6f5ddb0..51993e6c 100644 --- a/nkebao/src/pages/workspace/traffic-distribution/NewDistribution.tsx +++ b/nkebao/src/pages/workspace/traffic-distribution/NewDistribution.tsx @@ -1,10 +1,10 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; +import { Steps, StepItem } from 'tdesign-mobile-react'; import { Users, Search, Database, - Plus, X, } from 'lucide-react'; import { Button } from '@/components/ui/button'; @@ -16,39 +16,32 @@ import { Checkbox } from '@/components/ui/checkbox'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import Layout from '@/components/Layout'; import PageHeader from '@/components/PageHeader'; -import BottomNav from '@/components/BottomNav'; import { useToast } from '@/components/ui/toast'; import { fetchAccountList, Account } from '@/api/trafficDistribution'; import '@/components/Layout.css'; -// 步骤指示器组件 -function StepIndicator({ currentStep, steps }: { currentStep: number; steps: Step[] }) { - return ( -
- {steps.map((step, index) => ( -
-
- {React.cloneElement(step.icon as React.ReactElement, { className: "w-5 h-5" })} -
- {index < steps.length - 1 && ( -
- )} -
- ))} -
- ); -} - -interface Step { - id: number; - title: string; - icon: React.ReactNode; -} +// 移除原来的步骤指示器组件 +// function StepIndicator({ currentStep, steps }: { currentStep: number; steps: Step[] }) { +// return ( +//
+// {steps.map((step, index) => ( +//
+//
+// {React.cloneElement(step.icon as React.ReactElement, { className: "w-5 h-5" })} +//
+// {index < steps.length - 1 && ( +//
+// )} +//
+// ))} +//
+// ); +// } interface BasicInfoData { name: string; @@ -114,7 +107,7 @@ const AccountSelectionDialog = ({ const { toast } = useToast(); // 获取账号列表 - const fetchAccounts = async (pageNum: number = 1, reset: boolean = true) => { + const fetchAccounts = useCallback(async (pageNum: number = 1, reset: boolean = true) => { setLoading(true); try { const response = await fetchAccountList({ @@ -180,7 +173,7 @@ const AccountSelectionDialog = ({ } finally { setLoading(false); } - }; + }, [accounts.length, toast]); useEffect(() => { if (open) { @@ -188,7 +181,7 @@ const AccountSelectionDialog = ({ fetchAccounts(1, true); setPage(1); } - }, [open, selectedAccounts]); + }, [open, selectedAccounts, fetchAccounts]); const toggleAccount = (id: string) => { setTempSelectedAccounts(prev => @@ -314,10 +307,10 @@ export default function NewDistribution() { trafficPool: {}, }); - const steps: Step[] = [ - { id: 1, title: "基本信息", icon: }, - { id: 2, title: "目标设置", icon: }, - { id: 3, title: "流量池选择", icon: }, + const steps = [ + { title: "基本信息", content: "step1" }, + { title: "目标设置", content: "step2" }, + { title: "流量池选择", content: "step3" }, ]; // 生成默认计划名称 @@ -873,7 +866,13 @@ export default function NewDistribution() { >
- +
+ + {steps.map((step, index) => ( + + ))} + +
{currentStep === 0 && (