feat: 本次提交更新内容如下

场景获客列表搞定
This commit is contained in:
笔记本里的永平
2025-07-07 17:08:27 +08:00
parent 6543da9167
commit 5ff15472f5
352 changed files with 24040 additions and 18575 deletions

View File

@@ -0,0 +1,259 @@
"use client"
import { useState } from "react"
import { Card, CardContent } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Label } from "@/components/ui/label"
import { Input } from "@/components/ui/input"
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { ScrollArea } from "@/components/ui/scroll-area"
import { Checkbox } from "@/components/ui/checkbox"
import { Search, Users, User, MessageCircle, AlertCircle } from "lucide-react"
import { Alert, AlertDescription } from "@/components/ui/alert"
import Image from "next/image"
import type { ContentLibraryFormData } from "../page"
interface SourceSelectionProps {
formData: ContentLibraryFormData
updateFormData: (field: string, value: any) => void
onNext: () => void
onPrevious: () => void
}
// 模拟数据
const mockFriends = [
{ id: "f1", name: "张三", type: "friend", avatar: "/placeholder.svg?height=40&width=40" },
{ id: "f2", name: "李四", type: "friend", avatar: "/placeholder.svg?height=40&width=40" },
{ id: "f3", name: "王五", type: "friend", avatar: "/placeholder.svg?height=40&width=40" },
{ id: "f4", name: "赵六", type: "friend", avatar: "/placeholder.svg?height=40&width=40" },
{ id: "f5", name: "钱七", type: "friend", avatar: "/placeholder.svg?height=40&width=40" },
]
const mockGroups = [
{ id: "g1", name: "产品讨论群", type: "group", avatar: "/placeholder.svg?height=40&width=40" },
{ id: "g2", name: "市场营销群", type: "group", avatar: "/placeholder.svg?height=40&width=40" },
{ id: "g3", name: "技术交流群", type: "group", avatar: "/placeholder.svg?height=40&width=40" },
{ id: "g4", name: "客户服务群", type: "group", avatar: "/placeholder.svg?height=40&width=40" },
]
const mockOfficialAccounts = [
{ id: "o1", name: "科技前沿", type: "official", avatar: "/placeholder.svg?height=40&width=40" },
{ id: "o2", name: "营销之道", type: "official", avatar: "/placeholder.svg?height=40&width=40" },
{ id: "o3", name: "数据分析", type: "official", avatar: "/placeholder.svg?height=40&width=40" },
]
export function SourceSelection({ formData, updateFormData, onNext, onPrevious }: SourceSelectionProps) {
const [activeTab, setActiveTab] = useState<string>(
formData.type === "friends" ? "friends" : formData.type === "groups" ? "groups" : "all",
)
const [searchQuery, setSearchQuery] = useState("")
const [selectedSources, setSelectedSources] = useState<
{
id: string
name: string
type: string
avatar?: string
}[]
>(formData.sources)
// 根据内容库类型和当前标签过滤可选来源
const getFilteredSources = () => {
let sources = []
if (activeTab === "friends" || activeTab === "all") {
sources = [...sources, ...mockFriends]
}
if (activeTab === "groups" || activeTab === "all") {
sources = [...sources, ...mockGroups]
}
if (activeTab === "official" || activeTab === "all") {
sources = [...sources, ...mockOfficialAccounts]
}
// 应用搜索过滤
if (searchQuery) {
sources = sources.filter((source) => source.name.toLowerCase().includes(searchQuery.toLowerCase()))
}
return sources
}
const handleSourceToggle = (source: any) => {
const isSelected = selectedSources.some((s) => s.id === source.id)
if (isSelected) {
setSelectedSources(selectedSources.filter((s) => s.id !== source.id))
} else {
setSelectedSources([...selectedSources, source])
}
}
const handleNext = () => {
updateFormData("sources", selectedSources)
onNext()
}
const filteredSources = getFilteredSources()
const isFormValid = selectedSources.length > 0
return (
<div className="space-y-6">
<Card>
<CardContent className="pt-6">
<div className="space-y-6">
<div>
<h2 className="text-lg font-medium"></h2>
<p className="text-sm text-gray-500 mt-1"></p>
</div>
{formData.type !== "mixed" && (
<Alert variant="info" className="bg-blue-50 text-blue-700 border-blue-200">
<AlertCircle className="h-4 w-4" />
<AlertDescription>
{formData.type === "friends"
? "您已选择微信好友类型的内容库,请选择需要收集内容的微信好友。"
: formData.type === "groups"
? "您已选择微信群类型的内容库,请选择需要收集内容的微信群。"
: "您已选择朋友圈类型的内容库,请选择需要收集内容的来源。"}
</AlertDescription>
</Alert>
)}
<div className="space-y-4">
<div className="relative">
<Search className="absolute left-3 top-2.5 h-4 w-4 text-gray-400" />
<Input
placeholder="搜索来源..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-9"
/>
</div>
<Tabs defaultValue={activeTab} value={activeTab} onValueChange={setActiveTab}>
<TabsList className="grid w-full grid-cols-4">
<TabsTrigger value="all"></TabsTrigger>
<TabsTrigger value="friends" disabled={formData.type === "groups"}>
<User className="h-4 w-4 mr-1" />
</TabsTrigger>
<TabsTrigger value="groups" disabled={formData.type === "friends"}>
<Users className="h-4 w-4 mr-1" />
</TabsTrigger>
<TabsTrigger value="official">
<MessageCircle className="h-4 w-4 mr-1" />
</TabsTrigger>
</TabsList>
<div className="mt-4 border rounded-md">
<div className="p-3 border-b bg-gray-50">
<div className="flex items-center justify-between">
<span className="text-sm font-medium"></span>
<span className="text-xs text-gray-500"> {selectedSources.length} </span>
</div>
</div>
<ScrollArea className="h-[300px]">
<div className="p-3 space-y-2">
{filteredSources.length > 0 ? (
filteredSources.map((source) => {
const isSelected = selectedSources.some((s) => s.id === source.id)
return (
<div
key={source.id}
className={`flex items-center justify-between p-2 border rounded-md ${
isSelected ? "bg-blue-50 border-blue-200" : "bg-white"
}`}
>
<div className="flex items-center space-x-3">
<Checkbox
id={`source-${source.id}`}
checked={isSelected}
onCheckedChange={() => handleSourceToggle(source)}
/>
<div className="flex items-center space-x-2">
<div className="relative w-8 h-8 rounded-full overflow-hidden">
<Image
src={source.avatar || "/placeholder.svg"}
alt={source.name}
fill
className="object-cover"
/>
</div>
<div>
<p className="text-sm font-medium">{source.name}</p>
<p className="text-xs text-gray-500">
{source.type === "friend"
? "微信好友"
: source.type === "group"
? "微信群"
: "公众号"}
</p>
</div>
</div>
</div>
</div>
)
})
) : (
<div className="flex flex-col items-center justify-center py-8 text-gray-500">
<p></p>
<p className="text-sm mt-1"></p>
</div>
)}
</div>
</ScrollArea>
</div>
</Tabs>
{selectedSources.length > 0 && (
<div className="mt-4">
<Label className="text-base mb-2 block"></Label>
<div className="flex flex-wrap gap-2">
{selectedSources.map((source) => (
<div
key={source.id}
className="flex items-center space-x-2 bg-blue-50 border border-blue-200 rounded-md px-3 py-1"
>
<div className="relative w-5 h-5 rounded-full overflow-hidden">
<Image
src={source.avatar || "/placeholder.svg"}
alt={source.name}
fill
className="object-cover"
/>
</div>
<span className="text-sm">{source.name}</span>
<Button
variant="ghost"
size="sm"
className="h-5 w-5 p-0 rounded-full"
onClick={() => handleSourceToggle(source)}
>
×
</Button>
</div>
))}
</div>
</div>
)}
</div>
</div>
</CardContent>
</Card>
<div className="flex justify-between">
<Button variant="outline" onClick={onPrevious}>
</Button>
<Button onClick={handleNext} disabled={!isFormValid}>
</Button>
</div>
</div>
)
}