diff --git a/Cunkebao/app/workspace/auto-group/loading.tsx b/Cunkebao/app/workspace/auto-group/loading.tsx new file mode 100644 index 00000000..f15322a8 --- /dev/null +++ b/Cunkebao/app/workspace/auto-group/loading.tsx @@ -0,0 +1,3 @@ +export default function Loading() { + return null +} diff --git a/Cunkebao/app/workspace/auto-group/page.tsx b/Cunkebao/app/workspace/auto-group/page.tsx index 9ea241ec..bba756c8 100644 --- a/Cunkebao/app/workspace/auto-group/page.tsx +++ b/Cunkebao/app/workspace/auto-group/page.tsx @@ -1,19 +1,16 @@ "use client" -import { useState, useCallback } from "react" +import { useState } from "react" import { Button } from "@/components/ui/button" -import { PlusCircle, ArrowLeft } from "lucide-react" -import { StepIndicator } from "./components/step-indicator" -import { GroupSettings } from "./components/group-settings" -import { DeviceSelection } from "./components/device-selection" -import { TagSelection } from "./components/tag-selection" -import { useToast } from "@/components/ui/use-toast" - -// 保留原有的卡片列表视图 -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" +import { ChevronLeft, Plus, Filter, Search, RefreshCw, MoreVertical, Clock, Edit, Trash2, Eye } from "lucide-react" +import { Card } from "@/components/ui/card" +import { Input } from "@/components/ui/input" +import Link from "next/link" import { Badge } from "@/components/ui/badge" -import { Users, Settings, RefreshCcw } from "lucide-react" +import { useRouter } from "next/navigation" +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu" +import { Switch } from "@/components/ui/switch" +import { Users, Settings } from "lucide-react" interface Plan { id: string @@ -49,97 +46,30 @@ const mockPlans: Plan[] = [ }, ] -const steps = [ - { title: "群配置", description: "设置群人数与组数" }, - { title: "设备选择", description: "选择执行设备" }, - { title: "人群标签", description: "选择目标人群" }, -] - export default function AutoGroupPage() { + const router = useRouter() const [isCreating, setIsCreating] = useState(false) - const [currentStep, setCurrentStep] = useState(0) - const { toast } = useToast() + const [plans, setPlans] = useState(mockPlans) - const [formData, setFormData] = useState({ - name: "新建群计划", - fixedWechatIds: [] as string[], - groupingOption: "all" as "all" | "fixed", - fixedGroupCount: 5, - selectedDevices: [] as string[], - audienceTags: [] as string[], - trafficTags: [] as string[], - matchLogic: "or" as "and" | "or", - excludeTags: ["已拉群"] as string[], - }) + const handleDelete = (planId: string) => { + setPlans(plans.filter((plan) => plan.id !== planId)) + } - const handleStepClick = useCallback((step: number) => { - setCurrentStep(step) - }, []) + const handleEdit = (planId: string) => { + router.push(`/workspace/auto-group/${planId}/edit`) + } - const handleNext = useCallback(() => { - setCurrentStep((prev) => prev + 1) - }, []) + const handleView = (planId: string) => { + router.push(`/workspace/auto-group/${planId}`) + } - const handlePrevious = useCallback(() => { - setCurrentStep((prev) => prev - 1) - }, []) - - const handleComplete = useCallback(() => { - // 这里可以添加表单提交逻辑 - console.log("Form submitted:", formData) - toast({ - title: "计划创建成功", - description: `已成功创建"${formData.name}"计划`, - }) - setIsCreating(false) - setCurrentStep(0) - // 重置表单数据 - setFormData({ - name: "新建群计划", - fixedWechatIds: [], - groupingOption: "all", - fixedGroupCount: 5, - selectedDevices: [], - audienceTags: [], - trafficTags: [], - matchLogic: "or", - excludeTags: ["已拉群"], - }) - }, [formData, toast]) - - const handleCancel = useCallback(() => { - setIsCreating(false) - setCurrentStep(0) - }, []) - - // 使用useCallback包装回调函数,避免不必要的重新创建 - const handleGroupSettingsChange = useCallback( - (values: { - name: string - fixedWechatIds: string[] - groupingOption: "all" | "fixed" - fixedGroupCount: number - }) => { - setFormData((prev) => ({ ...prev, ...values })) - }, - [], - ) - - const handleDevicesChange = useCallback((devices: string[]) => { - setFormData((prev) => ({ ...prev, selectedDevices: devices })) - }, []) - - const handleTagsChange = useCallback( - (values: { - audienceTags: string[] - trafficTags: string[] - matchLogic: "and" | "or" - excludeTags: string[] - }) => { - setFormData((prev) => ({ ...prev, ...values })) - }, - [], - ) + const togglePlanStatus = (planId: string) => { + setPlans( + plans.map((plan) => + plan.id === planId ? { ...plan, status: plan.status === "running" ? "stopped" : "running" } : plan, + ), + ) + } const getStatusColor = (status: Plan["status"]) => { switch (status) { @@ -167,129 +97,114 @@ export default function AutoGroupPage() { } } - if (isCreating) { - return ( -
-
- -

