Files
cunkebao_v3/Cunkebao/app/workspace/pricing/edit/[id]/page.tsx

535 lines
20 KiB
TypeScript
Raw Normal View History

2025-03-29 16:50:39 +08:00
"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>
)
}