存客宝 React

This commit is contained in:
柳清爽
2025-03-29 16:50:39 +08:00
parent caea0b4b99
commit 7e7c199996
388 changed files with 53282 additions and 2076 deletions

View File

@@ -0,0 +1,402 @@
"use client"
import { useState, useEffect } from "react"
import { ChevronLeft, Phone, MessageSquare, Settings, Users } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Card } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Switch } from "@/components/ui/switch"
import { Textarea } from "@/components/ui/textarea"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { useRouter } from "next/navigation"
import { toast } from "@/components/ui/use-toast"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
export default function EditPhoneAcquisitionPlan({ params }: { params: { id: string } }) {
const router = useRouter()
const [loading, setLoading] = useState(true)
const [formData, setFormData] = useState({
planName: "",
phoneNumbers: ["400-123-4567"],
autoAddEnabled: true,
speechToTextEnabled: true,
questionExtractionEnabled: true,
greetingMessage: "",
followUpMessages: [],
addToGroups: [],
tags: [],
})
useEffect(() => {
// 模拟从API获取计划数据
const fetchPlanData = async () => {
try {
// 这里应该是实际的API调用
const mockData = {
planName: "招商电话获客",
phoneNumbers: ["400-123-4567"],
autoAddEnabled: true,
speechToTextEnabled: true,
questionExtractionEnabled: true,
greetingMessage: "您好,感谢您的来电咨询,我稍后会添加您为好友,为您提供更详细的资料。",
followUpMessages: [
"您好我是XX公司的客服刚才接到您的电话现在方便聊一下吗",
"关于您刚才咨询的问题,我这边整理了一些资料,希望对您有所帮助。",
],
addToGroups: ["产品咨询群", "招商合作群"],
tags: ["电话咨询", "招商意向"],
}
setFormData(mockData)
setLoading(false)
} catch (error) {
toast({
title: "加载失败",
description: "获取计划数据失败,请重试",
variant: "destructive",
})
setLoading(false)
}
}
fetchPlanData()
}, [])
const handleSave = async () => {
try {
// 这里应该是实际的API调用
await new Promise((resolve) => setTimeout(resolve, 1000))
toast({
title: "保存成功",
description: "电话获客计划已更新",
})
router.push(`/scenarios/phone`)
} catch (error) {
toast({
title: "保存失败",
description: "更新计划失败,请重试",
variant: "destructive",
})
}
}
const handleAddPhoneNumber = () => {
setFormData({
...formData,
phoneNumbers: [...formData.phoneNumbers, ""],
})
}
const handlePhoneNumberChange = (index: number, value: string) => {
const updatedPhoneNumbers = [...formData.phoneNumbers]
updatedPhoneNumbers[index] = value
setFormData({
...formData,
phoneNumbers: updatedPhoneNumbers,
})
}
const handleRemovePhoneNumber = (index: number) => {
const updatedPhoneNumbers = [...formData.phoneNumbers]
updatedPhoneNumbers.splice(index, 1)
setFormData({
...formData,
phoneNumbers: updatedPhoneNumbers,
})
}
const handleAddFollowUpMessage = () => {
setFormData({
...formData,
followUpMessages: [...formData.followUpMessages, ""],
})
}
const handleFollowUpMessageChange = (index: number, value: string) => {
const updatedMessages = [...formData.followUpMessages]
updatedMessages[index] = value
setFormData({
...formData,
followUpMessages: updatedMessages,
})
}
const handleRemoveFollowUpMessage = (index: number) => {
const updatedMessages = [...formData.followUpMessages]
updatedMessages.splice(index, 1)
setFormData({
...formData,
followUpMessages: updatedMessages,
})
}
if (loading) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600 mx-auto"></div>
<p className="mt-2 text-gray-600">...</p>
</div>
</div>
)
}
return (
<div className="min-h-screen bg-gray-50">
<div className="max-w-[390px] mx-auto bg-white min-h-screen flex flex-col">
<header className="sticky top-0 z-10 bg-white border-b">
<div className="flex items-center h-14 px-4">
<Button variant="ghost" size="icon" onClick={() => router.back()}>
<ChevronLeft className="h-5 w-5" />
</Button>
<h1 className="ml-2 text-lg font-medium"></h1>
</div>
</header>
<div className="flex-1 p-4">
<Tabs defaultValue="basic" className="w-full">
<TabsList className="grid w-full grid-cols-3 mb-4">
<TabsTrigger value="basic">
<Phone className="h-4 w-4 mr-2" />
</TabsTrigger>
<TabsTrigger value="message">
<MessageSquare className="h-4 w-4 mr-2" />
</TabsTrigger>
<TabsTrigger value="advanced">
<Settings className="h-4 w-4 mr-2" />
</TabsTrigger>
</TabsList>
<TabsContent value="basic" className="space-y-4">
<Card className="p-4">
<div className="space-y-4">
<div>
<Label htmlFor="planName"></Label>
<Input
id="planName"
value={formData.planName}
onChange={(e) => setFormData({ ...formData, planName: e.target.value })}
className="mt-1"
/>
</div>
<div>
<Label></Label>
<div className="space-y-2 mt-1">
{formData.phoneNumbers.map((phone, index) => (
<div key={index} className="flex items-center space-x-2">
<Input
value={phone}
onChange={(e) => handlePhoneNumberChange(index, e.target.value)}
placeholder="输入电话号码"
/>
{formData.phoneNumbers.length > 1 && (
<Button
variant="ghost"
size="sm"
onClick={() => handleRemovePhoneNumber(index)}
className="text-red-500 hover:text-red-700"
>
</Button>
)}
</div>
))}
<Button variant="outline" size="sm" onClick={handleAddPhoneNumber}>
</Button>
</div>
</div>
<div className="space-y-4">
<div className="flex items-center justify-between">
<div className="space-y-0.5">
<Label htmlFor="auto-add"></Label>
<p className="text-xs text-gray-500"></p>
</div>
<Switch
id="auto-add"
checked={formData.autoAddEnabled}
onCheckedChange={(checked) => setFormData({ ...formData, autoAddEnabled: checked })}
/>
</div>
<div className="flex items-center justify-between">
<div className="space-y-0.5">
<Label htmlFor="speech-to-text"></Label>
<p className="text-xs text-gray-500"></p>
</div>
<Switch
id="speech-to-text"
checked={formData.speechToTextEnabled}
onCheckedChange={(checked) => setFormData({ ...formData, speechToTextEnabled: checked })}
/>
</div>
<div className="flex items-center justify-between">
<div className="space-y-0.5">
<Label htmlFor="question-extraction"></Label>
<p className="text-xs text-gray-500"></p>
</div>
<Switch
id="question-extraction"
checked={formData.questionExtractionEnabled}
onCheckedChange={(checked) => setFormData({ ...formData, questionExtractionEnabled: checked })}
/>
</div>
</div>
</div>
</Card>
</TabsContent>
<TabsContent value="message" className="space-y-4">
<Card className="p-4">
<div className="space-y-4">
<div>
<Label htmlFor="greeting"></Label>
<Textarea
id="greeting"
value={formData.greetingMessage}
onChange={(e) => setFormData({ ...formData, greetingMessage: e.target.value })}
placeholder="输入添加好友后的问候语"
className="mt-1 min-h-[100px]"
/>
</div>
<div>
<Label></Label>
<div className="space-y-2 mt-1">
{formData.followUpMessages.map((message, index) => (
<div key={index} className="space-y-1">
<div className="flex items-center justify-between">
<Label className="text-sm"> {index + 1}</Label>
<Button
variant="ghost"
size="sm"
onClick={() => handleRemoveFollowUpMessage(index)}
className="text-red-500 hover:text-red-700"
>
</Button>
</div>
<Textarea
value={message}
onChange={(e) => handleFollowUpMessageChange(index, e.target.value)}
placeholder="输入跟进消息内容"
className="min-h-[80px]"
/>
</div>
))}
<Button variant="outline" size="sm" onClick={handleAddFollowUpMessage}>
</Button>
</div>
</div>
</div>
</Card>
</TabsContent>
<TabsContent value="advanced" className="space-y-4">
<Card className="p-4">
<div className="space-y-4">
<div>
<Label></Label>
<Select
value={formData.addToGroups.length > 0 ? "selected" : "none"}
onValueChange={(value) => {
if (value === "none") {
setFormData({ ...formData, addToGroups: [] })
}
}}
>
<SelectTrigger className="mt-1">
<SelectValue placeholder="选择群组" />
</SelectTrigger>
<SelectContent>
<SelectItem value="none"></SelectItem>
<SelectItem value="selected"> {formData.addToGroups.length} </SelectItem>
</SelectContent>
</Select>
{formData.addToGroups.length > 0 && (
<div className="mt-2 flex flex-wrap gap-2">
{formData.addToGroups.map((group, index) => (
<div
key={index}
className="flex items-center bg-blue-50 text-blue-700 rounded-full px-3 py-1"
>
<Users className="h-3 w-3 mr-1" />
<span className="text-sm">{group}</span>
<Button
variant="ghost"
size="sm"
className="ml-1 p-0 h-4 w-4 rounded-full"
onClick={() => {
const updatedGroups = [...formData.addToGroups]
updatedGroups.splice(index, 1)
setFormData({ ...formData, addToGroups: updatedGroups })
}}
>
×
</Button>
</div>
))}
</div>
)}
</div>
<div>
<Label></Label>
<div className="mt-2 flex flex-wrap gap-2">
{formData.tags.map((tag, index) => (
<div key={index} className="flex items-center bg-gray-100 rounded-full px-3 py-1">
<span className="text-sm">{tag}</span>
<Button
variant="ghost"
size="sm"
className="ml-1 p-0 h-4 w-4 rounded-full"
onClick={() => {
const updatedTags = [...formData.tags]
updatedTags.splice(index, 1)
setFormData({ ...formData, tags: updatedTags })
}}
>
×
</Button>
</div>
))}
<Input
placeholder="添加标签"
className="w-32"
onKeyDown={(e) => {
if (e.key === "Enter" && e.currentTarget.value) {
e.preventDefault()
setFormData({
...formData,
tags: [...formData.tags, e.currentTarget.value],
})
e.currentTarget.value = ""
}
}}
/>
</div>
</div>
</div>
</Card>
</TabsContent>
</Tabs>
</div>
<div className="sticky bottom-0 left-0 right-0 bg-white border-t p-4">
<Button className="w-full" onClick={handleSave}>
</Button>
</div>
</div>
</div>
)
}