"use client" import type React from "react" import { useState, useEffect, useRef, useMemo } from "react" import { Card } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Switch } from "@/components/ui/switch" import { QrCode, X, ChevronDown, Plus, Maximize2, Upload, Download, Settings, Minus } from "lucide-react" import { TooltipProvider } from "@/components/ui/tooltip" import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from "@/components/ui/table" import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription, } from "@/components/ui/dialog" import { toast } from "@/components/ui/use-toast" import { useSearchParams } from "next/navigation" interface BasicSettingsProps { formData: any onChange: (data: any) => void onNext?: () => void scenarios: any[] } interface Account { id: string nickname: string avatar: string } interface Material { id: string name: string type: string preview: string } interface PosterSectionProps { materials: Material[] selectedMaterials: Material[] onUpload: () => void onSelect: (material: Material) => void uploading: boolean fileInputRef: React.RefObject onFileChange: (event: React.ChangeEvent) => void onPreview: (url: string) => void onRemove: (id: string) => void } interface OrderSectionProps { materials: Material[] onUpload: () => void uploading: boolean fileInputRef: React.RefObject onFileChange: (event: React.ChangeEvent) => void } interface DouyinSectionProps { materials: Material[] onUpload: () => void uploading: boolean fileInputRef: React.RefObject onFileChange: (event: React.ChangeEvent) => void } interface PlaceholderSectionProps { title: string } const posterTemplates = [ { id: "poster-1", name: "点击领取", preview: "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/%E7%82%B9%E5%87%BB%E9%A2%86%E5%8F%961-tipd1HI7da6qooY5NkhxQnXBnT5LGU.gif", }, { id: "poster-2", name: "点击合作", preview: "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/%E7%82%B9%E5%87%BB%E5%90%88%E4%BD%9C-LPlMdgxtvhqCSr4IM1bZFEFDBF3ztI.gif", }, { id: "poster-3", name: "点击咨询", preview: "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/%E7%82%B9%E5%87%BB%E5%92%A8%E8%AF%A2-FTiyAMAPop2g9LvjLOLDz0VwPg3KVu.gif", }, { id: "poster-4", name: "点击签到", preview: "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/%E7%82%B9%E5%87%BB%E7%AD%BE%E5%88%B0-94TZIkjLldb4P2jTVlI6MkSDg0NbXi.gif", }, { id: "poster-5", name: "点击了解", preview: "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/%E7%82%B9%E5%87%BB%E4%BA%86%E8%A7%A3-6GCl7mQVdO4WIiykJyweSubLsTwj71.gif", }, { id: "poster-6", name: "点击报名", preview: "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/%E7%82%B9%E5%87%BB%E6%8A%A5%E5%90%8D-Mj0nnva0BiASeDAIhNNaRRAbjPgjEj.gif", }, ] const generateRandomAccounts = (count: number): Account[] => { return Array.from({ length: count }, (_, index) => ({ id: `account-${index + 1}`, nickname: `账号-${Math.random().toString(36).substring(2, 7)}`, avatar: `/placeholder.svg?height=40&width=40&text=${index + 1}`, })) } const generatePosterMaterials = (): Material[] => { return posterTemplates.map((template) => ({ id: template.id, name: template.name, type: "poster", preview: template.preview, })) } // 颜色池分为更浅的未选中和深色的选中 const tagColorPoolLight = [ "bg-blue-100 text-blue-600", "bg-green-100 text-green-600", "bg-purple-100 text-purple-600", "bg-red-100 text-red-600", "bg-orange-100 text-orange-600", "bg-yellow-100 text-yellow-600", "bg-gray-100 text-gray-600", "bg-pink-100 text-pink-600", ]; const tagColorPoolDark = [ "bg-blue-100 text-blue-600", "bg-green-100 text-green-600", "bg-purple-100 text-purple-600", "bg-red-100 text-red-600", "bg-orange-100 text-orange-600", "bg-yellow-100 text-yellow-600", "bg-gray-100 text-gray-600", "bg-pink-100 text-pink-600", ]; function getTagColorIdx(tag: string) { let hash = 0; for (let i = 0; i < tag.length; i++) { hash = tag.charCodeAt(i) + ((hash << 5) - hash); } return Math.abs(hash) % tagColorPoolLight.length; } // Section组件示例 const PosterSection = ({ materials, selectedMaterials, onUpload, onSelect, uploading, fileInputRef, onFileChange, onPreview, onRemove }: PosterSectionProps) => (
{materials.map((material) => (
m.id === material.id) ? "ring-2 ring-blue-600" : "hover:ring-2 hover:ring-blue-600" }`} onClick={() => onSelect(material)} > {material.name}
{material.name}
))}
{selectedMaterials.length > 0 && (
{selectedMaterials[0].name} onPreview(selectedMaterials[0].preview)} />
)}
) const OrderSection = ({ materials, onUpload, uploading, fileInputRef, onFileChange }: OrderSectionProps) => (
{materials.map((item: Material) => (
{item.name}
{item.name}
))}
) const DouyinSection = ({ materials, onUpload, uploading, fileInputRef, onFileChange }: DouyinSectionProps) => (
{materials.map((item: Material) => (
{item.name}
{item.name}
))}
) const PlaceholderSection = ({ title }: PlaceholderSectionProps) => (

暂无内容

) export function BasicSettings({ formData, onChange, onNext, scenarios, loadingScenes, planNameEdited }: BasicSettingsProps & { loadingScenes?: boolean, planNameEdited?: boolean }) { const [isAccountDialogOpen, setIsAccountDialogOpen] = useState(false) const [isMaterialDialogOpen, setIsMaterialDialogOpen] = useState(false) const [isQRCodeOpen, setIsQRCodeOpen] = useState(false) const [isPreviewOpen, setIsPreviewOpen] = useState(false) const [isPhoneSettingsOpen, setIsPhoneSettingsOpen] = useState(false) const [previewImage, setPreviewImage] = useState("") const [accounts] = useState(generateRandomAccounts(50)) const [materials, setMaterials] = useState(generatePosterMaterials()) const [selectedAccounts, setSelectedAccounts] = useState( formData.accounts?.length > 0 ? formData.accounts : [], ) const [selectedMaterials, setSelectedMaterials] = useState( formData.materials?.length > 0 ? formData.materials : [], ) const [showAllScenarios, setShowAllScenarios] = useState(false) const [isImportDialogOpen, setIsImportDialogOpen] = useState(false) const [importedTags, setImportedTags] = useState< Array<{ phone: string wechat: string source?: string orderAmount?: number orderDate?: string }> >(formData.importedTags || []) const [selectedScenarioTags, setSelectedScenarioTags] = useState(formData.scenarioTags || []) const [customTagInput, setCustomTagInput] = useState("") const [customTags, setCustomTags] = useState(formData.customTags || []) // 初始化电话获客设置 const [phoneSettings, setPhoneSettings] = useState({ autoAdd: formData.phoneSettings?.autoAdd ?? true, speechToText: formData.phoneSettings?.speechToText ?? true, questionExtraction: formData.phoneSettings?.questionExtraction ?? true, }) const [selectedPhoneTags, setSelectedPhoneTags] = useState(formData.phoneTags || []) const [phoneCallType, setPhoneCallType] = useState(formData.phoneCallType || "both") const fileInputRef = useRef(null) const [uploadingPoster, setUploadingPoster] = useState(false) // 新增不同场景的materials和上传逻辑 const [orderMaterials, setOrderMaterials] = useState([]) const [douyinMaterials, setDouyinMaterials] = useState([]) const orderFileInputRef = useRef(null) const douyinFileInputRef = useRef(null) const [uploadingOrder, setUploadingOrder] = useState(false) const [uploadingDouyin, setUploadingDouyin] = useState(false) // 新增小程序和链接封面上传相关state和ref const [miniAppCover, setMiniAppCover] = useState(formData.miniAppCover || "") const [uploadingMiniAppCover, setUploadingMiniAppCover] = useState(false) const miniAppFileInputRef = useRef(null) const [linkCover, setLinkCover] = useState(formData.linkCover || "") const [uploadingLinkCover, setUploadingLinkCover] = useState(false) const linkFileInputRef = useRef(null) const searchParams = useSearchParams() const type = searchParams.get("type") // 初始化时,如果有type参数且scenarios已加载,立即设置选中状态 useEffect(() => { if (!loadingScenes && scenarios.length > 0 && type && !planNameEdited) { const targetScenario = scenarios.find(scene => String(scene.id) === String(type)); if (targetScenario) { onChange({ sceneId: String(type), scenario: String(type), planName: targetScenario.name.includes('电话') ? `电话获客${new Date().toLocaleDateString("zh-CN").replace(/\//g, "")}` : targetScenario.name + new Date().toLocaleDateString("zh-CN").replace(/\//g, "") }); } } }, [loadingScenes, scenarios, type, planNameEdited]); // 添加 useEffect 来处理初始化的海报数据 useEffect(() => { if (formData.posters && Array.isArray(formData.posters)) { const validPosters = formData.posters.filter((poster: any) => poster && typeof poster === 'object' && 'id' in poster && 'name' in poster && 'type' in poster && 'preview' in poster ) setSelectedMaterials(validPosters.map((poster: any) => ({ id: poster.id, name: poster.name, type: poster.type || 'poster', // 确保有 type 字段 preview: poster.preview }))) } }, [formData.posters]) // 展示所有场景 const displayedScenarios = scenarios const handleTagToggle = (tag: string) => { const newTags = selectedPhoneTags.includes(tag) ? selectedPhoneTags.filter((t) => t !== tag) : [...selectedPhoneTags, tag] setSelectedPhoneTags(newTags) onChange({ ...formData, phoneTags: newTags }) } const handleCallTypeChange = (type: string) => { setPhoneCallType(type) onChange({ ...formData, phoneCallType: type }) } useEffect(() => { if (!formData.scenario) { onChange({ ...formData, scenario: "haibao" }) } if (!formData.planName) { if (formData.materials?.length > 0) { const today = new Date().toLocaleDateString("zh-CN").replace(/\//g, "") onChange({ ...formData, planName: `海报${today}` }) } else { onChange({ ...formData, planName: "场景" }) } } }, [formData, onChange]) const handleScenarioSelect = (scenarioId: string) => { const targetScenario = scenarios.find(s => String(s.id) === String(scenarioId)); if (targetScenario) { if (scenarioId === "phone") { const today = new Date().toLocaleDateString("zh-CN").replace(/\//g, ""); onChange({ sceneId: String(scenarioId), scenario: String(scenarioId), planName: `电话获客${today}` }); } else { onChange({ sceneId: String(scenarioId), scenario: String(scenarioId), planName: targetScenario.name + new Date().toLocaleDateString("zh-CN").replace(/\//g, "") }); } } } const handleScenarioTagToggle = (tag: string) => { const newTags = selectedScenarioTags.includes(tag) ? selectedScenarioTags.filter((t) => t !== tag) : [...selectedScenarioTags, tag] setSelectedScenarioTags(newTags) onChange({ ...formData, scenarioTags: newTags }) } const handleAddCustomTag = () => { if (!customTagInput.trim()) return const newTag = customTagInput.trim() if (customTags.includes(newTag)) return const updatedCustomTags = [...customTags, newTag] setCustomTags(updatedCustomTags) setCustomTagInput("") onChange({ ...formData, customTags: updatedCustomTags }) } const handleRemoveCustomTag = (tag: string) => { const updatedCustomTags = customTags.filter((t) => t !== tag) setCustomTags(updatedCustomTags) onChange({ ...formData, customTags: updatedCustomTags }) // 同时从选中标签中移除 const updatedSelectedTags = selectedScenarioTags.filter((t) => t !== tag) setSelectedScenarioTags(updatedSelectedTags) onChange({ ...formData, scenarioTags: updatedSelectedTags, customTags: updatedCustomTags }) } const handleAccountSelect = (account: Account) => { const updatedAccounts = [...selectedAccounts, account] setSelectedAccounts(updatedAccounts) onChange({ ...formData, accounts: updatedAccounts }) } const handleMaterialSelect = (material: Material) => { const updatedMaterials = [material] setSelectedMaterials(updatedMaterials) onChange({ ...formData, materials: updatedMaterials }) setIsMaterialDialogOpen(false) const today = new Date().toLocaleDateString("zh-CN").replace(/\//g, "") onChange({ ...formData, planName: `海报${today}`, materials: updatedMaterials }) } const handleRemoveAccount = (accountId: string) => { const updatedAccounts = selectedAccounts.filter((a) => a.id !== accountId) setSelectedAccounts(updatedAccounts) onChange({ ...formData, accounts: updatedAccounts }) } const handleRemoveMaterial = (materialId: string) => { const updatedMaterials = selectedMaterials.filter((m) => m.id !== materialId) setSelectedMaterials(updatedMaterials) onChange({ ...formData, materials: updatedMaterials }) } const handlePreviewImage = (imageUrl: string) => { setPreviewImage(imageUrl) setIsPreviewOpen(true) } const handleFileImport = (event: React.ChangeEvent) => { const file = event.target.files?.[0] if (file) { const reader = new FileReader() reader.onload = (e) => { try { const content = e.target?.result as string const rows = content.split("\n").filter((row) => row.trim()) const tags = rows.slice(1).map((row) => { const [phone, wechat, source, orderAmount, orderDate] = row.split(",") return { phone: phone.trim(), wechat: wechat.trim(), source: source?.trim(), orderAmount: orderAmount ? Number(orderAmount) : undefined, orderDate: orderDate?.trim(), } }) setImportedTags(tags) onChange({ ...formData, importedTags: tags }) } catch (error) { console.error("导入失败:", error) } } reader.readAsText(file) } } const handleDownloadTemplate = () => { const template = "电话号码,微信号,来源,订单金额,下单日期\n13800138000,wxid_123,抖音,99.00,2024-03-03" const blob = new Blob([template], { type: "text/csv" }) const url = window.URL.createObjectURL(blob) const a = document.createElement("a") a.href = url a.download = "订单导入模板.csv" document.body.appendChild(a) a.click() document.body.removeChild(a) window.URL.revokeObjectURL(url) } const handlePhoneSettingsUpdate = () => { onChange({ ...formData, phoneSettings }) setIsPhoneSettingsOpen(false) } const currentScenario = useMemo( () => scenarios.find((s: any) => String(s.id) === String(formData.scenario)), [scenarios, formData.scenario] ) // 调试日志 useEffect(() => { console.log('formData:', formData, 'currentScenario:', currentScenario); }, [formData, currentScenario]); const handleUploadPoster = () => { fileInputRef.current?.click() } const handlePosterFileChange = async (event: React.ChangeEvent) => { const file = event.target.files?.[0] if (!file) return if (!file.type.startsWith('image/')) { toast({ title: '请选择图片文件', variant: 'destructive' }) return } setUploadingPoster(true) const formData = new FormData() formData.append('file', file) try { const token = localStorage.getItem('token') const headers: HeadersInit = {} if (token) headers['Authorization'] = `Bearer ${token}` const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/v1/attachment/upload`, { method: 'POST', headers, body: formData, }) const result = await response.json() if (result.code === 200 && result.data?.url) { const newPoster = { id: `custom_${Date.now()}`, name: result.data.name || '自定义海报', preview: result.data.url, } setMaterials(prev => [newPoster, ...prev]) toast({ title: '上传成功', description: '海报已添加' }) } else { toast({ title: '上传失败', description: result.msg || '请重试', variant: 'destructive' }) } } catch (e: any) { toast({ title: '上传失败', description: e?.message || '请重试', variant: 'destructive' }) } finally { setUploadingPoster(false) if (fileInputRef.current) fileInputRef.current.value = '' } } const handleUploadOrder = () => { orderFileInputRef.current?.click() } const handleUploadDouyin = () => { douyinFileInputRef.current?.click() } const handleOrderFileChange = async (event: React.ChangeEvent) => { const file = event.target.files?.[0] if (!file) return setUploadingOrder(true) const formData = new FormData() formData.append('file', file) try { const token = localStorage.getItem('token') const headers: HeadersInit = {} if (token) headers['Authorization'] = `Bearer ${token}` const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/v1/attachment/upload`, { method: 'POST', headers, body: formData, }) const result = await response.json() if (result.code === 200 && result.data?.url) { const newItem = { id: `order_${Date.now()}`, name: result.data.name || '自定义订单', preview: result.data.url } setOrderMaterials(prev => [newItem, ...prev]) toast({ title: '上传成功', description: '订单模板已添加' }) } else { toast({ title: '上传失败', description: result.msg || '请重试', variant: 'destructive' }) } } catch (e: any) { toast({ title: '上传失败', description: e?.message || '请重试', variant: 'destructive' }) } finally { setUploadingOrder(false) if (orderFileInputRef.current) orderFileInputRef.current.value = '' } } const handleDouyinFileChange = async (event: React.ChangeEvent) => { const file = event.target.files?.[0] if (!file) return setUploadingDouyin(true) const formData = new FormData() formData.append('file', file) try { const token = localStorage.getItem('token') const headers: HeadersInit = {} if (token) headers['Authorization'] = `Bearer ${token}` const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/v1/attachment/upload`, { method: 'POST', headers, body: formData, }) const result = await response.json() if (result.code === 200 && result.data?.url) { const newItem = { id: `douyin_${Date.now()}`, name: result.data.name || '自定义抖音内容', preview: result.data.url } setDouyinMaterials(prev => [newItem, ...prev]) toast({ title: '上传成功', description: '抖音内容已添加' }) } else { toast({ title: '上传失败', description: result.msg || '请重试', variant: 'destructive' }) } } catch (e: any) { toast({ title: '上传失败', description: e?.message || '请重试', variant: 'destructive' }) } finally { setUploadingDouyin(false) if (douyinFileInputRef.current) douyinFileInputRef.current.value = '' } } // 上传小程序封面 const handleUploadMiniAppCover = () => { miniAppFileInputRef.current?.click() } const handleMiniAppFileChange = async (event: React.ChangeEvent) => { const file = event.target.files?.[0] if (!file) return setUploadingMiniAppCover(true) const formDataObj = new FormData() formDataObj.append('file', file) try { const token = localStorage.getItem('token') const headers: HeadersInit = {} if (token) headers['Authorization'] = `Bearer ${token}` const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/v1/attachment/upload`, { method: 'POST', headers, body: formDataObj, }) const result = await response.json() if (result.code === 200 && result.data?.url) { setMiniAppCover(result.data.url) onChange({ ...formData, miniAppCover: result.data.url }) toast({ title: '上传成功', description: '小程序封面已添加' }) } else { toast({ title: '上传失败', description: result.msg || '请重试', variant: 'destructive' }) } } catch (e: any) { toast({ title: '上传失败', description: e?.message || '请重试', variant: 'destructive' }) } finally { setUploadingMiniAppCover(false) if (miniAppFileInputRef.current) miniAppFileInputRef.current.value = '' } } // 上传链接封面 const handleUploadLinkCover = () => { linkFileInputRef.current?.click() } const handleLinkFileChange = async (event: React.ChangeEvent) => { const file = event.target.files?.[0] if (!file) return setUploadingLinkCover(true) const formDataObj = new FormData() formDataObj.append('file', file) try { const token = localStorage.getItem('token') const headers: HeadersInit = {} if (token) headers['Authorization'] = `Bearer ${token}` const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/v1/attachment/upload`, { method: 'POST', headers, body: formDataObj, }) const result = await response.json() if (result.code === 200 && result.data?.url) { setLinkCover(result.data.url) onChange({ ...formData, linkCover: result.data.url }) toast({ title: '上传成功', description: '链接封面已添加' }) } else { toast({ title: '上传失败', description: result.msg || '请重试', variant: 'destructive' }) } } catch (e: any) { toast({ title: '上传失败', description: e?.message || '请重试', variant: 'destructive' }) } finally { setUploadingLinkCover(false) if (linkFileInputRef.current) linkFileInputRef.current.value = '' } } const renderSceneExtra = () => { switch (currentScenario?.name) { case "海报获客": return ( ) case "订单获客": return ( ) case "抖音获客": return ( ) case "小红书获客": return case "电话获客": return case "公众号获客": return case "微信群获客": return case "付款码获客": return case "API获客": return case "小程序获客": return case "链接获客": return default: return null } } // 新增小程序和链接场景的功能区 const MiniAppSection = () => (
{miniAppCover && 小程序封面}
) const LinkSection = () => (
{linkCover && 链接封面}
) return (
{displayedScenarios.map((scenario) => ( ))}
{!showAllScenarios && ( )}
onChange({ ...formData, name: e.target.value })} placeholder="请输入计划名称" className="w-full" />
{currentScenario && (
{(currentScenario.scenarioTags || []).map((tag: string) => { const idx = getTagColorIdx(tag); const selected = selectedScenarioTags.includes(tag); return (
handleScenarioTagToggle(tag)} > {tag}
); })}
{customTags.length > 0 && (
{customTags.map((tag) => (
handleScenarioTagToggle(tag)} > {tag}
))}
)}
setCustomTagInput(e.target.value)} placeholder="输入自定义标签名称" className="flex-1" maxLength={8} onKeyPress={(e) => { if (e.key === "Enter") { handleAddCustomTag() } }} />
{selectedScenarioTags.length > 0 && (
已选择 {selectedScenarioTags.length} 个标签
)}
)} {currentScenario && ( <> {currentScenario.type === "social" && currentScenario.id !== "phone" && (
{selectedAccounts.length > 0 && (
{selectedAccounts.map((account) => (
{account.nickname} {account.nickname}
))}
)}
)} {String(formData.scenario) === "phone" && (
自动添加客户
{phoneSettings.autoAdd ? "已开启" : "已关闭"}
语音转文字
{phoneSettings.speechToText ? "已开启" : "已关闭"}
问题提取
{phoneSettings.questionExtraction ? "已开启" : "已关闭"}

提示:电话获客功能将自动记录来电信息,并根据设置执行相应操作

)} {String(formData.scenario) === "phone" && ( <>
handleCallTypeChange(phoneCallType === "inbound" ? "both" : "outbound")} >
发起外呼
主动向客户发起电话
handleCallTypeChange(phoneCallType === "outbound" ? "both" : "inbound")} >
接收来电
接听客户的来电
{(currentScenario.scenarioTags || []).map((tag: string) => { const idx = getTagColorIdx(tag); const selected = selectedPhoneTags.includes(tag); return (
handleTagToggle(tag)} > {tag}
); })}
)} {((currentScenario?.type === "material" || currentScenario?.name === "海报获客" || String(currentScenario?.id) === "1") && (
{renderSceneExtra()}
))} {String(currentScenario?.id) === "order" && (
{importedTags.length > 0 && (

已导入 {importedTags.length} 条数据

电话号码 微信号 来源 订单金额 {importedTags.slice(0, 5).map((tag, index) => ( {tag.phone} {tag.wechat} {tag.source} {tag.orderAmount} ))} {importedTags.length > 5 && ( 还有 {importedTags.length - 5} 条数据未显示 )}
)}
)} {String(formData.scenario) === "weixinqun" && ( <>
自动欢迎新成员
新成员入群时自动发送欢迎消息
onChange({ ...formData, autoWelcome: checked })} />
群活跃度监控
监控群聊活跃度并自动互动
onChange({ ...formData, activityMonitor: checked })} />
{formData.syncCount || 3} 条内容到群
建议每日推送3-5条内容,避免过度打扰群成员
{["上午 9:00-12:00", "下午 14:00-17:00", "晚上 19:00-21:00"].map((time, index) => (
{ const currentTimes = formData.pushTimes || [] const newTimes = currentTimes.includes(index) ? currentTimes.filter((t: number) => t !== index) : [...currentTimes, index] onChange({ ...formData, pushTimes: newTimes }) }} > {time}
))}
)} )}
onChange({ ...formData, enabled: checked })} />
选择账号
{accounts.map((account) => (
handleAccountSelect(account)} > {account.nickname} {selectedAccounts.find((a) => a.id === account.id) && (
)}
))}
绑定账号
二维码

请用相应的APP扫码

海报预览
预览
电话获客设置 配置电话获客的自动化功能,提高获客效率

来电后自动将客户添加为微信好友

推荐:开启此功能可提高转化率约30%

setPhoneSettings({ ...phoneSettings, autoAdd: checked })} className="data-[state=checked]:bg-blue-600" />

自动将通话内容转换为文字记录

支持普通话、粤语等多种方言识别

setPhoneSettings({ ...phoneSettings, speechToText: checked })} className="data-[state=checked]:bg-blue-600" />

自动从通话中提取客户的首句问题

AI智能识别客户意图,提高回复精准度

setPhoneSettings({ ...phoneSettings, questionExtraction: checked })} className="data-[state=checked]:bg-blue-600" />
导入订单标签
电话号码 微信号 来源 订单金额 下单日期 {importedTags.map((tag, index) => ( {tag.phone} {tag.wechat} {tag.source} {tag.orderAmount} {tag.orderDate} ))}
) }