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,150 @@
"use client"
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 { Textarea } from "@/components/ui/textarea"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
import { Switch } from "@/components/ui/switch"
import { HelpCircle } from "lucide-react"
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
import type { ContentLibraryFormData } from "../page"
interface BasicSettingsProps {
formData: ContentLibraryFormData
updateFormData: (field: string, value: any) => void
onNext: () => void
}
export function BasicSettings({ formData, updateFormData, onNext }: BasicSettingsProps) {
const isFormValid = formData.name.trim() !== ""
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>
<div className="space-y-4">
<div>
<div className="flex items-center justify-between">
<Label htmlFor="name" className="text-base">
</Label>
<span className="text-sm text-gray-500"></span>
</div>
<Input
id="name"
value={formData.name}
onChange={(e) => updateFormData("name", e.target.value)}
placeholder="请输入内容库名称"
className="mt-1.5"
/>
{formData.name.trim() === "" && <p className="text-sm text-red-500 mt-1"></p>}
</div>
<div>
<Label htmlFor="description" className="text-base">
</Label>
<Textarea
id="description"
value={formData.description}
onChange={(e) => updateFormData("description", e.target.value)}
placeholder="请输入内容库描述(选填)"
className="mt-1.5 min-h-[100px]"
/>
</div>
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label className="text-base"></Label>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<HelpCircle className="h-4 w-4 text-gray-400 cursor-help" />
</TooltipTrigger>
<TooltipContent className="max-w-xs">
<p className="text-sm"></p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<RadioGroup
value={formData.type}
onValueChange={(value) => updateFormData("type", value)}
className="mt-2"
>
<div className="flex items-start space-x-2 p-3 border rounded-md">
<RadioGroupItem value="friends" id="friends" className="mt-1" />
<div className="flex-1">
<Label htmlFor="friends" className="font-medium">
</Label>
<p className="text-sm text-gray-500 mt-1"></p>
</div>
</div>
<div className="flex items-start space-x-2 p-3 border rounded-md mt-2">
<RadioGroupItem value="groups" id="groups" className="mt-1" />
<div className="flex-1">
<Label htmlFor="groups" className="font-medium">
</Label>
<p className="text-sm text-gray-500 mt-1"></p>
</div>
</div>
<div className="flex items-start space-x-2 p-3 border rounded-md mt-2">
<RadioGroupItem value="moments" id="moments" className="mt-1" />
<div className="flex-1">
<Label htmlFor="moments" className="font-medium">
</Label>
<p className="text-sm text-gray-500 mt-1"></p>
</div>
</div>
<div className="flex items-start space-x-2 p-3 border rounded-md mt-2">
<RadioGroupItem value="mixed" id="mixed" className="mt-1" />
<div className="flex-1">
<Label htmlFor="mixed" className="font-medium">
</Label>
<p className="text-sm text-gray-500 mt-1"></p>
</div>
</div>
</RadioGroup>
</div>
<div className="flex items-center justify-between p-3 border rounded-md">
<div>
<Label htmlFor="enabled" className="font-medium">
</Label>
<p className="text-sm text-gray-500 mt-1"></p>
</div>
<Switch
id="enabled"
checked={formData.enabled}
onCheckedChange={(checked) => updateFormData("enabled", checked)}
/>
</div>
</div>
</div>
</CardContent>
</Card>
<div className="flex justify-end">
<Button onClick={onNext} disabled={!isFormValid}>
</Button>
</div>
</div>
)
}

View File