新建自动拉群计划

-
- -
- -
- - {currentStep === 0 && ( - - )} - - {currentStep === 1 && ( - - )} - - {currentStep === 2 && ( - - )} -
- ) - } - return ( -
-
-
-

微信自动拉群

- +
+
+
+
+ +

自动建群、自动进群

+
+ + +
+
- - - 进行中 - 已完成 - +
+ +
+
+ + +
+ + +
+
- -
- {mockPlans.map((plan) => ( - - -
- {plan.name} - {getStatusText(plan.status)} -
-
- -
-
- - 已建群数:{plan.groupCount} -
-
- - 群规模:{plan.groupSize} -
-
- - 更新时间:{plan.lastUpdated} -
-
-
+
+ {plans.map((plan) => ( + +
+ +
+
+

{plan.name}

+ + {getStatusText(plan.status)} + +
+
+ togglePlanStatus(plan.id)} /> + + + + + + handleView(plan.id)}> + + 查看 + + handleEdit(plan.id)}> + + 编辑 + + handleDelete(plan.id)}> + + 删除 + + + +
+
+ +
+
+
+ + 已建群数:{plan.groupCount} +
+
+ + 群规模:{plan.groupSize}人/群 +
+
+
+
总人数:{plan.totalFriends}人
+
+
{plan.tags.map((tag) => ( {tag} ))}
-
- - -
- - - ))} -
- +
+
+
- -
暂无已完成的计划
-
- +
+
+ + 更新时间:{plan.lastUpdated} +
+
+ + ))} +
) } - diff --git a/Cunkebao/app/workspace/auto-like/components/audience-tags.tsx b/Cunkebao/app/workspace/auto-like/components/audience-tags.tsx index ce3b134b..93c8bc37 100644 --- a/Cunkebao/app/workspace/auto-like/components/audience-tags.tsx +++ b/Cunkebao/app/workspace/auto-like/components/audience-tags.tsx @@ -1,51 +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 { Badge } from "@/components/ui/badge" -import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs" -import { Search, Plus, Trash2, LucideTag, Users } from "lucide-react" -import { ScrollArea } from "@/components/ui/scroll-area" -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog" -import { Textarea } from "@/components/ui/textarea" import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group" - -interface TagGroup { - id: string - name: string - description: string - type: "profession" | "interest" | "age" | "consumption" | "interaction" | "custom" - tags: Tag[] -} - -interface Tag { - id: string - name: string - count: number -} - -interface UserProfile { - id: string - name: string - avatar: string - tags: string[] - profession?: string - interest?: string - region?: string - lastActive?: string -} +import { Check, Plus, Tag, X } from "lucide-react" +import { Input } from "@/components/ui/input" +import { Badge } from "@/components/ui/badge" export interface AudienceTagsData { selectedTags: string[] @@ -53,457 +15,147 @@ export interface AudienceTagsData { } interface AudienceTagsProps { - initialData?: Partial + initialData: AudienceTagsData onSave: (data: AudienceTagsData) => void onBack: () => void } +// 模拟标签数据 +const predefinedTags = [ + "高意向", + "中意向", + "低意向", + "新客户", + "老客户", + "VIP客户", + "男性", + "女性", + "年轻人", + "中年人", + "老年人", + "城市", + "农村", + "高收入", + "中等收入", + "低收入", +] + export function AudienceTags({ initialData, onSave, onBack }: AudienceTagsProps) { - const [tagGroups, setTagGroups] = useState([]) - const [users, setUsers] = useState([]) - const [selectedTags, setSelectedTags] = useState(initialData?.selectedTags || []) - const [tagOperator, setTagOperator] = useState<"and" | "or">(initialData?.tagOperator || "or") - const [searchQuery, setSearchQuery] = useState("") - const [activeTab, setActiveTab] = useState("all") - const [newTagName, setNewTagName] = useState("") - const [newTagDescription, setNewTagDescription] = useState("") - const [newTagType, setNewTagType] = useState("custom") - const [isCreateTagDialogOpen, setIsCreateTagDialogOpen] = useState(false) + const [formData, setFormData] = useState(initialData) + const [newTag, setNewTag] = useState("") - // 模拟获取标签组和用户数据 - useEffect(() => { - const fetchData = async () => { - await new Promise((resolve) => setTimeout(resolve, 500)) + const toggleTag = (tag: string) => { + const newSelectedTags = formData.selectedTags.includes(tag) + ? formData.selectedTags.filter((t) => t !== tag) + : [...formData.selectedTags, tag] - // 模拟标签组数据 - const mockTagGroups: TagGroup[] = [ - { - id: "profession", - name: "职业", - description: "按照好友的职业分类", - type: "profession", - tags: [ - { id: "teacher", name: "教师", count: 15 }, - { id: "doctor", name: "医生", count: 8 }, - { id: "engineer", name: "工程师", count: 22 }, - { id: "business", name: "企业白领", count: 30 }, - { id: "freelancer", name: "自由职业", count: 12 }, - ], - }, - { - id: "interest", - name: "兴趣爱好", - description: "按照好友的兴趣爱好分类", - type: "interest", - tags: [ - { id: "photography", name: "摄影爱好者", count: 18 }, - { id: "sports", name: "运动达人", count: 25 }, - { id: "food", name: "美食爱好者", count: 32 }, - { id: "travel", name: "旅行达人", count: 20 }, - { id: "tech", name: "科技发烧友", count: 15 }, - ], - }, - { - id: "age", - name: "年龄范围", - description: "按照好友的年龄范围分类", - type: "age", - tags: [ - { id: "18-25", name: "18-25岁", count: 22 }, - { id: "26-35", name: "26-35岁", count: 45 }, - { id: "36-45", name: "36-45岁", count: 30 }, - { id: "46-55", name: "46-55岁", count: 15 }, - { id: "56+", name: "56岁以上", count: 8 }, - ], - }, - { - id: "consumption", - name: "消费能力", - description: "按照好友的消费能力分类", - type: "consumption", - tags: [ - { id: "high", name: "高消费", count: 12 }, - { id: "medium", name: "中等消费", count: 48 }, - { id: "low", name: "低消费", count: 30 }, - ], - }, - { - id: "interaction", - name: "互动频率", - description: "按照与好友的互动频率分类", - type: "interaction", - tags: [ - { id: "high-interaction", name: "高频互动", count: 15 }, - { id: "medium-interaction", name: "中频互动", count: 35 }, - { id: "low-interaction", name: "低频互动", count: 40 }, - { id: "new-friend", name: "近期新添加", count: 10 }, - ], - }, - { - id: "custom", - name: "自定义标签", - description: "自定义创建的标签", - type: "custom", - tags: [ - { id: "potential-customer", name: "潜在客户", count: 28 }, - { id: "vip", name: "VIP客户", count: 10 }, - { id: "partner", name: "合作伙伴", count: 5 }, - ], - }, - ] + setFormData({ ...formData, selectedTags: newSelectedTags }) + } - setTagGroups(mockTagGroups) - - // 模拟用户数据 - const mockUsers: UserProfile[] = Array.from({ length: 50 }, (_, i) => { - const professionTag = mockTagGroups[0].tags[Math.floor(Math.random() * mockTagGroups[0].tags.length)] - const interestTag = mockTagGroups[1].tags[Math.floor(Math.random() * mockTagGroups[1].tags.length)] - const ageTag = mockTagGroups[2].tags[Math.floor(Math.random() * mockTagGroups[2].tags.length)] - const consumptionTag = mockTagGroups[3].tags[Math.floor(Math.random() * mockTagGroups[3].tags.length)] - const interactionTag = mockTagGroups[4].tags[Math.floor(Math.random() * mockTagGroups[4].tags.length)] - - // 随机选择一些标签 - const userTags = [ - professionTag.id, - interestTag.id, - Math.random() > 0.5 ? ageTag.id : null, - Math.random() > 0.5 ? consumptionTag.id : null, - Math.random() > 0.5 ? interactionTag.id : null, - ].filter(Boolean) as string[] - - // 随机添加一些自定义标签 - if (Math.random() > 0.7) { - const customTag = mockTagGroups[5].tags[Math.floor(Math.random() * mockTagGroups[5].tags.length)] - userTags.push(customTag.id) - } - - return { - id: `user-${i + 1}`, - name: `用户${i + 1}`, - avatar: `https://api.dicebear.com/7.x/avataaars/svg?seed=${i}`, - tags: userTags, - profession: professionTag.name, - interest: interestTag.name, - region: ["北京", "上海", "广州", "深圳", "杭州"][Math.floor(Math.random() * 5)], - lastActive: `${Math.floor(Math.random() * 24)}小时前`, - } + const addCustomTag = () => { + if (newTag.trim() && !predefinedTags.includes(newTag) && !formData.selectedTags.includes(newTag)) { + setFormData({ + ...formData, + selectedTags: [...formData.selectedTags, newTag.trim()], }) - - setUsers(mockUsers) + setNewTag("") } - - fetchData() - }, []) - - // 获取所有标签 - const allTags = tagGroups.flatMap((group) => group.tags) - - // 根据选中的标签过滤用户 - const filteredUsers = users.filter((user) => { - if (selectedTags.length === 0) return true - - if (tagOperator === "and") { - return selectedTags.every((tagId) => user.tags.includes(tagId)) - } else { - return selectedTags.some((tagId) => user.tags.includes(tagId)) - } - }) - - // 根据搜索查询过滤标签 - const filteredTagGroups = tagGroups - .map((group) => ({ - ...group, - tags: group.tags.filter((tag) => tag.name.toLowerCase().includes(searchQuery.toLowerCase())), - })) - .filter((group) => group.tags.length > 0) - - // 根据标签类型过滤标签组 - const tabFilteredTagGroups = - activeTab === "all" ? filteredTagGroups : filteredTagGroups.filter((group) => group.id === activeTab) - - // 切换标签选择 - const toggleTag = (tagId: string) => { - setSelectedTags(selectedTags.includes(tagId) ? selectedTags.filter((id) => id !== tagId) : [...selectedTags, tagId]) - } - - // 创建新标签 - const handleCreateTag = () => { - if (newTagName.trim()) { - const newTag: Tag = { - id: `custom-${Date.now()}`, - name: newTagName.trim(), - count: 0, - } - - setTagGroups( - tagGroups.map((group) => (group.id === "custom" ? { ...group, tags: [...group.tags, newTag] } : group)), - ) - - setNewTagName("") - setNewTagDescription("") - setIsCreateTagDialogOpen(false) - } - } - - // 保存选择的标签 - const handleSave = () => { - onSave({ - selectedTags, - tagOperator, - }) } return ( -
- - - 指定点赞的人群标签 - 选择特定标签,只对带有这些标签的好友朋友圈进行点赞 - - - {/* 标签选择逻辑 */} -
- + + +
+
+ +

