Files
cunkebao_v3/Cunkebao/app/plans/new/steps/MessageSettings.tsx
2025-06-03 16:37:39 +08:00

555 lines
22 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import { useState } 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 { Textarea } from "@/components/ui/textarea"
import {
MessageSquare,
ImageIcon,
Video,
FileText,
Link2,
Users,
AppWindowIcon as Window,
Plus,
X,
Upload,
Clock,
} from "lucide-react"
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { toast } from "@/components/ui/use-toast"
interface MessageContent {
id: string
type: "text" | "image" | "video" | "file" | "miniprogram" | "link" | "group"
content: string
sendInterval?: number
intervalUnit?: "seconds" | "minutes"
scheduledTime?: {
hour: number
minute: number
second: number
}
title?: string
description?: string
address?: string
coverImage?: string
groupId?: string
linkUrl?: string
}
interface DayPlan {
day: number
messages: MessageContent[]
}
interface MessageSettingsProps {
formData: any
onChange: (data: any) => void
onNext: () => void
onPrev: () => void
}
// 消息类型配置
const messageTypes = [
{ id: "text", icon: MessageSquare, label: "文本" },
{ id: "image", icon: ImageIcon, label: "图片" },
{ id: "video", icon: Video, label: "视频" },
{ id: "file", icon: FileText, label: "文件" },
{ id: "miniprogram", icon: Window, label: "小程序" },
{ id: "link", icon: Link2, label: "链接" },
{ id: "group", icon: Users, label: "邀请入群" },
]
// 模拟群组数据
const mockGroups = [
{ id: "1", name: "产品交流群1", memberCount: 156 },
{ id: "2", name: "产品交流群2", memberCount: 234 },
{ id: "3", name: "产品交流群3", memberCount: 89 },
]
export function MessageSettings({ formData, onChange, onNext, onPrev }: MessageSettingsProps) {
const [dayPlans, setDayPlans] = useState<DayPlan[]>([
{
day: 0,
messages: [
{
id: "1",
type: "text",
content: "",
sendInterval: 5,
intervalUnit: "seconds", // 默认改为秒
},
],
},
])
const [isAddDayPlanOpen, setIsAddDayPlanOpen] = useState(false)
const [isGroupSelectOpen, setIsGroupSelectOpen] = useState(false)
const [selectedGroupId, setSelectedGroupId] = useState("")
// 添加新消息
const handleAddMessage = (dayIndex: number, type = "text") => {
const updatedPlans = [...dayPlans]
const newMessage: MessageContent = {
id: Date.now().toString(),
type: type as MessageContent["type"],
content: "",
}
if (dayPlans[dayIndex].day === 0) {
// 即时消息使用间隔设置
newMessage.sendInterval = 5
newMessage.intervalUnit = "seconds" // 默认改为秒
} else {
// 非即时消息使用具体时间设置
newMessage.scheduledTime = {
hour: 9,
minute: 0,
second: 0,
}
}
updatedPlans[dayIndex].messages.push(newMessage)
setDayPlans(updatedPlans)
onChange({ ...formData, messagePlans: updatedPlans })
}
// 更新消息内容
const handleUpdateMessage = (dayIndex: number, messageIndex: number, updates: Partial<MessageContent>) => {
const updatedPlans = [...dayPlans]
updatedPlans[dayIndex].messages[messageIndex] = {
...updatedPlans[dayIndex].messages[messageIndex],
...updates,
}
setDayPlans(updatedPlans)
onChange({ ...formData, messagePlans: updatedPlans })
}
// 删除消息
const handleRemoveMessage = (dayIndex: number, messageIndex: number) => {
const updatedPlans = [...dayPlans]
updatedPlans[dayIndex].messages.splice(messageIndex, 1)
setDayPlans(updatedPlans)
onChange({ ...formData, messagePlans: updatedPlans })
}
// 切换时间单位
const toggleIntervalUnit = (dayIndex: number, messageIndex: number) => {
const message = dayPlans[dayIndex].messages[messageIndex]
const newUnit = message.intervalUnit === "minutes" ? "seconds" : "minutes"
handleUpdateMessage(dayIndex, messageIndex, { intervalUnit: newUnit })
}
// 添加新的天数计划
const handleAddDayPlan = () => {
const newDay = dayPlans.length
setDayPlans([
...dayPlans,
{
day: newDay,
messages: [
{
id: Date.now().toString(),
type: "text",
content: "",
scheduledTime: {
hour: 9,
minute: 0,
second: 0,
},
},
],
},
])
setIsAddDayPlanOpen(false)
toast({
title: "添加成功",
description: `已添加第${newDay}天的消息计划`,
})
}
// 选择群组
const handleSelectGroup = (groupId: string) => {
setSelectedGroupId(groupId)
setIsGroupSelectOpen(false)
toast({
title: "选择成功",
description: `已选择群组:${mockGroups.find((g) => g.id === groupId)?.name}`,
})
}
// 处理文件上传
const handleFileUpload = (dayIndex: number, messageIndex: number, type: "image" | "video" | "file") => {
// 模拟文件上传
toast({
title: "上传成功",
description: `${type === "image" ? "图片" : type === "video" ? "视频" : "文件"}上传成功`,
})
}
return (
<Card className="p-6">
<div className="space-y-6">
<div className="flex items-center justify-between">
<h2 className="text-lg font-semibold"></h2>
<Button variant="outline" size="icon" onClick={() => setIsAddDayPlanOpen(true)}>
<Plus className="h-4 w-4" />
</Button>
</div>
<Tabs defaultValue="0" className="w-full">
<TabsList className="w-full">
{dayPlans.map((plan) => (
<TabsTrigger key={plan.day} value={plan.day.toString()} className="flex-1">
{plan.day === 0 ? "即时消息" : `${plan.day}`}
</TabsTrigger>
))}
</TabsList>
{dayPlans.map((plan, dayIndex) => (
<TabsContent key={plan.day} value={plan.day.toString()}>
<div className="space-y-4">
{plan.messages.map((message, messageIndex) => (
<div key={message.id} className="space-y-4 p-4 bg-gray-50 rounded-lg">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
{plan.day === 0 ? (
<>
<Label></Label>
<Input
type="number"
value={message.sendInterval}
onChange={(e) =>
handleUpdateMessage(dayIndex, messageIndex, { sendInterval: Number(e.target.value) })
}
className="w-20"
/>
<Button
variant="ghost"
size="sm"
onClick={() => toggleIntervalUnit(dayIndex, messageIndex)}
className="flex items-center space-x-1"
>
<Clock className="h-3 w-3" />
<span>{message.intervalUnit === "minutes" ? "分钟" : "秒"}</span>
</Button>
</>
) : (
<>
<Label></Label>
<div className="flex items-center space-x-1">
<Input
type="number"
min="0"
max="23"
value={message.scheduledTime?.hour || 0}
onChange={(e) =>
handleUpdateMessage(dayIndex, messageIndex, {
scheduledTime: {
...(message.scheduledTime || { hour: 0, minute: 0, second: 0 }),
hour: Number(e.target.value),
},
})
}
className="w-16"
/>
<span>:</span>
<Input
type="number"
min="0"
max="59"
value={message.scheduledTime?.minute || 0}
onChange={(e) =>
handleUpdateMessage(dayIndex, messageIndex, {
scheduledTime: {
...(message.scheduledTime || { hour: 0, minute: 0, second: 0 }),
minute: Number(e.target.value),
},
})
}
className="w-16"
/>
<span>:</span>
<Input
type="number"
min="0"
max="59"
value={message.scheduledTime?.second || 0}
onChange={(e) =>
handleUpdateMessage(dayIndex, messageIndex, {
scheduledTime: {
...(message.scheduledTime || { hour: 0, minute: 0, second: 0 }),
second: Number(e.target.value),
},
})
}
className="w-16"
/>
</div>
</>
)}
</div>
<Button variant="ghost" size="sm" onClick={() => handleRemoveMessage(dayIndex, messageIndex)}>
<X className="h-4 w-4" />
</Button>
</div>
<div className="flex items-center space-x-2 bg-white p-2 rounded-lg">
{messageTypes.map((type) => (
<Button
key={type.id}
variant={message.type === type.id ? "default" : "outline"}
size="sm"
onClick={() => handleUpdateMessage(dayIndex, messageIndex, { type: type.id as any })}
className="flex flex-col items-center p-2 h-auto"
>
<type.icon className="h-4 w-4" />
</Button>
))}
</div>
{message.type === "text" && (
<Textarea
value={message.content}
onChange={(e) => handleUpdateMessage(dayIndex, messageIndex, { content: e.target.value })}
placeholder="请输入消息内容"
className="min-h-[100px]"
/>
)}
{message.type === "miniprogram" && (
<div className="space-y-4">
<div className="space-y-2">
<Label>
<span className="text-red-500">*</span>
</Label>
<Input
value={message.title}
onChange={(e) => handleUpdateMessage(dayIndex, messageIndex, { title: e.target.value })}
placeholder="请输入小程序标题"
/>
</div>
<div className="space-y-2">
<Label></Label>
<Input
value={message.description}
onChange={(e) =>
handleUpdateMessage(dayIndex, messageIndex, { description: e.target.value })
}
placeholder="请输入小程序描述"
/>
</div>
<div className="space-y-2">
<Label>
<span className="text-red-500">*</span>
</Label>
<Input
value={message.address}
onChange={(e) => handleUpdateMessage(dayIndex, messageIndex, { address: e.target.value })}
placeholder="请输入小程序路径"
/>
</div>
<div className="space-y-2">
<Label>
<span className="text-red-500">*</span>
</Label>
<div className="border-2 border-dashed rounded-lg p-4 text-center">
{message.coverImage ? (
<div className="relative">
<img
src={message.coverImage || "/placeholder.svg"}
alt="封面"
className="max-w-[200px] mx-auto rounded-lg"
/>
<Button
variant="secondary"
size="sm"
className="absolute top-2 right-2"
onClick={() => handleUpdateMessage(dayIndex, messageIndex, { coverImage: undefined })}
>
<X className="h-4 w-4" />
</Button>
</div>
) : (
<Button
variant="outline"
className="w-full h-[120px]"
onClick={() => handleFileUpload(dayIndex, messageIndex, "image")}
>
<Upload className="h-4 w-4 mr-2" />
</Button>
)}
</div>
</div>
</div>
)}
{message.type === "link" && (
<div className="space-y-4">
<div className="space-y-2">
<Label>
<span className="text-red-500">*</span>
</Label>
<Input
value={message.title}
onChange={(e) => handleUpdateMessage(dayIndex, messageIndex, { title: e.target.value })}
placeholder="请输入链接标题"
/>
</div>
<div className="space-y-2">
<Label></Label>
<Input
value={message.description}
onChange={(e) =>
handleUpdateMessage(dayIndex, messageIndex, { description: e.target.value })
}
placeholder="请输入链接描述"
/>
</div>
<div className="space-y-2">
<Label>
<span className="text-red-500">*</span>
</Label>
<Input
value={message.linkUrl}
onChange={(e) => handleUpdateMessage(dayIndex, messageIndex, { linkUrl: e.target.value })}
placeholder="请输入链接地址"
/>
</div>
<div className="space-y-2">
<Label>
<span className="text-red-500">*</span>
</Label>
<div className="border-2 border-dashed rounded-lg p-4 text-center">
{message.coverImage ? (
<div className="relative">
<img
src={message.coverImage || "/placeholder.svg"}
alt="封面"
className="max-w-[200px] mx-auto rounded-lg"
/>
<Button
variant="secondary"
size="sm"
className="absolute top-2 right-2"
onClick={() => handleUpdateMessage(dayIndex, messageIndex, { coverImage: undefined })}
>
<X className="h-4 w-4" />
</Button>
</div>
) : (
<Button
variant="outline"
className="w-full h-[120px]"
onClick={() => handleFileUpload(dayIndex, messageIndex, "image")}
>
<Upload className="h-4 w-4 mr-2" />
</Button>
)}
</div>
</div>
</div>
)}
{message.type === "group" && (
<div className="space-y-2">
<Label>
<span className="text-red-500">*</span>
</Label>
<Button
variant="outline"
className="w-full justify-start"
onClick={() => setIsGroupSelectOpen(true)}
>
{selectedGroupId ? mockGroups.find((g) => g.id === selectedGroupId)?.name : "选择邀请入的群"}
</Button>
</div>
)}
{(message.type === "image" || message.type === "video" || message.type === "file") && (
<div className="border-2 border-dashed rounded-lg p-4 text-center">
<Button
variant="outline"
className="w-full h-[120px]"
onClick={() => handleFileUpload(dayIndex, messageIndex, message.type as any)}
>
<Upload className="h-4 w-4 mr-2" />
{message.type === "image" ? "图片" : message.type === "video" ? "视频" : "文件"}
</Button>
</div>
)}
</div>
))}
<Button variant="outline" onClick={() => handleAddMessage(dayIndex)} className="w-full">
<Plus className="w-4 h-4 mr-2" />
</Button>
</div>
</TabsContent>
))}
</Tabs>
<div className="flex justify-between pt-4">
<Button variant="outline" onClick={onPrev}>
</Button>
<Button onClick={onNext}></Button>
</div>
</div>
{/* 添加天数计划弹窗 */}
<Dialog open={isAddDayPlanOpen} onOpenChange={setIsAddDayPlanOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle></DialogTitle>
</DialogHeader>
<div className="py-4">
<p className="text-sm text-gray-500 mb-4"></p>
<Button onClick={handleAddDayPlan} className="w-full">
{dayPlans.length}
</Button>
</div>
</DialogContent>
</Dialog>
{/* 选择群聊弹窗 */}
<Dialog open={isGroupSelectOpen} onOpenChange={setIsGroupSelectOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle></DialogTitle>
</DialogHeader>
<div className="py-4">
<div className="space-y-2">
{mockGroups.map((group) => (
<div
key={group.id}
className={`p-4 rounded-lg cursor-pointer hover:bg-gray-100 ${
selectedGroupId === group.id ? "bg-blue-50 border border-blue-200" : ""
}`}
onClick={() => handleSelectGroup(group.id)}
>
<div className="font-medium">{group.name}</div>
<div className="text-sm text-gray-500">{group.memberCount}</div>
</div>
))}
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setIsGroupSelectOpen(false)}>
</Button>
<Button onClick={() => setIsGroupSelectOpen(false)}></Button>
</DialogFooter>
</DialogContent>
</Dialog>
</Card>
)
}