@@ -0,0 +1,150 @@
"use client"
import { Card, CardContent } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Label } from "@/components/ui/label"
import { Checkbox } from "@/components/ui/checkbox"
import { Switch } from "@/components/ui/switch"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { HelpCircle, FileText, ImageIcon, Video, LinkIcon, FileAudio } from "lucide-react"
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
import type { ContentLibraryFormData } from "../page"
interface ContentSettingsProps {
formData: ContentLibraryFormData
updateFormData: (field: string, value: any) => void
onNext: () => void
onPrevious: () => void
}
export function ContentSettings({ formData, updateFormData, onNext, onPrevious }: ContentSettingsProps) {
const contentTypes = [
{ id: "text", name: "文本", icon: FileText, description: "收集文本内容,如聊天记录、文章等" },
{ id: "image", name: "图片", icon: ImageIcon, description: "收集图片内容,如照片、截图等" },
{ id: "video", name: "视频", icon: Video, description: "收集视频内容,如短视频、直播回放等" },
{ id: "link", name: "链接", icon: LinkIcon, description: "收集链接内容,如网页链接、小程序链接等" },
{ id: "audio", name: "音频", icon: FileAudio, description: "收集音频内容,如语音消息、音乐等" },
]
const handleContentTypeToggle = (type: string) => {
const currentTypes = [...formData.contentTypes]
if (currentTypes.includes(type)) {
updateFormData(
"contentTypes",
currentTypes.filter((t) => t !== type),
)
} else {
updateFormData("contentTypes", [...currentTypes, type])
}
}
const handleAutoSyncChange = (checked: boolean) => {
updateFormData("autoSync", checked)
}
const handleSyncIntervalChange = (value: string) => {
updateFormData("syncInterval", Number.parseInt(value))
}
const isFormValid = formData.contentTypes.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>
<div className="space-y-4">
<div>
<div className="flex items-center justify-between">
<Label className="text-base"></Label>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<HelpCircle className="h-4 w-4 text-gray-400 cursor-help" />
</TooltipTrigger>
<TooltipContent className="max-w-xs">
<p className="text-sm"></p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<div className="mt-2 space-y-2">
{contentTypes.map((type) => (
<div
key={type.id}
className={`flex items-start space-x-2 p-3 border rounded-md ${
formData.contentTypes.includes(type.id) ? "bg-blue-50 border-blue-200" : "bg-white"
}`}
>
<Checkbox
id={`content-type-${type.id}`}
checked={formData.contentTypes.includes(type.id)}
onCheckedChange={() => handleContentTypeToggle(type.id)}
className="mt-1"
/>
<div className="flex-1">
<Label htmlFor={`content-type-${type.id}`} className="font-medium flex items-center">
<type.icon className="h-4 w-4 mr-2" />
{type.name}
</Label>
<p className="text-sm text-gray-500 mt-1">{type.description}</p>
</div>
</div>
))}
</div>
{formData.contentTypes.length === 0 && (
<p className="text-sm text-red-500 mt-1"></p>
)}
</div>
<div className="flex items-center justify-between p-3 border rounded-md">
<div>
<Label htmlFor="auto-sync" className="font-medium">
</Label>
<p className="text-sm text-gray-500 mt-1"></p>
</div>
<Switch id="auto-sync" checked={formData.autoSync} onCheckedChange={handleAutoSyncChange} />
</div>
{formData.autoSync && (
<div>
<Label htmlFor="sync-interval" className="text-base">
</Label>
<Select value={formData.syncInterval.toString()} onValueChange={handleSyncIntervalChange}>
<SelectTrigger id="sync-interval" className="mt-1.5">
<SelectValue placeholder="选择同步间隔" />
</SelectTrigger>
<SelectContent>
<SelectItem value="1">1</SelectItem>
<SelectItem value="6">6</SelectItem>
<SelectItem value="12">12</SelectItem>
<SelectItem value="24"></SelectItem>
<SelectItem value="168"></SelectItem>
</SelectContent>
</Select>
</div>
)}
</div>
</div>
</CardContent>
</Card>
<div className="flex justify-between">
<Button variant="outline" onClick={onPrevious}>
</Button>
<Button onClick={onNext} disabled={!isFormValid}>
</Button>
</div>
</div>
)
}

View File

