【操盘手端】自动点赞提交
This commit is contained in:
@@ -1,36 +1,13 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { useState } from "react"
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from "@/components/ui/dropdown-menu"
|
||||
import { CheckCircle2, ChevronDown, ChevronUp, Smartphone } from "lucide-react"
|
||||
import { ScrollArea } from "@/components/ui/scroll-area"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||
import { Search, RefreshCw, Smartphone, Database, Users } from "lucide-react"
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
|
||||
|
||||
interface Device {
|
||||
id: string
|
||||
name: string
|
||||
status: "online" | "offline"
|
||||
wechatId: string
|
||||
}
|
||||
|
||||
interface DatabaseItem {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
count: number
|
||||
}
|
||||
|
||||
interface AudienceGroup {
|
||||
id: string
|
||||
name: string
|
||||
count: number
|
||||
description: string
|
||||
}
|
||||
|
||||
export interface DeviceSelectionData {
|
||||
selectedDevices: string[]
|
||||
@@ -39,256 +16,213 @@ export interface DeviceSelectionData {
|
||||
}
|
||||
|
||||
interface DeviceSelectionProps {
|
||||
initialData?: Partial<DeviceSelectionData>
|
||||
initialData: DeviceSelectionData
|
||||
onSave: (data: DeviceSelectionData) => void
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
// 模拟设备数据
|
||||
const mockDevices = [
|
||||
{ id: "1", name: "iPhone 13", status: "online", lastActive: "刚刚" },
|
||||
{ id: "2", name: "华为 P40", status: "offline", lastActive: "3小时前" },
|
||||
{ id: "3", name: "小米 11", status: "online", lastActive: "1小时前" },
|
||||
{ id: "4", name: "OPPO Find X3", status: "offline", lastActive: "昨天" },
|
||||
{ id: "5", name: "vivo X60", status: "online", lastActive: "刚刚" },
|
||||
]
|
||||
|
||||
// 模拟数据库选项
|
||||
const databaseOptions = [
|
||||
{ id: "all", name: "全部客户" },
|
||||
{ id: "new", name: "新客户" },
|
||||
{ id: "vip", name: "VIP客户" },
|
||||
]
|
||||
|
||||
// 模拟用户群体选项
|
||||
const audienceOptions = [
|
||||
{ id: "all", name: "全部好友" },
|
||||
{ id: "active", name: "活跃好友" },
|
||||
{ id: "inactive", name: "不活跃好友" },
|
||||
{ id: "recent", name: "最近添加" },
|
||||
]
|
||||
|
||||
export function DeviceSelection({ initialData, onSave, onBack }: DeviceSelectionProps) {
|
||||
const [devices, setDevices] = useState<Device[]>([])
|
||||
const [databases, setDatabases] = useState<DatabaseItem[]>([])
|
||||
const [audienceGroups, setAudienceGroups] = useState<AudienceGroup[]>([])
|
||||
const [selectedDevices, setSelectedDevices] = useState<string[]>(initialData?.selectedDevices || [])
|
||||
const [selectedDatabase, setSelectedDatabase] = useState<string>(initialData?.selectedDatabase || "")
|
||||
const [selectedAudience, setSelectedAudience] = useState<string>(initialData?.selectedAudience || "")
|
||||
const [searchQuery, setSearchQuery] = useState("")
|
||||
const [activeTab, setActiveTab] = useState("all")
|
||||
const [formData, setFormData] = useState<DeviceSelectionData>(initialData)
|
||||
const [devices, setDevices] = useState(mockDevices)
|
||||
const [showAllDevices, setShowAllDevices] = useState(false)
|
||||
|
||||
// 模拟获取设备数据
|
||||
useEffect(() => {
|
||||
// 模拟设备数据
|
||||
const mockDevices: Device[] = Array.from({ length: 10 }, (_, i) => ({
|
||||
id: `device-${i + 1}`,
|
||||
name: `设备 ${i + 1}`,
|
||||
status: Math.random() > 0.3 ? "online" : "offline",
|
||||
wechatId: `wxid_${Math.random().toString(36).substr(2, 8)}`,
|
||||
}))
|
||||
setDevices(mockDevices)
|
||||
const toggleDevice = (deviceId: string) => {
|
||||
const newSelectedDevices = formData.selectedDevices.includes(deviceId)
|
||||
? formData.selectedDevices.filter((id) => id !== deviceId)
|
||||
: [...formData.selectedDevices, deviceId]
|
||||
|
||||
// 模拟数据库数据
|
||||
const mockDatabases: DatabaseItem[] = [
|
||||
{
|
||||
id: "db-1",
|
||||
name: "默认数据库",
|
||||
description: "系统默认的数据库",
|
||||
count: 1250,
|
||||
},
|
||||
{
|
||||
id: "db-2",
|
||||
name: "高净值客户",
|
||||
description: "高消费能力的客户群体",
|
||||
count: 450,
|
||||
},
|
||||
{
|
||||
id: "db-3",
|
||||
name: "潜在客户",
|
||||
description: "有购买意向的潜在客户",
|
||||
count: 780,
|
||||
},
|
||||
]
|
||||
setDatabases(mockDatabases)
|
||||
|
||||
// 模拟目标人群数据
|
||||
const mockAudienceGroups: AudienceGroup[] = [
|
||||
{
|
||||
id: "audience-1",
|
||||
name: "全部好友",
|
||||
count: 1250,
|
||||
description: "所有微信好友",
|
||||
},
|
||||
{
|
||||
id: "audience-2",
|
||||
name: "高频互动好友",
|
||||
count: 320,
|
||||
description: "经常互动的好友",
|
||||
},
|
||||
{
|
||||
id: "audience-3",
|
||||
name: "潜在客户",
|
||||
count: 450,
|
||||
description: "有购买意向的好友",
|
||||
},
|
||||
{
|
||||
id: "audience-4",
|
||||
name: "VIP客户",
|
||||
description: "已成交的VIP客户",
|
||||
},
|
||||
]
|
||||
setAudienceGroups(mockAudienceGroups)
|
||||
|
||||
// 设置默认选中的数据库和目标人群
|
||||
if (!initialData?.selectedDatabase) {
|
||||
setSelectedDatabase("db-1")
|
||||
}
|
||||
if (!initialData?.selectedAudience) {
|
||||
setSelectedAudience("audience-1")
|
||||
}
|
||||
}, [initialData?.selectedDatabase, initialData?.selectedAudience])
|
||||
|
||||
// 过滤设备
|
||||
const filteredDevices = devices.filter((device) => {
|
||||
const matchesSearch =
|
||||
device.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
device.wechatId.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
|
||||
const matchesTab =
|
||||
activeTab === "all" ||
|
||||
(activeTab === "selected" && selectedDevices.includes(device.id)) ||
|
||||
(activeTab === "online" && device.status === "online") ||
|
||||
(activeTab === "offline" && device.status === "offline")
|
||||
|
||||
return matchesSearch && matchesTab
|
||||
})
|
||||
|
||||
// 选择/取消选择单个设备
|
||||
const handleDeviceSelect = (deviceId: string) => {
|
||||
setSelectedDevices(
|
||||
selectedDevices.includes(deviceId)
|
||||
? selectedDevices.filter((id) => id !== deviceId)
|
||||
: [...selectedDevices, deviceId],
|
||||
)
|
||||
setFormData({ ...formData, selectedDevices: newSelectedDevices })
|
||||
}
|
||||
|
||||
// 保存选择的设备
|
||||
const handleSave = () => {
|
||||
onSave({
|
||||
selectedDevices,
|
||||
selectedDatabase,
|
||||
selectedAudience,
|
||||
})
|
||||
const selectAllDevices = () => {
|
||||
const allDeviceIds = devices.map((device) => device.id)
|
||||
setFormData({ ...formData, selectedDevices: allDeviceIds })
|
||||
}
|
||||
|
||||
const clearDeviceSelection = () => {
|
||||
setFormData({ ...formData, selectedDevices: [] })
|
||||
}
|
||||
|
||||
// 用于显示的设备
|
||||
const displayedDevices = showAllDevices ? devices : devices.slice(0, 3)
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>选择执行点赞任务的设备</CardTitle>
|
||||
<CardDescription>选择要执行自动点赞任务的设备、数据库和目标人群</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
{/* 设备筛选和搜索 */}
|
||||
<div className="space-y-4">
|
||||
<Label className="font-medium">选择设备</Label>
|
||||
<div className="flex flex-col sm:flex-row gap-4">
|
||||
<div className="relative flex-1">
|
||||
<Search className="absolute left-3 top-2.5 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
placeholder="搜索设备名称/微信号"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="pl-9"
|
||||
/>
|
||||
<Card className="mb-6">
|
||||
<CardContent className="pt-6">
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<Label className="text-base font-medium">选择点赞设备</Label>
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" size="sm" onClick={selectAllDevices}>
|
||||
全选
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" onClick={clearDeviceSelection}>
|
||||
清空
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Button variant="outline" size="icon">
|
||||
<RefreshCw className="h-4 w-4" />
|
||||
|
||||
<div className="space-y-2">
|
||||
{displayedDevices.map((device) => (
|
||||
<div
|
||||
key={device.id}
|
||||
className={`flex items-center p-3 rounded-md border cursor-pointer transition-colors ${
|
||||
formData.selectedDevices.includes(device.id)
|
||||
? "border-primary bg-primary/5"
|
||||
: "border-border hover:border-primary/50"
|
||||
}`}
|
||||
onClick={() => toggleDevice(device.id)}
|
||||
>
|
||||
<div className="flex items-center space-x-3 flex-1">
|
||||
<div
|
||||
className={`w-8 h-8 rounded-full flex items-center justify-center ${
|
||||
device.status === "online" ? "bg-green-100" : "bg-gray-100"
|
||||
}`}
|
||||
>
|
||||
<Smartphone
|
||||
className={`h-4 w-4 ${device.status === "online" ? "text-green-600" : "text-gray-400"}`}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="font-medium">{device.name}</p>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Badge variant={device.status === "online" ? "success" : "secondary"} className="text-xs">
|
||||
{device.status === "online" ? "在线" : "离线"}
|
||||
</Badge>
|
||||
<span className="text-xs text-muted-foreground">{device.lastActive}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{formData.selectedDevices.includes(device.id) && <CheckCircle2 className="h-5 w-5 text-primary" />}
|
||||
</div>
|
||||
))}
|
||||
|
||||
{devices.length > 3 && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="w-full text-muted-foreground"
|
||||
onClick={() => setShowAllDevices(!showAllDevices)}
|
||||
>
|
||||
{showAllDevices ? (
|
||||
<>
|
||||
<ChevronUp className="h-4 w-4 mr-2" />
|
||||
收起
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<ChevronDown className="h-4 w-4 mr-2" />
|
||||
展开更多({devices.length - 3}台)
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Label className="text-base font-medium">选择目标客户</Label>
|
||||
<p className="text-sm text-muted-foreground mb-2">选择需要点赞的目标客户群体</p>
|
||||
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" className="w-full justify-between">
|
||||
{databaseOptions.find((option) => option.id === formData.selectedDatabase)?.name ||
|
||||
"选择客户数据库"}
|
||||
<ChevronDown className="h-4 w-4 ml-2" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-full">
|
||||
<ScrollArea className="h-[200px]">
|
||||
{databaseOptions.map((option) => (
|
||||
<DropdownMenuItem
|
||||
key={option.id}
|
||||
onClick={() => setFormData({ ...formData, selectedDatabase: option.id })}
|
||||
className="flex items-center justify-between cursor-pointer"
|
||||
>
|
||||
{option.name}
|
||||
{formData.selectedDatabase === option.id && <CheckCircle2 className="h-4 w-4 text-primary" />}
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</ScrollArea>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label className="text-base font-medium">选择好友范围</Label>
|
||||
<p className="text-sm text-muted-foreground mb-2">选择需要点赞的好友范围</p>
|
||||
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" className="w-full justify-between">
|
||||
{audienceOptions.find((option) => option.id === formData.selectedAudience)?.name ||
|
||||
"选择好友范围"}
|
||||
<ChevronDown className="h-4 w-4 ml-2" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-full">
|
||||
<ScrollArea className="h-[200px]">
|
||||
{audienceOptions.map((option) => (
|
||||
<DropdownMenuItem
|
||||
key={option.id}
|
||||
onClick={() => setFormData({ ...formData, selectedAudience: option.id })}
|
||||
className="flex items-center justify-between cursor-pointer"
|
||||
>
|
||||
{option.name}
|
||||
{formData.selectedAudience === option.id && <CheckCircle2 className="h-4 w-4 text-primary" />}
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</ScrollArea>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between space-x-4">
|
||||
<Button variant="outline" className="flex-1" onClick={onBack}>
|
||||
上一步
|
||||
</Button>
|
||||
<Button
|
||||
className="flex-1"
|
||||
onClick={() => onSave(formData)}
|
||||
disabled={
|
||||
formData.selectedDevices.length === 0 || !formData.selectedDatabase || !formData.selectedAudience
|
||||
}
|
||||
>
|
||||
下一步
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 设备分类标签页 */}
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
|
||||
<TabsList className="grid grid-cols-4">
|
||||
<TabsTrigger value="all">全部设备</TabsTrigger>
|
||||
<TabsTrigger value="selected">已选择 ({selectedDevices.length})</TabsTrigger>
|
||||
<TabsTrigger value="online">在线设备</TabsTrigger>
|
||||
<TabsTrigger value="offline">离线设备</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
|
||||
{/* 设备列表 */}
|
||||
<div className="space-y-3">
|
||||
{filteredDevices.map((device) => (
|
||||
<Card
|
||||
key={device.id}
|
||||
className={`p-4 hover:shadow-md transition-shadow ${
|
||||
selectedDevices.includes(device.id) ? "ring-2 ring-primary" : ""
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center space-x-3">
|
||||
<Checkbox
|
||||
checked={selectedDevices.includes(device.id)}
|
||||
onCheckedChange={() => handleDeviceSelect(device.id)}
|
||||
/>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<div className="font-medium truncate flex items-center">
|
||||
<Smartphone className="h-4 w-4 mr-1 text-muted-foreground" />
|
||||
{device.name}
|
||||
</div>
|
||||
<Badge variant={device.status === "online" ? "success" : "secondary"} className="text-xs">
|
||||
{device.status === "online" ? "在线" : "离线"}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">微信号: {device.wechatId}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
|
||||
{filteredDevices.length === 0 && (
|
||||
<div className="text-center py-8 text-muted-foreground">未找到符合条件的设备</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 数据库选择 */}
|
||||
<div className="space-y-4 pt-4 border-t">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Database className="h-5 w-5 text-muted-foreground" />
|
||||
<Label className="font-medium">选择数据库</Label>
|
||||
</div>
|
||||
<RadioGroup value={selectedDatabase} onValueChange={setSelectedDatabase} className="space-y-3">
|
||||
{databases.map((db) => (
|
||||
<div key={db.id} className="flex items-start space-x-3">
|
||||
<RadioGroupItem value={db.id} id={db.id} />
|
||||
<div className="flex-1">
|
||||
<Label htmlFor={db.id} className="font-medium">
|
||||
{db.name}
|
||||
<Badge variant="outline" className="ml-2">
|
||||
{db.count}条数据
|
||||
</Badge>
|
||||
</Label>
|
||||
<p className="text-sm text-muted-foreground">{db.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</RadioGroup>
|
||||
</div>
|
||||
|
||||
{/* 目标人群选择 */}
|
||||
<div className="space-y-4 pt-4 border-t">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Users className="h-5 w-5 text-muted-foreground" />
|
||||
<Label className="font-medium">选择目标人群</Label>
|
||||
</div>
|
||||
<RadioGroup value={selectedAudience} onValueChange={setSelectedAudience} className="space-y-3">
|
||||
{audienceGroups.map((group) => (
|
||||
<div key={group.id} className="flex items-start space-x-3">
|
||||
<RadioGroupItem value={group.id} id={group.id} />
|
||||
<div className="flex-1">
|
||||
<Label htmlFor={group.id} className="font-medium">
|
||||
{group.name}
|
||||
<Badge variant="outline" className="ml-2">
|
||||
{group.count}人
|
||||
</Badge>
|
||||
</Label>
|
||||
<p className="text-sm text-muted-foreground">{group.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</RadioGroup>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<div className="flex justify-between">
|
||||
<Button variant="outline" onClick={onBack}>
|
||||
返回上一步
|
||||
</Button>
|
||||
<Button onClick={handleSave} disabled={selectedDevices.length === 0}>
|
||||
完成设置
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user