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

535 lines
20 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 { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { ChevronLeft, Users, Smartphone, Settings } from "lucide-react"
import { Badge } from "@/components/ui/badge"
import { Card } from "@/components/ui/card"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
import { Switch } from "@/components/ui/switch"
import { DeviceSelectionDialog } from "@/app/components/device-selection-dialog"
import { TrafficPoolSelector } from "@/app/components/traffic-pool-selector"
interface UserTag {
id: string
name: string
color: string
}
interface TrafficUser {
id: string
avatar: string
nickname: string
wechatId: string
phone: string
region: string
note: string
status: "pending" | "added" | "failed"
addTime: string
source: string
assignedTo: string
category: "potential" | "customer" | "lost"
tags: UserTag[]
}
type DeviceSelectionType = "all" | "new" | "specific"
// 模拟规则数据
const mockRuleData = [
{
id: "1",
name: "新用户流量分发",
selectedUsers: [
{
id: "user-1",
avatar: "/placeholder.svg?height=40&width=40",
nickname: "用户1",
wechatId: "wxid_abc123",
phone: "13800138000",
region: "北京",
note: "",
status: "added" as const,
addTime: "2023-05-15T08:30:00.000Z",
source: "抖音直播",
assignedTo: "销售1",
category: "potential" as const,
tags: [
{ id: "tag1", name: "潜在客户", color: "bg-blue-100 text-blue-800" },
{ id: "tag4", name: "需跟进", color: "bg-yellow-100 text-yellow-800" },
],
},
{
id: "user-2",
avatar: "/placeholder.svg?height=40&width=40",
nickname: "用户2",
wechatId: "wxid_def456",
phone: "13900139000",
region: "上海",
note: "这是用户2的备注",
status: "pending" as const,
addTime: "2023-05-16T10:15:00.000Z",
source: "小红书",
assignedTo: "销售2",
category: "potential" as const,
tags: [{ id: "tag1", name: "潜在客户", color: "bg-blue-100 text-blue-800" }],
},
],
deviceSelectionType: "all",
selectedDevices: [],
priority: "high",
createAsPackage: false,
price: "",
autoDistribute: true,
},
{
id: "2",
name: "高端用户流量包",
selectedUsers: [
{
id: "user-3",
avatar: "/placeholder.svg?height=40&width=40",
nickname: "用户3",
wechatId: "wxid_ghi789",
phone: "13700137000",
region: "广州",
note: "",
status: "added" as const,
addTime: "2023-05-14T14:20:00.000Z",
source: "微信朋友圈",
assignedTo: "销售3",
category: "customer" as const,
tags: [
{ id: "tag2", name: "高意向", color: "bg-green-100 text-green-800" },
{ id: "tag7", name: "企业客户", color: "bg-red-100 text-red-800" },
],
},
],
deviceSelectionType: "specific",
selectedDevices: ["device-1", "device-4", "device-5"],
priority: "low",
createAsPackage: true,
price: "2.5",
autoDistribute: false,
},
]
export default function EditRulePage({ params }: { params: { id: string } }) {
const router = useRouter()
const { id } = params
const [currentStep, setCurrentStep] = useState(1)
const [name, setName] = useState("")
// 流量池相关状态
const [selectedUsers, setSelectedUsers] = useState<TrafficUser[]>([])
const [trafficPoolDialogOpen, setTrafficPoolDialogOpen] = useState(false)
// 设备选择相关状态
const [deviceSelectionType, setDeviceSelectionType] = useState<DeviceSelectionType>("all")
const [selectedDevices, setSelectedDevices] = useState<string[]>([])
const [deviceDialogOpen, setDeviceDialogOpen] = useState(false)
// 规则设定相关状态
const [priority, setPriority] = useState("high")
const [createAsPackage, setCreateAsPackage] = useState(false)
const [price, setPrice] = useState("")
const [autoDistribute, setAutoDistribute] = useState(true)
const [loading, setLoading] = useState(true)
useEffect(() => {
// 在实际应用中这里会从API获取数据
// 这里使用模拟数据
const ruleItem = mockRuleData.find((item) => item.id === id)
if (ruleItem) {
setName(ruleItem.name)
setSelectedUsers(ruleItem.selectedUsers)
setDeviceSelectionType(ruleItem.deviceSelectionType as DeviceSelectionType)
setSelectedDevices(ruleItem.selectedDevices)
setPriority(ruleItem.priority)
setCreateAsPackage(ruleItem.createAsPackage)
setPrice(ruleItem.price)
setAutoDistribute(ruleItem.autoDistribute)
} else {
// 如果找不到数据,返回列表页
router.push("/workspace/pricing")
}
setLoading(false)
}, [id, router])
const handleDeviceSelect = (deviceIds: string[]) => {
setSelectedDevices(deviceIds)
}
const handleUserSelect = (users: TrafficUser[]) => {
setSelectedUsers(users)
}
const handleNext = () => {
if (currentStep === 1) {
if (name.trim() && selectedUsers.length > 0) {
setCurrentStep(2)
} else {
alert("请填写任务名称并选择流量池")
}
} else if (currentStep === 2) {
if (deviceSelectionType === "specific" && selectedDevices.length === 0) {
alert("请选择至少一个设备")
} else {
setCurrentStep(3)
}
}
}
const handlePrevious = () => {
if (currentStep === 2) {
setCurrentStep(1)
} else if (currentStep === 3) {
setCurrentStep(2)
}
}
const handleSubmit = () => {
// 在实际应用中这里会发送API请求更新数据
console.log({
id,
name,
selectedUsers: selectedUsers.map((user) => user.id),
deviceSelectionType,
selectedDevices: deviceSelectionType === "specific" ? selectedDevices : [],
priority,
createAsPackage,
price: createAsPackage ? price : "",
autoDistribute,
})
// 返回到列表页
router.push("/workspace/pricing")
}
if (loading) {
return (
<div className="flex justify-center items-center min-h-screen">
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-primary"></div>
</div>
)
}
return (
<div className="flex flex-col min-h-screen bg-gray-50">
{/* 顶部栏 */}
<header className="bg-white border-b border-gray-200 sticky top-0 z-10">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between">
<Button variant="ghost" size="icon" onClick={() => router.push("/workspace/pricing")} className="mr-2">
<ChevronLeft className="h-5 w-5" />
</Button>
<h1 className="text-lg font-medium"></h1>
<div className="w-10"></div> {/* 占位,保持标题居中 */}
</div>
</header>
{/* 进度条 */}
<div className="bg-white border-b border-gray-200">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
<div className="flex items-center">
<div className="flex-1">
<div className="flex items-center">
<div
className={`rounded-full h-10 w-10 flex items-center justify-center ${
currentStep >= 1 ? "bg-blue-600 text-white" : "bg-gray-200 text-gray-600"
}`}
>
<Users className="h-5 w-5" />
</div>
<div className={`h-1 flex-1 mx-2 ${currentStep >= 2 ? "bg-blue-600" : "bg-gray-200"}`}></div>
<div
className={`rounded-full h-10 w-10 flex items-center justify-center ${
currentStep >= 2 ? "bg-blue-600 text-white" : "bg-gray-200 text-gray-600"
}`}
>
<Smartphone className="h-5 w-5" />
</div>
<div className={`h-1 flex-1 mx-2 ${currentStep >= 3 ? "bg-blue-600" : "bg-gray-200"}`}></div>
<div
className={`rounded-full h-10 w-10 flex items-center justify-center ${
currentStep >= 3 ? "bg-blue-600 text-white" : "bg-gray-200 text-gray-600"
}`}
>
<Settings className="h-5 w-5" />
</div>
</div>
</div>
</div>
<div className="flex mt-2">
<div className="flex-1 text-center text-sm font-medium"></div>
<div className="flex-1 text-center text-sm font-medium"></div>
<div className="flex-1 text-center text-sm font-medium"></div>
</div>
</div>
</div>
{/* 主内容区 */}
<main className="flex-1 max-w-3xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
{currentStep === 1 ? (
<div className="space-y-6">
<div className="space-y-2">
<Label htmlFor="name"></Label>
<Input
id="name"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="请输入任务名称"
required
/>
</div>
<div className="space-y-4">
<div className="flex items-center justify-between">
<h2 className="text-lg font-medium"></h2>
<Button variant="outline" onClick={() => setTrafficPoolDialogOpen(true)}>
{selectedUsers.length > 0 ? "修改选择" : "选择流量池"}
</Button>
</div>
{selectedUsers.length > 0 ? (
<Card className="p-4">
<div className="flex items-center justify-between mb-2">
<h3 className="font-medium"></h3>
<Badge>{selectedUsers.length} </Badge>
</div>
<div className="space-y-2">
{/* 显示选中用户的标签统计 */}
<div className="flex flex-wrap gap-2">
{Array.from(
new Set(selectedUsers.flatMap((user) => user.tags.map((tag) => JSON.stringify(tag)))),
).map((tagJson) => {
const tag = JSON.parse(tagJson) as UserTag
const count = selectedUsers.filter((user) => user.tags.some((t) => t.id === tag.id)).length
return (
<Badge key={tag.id} className={tag.color}>
{tag.name} ({count})
</Badge>
)
})}
</div>
{/* 显示选中用户的来源统计 */}
<div className="mt-3">
<div className="text-sm font-medium mb-1"></div>
<div className="flex flex-wrap gap-2">
{Array.from(new Set(selectedUsers.map((user) => user.source))).map((source) => {
const count = selectedUsers.filter((user) => user.source === source).length
return (
<Badge key={source} variant="outline">
{source} ({count})
</Badge>
)
})}
</div>
</div>
</div>
</Card>
) : (
<Card className="p-8 flex flex-col items-center justify-center text-center text-gray-500">
<Users className="h-12 w-12 mb-4 text-gray-400" />
<p></p>
<p className="text-sm mt-1"></p>
</Card>
)}
<TrafficPoolSelector
open={trafficPoolDialogOpen}
onOpenChange={setTrafficPoolDialogOpen}
selectedUsers={selectedUsers}
onSelect={handleUserSelect}
/>
</div>
<div className="flex justify-end pt-4">
<Button type="button" onClick={handleNext} disabled={name.trim() === "" || selectedUsers.length === 0}>
</Button>
</div>
</div>
) : currentStep === 2 ? (
<div className="space-y-6">
<div className="space-y-4">
<h2 className="text-lg font-medium"></h2>
<RadioGroup
value={deviceSelectionType}
onValueChange={(value) => setDeviceSelectionType(value as DeviceSelectionType)}
className="space-y-4"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="all" id="all-devices" />
<Label htmlFor="all-devices" className="cursor-pointer">
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="new" id="new-devices" />
<Label htmlFor="new-devices" className="cursor-pointer">
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="specific" id="specific-devices" />
<Label htmlFor="specific-devices" className="cursor-pointer">
</Label>
</div>
</RadioGroup>
{deviceSelectionType === "specific" && (
<div className="mt-4">
<Button variant="outline" onClick={() => setDeviceDialogOpen(true)} className="w-full">
{selectedDevices.length > 0 ? `已选择 ${selectedDevices.length} 个设备` : "选择设备"}
</Button>
{selectedDevices.length > 0 && (
<Card className="mt-4 p-4">
<div className="flex items-center justify-between mb-2">
<h3 className="font-medium"></h3>
<Badge>{selectedDevices.length} </Badge>
</div>
<p className="text-sm text-gray-500">
{selectedDevices.length}
</p>
</Card>
)}
<DeviceSelectionDialog
open={deviceDialogOpen}
onOpenChange={setDeviceDialogOpen}
selectedDevices={selectedDevices}
onSelect={handleDeviceSelect}
/>
</div>
)}
{deviceSelectionType === "all" && (
<Card className="p-4 bg-blue-50 border-blue-200">
<p className="text-sm text-blue-700">
使线
</p>
</Card>
)}
{deviceSelectionType === "new" && (
<Card className="p-4 bg-green-50 border-green-200">
<p className="text-sm text-green-700">
使
</p>
</Card>
)}
</div>
{/* 按钮组 */}
<div className="flex justify-between pt-4">
<Button type="button" variant="outline" onClick={handlePrevious}>
</Button>
<Button
type="button"
onClick={handleNext}
disabled={deviceSelectionType === "specific" && selectedDevices.length === 0}
>
</Button>
</div>
</div>
) : (
<div className="space-y-6">
<div>
<h2 className="text-lg font-medium mb-4"></h2>
{/* 优先级选择 */}
<div className="space-y-4 mb-6">
<h3 className="font-medium"></h3>
<RadioGroup value={priority} onValueChange={setPriority} className="space-y-2">
<div className="flex items-center space-x-2">
<RadioGroupItem value="high" id="high-priority" />
<Label htmlFor="high-priority" className="cursor-pointer">
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="low" id="low-priority" />
<Label htmlFor="low-priority" className="cursor-pointer">
</Label>
</div>
</RadioGroup>
<Card className="p-3 bg-gray-50 border-gray-200">
<p className="text-sm text-gray-600">
</p>
</Card>
</div>
{/* 自动分发设置 */}
<div className="space-y-2 mb-6">
<div className="flex items-center justify-between">
<Label htmlFor="auto-distribute"></Label>
<Switch id="auto-distribute" checked={autoDistribute} onCheckedChange={setAutoDistribute} />
</div>
<p className="text-sm text-gray-500"></p>
</div>
{/* 创建为流量包 */}
<div className="space-y-4 mb-6">
<div className="flex items-center justify-between">
<Label htmlFor="create-package"></Label>
<Switch id="create-package" checked={createAsPackage} onCheckedChange={setCreateAsPackage} />
</div>
<p className="text-sm text-gray-500"></p>
{createAsPackage && (
<div className="space-y-2 mt-4">
<Label htmlFor="price">/</Label>
<div className="relative">
<span className="absolute left-3 top-1/2 transform -translate-y-1/2">¥</span>
<Input
id="price"
type="number"
step="0.01"
min="0"
value={price}
onChange={(e) => setPrice(e.target.value)}
placeholder="0.00"
className="pl-8"
required
/>
</div>
</div>
)}
</div>
</div>
{/* 按钮组 */}
<div className="flex justify-between pt-4">
<Button type="button" variant="outline" onClick={handlePrevious}>
</Button>
<Button type="button" onClick={handleSubmit} disabled={createAsPackage && !price}>
</Button>
</div>
</div>
)}
</main>
</div>
)
}