@@ -0,0 +1,244 @@
"use client"
import { useState } from "react"
import { Card, CardContent } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { FileText, ImageIcon, Video, LinkIcon, FileAudio, Tag, Clock, Check, X } from "lucide-react"
import Image from "next/image"
import type { ContentLibraryFormData } from "../page"
interface PreviewAndConfirmProps {
formData: ContentLibraryFormData
onSubmit: () => void
onPrevious: () => void
}
export function PreviewAndConfirm({ formData, onSubmit, onPrevious }: PreviewAndConfirmProps) {
const [isSubmitting, setIsSubmitting] = useState(false)
const getContentTypeIcon = (type: string) => {
switch (type) {
case "text":
return <FileText className="h-4 w-4" />
case "image":
return <ImageIcon className="h-4 w-4" />
case "video":
return <Video className="h-4 w-4" />
case "link":
return <LinkIcon className="h-4 w-4" />
case "audio":
return <FileAudio className="h-4 w-4" />
default:
return null
}
}
const getContentTypeName = (type: string) => {
switch (type) {
case "text":
return "文本"
case "image":
return "图片"
case "video":
return "视频"
case "link":
return "链接"
case "audio":
return "音频"
default:
return type
}
}
const getLibraryTypeName = (type: string) => {
switch (type) {
case "friends":
return "微信好友"
case "groups":
return "微信群"
case "moments":
return "朋友圈"
case "mixed":
return "混合来源"
default:
return type
}
}
const getSyncIntervalText = (hours: number) => {
if (hours === 1) return "每1小时"
if (hours === 6) return "每6小时"
if (hours === 12) return "每12小时"
if (hours === 24) return "每天"
if (hours === 168) return "每周"
return `${hours}小时`
}
const handleSubmit = async () => {
setIsSubmitting(true)
try {
await onSubmit()
} finally {
setIsSubmitting(false)
}
}
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>
<Tabs defaultValue="basic">
<TabsList className="grid w-full grid-cols-4">
<TabsTrigger value="basic"></TabsTrigger>
<TabsTrigger value="sources"></TabsTrigger>
<TabsTrigger value="content"></TabsTrigger>
<TabsTrigger value="tags"></TabsTrigger>
</TabsList>
<TabsContent value="basic" className="p-4 border rounded-md mt-4 space-y-4">
<div>
<h3 className="text-sm font-medium text-gray-500"></h3>
<p className="mt-1">{formData.name}</p>
</div>
{formData.description && (
<div>
<h3 className="text-sm font-medium text-gray-500"></h3>
<p className="mt-1">{formData.description}</p>
</div>
)}
<div>
<h3 className="text-sm font-medium text-gray-500"></h3>
<p className="mt-1">{getLibraryTypeName(formData.type)}</p>
</div>
<div>
<h3 className="text-sm font-medium text-gray-500"></h3>
<div className="flex items-center mt-1">
{formData.enabled ? (
<>
<Check className="h-4 w-4 text-green-500 mr-1" />
<span></span>
</>
) : (
<>
<X className="h-4 w-4 text-red-500 mr-1" />
<span></span>
</>
)}
</div>
</div>
</TabsContent>
<TabsContent value="sources" className="p-4 border rounded-md mt-4 space-y-4">
<div>
<h3 className="text-sm font-medium text-gray-500"></h3>
<div className="mt-2 space-y-2">
{formData.sources.length > 0 ? (
formData.sources.map((source) => (
<div key={source.id} className="flex items-center space-x-2 p-2 border rounded-md">
<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>
))
) : (
<p className="text-sm text-gray-500"></p>
)}
</div>
</div>
<div>
<h3 className="text-sm font-medium text-gray-500"></h3>
<p className="mt-1">{formData.sources.length} </p>
</div>
</TabsContent>
<TabsContent value="content" className="p-4 border rounded-md mt-4 space-y-4">
<div>
<h3 className="text-sm font-medium text-gray-500"></h3>
<div className="flex flex-wrap gap-2 mt-2">
{formData.contentTypes.map((type) => (
<Badge key={type} variant="outline" className="flex items-center gap-1">
{getContentTypeIcon(type)}
{getContentTypeName(type)}
</Badge>
))}
</div>
</div>
<div>
<h3 className="text-sm font-medium text-gray-500"></h3>
<div className="flex items-center mt-1">
{formData.autoSync ? (
<>
<Check className="h-4 w-4 text-green-500 mr-1" />
<span></span>
<Badge variant="outline" className="ml-2 flex items-center gap-1">
<Clock className="h-3 w-3" />
{getSyncIntervalText(formData.syncInterval)}
</Badge>
</>
) : (
<>
<X className="h-4 w-4 text-red-500 mr-1" />
<span></span>
</>
)}
</div>
</div>
</TabsContent>
<TabsContent value="tags" className="p-4 border rounded-md mt-4 space-y-4">
<div>
<h3 className="text-sm font-medium text-gray-500"></h3>
{formData.tags.length > 0 ? (
<div className="flex flex-wrap gap-2 mt-2">
{formData.tags.map((tag) => (
<Badge key={tag} variant="secondary" className="flex items-center gap-1">
<Tag className="h-3 w-3" />
{tag}
</Badge>
))}
</div>
) : (
<p className="text-sm text-gray-500 mt-1"></p>
)}
</div>
</TabsContent>
</Tabs>
</div>
</CardContent>
</Card>
<div className="flex justify-between">
<Button variant="outline" onClick={onPrevious}>
</Button>
<Button onClick={handleSubmit} disabled={isSubmitting}>
{isSubmitting ? "创建中..." : "创建内容库"}
</Button>
</div>
</div>
)
}

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>
)
}