选择需要点赞的人群标签

+ +
+ {predefinedTags.map((tag) => ( + toggleTag(tag)} + > + {formData.selectedTags.includes(tag) && } + {tag} + + ))} +
+ +
+
+ + setNewTag(e.target.value)} + className="pl-9" + placeholder="添加自定义标签" + onKeyDown={(e) => e.key === "Enter" && addCustomTag()} + /> +
+ +
+
+ +
+ +

选择多个标签之间的匹配关系

+ setTagOperator(value as "and" | "or")} - className="flex space-x-4" + value={formData.tagOperator} + onValueChange={(value) => setFormData({ ...formData, tagOperator: value as "and" | "or" })} + className="flex flex-col space-y-2" >
- - + +
- - + +
- {/* 已选标签展示 */} -
- -
- {selectedTags.length === 0 ? ( - 未选择任何标签,将对所有好友点赞 +
+ +
+ {formData.selectedTags.length === 0 ? ( +

未选择任何标签

) : ( - selectedTags.map((tagId) => { - const tag = allTags.find((t) => t.id === tagId) - return tag ? ( - - {tag.name} - - ) : null - }) - )} -
-
- - {/* 标签搜索和分类 */} -
-
-
- - setSearchQuery(e.target.value)} - className="pl-9" - /> -
- - - - - - - 创建新标签 - 创建一个新的标签来分类您的好友 - -
-
- - setNewTagName(e.target.value)} - /> -
-
- -