Files
ckb-SuperAdmin/nkebao/src/pages/scenarios/plan/new/steps/MessageSettings.tsx
笔记本里的永平 d763f482b1 feat: 本次提交更新内容如下
新建朋友圈同步
2025-07-23 16:44:13 +08:00

611 lines
20 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.

import React, { useState } from "react";
import { Form, Input, Button, Tabs, Modal, Alert, Upload, message } from "antd";
import {
PlusOutlined,
CloseOutlined,
UploadOutlined,
ClockCircleOutlined,
MessageOutlined,
PictureOutlined,
VideoCameraOutlined,
FileOutlined,
AppstoreOutlined,
LinkOutlined,
TeamOutlined,
} from "@ant-design/icons";
import Layout from "@/components/Layout/Layout";
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: MessageOutlined, label: "文本" },
{ id: "image", icon: PictureOutlined, label: "图片" },
{ id: "video", icon: VideoCameraOutlined, label: "视频" },
{ id: "file", icon: FileOutlined, label: "文件" },
{ id: "miniprogram", icon: AppstoreOutlined, label: "小程序" },
{ id: "link", icon: LinkOutlined, label: "链接" },
{ id: "group", icon: TeamOutlined, label: "邀请入群" },
];
// 模拟群组数据
const mockGroups = [
{ id: "1", name: "产品交流群1", memberCount: 156 },
{ id: "2", name: "产品交流群2", memberCount: 234 },
{ id: "3", name: "产品交流群3", memberCount: 89 },
];
const MessageSettings: React.FC<MessageSettingsProps> = ({
formData,
onChange,
onNext,
onPrev,
}) => {
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);
message.success(`已添加第${newDay}天的消息计划`);
};
// 选择群组
const handleSelectGroup = (groupId: string) => {
setSelectedGroupId(groupId);
setIsGroupSelectOpen(false);
message.success(
`已选择群组:${mockGroups.find((g) => g.id === groupId)?.name}`
);
};
// 处理文件上传
const handleFileUpload = (
dayIndex: number,
messageIndex: number,
type: "image" | "video" | "file"
) => {
message.success(
`${
type === "image" ? "图片" : type === "video" ? "视频" : "文件"
}上传成功`
);
};
const items = dayPlans.map((plan, dayIndex) => ({
key: plan.day.toString(),
label: plan.day === 0 ? "即时消息" : `${plan.day}`,
children: (
<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 ? (
<>
<div className="w-10"></div>
<div className="w-40">
<Input
type="number"
value={String(message.sendInterval)}
onChange={(e) =>
handleUpdateMessage(dayIndex, messageIndex, {
sendInterval: Number(e.target.value),
})
}
/>
</div>
<Button
onClick={() => toggleIntervalUnit(dayIndex, messageIndex)}
className="flex items-center space-x-1"
>
<ClockCircleOutlined className="h-3 w-3" />
<span>
{message.intervalUnit === "minutes" ? "分钟" : "秒"}
</span>
</Button>
</>
) : (
<>
<div className="font-medium"></div>
<div className="flex items-center space-x-1">
<Input
type="number"
min={0}
max={23}
value={String(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={String(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={String(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
onClick={() => handleRemoveMessage(dayIndex, messageIndex)}
>
<CloseOutlined 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}
type={message.type === type.id ? "primary" : "default"}
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" && (
<Input.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">
<div className="font-medium">
<span className="text-red-500">*</span>
</div>
<Input
value={message.title}
onChange={(e) =>
handleUpdateMessage(dayIndex, messageIndex, {
title: e.target.value,
})
}
placeholder="请输入小程序标题"
/>
</div>
<div className="space-y-2">
<div className="font-medium"></div>
<Input
value={message.description}
onChange={(e) =>
handleUpdateMessage(dayIndex, messageIndex, {
description: e.target.value,
})
}
placeholder="请输入小程序描述"
/>
</div>
<div className="space-y-2">
<div className="font-medium">
<span className="text-red-500">*</span>
</div>
<Input
value={message.address}
onChange={(e) =>
handleUpdateMessage(dayIndex, messageIndex, {
address: e.target.value,
})
}
placeholder="请输入小程序路径"
/>
</div>
<div className="space-y-2">
<div className="font-medium">
<span className="text-red-500">*</span>
</div>
<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
onClick={() =>
handleUpdateMessage(dayIndex, messageIndex, {
coverImage: undefined,
})
}
>
<CloseOutlined className="h-4 w-4" />
</Button>
</div>
) : (
<Button
onClick={() =>
handleFileUpload(dayIndex, messageIndex, "image")
}
>
<UploadOutlined className="h-4 w-4 mr-2" />
</Button>
)}
</div>
</div>
</div>
)}
{message.type === "link" && (
<div className="space-y-4">
<div className="space-y-2">
<div className="font-medium">
<span className="text-red-500">*</span>
</div>
<Input
value={message.title}
onChange={(e) =>
handleUpdateMessage(dayIndex, messageIndex, {
title: e.target.value,
})
}
placeholder="请输入链接标题"
/>
</div>
<div className="space-y-2">
<div className="font-medium"></div>
<Input
value={message.description}
onChange={(e) =>
handleUpdateMessage(dayIndex, messageIndex, {
description: e.target.value,
})
}
placeholder="请输入链接描述"
/>
</div>
<div className="space-y-2">
<div className="font-medium">
<span className="text-red-500">*</span>
</div>
<Input
value={message.linkUrl}
onChange={(e) =>
handleUpdateMessage(dayIndex, messageIndex, {
linkUrl: e.target.value,
})
}
placeholder="请输入链接地址"
/>
</div>
<div className="space-y-2">
<div className="font-medium">
<span className="text-red-500">*</span>
</div>
<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
onClick={() =>
handleUpdateMessage(dayIndex, messageIndex, {
coverImage: undefined,
})
}
>
<CloseOutlined className="h-4 w-4" />
</Button>
</div>
) : (
<Button
onClick={() =>
handleFileUpload(dayIndex, messageIndex, "image")
}
>
<UploadOutlined className="h-4 w-4 mr-2" />
</Button>
)}
</div>
</div>
</div>
)}
{message.type === "group" && (
<div className="space-y-2">
<div className="font-medium">
<span className="text-red-500">*</span>
</div>
<Button 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
onClick={() =>
handleFileUpload(
dayIndex,
messageIndex,
message.type as any
)
}
>
<UploadOutlined className="h-4 w-4 mr-2" />
{message.type === "image"
? "图片"
: message.type === "video"
? "视频"
: "文件"}
</Button>
</div>
)}
</div>
))}
<Button onClick={() => handleAddMessage(dayIndex)} className="w-full">
<PlusOutlined className="w-4 h-4 mr-2" />
</Button>
</div>
),
}));
return (
<>
<Layout
footer={
<div className="p-4 border-t bg-white">
<div className="flex justify-between">
<Button onClick={onPrev}></Button>
<Button type="primary" onClick={onNext}>
</Button>
</div>
</div>
}
>
<div className="p-4 space-y-6">
<div className="flex items-center justify-between">
<h2 className="text-lg font-semibold"></h2>
<Button onClick={() => setIsAddDayPlanOpen(true)}>
<PlusOutlined className="h-4 w-4" />
</Button>
</div>
<Tabs defaultActiveKey="0" items={items} />
</div>
</Layout>
{/* 添加天数计划弹窗 */}
<Modal
title="添加消息计划"
open={isAddDayPlanOpen}
onCancel={() => setIsAddDayPlanOpen(false)}
onOk={() => {
handleAddDayPlan();
setIsAddDayPlanOpen(false);
}}
>
<p className="text-sm text-gray-500 mb-4"></p>
<Button onClick={handleAddDayPlan} className="w-full">
{dayPlans.length}
</Button>
</Modal>
{/* 选择群聊弹窗 */}
<Modal
title="选择群聊"
open={isGroupSelectOpen}
onCancel={() => setIsGroupSelectOpen(false)}
onOk={() => {
handleSelectGroup(selectedGroupId);
setIsGroupSelectOpen(false);
}}
>
<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>
</Modal>
</>
);
};
export default MessageSettings;