View File

@@ -0,0 +1,151 @@
"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 { Badge } from "@/components/ui/badge"
import { Tag, Plus, X } from "lucide-react"
import type { ContentLibraryFormData } from "../page"
interface TagSettingsProps {
formData: ContentLibraryFormData
updateFormData: (field: string, value: any) => void
onNext: () => void
onPrevious: () => void
}
// 预设标签
const presetTags = ["营销素材", "产品介绍", "客户反馈", "行业资讯", "竞品分析", "培训资料", "案例分享", "活动宣传"]
export function TagSettings({ formData, updateFormData, onNext, onPrevious }: TagSettingsProps) {
const [newTag, setNewTag] = useState("")
const [tags, setTags] = useState<string[]>(formData.tags)
const [error, setError] = useState<string | null>(null)
const handleAddTag = () => {
if (!newTag.trim()) {
setError("标签不能为空")
return
}
if (tags.includes(newTag.trim())) {
setError("标签已存在")
return
}
setTags([...tags, newTag.trim()])
setNewTag("")
setError(null)
}
const handleRemoveTag = (tag: string) => {
setTags(tags.filter((t) => t !== tag))
}
const handleAddPresetTag = (tag: string) => {
if (tags.includes(tag)) {
return
}
setTags([...tags, tag])
}
const handleNext = () => {
updateFormData("tags", tags)
onNext()
}
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>
<div className="space-y-4">
<div>
<Label className="text-base"></Label>
<div className="flex mt-1.5">
<div className="relative flex-1">
<Tag className="absolute left-3 top-2.5 h-4 w-4 text-gray-400" />
<Input
value={newTag}
onChange={(e) => {
setNewTag(e.target.value)
setError(null)
}}
placeholder="输入标签名称"
className="pl-9"
onKeyDown={(e) => {
if (e.key === "Enter") {
e.preventDefault()
handleAddTag()
}
}}
/>
</div>
<Button type="button" onClick={handleAddTag} className="ml-2" disabled={!newTag.trim()}>
<Plus className="h-4 w-4 mr-1" />
</Button>
</div>
{error && <p className="text-sm text-red-500 mt-1">{error}</p>}
</div>
{tags.length > 0 && (
<div>
<Label className="text-base"></Label>
<div className="flex flex-wrap gap-2 mt-2">
{tags.map((tag) => (
<Badge key={tag} variant="secondary" className="flex items-center gap-1 px-3 py-1.5">
<Tag className="h-3 w-3" />
{tag}
<Button
variant="ghost"
size="sm"
className="h-4 w-4 p-0 ml-1 rounded-full"
onClick={() => handleRemoveTag(tag)}
>
<X className="h-3 w-3" />
</Button>
</Badge>
))}
</div>
</div>
)}
<div>
<Label className="text-base"></Label>
<div className="flex flex-wrap gap-2 mt-2">
{presetTags.map((tag) => (
<Badge
key={tag}
variant="outline"
className={`cursor-pointer hover:bg-gray-100 ${
tags.includes(tag) ? "bg-blue-50 text-blue-700 border-blue-200" : ""
}`}
onClick={() => handleAddPresetTag(tag)}
>
{tag}
</Badge>
))}
</div>
</div>
</div>
</div>
</CardContent>
</Card>
<div className="flex justify-between">
<Button variant="outline" onClick={onPrevious}>
</Button>
<Button onClick={handleNext}></Button>
</div>
</div>
)
}