更新 index.html 和 manifest.json 中的資源引用版本,並在新計劃頁面中調整海報模板的屬性名稱,將 preview 改為 url,以統一資料結構。
This commit is contained in:
2
Cunkebao/dist/.vite/manifest.json
vendored
2
Cunkebao/dist/.vite/manifest.json
vendored
@@ -32,7 +32,7 @@
|
|||||||
"name": "vendor"
|
"name": "vendor"
|
||||||
},
|
},
|
||||||
"index.html": {
|
"index.html": {
|
||||||
"file": "assets/index-CeSKt0aC.js",
|
"file": "assets/index-PKCtfAad.js",
|
||||||
"name": "index",
|
"name": "index",
|
||||||
"src": "index.html",
|
"src": "index.html",
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
|
|||||||
2
Cunkebao/dist/index.html
vendored
2
Cunkebao/dist/index.html
vendored
@@ -11,7 +11,7 @@
|
|||||||
</style>
|
</style>
|
||||||
<!-- 引入 uni-app web-view SDK(必须) -->
|
<!-- 引入 uni-app web-view SDK(必须) -->
|
||||||
<script type="text/javascript" src="./websdk.js"></script>
|
<script type="text/javascript" src="./websdk.js"></script>
|
||||||
<script type="module" crossorigin src="/assets/index-CeSKt0aC.js"></script>
|
<script type="module" crossorigin src="/assets/index-PKCtfAad.js"></script>
|
||||||
<link rel="modulepreload" crossorigin href="/assets/vendor-2vc8h_ct.js">
|
<link rel="modulepreload" crossorigin href="/assets/vendor-2vc8h_ct.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/ui-DE3rfvO3.js">
|
<link rel="modulepreload" crossorigin href="/assets/ui-DE3rfvO3.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/utils-BEiZ4iZ8.js">
|
<link rel="modulepreload" crossorigin href="/assets/utils-BEiZ4iZ8.js">
|
||||||
|
|||||||
@@ -137,12 +137,47 @@ const MainImgUpload: React.FC<MainImgUploadProps> = ({
|
|||||||
|
|
||||||
// 预览图片
|
// 预览图片
|
||||||
const handlePreview = (url: string) => {
|
const handlePreview = (url: string) => {
|
||||||
const img = new Image();
|
// 使用自定义全屏预览,确保不受父级容器限制
|
||||||
|
const modal = document.createElement("div");
|
||||||
|
modal.style.cssText = `
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background: rgba(0, 0, 0, 0.9);
|
||||||
|
z-index: 9999;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const img = document.createElement("img");
|
||||||
img.src = url;
|
img.src = url;
|
||||||
const newWindow = window.open();
|
img.style.cssText = `
|
||||||
if (newWindow) {
|
max-width: 90vw;
|
||||||
newWindow.document.write(img.outerHTML);
|
max-height: 90vh;
|
||||||
}
|
object-fit: contain;
|
||||||
|
border-radius: 8px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
document.body.removeChild(modal);
|
||||||
|
};
|
||||||
|
|
||||||
|
modal.addEventListener("click", closeModal);
|
||||||
|
modal.appendChild(img);
|
||||||
|
document.body.appendChild(modal);
|
||||||
|
|
||||||
|
// 添加键盘事件监听
|
||||||
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === "Escape") {
|
||||||
|
closeModal();
|
||||||
|
document.removeEventListener("keydown", handleKeyDown);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener("keydown", handleKeyDown);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 格式化文件大小
|
// 格式化文件大小
|
||||||
@@ -244,7 +279,20 @@ const MainImgUpload: React.FC<MainImgUploadProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={style.mainImgPreview}>
|
<div
|
||||||
|
className={style.mainImgPreview}
|
||||||
|
onClick={e => {
|
||||||
|
// 阻止事件冒泡,防止触发删除操作
|
||||||
|
e.stopPropagation();
|
||||||
|
// 点击图片预览区域时,触发文件选择
|
||||||
|
const uploadInput = document.querySelector(
|
||||||
|
'input[type="file"]',
|
||||||
|
) as HTMLInputElement;
|
||||||
|
if (uploadInput) {
|
||||||
|
uploadInput.click();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
src={file.url}
|
src={file.url}
|
||||||
alt={file.name}
|
alt={file.name}
|
||||||
@@ -257,7 +305,10 @@ const MainImgUpload: React.FC<MainImgUploadProps> = ({
|
|||||||
type="text"
|
type="text"
|
||||||
size="small"
|
size="small"
|
||||||
icon={<EyeOutlined />}
|
icon={<EyeOutlined />}
|
||||||
onClick={() => handlePreview(file.url || "")}
|
onClick={e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handlePreview(file.url || "");
|
||||||
|
}}
|
||||||
className={style.previewBtn}
|
className={style.previewBtn}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -265,7 +316,10 @@ const MainImgUpload: React.FC<MainImgUploadProps> = ({
|
|||||||
type="text"
|
type="text"
|
||||||
size="small"
|
size="small"
|
||||||
icon={<DeleteOutlined />}
|
icon={<DeleteOutlined />}
|
||||||
onClick={() => handleRemove()}
|
onClick={e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleRemove();
|
||||||
|
}}
|
||||||
className={style.deleteBtn}
|
className={style.deleteBtn}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
54
Cunkebao/src/pages/mobile/scenarios/plan/new/index.data.ts
Normal file
54
Cunkebao/src/pages/mobile/scenarios/plan/new/index.data.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
// 步骤定义 - 只保留三个步骤
|
||||||
|
import { DeviceSelectionItem } from "@/components/DeviceSelection/data";
|
||||||
|
import { GroupSelectionItem } from "@/components/GroupSelection/data";
|
||||||
|
export const steps = [
|
||||||
|
{ id: 1, title: "步骤一", subtitle: "基础设置" },
|
||||||
|
{ id: 2, title: "步骤二", subtitle: "好友申请设置" },
|
||||||
|
{ id: 3, title: "步骤三", subtitle: "消息设置" },
|
||||||
|
];
|
||||||
|
|
||||||
|
// 类型定义
|
||||||
|
export interface FormData {
|
||||||
|
name: string;
|
||||||
|
scenario: number;
|
||||||
|
status: number;
|
||||||
|
sceneId: string | number;
|
||||||
|
remarkType: string;
|
||||||
|
greeting: string;
|
||||||
|
addInterval: number;
|
||||||
|
startTime: string;
|
||||||
|
endTime: string;
|
||||||
|
enabled: boolean;
|
||||||
|
remarkFormat: string;
|
||||||
|
addFriendInterval: number;
|
||||||
|
posters: any[]; // 后续可替换为具体Poster类型
|
||||||
|
device: string[];
|
||||||
|
customTags: string[];
|
||||||
|
deveiceGroups: string[];
|
||||||
|
deveiceGroupsOptions: DeviceSelectionItem[];
|
||||||
|
wechatGroups: string[];
|
||||||
|
wechatGroupsOptions: GroupSelectionItem[];
|
||||||
|
messagePlans: any[];
|
||||||
|
}
|
||||||
|
export const defFormData: FormData = {
|
||||||
|
name: "",
|
||||||
|
scenario: 1,
|
||||||
|
status: 0,
|
||||||
|
sceneId: "",
|
||||||
|
remarkType: "phone",
|
||||||
|
greeting: "你好,请通过",
|
||||||
|
addInterval: 1,
|
||||||
|
startTime: "09:00",
|
||||||
|
endTime: "18:00",
|
||||||
|
enabled: true,
|
||||||
|
remarkFormat: "",
|
||||||
|
addFriendInterval: 1,
|
||||||
|
posters: [],
|
||||||
|
device: [],
|
||||||
|
customTags: [],
|
||||||
|
messagePlans: [],
|
||||||
|
deveiceGroups: [],
|
||||||
|
deveiceGroupsOptions: [],
|
||||||
|
wechatGroups: [],
|
||||||
|
wechatGroupsOptions: [],
|
||||||
|
};
|
||||||
@@ -8,62 +8,19 @@ import FriendRequestSettings from "./steps/FriendRequestSettings";
|
|||||||
import MessageSettings from "./steps/MessageSettings";
|
import MessageSettings from "./steps/MessageSettings";
|
||||||
import Layout from "@/components/Layout/Layout";
|
import Layout from "@/components/Layout/Layout";
|
||||||
import StepIndicator from "@/components/StepIndicator";
|
import StepIndicator from "@/components/StepIndicator";
|
||||||
import { DeviceSelectionItem } from "@/components/DeviceSelection/data";
|
|
||||||
import {
|
import {
|
||||||
getScenarioTypes,
|
getScenarioTypes,
|
||||||
createPlan,
|
createPlan,
|
||||||
getPlanDetail,
|
getPlanDetail,
|
||||||
updatePlan,
|
updatePlan,
|
||||||
} from "./index.api";
|
} from "./index.api";
|
||||||
|
import { FormData, defFormData, steps } from "./index.data";
|
||||||
// 步骤定义 - 只保留三个步骤
|
|
||||||
const steps = [
|
|
||||||
{ id: 1, title: "步骤一", subtitle: "基础设置" },
|
|
||||||
{ id: 2, title: "步骤二", subtitle: "好友申请设置" },
|
|
||||||
{ id: 3, title: "步骤三", subtitle: "消息设置" },
|
|
||||||
];
|
|
||||||
|
|
||||||
// 类型定义
|
|
||||||
interface FormData {
|
|
||||||
name: string;
|
|
||||||
scenario: number;
|
|
||||||
posters: any[]; // 后续可替换为具体Poster类型
|
|
||||||
device: string[];
|
|
||||||
customTags: string[];
|
|
||||||
remarkType: string;
|
|
||||||
greeting: string;
|
|
||||||
addInterval: number;
|
|
||||||
startTime: string;
|
|
||||||
endTime: string;
|
|
||||||
enabled: boolean;
|
|
||||||
sceneId: string | number;
|
|
||||||
remarkFormat: string;
|
|
||||||
addFriendInterval: number;
|
|
||||||
deveiceGroups: string[];
|
|
||||||
deveiceGroupsOptions: DeviceSelectionItem[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function NewPlan() {
|
export default function NewPlan() {
|
||||||
const router = useNavigate();
|
const router = useNavigate();
|
||||||
const [currentStep, setCurrentStep] = useState(1);
|
const [currentStep, setCurrentStep] = useState(1);
|
||||||
const [formData, setFormData] = useState<FormData>({
|
const [formData, setFormData] = useState<FormData>(defFormData);
|
||||||
name: "",
|
|
||||||
scenario: 1,
|
|
||||||
posters: [],
|
|
||||||
device: [],
|
|
||||||
customTags: [],
|
|
||||||
remarkType: "phone",
|
|
||||||
greeting: "你好,请通过",
|
|
||||||
addInterval: 1,
|
|
||||||
startTime: "09:00",
|
|
||||||
endTime: "18:00",
|
|
||||||
enabled: true,
|
|
||||||
sceneId: "",
|
|
||||||
remarkFormat: "",
|
|
||||||
addFriendInterval: 1,
|
|
||||||
deveiceGroups: [],
|
|
||||||
deveiceGroupsOptions: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
const [sceneList, setSceneList] = useState<any[]>([]);
|
const [sceneList, setSceneList] = useState<any[]>([]);
|
||||||
const [sceneLoading, setSceneLoading] = useState(true);
|
const [sceneLoading, setSceneLoading] = useState(true);
|
||||||
@@ -110,6 +67,10 @@ export default function NewPlan() {
|
|||||||
tips: detail.tips ?? "",
|
tips: detail.tips ?? "",
|
||||||
deveiceGroups: detail.deveiceGroups ?? [],
|
deveiceGroups: detail.deveiceGroups ?? [],
|
||||||
deveiceGroupsOptions: detail.deveiceGroupsOptions ?? [],
|
deveiceGroupsOptions: detail.deveiceGroupsOptions ?? [],
|
||||||
|
wechatGroups: detail.wechatGroups ?? [],
|
||||||
|
wechatGroupsOptions: detail.wechatGroupsOptions ?? [],
|
||||||
|
status: detail.status ?? 0,
|
||||||
|
messagePlans: detail.messagePlans ?? [],
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
if (scenarioId) {
|
if (scenarioId) {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import styles from "./base.module.scss";
|
|||||||
import { posterTemplates } from "./base.data";
|
import { posterTemplates } from "./base.data";
|
||||||
import GroupSelection from "@/components/GroupSelection";
|
import GroupSelection from "@/components/GroupSelection";
|
||||||
import FileUpload from "@/components/Upload/FileUpload";
|
import FileUpload from "@/components/Upload/FileUpload";
|
||||||
|
import { GroupSelectionItem } from "@/components/GroupSelection/data";
|
||||||
|
|
||||||
interface BasicSettingsProps {
|
interface BasicSettingsProps {
|
||||||
isEdit: boolean;
|
isEdit: boolean;
|
||||||
@@ -66,14 +67,6 @@ const BasicSettings: React.FC<BasicSettingsProps> = ({
|
|||||||
questionExtraction: formData.phoneSettings?.questionExtraction ?? true,
|
questionExtraction: formData.phoneSettings?.questionExtraction ?? true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 群设置相关状态
|
|
||||||
const [weixinqunName, setWeixinqunName] = useState(
|
|
||||||
formData.weixinqunName || "",
|
|
||||||
);
|
|
||||||
const [weixinqunNotice, setWeixinqunNotice] = useState(
|
|
||||||
formData.weixinqunNotice || "",
|
|
||||||
);
|
|
||||||
|
|
||||||
// 新增:自定义海报相关状态
|
// 新增:自定义海报相关状态
|
||||||
const [customPosters, setCustomPosters] = useState<Material[]>([]);
|
const [customPosters, setCustomPosters] = useState<Material[]>([]);
|
||||||
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
|
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
|
||||||
@@ -187,6 +180,13 @@ const BasicSettings: React.FC<BasicSettingsProps> = ({
|
|||||||
const openPoster =
|
const openPoster =
|
||||||
formData.scenario !== 1 ? { display: "none" } : { display: "block" };
|
formData.scenario !== 1 ? { display: "none" } : { display: "block" };
|
||||||
|
|
||||||
|
const handleWechatGroupSelect = (groups: GroupSelectionItem[]) => {
|
||||||
|
onChange({
|
||||||
|
...formData,
|
||||||
|
wechatGroups: groups.map(v => v.id),
|
||||||
|
wechatGroupsOptions: groups,
|
||||||
|
});
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div className={styles["basic-container"]}>
|
<div className={styles["basic-container"]}>
|
||||||
{/* 场景选择区块 */}
|
{/* 场景选择区块 */}
|
||||||
@@ -420,10 +420,8 @@ const BasicSettings: React.FC<BasicSettingsProps> = ({
|
|||||||
<div className={styles["basic-group-selection"]}>
|
<div className={styles["basic-group-selection"]}>
|
||||||
<div className={styles["basic-label"]}>选择群聊</div>
|
<div className={styles["basic-label"]}>选择群聊</div>
|
||||||
<GroupSelection
|
<GroupSelection
|
||||||
selectedOptions={formData.groupSelected || []}
|
selectedOptions={formData.wechatGroupsOptions || []}
|
||||||
onSelect={groups =>
|
onSelect={handleWechatGroupSelect}
|
||||||
onChange({ ...formData, groupSelected: groups })
|
|
||||||
}
|
|
||||||
placeholder="请选择微信群"
|
placeholder="请选择微信群"
|
||||||
className={styles["basic-group-selector"]}
|
className={styles["basic-group-selector"]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -0,0 +1,336 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Input, Button } from "antd";
|
||||||
|
import { CloseOutlined, ClockCircleOutlined } from "@ant-design/icons";
|
||||||
|
import styles from "./messages.module.scss";
|
||||||
|
// 导入Upload组件
|
||||||
|
import ImageUpload from "@/components/Upload/ImageUpload/ImageUpload";
|
||||||
|
import VideoUpload from "@/components/Upload/VideoUpload";
|
||||||
|
import FileUpload from "@/components/Upload/FileUpload";
|
||||||
|
import MainImgUpload from "@/components/Upload/MainImgUpload";
|
||||||
|
// 导入GroupSelection组件
|
||||||
|
import GroupSelection from "@/components/GroupSelection";
|
||||||
|
import { GroupSelectionItem } from "@/components/GroupSelection/data";
|
||||||
|
import { MessageContentItem, messageTypes } from "./base.data";
|
||||||
|
|
||||||
|
interface MessageCardProps {
|
||||||
|
message: MessageContentItem;
|
||||||
|
dayIndex: number;
|
||||||
|
messageIndex: number;
|
||||||
|
planDay: number;
|
||||||
|
onUpdateMessage: (
|
||||||
|
dayIndex: number,
|
||||||
|
messageIndex: number,
|
||||||
|
updates: Partial<MessageContentItem>,
|
||||||
|
) => void;
|
||||||
|
onRemoveMessage: (dayIndex: number, messageIndex: number) => void;
|
||||||
|
onToggleIntervalUnit: (dayIndex: number, messageIndex: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MessageCard: React.FC<MessageCardProps> = ({
|
||||||
|
message,
|
||||||
|
dayIndex,
|
||||||
|
messageIndex,
|
||||||
|
planDay,
|
||||||
|
onUpdateMessage,
|
||||||
|
onRemoveMessage,
|
||||||
|
onToggleIntervalUnit,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className={styles["messages-message-card"]}>
|
||||||
|
<div className={styles["messages-message-header"]}>
|
||||||
|
{/* 时间/间隔设置 */}
|
||||||
|
<div className={styles["messages-message-header-content"]}>
|
||||||
|
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
||||||
|
{planDay === 0 ? (
|
||||||
|
<>
|
||||||
|
<span style={{ minWidth: 36 }}>间隔</span>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
value={String(message.sendInterval || 5)}
|
||||||
|
onChange={e =>
|
||||||
|
onUpdateMessage(dayIndex, messageIndex, {
|
||||||
|
sendInterval: Number(e.target.value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
style={{ width: 60 }}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
onClick={() => onToggleIntervalUnit(dayIndex, messageIndex)}
|
||||||
|
>
|
||||||
|
<ClockCircleOutlined />
|
||||||
|
{message.intervalUnit === "minutes" ? "分钟" : "秒"}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span style={{ minWidth: 60 }}>发送时间</span>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
min={0}
|
||||||
|
max={23}
|
||||||
|
value={String(message.scheduledTime?.hour || 9)}
|
||||||
|
onChange={e =>
|
||||||
|
onUpdateMessage(dayIndex, messageIndex, {
|
||||||
|
scheduledTime: {
|
||||||
|
...(message.scheduledTime || {
|
||||||
|
hour: 9,
|
||||||
|
minute: 0,
|
||||||
|
second: 0,
|
||||||
|
}),
|
||||||
|
hour: Number(e.target.value),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
style={{ width: 40 }}
|
||||||
|
/>
|
||||||
|
<span>:</span>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
min={0}
|
||||||
|
max={59}
|
||||||
|
value={String(message.scheduledTime?.minute || 0)}
|
||||||
|
onChange={e =>
|
||||||
|
onUpdateMessage(dayIndex, messageIndex, {
|
||||||
|
scheduledTime: {
|
||||||
|
...(message.scheduledTime || {
|
||||||
|
hour: 9,
|
||||||
|
minute: 0,
|
||||||
|
second: 0,
|
||||||
|
}),
|
||||||
|
minute: Number(e.target.value),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
style={{ width: 40 }}
|
||||||
|
/>
|
||||||
|
<span>:</span>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
min={0}
|
||||||
|
max={59}
|
||||||
|
value={String(message.scheduledTime?.second || 0)}
|
||||||
|
onChange={e =>
|
||||||
|
onUpdateMessage(dayIndex, messageIndex, {
|
||||||
|
scheduledTime: {
|
||||||
|
...(message.scheduledTime || {
|
||||||
|
hour: 9,
|
||||||
|
minute: 0,
|
||||||
|
second: 0,
|
||||||
|
}),
|
||||||
|
second: Number(e.target.value),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
style={{ width: 40 }}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className={styles["messages-message-remove-btn"]}
|
||||||
|
onClick={() => onRemoveMessage(dayIndex, messageIndex)}
|
||||||
|
title="删除"
|
||||||
|
>
|
||||||
|
<CloseOutlined />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{/* 类型切换按钮 */}
|
||||||
|
<div className={styles["messages-message-type-btns"]}>
|
||||||
|
{messageTypes.map(type => (
|
||||||
|
<Button
|
||||||
|
key={type.id}
|
||||||
|
type={message.type === type.id ? "primary" : "default"}
|
||||||
|
onClick={() =>
|
||||||
|
onUpdateMessage(dayIndex, messageIndex, {
|
||||||
|
type: type.id as any,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className={styles["messages-message-type-btn"]}
|
||||||
|
>
|
||||||
|
<type.icon />
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles["messages-message-content"]}>
|
||||||
|
{/* 文本消息 */}
|
||||||
|
{message.type === "text" && (
|
||||||
|
<Input.TextArea
|
||||||
|
value={message.content}
|
||||||
|
onChange={e =>
|
||||||
|
onUpdateMessage(dayIndex, messageIndex, {
|
||||||
|
content: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder="请输入消息内容"
|
||||||
|
autoSize={{ minRows: 3, maxRows: 6 }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{/* 小程序消息 */}
|
||||||
|
{message.type === "miniprogram" && (
|
||||||
|
<>
|
||||||
|
<Input
|
||||||
|
value={message.title}
|
||||||
|
onChange={e =>
|
||||||
|
onUpdateMessage(dayIndex, messageIndex, {
|
||||||
|
title: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder="请输入小程序标题"
|
||||||
|
style={{ marginBottom: 8 }}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
value={message.description}
|
||||||
|
onChange={e =>
|
||||||
|
onUpdateMessage(dayIndex, messageIndex, {
|
||||||
|
description: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder="请输入小程序描述"
|
||||||
|
style={{ marginBottom: 8 }}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
value={message.address}
|
||||||
|
onChange={e =>
|
||||||
|
onUpdateMessage(dayIndex, messageIndex, {
|
||||||
|
address: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder="请输入小程序路径"
|
||||||
|
style={{ marginBottom: 8 }}
|
||||||
|
/>
|
||||||
|
<div style={{ marginBottom: 8 }}>
|
||||||
|
<MainImgUpload
|
||||||
|
value={message.content || ""}
|
||||||
|
onChange={url =>
|
||||||
|
onUpdateMessage(dayIndex, messageIndex, {
|
||||||
|
content: url,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
maxSize={5}
|
||||||
|
showPreview={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{/* 链接消息 */}
|
||||||
|
{message.type === "link" && (
|
||||||
|
<>
|
||||||
|
<Input
|
||||||
|
value={message.title}
|
||||||
|
onChange={e =>
|
||||||
|
onUpdateMessage(dayIndex, messageIndex, {
|
||||||
|
title: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder="请输入链接标题"
|
||||||
|
style={{ marginBottom: 8 }}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
value={message.description}
|
||||||
|
onChange={e =>
|
||||||
|
onUpdateMessage(dayIndex, messageIndex, {
|
||||||
|
description: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder="请输入链接描述"
|
||||||
|
style={{ marginBottom: 8 }}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
value={message.linkUrl}
|
||||||
|
onChange={e =>
|
||||||
|
onUpdateMessage(dayIndex, messageIndex, {
|
||||||
|
linkUrl: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder="请输入链接地址"
|
||||||
|
style={{ marginBottom: 8 }}
|
||||||
|
/>
|
||||||
|
<div style={{ marginBottom: 8 }}>
|
||||||
|
<MainImgUpload
|
||||||
|
value={message.coverImage || ""}
|
||||||
|
onChange={url =>
|
||||||
|
onUpdateMessage(dayIndex, messageIndex, {
|
||||||
|
coverImage: url,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
maxSize={5}
|
||||||
|
showPreview={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{/* 群邀请消息 */}
|
||||||
|
{message.type === "group" && (
|
||||||
|
<div style={{ marginBottom: 8 }}>
|
||||||
|
<GroupSelection
|
||||||
|
selectedOptions={message.groupOptions || []}
|
||||||
|
onSelect={(groups: GroupSelectionItem[]) => {
|
||||||
|
onUpdateMessage(dayIndex, messageIndex, {
|
||||||
|
groupIds: groups.map(v => v.id),
|
||||||
|
groupOptions: groups,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
placeholder="选择邀请入的群"
|
||||||
|
showSelectedList={true}
|
||||||
|
selectedListMaxHeight={200}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* 图片消息 */}
|
||||||
|
{message.type === "image" && (
|
||||||
|
<div style={{ marginBottom: 8 }}>
|
||||||
|
<ImageUpload
|
||||||
|
value={message.content ? [message.content] : []}
|
||||||
|
onChange={urls =>
|
||||||
|
onUpdateMessage(dayIndex, messageIndex, {
|
||||||
|
content: urls[0] || "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
count={1}
|
||||||
|
accept="image/*"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* 视频消息 */}
|
||||||
|
{message.type === "video" && (
|
||||||
|
<div style={{ marginBottom: 8 }}>
|
||||||
|
<VideoUpload
|
||||||
|
value={message.content || ""}
|
||||||
|
onChange={url => {
|
||||||
|
const videoUrl = Array.isArray(url) ? url[0] || "" : url;
|
||||||
|
onUpdateMessage(dayIndex, messageIndex, {
|
||||||
|
content: videoUrl,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
maxSize={50}
|
||||||
|
maxCount={5}
|
||||||
|
showPreview={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* 文件消息 */}
|
||||||
|
{message.type === "file" && (
|
||||||
|
<div style={{ marginBottom: 8 }}>
|
||||||
|
<FileUpload
|
||||||
|
value={message.content || ""}
|
||||||
|
onChange={url => {
|
||||||
|
const fileUrl = Array.isArray(url) ? url[0] || "" : url;
|
||||||
|
onUpdateMessage(dayIndex, messageIndex, {
|
||||||
|
content: fileUrl,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
maxSize={10}
|
||||||
|
maxCount={10}
|
||||||
|
showPreview={true}
|
||||||
|
acceptTypes={["excel", "word", "ppt"]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MessageCard;
|
||||||
@@ -1,96 +1,52 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Input, Button, Tabs, Modal, message } from "antd";
|
import { Button, Tabs, Modal, message } from "antd";
|
||||||
import {
|
import { PlusOutlined, CloseOutlined } from "@ant-design/icons";
|
||||||
PlusOutlined,
|
|
||||||
CloseOutlined,
|
|
||||||
ClockCircleOutlined,
|
|
||||||
MessageOutlined,
|
|
||||||
PictureOutlined,
|
|
||||||
VideoCameraOutlined,
|
|
||||||
FileOutlined,
|
|
||||||
AppstoreOutlined,
|
|
||||||
LinkOutlined,
|
|
||||||
TeamOutlined,
|
|
||||||
} from "@ant-design/icons";
|
|
||||||
import styles from "./messages.module.scss";
|
import styles from "./messages.module.scss";
|
||||||
// 导入Upload组件
|
import {
|
||||||
import ImageUpload from "@/components/Upload/ImageUpload/ImageUpload";
|
MessageContentItem,
|
||||||
import VideoUpload from "@/components/Upload/VideoUpload";
|
MessageContentGroup,
|
||||||
import FileUpload from "@/components/Upload/FileUpload";
|
MessageSettingsProps,
|
||||||
import MainImgUpload from "@/components/Upload/MainImgUpload";
|
} from "./base.data";
|
||||||
// 导入GroupSelection组件
|
import MessageCard from "./MessageCard";
|
||||||
import GroupSelection from "@/components/GroupSelection";
|
|
||||||
|
|
||||||
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;
|
|
||||||
groupIds?: string[]; // 改为数组以支持GroupSelection组件
|
|
||||||
linkUrl?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DayPlan {
|
|
||||||
day: number;
|
|
||||||
messages: MessageContent[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MessageSettingsProps {
|
|
||||||
formData: any;
|
|
||||||
onChange: (data: any) => 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 MessageSettings: React.FC<MessageSettingsProps> = ({
|
const MessageSettings: React.FC<MessageSettingsProps> = ({
|
||||||
formData,
|
formData,
|
||||||
onChange,
|
onChange,
|
||||||
}) => {
|
}) => {
|
||||||
const [dayPlans, setDayPlans] = useState<DayPlan[]>([
|
|
||||||
{
|
|
||||||
day: 0,
|
|
||||||
messages: [
|
|
||||||
{
|
|
||||||
id: "1",
|
|
||||||
type: "text",
|
|
||||||
content: "",
|
|
||||||
sendInterval: 5,
|
|
||||||
intervalUnit: "seconds",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
const [isAddDayPlanOpen, setIsAddDayPlanOpen] = useState(false);
|
const [isAddDayPlanOpen, setIsAddDayPlanOpen] = useState(false);
|
||||||
|
|
||||||
|
// 获取当前的消息计划,如果没有则使用默认值
|
||||||
|
const getCurrentMessagePlans = (): MessageContentGroup[] => {
|
||||||
|
if (formData.messagePlans && formData.messagePlans.length > 0) {
|
||||||
|
return formData.messagePlans;
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
day: 0,
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
id: "1",
|
||||||
|
type: "text",
|
||||||
|
content: "",
|
||||||
|
sendInterval: 5,
|
||||||
|
intervalUnit: "seconds",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
// 添加新消息
|
// 添加新消息
|
||||||
const handleAddMessage = (dayIndex: number, type = "text") => {
|
const handleAddMessage = (dayIndex: number, type = "text") => {
|
||||||
const updatedPlans = [...dayPlans];
|
const currentPlans = getCurrentMessagePlans();
|
||||||
const newMessage: MessageContent = {
|
const updatedPlans = [...currentPlans];
|
||||||
|
const newMessage: MessageContentItem = {
|
||||||
id: Date.now().toString(),
|
id: Date.now().toString(),
|
||||||
type: type as MessageContent["type"],
|
type: type as MessageContentItem["type"],
|
||||||
content: "",
|
content: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
if (dayPlans[dayIndex].day === 0) {
|
if (currentPlans[dayIndex].day === 0) {
|
||||||
newMessage.sendInterval = 5;
|
newMessage.sendInterval = 5;
|
||||||
newMessage.intervalUnit = "seconds";
|
newMessage.intervalUnit = "seconds";
|
||||||
} else {
|
} else {
|
||||||
@@ -102,7 +58,6 @@ const MessageSettings: React.FC<MessageSettingsProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
updatedPlans[dayIndex].messages.push(newMessage);
|
updatedPlans[dayIndex].messages.push(newMessage);
|
||||||
setDayPlans(updatedPlans);
|
|
||||||
onChange({ ...formData, messagePlans: updatedPlans });
|
onChange({ ...formData, messagePlans: updatedPlans });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -110,37 +65,39 @@ const MessageSettings: React.FC<MessageSettingsProps> = ({
|
|||||||
const handleUpdateMessage = (
|
const handleUpdateMessage = (
|
||||||
dayIndex: number,
|
dayIndex: number,
|
||||||
messageIndex: number,
|
messageIndex: number,
|
||||||
updates: Partial<MessageContent>,
|
updates: Partial<MessageContentItem>,
|
||||||
) => {
|
) => {
|
||||||
const updatedPlans = [...dayPlans];
|
const currentPlans = getCurrentMessagePlans();
|
||||||
|
const updatedPlans = [...currentPlans];
|
||||||
updatedPlans[dayIndex].messages[messageIndex] = {
|
updatedPlans[dayIndex].messages[messageIndex] = {
|
||||||
...updatedPlans[dayIndex].messages[messageIndex],
|
...updatedPlans[dayIndex].messages[messageIndex],
|
||||||
...updates,
|
...updates,
|
||||||
};
|
};
|
||||||
setDayPlans(updatedPlans);
|
|
||||||
onChange({ ...formData, messagePlans: updatedPlans });
|
onChange({ ...formData, messagePlans: updatedPlans });
|
||||||
};
|
};
|
||||||
|
|
||||||
// 删除消息
|
// 删除消息
|
||||||
const handleRemoveMessage = (dayIndex: number, messageIndex: number) => {
|
const handleRemoveMessage = (dayIndex: number, messageIndex: number) => {
|
||||||
const updatedPlans = [...dayPlans];
|
const currentPlans = getCurrentMessagePlans();
|
||||||
|
const updatedPlans = [...currentPlans];
|
||||||
updatedPlans[dayIndex].messages.splice(messageIndex, 1);
|
updatedPlans[dayIndex].messages.splice(messageIndex, 1);
|
||||||
setDayPlans(updatedPlans);
|
|
||||||
onChange({ ...formData, messagePlans: updatedPlans });
|
onChange({ ...formData, messagePlans: updatedPlans });
|
||||||
};
|
};
|
||||||
|
|
||||||
// 切换时间单位
|
// 切换时间单位
|
||||||
const toggleIntervalUnit = (dayIndex: number, messageIndex: number) => {
|
const toggleIntervalUnit = (dayIndex: number, messageIndex: number) => {
|
||||||
const message = dayPlans[dayIndex].messages[messageIndex];
|
const currentPlans = getCurrentMessagePlans();
|
||||||
|
const message = currentPlans[dayIndex].messages[messageIndex];
|
||||||
const newUnit = message.intervalUnit === "minutes" ? "seconds" : "minutes";
|
const newUnit = message.intervalUnit === "minutes" ? "seconds" : "minutes";
|
||||||
handleUpdateMessage(dayIndex, messageIndex, { intervalUnit: newUnit });
|
handleUpdateMessage(dayIndex, messageIndex, { intervalUnit: newUnit });
|
||||||
};
|
};
|
||||||
|
|
||||||
// 添加新的天数计划
|
// 添加新的天数计划
|
||||||
const handleAddDayPlan = () => {
|
const handleAddDayPlan = () => {
|
||||||
const newDay = dayPlans.length;
|
const currentPlans = getCurrentMessagePlans();
|
||||||
setDayPlans([
|
const newDay = currentPlans.length;
|
||||||
...dayPlans,
|
const updatedPlans = [
|
||||||
|
...currentPlans,
|
||||||
{
|
{
|
||||||
day: newDay,
|
day: newDay,
|
||||||
messages: [
|
messages: [
|
||||||
@@ -156,7 +113,8 @@ const MessageSettings: React.FC<MessageSettingsProps> = ({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]);
|
];
|
||||||
|
onChange({ ...formData, messagePlans: updatedPlans });
|
||||||
setIsAddDayPlanOpen(false);
|
setIsAddDayPlanOpen(false);
|
||||||
message.success(`已添加第${newDay}天的消息计划`);
|
message.success(`已添加第${newDay}天的消息计划`);
|
||||||
};
|
};
|
||||||
@@ -168,363 +126,82 @@ const MessageSettings: React.FC<MessageSettingsProps> = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const currentPlans = getCurrentMessagePlans();
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: "确认删除",
|
title: "确认删除",
|
||||||
content: `确定要删除第${dayPlans[dayIndex].day}天的消息计划吗?`,
|
content: `确定要删除第${currentPlans[dayIndex].day}天的消息计划吗?`,
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
const updatedPlans = dayPlans.filter((_, index) => index !== dayIndex);
|
const updatedPlans = currentPlans.filter(
|
||||||
|
(_, index) => index !== dayIndex,
|
||||||
|
);
|
||||||
// 重新计算天数
|
// 重新计算天数
|
||||||
const recalculatedPlans = updatedPlans.map((plan, index) => ({
|
const recalculatedPlans = updatedPlans.map((plan, index) => ({
|
||||||
...plan,
|
...plan,
|
||||||
day: index,
|
day: index,
|
||||||
}));
|
}));
|
||||||
setDayPlans(recalculatedPlans);
|
|
||||||
onChange({ ...formData, messagePlans: recalculatedPlans });
|
onChange({ ...formData, messagePlans: recalculatedPlans });
|
||||||
message.success(`已删除第${dayPlans[dayIndex].day}天的消息计划`);
|
message.success(`已删除第${currentPlans[dayIndex].day}天的消息计划`);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const items = dayPlans.map((plan, dayIndex) => ({
|
const items = getCurrentMessagePlans().map(
|
||||||
key: plan.day.toString(),
|
(plan: MessageContentGroup, dayIndex: number) => ({
|
||||||
label: (
|
key: plan.day.toString(),
|
||||||
<div
|
label: (
|
||||||
style={{
|
<div
|
||||||
display: "flex",
|
style={{
|
||||||
alignItems: "center",
|
display: "flex",
|
||||||
gap: "2px",
|
alignItems: "center",
|
||||||
}}
|
gap: "2px",
|
||||||
>
|
}}
|
||||||
<span>{plan.day === 0 ? "即时消息" : `第${plan.day}天`}</span>
|
|
||||||
{dayIndex > 0 && (
|
|
||||||
<Button
|
|
||||||
type="text"
|
|
||||||
size="small"
|
|
||||||
icon={<CloseOutlined />}
|
|
||||||
onClick={e => {
|
|
||||||
e.stopPropagation();
|
|
||||||
handleRemoveDayPlan(dayIndex);
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
padding: "0 4px",
|
|
||||||
minWidth: "auto",
|
|
||||||
color: "#ff4d4f",
|
|
||||||
fontSize: "12px",
|
|
||||||
}}
|
|
||||||
title="删除此天计划"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
children: (
|
|
||||||
<div className={styles["messages-day-panel"]}>
|
|
||||||
{plan.messages.map((message, messageIndex) => (
|
|
||||||
<div key={message.id} className={styles["messages-message-card"]}>
|
|
||||||
<div className={styles["messages-message-header"]}>
|
|
||||||
{/* 时间/间隔设置 */}
|
|
||||||
<div className={styles["messages-message-header-content"]}>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
|
||||||
{plan.day === 0 ? (
|
|
||||||
<>
|
|
||||||
<span style={{ minWidth: 36 }}>间隔</span>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
value={String(message.sendInterval || 5)}
|
|
||||||
onChange={e =>
|
|
||||||
handleUpdateMessage(dayIndex, messageIndex, {
|
|
||||||
sendInterval: Number(e.target.value),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
style={{ width: 60 }}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
onClick={() =>
|
|
||||||
toggleIntervalUnit(dayIndex, messageIndex)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<ClockCircleOutlined />
|
|
||||||
{message.intervalUnit === "minutes" ? "分钟" : "秒"}
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<span style={{ minWidth: 60 }}>发送时间</span>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
min={0}
|
|
||||||
max={23}
|
|
||||||
value={String(message.scheduledTime?.hour || 9)}
|
|
||||||
onChange={e =>
|
|
||||||
handleUpdateMessage(dayIndex, messageIndex, {
|
|
||||||
scheduledTime: {
|
|
||||||
...(message.scheduledTime || {
|
|
||||||
hour: 9,
|
|
||||||
minute: 0,
|
|
||||||
second: 0,
|
|
||||||
}),
|
|
||||||
hour: Number(e.target.value),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
style={{ width: 40 }}
|
|
||||||
/>
|
|
||||||
<span>:</span>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
min={0}
|
|
||||||
max={59}
|
|
||||||
value={String(message.scheduledTime?.minute || 0)}
|
|
||||||
onChange={e =>
|
|
||||||
handleUpdateMessage(dayIndex, messageIndex, {
|
|
||||||
scheduledTime: {
|
|
||||||
...(message.scheduledTime || {
|
|
||||||
hour: 9,
|
|
||||||
minute: 0,
|
|
||||||
second: 0,
|
|
||||||
}),
|
|
||||||
minute: Number(e.target.value),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
style={{ width: 40 }}
|
|
||||||
/>
|
|
||||||
<span>:</span>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
min={0}
|
|
||||||
max={59}
|
|
||||||
value={String(message.scheduledTime?.second || 0)}
|
|
||||||
onChange={e =>
|
|
||||||
handleUpdateMessage(dayIndex, messageIndex, {
|
|
||||||
scheduledTime: {
|
|
||||||
...(message.scheduledTime || {
|
|
||||||
hour: 9,
|
|
||||||
minute: 0,
|
|
||||||
second: 0,
|
|
||||||
}),
|
|
||||||
second: Number(e.target.value),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
style={{ width: 40 }}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
className={styles["messages-message-remove-btn"]}
|
|
||||||
onClick={() => handleRemoveMessage(dayIndex, messageIndex)}
|
|
||||||
title="删除"
|
|
||||||
>
|
|
||||||
<CloseOutlined />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{/* 类型切换按钮 */}
|
|
||||||
<div className={styles["messages-message-type-btns"]}>
|
|
||||||
{messageTypes.map(type => (
|
|
||||||
<Button
|
|
||||||
key={type.id}
|
|
||||||
type={message.type === type.id ? "primary" : "default"}
|
|
||||||
onClick={() =>
|
|
||||||
handleUpdateMessage(dayIndex, messageIndex, {
|
|
||||||
type: type.id as any,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
className={styles["messages-message-type-btn"]}
|
|
||||||
>
|
|
||||||
<type.icon />
|
|
||||||
</Button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className={styles["messages-message-content"]}>
|
|
||||||
{/* 文本消息 */}
|
|
||||||
{message.type === "text" && (
|
|
||||||
<Input.TextArea
|
|
||||||
value={message.content}
|
|
||||||
onChange={e =>
|
|
||||||
handleUpdateMessage(dayIndex, messageIndex, {
|
|
||||||
content: e.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
placeholder="请输入消息内容"
|
|
||||||
autoSize={{ minRows: 3, maxRows: 6 }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{/* 小程序消息 */}
|
|
||||||
{message.type === "miniprogram" && (
|
|
||||||
<>
|
|
||||||
<Input
|
|
||||||
value={message.title}
|
|
||||||
onChange={e =>
|
|
||||||
handleUpdateMessage(dayIndex, messageIndex, {
|
|
||||||
title: e.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
placeholder="请输入小程序标题"
|
|
||||||
style={{ marginBottom: 8 }}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
value={message.description}
|
|
||||||
onChange={e =>
|
|
||||||
handleUpdateMessage(dayIndex, messageIndex, {
|
|
||||||
description: e.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
placeholder="请输入小程序描述"
|
|
||||||
style={{ marginBottom: 8 }}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
value={message.address}
|
|
||||||
onChange={e =>
|
|
||||||
handleUpdateMessage(dayIndex, messageIndex, {
|
|
||||||
address: e.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
placeholder="请输入小程序路径"
|
|
||||||
style={{ marginBottom: 8 }}
|
|
||||||
/>
|
|
||||||
<div style={{ marginBottom: 8 }}>
|
|
||||||
<MainImgUpload
|
|
||||||
value={message.coverImage || ""}
|
|
||||||
onChange={url =>
|
|
||||||
handleUpdateMessage(dayIndex, messageIndex, {
|
|
||||||
coverImage: url,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
maxSize={5}
|
|
||||||
showPreview={true}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{/* 链接消息 */}
|
|
||||||
{message.type === "link" && (
|
|
||||||
<>
|
|
||||||
<Input
|
|
||||||
value={message.title}
|
|
||||||
onChange={e =>
|
|
||||||
handleUpdateMessage(dayIndex, messageIndex, {
|
|
||||||
title: e.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
placeholder="请输入链接标题"
|
|
||||||
style={{ marginBottom: 8 }}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
value={message.description}
|
|
||||||
onChange={e =>
|
|
||||||
handleUpdateMessage(dayIndex, messageIndex, {
|
|
||||||
description: e.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
placeholder="请输入链接描述"
|
|
||||||
style={{ marginBottom: 8 }}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
value={message.linkUrl}
|
|
||||||
onChange={e =>
|
|
||||||
handleUpdateMessage(dayIndex, messageIndex, {
|
|
||||||
linkUrl: e.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
placeholder="请输入链接地址"
|
|
||||||
style={{ marginBottom: 8 }}
|
|
||||||
/>
|
|
||||||
<div style={{ marginBottom: 8 }}>
|
|
||||||
<MainImgUpload
|
|
||||||
value={message.coverImage || ""}
|
|
||||||
onChange={url =>
|
|
||||||
handleUpdateMessage(dayIndex, messageIndex, {
|
|
||||||
coverImage: url,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
maxSize={5}
|
|
||||||
showPreview={true}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{/* 群邀请消息 */}
|
|
||||||
{message.type === "group" && (
|
|
||||||
<div style={{ marginBottom: 8 }}>
|
|
||||||
<GroupSelection
|
|
||||||
selectedOptions={message.groupIds || []}
|
|
||||||
onSelect={groupIds =>
|
|
||||||
handleUpdateMessage(dayIndex, messageIndex, {
|
|
||||||
groupIds: groupIds,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
placeholder="选择邀请入的群"
|
|
||||||
showSelectedList={true}
|
|
||||||
selectedListMaxHeight={200}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{/* 图片消息 */}
|
|
||||||
{message.type === "image" && (
|
|
||||||
<div style={{ marginBottom: 8 }}>
|
|
||||||
<ImageUpload
|
|
||||||
value={message.content ? [message.content] : []}
|
|
||||||
onChange={urls =>
|
|
||||||
handleUpdateMessage(dayIndex, messageIndex, {
|
|
||||||
content: urls[0] || "",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
count={10}
|
|
||||||
accept="image/*"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{/* 视频消息 */}
|
|
||||||
{message.type === "video" && (
|
|
||||||
<div style={{ marginBottom: 8 }}>
|
|
||||||
<VideoUpload
|
|
||||||
value={message.content || ""}
|
|
||||||
onChange={url => {
|
|
||||||
const videoUrl = Array.isArray(url) ? url[0] || "" : url;
|
|
||||||
handleUpdateMessage(dayIndex, messageIndex, {
|
|
||||||
content: videoUrl,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
maxSize={50}
|
|
||||||
maxCount={5}
|
|
||||||
showPreview={true}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{/* 文件消息 */}
|
|
||||||
{message.type === "file" && (
|
|
||||||
<div style={{ marginBottom: 8 }}>
|
|
||||||
<FileUpload
|
|
||||||
value={message.content || ""}
|
|
||||||
onChange={url => {
|
|
||||||
const fileUrl = Array.isArray(url) ? url[0] || "" : url;
|
|
||||||
handleUpdateMessage(dayIndex, messageIndex, {
|
|
||||||
content: fileUrl,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
maxSize={10}
|
|
||||||
maxCount={10}
|
|
||||||
showPreview={true}
|
|
||||||
acceptTypes={["excel", "word", "ppt"]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<Button
|
|
||||||
onClick={() => handleAddMessage(dayIndex)}
|
|
||||||
className={styles["messages-add-message-btn"]}
|
|
||||||
>
|
>
|
||||||
<PlusOutlined className="w-4 h-4 mr-2" />
|
<span>{plan.day === 0 ? "即时消息" : `第${plan.day}天`}</span>
|
||||||
添加消息
|
{dayIndex > 0 && (
|
||||||
</Button>
|
<Button
|
||||||
</div>
|
type="text"
|
||||||
),
|
size="small"
|
||||||
}));
|
icon={<CloseOutlined />}
|
||||||
|
onClick={e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleRemoveDayPlan(dayIndex);
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
padding: "0 4px",
|
||||||
|
minWidth: "auto",
|
||||||
|
color: "#ff4d4f",
|
||||||
|
fontSize: "12px",
|
||||||
|
}}
|
||||||
|
title="删除此天计划"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
children: (
|
||||||
|
<div className={styles["messages-day-panel"]}>
|
||||||
|
{plan.messages.map((message, messageIndex) => (
|
||||||
|
<MessageCard
|
||||||
|
key={message.id}
|
||||||
|
message={message}
|
||||||
|
dayIndex={dayIndex}
|
||||||
|
messageIndex={messageIndex}
|
||||||
|
planDay={plan.day}
|
||||||
|
onUpdateMessage={handleUpdateMessage}
|
||||||
|
onRemoveMessage={handleRemoveMessage}
|
||||||
|
onToggleIntervalUnit={toggleIntervalUnit}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
<Button
|
||||||
|
onClick={() => handleAddMessage(dayIndex)}
|
||||||
|
className={styles["messages-add-message-btn"]}
|
||||||
|
>
|
||||||
|
<PlusOutlined className="w-4 h-4 mr-2" />
|
||||||
|
添加消息
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles["messages-container"]}>
|
<div className={styles["messages-container"]}>
|
||||||
@@ -555,7 +232,7 @@ const MessageSettings: React.FC<MessageSettingsProps> = ({
|
|||||||
onClick={handleAddDayPlan}
|
onClick={handleAddDayPlan}
|
||||||
className={styles["messages-modal-btn"]}
|
className={styles["messages-modal-btn"]}
|
||||||
>
|
>
|
||||||
添加第 {dayPlans.length} 天计划
|
添加第 {getCurrentMessagePlans().length} 天计划
|
||||||
</Button>
|
</Button>
|
||||||
</Modal>
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -30,3 +30,53 @@ export const posterTemplates = [
|
|||||||
url: "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/%E7%82%B9%E5%87%BB%E6%8A%A5%E5%90%8D-Mj0nnva0BiASeDAIhNNaRRAbjPgjEj.gif",
|
url: "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/%E7%82%B9%E5%87%BB%E6%8A%A5%E5%90%8D-Mj0nnva0BiASeDAIhNNaRRAbjPgjEj.gif",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
// ========================================
|
||||||
|
import {
|
||||||
|
MessageOutlined,
|
||||||
|
PictureOutlined,
|
||||||
|
VideoCameraOutlined,
|
||||||
|
FileOutlined,
|
||||||
|
AppstoreOutlined,
|
||||||
|
LinkOutlined,
|
||||||
|
TeamOutlined,
|
||||||
|
} from "@ant-design/icons";
|
||||||
|
|
||||||
|
export interface MessageContentItem {
|
||||||
|
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;
|
||||||
|
groupIds?: string[]; // 改为数组以支持GroupSelection组件
|
||||||
|
groupOptions?: any[]; // 添加群选项数组
|
||||||
|
linkUrl?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MessageContentGroup {
|
||||||
|
day: number;
|
||||||
|
messages: MessageContentItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MessageSettingsProps {
|
||||||
|
formData: any;
|
||||||
|
onChange: (data: any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 消息类型配置
|
||||||
|
export 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: "邀请入群" },
|
||||||
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user