Files
cunkebao_v3/Cunkebao/app/workspace/traffic-distribution/[id]/edit/page.tsx
2025-04-09 09:31:09 +08:00

486 lines
19 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import { useState, useEffect } from "react"
import { useRouter } from "next/navigation"
import { ChevronLeft, Info, Users, Target, Settings, ArrowRight, ArrowLeft } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Textarea } from "@/components/ui/textarea"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
import { Switch } from "@/components/ui/switch"
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
import { Slider } from "@/components/ui/slider"
import { Badge } from "@/components/ui/badge"
import { TrafficPoolSelector } from "@/app/components/traffic-pool-selector"
// 模拟数据
const planDetails = {
id: "1",
name: "抖音直播引流计划",
description: "从抖音直播间获取的潜在客户流量分发",
status: "active",
source: "douyin",
sourceIcon: "🎬",
distributionMethod: "even",
targetGroups: ["新客户", "潜在客户"],
devices: ["iPhone 13", "华为 P40", "小米 11"],
totalUsers: 1250,
dailyAverage: 85,
weeklyData: [42, 56, 78, 64, 85, 92, 76],
createdAt: "2024-03-10T08:30:00Z",
lastUpdated: "2024-03-18T10:30:00Z",
rules: {
maxPerDay: 50,
timeRestriction: "custom",
customTimeStart: "09:00",
customTimeEnd: "21:00",
userFilters: [],
excludeTags: [],
},
selectedUsers: [],
}
export default function EditTrafficDistributionPage({ params }: { params: { id: string } }) {
const router = useRouter()
const [currentStep, setCurrentStep] = useState(1)
const [formData, setFormData] = useState({
name: "",
description: "",
source: "",
distributionMethod: "even", // even, priority, ratio
targetGroups: [] as string[],
targetDevices: [] as string[],
autoTag: true,
activeStatus: true,
priorityOrder: [] as string[],
ratioSettings: {} as Record<string, number>,
rules: {
maxPerDay: 50,
timeRestriction: "all", // all, custom
customTimeStart: "09:00",
customTimeEnd: "21:00",
userFilters: [] as string[],
excludeTags: [] as string[],
},
selectedUsers: [],
isPoolSelectorOpen: false,
})
// 加载计划详情
useEffect(() => {
// 模拟API请求
setFormData({
name: planDetails.name,
description: planDetails.description || "",
source: planDetails.source,
distributionMethod: planDetails.distributionMethod,
targetGroups: planDetails.targetGroups,
targetDevices: planDetails.devices,
autoTag: true,
activeStatus: planDetails.status === "active",
priorityOrder: planDetails.targetGroups,
ratioSettings: planDetails.targetGroups.reduce(
(acc, group, index, arr) => {
acc[group] = Math.floor(100 / arr.length)
return acc
},
{} as Record<string, number>,
),
rules: planDetails.rules,
selectedUsers: planDetails.selectedUsers || [],
isPoolSelectorOpen: false,
})
}, [params.id])
const updateFormData = (field: string, value: any) => {
setFormData((prev) => {
if (field.includes(".")) {
const [parent, child] = field.split(".")
return {
...prev,
[parent]: {
...prev[parent as keyof typeof prev],
[child]: value,
},
}
}
return { ...prev, [field]: value }
})
}
const handleNext = () => {
setCurrentStep((prev) => prev + 1)
}
const handleBack = () => {
if (currentStep > 1) {
setCurrentStep((prev) => prev - 1)
} else {
router.back()
}
}
const handleSubmit = () => {
// 这里处理表单提交逻辑
console.log("提交表单数据:", formData)
router.push(`/workspace/traffic-distribution/${params.id}`)
}
const isStep1Valid = formData.name && formData.source
const isStep2Valid = formData.targetGroups.length > 0 || formData.targetDevices.length > 0
const isStep3Valid = true // 规则设置可以有默认值
return (
<div className="flex-1 bg-white min-h-screen">
<header className="sticky top-0 z-10 bg-white border-b">
<div className="flex items-center justify-between p-4">
<div className="flex items-center space-x-3">
<Button variant="ghost" size="icon" onClick={handleBack}>
<ChevronLeft className="h-5 w-5" />
</Button>
<h1 className="text-lg font-medium"></h1>
</div>
</div>
</header>
<div className="p-4 max-w-3xl mx-auto">
{/* 步骤指示器 */}
<div className="mb-6">
<div className="flex items-center justify-between">
{[
{ step: 1, title: "基本信息", icon: <Info className="h-4 w-4" /> },
{ step: 2, title: "目标设置", icon: <Target className="h-4 w-4" /> },
{ step: 3, title: "规则配置", icon: <Settings className="h-4 w-4" /> },
].map(({ step, title, icon }) => (
<div key={step} className="flex flex-col items-center">
<div
className={`w-10 h-10 rounded-full flex items-center justify-center ${
step === currentStep
? "bg-blue-600 text-white"
: step < currentStep
? "bg-green-500 text-white"
: "bg-gray-200 text-gray-500"
}`}
>
{step < currentStep ? "✓" : icon}
</div>
<span className="text-xs mt-1">{title}</span>
</div>
))}
</div>
<div className="relative mt-2">
<div className="absolute top-0 left-0 right-0 h-1 bg-gray-200"></div>
<div
className="absolute top-0 left-0 h-1 bg-blue-600 transition-all"
style={{ width: `${((currentStep - 1) / 2) * 100}%` }}
></div>
</div>
</div>
{/* 步骤1基本信息 */}
{currentStep === 1 && (
<Card>
<CardHeader>
<CardTitle></CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="name">
<span className="text-red-500">*</span>
</Label>
<Input
id="name"
placeholder="输入分发计划名称"
value={formData.name}
onChange={(e) => updateFormData("name", e.target.value)}
/>
</div>
<div className="space-y-2">
<Label htmlFor="description"></Label>
<Textarea
id="description"
placeholder="简要描述该分发计划的目标和用途"
value={formData.description}
onChange={(e) => updateFormData("description", e.target.value)}
/>
</div>
<div className="space-y-2">
<Label htmlFor="source">
<span className="text-red-500">*</span>
</Label>
<Select value={formData.source} onValueChange={(value) => updateFormData("source", value)}>
<SelectTrigger>
<SelectValue placeholder="选择流量来源" />
</SelectTrigger>
<SelectContent>
<SelectItem value="douyin"></SelectItem>
<SelectItem value="xiaohongshu"></SelectItem>
<SelectItem value="wechat"></SelectItem>
<SelectItem value="weibo"></SelectItem>
<SelectItem value="other"></SelectItem>
</SelectContent>
</Select>
</div>
<div className="pt-4 flex justify-end">
<Button onClick={handleNext} disabled={!isStep1Valid}>
<ArrowRight className="ml-2 h-4 w-4" />
</Button>
</div>
</CardContent>
</Card>
)}
{/* 步骤2目标设置 */}
{currentStep === 2 && (
<Card>
<CardHeader>
<CardTitle></CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<Tabs defaultValue="groups">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="groups"></TabsTrigger>
<TabsTrigger value="devices"></TabsTrigger>
</TabsList>
<TabsContent value="groups" className="pt-4">
<div className="space-y-4">
<div className="grid grid-cols-2 gap-3">
{["新客户", "潜在客户", "老客户", "会员", "高价值用户", "流失用户"].map((group) => (
<Card
key={group}
className={`cursor-pointer hover:border-blue-400 transition-colors ${
formData.targetGroups.includes(group) ? "border-blue-500 bg-blue-50" : ""
}`}
onClick={() => {
const newGroups = formData.targetGroups.includes(group)
? formData.targetGroups.filter((g) => g !== group)
: [...formData.targetGroups, group]
updateFormData("targetGroups", newGroups)
}}
>
<CardContent className="p-3 text-center">{group}</CardContent>
</Card>
))}
</div>
<p className="text-xs text-gray-500"></p>
<div className="mt-4">
<Button
variant="outline"
onClick={() => updateFormData("isPoolSelectorOpen", true)}
className="w-full"
>
<Users className="mr-2 h-4 w-4" />
</Button>
{formData.selectedUsers.length > 0 && (
<div className="mt-2">
<p className="text-sm font-medium mb-1"> {formData.selectedUsers.length} </p>
<div className="flex flex-wrap gap-1">
{formData.selectedUsers.slice(0, 3).map((user: any) => (
<Badge key={user.id} variant="secondary">
{user.nickname}
</Badge>
))}
{formData.selectedUsers.length > 3 && (
<Badge variant="secondary">+{formData.selectedUsers.length - 3}</Badge>
)}
</div>
</div>
)}
</div>
</div>
</TabsContent>
<TabsContent value="devices" className="pt-4">
<div className="space-y-4">
<p className="text-sm"></p>
<Button variant="outline" className="w-full">
</Button>
<p className="text-xs text-gray-500"></p>
</div>
</TabsContent>
</Tabs>
<div className="pt-4 flex justify-between">
<Button variant="outline" onClick={handleBack}>
<ArrowLeft className="mr-2 h-4 w-4" />
</Button>
<Button onClick={handleNext} disabled={!isStep2Valid}>
<ArrowRight className="ml-2 h-4 w-4" />
</Button>
</div>
</CardContent>
</Card>
)}
{/* 步骤3规则配置 */}
{currentStep === 3 && (
<Card>
<CardHeader>
<CardTitle></CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="space-y-4">
<h3 className="text-sm font-medium"></h3>
<RadioGroup
value={formData.distributionMethod}
onValueChange={(value) => updateFormData("distributionMethod", value)}
className="space-y-3"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="even" id="even" />
<Label htmlFor="even" className="cursor-pointer">
</Label>
<span className="text-xs text-gray-500 ml-2">()</span>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="priority" id="priority" />
<Label htmlFor="priority" className="cursor-pointer">
</Label>
<span className="text-xs text-gray-500 ml-2">()</span>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="ratio" id="ratio" />
<Label htmlFor="ratio" className="cursor-pointer">
</Label>
<span className="text-xs text-gray-500 ml-2">()</span>
</div>
</RadioGroup>
</div>
<div className="space-y-4 border-t pt-4">
<h3 className="text-sm font-medium"></h3>
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label htmlFor="maxPerDay"></Label>
<span className="text-sm font-medium">{formData.rules.maxPerDay} /</span>
</div>
<Slider
id="maxPerDay"
min={10}
max={200}
step={10}
value={[formData.rules.maxPerDay]}
onValueChange={(value) => updateFormData("rules.maxPerDay", value[0])}
/>
<p className="text-xs text-gray-500"></p>
</div>
<div className="space-y-2">
<Label></Label>
<RadioGroup
value={formData.rules.timeRestriction}
onValueChange={(value) => updateFormData("rules.timeRestriction", value)}
className="space-y-3"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="all" id="all-time" />
<Label htmlFor="all-time" className="cursor-pointer">
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="custom" id="custom-time" />
<Label htmlFor="custom-time" className="cursor-pointer">
</Label>
</div>
</RadioGroup>
{formData.rules.timeRestriction === "custom" && (
<div className="grid grid-cols-2 gap-2 mt-2">
<div>
<Label htmlFor="timeStart" className="text-xs">
</Label>
<Input
id="timeStart"
type="time"
value={formData.rules.customTimeStart}
onChange={(e) => updateFormData("rules.customTimeStart", e.target.value)}
/>
</div>
<div>
<Label htmlFor="timeEnd" className="text-xs">
</Label>
<Input
id="timeEnd"
type="time"
value={formData.rules.customTimeEnd}
onChange={(e) => updateFormData("rules.customTimeEnd", e.target.value)}
/>
</div>
</div>
)}
</div>
</div>
<div className="space-y-2 border-t pt-4">
<div className="flex items-center justify-between">
<Label htmlFor="autoTag"></Label>
<Switch
id="autoTag"
checked={formData.autoTag}
onCheckedChange={(checked) => updateFormData("autoTag", checked)}
/>
</div>
<p className="text-xs text-gray-500"></p>
</div>
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label htmlFor="activeStatus"></Label>
<Switch
id="activeStatus"
checked={formData.activeStatus}
onCheckedChange={(checked) => updateFormData("activeStatus", checked)}
/>
</div>
<p className="text-xs text-gray-500"></p>
</div>
<div className="pt-4 flex justify-between">
<Button variant="outline" onClick={handleBack}>
<ArrowLeft className="mr-2 h-4 w-4" />
</Button>
<Button onClick={handleSubmit} disabled={!isStep3Valid}>
</Button>
</div>
</CardContent>
</Card>
)}
</div>
{/* 流量池选择器 */}
<TrafficPoolSelector
open={formData.isPoolSelectorOpen}
onOpenChange={(open) => updateFormData("isPoolSelectorOpen", open)}
selectedUsers={formData.selectedUsers}
onSelect={(users) => updateFormData("selectedUsers", users)}
/>
</div>
)
}